React官网井字格游戏拓展实现(附完整源码和最终成果截图)
前提
需要先依据react官网教程编写出基础的井字格游戏。
拓展实现及说明
最后面会有全部的源代码。
1、在游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号)
直接给history里的squres再添加一列,保存点击的i值,即第几个方格,再在moves里计算得出列号和行号。



ps: Math.trunc()方法是通过除去小数位来返回浮点数的整数部分
2、在历史记录列表中加粗显示当前选择的项目
给历史记录列表的按钮绑定一个动态样式即可。

3、使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)

4、添加一个可以升序或降序显示历史记录的按钮
定义一个排序标志,升序为true,降序false。然后通过这个标志来升序或降序显示history数组的内容即可。

5、每当有人获胜时,高亮显示连成一线的 3 颗棋子
需要修改原来的计算成功者的方法calculateWinner,返回连成一线的下标数组winnerList,board组件双重循环渲染squre组件时可根据这个winnerList传一个flag标志给squre组件,然后squre组件里如果这个flag标志为true就背景色改变,就OK了。

board组件里修改的内容,注意第一行一定要取返回值得第一项,否则会出现点击没反应的情况。

最后,square组件根据lightFlag标志,动态显示背景颜色就大功告成了!

6、当无人获胜时,显示一个平局的消息
判断当前的square是否填满,如果填满了,且未胜利即平局。

源码和最终成果截图
完整源码
import {useState} from 'react';function Square({value,lightFlag, onSquareClick}) {return (<buttonstyle={{backgroundColor: lightFlag ? 'green' : 'transparent'}}className="square"onClick={onSquareClick}>{value}</button>);
}function Board({xIsNext, squares, onPlay}) {function handleClick(i) {if (calculateWinner(squares)[0] || squares[i]) {return;}const nextSquares = squares.slice();if (xIsNext) {nextSquares[i] = 'X';} else {nextSquares[i] = 'O';}nextSquares[9] = i //增加一列记录下标,可通过计算转坐标onPlay(nextSquares);}const [winner,winnerLine] = calculateWinner(squares);let status;if (winner) {status = '获胜的是: ' + winner;} else {if(!squares.includes(null)){status='实力伯仲之间,平局'}else {status = '下一步棋手: ' + (xIsNext ? 'X' : 'O');}}const numbers = [0, 1, 2]const listItems = numbers.map((number, rowIndex) => {return (<div className="board-row" key={rowIndex}>{numbers.map((item, columnIndex) => {let lightFlag=winnerLine.includes(rowIndex * 3 + columnIndex)return renderSquare(rowIndex * 3 + columnIndex,lightFlag)})}</div>)})//生成方格function renderSquare(i,lightFlag) {return (<Squarekey={i}value={squares[i]}lightFlag={lightFlag}onSquareClick={() => handleClick(i)}/>);}return (<><div className="status">{status}</div>{listItems}{/**/}{/* handleClick(0)} />*/ }{/* handleClick(1)} />*/ }{/* handleClick(2)} />*/ }{/**/}{/**/}{/* handleClick(3)} />*/ }{/* handleClick(4)} />*/ }{/* handleClick(5)} />*/ }{/**/}{/**/}{/* handleClick(6)} />*/ }{/* handleClick(7)} />*/ }{/* handleClick(8)} />*/ }{/**/}</>);
}export default function Game() {const [history, setHistory] = useState([Array(10).fill(null)]);const [currentMove, setCurrentMove] = useState(0);const [sortReverseFlag,setSortReverseFlag]=useState(true); //排序标志const xIsNext = currentMove % 2 === 0;const currentSquares = history[currentMove];function handlePlay(nextSquares) {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;move=sortReverseFlag?move:history.length-move-1if (move > 0) {description = '跳到第' + move + '步,坐标('+ Math.trunc(squares[9] / 3 + 1) + ',' + (squares[9] % 3 + 1) + ')'+",下棋者:"+ (move%2 ?'X':'O');} else {description = '重新开始游戏';}return (<li key={move}><buttonstyle={{fontWeight: (move === currentMove) ? 'bold' : 'normal'}}onClick={() => jumpTo(move)}>{description}</button></li>);});return (<div className="game"><div className="game-board"><Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay}/></div><div className="game-info"><buttononClick={() => setSortReverseFlag(!sortReverseFlag)}>{sortReverseFlag ? '升序' : '降序'}</button><ol>{moves}</ol></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],lines[i]];}}return [null,[]];
}
最终成果截图
获胜截图:

倒序截图:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
