欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈

程序员文章站 2022-07-08 12:03:50
前言     本文主要介绍利用开源引擎lufylegend.js开发基于Html5的游戏--五子棋,主要叙述其详细开发过程。 游戏规则   &n...
前言

    本文主要介绍利用开源引擎lufylegend.js开发基于Html5的游戏--五子棋,主要叙述其详细开发过程。

游戏规则

    玩过五子棋的都应该知道五子棋的规则,这里就简单介绍其规则。

 

    1、传统五子棋的棋具与围棋大致相同,棋子分为黑白两色,棋盘为15×15,棋子放置于棋盘线交叉点上。两人对局,各执一色,轮流下一子,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。    

 

    2、由于传统五子棋具有不公平性,而现代五子棋禁手规则令一部分棋手望而怯步。于是产生了职业制传统五子棋,职业制传统五子棋虽然准备麻烦,但胜在简单公平,而且难度增加,久而习之,思维活跃。

 

规则如下:

1、准备19×19棋盘两张。

2、黑白子数目必须满足。

3、第一回合先手只能下一手,其余回合可以下连续两手。

4、后手每回合均可以下连续两手。

5、每颗子所投的棋盘没有限制。

6、只要任意一方在两个棋盘上且同一个回合上连为五子为胜。

7、若任意一方在两个棋盘上且不同一个回合上连为五子为负。

8、若任意一方在不足两个棋盘上且同一个回合上连为五子为负。

综合效应

“禁手”思维+“交换”思维+“井字游戏”原理=连珠

如:RIF规则、Sakata规则、Yamaguchi规则Tarannikov规则等。

为了方面起见,这里只考虑传统打法,也是大多数人喜欢的打法。

用到的术语

    活五:任意方向的相同颜色棋子连成5个棋子

    活四:任意方向的相同颜色棋子连成4个棋子,且两边都没有其他棋子

    冲四:任意方向的相同颜色棋子连成4个棋子,且一边没有其他棋子

    活三:任意方向的相同颜色棋子连成3个棋子,且两边都没有其他棋子

    死三:任意方向的相同颜色棋子连成3个棋子,且一边没有其他棋子

    活二:任意方向的相同颜色棋子连成2个棋子,且两边都没有其他棋子

    死二:任意方向的相同颜色棋子连成2个棋子,且一边没有其他棋子

    单一:任意方向的相同颜色棋子连成1个棋子,且两边都没有其他棋子

开发思路

     好了,废话不多说,接下来就介绍一下开发思路,首先要明确,当玩家每下一个棋子的时候,如何能教会电脑下棋,也就是如何让电脑知道往哪个地方下子。这里就需要让电脑扫描整个棋盘,分析整个棋型,通过计算得出哪个地方最有可能赢,或者最有可能阻止玩家赢。那么,就需要给每个棋型拟定一个分数,每次玩家下子的时候,就扫描棋盘,给定棋盘每个位置空子(即没有棋子的位置)的分值,然后就下分值最高的那个点。

     比如:如果这个地方如果电脑下子,可能构成活四,那么分数+5000,如果玩家可能构成活四,分数+2000。

详细过程

    我表达不是很好,说的很笼统,直接贴上代码吧。

    初始化棋盘

      

[javascript] 

//背景层、格子层、棋子层  

var backLayer,cellLayer,chessLayer;  

var BOARD_SIZE = 15;//棋盘格子数量(15*15);  

var OFFSET = 40;//格子偏移量  

var CELL_WIDTH = 40;//行宽  

var CENTER = 8;  

var array = new Array();//记录每个格子是否有棋子0表示空位1表示己方棋子2表示地方棋子  

var isPlay = true;//是否该玩家下棋  

var C_STEP = 0,P_STEP = 0;//AI和玩家走的棋数  

  

