前端技术搭建井字游戏(内含源码)

2023-11-08


✨ 写在前面

上周我们实通过前端基础实现了飞机大战游戏,今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏,功能也比较简单简单,也是想借助这样一个简单的功能,然后来帮助大家了解我们JavaScript在前端中的作用, 在前面的文章当中我们也提及到我们在本系列的专栏是循序渐进从简单到复杂的过程,后续会带领大家用前端实现翻卡片、扫雷、贪吃蛇等有趣的小游戏,纯前端语言实现,都会陆续带给大家。欢迎大家订阅我们这份前端小游戏的专栏。订阅链接:https://blog.csdn.net/jhxl_/category_12261013.html


✨ 功能介绍

在这里插入图片描述

井字游戏是一款经典的策略性棋盘游戏,也被称为井字棋或三子棋。游戏通过在3x3的棋盘上轮流落子,目标是将自己的棋子以横、竖或对角线的形式连成一条线,先达成连线的一方获胜。这款游戏简单易学,但需要一定的思考和策略,是一款适合所有年龄段的休闲游戏。

游戏开始时,棋盘上显示一个3x3的网格,玩家扮演的是"X"棋子,计算机扮演的是"O"棋子。玩家和计算机轮流落子,每个回合可以在空白的格子中放置自己的棋子。玩家通过点击空白格子来放置"X"棋子,计算机会自动进行下棋。当任意一方的棋子以横、竖或对角线的形式连成一条线时,该方获得胜利。如果棋盘填满且没有任何一方获胜,则游戏为平局。挑战计算机,思考最佳策略,争取在井字游戏中获得胜利吧!


✨ 页面搭建

创建文件

首先呢我们创建我们的HTML文件,这里我就直接命名为 井字游戏.html 了,大家可以随意命名, 文件创建生成后我们通过编辑器打开,这里我用的是VScode, 然后初始化我们的代码结构,那在这里告诉大家一个快捷键,就是我们敲上我们英文的一个 ! 我们敲击回车直接就会给我们生成基础版本的前端代码结构。

在这里插入图片描述

文档声明和编码设置: 在HTML文档的头部,使用<!DOCTYPE>声明HTML文档类型,确保浏览器以正确的方式渲染网页内容。同时,设置UTF-8编码,以确保浏览器能够正确地解析和显示中文字符。下面我就开始搭建我们的DOM结构了!

DOM结构搭建

这段HTML代码表示一个井字游戏的棋盘。让我为来看下每部分的含义吧!<div class="board">:这是游戏的棋盘容器,表示整个游戏区域。 <div class="cell">:这是每个格子,用于放置玩家和计算机的棋子。这里共有9个格子,按照3x3的形式排列,代表了井字游戏的九个位置。<div id="result"></div>:这是用于显示游戏结果的区域,初始时是空的。

在这个HTML结构中,通过使用CSS样式和JavaScript代码,可以实现井字游戏的显示、交互和结果展示。玩家可以点击格子,在空白的位置放置自己的棋子,然后计算机会根据规则进行下一步的落子,最终判断是否有玩家获胜或者平局。当然我们写上下面代码打开页面也是空白的,因为我们虽然写了标签,但是标签里面没有任何内容!

<div class="board">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
</div>
<div id="result"></div>

在这里插入图片描述


✨ 样式设置

我们看到了上面的的DOM已经搭建好了,但是页面什么都看不出来,下面我们简单的来配置一下样式吧,其实我们本专栏也是想带领大家掌握一些逻辑所以样式方面我们就一切从简;

这段样式代码定义了游戏界面的外观和布局。让我为小白逐个解释每个样式的含义:

.board:这是一个类选择器,用于选择游戏棋盘的容器元素。

  • display: grid;:设置元素的布局方式为网格布局。
  • grid-template-columns: repeat(3, 1fr);:定义了网格的列数为 3,每列的宽度平均分配。
  • grid-template-rows: repeat(3, 1fr);:定义了网格的行数为 3,每行的高度平均分配。
  • gap: 5px;:设置了网格之间的间隔为 5px。
  • width: 300px;:设置了容器的宽度为 300px。
  • height: 300px;:设置了容器的高度为 300px。
  • margin: 0 auto;:使容器在水平方向上居中对齐。
  • border: 1px solid #ccc;:设置容器的边框为 1px 的实线边框,颜色为 #ccc。
  • padding: 5px;:设置容器的内边距为 5px。

