Front end Gobang 2nd Edition

Try link: Gobang
The renderings are as follows:

Compared with the previous version, it mainly implements two functions:
One is to deal with the bug that the sound effect disappears, the other is to add a new human-computer mode.
Let's talk about the background sound effect first. In fact, the solution is very simple, that is, change the MP3 music name in Chinese to English.
Direct code:
HTML code:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gobang</title>
    <link rel="stylesheet" href="css/gobang.css">
</head>

<body>
    <a href="javascript:void(0)" id="computerPlay">Man-machine confrontation</a>
    <a href="" id="newGame">Reopening a Bureau</a>
    <a href="javascript:void(0)" id="personPlay">VS Human</a>
    <canvas id="canvas" width="480" height="480">
    </canvas>
    <audio class="audio">
        <source src="cbgm.mp3" type="audio/mp3" />
    </audio>
    <audio class="music">
        <source src="bgm.mp3" type="audio/mp3" />
    </audio>
    <script src="js/game.js"></script>
    <script src="js/ai.js"></script>
    <script src="js/type.js"></script>
</body>

</html>

As the code becomes more and more, the external file reference method I adopted in this version is to separate the css code and js code, which is more in line with the code format standard.
CSS Code:

body {
    margin: 0;
    background-color: #ccc;
}

#canvas {
    display: block;
    position: relative;
    margin: 135px auto;
    background-color: rgb(221, 168, 21);
}
a{
    position: absolute;
    width: 100px;
    height: 25px;
    font-family: Arial;
    color: white;
    border-radius: 10px;
    text-decoration: none;
    text-align: center;
}
#computerPlay {
    background-color: #f14343;
    top: 90px;
    left: 36%;
}
#computerPlay:hover {
    background: #c90707;
}
#personPlay {
    background-color: rgb(57, 190, 243);
    top: 90px;
    left: 57.5%;
}
#personPlay:hover{
    background: rgb(8, 168, 231);
}
#newGame{
    background-color: rgb(221, 224, 26);
    top: 90px;
    left: 47%;
}
#newGame:hover{
    background: rgb(198, 201, 40);
}

js code of game start and duel:

var canvas = document.querySelector("canvas");
var computerPlay = document.getElementById("computerPlay");
var personPlay = document.getElementById("personPlay");
var chessColor = ['black', 'white'];
var musicStart = false;
var step = 0;
var mapChess = [];
var mode = [
    [1, 0],
    [0, 1],
    [1, 1],
    [1, -1]
]
for (var i = 0; i < 15; i++) {
    mapChess[i] = [];
    for (var j = 0; j < 15; j++) {
        mapChess[i][j] = '';
    }
}
var ctx = canvas.getContext("2d");/* Get drawing environment */
for (var i = 1; i < 16; i++) {
    ctx.moveTo(30 * i, 30);
    ctx.lineTo(30 * i, 450);/* Describe the drawing path */
    ctx.moveTo(30, 30 * i);
    ctx.lineTo(450, 30 * i);
}
ctx.stroke();/* Draw all previous paths once */
drawPoint(120, 120);
drawPoint(120, 360);
drawPoint(360, 120);
drawPoint(360, 360);
drawPoint(240, 240);
computerPlay.addEventListener('click', computerStart, false);
personPlay.addEventListener('click', personStart, false);

