HTML5 五子棋 - JS/Canvas 遊戲

背景介紹

  由於以前用c#的winform中的gdi+,java圖形包作過五子棋,因此作這個邏輯思路也就得心應手,然而最近想溫故html5的canvas繪圖功能(公司通常不用這些),因此作了個五子棋,固然沒參考以前的客戶端代碼,只用使用以前計算輸贏判斷算法和電腦AI(網絡借取)的算法,固然如今html5作的五子棋百度一下很是多,可是本身實現一邊總歸是好事情,好了廢話很少說了進入正題。^_^javascript

界面功能介紹與後續可增長功能

 目前界面功能:html

  主界面包含html5

    1:人人、人機對戰選項 2:棋子外觀選擇 3:棋盤背景選擇 4:棋盤線條顏色選擇java

  遊戲界面包含git

    1:玩家名稱 2:玩家棋子 3:當前由誰下棋背景定位 4:玩家比分 5:功能菜單區域(從新開始和無限悔棋) 6:棋盤區域 7.勝利後連環棋子鏈接 8.最後下棋位置閃爍顯示 9.光標定位github

  遊戲結束界面算法

    1:勝利背景圖 2:勝利玩家姓名 3:繼續下一把按鈕canvas

 可增長功能c#

  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/

有興趣的能夠下載修改並提交代碼進來^_^

相關文章
相關標籤/搜索