Maison > interface Web > Tutoriel H5 > HTML5 Canvas implémente le partage de code pour le jeu de backgammon (images et texte)

HTML5 Canvas implémente le partage de code pour le jeu de backgammon (images et texte)

黄舟
Libérer: 2017-03-22 15:43:58
original
6472 Les gens l'ont consulté

Introduction au contexte

Parce que j'ai utilisé gdi+ dans winform dans un package graphique c# et java pour créer du backgammon, je connais donc cette idée logique. Cependant, récemment, je souhaite revoir la fonction de dessin de toile de html5. (les entreprises ne les utilisent généralement pas) Ceux-ci), j'ai donc fait un backgammon. Bien sûr, je n'ai pas fait référence au code client précédent, j'ai uniquement utilisé l'algorithme de jugement gagnant et perdant et l'algorithme d'IA informatique (emprunt réseau). bien sûr, il existe de nombreux backgammon réalisés en html5 sur Baidu, mais c'est toujours une bonne chose de se rendre compte d'un côté. Bon, arrêtons de parler et allons droit au but. ^_^

Présentation des fonctions d'interface et des fonctions qui pourront être ajoutées à l'avenir

Fonctions d'interface actuelles :

Interface principale Contient

1 : Options de combat homme-humain et homme-machine 2 : Sélection de l'apparence des pièces d'échecs 3 : Sélection de l'arrière-plan de l'échiquier 4 : Sélection de la couleur de la ligne de l'échiquier

L'interface du jeu comprend

1 : Nom du joueur 2 : Pièce du joueur 3 : Qui joue actuellement aux échecs et positionnement en arrière-plan 4 : Score du joueur 5 : Zone de menu de fonction (redémarrage et échecs à regret infini) 6 : Zone du plateau 7. Connectez les pièces d'échecs après la victoire 8. La dernière position d'échecs clignote et s'affiche 9. Positionnement du curseur

 Interface de fin de jeu

 1 : Image d'arrière-plan de la victoire 2 : Nom du joueur victorieux 3 : Continuer vers le bouton suivant

Des fonctions peuvent être ajoutées

1. Retourner à l'interface principale 2. Enregistrez le jeu et données associées 3. Lire le jeu et les données associées 4. Échange de personnage 5. Bataille en ligne (2 machines) 6. Enregistrement du temps de réflexion total des deux parties

Appréciation de la capture d'écran de l'interface (n'a pas pris le temps de embellir, c'est moche)

HTML5 Canvas implémente le partage de code pour le jeu de backgammon (images et texte)

Conception générale et introduction du code principal

Conception générale

Processus de jeu d'échecs : Le joueur ou l'IA de l'ordinateur joue aux échecs ---> > Définir les valeurs de coordonnées bidimensionnelles des pièces d'échecs ----> logique (jugement logique) - ---> (Joueur) Chaîne de cinq pièces sur un côté ---> >
  ↑                |
  |         ↓
  < ;------------------- ------------------------------- -------------------- -------------------------------Pas cinq fils

Processus de repentance (tout le monde joue) : Un joueur regrette le mouvement ----> Ouvrez la pile de records d'échecs et réglez-la Parce que c'est la dernière pièce d'échecs ---> l'image de la dernière pièce d'échecs ---> Effacer la valeur des coordonnées bidimensionnelles de la pièce d'échecs ---> Processus de repentance (bataille homme-machine) : Le joueur regrette les échecs ----> Affichez la pile d'enregistrements d'échecs 2 fois et définissez le dernier ordinateur comme dernier échec ---> ; images enregistrées ---> Effacer les valeurs de coordonnées bidimensionnelles des deux pièces d'échecs ---> Repositionner et afficher la dernière position d'échecs et flash

Introduction au code principal

  Le code principal est divisé en deux blocs : 1. Bloc logique d'interface 2. Bloc principal du jeu (l'interface et le code du jeu sont séparés, avec une logique claire et une division du travail claire)

SimulationÉvénementNotification : Le bloc logique principal du jeu, chaque résultat sera notifié à la couche d'interface pour interaction (similaire au délégué ou événement c# ou java)

Code logique de l'interface

