CARO游戏 - 使用JavaScript开发人类和计算机的游戏模式
#javascript #gamedev #game #games

贴片的贴片的三年 - - §2023年2月26日...

我将与之处理




我有很多花





trä或ngcaìgoìcwood yourniìubear ing aark cché¢n qua


lu°在花中使用很多花。

hó°°°°Cels nau na nha nha nha nha nha nha nha nha nha nha nha nha nho u°a




khã´ngSäìcaìcaìutiìmbijªìcCaìnhBäìngLäng
不与花一样相同 mi nhe nhe nhe nh nh nh nh nh nh nh nh°°a d i thu th ...

1.介绍

1.1。概述

caro是一个简单的游戏,我们都在小时候玩过。我们可以以两种模式玩游戏:人与人类或更先进的人类与计算机。 Howerver,我们很喜欢它,我们可以创建两台计算机,它们可以与它们一起玩。在文章中,我将以三种模式开发游戏:

  • 人与人类

  • 人与计算机

  • 计算机与计算机

1.2。介绍Caro游戏

gomoku以来在梅吉修复之前就在日本存在。[7] “ gomoku”这个名字来自日语,其中称为gomokunarabe(äºç®äply¦)。 GO表示五,Moku是碎片的反词,Narabe表示阵容。该游戏在中国很受欢迎,在中国被称为wuziqi(äº了£)。[8] Wu(äºWç)是指五,Zi(ÅZç)的意思是片段,而Qi(£Qã)是指中文的棋盘游戏类别。该游戏在韩国也很受欢迎,在韩国被称为Omok(QQ。[äºC®]),其结构和起源与日本名称相同。在十九世纪,该游戏被介绍给英国,在那里被称为Go Bang,据说是日语单词Goban的腐败,该单词本身是从中国k'i Pan(Qãpân)改编的。董事会。”

1.3。游戏规则

自由泳gomoku对任何一个玩家都没有限制,并且允许玩家通过创建五个或更多的石头来获胜,每个玩家交替一次将一块石头放置。

Black(首次移动的球员)长期以来一直在具有优势,甚至在L. Victor Allis证明Black可以强迫胜利之前(见下文)。 Renju试图通过额外的规则来减轻这种失衡,旨在降低黑人的第一名优势。它是在15ã15板上播放的,规则为三,四,四和四,并且仅适用于黑色。

  • 三和三禁止行动的规则同时形成了两条敞开的三个石头(在任一端都不会被对手的石头阻塞)。

  • 四个禁令的规则同时形成了两行的四个石头(开放与否)。

  • 如果玩家形成一条6或更多的石头,则可以防止玩家获胜。 Renju还利用各种锦标赛开放规则,例如SOOSãµRV-8,当前的国际标准。

在Caro(也称为Gomoku+,在越南人中很受欢迎),获胜者必须具有一条上线或一排未经破坏的五块石头,这两端都没有被阻止(覆盖范围不受此规则的影响)。这使游戏更加平衡,并为怀特提供了更大的力量。

2.游戏项目结构

The game project structure

  • css :这是所有CSS文件都保存的文件夹:style.html,使页面变得漂亮,等等。

  • 图像:这是保存游戏的所有图像的文件夹:背景,x,o,...

  • JS :这是保存所有JavaScript文件的文件夹:处理和处理游戏的所有功能。

  • caro.html,home.html :游戏接口。

3.开发游戏

3.1。游戏界面

  • home.html :Caro Game的主页。
<!DOCTYPE html>
<html lang="en">

<link rel="stylesheet" href="../common/css/home.css">
<link rel="stylesheet" href="css/caro.css">

<head>
    <meta charset="UTF-8">
    <title>Home game tic tac toe</title>
</head>
<body>
<div>
    <div class="options">
        <label for="list-type-play"></label><select id="list-type-play" class="hide-option option">
        <option selected="selected" disabled="disabled" value="">Select type play</option>
        <option value="2-players">2 players</option>
        <option value="player-computer">Player and computer</option>
        <option value="computer-computer">Computer and computer</option>
    </select>
    </div>

    <div class="options" style="margin-top: 30px">
        <label for="list-row"></label><select id="list-row" class="hide-option option">
        <option selected="selected" disabled="disabled" value="">Select amount of rows</option>
        <option value="10">10</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>
        <option value="60">60</option>
    </select>
    </div>

    <div class="options" style="margin-top: 30px">
        <label for="list-column"></label><select id="list-column" class="hide-option option">
        <option selected="selected" disabled="disabled" value="">Select amount of columns</option>
        <option value="10">10</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>
        <option value="60">60</option>
    </select>
    </div>
