摘要:由于二维数组中代表黑棋,代表白棋,,的数字集合即可代表棋盘上的棋子情况,可以对整个棋盘数组进行遍历,得到每一个可以下的位置的八个方向的棋子情况。
java基本入门之后,迎来第一个挑战——五子棋设计
寒假的时候,靠着看java手册,实现了双人对战并判断输赢的功能。但是一直卡在了人机对战上面。
之后随着学习的深入,终于实现。
以下详细的叙述一下整体的设计过程:
首先是五子棋窗口界面的设计,画窗体,加按钮,这些都比较基础,主要是要重写重绘的方法,否则每次改变窗体都会使其变化。
package wuziqi; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class FiveChessUI extends JPanel implements Config { /** *五子棋界面 */ private static final long serialVersionUID = 1L; private static int[][] chesses=new int[ROWS][COLUMNS]; public static void main(String[] args) { FiveChessUI fcUI = new FiveChessUI(); fcUI.initUI(); } public void initUI () { JFrame jf = new JFrame(); jf.setTitle("五子棋v0.5 by xzw"); jf.setSize(700,650); jf.setLocationRelativeTo(null); jf.add(this,BorderLayout.CENTER); this.setBackground(Color.ORANGE); jf.setDefaultCloseOperation(3); this.setLayout(new FlowLayout()); JPanel jp2 = new JPanel(); jp2.setPreferredSize(new Dimension(100, 0)); jp2.setBackground(Color.CYAN); // JButton jbuStart = new JButton("开始"); // jbuStart.setPreferredSize(new Dimension(70, 70)); JButton jbuReg = new JButton("悔棋"); jbuReg.setPreferredSize(new Dimension(70, 50)); JButton jbuR = new JButton("重来"); jbuR.setPreferredSize(new Dimension(70, 50)); JButton jbup2p = new JButton("双人"); jbup2p.setPreferredSize(new Dimension(70, 70)); JButton jbup2c = new JButton("人机"); jbup2c.setPreferredSize(new Dimension(70, 70)); JLabel la2 = new JLabel("当前执子:black"); jp2.add(jbup2p); jp2.add(jbup2c); jp2.add(jbuReg); jp2.add(jbuR); jp2.add(la2); jf.add(jp2,BorderLayout.EAST); jf.setVisible(true); Graphics g = this.getGraphics(); ChessListener e = new ChessListener(g,chesses,this); jbup2p.addActionListener(e); jbup2c.addActionListener(e); jbuReg.addActionListener(e); jbuR.addActionListener(e); } /** * 重写重绘方法 */ public void paint(Graphics g){ //调用父类的重绘窗体 super.paint(g); //重绘窗体的同时绘制棋盘和棋子 drawChessTable(g); drawChesses(g); } //画棋盘 public void drawChessTable (Graphics g){ g.setColor(Color.BLACK); for (int i=0;i其次是界面鼠标监听器的设计,窗体被分为2个界面,左边的界面作为棋盘,右边的界面用作放按钮,模式,悔棋,重来等按钮都放在上面。
在做这里的时候意识到自己还不会传参,因此要在不同类里对同一个对象进行修改就会很困难。于是我向请教学会了传参的方法,即构造函数传参法。在一个类的构造函数的参数里添加需要传递的参数,并声明this.var=var(var即为参数)然后在另一个类里调用这个类的方法时,声明对象时加入要传的参数,即实现了参数传递。
通过继承mouseAdapter类,重写该类的mouseReleased的方法,实现鼠标点击后画一个棋子。(重写方法时要求名称相同,参数相同,返回值相同)同时,要想做到在鼠标点击的附近的交叉点上画棋子,需要先计算出鼠标点击处的行列坐标。
定义一与棋盘相同大小的二维数组,用来存放棋子,每在一个交叉点下一颗棋子,就在数组相应位置将其置为1或2,便于之后的判断输赢以及人机算法的实现。另加一个变量判断所下棋子的黑白。
public ChessListener(Graphics g, int[][] chesses,JPanel jp) { this.g = g; this.chesses = chesses; this.jp=jp; a = new Check(chesses); } int flag=0; int r=0,c=0; public void mouseReleased(MouseEvent e) { // 得到鼠标事件发生的时候光标的位置 int x = e.getX(); int y = e.getY(); r = (x - X0 + CHESS_SIZE / 2) / CHESS_SIZE; c = (y - Y0 + CHESS_SIZE / 2) / CHESS_SIZE; if (chesses[r][c] == 0) { if (count == 0) { chesses[r][c] = 1; g.setColor(Color.BLACK); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count++; } else if (count == 1) { chesses[r][c] = 2; g.setColor(Color.WHITE); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count--; } } if (a.validateChess(r, c)) { if (count == 0) { JOptionPane.showMessageDialog(null, "WhiteWin"); jp.removeMouseListener(this); } else { JOptionPane.showMessageDialog(null, "BlackWin"); jp.removeMouseListener(this); } } }然后就是五子棋判断输赢的方法了。原理很简单,只要对每次下的子的四个方向判断是否有5个子连在一起即可。只要中间有一个颜色不同,立即break。
几天后发现,这里的判断输赢的方法有些不合理,斜向的判断方法有问题。有时会出现斜向4个棋子就被判赢了,经过
观察发现,斜向的两个方向我把合在了一起。所以就会出现4+1=5,判断赢的情况。
改进后代码如下:package wuziqi; public class Check { private int[][] chesses; public Check(int[][] chesses) { this.chesses = chesses; } public boolean validateChess(int x, int y) { boolean state = false; if (checkRow(x, y) == 5||checkColumn(x,y)==5||checkDiagonal1(x,y)==5||checkDiagonal2(x,y)==5) state = true; // System.out.print(chesses.length); return state; } public int checkRow(int x,int y) { int count=0; for (int i=x+1;i=0;i--) {//go left if (chesses[i][y]==chesses[x][y]) { count++; } else break; } return count; } public int checkColumn(int x,int y) { int count=0; for (int i=y+1;i =0;i--) {//go up if (chesses[x][i]==chesses[x][y]) { count++; } else break; } return count; } public int checkDiagonal1(int x,int y) { int count=1; for(inti=1;x+i =0&&y-i>=0;i++) {//go left up if (chesses[x-i][y-i]==chesses[x][y]) { count++; } else break; } return count; } public int checkDiagonal2(int x,int y) { int count=1; for (int i=1;x+i =0;i++) {//go right up if (chesses[x+i][y-i]==chesses[x][y]) { count++; } else break; } for (int i=1;x-i>=0&&y+i 接下来,就是花费我时间最长的人机算法了。首先要让电脑实现下棋,而且是在相对正常的位置落子,即要通过判断棋盘上棋子的形势。由于二维数组中1代表黑棋,2代表白棋,1,2的数字集合即可代表棋盘上的棋子情况,可以对整个棋盘数组进行遍历,得到每一个可以下的位置的八个方向的棋子情况。因此可以让每一种情况对应一种权值,权值越大,说明该位置越危险,最后只需找到权值最大的那个位置,落子,即可。
string来代表棋子情况,数组存权值,建立一个同时存string和int的的hashmap。并预存好每个情况对应的权值。
权值修改:(使电脑更智能)
public ChessAI(int chesses[][], int chessValue[][]) { this.chesses = chesses; this.chessValue = chessValue; //black hm.put("1", 11); hm.put("11", 110); hm.put("111", 1200); hm.put("1111", 11000); hm.put("12", 11); hm.put("112", 110); hm.put("1112", 1100); hm.put("11112", 11000); hm.put("21", 11); hm.put("211", 110); hm.put("2111", 1100); hm.put("21111", 11000); //white hm.put("2", 10); hm.put("22", 100); hm.put("222", 1100); hm.put("2222", 10000); hm.put("221", 100); hm.put("2221", 1000); hm.put("22221", 10000); hm.put("122", 100); hm.put("1222", 1000); hm.put("12222", 10000); } `` 每个位置,八个方向遍历,存入权值数组。public void AI() { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLUMNS; j++) { if (chesses[i][j] == 0) { String code = ""; color = 0; // 向东 for (int k = i + 1; k < ROWS; k++) { if (chesses[k][j] == 0) {// 为空跳出循环 break; } else { if (color == 0) {// 用color记住开始的地方棋子的颜色 color = chesses[k][j]; code += chesses[k][j]; } else if (chesses[k][j] == color) {// 颜色相同 code += chesses[k][j]; } else {// 颜色不同 code += chesses[k][j]; break; } } } // System.out.print(code); Integer value = hm.get(code); if (value != null) { chessValue[i][j] += value; } // 向西 code = ""; color = 0// south-east code = ""; color = 0; for (int p = 1; i + p < ROWS && j + p < COLUMNS; p++) { if (chesses[i + p][j + p] == 0) { break; } else { if (color == 0) {// 开始的地方棋子的颜色 color = chesses[i + p][j + p]; code += chesses[i + p][j + p]; } else if (chesses[i + p][j + p] == color) { code += chesses[i + p][j + p]; } else { code += chesses[i + p][j + p]; break; } } } value = hm.get(code); if (value != null) { chessValue[i][j] += value; }以及接下来ai监听器的设计,先由玩家自己下黑子之后,判断输赢。再调用ai算法,获取权值最大的点,然后在该点画白子,下完之后立即清空权值数组。以便于下次下子时,重新统计。
public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); r = (x - X0 + CHESS_SIZE / 2) / CHESS_SIZE; c = (y - Y0 + CHESS_SIZE / 2) / CHESS_SIZE; if (chesses[r][c] == 0 && count == 0) { chesses[r][c] = 1; g.setColor(Color.BLACK); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count++; } if (a.validateChess(r, c)) { JOptionPane.showMessageDialog(null, "BlackWin"); jp.removeMouseListener(this); } else { ai.AI(); r1 = ai.getr(chessValue); c1 = ai.getc(chessValue); if (chesses[r1][c1] == 0 && count == 1) { chesses[r1][c1] = 2; g.setColor(Color.WHITE); g.fillOval(X0 + r1 * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c1 * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count--; } if (a.validateChess(r1, c1)) { JOptionPane.showMessageDialog(null, "WhiteWin"); jp.removeMouseListener(this); } } // 电脑下完后清空 for (int i1 = 0; i1 < ROWS; i1++) { for (int j1 = 0; j1 < COLUMNS; j1++) { chessValue[i1][j1] = 0; } } }
至此,五子棋项目设计完成。但实际下的结果电脑有时候下的子还是不太科学,希望之后还能改善让难度更高些。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70313.html
摘要:简言之,机器学习是内功,而数据挖掘则是机器学习的一种用途。但本质上是我在学习机器学习方面的实战项目,所以我想办法利用机器学习的方面的算法实现。 BetaMeow的起源 前段时间AlphaGo和李世石广受关注,作为人工智能的脑残粉,看完比赛后激动不已,因为有一定的机器学习的基础,便打算撸一个棋类的AI,但我还算有点自知之明,围棋AI,甚至google打算做得通用AI是做不出的了,所以打算...
摘要:五子棋游戏博客官网示例实现源码之前一直在用,前几天看了下的官方文档,写了个加强对的理解,欢迎指正。五子棋游戏该模块实现了五子棋和井字游戏两个游戏。五子棋游戏只记录了最近步的数据,步以前的数据不会记录,故悔棋只可悔步以内的棋。 五子棋游戏 + 博客 demo + React官网示例实现 github 源码:https://github.com/moshang-xc/react-demo ...
阅读 2690·2021-11-22 13:52
阅读 1168·2021-10-14 09:43
阅读 3618·2019-08-30 15:56
阅读 2928·2019-08-30 13:22
阅读 3246·2019-08-30 13:10
阅读 1538·2019-08-26 13:45
阅读 1086·2019-08-26 11:47
阅读 2747·2019-08-23 18:13