Java初學(xué)者之五子棋游戲?qū)崿F(xiàn)教程
本文為大家分享了Java實現(xiàn)五子棋游戲的具體代碼,供大家參考,具體內(nèi)容如下
1.圖形化界面的創(chuàng)建
1.1創(chuàng)建JFrame窗體容器
1)JFrame窗體需要設(shè)置基本的大小、布局、默認(rèn)的關(guān)閉方式,以及最重要的設(shè)置可見。
1.2在JFrame上添加組件,用來繪制棋盤棋子和游戲操作。
1)棋盤棋子的繪制:自定義一個類去繼承JPanel,把繪制棋盤和棋子的方法重寫進(jìn)入paint()方法里,這樣當(dāng)窗體發(fā)生變化(放大、縮小、移動等操作時,棋盤棋子不會消失,棋局得以保存)。
2)悔棋、認(rèn)輸?shù)炔僮魍ㄟ^JButton按鈕添加鼠標(biāo)監(jiān)聽來實現(xiàn)。
2.關(guān)鍵點的實現(xiàn)
(使用六個類實現(xiàn)背后邏輯,UI類,DrawChessBoard類,GameMouse類,QiZi類,Location類以及AI類)。
2.1在棋盤點擊的位置繪制棋子
1)給棋盤添加鼠標(biāo)監(jiān)聽,獲取點擊位置的坐標(biāo)
UI類負(fù)責(zé)初始化1中的圖形化界面,并給DrawChessBoard類添加GameMouse類的監(jiān)聽。這樣在鼠標(biāo)點擊棋盤時通過重寫GameMouse類的mouseClicked(),就可以獲取鼠標(biāo)在棋盤上點擊的像素坐標(biāo)。
2)坐標(biāo)轉(zhuǎn)化成二位數(shù)組中的坐標(biāo)
保存棋盤上所有棋子位置用到QiZi類中的int[][] memory二維數(shù)組,這樣把像素坐標(biāo)轉(zhuǎn)化為二維數(shù)組中的坐標(biāo),并附上棋子顏色對應(yīng)的值,就可以保存棋盤上所有棋子的位置。
3)在棋盤上畫出棋子
在DrawChessBoard類paint()方法中遍歷QiZi類中的int[][] memory二維數(shù)組非零值,就可以在相應(yīng)位置調(diào)用畫筆方法畫出黑白棋子。
2.2判斷輸贏
1)下完棋子后,將下棋位置保存到QiZi類的int[][] memory二維數(shù)組中后,就可以以該點為中心計算其四個方向上連續(xù)棋子的數(shù)目,達(dá)到五個則通過JOptionPane類生成彈窗確定贏家。
2.2悔棋功能和提示最后落子位置功能的實現(xiàn)
1)每次成功下一顆棋子,就可以創(chuàng)建一個保存了棋子坐標(biāo)的Location對象,并將該對象添加到 QiZi類的ArrayList或者Stack容器當(dāng)中,當(dāng)鼠標(biāo)點擊悔棋Button后,清除QiZi類的int[][] memory二維數(shù)組相應(yīng)位置的數(shù)值(將之改為0即可),然后棋盤重繪棋子,就可以完成悔棋的效果。
2)同時可以找到容器中最后落子的位置,并在棋盤相應(yīng)的坐標(biāo)出畫出最后落子提示。
2.3開始、認(rèn)輸?shù)膶崿F(xiàn)
1)開始游戲,即重置游戲,將棋子類的相應(yīng)屬性清零即可,比如int[][] memory二維數(shù)組(即棋譜),owener=1(重置為白色),以及清楚棋盤上面的棋子。
2)認(rèn)輸就可以判斷當(dāng)前QiZi.owner的值,來判斷輸?shù)囊环讲⒔o出提示即可。
2.4AI的實現(xiàn)
1)默認(rèn)AI為黑方的情況下,需要在白色方落子之后調(diào)用AI下黑色棋子,所以在需要在GameMouse中下白棋的if分支中調(diào)用AI方法
2)AI的厲害與否取決于其設(shè)計,在這里提供一個思路:設(shè)置一個棋型對照表,給不同棋型賦值(如1111,代表白子四連,權(quán)重較高),輪到AI時,可以根據(jù)該表計算棋盤上每一個空位在八個方向總的權(quán)重大小,在權(quán)重最大處落子即可。棋型對照表中不同棋的權(quán)重設(shè)置,可以通過python等分析大量棋局來獲取,以此來訓(xùn)練AI,當(dāng)權(quán)重設(shè)置越合理,AI就越強。
3.其他功能
下子的動畫效果音效等可以通過開辟不同的線程來實現(xiàn),而網(wǎng)絡(luò)對戰(zhàn)則可增加網(wǎng)絡(luò)通信相關(guān)模塊即可。
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(){ //創(chuàng)建棋子對象 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("認(rèn)輸"); 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("玩家對戰(zhàn)"); JRadioButton rjdz=new JRadioButton("人機(jī)對戰(zhàn)"); 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); //設(shè)置窗體可見 jFrame.setVisible(true); AI ai=new AI(qizi,chessBoard); //獲取棋盤的鼠標(biāo)監(jiān)聽和畫筆并將該畫筆添加給鼠標(biāo) 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; //在點擊位置畫棋子,并將下子位置轉(zhuǎn)換成棋子在二維數(shù)組中的坐標(biāo) 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;//交換棋權(quán) } 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;//交換棋權(quán) } } } @Override public void actionPerformed(ActionEvent e) { int cellSize= DrawChessBoard.CELL_SIZE; String s=e.getActionCommand(); JOptionPane tc=new JOptionPane(); //悔棋功能的實現(xiàn) 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("認(rèn)輸")){ 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("人機(jī)對戰(zhàn)")){ ai.state=true; } if (s.equals("玩家對戰(zhàn)")){ 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; //存儲棋型權(quán)值 private HashMap<String,Integer> playValueTable=new HashMap<>(); //存儲每個可下點的權(quán)重大小 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](空點)位置的權(quán)值,保存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); //判斷權(quán)值最大的點,獲取該點的坐標(biāo)值并傳入qiZi.memory //按照value對HashMap<Location,Integer> locationsAndValues進(jìn)行排序找到最大值 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(); } }); //排序完畢取最大//獲取最大權(quán)重值對應(yīng)的空點坐標(biāo) 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;//交換棋權(quán) 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; } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java并發(fā)之原子性 有序性 可見性及Happen Before原則
一提到happens-before原則,就讓人有點“丈二和尚摸不著頭腦”。這個涵蓋了整個JMM中可見性原則的規(guī)則,究竟如何理解,把我個人一些理解記錄下來。下面可以和小編一起學(xué)習(xí)Java 并發(fā)四個原則2021-09-09