canvas简易人机五子棋

原创 2016年08月31日 11:14:24

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


其实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种开局分别是:
寒星 溪月 残月 雨月 金星 丘月 新月 
山月 游星 长星 峡月 恒星 水月 流星
浦月 岚月 银月 明星 名月 彗星 花月 
松月 疏星 斜月 瑞星 云月 

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

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


版权声明:本文为博主原创文章,未经博主允许不得转载。

C语言简单五子棋两人/五子棋人机

对于初学C语言的同学,五子棋不可谓不是一个好的练习。我们不但要考虑玩家及电脑的落子,还要考虑棋盘是否已满、是否有一方已经获得胜利。因此我们要考虑好各种情况,设定好函数及循环。         下面是两...
  • baidu_37964071
  • baidu_37964071
  • 2017年04月15日 21:04
  • 901

简易五子棋详细Java源代码(1)

package pkg2; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class Qi...
  • yongshi6
  • yongshi6
  • 2015年04月21日 17:10
  • 3632

五子棋人机对战的实现

最近我一直在编写五子棋的AI,但是搞了这么长时间,棋力一直停留在比较水的阶段,难以有什么比较好的突破。不知道贴吧里有没有别的朋友研究过类似的问题,于是想到来贴吧讨论一下。 我的程序主要遵循如下思路:...
  • u012706792
  • u012706792
  • 2014年11月21日 16:50
  • 8719

java五子棋人机对战算法分析

五子棋人机对战采用的算法,目前大都是搜索树算法,用一棵树来表示棋局发展的 种种可能性,这种树叫做博弈树(对局树)。根节点表示对局的开始状态,每一种可 能的走法造成的结果作为其子节点,而对每一个这样的子...
  • zzzypp
  • zzzypp
  • 2015年01月13日 15:59
  • 3972

人机版五子棋两种算法概述

人机版五子棋是很有挑战性的。至今好像没有保证可以取胜的算法,但已经有不少写的很专业的五子棋程序了。我在编写五子棋的过程中参考了不少资料,发现人机五子棋大致有两种策略。在这儿总结一下,与大家共享。先说两...
  • u011587401
  • u011587401
  • 2016年03月13日 14:46
  • 2719

五子棋人机对弈代码——之贪心算法

/*五子棋游戏的游戏模块的实现 zwdnet 2010年3月 zwdnet@163.com */ #include #include using namespace ...
  • handsomesunshineboy
  • handsomesunshineboy
  • 2010年12月26日 21:29
  • 6441

android开发五子棋人机对战

昨天我们讲了双人对战的算法,今天我们来说说人机对战,因为代码比较多,比较复杂,我在这边大致说一下算法问题。当然,有不懂的地方可以提出来,我会一一解答。因为下午还有事情要做,我这边就不画图了。 我就用...
  • sw950729
  • sw950729
  • 2016年07月19日 13:23
  • 2151

百行内实现五子棋人机对战

国庆没事,想看看最少多少行可以写一个人机对战起来游戏,于是有了这个Python版五子棋人机对战,仅仅几百行。 #! /usr/bin/env python # -*- coding: u...
  • skywind
  • skywind
  • 2012年11月09日 03:51
  • 12131

五子棋人机对弈代码——java版

算法是穷举递归法,只不过用java重新写了一遍import java.awt.Color; import java.awt.Container; import java.awt.Graphics;...
  • handsomesunshineboy
  • handsomesunshineboy
  • 2011年03月23日 19:02
  • 7349

java swing实现的人机对战五子棋

这个小程序是我1年多前自己写着玩的,ai
  • lgl1170860350
  • lgl1170860350
  • 2014年04月08日 16:22
  • 3113
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:canvas简易人机五子棋
举报原因:
原因补充:

(最多只允许输入30个字)