Java初學者之五子棋遊戲實現教程
本文為大傢分享瞭Java實現五子棋遊戲的具體代碼,供大傢參考,具體內容如下
1.圖形化界面的創建
1.1創建JFrame窗體容器
1)JFrame窗體需要設置基本的大小、佈局、默認的關閉方式,以及最重要的設置可見。
1.2在JFrame上添加組件,用來繪制棋盤棋子和遊戲操作。
1)棋盤棋子的繪制:自定義一個類去繼承JPanel,把繪制棋盤和棋子的方法重寫進入paint()方法裡,這樣當窗體發生變化(放大、縮小、移動等操作時,棋盤棋子不會消失,棋局得以保存)。
2)悔棋、認輸等操作通過JButton按鈕添加鼠標監聽來實現。
2.關鍵點的實現
(使用六個類實現背後邏輯,UI類,DrawChessBoard類,GameMouse類,QiZi類,Location類以及AI類)。
2.1在棋盤點擊的位置繪制棋子
1)給棋盤添加鼠標監聽,獲取點擊位置的坐標
UI類負責初始化1中的圖形化界面,並給DrawChessBoard類添加GameMouse類的監聽。這樣在鼠標點擊棋盤時通過重寫GameMouse類的mouseClicked(),就可以獲取鼠標在棋盤上點擊的像素坐標。
2)坐標轉化成二位數組中的坐標
保存棋盤上所有棋子位置用到QiZi類中的int[][] memory二維數組,這樣把像素坐標轉化為二維數組中的坐標,並附上棋子顏色對應的值,就可以保存棋盤上所有棋子的位置。
3)在棋盤上畫出棋子
在DrawChessBoard類paint()方法中遍歷QiZi類中的int[][] memory二維數組非零值,就可以在相應位置調用畫筆方法畫出黑白棋子。
2.2判斷輸贏
1)下完棋子後,將下棋位置保存到QiZi類的int[][] memory二維數組中後,就可以以該點為中心計算其四個方向上連續棋子的數目,達到五個則通過JOptionPane類生成彈窗確定贏傢。
2.2悔棋功能和提示最後落子位置功能的實現
1)每次成功下一顆棋子,就可以創建一個保存瞭棋子坐標的Location對象,並將該對象添加到 QiZi類的ArrayList或者Stack容器當中,當鼠標點擊悔棋Button後,清除QiZi類的int[][] memory二維數組相應位置的數值(將之改為0即可),然後棋盤重繪棋子,就可以完成悔棋的效果。
2)同時可以找到容器中最後落子的位置,並在棋盤相應的坐標出畫出最後落子提示。
2.3開始、認輸的實現
1)開始遊戲,即重置遊戲,將棋子類的相應屬性清零即可,比如int[][] memory二維數組(即棋譜),owener=1(重置為白色),以及清楚棋盤上面的棋子。
2)認輸就可以判斷當前QiZi.owner的值,來判斷輸的一方並給出提示即可。
2.4AI的實現
1)默認AI為黑方的情況下,需要在白色方落子之後調用AI下黑色棋子,所以在需要在GameMouse中下白棋的if分支中調用AI方法
2)AI的厲害與否取決於其設計,在這裡提供一個思路:設置一個棋型對照表,給不同棋型賦值(如1111,代表白子四連,權重較高),輪到AI時,可以根據該表計算棋盤上每一個空位在八個方向總的權重大小,在權重最大處落子即可。棋型對照表中不同棋的權重設置,可以通過python等分析大量棋局來獲取,以此來訓練AI,當權重設置越合理,AI就越強。
3.其他功能
下子的動畫效果音效等可以通過開辟不同的線程來實現,而網絡對戰則可增加網絡通信相關模塊即可。
4.源碼
package wuziqi925; import javax.swing.*; import java.awt.*; public class GameUI { public static void main(String[] args) { GameUI gameUI=new GameUI(); gameUI.showUI(); } public void showUI(){ //創建棋子對象 QiZi qizi=new QiZi(); //獲取窗體 JFrame jFrame=new JFrame(); jFrame.setSize(1000,795); jFrame.setDefaultCloseOperation(3); jFrame.setLocationRelativeTo(null); jFrame.setLayout(null); jFrame.setTitle("五子棋"); jFrame.setResizable(false); //窗體添加棋盤面板 DrawChessBoard chessBoard=new DrawChessBoard(qizi); jFrame.add(chessBoard); chessBoard.setSize(760,760); chessBoard.setBackground(Color.ORANGE); //測試JFrame框架像素大小,Insets[top=32,left=3,bottom=3,right=3] //System.out.println(jFrame.getInsets()); //窗體添加選項面板,用來畫棋盤 JPanel bp=new JPanel(); bp.setSize(236,760); bp.setBackground(Color.lightGray); bp.setLocation(760,0); bp.setLayout(null); jFrame.add(bp); //選項面板添加按鈕 JButton start=new JButton("開始"); start.setBackground(Color.white); start.setFont(new Font("華文行楷",Font.BOLD,20)); start.setBounds(40,350,150,50); JButton quit=new JButton("認輸"); quit.setBackground(Color.white); quit.setFont(new Font("華文行楷",Font.BOLD,20)); quit.setBounds(40,440,150,50); JButton undo=new JButton("悔棋"); undo.setBackground(Color.white); undo.setFont(new Font("華文行楷",Font.BOLD,20)); undo.setBounds(40,530,150,50); bp.add(start); bp.add(quit); bp.add(undo); //選擇模式選項 ButtonGroup bg=new ButtonGroup(); JRadioButton rrdz=new JRadioButton("玩傢對戰"); JRadioButton rjdz=new JRadioButton("人機對戰"); rrdz.setSize(120,30); rrdz.setLocation(55,60); rrdz.setFont(new Font("華文行楷",Font.BOLD,20)); rrdz.setVisible(true); rjdz.setSize(120,30); rjdz.setLocation(55,90); rjdz.setFont(new Font("華文行楷",Font.BOLD,20)); rjdz.setVisible(true); bg.add(rjdz); bg.add(rrdz); bp.add(rjdz); bp.add(rrdz); bp.setVisible(true); //設置窗體可見 jFrame.setVisible(true); AI ai=new AI(qizi,chessBoard); //獲取棋盤的鼠標監聽和畫筆並將該畫筆添加給鼠標 Graphics g1=chessBoard.getGraphics(); GameMouse gameMouse=new GameMouse(qizi,chessBoard,ai); chessBoard.addMouseListener(gameMouse); start.addActionListener(gameMouse); quit.addActionListener(gameMouse); undo.addActionListener(gameMouse); rrdz.addActionListener(gameMouse); rjdz.addActionListener(gameMouse); } }
package wuziqi925; import javax.swing.*; import java.awt.*; public class DrawChessBoard extends JPanel { QiZi qiZi; private static int LINE_NUM = 15; private static int MARGIN_WIDTH = 30; public static int CELL_SIZE = 50; public DrawChessBoard(QiZi qiZi) { this.qiZi = qiZi; } @Override public void paint(Graphics g) { super.paint(g); //畫棋盤 for (int i = 0; i < LINE_NUM; i++) { g.drawLine(MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE, MARGIN_WIDTH + i * CELL_SIZE); g.drawLine(MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE); } //畫棋盤上的點 g.fillOval(CELL_SIZE*3+22,CELL_SIZE*3+22,16,16); g.fillOval(CELL_SIZE*11+22,CELL_SIZE*3+22,16,16); g.fillOval(CELL_SIZE*3+22,CELL_SIZE*11+22,16,16); g.fillOval(CELL_SIZE*11+22,CELL_SIZE*11+22,16,16); //畫棋子 int[][] a = qiZi.memory; for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { if (a[i][j] == 1) { g.setColor(Color.white); g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46); } else if (a[i][j] == 2) { g.setColor(Color.black); g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46); } } } //畫出最後一步棋子的位置上的十字架 if (qiZi.arr.size() > 0) { Graphics2D g1=(Graphics2D)g; g1.setColor(Color.red); g1.setStroke(new BasicStroke(3.0f)); Location l = qiZi.arr.get(qiZi.arr.size() - 1); g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH - 8, CELL_SIZE * l.y + MARGIN_WIDTH, CELL_SIZE * l.x + MARGIN_WIDTH + 8, CELL_SIZE * l.y + MARGIN_WIDTH); g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH - 8, CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH + 8); } } }
package wuziqi925; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; public class GameMouse implements MouseListener, ActionListener { QiZi qizi; DrawChessBoard drawChessBoard; AI ai; public GameMouse() { } public GameMouse( QiZi qiZi, DrawChessBoard drawChessBoard,AI ai) { this.qizi = qiZi; this.drawChessBoard=drawChessBoard; this.ai=ai; } @Override public void mouseClicked(MouseEvent e) { int cellSize= DrawChessBoard.CELL_SIZE; int x=e.getX(); int xx=0; int y=e.getY(); int yy=0; //在點擊位置畫棋子,並將下子位置轉換成棋子在二維數組中的坐標 if (qizi.owner==1) { if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) { xx=(x - 30) / cellSize + 1; yy=(y - 30) / cellSize + 1; } else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) { xx=(x - 30) / cellSize + 1; yy=(y - 30) / cellSize; } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) { xx=(x - 30) / cellSize; yy=(y - 30) / cellSize + 1; } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) { xx=(x - 30) / cellSize; yy=(y - 30) / cellSize; } if(qizi.memory[xx][yy]==0) {//判斷無子 qizi.x=xx; qizi.y=yy; qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子並將棋子放入容器 Location location=new Location(qizi);//記錄剛下的棋子位置順序 qizi.arr.add(location); drawChessBoard.repaint();//繪制剛下的棋子 qizi.judgeWinner();//判斷輸贏 qizi.owner=2;//交換棋權 } if (ai.state){ ai.initiateAI(); } }else { qizi.owner=2; if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) { xx=(x - 30) / cellSize + 1; yy=(y - 30) / cellSize + 1; } else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) { xx=(x - 30) / cellSize + 1; yy=(y - 30) / cellSize; } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) { xx=(x - 30) / cellSize; yy=(y - 30) / cellSize + 1; } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) { xx=(x - 30) / cellSize; yy=(y - 30) / cellSize; } if(qizi.memory[xx][yy]==0) { qizi.x=xx; qizi.y=yy; qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子並將棋子放入容器 Location location=new Location(qizi);//記錄剛下的棋子位置順序 qizi.arr.add(location); drawChessBoard.repaint();//繪制剛下的棋子 qizi.judgeWinner();//判斷輸贏 qizi.owner=1;//交換棋權 } } } @Override public void actionPerformed(ActionEvent e) { int cellSize= DrawChessBoard.CELL_SIZE; String s=e.getActionCommand(); JOptionPane tc=new JOptionPane(); //悔棋功能的實現 if (s.equals("悔棋")){ if (qizi.arr.size()>0) { Location l = qizi.arr.get(qizi.arr.size() - 1); qizi.memory[l.x][l.y] = 0; qizi.arr.remove(qizi.arr.size() - 1); if (qizi.owner == 1) { qizi.owner = 2; } else if (qizi.owner == 2) { qizi.owner = 1; } }else { tc.showMessageDialog(null,"無棋可毀,請下棋!"); } //刷新棋盤 drawChessBoard.repaint(); } if (s.equals("開始")){ qizi.owner=1; qizi.memory=new int[15][15]; qizi.arr.clear(); qizi.win=false; drawChessBoard.repaint(); } if(s.equals("認輸")){ int whiteCount=0; int blackCount=0; for (int i = 0; i < qizi.memory.length; i++) { for (int j = 0; j < qizi.memory[i].length; j++) { if (qizi.memory[i][j]==1){ whiteCount++; }else if (qizi.memory[i][j]==2){ blackCount++; } } } if (whiteCount==blackCount){ tc.showMessageDialog(null,qizi.owner==1 ?"黑方投降,白方勝!":"白方投降,黑方勝!"); }else if(whiteCount>blackCount){ tc.showMessageDialog(null,"黑方投降,白方勝!"); }else { tc.showMessageDialog(null,"白方投降,黑方勝!"); } } if (s.equals("人機對戰")){ ai.state=true; } if (s.equals("玩傢對戰")){ ai.state=false; } } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } }
package wuziqi925; import javax.swing.*; import java.util.ArrayList; public class QiZi { int x; int y; int owner=1;//1 代表白色,2代表黑色 int[][] memory; ArrayList <Location>arr; boolean win=false; public QiZi() { memory=new int[15][15]; arr=new ArrayList<>(50); } public void judgeWinner() { JOptionPane tc=new JOptionPane(); int count1=0; int count2=0; int count3=0; int count4=0; //豎直方向檢測 for (int i = y-1; i >-1 ; i--) { if (memory[x][i]==owner){ count1++; }else { break; } } for (int i = y+1; i <15; i++) { if (memory[x][i]==owner){ count1++; }else { break; } } if (count1 > 3){ tc.showMessageDialog(null,owner==1?"白方勝":"黑方勝"); win=true; return; } //水平方向檢測 for (int i = x-1; i >-1 ; i--) { if (memory[i][y]==owner){ count2++; }else { break; } } for (int i = x+1; i <15; i++) { if (memory[i][y]==owner){ count2++; }else { break; } } if (count2 > 3){ tc.showMessageDialog(null,owner==1?"白方勝":"黑方勝"); win=true; return; } //在\方向上檢測 int yy=y; for (int i = x+1; i <15; i++) { if(yy==14){ break; } yy++; if (memory[i][yy]==owner){ count3++; }else { break; } } yy=y; for (int i = x-1; i >-1; i--) { if (yy==0){ break; } yy--; if (memory[i][yy]==owner){ count3++; }else { break; } } if (count3 > 3){ tc.showMessageDialog(null,owner==1?"白方勝":"黑方勝"); win=true; return; } //在/方向上檢測 yy=y; for (int i = x+1; i <15; i++) { if(yy==0){ break; } yy--; if (memory[i][yy]==owner){ count4++; }else { break; } } yy=y; for (int i = x-1; i >-1; i--) { if(yy==14){ break; } yy++; if (memory[i][yy]==owner){ count4++; }else { break; } } if (count4 > 3){ tc.showMessageDialog(null,owner==1?"白方勝":"黑方勝"); win=true; return; } } }
package wuziqi925; public class Location { QiZi qiZi; int x; int y; public Location(QiZi qiZi) { //記錄棋譜 x=qiZi.x; y=qiZi.y; } public Location(int x, int y) { this.x = x; this.y = y; } }
package wuziqi925; import java.util.*; public class AI { boolean state=false;//true為on false為off QiZi qiZi; //存儲棋型權值 private HashMap<String,Integer> playValueTable=new HashMap<>(); //存儲每個可下點的權重大小 private HashMap<Location,Integer> locationsAndValues=new HashMap<>(); DrawChessBoard drawChessBoard; int AIDO=0; public AI(QiZi qiZi,DrawChessBoard drawChessBoard){ this.drawChessBoard=drawChessBoard; this.qiZi=qiZi; //1代表該方向為白方棋子(玩傢),2代表該方向為黑方棋子(AI) playValueTable.put("22221",100); playValueTable.put("2222",100); playValueTable.put("11112",99); playValueTable.put("1111",0); playValueTable.put("2221",40); playValueTable.put("222",45); playValueTable.put("1112",35); playValueTable.put("111",46); playValueTable.put("22",25); playValueTable.put("221",20); playValueTable.put("11",15); playValueTable.put("112",10); playValueTable.put("21",5); playValueTable.put("2",10); playValueTable.put("1",8); playValueTable.put("12",2); } public void initiateAI(){ if (qiZi.win){ return; } int chessValue=0; //遍歷棋盤找到空點 for (int i = 0; i <qiZi.memory.length; i++) { for (int j = 0; j < qiZi.memory[i].length; j++) { //計算qiZi.memory[i][j](空點)位置的權值,保存ij位點 if (qiZi.memory[i][j]==0){ Location l=new Location(i,j); chessValue=countValue(i,j); locationsAndValues.put(l,chessValue); } } } //System.out.println(chessValue); //判斷權值最大的點,獲取該點的坐標值並傳入qiZi.memory //按照value對HashMap<Location,Integer> locationsAndValues進行排序找到最大值 Set<Map.Entry<Location,Integer>> set=locationsAndValues.entrySet(); List<Map.Entry<Location,Integer>> list=new ArrayList<>(set); list.sort(new Comparator<Map.Entry<Location, Integer>>() { @Override public int compare(Map.Entry<Location, Integer> o1, Map.Entry<Location, Integer> o2) { return o2.getValue()-o1.getValue(); } }); //排序完畢取最大//獲取最大權重值對應的空點坐標 Map.Entry<Location,Integer> maxSet=list.get(0); Location toDo=maxSet.getKey(); qiZi.x= toDo.x; qiZi.y= toDo.y; qiZi.memory[qiZi.x][qiZi.y]=qiZi.owner; Location location=new Location(qiZi);//記錄剛下的棋子位置順序 qiZi.arr.add(location); drawChessBoard.repaint();//繪制剛下的棋子 qiZi.judgeWinner();//判斷輸贏 qiZi.owner=1;//交換棋權 System.out.println(++AIDO); locationsAndValues.clear(); } private int countValue(int i,int j) { int totalValue=0; StringBuilder s1=new StringBuilder(); StringBuilder s2=new StringBuilder(); StringBuilder s3=new StringBuilder(); StringBuilder s4=new StringBuilder(); StringBuilder s5=new StringBuilder(); StringBuilder s6=new StringBuilder(); StringBuilder s7=new StringBuilder(); StringBuilder s8=new StringBuilder(); //八個方向去去判定 //North for (int k = j-1; k >-1 ; k--) { if (qiZi.memory[i][k]==1){ s1.append(1); }else if (qiZi.memory[i][k]==2) { s1.append(2); }else { break; } } int count1=playValueTable.get(s1.toString())==null?0:playValueTable.get(s1.toString()); totalValue+=count1; //South for (int k = j+1; k <15; k++) { if (qiZi.memory[i][k]==1){ s2.append(1); }else if (qiZi.memory[i][k]==2) { s2.append(2); }else { break; } } int count2=playValueTable.get(s2.toString())==null?0:playValueTable.get(s2.toString()); totalValue+=count2; //West for (int k = i-1; k >-1 ; k--) { if (qiZi.memory[k][j]==1){ s3.append(1); }else if (qiZi.memory[k][j]==2) { s3.append(2); }else { break; } } int count3=playValueTable.get(s3.toString())==null?0:playValueTable.get(s3.toString()); totalValue+=count3; //East for (int k = i+1; k <15; k++) { if (qiZi.memory[k][j]==1){ s4.append(1); }else if (qiZi.memory[k][j]==2) { s4.append(2); }else { break; } } int count4=playValueTable.get(s4.toString())==null?0:playValueTable.get(s4.toString()); totalValue+=count4; //SE int yy=j; for (int k = i+1; k < 15; k++) { if(yy==14){ break; } yy++; if (qiZi.memory[k][yy]==1){ s5.append(1); }else if (qiZi.memory[k][yy]==2) { s5.append(2); }else { break; } } int count5=playValueTable.get(s5.toString())==null?0:playValueTable.get(s5.toString()); totalValue+=count5; //NW yy=j; for (int k = i-1; k >-1; k--) { if(yy==0){ break; } yy--; if (qiZi.memory[k][yy]==1){ s6.append(1); }else if (qiZi.memory[k][yy]==2) { s6.append(2); }else { break; } } int count6=playValueTable.get(s6.toString())==null?0:playValueTable.get(s6.toString()); totalValue+=count6; //NE yy=j; for (int k = i+1; k <15; k++) { if(yy==0){ break; } yy--; if (qiZi.memory[k][yy]==1){ s7.append(1); }else if (qiZi.memory[k][yy]==2) { s7.append(2); }else { break; } } int count7=playValueTable.get(s7.toString())==null?0:playValueTable.get(s7.toString()); totalValue+=count6; //SW yy=j; for (int k = i-1; k >-1; k--) { if(yy==14){ break; } yy++; if (qiZi.memory[k][yy]==1){ s8.append(1); }else if (qiZi.memory[k][yy]==2) { s8.append(2); }else { break; } } int count8=playValueTable.get(s8.toString())==null?0:playValueTable.get(s8.toString()); totalValue+=count8; return totalValue; } }
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。
推薦閱讀:
- unity實現簡單計算器
- java基礎之 Arrays.toString()方法詳解
- Java實用工具之StringJoiner詳解
- 提高C# StringBuilder操作性能優化的方法
- Java 自定義動態數組方式