function personStart() {
    canvas.addEventListener('click', start, false);
    computerPlay.removeEventListener('click', computerStart, false);
}
function computerStart() {
    if (step == 0) {
        document.querySelector('.audio').play();
        document.querySelector('.music').play();
        drawChess(240, 240, 'black');
        mapChess[7][7] = 'black';
        step++;
    }
    canvas.addEventListener('click', aiStart, false);
    personPlay.removeEventListener('click', personStart, false);
}
function drawChess(x, y, color) {
    ctx.fillStyle = color;
    ctx.beginPath();/* write */
    ctx.arc(x, y, 13, Math.PI * 2, false);
    ctx.fill();
    ctx.stroke();
}
function drawPoint(x, y) {
    ctx.fillStyle = 'black';
    ctx.beginPath();/* write */
    ctx.arc(x, y, 2, Math.PI * 2, false);
    ctx.fill();
    ctx.stroke();
}
function start(e) {
    var audio = document.querySelector('.audio');
    var music = document.querySelector('.music');
    var color = chessColor[step % 2];
    var dx = Math.floor((e.offsetX + 15) / 30) - 1;
    var dy = Math.floor((e.offsetY + 15) / 30) - 1;
    if (dx < 0 || dx > 14 || dy < 0 || dy > 14) {
        return;
    }
    if (mapChess[dx][dy] == '') {
        var audioPromise = document.querySelector('.audio').play();
        document.querySelector('.music').play();
        music.muted = false;
        drawChess((dx + 1) * 30, (dy + 1) * 30, color);
        mapChess[dx][dy] = color;
        if (judge(dx, dy, color, mode[0], 5) ||
            judge(dx, dy, color, mode[1], 5) ||
            judge(dx, dy, color, mode[2], 5) ||
            judge(dx, dy, color, mode[3], 5)
        ) {
            music.muted = true;
            step % 2 == 0 ? alert("Black wins") : alert("Winning white chess");
            canvas.removeEventListener('click', start, false);
            personPlay.removeEventListener('click', personStart, false);
            return;
        }
        if (audioPromise !== undefined) {
            audioPromise.then(_ => {
                audio.paused = true;
            })
                .catch(error => {

                });
        }
        step++;
    }
}
function judge(x, y, color, mode, number) {
    var count = 1;
    for (var i = 1; i < number; i++) {
        if (mapChess[x + i * mode[0]]) {
            if (mapChess[x + i * mode[0]][y + i * mode[1]] == color) {
                count++;
            } else {
                break;
            }
        }
    }
    for (var i = 1; i < number; i++) {
        if (mapChess[x - i * mode[0]]) {
            if (mapChess[x - i * mode[0]][y - i * mode[1]] == color) {
                count++;
            } else {
                break;
            }
        }
    }
    return count >= number ? true : false;
}

In addition to adding three new buttons and binding their click events (this part of the code should be easy to understand), the rest of the code has been explained in the previous version. If you can't understand the code, please refer to the following links: First version of front end Gobang
Then it is to realize the ai function of the new Gobang. Regardless of ai, its general realization idea is almost the same as that of the duel, except that after the player finishes the next step, the computer follows the next step.
js code of computer playing chess:

var backX = 0;
var backY = 0;

function aiStart(e) {
    var audio = document.querySelector('.audio');
    var music = document.querySelector('.music');
    var color = chessColor[step % 2];
    var dx = Math.floor((e.offsetX + 15) / 30) - 1;
    var dy = Math.floor((e.offsetY + 15) / 30) - 1;
    if (dx < 0 || dx > 14 || dy < 0 || dy > 14) {
        return;
    }
    if (mapChess[dx][dy] == '') {
        var audioPromise = document.querySelector('.audio').play();
        document.querySelector('.music').play();
        music.muted = false;
        //Game player
        drawChess((dx + 1) * 30, (dy + 1) * 30, color);
        mapChess[dx][dy] = color;
        if (judge(dx, dy, color, mode[0], 5) ||
            judge(dx, dy, color, mode[1], 5) ||
            judge(dx, dy, color, mode[2], 5) ||
            judge(dx, dy, color, mode[3], 5)
        ) {
            music.muted = true;
            canvas.removeEventListener('click', aiStart, false);
            computerPlay.removeEventListener('click', computerStart, false);
            step % 2 == 0 ? alert("Black wins") : alert("Winning white chess");
            return;
        }
        step++;
        //Computer drop
        AI();
        dx = backX;
        dy = backY;
        drawChess((dx + 1) * 30, (dy + 1) * 30, 'black');
        mapChess[dx][dy] = 'black';
        if (judge(dx, dy, 'black', mode[0], 5) ||
            judge(dx, dy, 'black', mode[1], 5) ||
            judge(dx, dy, 'black', mode[2], 5) ||
            judge(dx, dy, 'black', mode[3], 5)
        ) {
            music.muted = true;
            canvas.removeEventListener('click', aiStart, false);
            computerPlay.removeEventListener('click', computerStart, false);
            step % 2 == 0 ? alert("Black wins") : alert("Winning white chess");
            return;
        }
        if (audioPromise !== undefined) {
            audioPromise.then(_ => {
                audio.paused = true;
            })
                .catch(error => {

                });
        }
        step++;
    }
}

function AI() {
    var computerScore = [];
    var personScore = [];
    var maxScore = 0;
    for (var i = 0; i < 15; i++) {
        personScore[i] = [];
        computerScore[i] = [];
        for (var j = 0; j < 15; j++) {
            personScore[i][j] = 0;
            computerScore[i][j] = 0;
        }
    }
    for (var i = 0; i < mapChess.length; i++) {
        for (var j = 0; j < mapChess.length; j++) {
            if (mapChess[i][j] == '') {
                var sum = 0;
                //One step score
                for (var k = 0; k <= 3; k++) {
                    var perArr = getType(i, j, 'white', mode[k]);
                    personScore[i][j] += 4 * Math.pow(10, perArr[1] - perArr[0]);
                    var comArr = getType(i, j, 'black', mode[k]);
                    computerScore[i][j] += 6 * Math.pow(10, comArr[1] - comArr[0]);
                }
                sum = computerScore[i][j] + personScore[i][j];
                if (sum > maxScore) {
                    maxScore = sum;
                    xMax = i;
                    yMax = j;
                }
            }
        }
    }
    backX = xMax;
    backY = yMax;
}

Two points are explained in the function of aiStart:

  1. Because the computer does not need to trigger the click event to play chess, I directly bind it to the back of the player.
  2. The so-called ai chess is nothing more than to get the coordinates of the final position through a series of complex logical calculations, so I use the full solution variables backX, backY to receive the final computer chess position.

There are two points in the AI function:

  1. First of all, we need to use computerScore and personScore to store the calculated scores of each position in the chessboard, so they are also as large as the chessboard array. And because of the change of some positions after each game, they are also local variables. (just because only part of the positions have to be changed, this is also the optimization point of the chess calculation function.)
  2. The getType function is used to get the score in one direction of a point (only four directions). Here I set the score coefficient of the computer to 6 and the score coefficient of the player to 4. Because the purpose of playing chess is to win, the advantage of our side is more important. But at the same time, if the enemy is low, we also need to fight for it. That's why the score I set in the end is between the two To sum rather than to make a difference.

Finally, the most magical getType function is highlighted:

function getType(x, y, color, mode) {
    var countBlock = 0;
    var count = 1;
    var i = 1;
    while ((x + i * mode[0]) < 15 && (y + i * mode[1]) < 15 && mapChess[x + i * mode[0]][y + i * mode[1]] != '') {
        if (mapChess[x + i * mode[0]][y + i * mode[1]] == color) {
            count++;
        } else {
            countBlock++;
            break;
        }
        i++;
    }
    if ((x + i * mode[0]) < 15 || (y + i * mode[1]) < 15) {
        countBlock = 4;
    }
    i = 1;
    while ((x - i * mode[0]) >= 0 && (y - i * mode[1]) >= 0 && mapChess[x - i * mode[0]][y - i * mode[1]] != '') {
        if (mapChess[x - i * mode[0]][y - i * mode[1]] == color) {
            count++;
        } else {
            countBlock++;
            break;
        }
        i++;
    }
    if ((x - i * mode[0]) >= 0 || (y - i * mode[1]) >= 0) {
        countBlock = 4;
    }
    return [countBlock, count];
}

Three points are explained in the getType function:

  1. First of all, why is it magical? Because this function includes five single positions: sleep one, live one, die one, sleep two, live two, sleep three, live three, die three, sleep four, die four, live four (in this case, death means that both ends have been blocked by the opponent's chess pieces). Note that this function does not include sleep two, live two and so on Consideration. (but I will add later versions)
  2. There is also a boundary consideration of countBlock = 4, which is to avoid useless chess playing on the computer. However, there are still problems in my setting
  3. The final result is to subtract the countBlock value from the count value as the index. In fact, this is not the best algorithm, but it is a very efficient algorithm.

In a word, there are still many bug s in the ai of this computer, but as far as the level of single step chess is concerned, I feel that I have been careless. The follow-up focus will be to give the computer the function of calculating chess and even killing. This part of the algorithm is very difficult. I don't have this ability at present, so I have to learn more continuously. At present, I have a thought of backtracking pruning algorithm. Here I welcome comments from the comments area!

Published 53 original articles, won praise 23, visited 7993
Private letter follow

Tags: Javascript REST

Posted on Mon, 09 Mar 2020 00:01:56 -0700 by abigbluewhale