在线五子棋对战 --- 人机对战的实现

1. 人机对战

要增添一个人机对战的模块, 最大的难点就是如何让人机知道下在什么位置是最好的, 不仅要具备进攻的能力, 还需要具备防守的能力.

这里当人机第一次走的时候, 采用标准开局, 下子在最中间.
当玩家走了之后, 人机就需要去判定下在什么位置合理.

这里采用的是评分表的方法来计算落子在每一个位置的分数, 根据最高分数来进行下子.

1.1 演示

在这里插入图片描述

1.2 评分表

分析棋形的几种情况.
例如, 自己是黑子.
"_" 代表 没有子, "1" 代表 黑子 , "0" 代表 白子

当落子只有一颗子的情况
_ 1 _
_ 1 0
0 1 _
0 1 0

当落子有两颗子的情况
_ 1 1 _
_ 1 1 0
0 1 1 _
0 1 1 0

当落子有三颗子的情况
_ 1 1 1 _
_ 1 1 1 0
0 1 1 1 _
0 1 1 1 0

当落子有四颗子的情况
_ 1 1 1 1 _
_ 1 1 1 1 0
0 1 1 1 1 _
0 1 1 1 1 0

当落子有五颗子的情况
_ 1 1 1 1 1 _
_ 1 1 1 1 1 0
0 1 1 1 1 1 _
0 1 1 1 1 1 0

这里大概的情况分为这几种, 分别对这几种情况进行一个分数的设定, 让机器人根据分数的高低优先去处理某种情况.

这里设计一种分数表

一子情况

在这里插入图片描述

二子情况

在这里插入图片描述

三子情况

在这里插入图片描述

四子情况

在这里插入图片描述

五子情况

在这里插入图片描述

1.3 算法思路

使用暴力搜索的方法, 将棋盘中每个空格的子, 当下子的时候, 去判定下子之后, 横向得分情况, 竖向得分情况, 左斜得分情况, 右斜的得分情况.

例如, 下子是黑子.
 
横向, 去看左边有多少个黑子, 多少白子, 去看右边有多少个黑子, 多少白子. 然后根据评分表, 算得分数.
 
纵向, 去看上方有多少黑子, 多少白子, 再去下方看有多少个黑子, 多少白子, 然后根据评分表, 算得分数.
 
左斜, 去左下方看有多少黑子, 多少白子, 再去右上方看有多少黑子, 多少白子, 然后根据评分表, 算得分数.
 
右斜. 去左上方看有多少黑子,多少白子, 再去右下方看有多少黑子, 多少白子, 然后根据评分表, 算得分数.

将四个方向的得分加起来, 算得分数, 进行下子.

这里这种算法, 只具备了进攻, 不具备防守的能力. 要想人机下棋具备防守的能力, 还需要让人机对玩家落子的分数进行评估, 如果玩家落子得分更高, 就需要考虑去防守了.

这里就让, 对每个位置, 机器人落子, 和玩家落子, 算得机器人落子的得分, 和玩家落子的得分, 对分数进行相加, 然后再去比较, 当前是最大的得分情况, 就去当前位置落子.

这里进行测试的时候, 发现出现四子的时候, 会出现不进攻 或者不防守的情况. 所以在判定的时候, 如果出现了五子连珠的情况, 首先进攻. 如果对方有四子, 自己没法五子, 首先防守.

1.4 具体代码

1.4.1 评分表方法

根据评分表来分配分数, my表示我下的棋子, his表示他下的棋子

public int score(int my,int his){
    
    
        if(my > 5) return 200000;
        if(my == 5 && his == 0) return 200000;
        if(my == 5 && his == 1) return 200000;
        if(my == 5 && his == 2) return 200000;
        if(my == 4 && his == 1) return 3000;
        if(my == 4 && his == 0) return 50000;
        if(my == 4 && his == 2) return 1000;
        if(my == 3 && his == 0) return 3000;
        if(my == 3 && his == 1) return 1000;
        if(my == 3 && his == 2) return 500;
        if(my == 2 && his == 0) return 500;
        if(my == 2 && his == 1) return 200;
        if(my == 2 && his == 2) return 100;
        if(my == 1 && his == 0) return 100;
        if(my == 1 && his == 1) return 50;
        if(my == 1 && his == 2) return 30;
        return 0;
    }

1.4.2 横向得分方法

算得横向自己棋子数, 和他的棋子数
如果遇到空格就不计算了, 遇到别人的棋子也不计算了

