React 教程:井字棋游戲

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中。中間件通過請求…