function main(){  

    backLayer = new LSprite();  

    backLayer.graphics.drawRect(1,"darkorange",[0,0,LGlobal.width,LGlobal.height],true,"darkorange");  

    addChild(backLayer);  

    var textFiled;  

    //画棋盘周围的数字和字母  

    for(var i = 0;i<BOARD_SIZE;i++){  

        textField = new LTextField();  

        textField.color = "black";  

        textField.x = OFFSET >> 2;  

        textField.y = OFFSET+(CELL_WIDTH*(i));  

        textField.font="Arial";  

        textField.size = 8;  

        textField.text = BOARD_SIZE - i;  

        backLayer.addChild(textField);  

        textField = new LTextField();  

        textField.color = "black";  

        textField.x = OFFSET+(CELL_WIDTH*i);  

        textField.y = ((OFFSET*3) >> 2) + OFFSET + CELL_WIDTH * (BOARD_SIZE-1);  

        textField.font = "Arial";  

        textField.size = 8;  

        textField.text = String.fromCharCode(65+i);  

        backLayer.addChild(textField);  

    }  

    //AI初始化  

    AI.init();  

      

    //画出棋盘  

    drawBoard();  

      

    chessLayer = new LSprite();  

    backLayer.addChild(chessLayer);  

      

    //按钮  

    //var resetButton = new LButtonSample1("重玩");  

    //resetButton.x = 10;  

    //resetButton.y = 500;  

    //backLayer.addChild(resetButton);  

    //resetButton.addEventListener(LMouseEvent.MOUSE_UP,reset);  

      

    backLayer.addEventListener(LMouseEvent.MOUSE_MOVE,onmove);  

    backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,ondown);  

      

};  

  

//function reset(){  

      

//};  

  

//画棋盘  

function drawBoard(){  

    //画竖线条  

    for(var i = 0;i < BOARD_SIZE;i++){  

        backLayer.graphics.drawLine(2,"#000000",[i*CELL_WIDTH+OFFSET,OFFSET,i*CELL_WIDTH+OFFSET,(BOARD_SIZE-1)*CELL_WIDTH+OFFSET]);  

    }  

    //画横线条  

    for(var i = 0;i < BOARD_SIZE;i++){  

        backLayer.graphics.drawLine(2,"#000000",[OFFSET,i*CELL_WIDTH+OFFSET,(BOARD_SIZE-1)*CELL_WIDTH+OFFSET,i*CELL_WIDTH+OFFSET]);  

    }     

      

    //画棋盘上的小圆点  

    drawStar(CENTER,CENTER);  

    drawStar((BOARD_SIZE + 1) >> 2,(BOARD_SIZE + 1) >> 2);  

    drawStar((BOARD_SIZE + 1) >> 2,((BOARD_SIZE + 1) * 3) >> 2);  

    drawStar(((BOARD_SIZE + 1)*3) >> 2,(BOARD_SIZE + 1) >> 2);  

    drawStar(((BOARD_SIZE + 1)*3) >> 2,((BOARD_SIZE + 1) * 3) >> 2);  

};  

  

function drawStar(cx,cy){  

    var x = (cx - 1)*CELL_WIDTH+OFFSET;  

    var y = (cy - 1) * CELL_WIDTH+OFFSET;  

    backLayer.graphics.drawArc(0,"#000000",[x,y,4,0,Math.PI * 2],true,"#000000");  

};  

[javascript]  

//在棋盘指定位置画出黑白棋子  

function drawChess(cx,cy,color){  

    //棋子欲放入格子的中心坐标  

    var x = cx * CELL_WIDTH + OFFSET;  

    var y = cy * CELL_WIDTH + OFFSET;  

    var R = CELL_WIDTH >> 1;//棋子半径,为格子宽度/2  

    chessLayer.graphics.drawArc(0,color,[x,y,R,0,Math.PI * 2],true,color);  

    isPlay = false;  

};  

//画出鼠标点击后棋子欲落下的区域  

