五子棋与AI

来源:互联网 发布:美国大学排名 知乎 编辑:程序博客网 时间:2024/05/17 00:55

一.五子棋棋盘的实现

五子棋棋盘由窗体及添加在窗体上的组件组成。要实现五子棋棋盘只需实现一个窗体并在窗体上添加相应的组件。

1.实现一个窗体并在窗体上添加面板

2.在面板上添加菜单栏和按钮

前两个步骤的实现比较简单,此处不再添加代码。

3.在面板上画出棋盘的线条

因每次重绘都会使得线条消失,为解决这个问题需重写paint方法,在paint方法中实现线条的绘制。

public void paint(Graphics g) {super.paint(g);                /*绘制棋盘,line为线条数,size为格子大小,x0、y0为线条起始坐标,x、y为线条的终止坐标*/for (int i = 0; i < line; i++) {                       //绘制横向线条                        g.drawLine(x0, y0 + size * i, x, y0 + size * i);                       //绘制纵向线条g.drawLine(x0 + size * i, y0, x0 + size * i, y);}}


二.棋子的实现

绘制棋子需要借助MouseListener来实现,当鼠标点击时则画出一个圆。此处需解决四个问题:

1.棋子黑白颜色的交替出现

2.当鼠标点击的位置不在棋盘线条交叉处时,如何确定棋子的坐标

3.已绘制棋子的地方不能再绘制棋子

4.判断输赢的问题

       //定义数组记录在哪个交叉点画下了哪一个颜色的棋子int[][] chess=new int[line][line];        JPanel jp;Graphics g;JFrame jf;// 定义参数记录鼠标按下的坐标private int x, y;// 定义xx和yy记录画下棋子的位置为第几个交叉点 private int xx = 0, yy = 0;       // 定义数组按下棋子的先后顺序记录其坐标private int a[] = new int[100];private int b[] = new int[100];// 定义变量记录是否按下开始键private int start = 0;// 定义变量记录模式,1为人人模式,2为人机模式private int machine = 0;public void mouseClicked(MouseEvent e) {g = jp.getGraphics();x = e.getX();y = e.getY();// 若坐标超过格子的一半则画在下一个交叉点,否则画在上一个交叉点if ((x - x0) % size > size / 2) {xx = (x - x0) / size + 1;} else {xx = (x - x0) / size;}if ((y - y0) % size > size / 2) {yy = (y - y0) / size + 1;} else {yy = (y - y0) / size;}if (chess[xx][yy] == 0) {if (count % 2 == 0 && start != 0 && machine == 1) {g.setColor(Color.BLACK);g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;// 用数字1表示在该位置画下了黑棋chess[xx][yy] = 1;a[count] = xx;b[count] = yy;// 判断输赢并弹出提示窗体同时结束本局if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {black();start = 0;}} else if (count % 2 != 0 && start != 0 && machine == 1) {g.setColor(Color.WHITE);g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;// 用数字2表示在该位置画下了白棋chess[xx][yy] = 2;a[count] = xx;b[count] = yy;// 判断输赢并弹出提示窗体同时结束本局if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {white();start = 0;}}   }}

为解决重绘时棋子消失的问题需要在paint方法里遍历记录棋子的数组,然后重绘棋子

// 重写paint方法public void paint(Graphics g) {super.paint(g);for (int i = 0; i < line; i++) {g.drawLine(x0, y0 + size * i, x, y0 + size * i);g.drawLine(x0 + size * i, y0, x0 + size * i, y);}// 遍历chess数组for (int i = 0; i < chess.length; i++) {for (int j = 0; j < chess[i].length; j++) {if (chess[i][j] == 1) {g.setColor(Color.BLACK);g.fillOval(i * size + x0 - d / 2, j * size + y0 - d / 2, d, d);} else if (chess[i][j] == 2) {g.setColor(Color.WHITE);g.fillOval(i * size + x0 - d / 2, j * size + y0 - d / 2, d, d);}}}}}// 定义判断输赢的方法public int sp() {int count = 0; // 记录棋子相连个数// 向右比较for (int i = xx + 1; i < chess.length; i++) {if (chess[i][yy] == chess[xx][yy]) {count++;} else {break;}}// 向左比较for (int i = xx; i >= 0; i--) {if (chess[i][yy] == chess[xx][yy]) {count++;} else {break;}}return count;}public int ve() {int count = 0;// 向下比较for (int i = yy + 1; i < chess.length; i++) {if (chess[xx][i] == chess[xx][yy]) {count++;} else {break;}}// 向上比较for (int i = yy; i >= 0; i--) {if (chess[xx][i] == chess[xx][yy]) {count++;} else {break;}}return count;}public int nw() {int count = 0;// 向右下比较for (int i = yy + 1, j = xx + 1; i < chess.length && j < chess.length; i++, j++) {if (chess[j][i] == chess[xx][yy]) {count++;} else {break;}}// 向左上比较for (int i = yy, j = xx; i >= 0 && j >= 0; i--, j--) {if (chess[j][i] == chess[xx][yy]) {count++;} else {break;}}return count;}public int ne() {int count = 0;// 向左下比较for (int i = yy + 1, j = xx - 1; i < chess.length && j >= 0; i++, j--) {if (chess[j][i] == chess[xx][yy]) {count++;} else {break;}}// 向右上比较for (int i = yy, j = xx; i >= 0 && j < chess.length; i--, j++) {if (chess[j][i] == chess[xx][yy]) {count++;} else {break;}}return count;}// 定义白棋胜利的方法public void white() {JFrame frame = new JFrame();frame.setSize(300, 300);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(2);frame.setBackground(new Color(255, 183, 111));// 在窗体上添加北边面板JPanel panelnorth = new JPanel();panelnorth.setPreferredSize(new Dimension(100, 30));panelnorth.setBackground(new Color(255, 183, 111));frame.add(panelnorth, BorderLayout.NORTH);// 在窗体上添加西边面板JPanel panelwest = new JPanel();panelwest.setPreferredSize(new Dimension(100, 50));panelwest.setBackground(new Color(255, 183, 111));frame.add(panelwest, BorderLayout.WEST);// 添加中间面板JPanel panelcenter = new JPanel();panelcenter.setBackground(new Color(255, 183, 111));JLabel label = new JLabel("白子胜利!");panelcenter.add(label);panelcenter.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 100));frame.add(panelcenter, BorderLayout.CENTER);frame.setVisible(true);}// 定义黑棋胜利的方法public void black() {JFrame frame = new JFrame();frame.setSize(300, 300);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(2);frame.setBackground(new Color(255, 183, 111));// 在窗体上添加北边面板JPanel panelnorth = new JPanel();panelnorth.setPreferredSize(new Dimension(100, 30));panelnorth.setBackground(new Color(255, 183, 111));frame.add(panelnorth, BorderLayout.NORTH);// 在窗体上添加西边面板JPanel panelwest = new JPanel();panelwest.setPreferredSize(new Dimension(100, 50));panelwest.setBackground(new Color(255, 183, 111));frame.add(panelwest, BorderLayout.WEST);// 添加中间面板JPanel panelcenter = new JPanel();panelcenter.setBackground(new Color(255, 183, 111));JLabel label = new JLabel("黑子胜利!");panelcenter.add(label);panelcenter.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 100));frame.add(panelcenter, BorderLayout.CENTER);frame.setVisible(true);}}

四.按钮功能的实现

按钮功能的实现需要借助ActionListener
public void actionPerformed(ActionEvent e) {if (e.getActionCommand().equals("悔棋") && machine == 1) {chess[a[count]][b[count]] = 0;count--;jf.repaint();} else if (e.getActionCommand().equals("重新开始")) {for (int i = 0; i < chess.length; i++) {for (int j = 0; j < chess[i].length; j++) {chess[i][j] = 0;}}count = 0;jf.repaint();start = 1;} else if (e.getActionCommand().equals("悔棋") && machine == 2) {chess[a[count]][b[count]] = 0;count--;chess[a[count]][b[count]] = 0;count--;jf.repaint();} else if (e.getActionCommand().equals("认输")) {if (count % 2 == 0) {white();start = 0;} else if (count % 2 != 0) {black();start = 0;}} else if (e.getActionCommand().equals("开始游戏")) {for (int i = 0; i < chess.length; i++) {for (int j = 0; j < chess[i].length; j++) {chess[i][j] = 0;}}count = 0;jf.repaint();start = 1;} else if (e.getActionCommand().equals("人机模式")) {machine = 2;} else if (e.getActionCommand().equals("人人模式")) {machine = 1;}}

五.AI的实现

ai的实现需要定义一个哈希表记录权值,电脑在整个棋盘权值最大的地方下棋子
// 定义数组记录棋盘各个空位置的权值private int[][] chessValue = new int[15][15];// 定义哈希表记录权值HashMap<String, Integer> hm = new HashMap<String, Integer>();public GameListener(JPanel jp, JFrame jf) {this.jp = jp;this.jf = jf;// 为哈希表赋值hm.put("12", 30);hm.put("112", 300);hm.put("1112", 3000);hm.put("11112", 30000);hm.put("1", 40);hm.put("11", 400);hm.put("111", 4000);hm.put("1111", 40000);hm.put("2", 20);hm.put("22", 200);hm.put("222", 2000);hm.put("2222", 20000);hm.put("21", 10);hm.put("221", 100);hm.put("2221", 1000);hm.put("22221", 10000);}public void ai() {// 遍历整个棋盘计算空位置的权值for (int i = 0; i < chess.length; i++) {for (int j = 0; j < chess.length; j++) {// 定义变量记录棋子相连情况String code = "";// 定义变量记录右边第一个棋子的颜色int ch = 0;// 当前位置为空,记录当前位置四周棋子相连情况if (chess[i][j] == 0) {// 向右记录棋子相连情况for (int x = i + 1; x < chess.length; x++) {// 若第一个位置为空,跳出循环if (chess[x][j] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][j];// 记录第一颗棋子的相连情况ch = chess[x][j];// 记录第一颗棋子的颜色} else if (ch == chess[x][j]) {// 与上一颗棋子颜色相同code += chess[x][j];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][j];break;}}}// 根据code值从哈希表中提取权值Integer value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 向左记录棋子相连情况for (int x = i - 1; x >= 0; x--) {// 若第一个位置为空,跳出循环if (chess[x][j] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][j];// 记录第一颗棋子的相连情况ch = chess[x][j];// 记录第一颗棋子的颜色} else if (ch == chess[x][j]) {// 与上一颗棋子颜色相同code += chess[x][j];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][j];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 向下记录棋子相连情况for (int y = j + 1; y < chess[j].length; y++) {// 若第一个位置为空,跳出循环if (chess[i][y] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[i][y];// 记录第一颗棋子的相连情况ch = chess[i][y];// 记录第一颗棋子的颜色} else if (ch == chess[i][y]) {// 与上一颗棋子颜色相同code += chess[i][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[i][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 向上记录棋子相连情况for (int y = j - 1; y >= 0; y--) {// 若第一个位置为空,跳出循环if (chess[i][y] == 0) {break;} else {if (ch == 0) {// 右边第一颗棋子code += chess[i][y];// 记录右边第一颗棋子的相连情况ch = chess[i][y];// 记录右边第一颗棋子的颜色} else if (ch == chess[i][y]) {// 与上一颗棋子颜色相同code += chess[i][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[i][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 像右下记录棋子相连情况for (int x = i + 1, y = j + 1; x < chess[i].length && y < chess[j].length; x++, y++) {// 若第一个位置为空,跳出循环if (chess[x][y] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][y];// 记录第一颗棋子的相连情况ch = chess[x][y];// 记录第一颗棋子的颜色} else if (ch == chess[x][y]) {// 与上一颗棋子颜色相同code += chess[x][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 像左上记录棋子相连情况for (int x = i - 1, y = j - 1; x >= 0 && y >= 0; x--, y--) {// 若第一个位置为空,跳出循环if (chess[x][y] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][y];// 记录第一颗棋子的相连情况ch = chess[x][y];// 记录第一颗棋子的颜色} else if (ch == chess[x][y]) {// 与上一颗棋子颜色相同code += chess[x][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 像右上搜索for (int x = i + 1, y = j - 1; x < chess[i].length && y >= 0; x++, y--) {// 若第一个位置为空,跳出循环if (chess[x][y] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][y];// 记录第一颗棋子的相连情况ch = chess[x][y];// 记录第一颗棋子的颜色} else if (ch == chess[x][y]) {// 与上一颗棋子颜色相同code += chess[x][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}// 清空code和ch的值code = "";ch = 0;// 像左下搜索for (int x = i - 1, y = j + 1; x >= 0 && y < chess[j].length; x--, y++) {// 若第一个位置为空,跳出循环if (chess[x][y] == 0) {break;} else {if (ch == 0) {// 第一颗棋子code += chess[x][y];// 记录第一颗棋子的相连情况ch = chess[x][y];// 记录第一颗棋子的颜色} else if (ch == chess[x][y]) {// 与上一颗棋子颜色相同code += chess[x][y];} else {// 与上一颗棋子颜色不同,保存项链情况并跳出循环code += chess[x][y];break;}}}// 根据code值从哈希表中提取权值value = hm.get(code);if (value != null) {chessValue[i][j] += value;}}}}// 遍历chessValue数组取出最大值的位置int max = chessValue[0][0];for (int i = 0; i < chessValue.length; i++) {for (int j = 0; j < chessValue[i].length; j++) {if (chessValue[i][j] >= max) {max = chessValue[i][j];xx = i;yy = j;}}}}public void mouseClicked(MouseEvent e) {g = jp.getGraphics();x = e.getX();y = e.getY();// 若坐标超过格子的一半则画在下一个交叉点,否则画在上一个交叉点if ((x - x0) % size > size / 2) {xx = (x - x0) / size + 1;} else {xx = (x - x0) / size;}if ((y - y0) % size > size / 2) {yy = (y - y0) / size + 1;} else {yy = (y - y0) / size;}if (chess[xx][yy] == 0) {if (count % 2 == 0 && start != 0 && machine == 1) {g.setColor(Color.BLACK);g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;// 用数字1表示在该位置画下了黑棋chess[xx][yy] = 1;a[count] = xx;b[count] = yy;// 判断输赢并弹出提示窗体同时结束本局if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {black();start = 0;}} else if (count % 2 != 0 && start != 0 && machine == 1) {g.setColor(Color.WHITE);g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;// 用数字2表示在该位置画下了白棋chess[xx][yy] = 2;a[count] = xx;b[count] = yy;// 判断输赢并弹出提示窗体同时结束本局if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {white();start = 0;}} else if (count % 2 == 0 && start != 0 && machine == 2) {g.setColor(Color.BLACK);g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;// 用数字1表示在该位置画下了黑棋chess[xx][yy] = 1;a[count] = xx;b[count] = yy;// 判断输赢并弹出提示窗体同时结束本局if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {black();start = 0;}g.setColor(Color.WHITE);ai();g.fillOval(xx * size + x0 - d / 2, yy * size + y0 - d / 2, d, d);count++;chess[xx][yy] = 2;a[count] = xx;b[count] = yy;if (sp() == 5 || ve() == 5 || nw() == 5 || ne() == 5) {white();start = 0;}// 输出权值数组for (int i = 0; i < chessValue.length; i++) {for (int j = 0; j < chessValue[i].length; j++) {System.out.print(chessValue[i][j] + " ");}System.out.println();}System.out.println();// 清空chessValuefor (int i = 0; i < chessValue.length; i++) {for (int j = 0; j < chessValue[i].length; j++) {chessValue[i][j] = 0;}}}}}
原创粉丝点击