Java实现在线五子棋对战游戏(人机对战)

 更新时间:2022年09月27日 08:25:51   作者:独一无二的哈密瓜  
这篇文章主要为大家详细介绍了如何利用Java语言实现在线五子棋对战游戏(人机对战),文中的实现步骤讲解详细,感兴趣的可以尝试一下

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

到此这篇关于Java实现在线五子棋对战游戏(人机对战)的文章就介绍到这了,更多相关Java在线五子棋游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java实现图片水平和垂直翻转效果

    java实现图片水平和垂直翻转效果

    这篇文章主要为大家详细介绍了java实现图片水平和垂直翻转效果,图片旋转的灵活运用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • RabbitMQ消息拒绝如何解决

    RabbitMQ消息拒绝如何解决

    这篇文章主要介绍了RabbitMQ消息拒绝如何解决问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Java锁擦除与锁粗化概念和使用详解

    Java锁擦除与锁粗化概念和使用详解

    这篇文章主要介绍了Java锁擦除与锁粗化概念和使用,锁擦除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行
    2023-02-02
  • 浅析JAVA 循环结构

    浅析JAVA 循环结构

    这篇文章主要介绍了JAVA 循环结构的相关资料,文中讲解的非常细致,示例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • 面试题:java中为什么foreach中不允许对元素进行add和remove

    面试题:java中为什么foreach中不允许对元素进行add和remove

    读者遇到了一个比较经典的面试题,也就是标题上说的,为什么 foreach 中不允许对元素进行 add 和 remove,本文就详细的介绍一下,感兴趣的可以了解一下
    2021-10-10
  • 解决在微服务环境下远程调用feign和异步线程存在请求数据丢失问题

    解决在微服务环境下远程调用feign和异步线程存在请求数据丢失问题

    这篇文章主要介绍了解决在微服务环境下远程调用feign和异步线程存在请求数据丢失问题,主要包括无异步线程得情况下feign远程调用,异步情况下丢失上下文问题,需要的朋友可以参考下
    2022-05-05
  • SpringCloud中的Seata基本介绍与安装教程

    SpringCloud中的Seata基本介绍与安装教程

    Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务,这篇文章主要介绍了SpringCloud之Seata基本介绍与安装,需要的朋友可以参考下
    2024-01-01
  • 使用Spring Boot实现操作数据库的接口的过程

    使用Spring Boot实现操作数据库的接口的过程

    本文给大家分享使用Spring Boot实现操作数据库的接口的过程,包括springboot原理解析及实例代码详解,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • java System类和Arrays类详解

    java System类和Arrays类详解

    这篇文章主要介绍了java System类和Arrays类详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • Java 8新增的方法参数反射实例分析

    Java 8新增的方法参数反射实例分析

    这篇文章主要介绍了Java 8新增的方法参数反射,结合实例形式分析了java8新增api方法参数反射相关操作技巧,需要的朋友可以参考下
    2019-07-07

最新评论