function drawCell(cx,cy){  

    if(cx >= 0 && cx < BOARD_SIZE && cy >= 0 && cy < BOARD_SIZE){  

        if(cellLayer){  

            cellLayer.die();  

            cellLayer.removeAllChild();  

            backLayer.removeChild(cellLayer);  

            cellLayer = null;  

        }  

        cellLayer = new LSprite();  

        backLayer.addChild(cellLayer);  

        var length = CELL_WIDTH >> 1;  

        cx = cx * CELL_WIDTH + OFFSET;  

        cy = cy * CELL_WIDTH + OFFSET;  

        cellLayer.graphics.drawLine(2,"red",[cx-length,cy - length,cx-length/2,cy-length]);  

        cellLayer.graphics.drawLine(2,"red",[cx-length,cy - length,cx-length,cy-length/2]);  

        cellLayer.graphics.drawLine(2,"red",[cx+length,cy - length,cx+length/2,cy-length]);  

        cellLayer.graphics.drawLine(2,"red",[cx+length,cy - length,cx+length,cy-length/2]);  

        cellLayer.graphics.drawLine(2,"red",[cx+length,cy + length,cx+length,cy+length/2]);  

        cellLayer.graphics.drawLine(2,"red",[cx+length,cy + length,cx+length/2,cy+length]);  

        cellLayer.graphics.drawLine(2,"red",[cx-length,cy + length,cx-length/2,cy+length]);  

        cellLayer.graphics.drawLine(2,"red",[cx-length,cy + length,cx-length,cy+length/2]);  

    }  

};  

 

由于五子棋的核心算法就是AI部分,所以这部分初始化棋盘的代码大可不必深究,接下来就是AI算法。直接贴代码吧

[javascript]  

var AI = AI || {};  

//代表每个方向  

AI.direction = {  

    TOP:1,  

    BOTTOM:2,  

    LEFT:3,  

    RIGHT:4,  

    LEFT_TOP:5,  

    LEFT_BOTTOM:6,  

    RIGHT_TOP:7,  

    RIGHT_BOTTOM:8  

};  

//初始化  

AI.init = function(){  

    //初始化数组为0  

    for(var i = 0;i < BOARD_SIZE;i++){  

        array[i] = new Array();  

        for(var j = 0;j < BOARD_SIZE;j++){  

            array[i][j] = 0;  

        }  

    }  

};  

//AI棋型分析  

AI.analysis = function(x,y){  

    //如果为第一步则,在玩家棋周围一格随机下棋,保证每一局棋第一步都不一样  

    if(P_STEP == 1){  

        return this.getFirstPoint(x,y);  

    }  

    var maxX = new Array(),  

        maxY = new Array(),  

        maxWeight = 0,  

        max = new Array(),  

        min = new Array(),  

        i, j, tem;  

        for (i = BOARD_SIZE - 1; i >= 0; i--) {  

            for (j = BOARD_SIZE; j >= 0; j--) {  

                if (array[i][j] !== 0) {  

                    continue;  

                }  

                tem = this.computerWeight(i, j,2);  

                if (tem > maxWeight) {  

                    maxWeight = tem;  

                    maxX = i;  

                    maxY = j;  

                }  

            }  

        }  

    return new Point(maxX,maxY);  

};  

//下子到i,j X方向 结果: 多少连子 两边是否截断  

AI.putDirectX = function (i, j, chessColor) {  

        var m, n,  

            nums = 1,  

            side1 = false,//两边是否被截断  

            side2 = false;  

        for (m = j - 1; m >= 0; m--) {  

            if (array[i][m] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[i][m] === 0) {  

                    side1 = true;//如果为空子,则没有截断  

                }  

                break;  

            }  

        }  

        for (m = j + 1; m < BOARD_SIZE; m++) {  

            if (array[i][m] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[i][m] === 0) {  

                    side2 = true;  

                }  

                break;  

            }  

        }  

        return {"nums": nums, "side1": side1, "side2": side2};  

    },  

    //下子到i,j Y方向 结果  

