一个简单的五子棋程序,实现了人机对战、人人对战、悔棋、认输功能。
一、首先是制作界面,也就是下棋界面。要画一个棋盘,添加按钮实现各种功能。
(1)创建一个ChessTable类来存放棋盘的基础数据,比如横向线的条数、纵向线的条数、单元格的大小、棋子的直径等。定义一个类是为了更改棋盘基础数据的时候更加方便。
public interface ChessTable {
public int x0 = 50; //表格左上角起点的x值
public int y0 = 70; //表格左上角起点的y值
public int rows = 11; //横向线的条数
public int columns = 11; //纵向线的条数
public int chess_size = 30; //棋子的直径
public int size = 40; //单元格的大小
}
(2)接下来是下棋界面的设计,考虑到后面要实现悔棋和改变界面大小,就要用到重绘。所以在创建类的时候直接继承JFrame,然后在类里面重写paint()方法来实现重绘。
JPanel是一个容器组件,可以把按钮添加到JPanel中。然后指定JPanel存放的位置。
通过ImageIcon可以在界面中添加背景图片,增加观赏性。
public class Gobang extends JFrame {
public static void main(String[] args) {
Gobang gb = new Gobang();
gb.showJFrame();
}
//初始化窗体的方法
public void showJFrame() {
this.setTitle("五子棋");
this.setSize(650, 550);
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
this.setResizable(false);//界面不可改变大小
this.setLayout(new BorderLayout());
//添加容器组件
JPanel jl = new JPanel();
jl.setPreferredSize(new Dimension(150,0));
jl.setBackground(Color.LIGHT_GRAY);
JButton start = new JButton("开始新游戏");
start.setPreferredSize(new Dimension(100,30));
jl.add(start);
JButton back = new JButton("悔棋");
back.setPreferredSize(new Dimension(80,30));
jl.add(back);
JButton giveup = new JButton("认输");
giveup.setPreferredSize(new Dimension(80,30));
jl.add(giveup);
String[] type = {"人人对战","人机对战"};
JComboBox box = new JComboBox<>(type);
box.setPreferredSize(new Dimension(90,30));
jl.add(box);
this.add(jl,BorderLayout.EAST);
this.setVisible(true);
}
//重写绘制界面的方法
public void paint(Graphics g) {
//在界面加上图片
ImageIcon image = new ImageIcon("C:\\Users\\某某某\\Desktop\\文档\\五子棋背景图\\1.jpg");
g.drawImage(image.getImage(),50,70,400,400,null);
}
//画棋盘
public void drawChessTable(Graphics g) {
//画横线
for(int i = 0;i < ChessTable.rows;i++) {
g.drawLine(ChessTable.x0, ChessTable.y0+i*ChessTable.size,
ChessTable.x0+(ChessTable.columns - 1)*ChessTable.size, ChessTable.y0+i*ChessTable.size);
}
//画竖线
for(int j = 0;j < ChessTable.rows;j++) {
g.drawLine(ChessTable.x0+j*ChessTable.size, ChessTable.y0,
ChessTable.x0+j*ChessTable.size, ChessTable.y0+(ChessTable.rows - 1)*ChessTable.size);
}
}
}
效果图如下:
(3)添加监听器,收集按钮上的信息。
收集按钮上的信息通过addActionListener()监听方法。
要实现下棋,就要获取点击位置的坐标,需要addMouseListener()监听方法。
添加监听方法就要有事件处理类,创建GobangListener类作为事件处理类。
继承MouseAdapter鼠标适配类,MouseAdapter类里继承了MouseListener, MouseWheelListener, MouseMotionListener。继承MouseAdapter是因为MouseAdapter只需要重写用到的方法,不需要重写所有的抽象方法。
同时,还要继承ActionListener。
//实例化事件处理类的对象,并把画笔、数组传递过去
GobangListener gl = new GobangListener(this,chesses);
//添加动作监听方法
start.addActionListener(gl);
back.addActionListener(gl);
giveup.addActionListener(gl);
box.addActionListener(gl);
public class GobangListener extends MouseAdapter implements ActionListener {
//重写抽象方法
}
(4)定义一个数组来存放棋子信息,当你在该位置下棋后,就不能再下棋了。
将窗体传递到GobangListener类,可以在窗体中获取画笔,也可以添加MouseListener监听方法。
public class Gobang extends JFrame {
//定义一个二维数组,用来标记棋盘上的位置
private int[][] chesses = new int[ChessTable.rows][ChessTable.columns];
}
public class GobangListener extends MouseAdapter implements ActionListener {
private Gobang ct;
private Graphics2D g;
private int[][] chesses;
// 构造方法
public GobangListener(Gobang G, int[][] chesses) {
this.ct = G;
this.chesses = chesses;
// 获取窗体上的画笔对象
g = (Graphics2D) this.ct.getGraphics();
//画笔防锯齿
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 设置画笔抗锯齿
}
}
(5)实现下棋、悔棋、和重绘。
用count来计数,判断轮到谁下棋。
通过chesses[i][j]来判断该位置是否能下棋。
悔棋要通过数组队列来实现,定义一个数组队列ArrayList。Point是一个类,可以直接用,可以获取x,y坐标值。
重绘则根据chesses数组中的信息再画一次。
调用判断输赢方法judge(),决出胜利后,移除MouseListener()不能再下棋。
//重写绘制窗体的方法
public void paint(Graphics g) {
super.paint(g);
ImageIcon image = new ImageIcon("C:\\Users\\庞志贤\\Desktop\\文档\\五子棋背景图\\1.jpg");
g.drawImage(image.getImage(),50,70,400,400,null);
drawChessTable(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);// 设置画笔抗锯齿
//按照数组中的信息进行重绘
for(int j = 0;j < ChessTable.rows;j++) {
for(int i = 0;i < ChessTable.columns;i++) {
int x = ChessTable.x0 + i*ChessTable.size;
int y = ChessTable.y0 + j*ChessTable.size;
if(chesses[i][j] == 1) {
g.setColor(Color.black);
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2,
ChessTable.chess_size, ChessTable.chess_size);
}else if(chesses[i][j] == -1) {
g.setColor(Color.WHITE);
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2,
ChessTable.chess_size, ChessTable.chess_size);
}
}
}
}
public class GobangListener extends MouseAdapter implements ActionListener {
private Gobang ct;
private Graphics2D g;
private int[][] chesses;
private int count;
private ArrayList list = new ArrayList();
public void mouseReleased(MouseEvent e) {
// 获取鼠标事件发生时光标的位置
int x1 = e.getX();
int y1 = e.getY();
for (int j = 0; j < ChessTable.rows; j++) {
for (int i = 0; i < ChessTable.columns; i++) {
int x = ChessTable.x0 + i * ChessTable.size;
int y = ChessTable.y0 + j * ChessTable.size;
if (x1 > x - ChessTable.size / 3 && x1 < x + ChessTable.size / 3 && y1 > y - ChessTable.size / 3
&& y1 < y + ChessTable.size / 3) {
// 如果选择的位置没有棋子
if (chesses[i][j] == 0) {
if (count == 0) {
// 如果是黑子就为1
chesses[i][j] = 1;
g.setColor(Color.black);
count++;
// 调用判断输赢的方法
if (judge(i, j)) {
System.out.println("执黑子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
}
} else {
// 如果是白子就为-1
chesses[i][j] = -1;
g.setColor(Color.WHITE);
count--;
// 调用判断输赢的方法
if (judge(i, j)) {
System.out.println("执白子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
}
}
// 以最近的交叉点为圆心画圆
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2, ChessTable.chess_size,
ChessTable.chess_size);
Point point = new Point(x, y);
list.add(point);// 根据图形的数据实例化Array对象
return;
}
}
}
}
}
}
(6)判断输赢。
下完一颗棋子后就要判断一次。一颗棋子的周围是否有五颗连续的同色棋子。分四个方向,水平、竖直、左斜、右斜。
public boolean judge(int x, int y) {
int count = 1;
// 向右
for (int i = x + 1; i < chesses.length; i++) {
if (chesses[i][y] == chesses[x][y]) {
count++;
} else
break;
}
// 向左
for (int i = x - 1; i >= 0; i--) {
if (chesses[i][y] == chesses[x][y]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
// 向上
for (int j = y + 1; j < chesses.length; j++) {
if (chesses[x][j] == chesses[x][y]) {
count++;
} else
break;
}
// 向下
for (int j = y - 1; j >= 0; j--) {
if (chesses[x][j] == chesses[x][y]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
// 右下
for (int i = x + 1, j = y + 1; i < chesses.length && j < chesses.length; i++, j++) {
if (chesses[i][j] == chesses[x][y]) {
count++;
} else
break;
}
// 左上
for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
if (chesses[i][j] == chesses[x][y]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
// 右上
for (int i = x + 1, j = y - 1; i < chesses.length && j >= 0; i++, j--) {
if (chesses[i][j] == chesses[x][y]) {
count++;
} else
break;
}
// 左下
for (int i = x - 1, j = y + 1; i >= 0 && j < chesses.length; i--, j++) {
if (chesses[i][j] == chesses[x][y]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
return false;
}
二、五子棋的AI算法
AI算法:通过计算的方法选择最优的位置自动下棋。
(1)考虑棋盘上可能出现出现的各种情况。选择一个空位来计算权值,最后选择权值最大的位置来下棋。空位周围棋子的存在情况,可以分为活连、眠连。
// 定义HashMap的集合对象,用来存储棋子相连的情况
private static HashMap<String, Integer> map = new HashMap<String, Integer>();
// 活连、眠连,可能出现的各种情况
static {
map.put("010", 10);// 活一连(黑)
map.put("0-10", 10);// 活一连(白)
map.put("01010", 70);// 活二连(黑)
map.put("0110", 70);// 活二连(黑)
map.put("0-10-10", 70);// 活二连(白)
map.put("0-1-10", 70);// 活二连(白)
map.put("01110", 120);// 活三连(黑)
map.put("0-1-1-10", 120);// 活三连(白)
map.put("011110", 5000);// 活四连(黑)
map.put("0-1-1-1-10", 5000);// 活四连(白)
map.put("1-1", 1);// 眠一连(黑)
map.put("-11", 1);// 眠一连(白)
map.put("011-1", 50);// 眠二连(黑)
map.put("-1110", 50);// 眠二连(黑)
map.put("0-1-11", 50);// 眠二连(白)
map.put("1-1-10", 50);// 眠二连(白)
map.put("0111-1", 100);// 眠三连(黑)
map.put("-11110", 100);// 眠三连(黑)
map.put("0-1-1-11", 100);// 眠三连(白)
map.put("1-1-1-10", 100);// 眠三连(白)
map.put("01111-1", 2500);// 眠四连(黑)
map.put("-111110", 2500);// 眠四连(黑)
map.put("1-1-1-1-10", 2500);// 眠四连(白)
map.put("0-1-1-1-11", 2500);// 眠四连(白)
}
(2)计算权值就要先判断各个方向的活连、眠连情况,然后根据权重来计算权值。
// 定义权值数组,用来存储权值
private int[][] weightArray = new int[ChessTable.rows][ChessTable.columns];
// 权值计算方法
public void weight() {
// 统计存储棋子的chesses数组,根据棋子数组中相连的情况来计算权值存入到weightArray数组中
// 统计四个方向,水平(向左、向右)、竖直(向上、向下)、左斜(左下、右上)、右斜(左上、右下)
for (int r = 0; r < chesses.length; r++) {
for (int c = 0; c < chesses.length; c++) {
if (chesses[r][c] == 0) {// 判断当前点是否为空位(没有棋子)
String code = "0"; // 记录棋子相连的情况
int chess = 0; // 记录棋子出现的次数
int number = 0; // 记录空位出现的次数
// 向左
for (int c1 = c - 1; c1 >= 0; c1--) {
if (chesses[r][c1] == 0) {// 判断当前交叉点的左边是否为空位
if (c == c1 + 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r][c1] == chesses[r][c1 + 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r][c1] == chesses[r][c1 + 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的左边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r][c1]; // 存储第一次出现的棋子
code = code + chesses[r][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r][c1]; // 记录棋子相连的情况
break;
}
}
}
// 根据code的数据,作为map的key,从map中获取对应的value
Integer value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 向右
for (int c1 = c + 1; c1 < chesses.length - 1; c1++) {
if (chesses[r][c1] == 0) {// 判断当前交叉点的右边是否为空位
if (c == c1 - 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r][c1] == chesses[r][c1 - 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r][c1] == chesses[r][c1 - 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的右边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r][c1]; // 存储第一次出现的棋子
code = code + chesses[r][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r][c1]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 向上
for (int r1 = r - 1; r1 >= 0; r1--) {
if (chesses[r1][c] == 0) {// 判断当前交叉点的上边是否为空位
if (r == r1 + 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c] == chesses[r1 + 1][c]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c] == chesses[r1 + 1][c]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的上边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c]; // 存储第一次出现的棋子
code = code + chesses[r1][c]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 向下
for (int r1 = r + 1; r1 < chesses.length - 1; r1++) {
if (chesses[r1][c] == 0) {// 判断当前交叉点的下边是否为空位
if (r == r1 - 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c] == chesses[r1 - 1][c]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c] == chesses[r1 - 1][c]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的下边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c]; // 存储第一次出现的棋子
code = code + chesses[r1][c]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 左下
for (int r1 = r - 1, c1 = c + 1; r1 >= 0 && c1 < chesses.length - 1; r1--, c1++) {
if (chesses[r1][c1] == 0) {// 判断当前交叉点的左下边是否为空位
if (r == r1 + 1 && c == c1 - 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c1] == chesses[r1 + 1][c1 - 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c1] == chesses[r1 + 1][c1 - 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的左下边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c1]; // 存储第一次出现的棋子
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c1]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 右上
for (int r1 = r + 1, c1 = c - 1; r1 < chesses.length - 1 && c1 >= 0; r1++, c1--) {
if (chesses[r1][c1] == 0) {// 判断当前交叉点的右上边是否为空位
if (r == r1 - 1 && c == c1 + 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c1] == chesses[r1 - 1][c1 + 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c1] == chesses[r1 - 1][c1 + 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的右上边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c1]; // 存储第一次出现的棋子
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c1]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 左上
for (int r1 = r - 1, c1 = c - 1; r1 >= 0 && c1 >= 0; r1--, c1--) {
if (chesses[r1][c1] == 0) {// 判断当前交叉点的左上边是否为空位
if (r == r1 + 1 && c == c1 + 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c1] == chesses[r1 + 1][c1 + 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c1] == chesses[r1 + 1][c1 + 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的左上边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c1]; // 存储第一次出现的棋子
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c1]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
// 右下
for (int r1 = r + 1, c1 = c + 1; r1 < chesses.length - 1 && c1 < chesses.length - 1; r1++, c1++) {
if (chesses[r1][c1] == 0) {// 判断当前交叉点的右下边是否为空位
if (r == r1 - 1 && c == c1 - 1) {// 判断是否为两个连续的空位,如果是则停止
break;
} else if (number == 0) {// 表示第一次出现空位
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 1) {// 表示第二次出现空位
if (chesses[r1][c1] == chesses[r1 - 1][c1 - 1]) {// 检测两个位置是否都为空位
break;
}
code = code + chesses[r1][c1]; // 记录棋子相连的情况
number++; // 空位出现次数增加1
} else if (number == 2) { // 表示第三次出现空位
if (chesses[r1][c1] == chesses[r1 - 1][c1 - 1]) { // 检测两个位置是否都为空位
break;
}
}
} else {// 当前交叉点的右下边是棋子
if (chess == 0) { // 表示第一次出现棋子
chess = chesses[r1][c1]; // 存储第一次出现的棋子
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else if (chess == chesses[r1][c1]) { // 判断是否和第一次出现的棋子颜色相同
code = code + chesses[r1][c1]; // 记录棋子相连的情况
} else { // 表示此处的棋子和第一次出现的颜色不一样
code = code + chesses[r1][c1]; // 记录棋子相连的情况
break;
}
}
}
System.out.println(code);
// 根据code的数据,作为map的key,从map中获取对应的value
value = map.get(code);
if (value != null) {// 判断value是否不为null
weightArray[r][c] += value;
}
} // 第一个if循环
} // 第二个for循环
} // 第一个for循环
}// 方法
(3)点击开始新游戏,添加鼠标监听方法。然后选择对战模式。
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("开始新游戏")) {
// 添加鼠标监听方法
ct.addMouseListener(this);
// 开始新游戏需要把棋盘数组重置
for (int j = 0; j < ChessTable.rows; j++) {
for (int i = 0; i < ChessTable.columns; i++) {
chesses[i][j] = 0;
}
}
ct.repaint();// 重绘棋盘
// 黑棋先下
count = 0;
} else if (e.getActionCommand().equals("悔棋")) {
if (list.size() > 0) {
Point point = list.remove(list.size() - 1);
int i = (point.x - ChessTable.x0) / ChessTable.size;
int j = (point.y - ChessTable.y0) / ChessTable.size;
chesses[i][j] = 0;
}
if (count == 1) {
count--;
} else {
count++;
}
// 重绘
ct.repaint();
} else if (e.getActionCommand().equals("认输")) {
if (list.size() > 0) {
if (count == 0) {
System.out.println("执白子获胜,执黑子认输");
} else {
System.out.println("执黑子获胜,执白子认输");
}
ct.removeMouseListener(this);
}
} else if (e.getSource() instanceof JComboBox) {
// instanceof关键字主要功能是判断这个事件源是不是JComboBox的
// 取到对象
JComboBox comboBox = (JComboBox) e.getSource();
// 取到下拉菜单被选择的内容
name = comboBox.getSelectedItem().toString();
System.out.println("选择了对战模式" + " " + name);
}
}
(4)如果是人机对战。要实现电脑自动画棋子的方法。判断出哪个位置为最大的权值,然后画棋子。
public void pve_draw() {
this.weight();
int max = 0;
int xIndex = 0, yIndex = 0;
int p = 0, q = 0;
for (int i = 0; i < weightArray.length - 1; i++) {
for (int j = 0; j < weightArray.length - 1; j++) {
if (weightArray[i][j] > max) { // 找到第一个最大的权值
max = weightArray[i][j];
p = i;
q = j;
yIndex = i + 1;
xIndex = j + 1;
}
}
}
System.out.println(max + " " + "row:" + yIndex + " " + "column:" + xIndex + " " + p + " " + q);
// 找出与权值最大值相等的数据
int r = 0, c = 0;
for (int i = 0; i < weightArray.length - 1; i++) {
for (int j = 0; j < weightArray.length - 1; j++) {
if (weightArray[i][j] == max) { // 找到第一个最大的
max = weightArray[i][j];
r = i;
c = j;
yIndex = i + 1;
xIndex = j + 1;
}
}
}
if (r != p && c != q) {
System.out.println(max + " " + "row:" + yIndex + " " + "column:" + xIndex + " " + r + " " + c);
}
// 画棋子
if (chesses[p][q] == 0) {
int x = ChessTable.x0 + p * ChessTable.size;
int y = ChessTable.y0 + q * ChessTable.size;
g.setColor(Color.WHITE);
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2, ChessTable.chess_size,
ChessTable.chess_size);
Point point = new Point(x, y);
list.add(point);
chesses[p][q] = -1;
weightArray[p][q] = 0; // 用完这个点要清空
// 判断输赢
if (judge(p, q)) {
System.out.println("电脑执白子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
}
}
}
(5)如果是人人对战模式,则是轮流下棋。
public void pvp(int x1, int y1) {
for (int j = 0; j < ChessTable.rows; j++) {
for (int i = 0; i < ChessTable.columns; i++) {
int x = ChessTable.x0 + i * ChessTable.size;
int y = ChessTable.y0 + j * ChessTable.size;
if (x1 > x - ChessTable.size / 3 && x1 < x + ChessTable.size / 3 && y1 > y - ChessTable.size / 3
&& y1 < y + ChessTable.size / 3) {
// 如果选择的位置没有棋子
if (chesses[i][j] == 0) {
if (count == 0) {
// 如果是黑子就为1
chesses[i][j] = 1;
g.setColor(Color.black);
count++;
// 调用判断输赢的方法
if (judge(i, j)) {
System.out.println("执黑子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
}
} else {
// 如果是白子就为-1
chesses[i][j] = -1;
g.setColor(Color.WHITE);
count--;
// 调用判断输赢的方法
if (judge(i, j)) {
System.out.println("执白子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
}
}
// 以最近的交叉点为圆心画圆
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2, ChessTable.chess_size,
ChessTable.chess_size);
Point point = new Point(x, y);
list.add(point);// 根据图形的数据实例化Array对象
return;
}
}
}
}
}
(6)最后则是判断选择了哪种游戏方式。
public void mouseReleased(MouseEvent e) {
// 获取鼠标事件发生时光标的位置
int x1 = e.getX();
int y1 = e.getY();
// 人机大战
if (name.equals("人机对战")) {
for (int j = 0; j < ChessTable.rows; j++) {
for (int i = 0; i < ChessTable.columns; i++) {
int x = ChessTable.x0 + i * ChessTable.size;
int y = ChessTable.y0 + j * ChessTable.size;
if (x1 > x - ChessTable.size / 3 && x1 < x + ChessTable.size / 3 && y1 > y - ChessTable.size / 3
&& y1 < y + ChessTable.size / 3) {
if (chesses[i][j] == 0) {
chesses[i][j] = 1;
g.setColor(Color.black);
// 以最近的交叉点为圆心画圆
g.fillOval(x - ChessTable.chess_size / 2, y - ChessTable.chess_size / 2,
ChessTable.chess_size, ChessTable.chess_size);
// 调用判断输赢的方法
if (judge(i, j)) {
System.out.println("执黑子胜利");
// 判断输赢后移除鼠标监听方法,不能再下棋子
ct.removeMouseListener(this);
//判断输赢后,弹出一个窗口
// JOptionPane.showMessageDialog(null, "黑棋赢");
return;
}
Point point = new Point(x, y);
list.add(point);// 根据图形的数据实例化Array对象
this.pve_draw();
System.out.println(x1 + " " + y1);
return;
}
}
}
}
} else if (name.equals("人人对战")) {
System.out.println(x1 + " " + y1);
this.pvp(x1, y1);
}
}