zoukankan      html  css  js  c++  java
  • HTML5 五子棋

    背景介绍

      因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当然没参考之前的客户端代码,只用使用之前计算输赢判断算法和电脑AI(网络借取)的算法,当然现在html5做的五子棋百度一下非常多,但是自己实现一边总归是好事情,好了废话不多说了进入正题。^_^

    界面功能介绍与后续可增加功能

     目前界面功能:

      主界面包含

        1:人人、人机对战选项 2:棋子外观选择 3:棋盘背景选择 4:棋盘线条颜色选择

      游戏界面包含

        1:玩家名称 2:玩家棋子 3:当前由谁下棋背景定位 4:玩家比分 5:功能菜单区域(重新开始和无限悔棋) 6:棋盘区域 7.胜利后连环棋子连接 8.最后下棋位置闪烁显示 9.光标定位

      游戏结束界面

        1:胜利背景图 2:胜利玩家姓名 3:继续下一把按钮

     可增加功能

      1.返回主界面 2.保存棋局和相关数据 3.读取棋局和相关数据 4.交换角色 5.网络对战(2台机器)6.双方思考总时间记录

    演示地址

    http://sandbox.runjs.cn/show/pl3fyuy4   (注意:没有加棋子图片下载提示,如果使用仿真棋子,出现下棋为空,请等待棋子图片下载完毕)

    界面截图赏析(没花时间美化,比较丑)

     相关设置选择    

      

    整体设计和主代码介绍

    整体设计

      下棋流程:玩家or电脑AI下棋 ---> 绘制棋子 ---> 设定棋子二维坐标值 ----> logic(逻辑判断) ----> (玩家)一方五子连环 ---> 获胜界面
          ↑                                          |
          |                                          ↓
          <--------------------------------------------------------------------------------------------没有五子

     

      悔棋流程(人人对战):一方玩家悔棋 ----> 弹出下棋记录堆栈并设定为它是最后一枚棋 ---> 清除最后一枚棋子图像 ---> 清除棋子二维坐标值---> 重新定位显示最后下棋位置并闪烁

      悔棋流程(人机对战):玩方悔棋 ----> 弹出下棋记录堆栈2次,设定上一次电脑为最后一枚棋 ---> 清除弹出的2次记录图像 ---> 清除棋子2个棋子二维坐标值---> 重新定位显示最后下棋位置并闪烁

    主代码介绍

       主代码分为二块: 1.界面逻辑块  2.游戏主体块 (界面与游戏代码分离,逻辑清晰,分工明确)

    模拟事件通知:游戏主体逻辑块,每次结果都会通知到界面层来进行交互(类似于c#或者java的委托或事件)

    界面逻辑代码

      1 <script type="text/javascript">
      2         var gb = null;
      3         var infoboj = document.getElementsByClassName("info")[0];
      4         var pl1obj = document.getElementById("pl1");
      5         var pl2obj = document.getElementById("pl2");
      6         var plname1obj = document.getElementById("plname1");
      7         var plname2obj = document.getElementById("plname2");
      8         var chesstypeobj = document.getElementsByName("chesstype");
      9         var chesscolorobj = document.getElementsByName("chesscolor");
     10         var chessbgObj = document.getElementsByName("chessbg");
     11         var winerpnl = document.getElementById("winer");
     12         document.getElementById("startgame").addEventListener("click", function() {
     13             
     14             function initParams() {
     15                 var chessTypeValue = 1;
     16                 if (chesstypeobj.length > 0) {
     17                     for (var i = 0; i < chesstypeobj.length; i++) {
     18                         if (chesstypeobj[i].checked) {
     19                             chessTypeValue = chesstypeobj[i].value;
     20                             break;
     21                         }
     22                     }
     23                 }
     24                 var linevalue = "";
     25                 if (chesscolorobj.length > 0) {
     26                     for (var i = 0; i < chesscolorobj.length; i++) {
     27                         if (chesscolorobj[i].checked) {
     28                             linevalue = chesscolorobj[i].value;
     29                             break;
     30                         }
     31                     }
     32                 }
     33                 var bcorimgvalue = "";
     34                 if (chessbgObj.length > 0) {
     35                     for (var i = 0; i < chessbgObj.length; i++) {
     36                         if (chessbgObj[i].checked) {
     37                             bcorimgvalue = chessbgObj[i].value;
     38                             break;
     39                         }
     40                     }
     41                 }
     42                 return {
     43                     lineColor: linevalue,
     44                     chessType: chessTypeValue, //1 色彩棋子 2 仿真棋子
     45                     playAName: plname1Input.value,
     46                     playBName: plname2Input.value,
     47                     backColorORImg: bcorimgvalue,
     48                     playAImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playA.png",
     49                     playBImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playB.png",
     50                     playerBIsComputer:openComputer.checked
     51                 };
     52             }
     53             document.getElementById("cc").style.display = "block";
     54             gb = new gobang(initParams());
     55             /**
     56              * 设置一些界面信息
     57              * @param {Object} opt
     58              */
     59             gb.info = function(opt) {
     60                     infoboj.style.visibility = "visible";
     61                     document.getElementsByClassName("startpnl")[0].style.visibility = "hidden";
     62                     plname1obj.innerHTML = opt.playAName;
     63                     plname2obj.innerHTML = opt.playBName;
     64                     if (opt.chessType == 1) {
     65                         var span1 = document.createElement("span");
     66                         pl1obj.insertBefore(span1, plname1obj);
     67                         var span2 = document.createElement("span");
     68                         pl2obj.insertBefore(span2, plname2obj);
     69                     } else {
     70                         var img1 = document.createElement("img");
     71                         img1.src = opt.playAImg;
     72                         pl1obj.insertBefore(img1, plname1obj);
     73                         var img2 = document.createElement("img");
     74                         img2.src = opt.playBImg;
     75                         pl2obj.insertBefore(img2, plname2obj);
     76                     }
     77                 }
     78                 /**
     79                  * 每次下棋后触发事件 
     80                  * @param {Object} c2d
     81                  */
     82             gb.operate = function(opt, c2d) {
     83                 if (!c2d.winer || c2d.winer <= 0) {
     84                     pl1obj.removeAttribute("class", "curr");
     85                     pl2obj.removeAttribute("class", "curr");
     86                     if (c2d.player == 1) {
     87                         pl2obj.setAttribute("class", "curr");
     88                     } else {
     89                         pl1obj.setAttribute("class", "curr");
     90                     }
     91                     document.getElementById("backChessman").innerHTML="悔棋("+c2d.canBackTimes+")";
     92                 } else {
     93                     var winname = c2d.winer == 1 ? opt.playAName : opt.playBName;
     94                     var str = "恭喜,【" + winname + "】赢了!"
     95                     alert(str);
     96                     winerpnl.style.display = "block";
     97                     document.getElementById("winerName").innerHTML = "恭喜,【" + winname + "】赢了!";
     98                     document.getElementById("pl" + c2d.winer).style.backgroundColor = "pink";
     99                     document.getElementById("scoreA").innerHTML = c2d.playScoreA;
    100                     document.getElementById("scoreB").innerHTML = c2d.playScoreB;
    101                 }
    102             }
    103             gb.start();
    104         });
    105         
    106         document.getElementById("openComputer").addEventListener("change", function() {
    107             if (this.checked) {
    108                 plname2Input.value = "电脑";
    109                 plname2Input.disabled = "disabled";
    110             } else {
    111                 plname2Input.value = "玩家二";
    112                 plname2Input.disabled = "";
    113             }
    114         });
    115         
    116         //document.getElementById("openComputer").checked="checked";
    117         
    118         //重新开始
    119         function restartgui() {
    120             if (gb) {
    121                 winerpnl.style.display = "none";
    122                 pl1obj.removeAttribute("class", "curr");
    123                 pl2obj.removeAttribute("class", "curr");
    124                 document.getElementById("pl1").style.backgroundColor = "";
    125                 document.getElementById("pl2").style.backgroundColor = "";
    126                 gb.restart();
    127             }
    128         };
    129     </script>

    游戏主体代码块(只包含函数声明代码)

    //  ========== 
    //  =name:gobang 游戏 
    //  =anthor:jasnature
    //  =last modify date:2016-04-13
    //  ========== 
    (function(win) {
    
        var gb = function(option) {
    
            var self = this,
                canObj = document.getElementById("cc"),
                can = canObj.getContext("2d");
            self.contextObj = canObj;
            self.context = can;
    
            if (!self.context) {
                alert("浏览器不支持html5");
                return;
            };
    
            self.Opt = {
                lineColor: "green",
                chessType: 1, //1 色彩棋子 2 仿真棋子
                playAName: "play1",
                playBName: "play2",
                playAColor: "red",
                playBColor: "blue",
                playAImg: "img/playA.png",
                playBImg: "img/playB.png",
                backColorORImg: "default",
                playerBIsComputer: false
            };
    
            self.operate;
    
            //合并属性
            for (var a in option) {
                //console.log(opt[a]);
                self.Opt[a] = option[a];
            };
    
            //私有变量
            var my = {};
            my.enableCalcWeightNum = false; //显示AI分数
            my.gameover = false;
            //棋盘相关
            my.baseWidth = 30;
            my.lastFocusPoint = {}; //鼠标最后移动到的坐标点,计算后的
            my.cw = self.contextObj.offsetWidth; //棋盘宽
            my.ch = self.contextObj.offsetHeight; //
            my.xlen = Math.ceil(my.cw / my.baseWidth); //行数
            my.ylen = Math.ceil(my.ch / my.baseWidth); //
            my.chessRadius = 14; //棋子半径
            my.playerBIsComputer = false; //棋手B是否是电脑
            my.ComputerThinking = false; //电脑是否在下棋
            my.goBackC2dIsComputer = false; //最后下棋是否为电脑
    
            my.switcher = 1; //由谁下棋了 1-a 2-b or computer
            my.winer = -1; //赢家,值参考my.switcher
            my.playScoreA = 0;
            my.playScoreB = 0;
            //x,y 正方形数量(20*20)
            my.rectNum = my.xlen;
            //存储已下的点
            my.rectMap = [];
            my.NO_CHESS = -1; //没有棋子标识
            my.goBackC2d = {}; //最后下的数组转换坐标
            my.downChessmanStackC2d = []; // 记录已下棋子的顺序和位置,堆栈
    
            my.focusFlashInterval = null; //焦点闪烁线程
            my.focusChangeColors = ["red", "fuchsia", "#ADFF2F", "yellow", "purple", "blue"];
            my.eventBinded = false;
            my.currChessBackImg = null;
            my.currChessAImg = null;
            my.currChessBImg = null;
            my.currDrawChessImg = null;
            my.ChessDownNum = 0; //2个玩家 下棋总数
    
            /**
             * 开始游戏
             */
            self.start = function() {
                
            };
    
            /**
             * 重新开始游戏
             */
            self.restart = function() {
    
                
            };
    
            /**
             * 悔棋一步 ,清棋子,并返回上一次参数
             */
            self.back = function() {
    
                
            }
    
            /**
             * 初始化一些数据
             */
            function init() {
    
                
            }
    
            //        self.paint = function() {
            //
            //            //window.requestAnimationFrame(drawChessboard);
            //        };
    
            /**
             * 游戏逻辑
             */
            function logic(loc, iscomputer) {
                
    
            };
    
            /**
             * 判断是否有玩家胜出
             * @param {Object} c2d
             */
            function isWin(c2d) {
                
    
                return false;
            }
    
            /**
             * 连接赢家棋子线
             * @param {Object} points
             */
            function joinWinLine(points) {
    
                
    
            }
    
            /**
             * 画棋盘
             */
            function drawChessboard() {
                
            };
    
            /**
             * 画棋子
             * @param {Object} loc 鼠标点击位置
             */
            function drawChessman(c2d) {
    
                
            }
    
            function drawRect(lastRecord, defColor) {
                
            }
    
            /**
             * 闪烁最后下棋点
             */
            function flashFocusChessman() {
    
                
            }
    
            /**
             * 清棋子
             * @param {Object} c2d
             */
            function clearChessman() {
    
                
            }
    
            /**
             * @param {Object} loc
             * @return {Object} I 二维数组横点(),J二维数组纵点,IX 横点起始坐标,JY纵点起始坐标,player 最后下棋玩, winer 赢家
             */
            function calc2dPoint(loc) {
                var txp = Math.floor(loc.x / my.baseWidth),
                    typ = Math.floor(loc.y / my.baseWidth)
                dxp = txp * my.baseWidth, dyp = typ * my.baseWidth;
    
                loc.I = txp;
                loc.J = typ;
                loc.IX = dxp;
                loc.JY = dyp;
    
                return loc;
            }
    
            my.isChangeDraw = true;
    
            /**
             * 位置移动光标
             * @param {Object} loc
             */
            function moveFocus(loc) {
                
            }
    
            /**
             * 绑定事件
             */
            function bindEvent() {
                if (!my.eventBinded) {
                    self.contextObj.addEventListener("touchstart", function(event) {
                        //console.log(event);
                        var touchObj = event.touches[0];
                        eventHandle({
                            s: "touch",
                            x: touchObj.clientX - this.offsetLeft,
                            y: touchObj.clientY - this.offsetTop
                        })
                    });
                    self.contextObj.addEventListener("click", function(event) {
                        //console.log("click event");
                        eventHandle({
                            s: "click",
                            x: event.offsetX,
                            y: event.offsetY
                        })
                    });
    
                    self.contextObj.addEventListener("mousemove", function(event) {
                        //console.log("mousemove event");
                        moveFocus({
                            x: event.offsetX,
                            y: event.offsetY
                        });
    
                    });
    
                    my.eventBinded = true;
                }
    
                function eventHandle(ps) {
                    if (!my.gameover && !my.ComputerThinking) {
                        logic(ps);
    
                        if (my.playerBIsComputer && my.switcher == 2) {
                            my.ComputerThinking = true;
                            var pp = AI.analysis(my.goBackC2d.I, my.goBackC2d.J);
                            logic({
                                I: pp.x,
                                J: pp.y
                            }, true);
                            my.ComputerThinking = false;
                        }
                    }
                    event.preventDefault();
                    event.stopPropagation();
                    return false;
                }
    
            }
    
    
        };
    
        win.gobang = gb;
    
    })(window);
    主要算法介绍

    玩家OR电脑胜出算法

    /**
             * 判断是否有玩家胜出
             * @param {Object} c2d
             */
            function isWin(c2d) {
                //四个放心计数 竖 横 左斜 右斜
                var hcount = 0,
                    vcount = 0,
                    lbhcount = 0,
                    rbhcount = 0,
                    temp = 0;
    
                var countArray = [];
    
                //左-1
                for (var i = c2d.I; i >= 0; i--) {
                    temp = my.rectMap[i][c2d.J];
                    if (temp < 0 || temp !== c2d.player) {
                        break;
                    }
                    hcount++;
                    countArray.push({
                        I: i,
                        J: c2d.J
                    });
                }
                //右-1
                for (var i = c2d.I + 1; i < my.rectMap.length; i++) {
                    temp = my.rectMap[i][c2d.J];
                    if (temp < 0 || temp !== c2d.player) {
                        break;
                    }
                    hcount++;
                    countArray.push({
                        I: i,
                        J: c2d.J
                    });
                }
    
                if (countArray.length < 5) {
                    countArray = [];
                    //上-2
                    for (var j = c2d.J; j >= 0; j--) {
                        temp = my.rectMap[c2d.I][j];
                        if (temp < 0 || temp !== c2d.player) {
                            break;
                        }
                        vcount++;
    
                        countArray.push({
                            I: c2d.I,
                            J: j
                        });
                    }
                    //下-2
                    for (var j = c2d.J + 1; j < my.rectMap[c2d.I].length; j++) {
                        temp = my.rectMap[c2d.I][j];
                        if (temp < 0 || temp !== c2d.player) {
                            break;
                        }
                        vcount++;
                        countArray.push({
                            I: c2d.I,
                            J: j
                        });
                    }
                }
    
                if (countArray.length < 5) {
                    countArray = [];
                    //左上
                    for (var i = c2d.I, j = c2d.J; i >= 0, j >= 0; i--, j--) {
                        if (i < 0 || j < 0) break;
                        temp = my.rectMap[i][j];
                        if (temp < 0 || temp !== c2d.player) {
                            break;
                        }
                        lbhcount++;
                        countArray.push({
                            I: i,
                            J: j
                        });
                    }
                    //右下
                    if (c2d.I < my.rectMap.length - 1 && c2d.I < my.rectMap[0].length - 1) {
                        for (var i = c2d.I + 1, j = c2d.J + 1; i < my.rectMap.length, j < my.rectMap[0].length; i++, j++) {
                            if (i >= my.rectMap.length || j >= my.rectMap.length) break;
                            temp = my.rectMap[i][j];
                            if (temp < 0 || temp !== c2d.player) {
                                break;
                            }
                            lbhcount++;
                            countArray.push({
                                I: i,
                                J: j
                            });
                        }
                    }
                }
                if (countArray.length < 5) {
                    countArray = [];
                    //右上
                    for (var i = c2d.I, j = c2d.J; i < my.rectMap.length, j >= 0; i++, j--) {
                        if (i >= my.rectMap.length || j < 0) break;
                        temp = my.rectMap[i][j];
                        if (temp < 0 || temp !== c2d.player) {
                            break;
                        }
                        rbhcount++;
                        countArray.push({
                            I: i,
                            J: j
                        });
                    }
                    //左下
                    if (c2d.I >= 1 && c2d.J < my.rectMap[0].length - 1) {
                        for (var i = c2d.I - 1, j = c2d.J + 1; i > 0, j < my.rectMap[0].length; i--, j++) {
                            if (j >= my.rectMap.length || i < 0) break;
                            temp = my.rectMap[i][j];
                            if (temp < 0 || temp !== c2d.player) {
                                break;
                            }
                            rbhcount++;
                            countArray.push({
                                I: i,
                                J: j
                            });
                        }
                    }
                }
    
                if (hcount >= 5 || vcount >= 5 || lbhcount >= 5 || rbhcount >= 5) {
                    my.winer = c2d.player;
                    my.gameover = true;
    
                    joinWinLine(countArray);
    
                    return true;
                }
    
                return false;
            }

    算法简介:主要思路是搜索最后落下棋子的位置(二维坐标)计算 米  字形线坐标,看是否有连续5个或以上棋子出现。

    连接赢家棋子线

    /**
             * 连接赢家棋子线
             * @param {Object} points
             */
            function joinWinLine(points) {
    
                points.sort(function(left, right) {
                    return (left.I + left.J) > (right.I + right.J);
                });
    
                var startP = points.shift();
                var endP = points.pop();
                var poffset = my.baseWidth / 2;
                can.strokeStyle = "#FF0000";
                can.lineWidth = 2;
                can.beginPath();
                var spx = startP.I * my.baseWidth + poffset,
                    spy = startP.J * my.baseWidth + poffset;
                can.arc(spx, spy, my.baseWidth / 4, 0, 2 * Math.PI, false);
                can.moveTo(spx, spy);
                var epx = endP.I * my.baseWidth + poffset,
                    epy = endP.J * my.baseWidth + poffset;
                can.lineTo(epx, epy);
                can.moveTo(epx + my.baseWidth / 4, epy);
                can.arc(epx, epy, my.baseWidth / 4, 0, 2 * Math.PI, false);
    
                can.closePath();
                can.stroke();
    
            }

    算法简介:根据赢家返回的连子位置集合,做坐标大小位置排序,直接使用lineto 连接 第一个棋子和最后一个

    坐标换算

    /**
             * 坐标换算
             * @param {Object} loc
             * @return {Object} I 二维数组横点(),J二维数组纵点,IX 横点起始坐标,JY纵点起始坐标,player 最后下棋玩, winer 赢家
             */
            function calc2dPoint(loc) {
                var txp = Math.floor(loc.x / my.baseWidth),
                    typ = Math.floor(loc.y / my.baseWidth)
                dxp = txp * my.baseWidth, dyp = typ * my.baseWidth;
    
                loc.I = txp;
                loc.J = typ;
                loc.IX = dxp;
                loc.JY = dyp;
    
                return loc;
            }

    算法简介:这个比较简单,根据每个格子的宽度计算出实际坐标

    电脑AI主要代码(修改来源于网络)

    /**
             * AI棋型分析 
             */
            AI.analysis = function(x, y) {
                //如果为第一步则,在玩家棋周围一格随机下棋,保证每一局棋第一步都不一样
                if (my.ChessDownNum == 1) {
                    return this.getFirstPoint(x, y);
                }
                var maxX = 0,
                    maxY = 0,
                    maxWeight = 0,
                    i, j, tem;
    
                for (i = BOARD_SIZE - 1; i >= 0; i--) {
                    for (j = BOARD_SIZE - 1; j >= 0; j--) {
                        if (my.rectMap[i][j] !== -1) {
                            continue;
                        }
                        tem = this.computerWeight(i, j, 2);
                        if (tem > maxWeight) {
                            maxWeight = tem;
                            maxX = i;
                            maxY = j;
    
                        }
                        if (my.enableCalcWeightNum) {
                            can.clearRect(i * 30 + 2, j * 30 + 2, 24, 24);
                            can.fillText(maxWeight, i * 30 + 5, j * 30 + 15, 30);
                        }
                    }
                }
                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 (my.rectMap[i][m] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[i][m] === my.NO_CHESS) {
                            side1 = true; //如果为空子,则没有截断
                        }
                        break;
                    }
                }
                for (m = j + 1; m < BOARD_SIZE; m++) {
                    if (my.rectMap[i][m] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[i][m] === my.NO_CHESS) {
                            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 (my.rectMap[m][j] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][j] === my.NO_CHESS) {
                            side1 = true;
                        }
                        break;
                    }
                }
                for (m = i + 1; m < BOARD_SIZE; m++) {
                    if (my.rectMap[m][j] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][j] === my.NO_CHESS) {
                            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 (my.rectMap[m][n] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][n] === my.NO_CHESS) {
                            side1 = true;
                        }
                        break;
                    }
                }
                for (m = i + 1, n = j + 1; m < BOARD_SIZE && n < BOARD_SIZE; m++, n++) {
                    if (my.rectMap[m][n] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][n] === my.NO_CHESS) {
                            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 (my.rectMap[m][n] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][n] === my.NO_CHESS) {
                            side1 = true;
                        }
                        break;
                    }
                }
                for (m = i + 1, n = j - 1; m < BOARD_SIZE && n >= 0; m++, n--) {
                    if (my.rectMap[m][n] === chessColor) {
                        nums++;
                    } else {
                        if (my.rectMap[m][n] === my.NO_CHESS) {
                            side2 = true;
                        }
                        break;
                    }
                }
                return {
                    "nums": nums,
                    "side1": side1,
                    "side2": side2
                };
            };
    
            /**
             * 计算AI下棋权重 
             * chessColor 玩家1为玩家2为AI
             */
            AI.computerWeight = function(i, j, chessColor) {
                //基于棋盘位置权重(越靠近棋盘中心权重越大)
                var weight = 19 - (Math.abs(i - 19 / 2) + Math.abs(j - 19 / 2)),
                    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;
            };

    AI分析:这个只是最简单的算法,其实很简单,计算每个没有下棋坐标的分数,也是按照 字形 计算,计算格子8个方向出现的 一个棋子 二个棋子 三个棋子 四个棋子,其中还分为是否被截断,其实就是边缘是否被堵死。

    其实这个AI算法后续还有很多可以优化,比如 断跳 二活 其实就是2个交叉的 活二  , 因为是断掉的所以没有纳入算法权重计算,如果加入这个算法,估计很难下赢电脑了。

    如符号图:

    *        *
    *      *
    空位         
    下这里

    因为不是连续的,所有没有纳入。

    开源与下载

     http://jasnature.github.io/gobang_html5/

    有兴趣的可以下载修改并提交代码进来^_^

  • 相关阅读:
    源码实现 --> strcmp
    源码实现 --> strdel
    源码实现 --> strcpy
    SoC的Testbench中的简易bus_monitor(加入print函数)
    debian 安装后需做的几件事
    使用Perl合并文件
    一个简单的Verilog计数器模型
    Environment Modules简单使用
    Git push “fatal: Authentication failed ”
    使用SystemC进行硬件仿真
  • 原文地址:https://www.cnblogs.com/NatureSex/p/5390145.html
Copyright © 2011-2022 走看看