</div>

<div class="button" id="button" onclick="handleLetGo()">Let's go!</div>
</body>
</html>

<script type="text/javascript" src="js/caro-home.js"></script>
  • caro.html :主游戏接口
<!DOCTYPE html>
<html lang="en">

<link rel="stylesheet" href="css/caro.css">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Game caro</title>
    <link href="https://fonts.googleapis.com/css?family=Indie+Flower" rel="stylesheet">
</head>
<body>

<h1>Game caro X-O</h1>
<table id="table_game">
</table>

</body>
</html>

<script type="text/javascript" src="js/constants.js"></script>
<script type="text/javascript" src="js/caro-main.js"></script>
  • caro.css :我们可以为所有HTML元素添加样式:颜色页,背景,字体和尺寸单词。
body {
    background-color: rgb(32, 32, 32);
    background-image: url("https://janschreiber.github.io/img2/black-chalk.jpg");
    color: rgb(230, 230, 230);
    text-align: center;
    font-family: 'Indie Flower', 'Comic Sans', cursive;
    font-size: 0.7em;
}
h1 {
    line-height: 1em;
    margin-bottom: 0;
    padding-bottom: 5px;
    font-size: 2.8em;
    font-weight: bold;
}
h2 {
    font-size: 1.3em;
    font-weight: bold;
    padding: 0;
    margin: 0;

}
h3 {
    font-size: 1.1em;
    text-decoration: underline;
    text-decoration-style: dashed;
    padding: 0;
    margin: 10px 0 2px 0;
}
table {
    margin: 2% auto;
    border-collapse: collapse;
}
#table_game {
    position: relative;
    font-size: 120px;
    margin: 1% auto;
    border-collapse: collapse;
}
.td_game {
    border: 4px solid rgb(230, 230, 230);
    width: 90px;
    height: 90px;
    padding: 0;
    vertical-align: middle;
    text-align: center;
}
.fixed {
    width: 90px;
    height: 90px;
    line-height: 90px;
    display: block;
    overflow: hidden;
    cursor: pointer;
}
.td_list {
    text-align: center;
    font-size: 1.3em;
    font-weight: bold;
}
.th_list {
    font-size: 1.3em;
    font-weight: bold;
    text-align: center;
    text-decoration: underline;
}
#restart {
    font-size: 3em;
    width: 1em;
    height: 0.9em;
    cursor: pointer;
    margin: 0 auto;
    overflow: hidden;
}
.x {
    color: darksalmon;
    position: relative;
    top: -8px;
    font-size: 1.2em;
    cursor: default;
}
.o {
    color: aquamarine;
    position: relative;
    top: -7px;
    font-size: 1.0em;
    cursor: default;
}

/* modal background */
.modal {
    display: none;
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto; /* enable scroll if needed */
    background-color: black; /* fallback color */
    background-color: rgba(0, 0, 0, 0.6);
}

/* modal content */
.modal-content {
    background-color: rgb(240, 240, 240);
    color: rgb(32, 32, 32);
    font-size: 2em;
    font-weight: bold;
    /* 16 % from the top and centered */
    margin: 16% auto;
    padding: 20px;
    border: 2px solid black;
    border-radius: 10px;
    width: 380px;
    max-width: 80%;
}
.modal-content p {
    margin: 0;
    padding: 0;
}

/* close button for modal dialog */
.close {
    color: rgb(170, 170, 170);
    float: right;
    position: relative;
    top: -25px;
    right: -10px;
    font-size: 34px;
    font-weight: bold;
}
.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

.win-color {
    background-color: rgb(240, 240, 240);
}

因为游戏接口仅具有HTML和CSS。它并不复杂或困难,所以让我们阅读和理解它!下面是结果

game interface

game interface

3.2。处理游戏的所有功能

caro-home.js :这是处理主页上所有功能和事件的文件。在主页上,我们将仅处理玩家单击按钮时发生的事件,让我们去。我们将添加三个参数:游戏模式,行总和和列的总和。

function handleLetGo() {
    let typePlay = document.getElementById("list-type-play").value;
    let rows = document.getElementById("list-row").value;
    let columns = document.getElementById("list-column").value;

    if (typePlay === "" || rows === "" || columns === "") {
        alert("Vui lòng chọn kiểu chơi");
        return
    }
    window.location.href = "/game-development/games/caro/caro.html?type=" + typePlay + "&rows=" + rows + "&columns=" + columns;
}