public int getXScore(int x,int y, int chess){
    
    
        int my = 1;
        int his = 0;
        for(int i = x-1; i >= 0; i--){
    
    
            if(chess == board[i][y]){
    
    
                my++;
            }else if(board[i][y] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        for(int i = x+1; i<board.length; i++) {
    
    
            if(chess == board[i][y]){
    
    
                my++;
            }else if(board[i][y] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        return score(my,his);
    }

1.4.3 纵向得分方法

算得纵向自己棋子数, 和他的棋子数
如果遇到空格就不计算了, 遇到别人的棋子也不计算了

    private int getYScore(int x, int y, int chess) {
    
    
        int my = 1;
        int his = 0;
        for(int i = y-1; i >= 0; i--){
    
    
            if(chess == board[x][i]){
    
    
                my++;
            }else if(board[x][i] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        for(int i = y+1; i < board.length; i++){
    
    
            if(chess == board[x][i]){
    
    
                my++;
            }else if(board[x][i] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        return score(my,his);
    }

1.4.4 左斜得分方法

算得左斜向自己棋子数, 和他的棋子数
如果遇到空格就不计算了, 遇到别人的棋子也不计算了

    private int getSkewScore2(int x, int y, int chess) {
    
    
        int my = 1;
        int his = 0;
        for(int i = x+1,j=y-1; i<board.length && j >=0; i++,j--){
    
    
            if(chess == board[i][j]){
    
    
                my++;
            }else if(board[i][j] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        for(int i = x-1,j=y+1; i>=0 && j<board.length; i--,j++){
    
    
            if(chess == board[i][j]){
    
    
                my++;
            }else if(board[i][j] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        return score(my,his);
    }

1.4.5 右斜得分方法

算得右斜向自己棋子数, 和他的棋子数
如果遇到空格就不计算了, 遇到别人的棋子也不计算了

 private int getSkewScore1(int x, int y, int chess) {
    
    
        int my = 1;
        int his = 0;
        for(int i = x-1,j =y-1; i >=0 && j>=0; i--,j--){
    
    
            if(chess == board[i][j]){
    
    
                my++;
            }else if(board[i][j] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        for(int i = x+1,j=y+1; j<board.length && i < board.length; i++,j++){
    
    
            if(chess == board[i][j]){
    
    
                my++;
            }else if(board[i][j] == 0){
    
    
                break;
            }else{
    
    
                his++;
                break;
            }
        }
        return score(my,his);
    }

1.4.6 落子总得分方法

这里如果自己下的子可以优先五子连珠就直接下棋.
如果没有这种情况, 再去判定是否他可以五子连珠, 如果有直接堵住
其他就计算总分数

    public int getScore(int x,int y) {
    
    
        int numX1 = getXScore(x,y,1);
        int numX2 = getXScore(x,y,2);
        int numY1 = getYScore(x,y,1);
        int numY2 = getYScore(x,y,2);
        int skew1 = getSkewScore1(x,y,1);
        int skew2 = getSkewScore1(x,y,2);
        int skew3 = getSkewScore2(x,y,1);
        int skew4 = getSkewScore2(x,y,2);
        if(numX2 >= 200000 || numY2 >= 200000 || skew2 >= 200000 || skew4 >= 200000) {
    
    
            return Integer.MAX_VALUE;
        }
        if(numX1 >= 200000 || numY1 >= 200000 || skew1 >= 200000 || skew3 >= 200000){
    
    
            return Integer.MAX_VALUE;
        }
        int xScore = getXScore(x,y,1)+getXScore(x,y,2);
        int yScore = getYScore(x,y,1)+getYScore(x,y,2);
        int skewScore1 = getSkewScore1(x,y,1)+getSkewScore1(x,y,2);
        int skewScore2 = getSkewScore2(x,y,1)+getSkewScore2(x,y,2);
        return xScore + yScore + skewScore1 + skewScore2;
    }

1.4.7 确认落子位置的方法

 public int[] concluate() {
    
    
        int[] res = new int[2];
        int max = 0;
        for(int i = 0; i < Constant.ROW; i++) {
    
    
            for(int j = 0; j < Constant.COL; j++) {
    
    
                if(board[i][j] != 0) {
    
    
                    continue;
                }
                int num = getScore(i,j);
                if(num == 200000){
    
    
                    res[0] = i;
                    res[1] = j;
                    return res;
                }
                if(num > max) {
    
    
                    max = num;
                    res[0] = i;
                    res[1] = j;
                }
            }
        }
        return res;
    }

猜你喜欢

转载自blog.csdn.net/wwzzzzzzzzzzzzz/article/details/127061995