前端技术搭建井字游戏(内含源码)
The sand accumulates to form a pagoda
- ✨ 写在前面
- ✨ 功能介绍
- ✨ 页面搭建
- ✨ 样式设置
- ✨ 逻辑部分
✨ 写在前面
上周我们实通过前端基础实现了飞机大战游戏,今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏,功能也比较简单简单,也是想借助这样一个简单的功能,然后来帮助大家了解我们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}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!
相关文章:
前端技术搭建井字游戏(内含源码)
The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了飞机大战游戏,今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏,功能也比较简单简单&#x…...
视频截取gif方法分享,利用gif制作工具在线制作动图
表情包作为聊天社交中调节氛围的工具,而动态的gif表情包更是深受大众的喜爱。那么,这种gif动态图片要怎么制作呢?其实,很简单不需要下载软件,小白也能轻松操作的。 一、什么工具能够制作gif动画呢? 使用G…...
VRRP高级特性——管理VRRP
目录 管理VRRP备份组与业务VRRP备份组 管理VRRP备份组的两种实现方式 配置管理备份组 当在设备上配置了多个VRRP备份组时,为了减少设备间交互大量的VRRP协议报文,可以将其中一个VRRP备份组配置为管理VRRP备份组(mVRRP)…...
FreeRTOS内核:详解Task各状态(GPT4帮写)
FreeRTOS内核:详解Task各状态(GPT4帮写) 1. 背景2. Task顶层状态区分3. 运行状态(Running)4. 非运行状态4.1 阻塞态(Blocked):4.2 挂起态(Suspended)4.3 就绪…...
基于粒子群优化算法的最佳方式优化无线传感器节点的位置(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 此代码优化了由于电池耗尽而产生覆盖空洞后 WSN 节点的位置。如果活动通信中的任何节点死亡,则通过PSO优化再次定位…...
第一章 Andorid系统移植与驱动开发概述 - 读书笔记
Android驱动月考1 第一章 Andorid系统移植与驱动开发概述 - 读书笔记 1.Android系统的架构: (1)Linux内核,Android是基于Linux内核的操作系统,并且开源,所以Android与Ubuntu等操作系统的差别很小&#x…...
vi编辑器的三种模式及其对应模式下常用指令
vi是Linux系统的第一个全屏幕交互式编辑工具,在嵌入式的 学习中是一个不可或缺的强大的文本编辑工具。 一、三种模式 命令模式 如何进入命令模式:按esc键 复制:yy nyy(n:行数) 删除(剪切): dd ndd 粘贴:p 撤销&…...
webpack: 5 报错,错误
webpack-报错:Uncaught ReferenceError: $ is not defined (webpack) webpack打包jquery的插件(EasyLazyLoad)时,报错 方法一: //多个js文件用到jquery,用这种方法 在jquery.min.js的做最后写上下面的代码…...
MyBatis的缓存
文章目录 一、MyBatis的一级缓存二、MyBatis的二级缓存三、MyBatis缓存查询的顺序 一、MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就 会从缓存中直接获取,不会从…...
c语言-位段
有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用0和1表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。 **在结构体…...
Servlet3.0 新特性全解
Servlet3.0新特性全解 tomcat 7以上的版本都支持Servlet 3.0 Servlet 3.0 新增特性 注解支持;Servlet、Filter、Listener无需在web.xml中进行配置,可以通过对应注解进行配置;支持Web模块;Servlet异步处理;文件上传AP…...
PAT A1045 Favorite Color Stripe
1045 Favorite Color Stripe 分数 30 作者 CHEN, Yue 单位 浙江大学 Eva is trying to make her own color stripe out of a given one. She would like to keep only her favorite colors in her favorite order by cutting off those unwanted pieces and sewing the rem…...
真实业务场景使用-门面模式(外观)设计模式
1.前言 最近接到要修改的业务功能,这个业务增删改查很多功能都需要校验时间,比如: 1.失效时间不能超过自己父表的失效时间, 2.失效时间不能是当前时间 3.失效时间不能早于生效时间 类似这样的不同的判断还有很多,…...
基于多动作深度强化学习的柔性车间调度研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
出口亚马逊平衡车CE/UKCA认证注意事项
平衡车UKC认证 CE认证 认证项目:BS EN/EN71-1-2-3 UKCA认证标志与CE认证标志有什么不同? UKCA标记过程基本上遵循与CE标记相同的规则和规定。大多数制造商仍然可以根据测试结果和其他技术文档自行声明他们的产品,但在特定情况下,他们需要从第…...
云原生环境下的安全实践:保护应用程序和数据的关键策略
文章目录 云原生环境下的安全实践:保护应用程序和数据的关键策略一.安全措施和实践1. 身份和访问管理:2. 容器安全:3. 网络安全:4. 日志和监控:5. 持续集成和持续交付(CI/CD)安全:6.…...
vue 改变数据后,数据变化页面不刷新
文章目录 导文文章重点方法一:使用this.$forceUpdate()强制刷新方法二:Vue.set(object, key, value)方法三:this.$nextTick方法四:$set方法 导文 在vue项目中,会遇到修改完数据,但是视图却没有更新的情况 v…...
【Qt编程之Widgets模块】-006:QSortFilterProxyModel代理的使用方法
QSortFilterProxyModel是model的代理,不能单独使用,真正的数据需要另外的一个model提供,它的工鞥呢是对被代理的model(source model)进行排序和过滤。所谓过滤:也就是说按着你输入的内容进行数据的筛选,因为器过滤功能…...
上林赋 汉 司马相如
亡是公听然而笑曰:“楚则失矣,而齐亦未为得也。夫使诸侯纳贡者,非为财币,所以述职也。封疆画界者,非为守御,所以禁淫也。今齐列为东藩,而外私肃慎,捐国逾限,越海而田&…...
7.对象模型
对象模型 信号和槽 信号和槽是一种用于对象之间通信的机制。信号是对象发出的通知,槽是用于接收这些通知的函数。 当对象的状态发生变化时[按钮被点击],它会发出一个信号[clicked()],然后与该对象连接的槽函数将被自动调用。 若某个信号与多…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解
在我的上一篇博客:基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目,该项目展示了一个强大的框架,旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人,更是一个集…...