AI.putDirectY = function (i, j, chessColor) {  

        var m, n,  

            nums = 1,  

            side1 = false,  

            side2 = false;  

        for (m = i - 1; m >= 0; m--) {  

            if (array[m][j] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][j] === 0) {  

                    side1 = true;  

                }  

                break;  

            }  

        }  

        for (m = i + 1; m < BOARD_SIZE; m++) {  

            if (array[m][j] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][j] === 0) {  

                    side2 = true;  

                }  

                break;  

            }  

        }  

        return {"nums": nums, "side1": side1, "side2": side2};  

    },  

    //下子到i,j XY方向 结果  

AI.putDirectXY = function (i, j, chessColor) {  

        var m, n,  

            nums = 1,  

            side1 = false,  

            side2 = false;  

        for (m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {  

            if (array[m][n] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][n] === 0) {  

                    side1 = true;  

                }  

                break;  

            }  

        }  

        for (m = i + 1, n = j + 1; m < BOARD_SIZE && n < BOARD_SIZE; m++, n++) {  

            if (array[m][n] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][n] === 0) {  

                    side2 = true;  

                }  

                break;  

            }  

        }  

        return {"nums": nums, "side1": side1, "side2": side2};  

    },  

AI.putDirectYX = function (i, j, chessColor) {  

        var m, n,  

            nums = 1,  

            side1 = false,  

            side2 = false;  

        for (m = i - 1, n = j + 1; m >= 0 && n < BOARD_SIZE; m--, n++) {  

            if (array[m][n] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][n] === 0) {  

                    side1 = true;  

                }  

                break;  

            }  

        }  

        for (m = i + 1, n = j - 1; m < BOARD_SIZE && n >= 0; m++, n--) {  

            if (array[m][n] === chessColor) {  

                nums++;  

            }  

            else {  

                if (array[m][n] === 0) {  

                    side2 = true;  

                }  

                break;  

            }  

        }  

        return {"nums": nums, "side1": side1, "side2": side2};  

    },  

//计算AI下棋权重  

AI.computerWeight = function(i,j,chessColor){  

    var weight = (BOARD_SIZE - 1) - (Math.abs(i - BOARD_SIZE >> 1) + Math.abs(j - BOARD_SIZE >> 1)), //基于棋盘位置权重(越靠近棋盘中心权重越大)  

    pointInfo = {}, //某点下子后连子信息  

    //x方向  

    pointInfo = this.putDirectX(i, j, chessColor);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重  

    pointInfo = this.putDirectX(i, j, chessColor-1);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重  

    //y方向  

    pointInfo = this.putDirectY(i, j, chessColor);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重  

    pointInfo = this.putDirectY(i, j, chessColor-1);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重  

    //左斜方向  

    pointInfo = this.putDirectXY(i, j, chessColor);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重  

    pointInfo = this.putDirectXY(i, j, chessColor-1);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重  

    //右斜方向  

    pointInfo = this.putDirectYX(i, j, chessColor);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true);//AI下子权重  

    pointInfo = this.putDirectYX(i, j, chessColor-1);  

    weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false);//player下子权重  

    return weight;  

};  

//权重方案   活:两边为空可下子,死:一边为空  

AI.weightStatus = function (nums, side1, side2, isAI) {  

        var weight = 0;  

        switch (nums) {  

            case 1:  

                if (side1 && side2) {  

                    weight = isAI ? 15 : 10;    //一  

                }  

                break;  

            case 2:  

                if (side1 && side2) {  

                    weight = isAI ? 100 : 50;   //活二  

                }  

                else if (side1 || side2) {  

                    weight = isAI ? 10 : 5; //死二  

                }  

                break;  

            case 3:  

                if (side1 && side2) {  

                    weight = isAI ? 500 : 200;  //活三  

                }  

                else if (side1 || side2) {  

                    weight = isAI ? 30 : 20;    //死三  

                }  

                break;  

            case 4:  

                if (side1 && side2) {  

                    weight = isAI ? 5000 : 2000;    //活四  

                }  

                else if (side1 || side2) {  

                    weight = isAI ? 400 : 100;  //死四  

                }  

                break;  

            case 5:  

                weight = isAI ? 100000 : 10000; //五  

                break;  

            default:  

                weight = isAI ? 500000 : 250000;  

                break;  

        }  

        return weight;  

    };  

