React练习-井字棋游戏

官网地址:https://react.docschina.org/learn/tutorial-tic-tac-toe
自己做小练习时没有看官方的解题,还是蛮有成就感的hhhhh~(官方的做法是把可以胜利的情况都列出来,但是感觉我这个方法似乎扩展性更强一些 ~~~/狗头)

import { useState } from "react"
// 格子面板
function Board ({ allData, onChangeSquare }) {
  return (
    <div className={'board'} style={{ width: '110px' }}>
      {allData.map((val, index) => <button key={index} className="square" onClick={() => onChangeSquare(index)}>{val}</button>)}
    </div>
  );
}
// 记录面板
function History ({ historyList, onChangeHistory }) {
  return (
    <div>
      {
        historyList.map((history, index) => {
          return (
            <div key={index} onClick={() => onChangeHistory(index)}>
              <span style={{ marginLeft: '70px' }}></span>{index + 1}.<button>Go to {index ? `move #${index}` : 'game start'}</button>
            </div>
          )
        })
      }
    </div>
  );
}
// 整体面板
export default function All () {
  const [res, setRes] = useState(Array(9).fill(''))
  const [nextFlag, setNextFlag] = useState('X')
  const [winner, setWinner] = useState('')
  const [curStep, setCurStep] = useState(0)
  const [historyList, setHistoryList] = useState([res])
  // 判断是否胜利
  const judgeWinner = (history) => {
    setWinner('')
    if ((history[0] === history[1] && history[0] === history[2]) || (history[0] === history[3] && history[0] === history[6])) {
      setWinner(history[0])
    } else if ((history[8] === history[7] && history[8] === history[6]) || (history[8] === history[5] && history[8] === history[2])) {
      setWinner(history[8])
    } else {
      // 以中心判断,同样间距的下标相加=9则表示在一条线上
      const winnerFlag = history[4]
      const indexList = history.map((val, index) => val === winnerFlag ? index : -1).filter(index => index !== -1)
      for (i = 0; indexList[i] < 4; i++) {
        if (history[indexList[i]] === winnerFlag && history[8 - indexList[i]] === winnerFlag) {
          setWinner(winnerFlag)
        }
      }
    }
  }
  // 更新面板
  const updateBoard = (newVal) => {
    setRes(newVal)
    // 判断是否获胜
    judgeWinner(newVal)
  }
  // 点击方块
  const onChangeSquare = (index) => {
    // 此处有值或已成功则不改变
    if (res[index] || winner) {
      return
    }
    // 更新对应位置
    const history = [...res]
    history.splice(index, 1, nextFlag)
    if (curStep && curStep !== historyList.length - 1) {
      // 从某一条记录进入后,点击方块则清除该条之后的记录
      historyList.splice(curStep + 1)
    }
    // 添加记录
    setHistoryList([...historyList, history])
    // 更新面板
    updateBoard(history)
    // 判断下一步的标记
    setNextFlag(nextFlag === 'X' ? 'O' : 'X')
    // 每次点击方块后均初始化当前记录下标
    setCurStep(0)
  }
  // 点击某条记录
  const onChangeHistory = (index) => {
    // 保存当前点击的记录下标
    setCurStep(index)
    if (!index) {
      // 重新开始时,初始化面板/记录/获胜方
      updateBoard(Array(9).fill(''))
      setHistoryList([res])
      setNextFlag('X')
    } else {
      // 跳转至某条记录时,更新面板并重新记录获胜方
      updateBoard(historyList[index])
      setNextFlag(index % 2 ? 'O' : 'X')
    }
  }
  return (
    <>
      <p>{winner && !(curStep && curStep !== historyList.length - 1) ? `Winner:${winner}` : `Next Player:${nextFlag}`}</p>
      <div style={{ display: 'flex' }}>
        <Board allData={res} onChangeSquare={onChangeSquare} />
        <History historyList={historyList} onChangeHistory={onChangeHistory} />
      </div>
    </>
  );
}

未胜利界面
多条记录且胜利界面

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值