caro-main.js :这是处理游戏中所有主要事件的文件。在这一点上,我们将开发许多事件和功能。让我们和我一起发展游戏! ð7

当然!游戏将首次启动变量,事件,音乐和图像,... 函数init 将检索游戏模式的主页值,行总和和列的总和。基于该信息,我们将创建一个游戏矩阵,其中 x的 x 的总和。游戏矩阵将保存游戏的状态并按表标签绘制它。

let tableXO = document.getElementById("table_game");
tableXO.innerHTML = tableContent

完整的源代码功能init

function init() {
    player = X;
    matrixGame = [];
    typeGame = TWO_PLAYER;
    const urlParams = new URLSearchParams(window.location.search);
    let rows = urlParams.get("rows");
    let columns = urlParams.get("columns");

    if (rows === "" || columns === "" || (urlParams.get("type") !== TWO_PLAYER && urlParams.get("type") !== COMPUTER && urlParams.get("type") !== COMPUTER_COMPUTER)) {
        window.location.href = "/game-development/games/caro/home.html";
    }

    typeGame = urlParams.get("type")

    // Data table
    let tableXO = document.getElementById("table_game");
    let tableContent = "";

    for (let row = 0; row < rows; row++) {
        let arr = [];
        let rowHTML = "<tr>";
        for (let col = 0; col < columns; col++) {
            arr.push("");
            rowHTML += `<td class="td_game"><div id="` + row.toString() + "-" + col.toString() + `" onclick="handleClick(this.id)" class="fixed"></div></td>`
        }
        rowHTML += "</tr>";

        tableContent += rowHTML;
        matrixGame.push(arr);
    }

    tableXO.innerHTML = tableContent
}

window.addEventListener("load", (event) => {
    init();
});

处理游戏中的抽奖和获胜状态的检查非常简单:

  • 绘制状态游戏:我们将循环浏览游戏矩阵中的所有元素(行X列)。如果所有元素都用“”表示,那么此位置就没有轮回播放了。和所有元素! =“” =>绘制游戏。

  • 赢得状态游戏:我们将检查水平,垂直,右角和左对角线。如果在同一回合中具有超过5个,则玩家或计算机将赢得游戏。

源代码:

draw

function checkDraw() {
    for (let i = 0; i < matrixGame.length; i++) {
        for (let j = 0; j < matrixGame[0].length; j++) {
            if (matrixGame[i][j] === "") {
                return false
            }
        }
    }

    return true
}

win

function getHorizontal(x, y, player) {
    let count = 1;
    for (let i = 1; i < 5; i++) {
        if (y + i < matrixGame[0].length && matrixGame[x][y + i] === player) {
            count++;
        } else {
            break
        }
    }

    for (let i = 1; i < 5; i++) {
        if (y - i >= 0 && y - i < matrixGame[0].length && matrixGame[x][y - i] === player) {
            count++;
        } else {
            break
        }
    }

    return count;
}

function getVertical(x, y, player) {
    let count = 1;
    for (let i = 1; i < 5; i++) {
        if (x + i < matrixGame.length && matrixGame[x + i][y] === player) {
            count++;
        } else {
            break
        }
    }

    for (let i = 1; i < 5; i++) {
        if (x - i >= 0 && x - i < matrixGame.length && matrixGame[x - i][y] === player) {
            count++;
        } else {
            break
        }
    }

    return count;
}

function getRightDiagonal(x, y, player) {
    let count = 1;
    for (let i = 1; i < 5; i++) {
        if (x - i >= 0 && x - i < matrixGame.length && y + i < matrixGame[0].length && matrixGame[x - i][y + i] === player) {
            count++;
        } else {
            break
        }
    }

    for (let i = 1; i < 5; i++) {
        if (x + i < matrixGame.length && y - i >= 0 && y - i < matrixGame[0].length && matrixGame[x + i][y - i] === player) {
            count++;
        } else {
            break
        }
    }

    return count;
}

function getLeftDiagonal(x, y, player) {
    let count = 1;
    for (let i = 1; i < 5; i++) {
        if (x - i >= 0 && x - i < matrixGame.length && y - i >= 0 && y - i < matrixGame[0].length && matrixGame[x - i][y - i] === player) {
            count++;
        } else {
            break
        }
    }

    for (let i = 1; i < 5; i++) {
        if (x + i < matrixGame.length && y + i < matrixGame[0].length && matrixGame[x + i][y + i] === player) {
            count++;
        } else {
            break
        }
    }

    return count;
}