.cell:这是一个类选择器,用于选择游戏棋盘中的每个单元格元素。

  • display: flex;:设置元素的布局方式为弹性布局。
  • align-items: center;:在交叉轴上居中对齐元素内容。
  • justify-content: center;:在主轴上居中对齐元素内容。
  • font-size: 24px;:设置字体大小为 24px。
  • font-weight: bold;:设置字体加粗。
  • background-color: #f2f2f2;:设置背景颜色为 #f2f2f2,即浅灰色。
  • cursor: pointer;:将鼠标光标设置为手型指示器,表示单元格可以点击。

#result:这是一个 ID 选择器,用于选择游戏结果信息的元素。

  • text-align: center;:将文本内容居中对齐。
  • font-size: 24px;:设置字体大小为 24px。
  • margin-top: 20px;:设置顶部外边距为 20px,用于与上方元素保持一定的间距。

这些样式定义了游戏界面的布局、单元格的样式和游戏结果信息的样式,使界面看起来更加整齐、美观,并提供了互动性和可点击性。

<style>
    .board {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: repeat(3, 1fr);
        gap: 5px;
        width: 300px;
        height: 300px;
        margin: 0 auto;
        border: 1px solid #ccc;
        padding: 5px;
    }

    .cell {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 24px;
        font-weight: bold;
        background-color: #f2f2f2;
        cursor: pointer;
    }

    #result {
        text-align: center;
        font-size: 24px;
        margin-top: 20px;
    }
</style>

在这里插入图片描述

✨ 逻辑部分

上面我们搭建了基本的样式,下面呢我们就通过js代码,实现我们游戏的功能吧首先我们定义了一些变量,来使用:

  • const board:表示游戏棋盘的状态数组,存储每个格子的标记。
  • const cells:存储所有格子的元素,用于操作和监听每个格子。
  • const resultElement:用于显示游戏结果的元素。
  • let currentPlayer:表示当前玩家的标记,初始为 “X”。
  • let gameEnded:表示游戏是否结束的标志,初始为 false
const board = ["", "", "", "", "", "", "", "", ""];
const cells = document.querySelectorAll(".cell");
const resultElement = document.getElementById("result");
let currentPlayer = "X";
let gameEnded = false;

同时我们编写了一些函数,下面就是每个函数的大致作用:

  • updateGameState(cellIndex):更新游戏状态,处理玩家的移动。
  • checkWin(player):检查玩家是否获胜。
  • checkDraw():检查游戏是否平局。
  • endGame(message):结束游戏,显示最终结果。
  • makeComputerMove():模拟电脑进行移动。
  • renderBoard():渲染棋盘状态。
  • resetGame():重置游戏为初始状态。

最后,通过监听格子的点击事件,调用 updateGameState 函数处理玩家的移动,并在页面加载完成后调用 resetGame 函数初始化游戏。

<script>
    function updateGameState(cellIndex) {
        if (!gameEnded && board[cellIndex] === "") {
            board[cellIndex] = currentPlayer;
            renderBoard();
            if (checkWin(currentPlayer)) {
                endGame("Player " + currentPlayer + " wins!");
            } else if (checkDraw()) {
                endGame("It's a draw!");
            } else {
                currentPlayer = currentPlayer === "X" ? "O" : "X";
                if (currentPlayer === "O") {
                    setTimeout(makeComputerMove, 500);
                }
            }
        }
    }

    function checkWin(player) {
        const winningCombinations = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6]
        ];

        for (let i = 0; i < winningCombinations.length; i++) {
            const [a, b, c] = winningCombinations[i];
            if (board[a] === player && board[b] === player && board[c] === player) {
                return true;
            }
        }
        return false;
    }

    function checkDraw() {
        return board.every(cell => cell !== "");
    }

    function endGame(message) {
        gameEnded = true;
        resultElement.textContent = message;
    }

    function makeComputerMove() {
        const emptyCells = board.reduce((acc, cell, index) => {
            if (cell === "") {
                acc.push(index);
            }
            return acc
        }
            , []);
        if (emptyCells.length > 0) {
            const randomIndex = Math.floor(Math.random() * emptyCells.length);
            const computerMove = emptyCells[randomIndex];
            updateGameState(computerMove);
        }
    }

    function renderBoard() {
        for (let i = 0; i < cells.length; i++) {
            cells[i].textContent = board[i];
        }
    }

    function resetGame() {
        board.fill("");
        currentPlayer = "X";
        gameEnded = false;
        resultElement.textContent = "";
        renderBoard();
    }

    cells.forEach((cell, index) => {
        cell.addEventListener("click", () => updateGameState(index));
    });

    resetGame();
</script>

完整代码

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        .board {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            grid-template-rows: repeat(3, 1fr);
            gap: 5px;
            width: 300px;
            height: 300px;
            margin: 0 auto;
            border: 1px solid #ccc;
            padding: 5px;
        }

        .cell {
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            font-weight: bold;
            background-color: #f2f2f2;
            cursor: pointer;
        }

        #result {
            text-align: center;
            font-size: 24px;
            margin-top: 20px;
        }
    </style>
</head>

<body>
    <div class="board">
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
    </div>
    <div id="result"></div>
</body>

<script>
    const board = ["", "", "", "", "", "", "", "", ""];
    const cells = document.querySelectorAll(".cell");
    const resultElement = document.getElementById("result");
    let currentPlayer = "X";
    let gameEnded = false;

    function updateGameState(cellIndex) {
        if (!gameEnded && board[cellIndex] === "") {
            board[cellIndex] = currentPlayer;
            renderBoard();
            if (checkWin(currentPlayer)) {
                endGame("Player " + currentPlayer + " wins!");
            } else if (checkDraw()) {
                endGame("It's a draw!");
            } else {
                currentPlayer = currentPlayer === "X" ? "O" : "X";
                if (currentPlayer === "O") {
                    setTimeout(makeComputerMove, 500);
                }
            }
        }
    }

    function checkWin(player) {
        const winningCombinations = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6]
        ];

        for (let i = 0; i < winningCombinations.length; i++) {
            const [a, b, c] = winningCombinations[i];
            if (board[a] === player && board[b] === player && board[c] === player) {
                return true;
            }
        }
        return false;
    }

    function checkDraw() {
        return board.every(cell => cell !== "");
    }

    function endGame(message) {
        gameEnded = true;
        resultElement.textContent = message;
    }

    function makeComputerMove() {
        const emptyCells = board.reduce((acc, cell, index) => {
            if (cell === "") {
                acc.push(index);
            }
            return acc
        }
            , []);
        if (emptyCells.length > 0) {
            const randomIndex = Math.floor(Math.random() * emptyCells.length);
            const computerMove = emptyCells[randomIndex];
            updateGameState(computerMove);
        }
    }

    function renderBoard() {
        for (let i = 0; i < cells.length; i++) {
            cells[i].textContent = board[i];
        }
    }

    function resetGame() {
        board.fill("");
        currentPlayer = "X";
        gameEnded = false;
        resultElement.textContent = "";
        renderBoard();
    }

    cells.forEach((cell, index) => {
        cell.addEventListener("click", () => updateGameState(index));
    });

    resetGame();
</script>

</html>

本期推荐 点击此处购买

