使用React实现井字棋小游戏

it2023-09-19  73

使用React实现井字棋小游戏

按照React官方教程来实现一个井字棋小游戏并完善其他功能,这里主要讲怎么完善这些功能 在游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号)。在历史记录列表中加粗显示当前选择的项目。使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)。添加一个可以升序或降序显示历史记录的按钮。每当有人获胜时,高亮显示连成一线的 3 颗棋子。当无人获胜时,显示一个平局的消息。 点击此处获取React官方井字棋小游戏源码———官方代码.点击此处获取已完善的井字棋的源码———已完善代码.

1.添加坐标功能

在state的history中添加coordinate this.state = { // square: Array(9).fill(null), xIsNext: true, history: [ { square: Array(9).fill(null), coordinate: 0 } ], stepNumber: 0, order: false } 点击时根据index添加坐标信息 handleClick(i) { let history = this.state.history.slice(0, this.state.stepNumber + 1); let current = history[history.length - 1] let square = current.square.slice(); //如果存在获胜者或此棋格已有值,则返回 if (calculateWinner(square) || square[i]) { return; } //根据xIsNext判断值为X或0 square[i] = this.state.xIsNext ? "X" : "O"; //计算此步的x坐标 let x = Math.floor(i / 3) + 1 //计算此步的y坐标 let y = i % 3 + 1 //生成坐标信息 let coordinate = square[i] + '(' + x + ',' + y + ')' this.setState({ history: history.concat([ { square: square, coordinate: coordinate } ]), stepNumber: history.length, xIsNext: !this.state.xIsNext }); } 渲染时渲染坐标 const moves = history.map((step, index) => { const desc = index ? 'Go to move : ' + step.coordinate : 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)} className='btn' {desc}</button> </li> ); });

效果图:

2.加粗显示当前选择的项目

根据stepNumber判断按钮是否对应当前项目,对应的话加入相应的样式 const moves = history.map((step, index) => { const desc = index ? 'Go to move : ' + step.coordinate : 'Go to game start'; let light = { color: 'rgb(158, 147, 147)' }; if(index === this.state.stepNumber){ if(winner){ light={ color: 'red' } }else{ light={ color: '#000' } } } return ( <li key={index}> <button onClick={() => this.jumpTo(index)} className='btn' style={light}>{desc}</button> </li> ); });

效果图:

3.使用两个循环来渲染出棋盘的格子

通过两个for循环来生成3X3表格 const SquareRow = [] for (let i = 0; i < 3; i++) { let str = []; //两重循环遍历出棋盘 for (let j = 0; j < 3; j++) { let light = false; if (this.props.winnnerStep.indexOf(i * 3 + j) != -1) { light = true; } else { light = false; } str.push( <Square value={this.props.square[i * 3 + j]} key={i * 3 + j} onClick={() => this.props.handleClick(i * 3 + j)} light={light} /> ) } let square = <div className="board-row">{str}</div> SquareRow.push(square) } let showSquare = SquareRow.map((item, index) => { return <div key={index}>{item}</div> }) return ( <div> <div> {showSquare} </div> <div> <button onClick={() => this.props.reSetClick()} className='reset_btn'>重置</button> </div> </div> );

4.添加一个可以升序或降序显示历史记录的按钮

添加一个state:order,(order为false时为正序,true时为倒序) this.state = { xIsNext: true, history: [ { square: Array(9).fill(null), coordinate: 0 } ], stepNumber: 0, order: false } 渲染按钮列表时判断order的值,并根据值渲染按钮列表和标题箭头 let order = this.state.order ? '▲' : '▼' const moves = history.map((step, index) => { const desc = index ? 'Go to move : ' + step.coordinate : 'Go to game start'; return ( <li key={index}> <button onClick={() => this.jumpTo(index)} className='btn'>{desc}</button> </li> ); }); if (this.state.order) { moves.reverse() } return ( <div className="game"> <div className="game-board"> <Board square={current.square} handleClick={this.handleClick.bind(this)} winnnerStep={winnerStep} reSetClick={this.reSetClick.bind(this)} /> </div> <div className="game-info"> <div onClick={this.changeOrder.bind(this)}> <div className='status'> {status} </div> <div className='order'> {order} </div> </div> <ul>{moves}</ul> </div> </div> );

效果图:

5.每当有人获胜时,高亮显示连成一线的 3 颗棋子

获胜时获取3连棋子的位置(step) 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]) { //返回获胜的value和三连棋子的位置 return { user: squares[a], step: [a, b, c] }; } } return null; } 将step传给子组件 let history = this.state.history; let current = history[this.state.stepNumber]; let winner = calculateWinner(current.square); //初始化获胜者获胜步的位置数组 let winnerStep = [] if (winner) { status = '获胜者:' + winner.user; //将连成一线的 3 颗棋子的位置放入数组 winnerStep = winner.step } <Board square={current.square} handleClick={this.handleClick.bind(this)} winnnerStep={winnerStep} reSetClick={this.reSetClick.bind(this)} /> 在循环渲染棋盘时判断当前棋子是否在3连棋子数组内 if (this.props.winnnerStep.indexOf(i * 3 + j) != -1) { light = true; } else { light = false; } str.push( <Square value={this.props.square[i * 3 + j]} key={i * 3 + j} onClick={() => this.props.handleClick(i * 3 + j)} light={light} /> )

效果图:

6.当无人获胜时,显示一个平局的消息

渲染时判断棋盘是否已满,如果已满则将status改为平局 //设置标量标记棋盘是否已满 let flag = true; //遍历判断棋盘是否已满 for (let i = 0; i < current.square.length; i++) { if (current.square[i] === null) { flag = false; } } //初始化状态信息(显示对局情况) let status; if (winner) { status = '获胜者:' + winner.user; //将连成一线的 3 颗棋子的位置放入数组 winnerStep = winner.step } else { if(flag){ status = '平局' }else{ status = "下一位选手: " + (this.state.xIsNext ? "X" : "O"); } }

效果图:

最新回复(0)