Java五子棋(人机版)
2021/7/20 9:06:22
本文主要是介绍Java五子棋(人机版),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
<iframe allowfullscreen="true" data-mediaembed="csdn" id="nKaQimkf-1626741607279" src="https://live.csdn.net/v/embed/171268"></iframe>Java五子棋,老程序员也花了3天
作者简介
作者名:编程界明世隐
简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!
系列目录
1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. Java植物大战僵尸
5. Java消消乐(天天爱消除)
6. Java贪吃蛇小游戏
7. Java扫雷小游戏
8. Java坦克大战
效果图
实现思路
1.创建运行窗口并添加背景色。
2.绘制棋盘。
3.用二维数组来控制起码落子位置、绘制指示器。
4.鼠标在落子位置处点击可落子。
5.落子后检查是否获得胜利。
6.机器判断下一步,并落子。
7.机器判断是否获得胜利。
代码实现
创建窗口
首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。
/* * 游戏窗体类 */ public class GameFrame extends JFrame { public GameFrame() { setTitle("五子棋");//设置标题 setSize(620, 670);//设定尺寸 getContentPane().setBackground(new Color(209,146,17));//添加背景色 setLayout(new BorderLayout()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序 setLocationRelativeTo(null); //设置居中 setResizable(false); //不允许修改界面大小 } }
创建面板容器GamePanel继承至JPanel
import javax.swing.JFrame; import javax.swing.JPanel; /* * 画布类 */ public class GamePanel extends JPanel { private static final long serialVersionUID = 1L; GamePanel gamePanel=this; private JFrame mainFrame=null; //构造里面初始化相关参数 public GamePanel(JFrame frame){ this.setLayout(null); this.setOpaque(false); mainFrame = frame; mainFrame.requestFocus(); mainFrame.setVisible(true); } }
再创建一个Main类,来启动这个窗口。
public class Main { //主类 public static void main(String[] args) { GameFrame frame = new GameFrame(); GamePanel gamePanel = new GamePanel(frame); frame.add(gamePanel); frame.setVisible(true);//设定显示 } }
右键执行这个Main类,窗口建出来了
创建菜单及菜单选项
创建菜单
private void initMenu(){ // 创建菜单及菜单选项 jmb = new JMenuBar(); JMenu jm1 = new JMenu("游戏"); jm1.setFont(new Font("思源宋体", Font.BOLD, 18));// 设置菜单显示的字体 JMenu jm2 = new JMenu("帮助"); jm2.setFont(new Font("思源宋体", Font.BOLD, 18));// 设置菜单显示的字体 JMenuItem jmi1 = new JMenuItem("开始新游戏"); JMenuItem jmi2 = new JMenuItem("退出"); jmi1.setFont(new Font("思源宋体", Font.BOLD, 18)); jmi2.setFont(new Font("思源宋体", Font.BOLD, 18)); JMenuItem jmi3 = new JMenuItem("操作说明"); jmi3.setFont(new Font("思源宋体", Font.BOLD, 18)); JMenuItem jmi4 = new JMenuItem("成功/失败判定"); jmi4.setFont(new Font("思源宋体", Font.BOLD, 18)); jm1.add(jmi1); jm1.add(jmi2); jm2.add(jmi3); jm2.add(jmi4); jmb.add(jm1); jmb.add(jm2); mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上 jmi1.addActionListener(this); jmi1.setActionCommand("Restart"); jmi2.addActionListener(this); jmi2.setActionCommand("Exit"); jmi3.addActionListener(this); jmi3.setActionCommand("help"); jmi4.addActionListener(this); jmi4.setActionCommand("lost"); }
实现ActionListener并重写方法actionPerformed
此时GamePanel是报错的,重写actionPerformed方法。
actionPerformed方法的实现
@Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); System.out.println(command); UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18))); UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18))); if ("Exit".equals(command)) { Object[] options = { "确定", "取消" }; int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.exit(0); } }else if("Restart".equals(command)){ if(!"end".equals(gamePanel.gameFlag)){ JOptionPane.showMessageDialog(null, "正在游戏中无法重新开始!", "提示!", JOptionPane.INFORMATION_MESSAGE); }else{ if(gamePanel!=null) { gamePanel.restart(); } } }else if("help".equals(command)){ JOptionPane.showMessageDialog(null, "鼠标在指示器位置点下,则落子!", "提示!", JOptionPane.INFORMATION_MESSAGE); }else if("lost".equals(command)){ JOptionPane.showMessageDialog(null, "五子连珠方获得胜利!", "提示!", JOptionPane.INFORMATION_MESSAGE); } }
此时的GamePanel代码如下:
package main; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.plaf.FontUIResource; /* * 画布类 */ public class GamePanel extends JPanel implements ActionListener{ private static final long serialVersionUID = 1L; GamePanel gamePanel=this; private JFrame mainFrame=null; JMenuBar jmb=null; public String gameFlag=""; //构造里面初始化相关参数 public GamePanel(JFrame frame){ this.setLayout(null); this.setOpaque(false); mainFrame = frame; //创建按钮 initMenu(); mainFrame.requestFocus(); mainFrame.setVisible(true); } private void initMenu(){ // 创建菜单及菜单选项 jmb = new JMenuBar(); JMenu jm1 = new JMenu("游戏"); jm1.setFont(new Font("思源宋体", Font.BOLD, 18));// 设置菜单显示的字体 JMenu jm2 = new JMenu("帮助"); jm2.setFont(new Font("思源宋体", Font.BOLD, 18));// 设置菜单显示的字体 JMenuItem jmi1 = new JMenuItem("开始新游戏"); JMenuItem jmi2 = new JMenuItem("退出"); jmi1.setFont(new Font("思源宋体", Font.BOLD, 18)); jmi2.setFont(new Font("思源宋体", Font.BOLD, 18)); JMenuItem jmi3 = new JMenuItem("操作说明"); jmi3.setFont(new Font("思源宋体", Font.BOLD, 18)); JMenuItem jmi4 = new JMenuItem("成功/失败判定"); jmi4.setFont(new Font("思源宋体", Font.BOLD, 18)); jm1.add(jmi1); jm1.add(jmi2); jm2.add(jmi3); jm2.add(jmi4); jmb.add(jm1); jmb.add(jm2); mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上 jmi1.addActionListener(this); jmi1.setActionCommand("Restart"); jmi2.addActionListener(this); jmi2.setActionCommand("Exit"); jmi3.addActionListener(this); jmi3.setActionCommand("help"); jmi4.addActionListener(this); jmi4.setActionCommand("lost"); } //重新开始 public void restart() { //游戏开始标记 gameFlag="start"; } @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); System.out.println(command); UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18))); UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18))); if ("Exit".equals(command)) { Object[] options = { "确定", "取消" }; int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.exit(0); } }else if("Restart".equals(command)){ if(!"end".equals(gamePanel.gameFlag)){ JOptionPane.showMessageDialog(null, "正在游戏中无法重新开始!", "提示!", JOptionPane.INFORMATION_MESSAGE); }else{ if(gamePanel!=null) { gamePanel.restart(); } } }else if("help".equals(command)){ JOptionPane.showMessageDialog(null, "鼠标在指示器位置点下,则落子!", "提示!", JOptionPane.INFORMATION_MESSAGE); }else if("lost".equals(command)){ JOptionPane.showMessageDialog(null, "五子连珠方获得胜利!", "提示!", JOptionPane.INFORMATION_MESSAGE); } } }
运行一下
绘制棋盘
重写paint方法
@Override public void paint(java.awt.Graphics g) { super.paint(g); }
绘制横竖相交接的线
定义15行、15列
public static final int ROWS=15; public static final int COLS=15;
绘制网格
//绘制网格 private void drawGrid(Graphics g) { Graphics2D g_2d=(Graphics2D)g; int start=26; int x1=start; int y1=20; int x2=586; int y2=20; for (int i = 0; i < ROWS; i++) { y1 = start + 40*i; y2 = y1; g_2d.drawLine(x1, y1, x2, y2); } y1=start; y2=586; for (int i = 0; i < COLS; i++) { x1 = start + 40*i; x2 = x1; g_2d.drawLine(x1, y1, x2, y2); } }
绘制5个圆点
//绘制5个黑点 private void draw5Point(Graphics g) { //第1个点 g.fillArc(142, 142, 8, 8, 0, 360); //第2个点 g.fillArc(462, 142, 8, 8, 0, 360); //第3个点 g.fillArc(142, 462, 8, 8, 0, 360); //第4个点 g.fillArc(462, 462, 8, 8, 0, 360); //中心点 g.fillArc(302, 302, 8, 8, 0, 360); }
在paint方法里面调用以上2个方法
@Override public void paint(java.awt.Graphics g) { super.paint(g); //绘制网格 drawGrid(g); //绘制5个黑点 draw5Point(g); }
棋盘已经绘制完成
实现落子指示器
- 创建指示器类
package main; import java.awt.Color; import java.awt.Graphics; //指示器类 public class Pointer { private int i=0;//二维下标i private int j=0;//二维下标j private int x=0;//坐标X private int y=0;//坐标Y private GamePanel panel=null; private Color color=null; private int h=40;//指示的大小 private boolean isShow=false;//是否展示 private int qizi = 0 ;//棋子类型 0:无 1:白棋 2:黑棋 public Pointer(int x,int y,int i,int j,Color color,GamePanel panel){ this.x=x; this.y=y; this.i=i; this.j=j; this.panel=panel; this.color=color; } //绘制 void draw(Graphics g){ Color oColor = g.getColor(); if(color!=null){ g.setColor(color); } if(isShow){ //绘制指示器 g.drawRect(x-h/2, y-h/2, h, h); } if(color!=null){//用完设置回去颜色 g.setColor(oColor); } } //判断鼠标是否在指针范围内 boolean isPoint(int x,int y){ //大于左上角,小于右下角的坐标则肯定在范围内 if(x>this.x-h/2 && y >this.y-h/2 && x<this.x+h/2 && y <this.y+h/2){ return true; } return false; } public boolean isShow() { return isShow; } public void setShow(boolean isShow) { this.isShow = isShow; } public int getQizi() { return qizi; } public void setQizi(int qizi) { this.qizi = qizi; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getI() { return i; } public void setI(int i) { this.i = i; } public int getJ() { return j; } public void setJ(int j) { this.j = j; } }
- 初始化二维数组
public Pointer points[][] = new Pointer[ROWS][COLS];
- 创建指示器实例对象
//创建二维数组 private void createArr() { int x=0,y=0; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { y = 26 + 40*i; x = 26 + 40*j; Pointer pointer = new Pointer(x, y, i,j,new Color(255,0,0), this); points[i][j] = pointer; } } }
- 初始化调用
//初始化相关对象 private void init() { createArr(); //游戏开始标记 gameFlag="start"; }
同时 init方法 在GamePanel 的构造方法调用。
- paint方法中遍历二维数组并且绘制。
@Override public void paint(java.awt.Graphics g) { super.paint(g); //绘制网格 drawGrid(g); //绘制5个黑点 draw5Point(g); //绘制指示器 Pointer pointer = null; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { pointer = points[i][j] ; if(pointer!=null){ pointer.draw(g); } } } }
运行如下
但是这个指示器方块不是我们想要的,需要改一下
- 修改指示器类的绘图代码
在Pointer方法中新增方法,并在指示器绘制的时候调用此方法,而不是之前的drawRect。
private void drawPointer(Graphics g) { Graphics2D g2 = (Graphics2D)g; //g是Graphics对象 g2.setStroke(new BasicStroke(2.0f)); /* * 1.先计算4个顶点 * 2.依次从每个顶点绘制横竖两条线 */ //左上角 int x1 = x-h/2; int y1 = y-h/2; //向右画线 int x2 = x1+1*h/4; int y2 = y1; g2.drawLine(x1, y1, x2, y2); //向下画线 x2 = x1; y2 = y1+1*h/4; g2.drawLine(x1, y1, x2, y2); //右上角 x1 = x+h/2; y1 = y-h/2; //向左画线 x2 = x1-1*h/4; y2 = y1; g2.drawLine(x1, y1, x2, y2); //向下画线 x2 = x1; y2 = y1+1*h/4; g2.drawLine(x1, y1, x2, y2); //右下角 x1 = x+h/2; y1 = y+h/2; //向左画线 x2 = x1-1*h/4; y2 = y1; g2.drawLine(x1, y1, x2, y2); //向上画线 x2 = x1; y2 = y1-1*h/4; g2.drawLine(x1, y1, x2, y2); //左下角 x1 = x-h/2; y1 = y+h/2; //向右画线 x2 = x1+1*h/4; y2 = y1; g2.drawLine(x1, y1, x2, y2); //向上画线 x2 = x1; y2 = y1-1*h/4; g2.drawLine(x1, y1, x2, y2); }
再运行
落子
- 创建ImageValue加载类
import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; public class ImageValue { public static BufferedImage whiteQiziImage ; public static BufferedImage blackQiziImage ; //路径 public static String ImagePath = "/images/"; //将图片初始化 public static void init(){ String whitePath = ImagePath +"white.png"; String blackPath = ImagePath +"black.png"; try { whiteQiziImage = ImageIO.read(ImageValue.class.getResource(whitePath)); blackQiziImage = ImageIO.read(ImageValue.class.getResource(blackPath)); } catch (IOException e) { e.printStackTrace(); } } }
- 创建落子类
import java.awt.Color; import java.awt.Graphics; import common.ImageValue; public class Qizi { private int x = 0; private int y = 0; private int r = 36; private GamePanel panel = null; private Color color = null; private int type = 1;// 棋子类型 1:白棋 2:黑棋 public Qizi(int x, int y, int type, GamePanel panel) { this.x = x; this.y = y; this.panel = panel; this.type=type; } // 绘制 void draw(Graphics g) { Color oColor = g.getColor(); if (type == 1) {// 白色 g.drawImage(ImageValue.whiteQiziImage, x - r / 2, y - r / 2,r,r, null); } else {// 黑色 g.drawImage(ImageValue.blackQiziImage, x - r / 2, y - r / 2,r,r, null); } if (color != null) {// 用完设置回去颜色 g.setColor(oColor); } } public int getType() { return type; } public void setType(int type) { this.type = type; } }
- 在createMouseListener方法中重写mouseClicked,创建棋子
@Override public void mouseClicked(MouseEvent e) { //在合适的位置点击则进行落子操作 if(!"start".equals(gameFlag)) return ; int x = e.getX(); int y = e.getY(); Pointer pointer; for (int i = 0; i <ROWS; i++) { for (int j = 0; j < COLS; j++) { pointer = points[i][j]; if(pointer==null)continue; //被点击,且没有棋子,则可以落子 if(pointer.isPoint(x, y) && pointer.getQizi()==0){ Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), 2, gamePanel); pointer.setQizi(2); qizis.add(qizi); //重绘画布 repaint(); return ; } } } }
- 在paint 方法中绘制棋子
@Override public void paint(java.awt.Graphics g) { super.paint(g); //绘制网格 drawGrid(g); //绘制5个黑点 draw5Point(g); //绘制指示器 Pointer pointer = null; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { pointer = points[i][j] ; if(pointer!=null){ pointer.draw(g); } } } //绘制棋子 Qizi qizi=null; for (int i = 0; i < qizis.size(); i++) { qizi = (Qizi)qizis.get(i); qizi.draw(g); } }
加入电脑AI
- 创建AI类
- 创建静态方法next(下一步)
- 创建静态方法has5(连成5子)
public class AI { //AI进行下一步 static void next(GamePanel gamePanel){ } //判断五子连珠 static boolean has5(Pointer pointer1,GamePanel gamePanel){ return false; } }
- 在你落子后,会先执行has5方法,根据返回来决定走向
- has5返回true则表示你获得胜利,否则AI将会走一步棋
在刚才的mouseClicked修改代码
@Override public void mouseClicked(MouseEvent e) { //在合适的位置点击则进行落子操作 if(!"start".equals(gameFlag)) return ; int x = e.getX(); int y = e.getY(); Pointer pointer; for (int i = 0; i <ROWS; i++) { for (int j = 0; j < COLS; j++) { pointer = points[i][j]; if(pointer==null)continue; //被点击,且没有棋子,则可以落子 if(pointer.isPoint(x, y) && pointer.getQizi()==0){ Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), 2, gamePanel); pointer.setQizi(2); qizis.add(qizi); //重绘画布 repaint(); //判断有没有五子连珠的情况 if(AI.has5(pointer, gamePanel)){ gamePanel.gameWin(); }else{ AI.next(gamePanel); } return ; } } } }
- 在AI类中随机落子(创建方法)
- 随机获取下标 i 和 j。
- 通过下标从二维数组取到指示器对象。
- 如果此指示器被占用则再次随机(递归),直到正确获取到指示器对象。
- 在此指示器位置,创建棋子对象并更新指示器的棋子信息。
- Qizi类中加入last属性,表示AI下的最后一个棋子。
- 根据last在对应的位置创建一个小红方块标示AI的最后落子。
//随机落子 static boolean luoziRandom(GamePanel gamePanel){ Pointer pointer = getRandomPointer(gamePanel); luozi(pointer, gamePanel,1); return true; } //获取随机下的棋子 static Pointer getRandomPointer(GamePanel gamePanel){ Random random = new Random(); int i = random.nextInt(gamePanel.ROWS); int j = random.nextInt(gamePanel.COLS); //取得随机到的格子 Pointer pointer = gamePanel.points[i][j]; if(pointer.getQizi()!=0){//如果当前格子已经下了棋子,则递归重新取 pointer = getRandomPointer(gamePanel); } return pointer; } //AI落子操作 static void luozi(Pointer pointer,GamePanel gamePanel,int type){ if(pointer.getQizi()==0){//如果没有棋子,则落子 Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), type, gamePanel); qizi.setLast(true); pointer.setQizi(type); gamePanel.qizis.add(qizi); //重绘画布 gamePanel.repaint(); //判断电脑有没有五子连珠的情况 if(AI.has5(pointer, gamePanel)){ gamePanel.gameOver(); } } }
- AI的next方法调用随机落子
//AI进行下一步 static void next(GamePanel gamePanel){ luoziRandom(gamePanel); }
运行效果:
- 小红方块一直在,修改代码
仅需在落子前将其他小红方块清除即可
//清除电脑棋子的最后一个棋子指示器 private void clearAILast() { Qizi qizi; for (int i = 0; i < qizis.size(); i++) { qizi = (Qizi)qizis.get(i); if(qizi!=null && qizi.getType()==1){ qizi.setLast(false); } } }
AI算法
棋子的4个方向
-
横向
-
竖向
-
右捺
-
左撇
权重分
描述:计算出分数,最高的分数来决定下一步的落子。
左开:就是说左边可落子
右开:右边可落子
3子的相关定义(4、5子类似)
什么是3子左开,就是目前有2个子,下一个子可以落子左边
3子只能落子在中间,图示:
3子右开就是落子在右边
只计算3个子以上的分数
类型 | 3子左开 | 3子 | 3子右开 |
---|---|---|---|
得分 | 32 | 30 | 31 |
类型 | 4子左开 | 4子 | 4子右开 |
---|---|---|---|
得分 | 42 | 40 | 41 |
类型 | 5子左开 | 5子 | 5子右开 |
---|---|---|---|
得分 | 52 | 50 | 51 |
通过上述表可以看到,落子权重分顺序:5>4>3,同时:左>右>中。
计算横向权重分
- 从左往右判断
* 横向下标 i 是一样的,循环从当前位置 j 加1开始。
* 当碰到和当前子一样的就计数器 +1。
* 当碰到不一样的就退出循环,表示堵住 。
* 如果碰到空子,是第一次计数器 +1,第二次退出循环。
* 判断左开和右开的状态。
* 根据计数器和左右开的状态,计算出分数
- 从右往左判断
* 横向下标 i 是一样的,循环从当前位置 j 减1开始。
* 当碰到和当前子一样的就计数器 +1。
* 当碰到不一样的就退出循环,表示堵住 。
* 如果碰到空子,是第一次计数器 +1,第二次退出循环。
* 判断左开和右开的状态。
* 根据计数器、左右开的状态,计算出分数和落子的位置。
其实左右还是很相似的
static Data getData(Pointer pointer,int dir,int type,GamePanel gamePanel){ Pointer[][] points = gamePanel.points; int i = pointer.getI(); int j = pointer.getJ(); Data resData = new Data(); Pointer tempPointer; int num=1;//默认是1,因为其中自己就是一个子。 int num2=1;//默认是1,用来累加连续的棋子数 int breakNum=0;//默认是0,有一个则不能通过了。 boolean lClosed=false;//左边是否关闭 boolean rClosed=false;//右边是否关闭 if(dir==1){//横向 //往右循环,判断能与当前pointer 相同的棋子连续多少个。 if(type==1){ for (int k = j+1; k < gamePanel.COLS; k++) { tempPointer = points[i][k]; if(tempPointer.getQizi()==pointer.getQizi()){//连续 num++; num2++; if(k == gamePanel.COLS-1){//如果最后一个子也是连续的,则也是右关闭的 rClosed = true; } }else if(tempPointer.getQizi()==0){//空白子 if(breakNum==1){//有一个则不能通过了 if(points[i][k-1].getQizi()==0){//如果前一个是空子,要设置成不是中断的 breakNum=0; }else{ breakNum=2; } break; } breakNum=1; num++; //是中断的那种,这里设定好落子位置 resData.setI(i); resData.setJ(k); }else{//对立子,右关闭 rClosed = true; break; } } //判断是否左关闭 if(j==0){//当前子就是最左边的子 lClosed = true; }else{ tempPointer = points[i][j-1]; if(tempPointer.getQizi()!=0){//如果当前子的左边有子,则左关闭 lClosed = true; } } }else{//从右往左 for (int k = j-1; k >=0; k--) { tempPointer = points[i][k]; if(tempPointer.getQizi()==pointer.getQizi()){//连续 num++; num2++; if(k == 0){//如果最后一个子也是连续的,则也是左关闭的 lClosed = true; } }else if(tempPointer.getQizi()==0){//空白子 if(breakNum==1){//有一个则不能通过了。 if(points[i][k+1].getQizi()==0){//如果前一个是空子,要设置成不是中断的 breakNum=0; }else{ breakNum=2; } break; } breakNum=1; num++; //是中断的那种,这里设定好落子位置 resData.setI(i); resData.setJ(k); }else{//对立子,左关闭 lClosed = true; break; } } //判断是否右关闭 if(j==gamePanel.COLS-1){//当前子就是最右边的子 rClosed = true; }else{ tempPointer = points[i][j+1]; if(tempPointer.getQizi()!=0){//如果当前子的右边有子,则右关闭 rClosed = true; } } } } setCount(resData, i, j, dir, type, num,num2, breakNum, lClosed, rClosed); return resData; } //计算并设置分数 static void setCount(Data data,int i,int j,int dir,int type, int num,int num2,int breakNum,boolean lClosed,boolean rClosed){ int count=0; if(num>2){//连续3个子以上 if(num==3){//设定默认分 count=30; }else if(num==4){ count=40; }else if(num==5){ count=50; } if(num2>=5&&breakNum==0){//用来判断是否五子或五子以上 count=100; //设定好权重分 data.setCount(count); return ; } if(breakNum==0){//如果不是中断的那种 if(lClosed&&rClosed){//如果没有中断,并且左右都关闭了,则分数为-1,-1表示落子的时候要过滤掉 count = -1; }else if(!lClosed){//如果是中断的那种,左边未关闭 count+=2;//加2分 if(dir==1){ if(type==1){ data.setI(i); data.setJ(j-1); }else{ data.setI(i); data.setJ(j-num+1); } }else if(dir==2){ if(type==1){ data.setI(i-1); data.setJ(j); }else{ data.setI(i-num+1); data.setJ(j); } }else if(dir==3){ if(type==1){ data.setI(i-1); data.setJ(j-1); }else{ data.setI(i-num+1); data.setJ(j-num+1); } }else if(dir==4){ if(type==1){ data.setI(i+1); data.setJ(j-1); }else{ data.setI(i+num-1); data.setJ(j-num+1); } } }else if(!rClosed){//如果是中断的那种,右边未关闭 count+=1;//加1分 if(dir==1){ if(type==1){ data.setI(i); data.setJ(j+num-1); }else{ data.setI(i); data.setJ(j+1); } }else if(dir==2){ if(type==1){ data.setI(i+num-1); data.setJ(j); }else{ data.setI(i+1); data.setJ(j); } }else if(dir==3){ if(type==1){ data.setI(i+num-1); data.setJ(j+num-1); }else{ data.setI(i+1); data.setJ(j+1); } }else if(dir==4){ if(type==1){ data.setI(i-num+1); data.setJ(j+num-1); }else{ data.setI(i-1); data.setJ(j+1); } } } }else{//如果中断, if(num!=5){//num不是5, 并且左右都关闭,也要过滤 if(lClosed&&rClosed){ count = -1; } } } //设定好权重分 data.setCount(count); } }
AI落子处理
- 循环取横向、竖向、右捺、左撇 4种分数,放到List集合中
- 对集合进行排序(分数从高到底)
- 第一个分数值作为下一步落子的位置
- 落子操作(如果集合没有值,则进行随机落子)
//进行下一步 static boolean go(GamePanel gamePanel){ List<Data> datas=new ArrayList<Data>(); //循环找出黑棋,判断此棋子的1横向 2纵向 3右捺 4左撇 是否有4子的情况, Pointer pointer; for (int i = 0; i <gamePanel.ROWS; i++) { for (int j = 0; j < gamePanel.COLS; j++) { pointer = gamePanel.points[i][j]; if(pointer==null)continue; if(pointer.getQizi()==0){//没有棋子则跳过 continue; } //循环4个方向 int dir=1; for (int k = 1; k <= 4; k++) { dir = k; Data data = getData(pointer, dir,1, gamePanel); if(data.getCount()!=-1&&data.getCount()!=0){//0和-1 的过滤掉 datas.add(data); } data = getData(pointer, dir, 2,gamePanel); if(data.getCount()!=-1&&data.getCount()!=0){//0和-1 的过滤掉 datas.add(data); } } } } //按权重分排序处理,从大到小 Collections.sort(datas, new DataCount()); /*for (int i = 0; i < datas.size(); i++) { System.out.println("----------"+datas.get(i).getCount()); }*/ if(datas.size()>0){//取第一个位置落子 Data data = datas.get(0); Pointer p = gamePanel.points[data.getI()][data.getJ()]; luozi(p, gamePanel, 1); return true; } return false; }
五子或者以上的判断就很简单了,当棋子是连续的并且计数器大于5就成功了!
这里只介绍了横向的,另外3个情况也差不多,就是注意下标的处理即可。
最后
1. AI还不是特别智能,应该算简单版吧,赢的难度不大.
2. 可能会有我没发现的bug吧,望理解!
看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。
相关阅读
1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. JavaWeb图书管理系统
5. JavaWeb学生宿舍管理系统
6. JavaWeb在线考试系统
为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,原件129元现价 29 元,先到先得,有兴趣的小伙伴可以了解一下!
这篇关于Java五子棋(人机版)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27数据结构与算法面试题详解及练习
- 2024-12-27网络请求面试题详解与实战
- 2024-12-27数据结构和算法面试真题详解与实战教程
- 2024-12-27网络请求面试真题解析与实战教程
- 2024-12-27数据结构和算法大厂面试真题详解与实战指南
- 2024-12-27TS大厂面试真题解析与应对策略
- 2024-12-27TS大厂面试真题详解与解析
- 2024-12-27网站安全入门:如何识别和修复漏洞
- 2024-12-27SQL注入基础教程
- 2024-12-27初学者指南:理解和修复跨域漏洞