Bloc de code du corps du jeu (uniquement contient des fonctions

Code de déclaration)
<script type="text/javascript">
        var gb = null;
        var infoboj = document.getElementsByClassName("info")[0];
        var pl1obj = document.getElementById("pl1");
        var pl2obj = document.getElementById("pl2");
        var plname1obj = document.getElementById("plname1");
        var plname2obj = document.getElementById("plname2");
        var chesstypeobj = document.getElementsByName("chesstype");
        var chesscolorobj = document.getElementsByName("chesscolor");
        var chessbgObj = document.getElementsByName("chessbg");
        var winerpnl = document.getElementById("winer");
        document.getElementById("startgame").addEventListener("click", function() {
            
            function initParams() {
                var chessTypeValue = 1;
                if (chesstypeobj.length > 0) {
                    for (var i = 0; i < chesstypeobj.length; i++) {
                        if (chesstypeobj[i].checked) {
                            chessTypeValue = chesstypeobj[i].value;
                            break;
                        }
                    }
                }
                var linevalue = "";
                if (chesscolorobj.length > 0) {
                    for (var i = 0; i < chesscolorobj.length; i++) {
                        if (chesscolorobj[i].checked) {
                            linevalue = chesscolorobj[i].value;
                            break;
                        }
                    }
                }
                var bcorimgvalue = "";
                if (chessbgObj.length > 0) {
                    for (var i = 0; i < chessbgObj.length; i++) {
                        if (chessbgObj[i].checked) {
                            bcorimgvalue = chessbgObj[i].value;
                            break;
                        }
                    }
                }
                return {
                    lineColor: linevalue,
                    chessType: chessTypeValue, //1 色彩棋子 2 仿真棋子
                    playAName: plname1Input.value,
                    playBName: plname2Input.value,
                    backColorORImg: bcorimgvalue,
                    playAImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playA.png",
                    playBImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playB.png",
                    playerBIsComputer:openComputer.checked
                };
            }
            document.getElementById("cc").style.display = "block";
            gb = new gobang(initParams());
            /**
             * 设置一些界面信息
             * @param {Object} opt
             */
            gb.info = function(opt) {
                    infoboj.style.visibility = "visible";
                    document.getElementsByClassName("startpnl")[0].style.visibility = "hidden";
                    plname1obj.innerHTML = opt.playAName;
                    plname2obj.innerHTML = opt.playBName;
                    if (opt.chessType == 1) {
                        var span1 = document.createElement("span");
                        pl1obj.insertBefore(span1, plname1obj);
                        var span2 = document.createElement("span");
                        pl2obj.insertBefore(span2, plname2obj);
                    } else {
                        var img1 = document.createElement("img");
                        img1.src = opt.playAImg;
                        pl1obj.insertBefore(img1, plname1obj);
                        var img2 = document.createElement("img");
                        img2.src = opt.playBImg;
                        pl2obj.insertBefore(img2, plname2obj);
                    }
                }
                /**
                 * 每次下棋后触发事件 
                 * @param {Object} c2d
                 */
            gb.operate = function(opt, c2d) {
                if (!c2d.winer || c2d.winer <= 0) {
                    pl1obj.removeAttribute("class", "curr");
                    pl2obj.removeAttribute("class", "curr");
                    if (c2d.player == 1) {
                        pl2obj.setAttribute("class", "curr");
                    } else {
                        pl1obj.setAttribute("class", "curr");
                    }
                    document.getElementById("backChessman").innerHTML="悔棋("+c2d.canBackTimes+")";
                } else {
                    var winname = c2d.winer == 1 ? opt.playAName : opt.playBName;
                    var str = "恭喜,【" + winname + "】赢了!"
                    alert(str);
                    winerpnl.style.display = "block";
                    document.getElementById("winerName").innerHTML = "恭喜,【" + winname + "】赢了!";
                    document.getElementById("pl" + c2d.winer).style.backgroundColor = "pink";
                    document.getElementById("scoreA").innerHTML = c2d.playScoreA;
                    document.getElementById("scoreB").innerHTML = c2d.playScoreB;
                }
            }
            gb.start();
        });
        
        document.getElementById("openComputer").addEventListener("change", function() {
            if (this.checked) {
                plname2Input.value = "电脑";
                plname2Input.disabled = "disabled";
            } else {
                plname2Input.value = "玩家二";
                plname2Input.disabled = "";
            }
        });
        
        //document.getElementById("openComputer").checked="checked";
        
        //重新开始
        function restartgui() {
            if (gb) {
                winerpnl.style.display = "none";
                pl1obj.removeAttribute("class", "curr");
                pl2obj.removeAttribute("class", "curr");
                document.getElementById("pl1").style.backgroundColor = "";
                document.getElementById("pl2").style.backgroundColor = "";
                gb.restart();
            }
        };
    </script>
Copier après la connexion

Introduction à l'algorithme principal

玩家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;
        }
Copier après la connexion

算法简介:主要思路是搜索最后落下棋子的位置(二维坐标)计算字形线坐标,看是否有连续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();

        }
Copier après la connexion

算法简介:根据赢家返回的连子位置集合,做坐标大小位置排序,直接使用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;
        }
Copier après la connexion

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

电脑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;
        };
Copier après la connexion

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

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

如符号图:

*        *
*      *
空位         
下这里

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal