当前位置: 首页 > news >正文

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

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 ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了飞机大战游戏&#xff0c;今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏&#xff0c;功能也比较简单简单&#x…...

视频截取gif方法分享,利用gif制作工具在线制作动图

表情包作为聊天社交中调节氛围的工具&#xff0c;而动态的gif表情包更是深受大众的喜爱。那么&#xff0c;这种gif动态图片要怎么制作呢&#xff1f;其实&#xff0c;很简单不需要下载软件&#xff0c;小白也能轻松操作的。 一、什么工具能够制作gif动画呢&#xff1f; 使用G…...

VRRP高级特性——管理VRRP

目录 管理VRRP备份组与业务VRRP备份组 管理VRRP备份组的两种实现方式 配置管理备份组 当在设备上配置了多个VRRP备份组时&#xff0c;为了减少设备间交互大量的VRRP协议报文&#xff0c;可以将其中一个VRRP备份组配置为管理VRRP备份组&#xff08;mVRRP&#xff09;&#xf…...

FreeRTOS内核:详解Task各状态(GPT4帮写)

FreeRTOS内核&#xff1a;详解Task各状态&#xff08;GPT4帮写&#xff09; 1. 背景2. Task顶层状态区分3. 运行状态&#xff08;Running&#xff09;4. 非运行状态4.1 阻塞态&#xff08;Blocked&#xff09;&#xff1a;4.2 挂起态&#xff08;Suspended&#xff09;4.3 就绪…...

基于粒子群优化算法的最佳方式优化无线传感器节点的位置(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 此代码优化了由于电池耗尽而产生覆盖空洞后 WSN 节点的位置。如果活动通信中的任何节点死亡&#xff0c;则通过PSO优化再次定位…...

第一章 Andorid系统移植与驱动开发概述 - 读书笔记

Android驱动月考1 第一章 Andorid系统移植与驱动开发概述 - 读书笔记 1.Android系统的架构&#xff1a; &#xff08;1&#xff09;Linux内核&#xff0c;Android是基于Linux内核的操作系统&#xff0c;并且开源&#xff0c;所以Android与Ubuntu等操作系统的差别很小&#x…...

vi编辑器的三种模式及其对应模式下常用指令

vi是Linux系统的第一个全屏幕交互式编辑工具&#xff0c;在嵌入式的 学习中是一个不可或缺的强大的文本编辑工具。 一、三种模式 命令模式 如何进入命令模式&#xff1a;按esc键 复制&#xff1a;yy nyy(n&#xff1a;行数) 删除(剪切): dd ndd 粘贴&#xff1a;p 撤销&…...

webpack: 5 报错,错误

webpack-报错&#xff1a;Uncaught ReferenceError: $ is not defined (webpack) webpack打包jquery的插件&#xff08;EasyLazyLoad&#xff09;时&#xff0c;报错 方法一&#xff1a; //多个js文件用到jquery&#xff0c;用这种方法 在jquery.min.js的做最后写上下面的代码…...

MyBatis的缓存

文章目录 一、MyBatis的一级缓存二、MyBatis的二级缓存三、MyBatis缓存查询的顺序 一、MyBatis的一级缓存 一级缓存是SqlSession级别的&#xff0c;通过同一个SqlSession查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就 会从缓存中直接获取&#xff0c;不会从…...

c语言-位段

有些数据在存储时并不需要占用一个完整的字节&#xff0c;只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态&#xff0c;用0和1表示足以&#xff0c;也就是用一个二进位。正是基于这种考虑&#xff0c;C语言又提供了一种叫做位域的数据结构。 ​ **在结构体…...

Servlet3.0 新特性全解

Servlet3.0新特性全解 tomcat 7以上的版本都支持Servlet 3.0 Servlet 3.0 新增特性 注解支持&#xff1b;Servlet、Filter、Listener无需在web.xml中进行配置&#xff0c;可以通过对应注解进行配置&#xff1b;支持Web模块&#xff1b;Servlet异步处理&#xff1b;文件上传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.前言 最近接到要修改的业务功能&#xff0c;这个业务增删改查很多功能都需要校验时间&#xff0c;比如&#xff1a; 1.失效时间不能超过自己父表的失效时间&#xff0c; 2.失效时间不能是当前时间 3.失效时间不能早于生效时间 类似这样的不同的判断还有很多&#xff0c;…...

基于多动作深度强化学习的柔性车间调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

出口亚马逊平衡车CE/UKCA认证注意事项

平衡车UKC认证 CE认证 认证项目&#xff1a;BS EN/EN71-1-2-3 UKCA认证标志与CE认证标志有什么不同? UKCA标记过程基本上遵循与CE标记相同的规则和规定。大多数制造商仍然可以根据测试结果和其他技术文档自行声明他们的产品&#xff0c;但在特定情况下&#xff0c;他们需要从第…...

云原生环境下的安全实践:保护应用程序和数据的关键策略

文章目录 云原生环境下的安全实践&#xff1a;保护应用程序和数据的关键策略一.安全措施和实践1. 身份和访问管理&#xff1a;2. 容器安全&#xff1a;3. 网络安全&#xff1a;4. 日志和监控&#xff1a;5. 持续集成和持续交付&#xff08;CI/CD&#xff09;安全&#xff1a;6.…...

vue 改变数据后,数据变化页面不刷新

文章目录 导文文章重点方法一&#xff1a;使用this.$forceUpdate()强制刷新方法二&#xff1a;Vue.set(object, key, value)方法三&#xff1a;this.$nextTick方法四&#xff1a;$set方法 导文 在vue项目中&#xff0c;会遇到修改完数据&#xff0c;但是视图却没有更新的情况 v…...

【Qt编程之Widgets模块】-006:QSortFilterProxyModel代理的使用方法

QSortFilterProxyModel是model的代理&#xff0c;不能单独使用&#xff0c;真正的数据需要另外的一个model提供&#xff0c;它的工鞥呢是对被代理的model(source model)进行排序和过滤。所谓过滤&#xff1a;也就是说按着你输入的内容进行数据的筛选&#xff0c;因为器过滤功能…...

上林赋 汉 司马相如

亡是公听然而笑曰&#xff1a;“楚则失矣&#xff0c;而齐亦未为得也。夫使诸侯纳贡者&#xff0c;非为财币&#xff0c;所以述职也。封疆画界者&#xff0c;非为守御&#xff0c;所以禁淫也。今齐列为东藩&#xff0c;而外私肃慎&#xff0c;捐国逾限&#xff0c;越海而田&…...

7.对象模型

对象模型 信号和槽 信号和槽是一种用于对象之间通信的机制。信号是对象发出的通知&#xff0c;槽是用于接收这些通知的函数。 当对象的状态发生变化时[按钮被点击]&#xff0c;它会发出一个信号[clicked()]&#xff0c;然后与该对象连接的槽函数将被自动调用。 若某个信号与多…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...