JAVA聯機版五子棋——原始碼(一個類一個main暴力開發)
第一次寫部落格,排版啥的都比較亂,大家不要嫌棄啊。
所謂暴力開發,其實是啥也不會,硬懟哈哈哈。只是剛學會一點JAVA程式設計,參考網路資源寫了個聯機版五子棋(支援單機),來這裡記錄一下學習過程,而且我也是靠別人的分享才能寫出來,所以本著互相幫助的原則,分享給大家,拋磚引玉,共同進步。
作為一個半路出家,正在學習道路上的渣渣,摸索著寫了這樣一個亂亂的五子棋,很多地方都不正規,比如因為沒有系統學過GUI,對話方塊都用的JOptionPane元件,模式選擇對話方塊也放在main方法裡,算是偷懶吧。。
正如標題裡寫的“一個類一個main”,寫的時候為了快速且方便,都放在一個類裡了,因此大家想要測試的話,直接整個複製就行了,沒啥黑科技,JDK自帶的包就能執行。
因為個人的惡趣味,本五子棋起名“老鐵互懟五子棋”,當然各位可以自己隨意修改。
轉載也請隨意,標明出處就行了。
遊戲開啟後是這個介面,直接選擇遊戲模式,不能半途選別的模式,如果要更換,需要重啟遊戲(不然老出線程異常,所以直接砍掉這個功能哈哈哈。。。)。
選擇“是”則為聯機版,“否”或關閉對話方塊為單機版。
聯機版需要輸入對方的主機名或IP(區域網)。
沒有整合GUI,所以主機名和埠號都需要分別輸入(偷個懶)。
這裡輸入本機的埠號。
輸入對方埠號,雙方埠號需要互相對應。
以上步驟都沒錯且網路沒問題,則連線成功,點選確定可以開始遊戲,先落子的為黑棋。
說明一下,如果輸入的主機名或地址為本機,且輸入的埠號一致,則為單機模式(預防我預設的9653埠號被佔,這種方式可以自己設定埠號)。
因為使用了執行緒,可以本機開啟兩個遊戲介面,聯機模式下可以左右互搏(狗頭),也是實現單機模式的原理(好像也是開發中執行緒老是佔用的罪魁禍首,難受)。
如果一開始選擇了“否”或關閉對話方塊,則為單機模式,顯示下圖所示對話方塊,點選確定開始遊戲。
遊戲介面如下,剛落下的棋子有外框顯示。
程式碼如下(註釋可能有點囉嗦。。):
package netWuZiQiDemo; //開啟遊戲選擇"是"則為聯機模式,選擇"否"或關閉對話方塊則為單機模式,預設埠號9653(這是一個神奇數字) //本版本聯機需要輸入對方主機名或IP地址,並且需要雙方埠號對應相反 //本版本聯機模式,雙方輪流執黑 //如果主機名或IP為自己主機(或沒有輸入),則轉為單機模式,此時需兩個埠號一致 //為了解決執行緒異常問題(水平有限...),特推出此魔改版,開啟遊戲必須選擇其中一種模式(聯機或單機),要想更換,則要關閉遊戲重新開啟 import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; /** * 老鐵互懟五子棋1.0 * @author Arno-Woke * */ public class NetLTWZQDemo extends JPanel implements MouseListener, Runnable { int x;// 落子處棋盤座標 int y; int[][] chess = new int[12][12];// 棋盤大小為12x12 int ex;// 傳送滑鼠點選座標 int ey; boolean me = true;// 初始化為己方先落黑子 boolean one = true;// 控制聯機雙方每次只能一個人下子 static boolean start = false;// 預設開啟遊戲不能下子,需先選擇遊戲模式 static InetAddress inetAddress;// 客戶端確認服務端的IP地址 static String UsernameOrIp;// 聯機對方主機名或IP地址 // 客戶端和服務端埠號一致則為單機版五子棋 static int clientPort;// 設定客戶端埠 static int serverPort;// 設定服務端埠 static boolean danJi;// 選擇單機模式,則程式只判斷一次勝負,防止對局結束後無法落子,若聯機模式,則判斷兩次勝負 static Thread t;// 宣告一個執行緒 private static final long serialVersionUID = 1L;//emmm 沒什麼用,提示要有一個就放一個吧 public static void main(String[] args) { JFrame jFrame = new JFrame("老鐵互懟五子棋"); jFrame.setBounds((Toolkit.getDefaultToolkit().getScreenSize().width - 655) / 2, (Toolkit.getDefaultToolkit().getScreenSize().height - 675) / 2, 655, 675);// 視窗螢幕正中放置 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setResizable(false);//窗體大小固定 jFrame.setVisible(true);//窗體設定可見 NetLTWZQDemo netLTWZQDemo = new NetLTWZQDemo(); jFrame.add(netLTWZQDemo); t = new Thread(netLTWZQDemo); //為了方便(又懶又笨..),直接把所有內容寫到一個類裡,且把模式選擇對話方塊放在main方法裡 int respones = JOptionPane.showConfirmDialog(null, "老鐵你愁啥,想找個人懟懟?", "生死看淡,不服就幹!", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (respones == JOptionPane.YES_OPTION) { try { UsernameOrIp = JOptionPane.showInputDialog(null, "輸入丫主機名或IP", "記小本本上每天看兩遍", JOptionPane.WARNING_MESSAGE) .trim(); clientPort = Integer.parseInt(JOptionPane .showInputDialog(null, "老鐵從幾號門出發?", "門牌號:1024~65535", JOptionPane.WARNING_MESSAGE) .trim()); serverPort = Integer.parseInt(JOptionPane .showInputDialog(null, "對面在幾號門?", "門牌號:1024~65535", JOptionPane.WARNING_MESSAGE) .trim()); } catch (Exception e) { JOptionPane.showMessageDialog(null, "輸錯了,老鐵從頭再來吧!", "憐憫的眼神看著你", JOptionPane.ERROR_MESSAGE); System.exit(0); e.printStackTrace(); } try { inetAddress = InetAddress.getByName(UsernameOrIp); } catch (UnknownHostException ex) { ex.printStackTrace(); } if (inetAddress != null) { JOptionPane.showMessageDialog(null, "開懟!", "安排上了!", JOptionPane.WARNING_MESSAGE); start = true;//設定遊戲開始 if (clientPort == serverPort) {// 若主機名或IP為自己電腦(或不填寫)且兩個埠一樣,則為單機模式 danJi = true;//開啟單機模式 } else { danJi = false;// 聯機模式,設定單機模式為false } t.start(); } } if (respones == JOptionPane.NO_OPTION || respones == JOptionPane.CLOSED_OPTION) { JOptionPane.showMessageDialog(null, "自懟?啥也別說了,老鐵雙擊666", "冷笑不語", JOptionPane.INFORMATION_MESSAGE); try { inetAddress = InetAddress.getLocalHost(); serverPort = clientPort = 9653;// 預設埠號為9653 start = true;//設定遊戲開始 danJi = true;// 開啟單機模式 t.start();//開啟執行緒開啟遊戲 } catch (UnknownHostException ex) { JOptionPane.showMessageDialog(null, "老鐵,本地網路出問題了吧?", "是時候換臺電腦了老鐵", JOptionPane.ERROR_MESSAGE); System.exit(0); ex.printStackTrace(); } } } @Override public void run() {// 使用執行緒,埠號一致可進行單機遊戲 clearChess();//遊戲開啟,重繪棋盤 receive();//開啟服務端接收資料 } public NetLTWZQDemo() { setBackground(Color.gray); addMouseListener(this);//註冊滑鼠事件 setVisible(true);//設定窗體可見 } public void paintComponent(Graphics g) { Graphics2D g2D = (Graphics2D) g;// 使用Graphics2D使構圖平滑 g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); super.paintComponent(g); drawChessBoard(g); drawChess(g); } public void drawChess(Graphics g) { for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { if (chess[i][j] == 1) { g.setColor(Color.black); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 2) { g.setColor(Color.white); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 11) {// 點選落黑子時外加個黑框 g.setColor(Color.black); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); g.drawRect((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 22) {// 點選落白子時棋子外加個白框 g.setColor(Color.white); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); g.drawRect((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } } } } public void drawChessBoard(Graphics g) { // 畫棋盤 for (int i = 50; i <= 600; i += 50) { g.setColor(Color.WHITE); g.drawLine(i, 50, i, 600); g.drawLine(50, i, 600, i); } } public void clearChess() {// 初始化棋盤 for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { chess[i][j] = 0; } } me = true;// 初始化為己方落黑子 repaint(); } //這個判斷勝負的方法是抄的網上一段程式碼,略作修改(為保持原貌幾乎沒有修改,提醒自己不能這麼幹,嗯!),emmm,互相學習嘛,哈哈哈(尬笑),實在是想的腦闊疼,偷個懶...(不過好像點的太快,會出現bug,導致沒有5子連著也會判勝,可能是錯覺吧...) void checkWiner() {// 判斷勝方 int black_count = 0; int white_count = 0; for (int i = 0; i < 12; i++) {// 橫向判斷 for (int j = 0; j < 12; j++) { if (chess[i][j] == 1 || chess[i][j] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋勝利"); clearChess(); return; } } else { black_count = 0; } if (chess[i][j] == 2 || chess[i][j] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋勝利"); clearChess(); return; } } else { white_count = 0; } } } for (int i = 0; i < 12; i++) {// 豎向判斷 for (int j = 0; j < 12; j++) { if (chess[j][i] == 1 || chess[j][i] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋勝利"); clearChess(); return; } } else { black_count = 0; } if (chess[j][i] == 2 || chess[j][i] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋勝利"); clearChess(); return; } } else { white_count = 0; } } } for (int i = 0; i < 7; i++) {// 左向右斜判斷 for (int j = 0; j < 7; j++) { for (int k = 0; k < 5; k++) { if (chess[i + k][j + k] == 1 || chess[i + k][j + k] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋勝利"); clearChess(); return; } } else { black_count = 0; } if (chess[i + k][j + k] == 2 || chess[i + k][j + k] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋勝利"); clearChess(); return; } } else { white_count = 0; } } } } for (int i = 4; i < 12; i++) {// 右向左斜判斷 11->12 for (int j = 6; j >= 0; j--) { for (int k = 0; k < 5; k++) { if (chess[i - k][j + k] == 1 || chess[i - k][j + k] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋勝利"); clearChess(); return; } } else { black_count = 0; } if (chess[i - k][j + k] == 2 || chess[i - k][j + k] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋勝利"); clearChess(); return; } } else { white_count = 0; } } } } } @Override public void mouseReleased(MouseEvent e) { System.out.println(e.getX() + "==" + e.getY());// 在棋盤上點選滑鼠時在控制檯輸出座標資訊,方便測試 if (start == false) { return; } ex = e.getX(); ey = e.getY(); if (ex >= 25 && ex <= 625 && ey >= 25 && ey <= 625) {// 控制滑鼠點選位於棋盤內部 x = (ex - 19) / 50;// 使棋子排列在棋盤交點處 y = (ey - 19) / 50; } if (chess[x][y] != 0) {// 當點選位置已經有棋子,則此次點選無效,並繼續落子 return; } if (one == true) {// 輪到一方落子時,落子判斷勝負,並傳送滑鼠點選的座標 doit();// 若傳送座標前判斷勝負,則會導致最後一顆棋子落子資訊阻塞,判斷完畢後再次傳送,故此處不判斷 sendXY(ex, ey);// 先傳送座標再判斷勝負 if (danJi == false) {// 聯機模式下才進行此次勝負判斷 checkWiner(); } } one = false;// 換到對方落子 } private void doit() {// 判斷落子顏色及判斷勝方 x = (ex - 19) / 50; y = (ey - 19) / 50; if (chess[x][y] == 0) {// 點選位置無棋子 if (me == true) { chess[x][y] = 11;// 黑子 for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { if (chess[i][j] == 22) { chess[i][j] = 2;// 把剛才下的加白框的白子轉換為普通白子 } } } me = false;// 換為白子落子 } else if (me == false) { chess[x][y] = 22;// 白子 for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { if (chess[i][j] == 11) { chess[i][j] = 1;// 把剛才下的加黑框的黑子轉換為普通黑子 } } } me = true;// 換為黑子落子 } } repaint();// 重繪棋盤後判斷勝方 } public void sendXY(int xSend, int ySend) {// 傳送滑鼠點選的座標 try {// 問題在於每次傳送座標都要重新建立一個socket物件,不知道怎麼改進 Socket socket = new Socket(inetAddress, serverPort);// 設定對方伺服器端IP,並設定埠號為9653; OutputStream os = socket.getOutputStream(); String strr = xSend + "-" + ySend;// 使用-符號連線座標,並以字串形式傳送到服務端 os.write(strr.getBytes()); socket.shutdownOutput();// 及時傳送位置資訊,不用closs()是保持通訊一直連線 } catch (Exception ex) { JOptionPane.showMessageDialog(null, "老鐵,多人一起懟不合適吧?", "懟亦有道!", JOptionPane.ERROR_MESSAGE); System.exit(0); ex.printStackTrace(); } } public void receive() {// 服務端接收對方發來的座標 try { ServerSocket serverSocket = new ServerSocket(clientPort);// 從該埠接收資料 while (true) { Socket sSocket = serverSocket.accept(); InputStream is = sSocket.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String strReceive = new String(bys, 0, len); System.out.println(strReceive);//接收到的字串輸出在控制檯,方便測試 String[] strs = strReceive.split("-");// 使用"-"分割字串,得到座標 ex = Integer.parseInt(strs[0]); ey = Integer.parseInt(strs[1]); doit();// 得到座標後,重繪棋盤並判斷勝方 checkWiner(); one = true;// 己方不能落子,棋權交給對方 } } catch (IOException e) { JOptionPane.showMessageDialog(null, "老鐵,多人一起懟不合適吧?", "懟亦有道!", JOptionPane.ERROR_MESSAGE); System.exit(0); e.printStackTrace(); } } @Override public void mouseClicked(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } }