canvas简易人机五子棋

中学时看过一本关于围棋的漫画《棋魂》,奈何天赋有限,围棋至今也不会……好吧,退而求其次,五子棋相对简单一点。对着网上的教程实现了一个简单的五子棋:


其实ui的实现并不难,主要记录下ai的思路吧。

// 绘制棋盘
        for (var i = 0; i < 15; i++) {
            context.beginPath();
            context.moveTo(15 + i * 30, 15);
            context.lineTo(15 + i * 30, 435);
            context.stroke();
            context.beginPath();
            context.moveTo(15, 15 + i * 30);
            context.lineTo(435, 15 + i * 30);
            context.stroke();
        };
五子棋的棋盘为15*15,落子黑先白后,落子的过程其实就是在绘制旗子。
// 绘制棋子
        function oneStep(x, y, color) {
            // x,y为棋子在棋盘的坐标索引,color为黑棋或白棋
            context.beginPath();
            context.arc(15 + x * 30, 15 + y * 30, 12, 0, 2 * Math.PI);
            context.closePath();
            var gradient = context.createRadialGradient(15 + x * 30 + 2, 15 + y * 30 - 2, 12, 15 + x * 30 + 2, 15 + y * 30 - 2, 0);
            if (color) {
                gradient.addColorStop(0, '#0a0a0a');
                gradient.addColorStop(1, '#636766');
            } else {
                gradient.addColorStop(0, '#d1d1d1');
                gradient.addColorStop(1, '#f9f9f9');
            };
            context.fillStyle = gradient;
            context.fill();
        }

需要一个二维数组记录当前棋盘的落子情况,每次落子需要判断胜负以及是否结束。

var me = true;
var chessBoard = []; //创建一个二维数组用于记录当前棋盘的落子情况
var wins = []; //三维数组记录五子棋所有的赢法
var count = 0; //记录五子棋所有赢法的索引
var over = false;

//落子事件
        chess.onclick = function(e) {
            var x = e.offsetX;
            var y = e.offsetY;
            var i = Math.floor(x / 30);
            var j = Math.floor(y / 30);
            if (chessBoard[i][j] == 0 && !over) { //没有落子的位置才能落子,黑子为1,白子为2
                oneStep(i, j, me);
                if (me) {
                    chessBoard[i][j] = 1;
                } else {
                    chessBoard[i][j] = 2;
                };
                console.log(chessBoard);

                winner(i, j, me);
                me = !me;
                if (!over) {
                    computerAI(me);
                };
            };
        }
先说判断胜负,其实无论哪方,五子连珠作为胜利的条件,在15*15的棋盘上胜利的所有情况是可以枚举出来的。
        for (var i = 0; i < 15; i++) { //横向统计所有的赢法
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[i][j + k][count] = true;
                };
                count++;
            };
        };
        for (var i = 0; i < 15; i++) { //纵向统计所有的赢法
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[j + k][i][count] = true;
                };
                count++;
            };
        };
        for (var i = 0; i < 11; i++) { //斜向统计所有的赢法
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[i + k][j + k][count] = true;
                };
                count++;
            };
        };
        for (var i = 0; i < 11; i++) { //斜向统计所有的赢法
            for (var j = 14; j > 3; j--) {
                for (var k = 0; k < 5; k++) {
                    wins[i + k][j - k][count] = true;
                };
                count++;
            };
        };
        console.log(count);

关键在于wins这个三维数组,有点难理解,举个例子:假如五子棋只有一种赢法:


那么count的值为1,但五子棋可不能一次就落五个子(这还怎么玩?)所以,每一种赢法要包含五个落子的坐标,也就是说,这五个落子的位置,无论落子先后,只要达成五子连珠,此种赢法就实现了。
        var black = [];
        var white = [];
        //分别记录五子棋黑白的赢法数组
这两个数组结合wins数组来判断胜负,五子棋共有572种赢法,默认黑子与白子的赢法都为0。
        for (var i = 0; i < count; i++) { //开局默认黑白所有的赢法都是0
            black[i] = 0;
            white[i] = 0;
        };

还是用上面的例子,如果黑方在第一种赢法处落下一子,那么黑子的第一种赢法+1,同时白子此种赢法作废。