//判断是否胜出,胜返回true否则返回false  

//思路:从下子的地方为中心朝4个方向判断,若连成五子,遇空子或敌方棋子则改变方向则胜出  

//不用全盘遍历,因为只有下子的地方才会有胜出的可能  

//flag标识AI还是玩家1为玩家2为AI  

AI.isAIWin = function(x,y,flag){  

    var count1 = 0;  

    var count2 = 0;  

    var count3 = 0;  

    var count4 = 0;  

    //左右判断  

    for(var i = x;i >= 0;i--){  

        if(array[i][y]!=flag){  

            break;  

        }  

        count1++;  

    }  

    for(var i = x+1;i<BOARD_SIZE;i++){  

        if(array[i][y] != flag){  

            break;  

        }  

        count1++;  

    }  

    //上下判断  

    for(var i = y;i>=0;i--){  

        if(array[x][i] != flag){  

            break;  

        }  

        count2++;  

    }  

    for(var i = y+1;i<BOARD_SIZE;i++){  

        if(array[x][i] != flag){  

            break;  

        }  

        count2++;  

    }  

    //左上右下判断  

    for(var i = x,j=y;i>=0&&j>=0;i--,j--){  

        if(array[i][j] != flag){  

            break;  

        }  

        count3++;  

    }  

    for(var i = x+1,j=y+1;i<BOARD_SIZE&&j<BOARD_SIZE;i++,j++){  

        if(array[i][j] != flag){  

            break;  

        }  

        count3++;  

    }  

    //右上左下判断  

    for(var i =x,j=y;i>=0&&j<BOARD_SIZE;i--,j++){  

        if(array[i][j] != flag){  

            break;  

        }  

        count4++;  

    }  

    for(var i =x+1,j=y-1;i<BOARD_SIZE&&j>=0;i++,j--){  

        if(array[i][j] != flag){  

            break;  

        }  

        count4++;  

    }  

    var win = 0;//AI是否赢了  

    if(count1>=5||count2>=5||count3>=5||count4>=5){  

        win = flag;  

    }  

    return win;  

};  

//AI第一步棋  

//参数x,y为玩家第一步棋的坐标  

AI.getFirstPoint = function(x,y){  

    var point = new Point(x,y);  

    if(x < 3 || x > BOARD_SIZE - 3 || y < 3 || y > BOARD_SIZE - 3){  

        point.x = BOARD_SIZE >> 1;  

        point.y = BOARD_SIZE >> 1;  

    }else{  

        var direction = random({  

            min:1,  

            max:8  

        });  

        switch(direction){  

        case this.direction.TOP:  

            point.y = y - 1;  

            break;  

        case this.direction.BOTTOM:  

            point.y = y + 1;  

            break;  

        case this.direction.LEFT:  

            point.x = x - 1;  

            break;  

        case this.direction.RIGHT:  

            point.x = x + 1;  

            break;  

        case this.direction.LEFT_TOP:  

            point.x = x - 1;  

            point.y = y - 1;  

            break;  

        case this.direction.LEFT_BOTTOM:  

            point.x = x - 1;  

            point.y = y + 1;  

            break;  

        case this.direction.RIGHT_TOP:  

            point.x = x + 1;  

            point.y = y - 1;  

            break;  

        case this.direction.RIGHT_BOTTOM:  

            point.x = x + 1;  

            point.y = y + 1;  

            break;  

            default:  

                point.x = x - 1;  

                point.y = y - 1;  

                break;  

        }  

    }  

    return point;  

};  

 

[javascript]  

function Point(x,y){  

    var self = this;  

    self.x = x;  

    self.y = y;  

};  

最后的效果如下:

利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈

 

好了,整个五子棋就开发完成了,有任何疑问,我们可以交流交流