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 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 playValueTable=new HashMap<>(); //存储每个可下点的权重大小 private HashMap 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=https://www.it610.com/article/0; //遍历棋盘找到空点 for (int i = 0; i locationsAndValues进行排序找到最大值 Set> set=locationsAndValues.entrySet(); List> list=new ArrayList<>(set); list.sort(new Comparator>() { @Override public int compare(Map.Entry o1, Map.Entry o2) { return o2.getValue()-o1.getValue(); } }); //排序完毕取最大//获取最大权重值对应的空点坐标 Map.Entry 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=https://www.it610.com/article/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; } }

【java|Java初学者练习之五子棋游戏教程(附源码)】

    推荐阅读