//判断输赢
        function winner(i, j, color) {
            for (var k = 0; k < count; k++) {
                if (wins[i][j][k]) {
                    if (color) {
                        black[k]++;
                        white[k] = 6;
                        //如果某种赢法黑子已经落子,白子此种赢法就作废
                    } else {
                        white[k]++;
                        black[k] = 6;
                    };
                    if (black[k] == 5) {
                        alert('黑子获胜');
                        over = true;
                    };
                    if (white[k] == 5) {
                        alert('白子获胜');
                        over = true;
                    };
                };
            };
        }
代码到这里,已经能实现五子棋的规则逻辑了,接下来实现ai。

var blackScore = [];
var whiteScore = [];
//分别记录五子棋黑白的二维得分数组
这个ai其实挺简单的,实现思路就是通过遍历每一个能落子的空坐标,然后结合算法找出分数最高的一个位置落子。
        //AI
        function computerAI(color) {
            var max = 0;
            var u = 0;
            var v = 0;
            // 保存最大的分数和相应坐标

            for (var i = 0; i < 15; i++) {
                blackScore[i] = [];
                whiteScore[i] = [];
                for (var j = 0; j < 15; j++) {
                    blackScore[i][j] = 0;
                    whiteScore[i][j] = 0;
                };
            };
            //每个坐标的分数为零

            //遍历每个空坐标,如果某种赢法已经落子的数量越大则该坐标加分越多
            //同理拦截对方的落子
            //加分的数值很重要
            for (var i = 0; i < 15; i++) {
                for (var j = 0; j < 15; j++) {
                    if (chessBoard[i][j] == 0) {
                        for (var k = 0; k < count; k++) {
                            if (wins[i][j][k]) {
                                switch (black[k]) {
                                    case 1:
                                        blackScore[i][j] += 2;
                                        break;
                                    case 2:
                                        blackScore[i][j] += 5;
                                        break;
                                    case 3:
                                        blackScore[i][j] += 20;
                                        break;
                                    case 4:
                                        blackScore[i][j] += 50;
                                        break;
                                }
                                switch (white[k]) {
                                    case 1:
                                        whiteScore[i][j] += 2;
                                        break;
                                    case 2:
                                        whiteScore[i][j] += 5;
                                        break;
                                    case 3:
                                        whiteScore[i][j] += 20;
                                        break;
                                    case 4:
                                        whiteScore[i][j] += 50;
                                        break;
                                }

                                //找出得分最高的坐标点
                                if (blackScore[i][j] > max) {
                                    max = blackScore[i][j];
                                    u = i;
                                    v = j;
                                } else if (blackScore[i][j] == max) {
                                    if (whiteScore[i][j] > whiteScore[u][v]) {
                                        u = i;
                                        v = j;
                                    }
                                }
                                if (whiteScore[i][j] > max) {
                                    max = whiteScore[i][j];
                                    u = i;
                                    v = j;
                                } else if (whiteScore[i][j] == max) {
                                    if (blackScore[i][j] > blackScore[u][v]) {
                                        u = i;
                                        v = j;
                                    }
                                }

                            };
                        };
                    };
                };
            };

            oneStep(u, v, color);
            color ? chessBoard[u][v] = 1 : chessBoard[u][v] = 2;
            winner(u, v, color);
            me = !color;

        }

五子棋的棋盘上,每一个位置都存在多种赢法,此算法的逻辑就是假如一个空坐标还未落子,那么遍历所有的赢法,如果黑方在此种赢法已经落下一子,那么这个坐标对黑方有利,加分;如果黑方落下二子,那么分数更高;白方也是同理……找出最有价值的坐标落子。

至于如何加分,可以借鉴网上的评分表:


代码只是实现了思路,没有优化;而且此类“”民间规则“”五子棋都是先手必胜,

在五子棋专业规则中规定,一共有26种开局。直指开局13种,斜指开局13种。
这26种开局分别是:
寒星 溪月 残月 雨月 金星 丘月 新月 
山月 游星 长星 峡月 恒星 水月 流星
浦月 岚月 银月 明星 名月 彗星 花月 
松月 疏星 斜月 瑞星 云月 

其中公认黑必胜的开局有:花月,浦月
黑必败开局有:彗星,游星
以上之适用于五子棋专业规则
在民间规则里,几乎全部是黑先手必胜。

专业的五子棋比赛还有禁手、三手交换、五手两打等限制,以后有机会再研究吧……


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值