在这里插入图片描述

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端技术搭建井字游戏(内含源码) 的相关文章

  • 使用 jQuery 更改父元素样式

    我有下一个 html 设置 div class one div class two a href class three Click a div div 我想更改具有类的元素的背景颜色 one当我点击元素时 three使用 jQuery 这
  • 如何向上转型以限制对象属性

    在 JavaScript 中 如何从子类向上转换到超类以自动删除超类中不存在的对象属性 示例 假设有以下 2 个类 class ClassA constructor public a string public b string class
  • 如何在 DOM 中的每个元素中调用函数,即使它们是动态创建的

    我想对 DOM 上的特定元素调用函数 例如 red css backgroundColor pink 它适用于 DOM 中已经存在的任何元素 但我也希望在动态添加到 DOM 的元素中调用此方法 我尝试过类似的事情 red on functi
  • 水平滚动的表格上的“粘性”标题......完全不可能?

    经过过去几个小时的研究后 我开始认为这是不可能的 即使在最新的浏览器上也是如此 HTML table具有水平滚动的元素 带有 粘性 thead在顶部 作为垂直滚动的周围网页的一部分 这是我的尝试 a height 100px backgro
  • 如何立即启动setInterval循环? [复制]

    这个问题在这里已经有答案了 在一个简单的setInterval setInterval function Do something every 9 seconds 9000 第一个动作将在 9 秒后发生 t 9s 如何强制循环立即执行第一个
  • 设置双指缩放时精确的滚动位置

    我正在创建一个地图应用程序 它将标记图像放置在画布上并滚动到它 我正在使用浏览器的捏缩放和滚动来放大 缩小地图 然而 我注意到有一些奇怪的行为 我想知道如何解决它 这有点难以解释 但我们开始吧 假设您处于网页的标准缩放级别 无法进一步缩小
  • JS 保留以零结尾的小数[重复]

    这个问题在这里已经有答案了 在JavaScript中 是否可以 锁定 十进制数 以保留以零结尾的 浮点数 例如 我有 2 个不同的数字 如下所示 伪代码 let a 1 0 let b 1 00 a b true should be fal
  • karma/jasmine 控制台更详细的测试结果

    我使用 Karma 和 Jasmine 进行 javascript 单元测试 假设我有一个失败的测试 如下所示 expect objectA toEqual expectedObjectA 当失败时 我看到控制台上转储了两个对象 并显示一条
  • 为什么这行带有“await”的代码会触发微任务队列处理?

    以下引用是我理解微任务队列处理的主要参考 当 JS 堆栈清空时 就会处理微任务 承诺使用 杰克 阿奇博尔德 https twitter com jaffathecake status 954653170986311680 这对我来说没有意义
  • 如何在 React Native 上显示 SVG 文件?

    我想显示 svg 文件 我有一堆 svg 图像 但我找不到显示的方式 我尝试使用Image and Use的组成部分反应本机 svg https github com magicismight react native svg但他们不这样做
  • 使用 JQuery 禁用和启用所有超链接

    我有以下禁用所有超链接的内容 但在事件发生后我想再次启用它们 我该如何执行此操作 a click function return false 我认为这不仅仅是将其设置为 true 那么简单 谢谢大家 不要以这种方式绑定 点击 处理程序 而是
  • Web组件中嵌套槽的内容不可见

    我有一个 Web 组件 它应该接受任意元素来包装其内容 虽然我可以在 Chrome 开发工具中看到插槽已正确分配 但 DOM 中什么也没有出现 以前有人见过这个问题吗 定义 class ExampleParent extends HTMLE
  • 类中可以有生成器 getter 吗?

    我的意思是吸气剂是发电机 我相信这一切都是 ES6 也许像这样 class a get count let i 10 while i yield i let b new a for const i of b count console lo
  • Service Worker 与 Shared Worker

    Service Worker 和 Shared Worker 有什么区别 我什么时候应该使用 Service Worker 而不是 Shared Worker 反之亦然 Service Worker 具有共享 Worker 之外的附加功能
  • 摩纳哥:如何添加内联自动完成/代码建议?

    我找不到任何有关如何添加内联自动完成功能的示例 如下图所示 有人可以指导我如何在摩纳哥做到这一点吗 这可以在 v1 66 中启用 现在在 Insiders 中 The editor quickSuggestions设置现在接受内联为 配置值
  • ThreeJS 中阴影的奇怪行为

    所以我有一个 ThreeJS 场景 并且添加了一些球体 多材质 我还添加了定向光 this light new THREE DirectionalLight 0xFFFFFF 1 this light position set 2 10 2
  • 以角度访问窗口 TemplateUrl 内的范围

    我的模式有一个 windowTemplateUrl 如下 div class modal fade div class modal dialog div class modal content square btn div div div
  • d3.event.translate 在触摸设备的缩放上包含 NaN

    我使用 d3 为我的 svg 编写了一个自定义缩放函数 如下所示 Zoom behavior function myzoom xpos d3 event translate 0 ypos d3 event translate 1 vis a
  • ng-include 和 ng-view 不同时加载

    下面是我的应用程序的结构 很简单 页眉和页脚是非常小的文件 而主页上的 ng view 要大得多 当我进入该页面时 我注意到了这一点 首先加载两个 ng include 然后 ng view 出现 页脚被推到底部 页脚闪烁大约 0 1 秒
  • Chrome 扩展:强制 popup.html 关闭

    我想知道是否可以强制 popup html 关闭 在弹出的 javascript 中 window close

