简介
本文将会基于React 实现五子棋小游戏,游戏规则为先让5颗棋子连成1线的一方获胜。
实现效果
技术实现
页面布局
<div><table style={{border: '1px solid #000', borderCollapse: 'collapse', backgroundColor: 'lightgray'}}><tbody>{squares.map((row, rowIndex) => {return <tr key={rowIndex}>{row.map((col, colIndex) => {return <td key={colIndex}style={{border: '1px solid #000', width: '30px', height: '30px'}}onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5){ // 左上if (rowIndex > 0){rowIndex--;}if (colIndex > 0){colIndex--;}}if (left < 0.5 && top > 0.5){ // 左下if (colIndex > 0){colIndex--;}}if (left > 0.5 && top < 0.5){ // 右上if (rowIndex > 0){rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);}}>{col === 'white' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'white',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div>: (col === 'black' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'black',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div> : col)}</td>})}</tr>})}</tbody></table></div>
本文用table实现游戏布局,棋盘大小为15 * 15,背景色为浅灰,棋子为白色或黑色。
由于棋子是位于交叉点上,需要将table cell 定位到右下角, right - 50%, bottom -50%,
下棋逻辑
onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5) { // 左上if (rowIndex > 0) {rowIndex--;}if (colIndex > 0) {colIndex--;}}if (left < 0.5 && top > 0.5) { // 左下if (colIndex > 0) {colIndex--;}}if (left > 0.5 && top < 0.5) { // 右上if (rowIndex > 0) {rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);
}
}
基于times控制是白方还是黑方,然后更新squares状态,棋盘会根据squares状态进行显示。
对于每个交叉点,棋子是以交叉点为右下角的table cell。
计算点击位置位于table cell的哪个区域,然后计算交叉点对应的table cell。
左上 -> r-- c--
右上 -> r-- c
左下 -> r c--
右下 -> r ,c
获胜逻辑判断
useEffect(()=>{// 计算dp的值for (let i = 0; i < 15; i++) {for (let j = 0; j < 15; j++) {if (squares[i][j] === '') {continue;}// 判断左边if (j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断上边if (i >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右边if (j <= 10){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断下边if (i <= 10){let num = 1;for (let k = 1; k < 5; k++){if (squares[i + k][j] === squares[i][j]) {num++;} else {break;}}console.log(i + ',' + j + ':'+ num)if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断左上if (i >= 5 && j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右上if (i <= 10 && j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断左下if (i >= 5 && j <= 10){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右下if (i <= 10 && j <= 10){let num = 1;for (let k = 1; k < 5; k++){if (squares[i + k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}}}}, [squares])
判断每个棋子上下左右,左上,右上,左下,右下是否有连续5个棋子。
整体代码
const Board = () => {/*** 创建一个 15 * 15 的棋盘*/const initSquares = Array.from({length: 15},() => new Array(15).fill(''));const [squares, setSquares] = useState(initSquares);const [times, setTimes] = useState(0);useEffect(() => {// 计算dp的值for (let i = 0; i < 15; i++) {for (let j = 0; j < 15; j++) {if (squares[i][j] === '') {continue;}// 判断左边if (j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断上边if (i >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右边if (j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断下边if (i <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断左上if (i >= 5 && j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右上if (i <= 10 && j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断左下if (i >= 5 && j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右下if (i <= 10 && j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}}}}, [squares])return <div><table style={{border: '1px solid #000', borderCollapse: 'collapse', backgroundColor: 'lightgray'}}><tbody>{squares.map((row, rowIndex) => {return <tr key={rowIndex}>{row.map((col, colIndex) => {return <td key={colIndex}style={{border: '1px solid #000', width: '30px', height: '30px'}}onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5) { // 左上if (rowIndex > 0) {rowIndex--;}if (colIndex > 0) {colIndex--;}}if (left < 0.5 && top > 0.5) { // 左下if (colIndex > 0) {colIndex--;}}if (left > 0.5 && top < 0.5) { // 右上if (rowIndex > 0) {rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);}}>{col === 'white' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'white',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div>: (col === 'black' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'black',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div> : col)}</td>})}</tr>})}</tbody></table></div>
}