function checkWin(points) {
    return getHorizontal(Number(points[0]), Number(points[1]), player) >= 5
    || getVertical(Number(points[0]), Number(points[1]), player) >= 5
    || getRightDiagonal(Number(points[0]), Number(points[1]), player) >= 5
    || getLeftDiagonal(Number(points[0]), Number(points[1]), player) >= 5
}

3.3。游戏模式人类和人类

我们在3.2中处理了所有游戏功能,包括初始化游戏,检查抽奖并赢得游戏。在3.3中,我们将在玩家或计算机打开游戏矩阵时处理活动。

  • 如果函数 processClick 返回值“ win”,那么我们将通知带有内容“ x/o是获胜者”的消息,然后再次启动游戏。

  • 如果函数 processClick 返回值“绘制”母鸡,我们将通知带有内容“绘制”的消息并再次启动游戏。

function handleClick(id) {
    switch (processClick(id)) {
        case WIN:
            setTimeout(function () {
                alert("Player: " + player + " is winner");

                // reset game
                init();
            }, 100);
            break;
        case DRAW:
            setTimeout(function () {
                alert("Draw");

                // reset game
                init();
            }, 100);
            break;
    }
}

函数流程:当玩家或计算机在“人与人类”游戏模式中播放时,处理事件。我们将遵循以下步骤:

  • 步骤1:如果游戏矩阵中的此位置的值为“ x”或“ o”,那么我们将返回void。

  • 步骤2:如果播放器是X

  • 步骤3:如果播放器是o,则将游戏矩阵中位置的值设置为“ O”,然后在游戏界面上绘制X html document.getElementById(id).innerHTML = OText;

  • 步骤4:检查游戏是平局还是胜利。如果是绘制的,请返回“绘制”,如果是赢,则返回“赢”。

  • 步骤5:交换转弯javascript player = player === X ? O : X;

function processClick(id) {
    let points = id.split("-");

    switch (typeGame) {
        case TWO_PLAYER:

            if (matrixGame[Number(points[0])][Number(points[1])] === X || matrixGame[Number(points[0])][Number(points[1])] === O) {
                return
            }

            if (player === X) {
                matrixGame[Number(points[0])][Number(points[1])] = X;
                document.getElementById(id).innerHTML = XText;
            }

            if (player === O) {
                matrixGame[Number(points[0])][Number(points[1])] = O;
                document.getElementById(id).innerHTML = OText;
            }

            if (checkWin(points)) {
                return WIN;
            }

            // check draw
            if (checkDraw()) {
                return DRAW;
            }

            player = player === X ? O : X;
            break;
        case COMPUTER:
            // source code to process play with computer
    }
}

3.4。游戏模式人类和计算机

有很多算法:minimax,爬山,...它们被用来选择一些最佳转弯。 Minimax算法的示例:

minimax是人工智能,决策理论,游戏理论,统计和哲学中使用的决策规则,用于最大程度地减少最坏情况(最大损失)场景的损失。在处理收益时,它被称为“最大值”,以最大化最低收益。最初是针对多个玩家零和游戏理论制定的,涵盖了玩家采取替代动作的情况以及同时动作的案例,它也已扩展到更复杂的游戏,并在不确定性的情况下进行了一般决策。

Minimax algorithm

在这里,我们将简化游戏,并根据玩家以前在游戏中的经验,确定玩家获胜的策略。

参考论文:

请参阅本文:L. Victor Allis, H. J. van den Herik, M. P. H. Huntjens, 1993. Go-Moku and Threat-Space Search

让我们去GOððð

我们将创建2个常数: map_score_computer map_point_human

  • map_score_computer :我们将检查它在相同的转弯=> win =>中是否具有5个回合的分数是无穷大。如果它在相同的转弯中具有4个=>分数=为2000点。如果在同一转弯中具有3个=>分数=为500点。如果在相同的转弯中具有2个=>分数= 300点。如果它在相同的转弯中具有1个=>分数= 100点。此分数代表攻击

  • map_point_human :如果它在相同的转弯中=>得分= 999999点。如果在相同的转弯中具有3个=>分数= 1000点。如果在相同的转弯中具有2个=>分数= 400点。如果在同一转弯中具有1个,则分数= 10分。如果它在相同的转弯中为零=>分数= 0点。此分数代表辩护