随机推荐

  • 以太坊学习笔记(三)——搭建以太坊私链

    以太坊私链的搭建可以直接通过下载程序进行安装 也可以通过编译源码安装 本文介绍通过编译源码进行安装 编译源码 1 准备环境 我们下载的是go语言的源码 首先需要正确的安装go语言环境 如何正确安装go语言环境 大家可以去网上找教程 2 下载
  • AndroidO audio系统之AudioPolicyService分析(三)

    1 AudioPolicyService基础 AudioPolicy在Android系统中主要负责Audio 策略 相关的问题 它和AudioFlinger一起组成了Android Audio系统的两个服务 一个负责管理audio的 路由
  • QStringList 常用方法

    QStringList类 常用方法 定义一个字符串链表 QStringList weekList 往链表中添加元素 weekList lt lt 星期一 lt lt 星期二 lt lt 星期三 lt lt 星期四 weekList lt l
  • 麻雀算法SSA优化LSTM超参数

    前言 LSTM 航空乘客预测单步预测的两种情况 简单运用LSTM 模型进行预测分析 加入注意力机制的LSTM 对航空乘客预测采用了目前市面上比较流行的注意力机制 将两者进行结合预测 多层 LSTM 对航空乘客预测 简单运用多层的LSTM 模
  • Shapley Values

    今天来学习一下Shapley Values 先上概念 以及研究背景 borrowed from Wikipedia The Shapley value is a solution concept in cooperative game th
  • 环形链表之快慢指针

    环形链表 前言 一 案例 1 环形链表 2 环形链表II 二 题解 1 环形链表 2 环形链表II 3 源码 4 寻找入环点的数学解法 总结 参考文献 前言 对于环形链表 通过快慢指针 如果存在环 这这两个指针一定会相遇 这是一种经典的判断
  • 服务器虚拟化的七大好处

    将服务器物理资源抽象成逻辑资源 让一台服务器变成几台甚至上百台相互隔离的虚拟服务器 我们不再受限于物理上的界限 而是让CPU 内存 磁盘 I O等硬件变成可以动态管理的 资源池 从而提高资源的利用率 简化系统管理 实现服务器整合 让IT对业
  • 遇到的问题----java.lang.reflect.InvocationTargetException

    发现问题 因为是从servlet中反射调用方式 所以只要是在调用反射方法中出现的任何错误都会包此错误 关键在于看下面的错误报告 如下图 解决问题 然后经过一点点的排查发现 namespace com bjsxt mapper Employe
  • 将List中的某一个元素移动到首位或指定位置

    List集合的特点是有序 有下标 可重复的 问题场景 从数据库查询多条数据放到List集合中 但突然想把集合中某一条数据向上移动 放到某一条数据后边 此时你又不能改变从数据库中查询结果的顺序 所以只能对集合进行处理 方法一 使用 Colle
  • How to Control Power Switch Rush Current

    原文链接 https community cadence com cadence blogs 8 b lp posts how to control power switch rush current While there are mul
  • mysql列转行

    原表select from test table 列转行select sheng substring index substring index b shi a help topic id 1 1 as shi from mysql hel
  • C#怎么测试静态方法?我给出了2种方案

    问题 假设有一个方法需要判断当前小时范围 代码如下 public class Class1 public bool SomeMethod var hour DateTime Now Hour if hour gt 9 hour lt 12
  • 遥感影像深度学习样本对制作教程3——从GEE下载训练数据

    关注公众号GeodataAnalysis 回复20230505获取示例数据和代码 这三章的代码都放在一起 上手运行一下代码更容易弄懂 遥感数据多种多样 存储格式各异 处理起来很麻烦 比如很多MODIS数据都是采用HDF格式存储的 在制作深度
  • 模板编程:模板完全特例化

    模板有类模板和函数模板 类模板存在偏特例化 和完全特例化 类模板 类模板完全特例化 类模板偏特化 函数模板只有完全特例化 函数模板完全特特化重点 需要在特例化版本前面加template lt gt 告诉编译器 这个函数是对模板进行特例化 特
  • stm32F1的 PA13/PA14/PA15/PB3/PB4 作为普通引脚使用

    代码链接 https blog csdn net Mark md article details 107411081
  • AS3 通过方法名称 进行调用

    package public class ObjectBinder public var targetInstance public function ObjectBinder targetInstance this targetInsta
  • 运维常用工具

    操作系统 Centos Ubuntu Redhat suse Freebsd 网站服务 nginx apache lighttpd php tomcat resin 数据 库 MySQL MariaDB PostgreSQL DB中间件 m
  • Android Handler被弃用,那么以后怎么使用Handler,或者类似的功能

    Android API30左右 Android应用在使用传统写法使用Handler类的时候会显示删除线 并提示相关的方法已经被弃用 不建议使用 Handler handler new Handler Override public void
  • PyQt5学习记录----案例1实践

    案例1 创建多个用于信息提示的QLabel 要求 1 凡是提示的QLabel控件 都需设置 字体大小 25px 字体颜色 灰色 边框圆角 8px 2 信息提示分多个级别 正常 normal 绿色边框及字体 警告 warning 黄色边框及字
  • 前端技术搭建井字游戏(内含源码)

    The sand accumulates to form a pagoda 写在前面 功能介绍 页面搭建 样式设置 逻辑部分 写在前面 上周我们实通过前端基础实现了飞机大战游戏 今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