React 教程:井字棋游戏

使用 React 实现一个交互式的井字棋游戏,并配上好看的样式

// 导入必要的CSS样式和React库
import "./App.css";
import { useState } from "react";// Square组件 - 表示棋盘上的一个格子
function Square({ value, onSquareClick }) {return (<button className="square" onClick={onSquareClick}>{value}</button>);
}// App组件 - 游戏的主组件
export default function App() {// 使用useState钩子管理游戏历史状态// 初始历史是一个包含9个null的数组(表示空棋盘)const [history, setHistory] = useState([Array(9).fill(null)]);// 当前步骤的索引(表示当前显示的是哪一步的棋盘状态)const [currentMove, setCurrentMove] = useState(0);// 判断当前玩家是否是"X"(❌)// 根据当前步骤的奇偶性决定玩家顺序const xIsNext = currentMove % 2 === 0;// 获取当前棋盘的方格状态const currentSquares = history[currentMove];// 处理玩家落子function handlePlay(nextSquares) {// 创建新的历史记录:// 1. 复制从第一步到当前步骤的历史// 2. 添加新的棋盘状态const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];// 更新历史状态setHistory(nextHistory);// 将当前步骤设置为最新的一步setCurrentMove(nextHistory.length - 1);}// 跳转到指定步骤function jumpTo(nextMove) {setCurrentMove(nextMove);}// 生成历史步骤列表const moves = history.map((squares, move) => {let description;// 如果是第一步,显示"重新开始游戏",否则显示"转到第X步"description = move > 0 ? `转到第 ${move}` : "重新开始游戏";return (<li key={move}><button onClick={() => jumpTo(move)}>{description}</button></li>);});// 渲染游戏界面return (<div className="game">{/* 游戏棋盘区域 */}<div className="game-board">{/* Board组件 - 渲染当前棋盘状态 */}<Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} /></div>{/* 游戏信息区域(历史记录) */}<div className="game-info"><ol>{moves}</ol></div></div>);
}// Board组件 - 游戏棋盘
function Board({ xIsNext, squares, onPlay }) {// 处理格子点击事件function handleClick(i) {// 如果该格子已有内容或已有赢家,则不做任何处理if (squares[i] || calculateWinner(squares)) return;// 创建新的棋盘状态副本(React状态不可变原则)const nextSquares = squares.slice();// 根据当前玩家设置格子的内容nextSquares[i] = xIsNext ? "❌" : "⭕";// 调用父组件传递的onPlay函数更新游戏状态onPlay(nextSquares);}// 计算当前是否有赢家const winner = calculateWinner(squares);// 设置状态文本let status;if (winner) {status = "获胜者: " + winner;} else {status = "下一位玩家: " + (xIsNext ? "❌" : "⭕");}// 渲染棋盘return (<>{/* 显示游戏状态(赢家或下一步玩家) */}<div className="status">{status}</div>{/* 棋盘容器 */}<divstyle={{display: "flex",justifyContent: "center",alignItems: "center",padding: "20px",}}>{/* 棋盘主体 */}<div className="calculator-board">{/* 第一行 */}<div className="board-row"><Square value={squares[0]} onSquareClick={() => handleClick(0)} /><Square value={squares[1]} onSquareClick={() => handleClick(1)} /><Square value={squares[2]} onSquareClick={() => handleClick(2)} /></div>{/* 第二行 */}<div className="board-row"><Square value={squares[3]} onSquareClick={() => handleClick(3)} /><Square value={squares[4]} onSquareClick={() => handleClick(4)} /><Square value={squares[5]} onSquareClick={() => handleClick(5)} /></div>{/* 第三行 */}<div className="board-row"><Square value={squares[6]} onSquareClick={() => handleClick(6)} /><Square value={squares[7]} onSquareClick={() => handleClick(7)} /><Square value={squares[8]} onSquareClick={() => handleClick(8)} /></div></div></div></>);
}// 计算赢家函数
function calculateWinner(squares) {// 所有可能的获胜线(水平、垂直、对角线)const lines = [[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 < lines.length; i++) {// 解构当前线的三个位置const [a, b, c] = lines[i];// 检查三个格子是否相同且不为空if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {// 返回赢家("❌"或"⭕")return squares[a];}}// 没有赢家则返回nullreturn null;
}// 游戏结束注释
#root {max-width: 1280px;margin: 0 auto;padding: 2rem;text-align: center;
}/* 九宫格容器 */
.calculator-board {display: flex;flex-direction: column;gap: 8px;background-color: #2c3e50;padding: 20px;border-radius: 12px;box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}/* 按钮行 */
.board-row {display: flex;gap: 8px;
}/* 按钮样式 */
.square {width: 80px;height: 80px;background-color: #3498db;border: none;border-radius: 8px;font-size: 36px;color: white;cursor: pointer;display: flex;justify-content: center;align-items: center;transition: all 0.2s ease;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}/* 按钮悬停效果 */
.square:hover {background-color: #2980b9;transform: translateY(-3px);box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
}/* 按钮点击效果 */
.square:active {transform: translateY(1px);box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}.game-info ol {list-style-type: none; /* 移除默认的序列编号 */padding-left: 0; /* 移除默认的左内边距 */margin: 0; /* 移除默认的外边距 */
}.game-info button {background: #f0f0f0;border: 1px solid #ddd;border-radius: 4px;padding: 8px 12px;cursor: pointer;width: 240px;text-align: left;transition: background 0.2s;
}.game-info button:hover {background: #e0e0e0;
}.game-info button.active {background: #3498db;color: white;font-weight: bold;
}.game {display: flex;flex-direction: row;align-self: center;justify-self: center;gap: 20px; /* 设置两个div之间的间距 */
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90332.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90332.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/90332.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React源码2 React中的工厂函数:createRoot()

#React V18.2 源码前置基础知识&#xff1a;工厂函数工厂函数是一种设计模式&#xff0c;用于动态创建对象或函数实例。其核心思想是通过封装对象创建的细节&#xff0c;提供统一的接口&#xff0c;从而增强代码的灵活性和可维护性&#xff0c;有一些核心作用&#xff1a;解耦创…

《UE5_C++多人TPS完整教程》学习笔记42 ——《P43 瞄准(Aiming)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P43 瞄准&#xff08;Aiming&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephen Ulibarri…

SQL Server 临时表、表变量与WITH语句的用法与区别

引言 在SQL Server数据处理中,临时表、表变量和WITH语句(CTE)是关键的中间结果集管理工具。临时表适合大数据量操作,表变量优化小数据量场景,而CTE则简化复杂查询逻辑。三者选择需综合考量数据量级、事务需求及代码可读性。本文将深入解析其工作机制,通过实测对比指导场…

【Android】组件及布局介绍

一&#xff1a;代码分析 1&#xff1a;Android界面开发方式 &#xff08;1&#xff09;JavaView&#xff08;传统视图系统&#xff09; 这是 Android 早期的开发方式&#xff0c;用 Java 或 Kotlin 代码配合 XML 布局文件 来构建界面。&#xff08;简单了解即可&#xff09; 分…

Android 音视频 IPC序列化工具-Flattenable

Android Binder与AIDL与Service使用案例及分析-CSDN博客 讲讲这个类,被用在Android音视频中,跨进程序列化反序列化用。与Binder驱动有很强的联系。位于: feameworks/native/utils/Flattenable.h Flattenable, 译为令人满意的。可能是作者十分满意自己的这些作品吧,起了这…

文献学习|全面绘制和建模水稻调控组景观揭示了复杂性状背后的调控架构。

摘要&#xff1a; 解析调控复杂性状的机制对于推进作物改良至关重要。在此&#xff0c;我们提出了一个全面的水稻&#xff08;Oryza sativa&#xff09;调控组图谱&#xff0c;涵盖了来自三个代表性品种的23种不同组织的染色质可及性。我们的研究揭示了117,176个独特的开放染色…

Linux的压缩与解压缩

一、使用tar命令进行打包与解包 1.0、tar命令简介和常用选项 tar命令是Linux中经常使用的归档工具&#xff0c;它的主要功能是【对文件或者目录进行打包归档】&#xff0c;归档为一个文件&#xff0c;但是并不进行压缩&#xff1b;tar命令的归档操作效果如下&#xff1a; tar命…

OpenCV+OCR实现弧形文字识别

以下是基于OpenCV与OCR实现弧形文字识别的完整技术方案&#xff0c;结合了图像预处理、几何变换与OCR引擎调用等关键步骤&#xff0c;并提供优化技巧&#xff1a;&#x1f50d; 一、技术原理弧形文字识别的核心在于​​将弯曲文本转换为水平直线​​&#xff0c;便于OCR引擎处理…

【保姆级目标检测教程】Ubuntu 20.04 部署 YOLOv13 全流程(附训练/推理代码)

前言 YOLOv13 是 YOLO 系列的全新一代实时目标检测框架&#xff0c;在保持极高推理速度的同时显著提升了检测精度&#xff0c;广泛适用于嵌入式部署、工业质检、智能安防等多种场景。该版本提供了 Nano、Small、Large、X-Large 四种模型规格&#xff0c;用户可以根据计算资源和…

【大模型】到底什么是Function Calling和MCP,以及和ReAct推理的关系是什么?

文章目录背景&#xff1a;什么是Agent&#xff1f;背景&#xff1a;为什么需要Function Calling或者MCP&#xff1f;Function Calling和MCP在用户请求中的整体流程Function Calling&#xff08;函数/工具调用&#xff09;MCP (Model Context Protocol)ReAct (Reasoning and Act…

CANDENCE 17.4 进行元器件缓存更新

在我从立创商城导入CANDENCE元器件后&#xff0c;在ORCAD放置元器件时出现了下面的错误解决办法&#xff1a;1、在左边找到 Design Cache文件夹&#xff0c;在文件夹上鼠标右击选择 Cleanup Cache2、再放置该元器件&#xff0c;不管这个&#xff0c;点击确定3、这时候成功放上…

深入理解Kafka幂等性:原理、边界与最佳实践

一、什么是真正的消息幂等性&#xff1f; 消息系统的幂等性经常被误解&#xff0c;我们需要明确其精确含义和能力边界&#xff1a; 1. 正确定义 Kafka幂等性保证的是&#xff1a;在消息传输过程中&#xff0c;无论因网络重试、生产者重启等故障导致的消息重复发送&#xff0c;B…

【RTSP从零实践】8、多播传输H264码流的RTSP服务器——最简单的实现例子(附带源码)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【Linux】基础开发工具(3)

1. 版本控制器Git1.1 Git的简史Git 的历史可以追溯到 2005 年1。当时 Linux 内核项目的开发团队一直使用 BitKeeper 进行版本管理&#xff0c;但由于一位 Linux 开发成员写了一个连接 BitKeeper 仓库的外挂&#xff0c;BitMover 公司决定中止 Linux 免费使用 BitKeeper 的授权1…

synchronized 的使用和特性

synchronized 锁对象 普通方法 synchronized 锁普通方法时&#xff0c;其锁的对象是调用该方法的实例 public synchronized void method() { // 方法体 } 静态方法 静态方法的锁对象是所属的 class&#xff0c;全局只有一个。 public static synchronized void staticMetho…

Gin Web 层集成 Viper 配置文件和 Zap 日志文件指南(下)

在微服务架构中&#xff0c;Gin 常被用作 Web 层框架&#xff0c;而 Viper 用于管理配置文件&#xff0c;Zap 则提供高性能的日志记录功能。下面将详细介绍如何在 Gin Web 层集成 Viper 配置文件和 Zap 日志文件。 1. 项目概述 假设我们有一个基于 Go 语言的微服务项目&#…

IoTDB:专为物联网场景设计的高性能时序数据库

什么是IoTDB&#xff1f;IoTDB&#xff08;Internet of Things Database&#xff09;是一款开源的时序数据库管理系统&#xff0c;专为物联网&#xff08;IoT&#xff09;场景设计&#xff0c;由清华大学软件学院团队自研&#xff0c;天谋科技团队负责维护。它针对物联网数据的…

[netty5: MessageAggregator HttpObjectAggregator]-源码解析

在阅读这篇文章前&#xff0c;推荐先阅读 [netty5: ByteToMessageCodec & MessageToByteEncoder & ByteToMessageDecoder]-源码分析[netty5: HttpObject]-源码解析 100-continue 100-continue 是 HTTP/1.1 协议中的一种机制&#xff0c;用于客户端在发送大体积请求体…

前端学习1--行内元素 vs 块级元素(基础概念+案例实操)

一、内外边距学习&#xff1a;&#xff08;1&#xff09;简单理解&#xff1a;padding为内边距。padding不会影响元素的位置&#xff0c;只会调整元素的内容&#xff08;文字&#xff09;与边框之间的间距。margin为外边距。margin会影响元素在流式布局中的位置&#xff0c;改变…

Express + mysql2 + jwt 实现简单的登录鉴权

目前项目中使用Express 实现简单API功能&#xff0c;需要提供一套登录鉴权方案。这边是API侧实现 相关路由的登录鉴权。大体思路&#xff1a;就是&#xff0c;登录接口中通过jwt加密 token返回前端&#xff0c;前端其他接口把加密好的放入请求头Authorization中。中间件通过请求…