const MAP_SCORE_COMPUTER = new Map([
    [5, Infinity], [4, 2000], [3, 500], [2, 300], [1, 100]
])
const MAP_POINT_HUMAN = new Map([
    [4, 999999], [3, 1000], [2, 400], [1, 10], [0, 0]
])

函数getpointscomputer :该功能将获得位置,这是计算机的转。

  • 步骤1: maxScore 是计算机可以达到的最高分数。 ListScorePoint 是计算机的滴答位置的数组, PointsComputer 是计算机最高得分的数组。

  • 步骤2:我们将循环浏览游戏矩阵并进行检查。如果位置的值为“”,那么我们将在水平,垂直,右角和左对角线之后获得最高的相同转弯。转弯的得分= map_score_computer [sá»næ°°»cliá»nká»lá»nnhất] + map_point_human [最高的相同转弯]

  • 步骤3:我们将循环遍历具有得分等于MAXSCORE => push元素的游戏矩阵 pointscomputer

  • 步骤4:因为每个游戏状态都会以相同的maxScore具有许多转弯:为了使其更公平,我们将随机分配点。

function getPointsComputer() {
    let maxScore = -Infinity
    let pointsComputer = []
    let listScorePoint = []
    for (let i = 0; i < matrixGame.length; i++) {
        for (let j = 0; j < matrixGame[0].length; j++) {
            if (matrixGame[i][j] === "") {
                let score = MAP_SCORE_COMPUTER.get(Math.max(getHorizontal(i, j, O),getVertical(i, j, O),getRightDiagonal(i, j, O),getLeftDiagonal(i, j, O))) +
                    MAP_POINT_HUMAN.get(Math.max(getHorizontal(i, j, X),getVertical(i, j, X),getRightDiagonal(i, j, X),getLeftDiagonal(i, j, X)) - 1)
                if (maxScore <= score) {
                    maxScore = score
                    listScorePoint.push({
                        "score": score,
                        "point": [i,j],
                    })
                }
            }
        }
    }

    // get list max score
    for (const element of listScorePoint) {
        if (element.score === maxScore) {
            pointsComputer.push(element.point)
        }
    }
    return pointsComputer[Math.floor(Math.random()*pointsComputer.length)]
}

3.5。游戏模式计算机和计算机

游戏模式是在游戏模式下的fancyððð的,我们将用另一台计算机替换人类的转弯。我们仍然使用函数getpointscomputer获取转弯位置。

async function ComputerAndComputer(sumPoints) {
    for (let i = 0; i < sumPoints; i++) {
        await delay(100);
        // computer A
        let pointsComputerA = getPointsComputer()
        matrixGame[pointsComputerA[0]][pointsComputerA[1]] = X;
        document.getElementById(pointsComputerA[0].toString() + "-" + pointsComputerA[1].toString()).innerHTML = XText;

        // check win
        if (checkWin(pointsComputerA)) {
            return WIN;
        }

        // check draw
        if (checkDraw()) {
            return DRAW;
        }

        player = player === X ? O : X;

        await delay(100);
        // computer B
        let pointsComputerB = getPointsComputer()
        matrixGame[pointsComputerB[0]][pointsComputerB[1]] = O;
        document.getElementById(pointsComputerB[0].toString() + "-" + pointsComputerB[1].toString()).innerHTML = OText;

        // check win
        if (checkWin(pointsComputerB)) {
            return WIN;
        }

        // check draw
        if (checkDraw()) {
            return DRAW;
        }

        player = player === X ? O : X;
    }
}

我们将在加载事件中检查游戏模式是否为“计算机和计算机”,然后调用功能“计算机和计算机”以使两台计算机与它们一起播放。

注意:令sumpoints = matrixgame.length x matrixgame [0] .length 是最大转弯的总和。如果达到最大转弯,则绘制游戏状态。

window.addEventListener("load", (event) => {
    init();

    if(typeGame === COMPUTER_COMPUTER) {
        let sumPoints = matrixGame.length * matrixGame[0].length
        ComputerAndComputer(sumPoints).then(state => {
            switch (state) {
                case WIN:
                    setTimeout(function () {
                        alert("Player: " + player + " is winner");

                        // reset game
                        init();
                        location.reload();
                    }, 100);
                    break;
                case DRAW:
                    setTimeout(function () {
                        alert("Draw");

                        // reset game
                        init();
                        location.reload();
                    }, 100);
                    break;
            }
        })
    }
});

4。结论

这是产生的一些图像和视频。 ^^

参考:https://tuannguyenhust.hashnode.dev/