Java五子棋(人机版),昨天买的棋子今天就用不上了

news/2024/10/17 22:15:44/

Java五子棋,老程序员也花了3天

作者简介

作者名:编程界明世隐
简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!

热门专栏推荐

【1】Java小游戏(俄罗斯方块、飞机大战、植物大战僵尸等)
【2】JavaWeb项目实战(图书管理、在线考试、宿舍管理等)
【3】JavaScript精彩实例(飞机大战、贪吃蛇、验证码等)
【4】Java小白入门200例
【5】从零学Java、趣学Java
【6】Idea从零到精通

系列目录

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";}@Overridepublic 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);
}

棋盘已经绘制完成
在这里插入图片描述

实现落子指示器

  1. 创建指示器类
package main;import java.awt.Color;
import java.awt.Graphics;//指示器类
public class Pointer {private int i=0;//二维下标iprivate int j=0;//二维下标jprivate int x=0;//坐标Xprivate int y=0;//坐标Yprivate 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;}
}
  1. 初始化二维数组
public Pointer points[][] =  new Pointer[ROWS][COLS];
  1. 创建指示器实例对象
//创建二维数组
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;}}		
}
  1. 初始化调用
//初始化相关对象
private void init() {createArr();//游戏开始标记gameFlag="start";
}

同时 init方法 在GamePanel 的构造方法调用。

  1. 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);}}}
}

运行如下
在这里插入图片描述

但是这个指示器方块不是我们想要的,需要改一下

  1. 修改指示器类的绘图代码

在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);
}

再运行

在这里插入图片描述

落子

  1. 创建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();}}
}
  1. 创建落子类

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;}
}
  1. 在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 ;}}}
}
  1. 在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

  1. 创建AI类
  2. 创建静态方法next(下一步)
  3. 创建静态方法has5(连成5子)
public class AI {//AI进行下一步static void next(GamePanel gamePanel){}//判断五子连珠static boolean has5(Pointer pointer1,GamePanel gamePanel){return false;}
}
  1. 在你落子后,会先执行has5方法,根据返回来决定走向
  2. 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 ;}}}
}
  1. 在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();}}
}
  1. AI的next方法调用随机落子
//AI进行下一步static void next(GamePanel gamePanel){luoziRandom(gamePanel);}

运行效果:

在这里插入图片描述

  1. 小红方块一直在,修改代码
    仅需在落子前将其他小红方块清除即可
//清除电脑棋子的最后一个棋子指示器
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子右开
得分323031
类型4子左开4子4子右开
得分424041
类型5子左开5子5子右开
得分525051

通过上述表可以看到,落子权重分顺序:5>4>3,同时:左>右>中。

计算横向权重分

  1. 从左往右判断

* 横向下标 i 是一样的,循环从当前位置 j 加1开始。
* 当碰到和当前子一样的就计数器 +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吧,望理解!

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。

代码获取方式
订阅我的专栏《Java游戏大全》后,可以查看专栏内所有的文章,并且联系博主免费获取其中1-3份你心仪的源代码,专栏的文章都是上过csdn热榜的,值得信赖!专栏内目前有[13]篇实例,未来2个月内专栏会更新到15篇以上,一般2周一更,了解一下我的专栏

热门专栏推荐

【1】Java小游戏(俄罗斯方块、飞机大战、植物大战僵尸等)
【2】JavaWeb项目实战(图书管理、在线考试、宿舍管理等)
【3】JavaScript精彩实例(飞机大战、贪吃蛇、验证码等)
【4】Java小白入门200例
【5】从零学Java、趣学Java
【6】Idea从零到精通

更多精彩

1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. Java五子棋人机版
5. Java植物大战僵尸
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战


http://www.ppmy.cn/news/563039.html

相关文章

文科生从0学Python转数据分析学习建议避坑指南

我本科是财务管理&#xff0c;文科专业&#xff0c;零基础学习Python转行数分后&#xff0c;现在我的日常工作都离不开它。 接下来&#xff0c;给各位跟我一样无编程经验的朋友一些学习的建议 目标导向&#xff1a;先搞清楚为啥要学 Python几乎可以做任何事&#xff0c;但我…

数据库是如何工作的

数据库是如何工作的 注&#xff1a; 本文翻译自db_tutorial. 数据库计算机世界的一个基础软件&#xff0c;要想深入了解数据库&#xff0c;就不得不思考如下几个问题&#xff1a; 数据以什么格式保存&#xff1f;&#xff08;在内存和磁盘上&#xff09;它何时从内存移动到磁…

日立医疗影像诊断业务正式加入富士胶片集团

富士胶片株式会社宣布&#xff0c;于2021年3月31日完成对株式会社日立制作所&#xff08;以下简称“日立”&#xff09;旗下影像诊断相关业务的收购程序&#xff0c;标志着日立为继承相关业务而成立的新公司“富士胶片医疗健康”正式成为富士胶片集团全资子公司&#xff0c;全新…

云胶片(云影像)功能简介

1 首先&#xff0c;云胶片不能是这样的&#xff0c;这个只是换瓶不换药。这种图只是影像科老师在全序列图像中&#xff0c;选取的部分影像&#xff0c;患者没有拿到自己全部数据&#xff1b;另外&#xff0c;通过N-Print&#xff08;胶片打印标准&#xff09;打印的图像都是通过…

美通企业日报 | 全球金融科技50强榜中企居首;施乐退出与富士胶片合资企业...

今日看点 11月4日&#xff0c;KPMG&#xff08;毕马威&#xff09;和金融科技投资公司H2 Ventures联合发布了2019年全球金融科技100强榜单 (Fintech100)。入选 Fintech100的公司&#xff0c;既有全球首屈一指的50家金融科技公司&#xff0c;也包括引人注目的50家“明日之星”&a…

那些年,我了解过的医疗器械(偏影像方向)公司 (2017.03.24 Updating...)

本文由Markdown语法编辑器编辑完成。 背景&#xff1a; 不知不觉&#xff0c;在医疗软件行业工作已经三年&#xff08;2013.07至今&#xff09;有余了。如果算上研究生期间攻读生物医学工程硕士的那三年&#xff08;2010.092013.07&#xff09;&#xff0c;我在医疗器械行业工…

富士胶片基于AI研发新技术 支持新冠病毒肺炎诊断及疗效评估

富士胶片株式会社&#xff08;社长&#xff1a;助野健儿&#xff09;宣布正在进行一项研究&#xff0c;将基于人工智能&#xff08;AI&#xff09;技术为新型冠状病毒肺炎患者的影像诊断和治疗评估提供支持。富士胶片与京都大学&#xff08;医学研究生院呼吸医学系 平井丰博教授…

胶片巨头今何在:富士胶片如何“起死回春”

OFweek光学网讯&#xff1a;富士和柯达&#xff0c;同为胶片时代的巨子&#xff0c;但曾经无比强大的柯达如今彻底退出了历史舞台&#xff0c;而富士却及时壮士断臂&#xff0c;敢于自毁长城涅槃重生。在强有力的领袖带领下&#xff0c;富士不走捷径&#xff0c;踏踏实实地投入…