react实现狼吃羊游戏

基础规则

https://blog.csdn.net/zslngu/article/details/115743943

效果

录屏2026-03-14 21.36.50-狼吃羊-reac

代码

ts 复制代码
import { Role } from "@/base/MyEnums.ts"
import WolfGoatRole from "@/components/WolfGoatRole.tsx"
import { useState } from "react"

export default function WolfGoatPage() {
  const row = 5
  const col = 5

  const [matrix, setMatrix] = useState([
    ["0", Role.WOLF, Role.WOLF, Role.WOLF, "0"],
    ["0", "0", "0", "0", "0"],
    [Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT],
    [Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT],
    [Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT, Role.GOAT],
  ])

  const eq = (a, b) => {
    return a[0] == b[0] && a[1] == b[1]
  }

  const hasPieceSelect = () => {
    return !eq(currClick, [-1, -1])
  }

  enum Status {
    WAIT_SELECT_PIECE,
    HAS_SELECT_PIECE,
  }

  const calStatus = () => {
    if (hasPieceSelect()) {
      return Status.HAS_SELECT_PIECE
    } else {
      return Status.WAIT_SELECT_PIECE
    }
  }

  // 选中的棋子
  const [currClick, setCurrClick] = useState([-1, -1])

  const fetchRole = ([a, b]: number[]) : Role | string => {
    return matrix[a][b]
  }

  const bgColor = (i, j) => {
    if (fetchRole([i, j]) == Role.WOLF) {
      return "bg-red-300"
    } else if (fetchRole([i, j]) == Role.GOAT) {
      return "bg-green-300"
    }
  }

  interface HistoryVO {
    targetPos: number[]
    role: Role | string,
    currentPos: number[]
    msg: string
  }

  const [history, setHistory] = useState<Array<HistoryVO>>([])

  const canNormalStep = ([i, j]: number[], [a, b]: number[]) => {
    const res =
      Math.abs(i - a) + Math.abs(j - b) == 1 && fetchRole([a, b]) == "0"
    console.log(`judge can normal return ${res} ${[i, j]} ${[a, b]} ${fetchRole([a, b])}`)
    return res
  }

  const copyMatrix = (param: Array<Array<number | Role | string>>) => {
    return matrix.map((arr, i) => {
      return arr.map((value, j) => {
        let idx = 0
        for (idx = 0; idx < param.length; idx++) {
          const p = param[idx]
          if (eq([i, j], [p[0], p[1]])) {
            return p[2].toString()
          }
        }
        return value
      })
    })
  }

  const canEat = ([i, j]: number[], [a, b]: number[]) => {
    const roleCan =
      fetchRole([i, j]) == Role.WOLF && fetchRole([a, b]) == Role.GOAT
    const postCan =
      (i == a && Math.abs(j - b) == 2) || (Math.abs(i - a) == 2 && j == b)
    return roleCan && postCan
  }

  //
  // 状态: 待选棋子  选棋子操作
  // 状体;已选棋子
  //       选一样的 取消选中
  //       走棋 同时判断规则
  const onClickCell = (i, j, e) => {
    // 待选棋子
    const status = calStatus()

    if (status == Status.WAIT_SELECT_PIECE) {
      if (fetchRole([i, j]) == '0') {
        return
      }
      setCurrClick([i, j])
      return
    }

    // 已选了棋子
    // 取消
    // 重选
    // 走路
    // 吃子
    else if (status == Status.HAS_SELECT_PIECE) {
      if (eq(currClick, [i, j])) {
        setCurrClick([-1, -1])
        return
      } else if (fetchRole(currClick) == fetchRole([i, j])) {
        setCurrClick([i, j])
        return
      }
      else if (canNormalStep(currClick, [i, j])) {
        setHistory([{
          targetPos: [i, j],
          currentPos: currClick,
          role: fetchRole(currClick),
          msg: `${fetchRole(currClick)} 从 (${currClick[0]}, ${currClick[1]}) 移动到 (${i}, ${j})`
        }, ...history])
        const a = Array<Array<number | Role | string>>([])
        a.push([i, j, fetchRole(currClick)])
        a.push([currClick[0], currClick[1], "0"])
        setMatrix(copyMatrix(a))
        setCurrClick([-1, -1])
        return
      } else if (canEat(currClick, [i, j])) {
        setHistory([
          {
            targetPos: [i, j],
            currentPos: currClick,
            role: fetchRole(currClick),
            msg: `${fetchRole(currClick)} 从 (${currClick[0]}, ${currClick[1]}) 吃掉了 (${i}, ${j}) 的 ${fetchRole([i, j])}`,
          },
          ...history,
        ])
        const a = Array<Array<number | Role | string>>([])
        a.push([i, j, fetchRole(currClick)])
        a.push([currClick[0], currClick[1], "0"])
        setMatrix(copyMatrix(a))
        setCurrClick([-1, -1])
      }
    }
  }

  return (
    <div className="flex-col items-center" style={{ paddingTop: "40px" }}>
      <div>当前选中棋子 {currClick}</div>
      <div>
        {
          history
            .map((value, idx, arr) => {
              if (idx < 3) {
                return (<p>
                  第{arr.length - idx}手 {value.msg}
                </p>)
              } else {
                return (<></>)
              }
            })
        }
      </div>
      <table className="w-full table-auto border-collapse">
        <tbody>
          {Array.from({ length: row }, (_, i) => (
            <tr key={i}>
              {Array.from({ length: col }, (_, j) => (
                <td
                  key={j}
                  className={
                    "border px-10 py-15" +
                    (i == currClick[0] && j == currClick[1]
                      ? " " + bgColor(i, j)
                      : "")
                  }
                  onClick={(e) => onClickCell(i, j, onClickCell)}
                >
                  {matrix[i][j] === Role.WOLF ? (
                    <WolfGoatRole role={Role.WOLF} />
                  ) : (
                    <></>
                  )}

                  {matrix[i][j] === Role.GOAT ? (
                    <WolfGoatRole role={Role.GOAT} />
                  ) : (
                    <></>
                  )}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}
相关推荐
这是个栗子8 小时前
TypeScript(三)
前端·javascript·typescript·react
前端精髓10 小时前
移除 Effect 依赖
前端·javascript·react.js
lpfasd12312 小时前
TypeScript + Cloudflare 全家桶部署项目全流程
前端·javascript·typescript
前端Hardy12 小时前
字节/腾讯内部流出!Claude Code 2026王炸玩法!效率暴涨10倍
前端·javascript·vue.js
世人万千丶12 小时前
Flutter 框架跨平台鸿蒙开发 - 鸿蒙版本五子棋游戏应用
学习·flutter·游戏·华为·harmonyos·鸿蒙
前端Hardy12 小时前
大厂都在偷偷用的 Cursor Rules 封装!告别重复 Prompt,AI 编程效率翻倍
前端·javascript·面试
kyriewen12 小时前
Vite:比Webpack快100倍的“闪电侠”,原理竟然这么简单?
前端·javascript·vite
竹林81812 小时前
RainbowKit快速集成多链钱包连接:从“连不上”到丝滑切换的踩坑实录
前端·javascript
前端Hardy12 小时前
Cursor Rules 完全指南(2026 最新版)
前端·javascript·面试
牛奶13 小时前
浏览器是怎么把代码变成页面的?
前端·javascript·chrome