+++
date = '2025-12-16T16:46:30+08:00'
draft = false
title = '计算概论A 2025年秋 大作业（更新）'
tags = ['计算概论']
license = 'MIT Licence'
description = '供计算概论A助教验收'
slug = '007'
+++
## 文件下载
已编译的二进制文件：[oasa25](oasa25)（需要先`chmod +x oasa25`才能运行）  
Swift源代码：[oasa25.swift](oasa25.swift)  
Botzone简单交互：[oasa25.cpp](oasa25.cpp)
> 注意：请使用macOS 14.0及以上版本运行上面的二进制文件。
## 基本信息
- 程序名称：oasa25；
- 编写语言：Swift 5；
- 编译环境：
  - IDE：Xcode Version 26.0.1 (17A400)；
  - macOS：Tahoe 26.0.1 (25A362)；
  - 设备：MacBook Air (13英寸, M3, 2024年)；
- 借助AI：DeepSeek；
  - 主要用途：
    - 在原来代码的基础上进行优化；
    - 讲解Swift语法；
    - 修复语法错误；
    - 协助给程序取名；
      - "oasa"(“おアサ”)是“大きいアサインメント”（“大作业”）的缩略；
      - "25"是我的数学分析I期中考试成绩。
- 程序特色：
  - 使用上：
    - 友好的GUI界面；
      - 包含分栏显示；
      - 很多图标；
    - 二次确认，防止误操作；
    - 完善的存档保存/另存为/读取功能；
    - 清晰的状态显示；
    - 支持历史记录查看；
    - 合法路径绿色高亮显示，被阻挡的路径红色高亮显示；
    - 支持自由调节蒙特卡洛算法评分参数；
    - 终端输出蒙特卡洛方法日志；
  - 技术上：
    - 使用SwiftUI的现成控件实现GUI界面，减少所需代码；
    - 使用多个状态变量，状态划分清晰；
    - 代码分成不同`struct`，分工实现不同功能；
    - 变量名、函数名等清晰易读（感谢DeepSeek）；
    - 并行处理，提高运行效率；
    - 实现蒙特卡洛算法；
    - 缺点：不管怎么调，算法都有点傻傻的，只比随机算法好一点点，在Botzone上被打得毫无还手之力（期末了😭难debug）。
## 编译方法
```bash
swiftc -parse-as-library <swift源代码路径> -o oasa25
```
由于使用SwiftUI，需要使用macOS编译，如果助教老师使用的不是macOS，要不线下使用我的电脑验收吧。
## 程序结构（详见源代码）
- `import`部分：
  - `Combine`：用于实现`ObservableObject`类型的`GameState`；
  - `SwiftUI`：提供程序的UI控件；
  - `UniformTypeIdentifiers`：用于存取文件时对文件类型的规定；
- `enum GamePhase:`：设定三个游戏阶段的值；
  - 选择棋子阶段`selectPiece`；
  - 移动棋子阶段`movePiece`；
  - 放置障碍物阶段`placeArrow`；
- `struct HistoryEntry`：历史记录数据结构；
  - id+字符串；
- `struct gameData`：规定游戏存档的结构；
  - 棋盘数据`chessBoard`（二维数组）；
  - 回合数据`blackRound`（Bool值）；
  - 历史记录数据`history`（HistoryEntry数组）；
  - 选中棋子位置`selectedPieceRow`和`selectedPieceCol`（整数）；
  - 阶段数据`gamePhase`；
  - 上次计算的可用路径`availableMoves`（二维数组）；
    - 读档后无需再次计算；
  - 游戏结束状态`isGameOver`（Bool值）；
  - 自动下棋选项`blackStrategy`和`whiteStrategy`（字符串）；
  - 当前回合数`roundNum`（整数）；
  - 蒙特卡洛方法计算结果`mcList`（数组）；
  - 控制面板参数值`blackControlFactor`、`blackSafetyFactor`、`blackSurroundFactor`、`blackCenterFactor`、`whiteControlFactor`、`whiteSafetyFactor`、`whiteSurroundFactor`和`whiteCenterFactor`（均为双精度浮点类型）；
- `class GameState`：游戏状态环境对象
  - 声明并初始化各个状态变量（详细列表如上）；
- `struct GameManager`：游戏逻辑管理器；
  - `func saveGame`：借助Swift的`NSSavePanel()`功能进行存档的保存（同时会调用自定义的`saveChessBoardToJSON()`）；
  - `func loadGame`：借助Swift的`NSOpenPanel()`功能进行存档的读取（同时会调用自定义的`loadChessBoardFromJSON()`）；
  - `func saveChessBoardToJSON`：借助Swift的`JSONEncoder()`，保存当前棋局为`.json`存档；
  - `func loadChessBoardFromJSON`：借助Swift的`JSONDecoder()`，从`.json`存档中读取各个变量，还原棋局；
  - `func initializeChessBoard`：新建游戏时的棋盘初始化；
  - `func addHistory`：将最近一步操作插入到`history`字符串组的索引`0`位置的一个简单函数；
  - `func calculateAvailableMoves`：计算可移动的位置，用于给设定按钮状态和染色提供数据；
  - `func getAllAvailablePieces`：获取所有可用的棋子位置，用于设定按钮状态；
  - `func getAllCounterPieces`：除了参数相反，和上面函数功能相同；
  - `func getAllAvailableMoves`：获取所有可移动/可放置障碍物的位置，染色；
  - `func checkWinCondition`和`func checkAndHandleWinCondition`：检查胜利；
  - `func handleSquareTap`：处理格子点击；
  - 蒙特卡洛算法实现部分
    - `private func generateMcListForCurrentTurn`：更新`mcList`状态变量
    - `private func findBestMoveWithMonteCarlo`：通过给定第一行动回合+往下随机8个回合（并重走30次）计算平均得分，为上述函数提供计算结果；
    - `private func getAllPossibleMoves`：模拟完整的一个回合的可行操作方案；
    - `private func simulateRandomGame`：模拟完整一个回合的随机下棋过程；
    - `private func getRandomMove`：随机选取一个回合的下棋方案（返回值：`piece: (Int, Int), target: (Int, Int), arrow: (Int, Int)`）
    - `private func isGameOver`：检查游戏是否结束（模拟下棋版）；
    - `private func calculateFinalScore`：
      - 若中途获胜：返回`1.0`或`-1.0`；
      - 否则使用下面函数的返回值；
    - `private func generateRandomMcList`：生成随机走法列表（备用）；
      - 因为Swift要防止使用空值，所以需要一个这样的函数来保证每种情况都有值，实际上不会被调用；
    - `private func evaluateBoard`：3项评估分数求和+归一化到[-1,1]；
    - `private func calculateSimuMoves`：计算可以移动的方案（不修改GameState版）；
    - `private func mcSelect`：基于已生成的`mcList`执行选择；
    - `private func mcMove`：基于已生成的`mcList`执行移动；
    - `private func mcPlace`：基于已生成的`mcList`放置障碍物；
  - 综合的自动下棋：`func autoOperate`
  - 随机下棋：
    - `private func randomSelect`；
    - `private func randomMove`；
    - `private func randomPlace`；
  - 局势评估算法：
    - `private func controlScore`：控制分=倍率*所有棋子可移动格子数目之和；
    - `private func safetyScore`：安全分=每个棋子求和：
      - +倍率*距离边界的最小距离；
        - 若被完全包围：-包围惩罚；
        - 否则：+倍率*所有棋子可移动格子数目之和；（怎么感觉和上面控制分有点重复）
    - `private func centerScore`：中心分=每个棋子求和：
      - 若在中心：+倍率*5；
      - +(10-离中心点距离)；
- `struct ChessSquareView`：棋盘格子视图；
  - 实现单个棋盘格子的显示；
  - `func getSquareColor`：染色；
  - `func isInteractive`：设定按钮状态；
- `struct ChessBoardRowView`：棋盘行视图；
  - 实现棋盘单行的显示；
  - 调用`struct ChessSquareView`；
- `struct ChessBoardView`：棋盘视图；
  - 实现棋盘的完整显示；
  - 调用`struct ChessBoardRowView`；
- `struct MenuButtonView`：菜单按钮视图；
  - 作为定义主菜单按钮的模板，避免代码的大量重复；
- `struct SidebarView`：左侧栏视图；
  - `var gameInfoView`：显示当前存档路径、当前回合、当前阶段、历史操作；
  - `var phaseDescription`：把阶段的代号转换成文字描述；
  - `var welcomeView`：显示第一局游戏开始前的引导信息；
- `struct ParameterSliderView`：负责单一滑块组件；
- `struct ParameterSettingsView`：生成完整滑块界面；
- `struct RightView`：右侧栏视图；
  - 自动下棋控制面板，支持调节参数；
- `struct MainMenuView`：主菜单视图；
  - 实现主菜单的显示；
  - 多次调用`struct MenuButtonView`，实现各个菜单按钮；
  - 依据状态变量的不同，显示不同的按钮；
- `struct ContentView`：主视图。
  - 划分窗口结构：
    - 调用`SidebarView`，实现左侧栏的状态显示；
    - 调用`MainMenuView`，实现左下角的菜单控制；
    - 调用`ChessBoardView`，实现右侧的棋盘显示。
    - 调用`RightView`；
  - 定义函数`private func setWindowTitle`
    - 使用`.onAppear`在程序启动时设置窗口标题。

## 源代码展示
下载[oasa25.swift](oasa25.swift)。
```swift
import Combine
import SwiftUI
import UniformTypeIdentifiers
// MARK: - 游戏阶段枚举
enum GamePhase: Int, Codable {
    case selectPiece = 0
    case movePiece = 1
    case placeArrow = 2
}

// MARK: - 历史记录条目（修复版）
struct HistoryEntry: Identifiable, Codable, Hashable {
    let id: UUID
    let action: String

    init(action: String) {
        self.id = UUID()
        self.action = action
    }

    // 为了兼容现有代码的描述
    var description: String { action }
}

// MARK: - 存档文件结构（修复版）
struct GameData: Codable {
    let chessBoard: [[Int]]
    let blackRound: Bool
    let history: [HistoryEntry]  // 修改为HistoryEntry数组
    let selectedPieceRow: Int?
    let selectedPieceCol: Int?
    let gamePhase: GamePhase
    let availableMoves: [[Int]]
    let isGameOver: Bool  // 新增：游戏结束状态
    let blackStrategy: String
    let whiteStrategy: String
    let roundNum: Int
    let mcList: [Int]
    let blackControlFactor: Double
    let blackSafetyFactor: Double
    let blackSurroundFactor: Double
    let blackCenterFactor: Double
    let whiteControlFactor: Double
    let whiteSafetyFactor: Double
    let whiteSurroundFactor: Double
    let whiteCenterFactor: Double
}

// MARK: - 游戏状态环境对象
class GameState: ObservableObject {
    @Published var withFile = false
    @Published var chessBoard = Array(
        repeating: Array(repeating: 0, count: 8),
        count: 8
    )
    @Published var currentUrl: String = ""
    @Published var unsavable = true
    @Published var isPlaying = false
    @Published var blackRound = true
    @Published var history: [HistoryEntry] = []  // 修改为HistoryEntry数组
    @Published var availableMoves: [[Int]] = Array(
        repeating: Array(repeating: 0, count: 8),
        count: 8
    )
    @Published var selectedPiece: (Int, Int)? = nil
    @Published var gamePhase: GamePhase = .selectPiece
    @Published var whiteAutoOperate = false
    @Published var blackAutoOperate = false
    @Published var isGameOver: Bool = false  // 新增：游戏结束状态
    @Published var blackStrategy: String = "纯随机下棋"
    @Published var whiteStrategy: String = "纯随机下棋"
    @Published var roundNum: Int = 0
    @Published var mcList: [Int] = []  // 结构：[当前回合数,是否为黑方行动,选择棋子行坐标,选择棋子列坐标,移动棋子行坐标,移动棋子列坐标,放置障碍物行坐标,放置障碍物列坐标]
    @Published var blackControlFactor: Double = 1
    @Published var blackSafetyFactor: Double = 1
    @Published var blackSurroundFactor: Double = 10
    @Published var blackCenterFactor: Double = 1
    @Published var whiteControlFactor: Double = 1
    @Published var whiteSafetyFactor: Double = 1
    @Published var whiteSurroundFactor: Double = 10
    @Published var whiteCenterFactor: Double = 1
}

// MARK: - 游戏逻辑管理器
struct GameManager {
    let gameState: GameState

    // MARK: - 文件操作与游戏初始化
    func saveGame() {
        let savePanel = NSSavePanel()
        savePanel.title = "保存游戏"
        savePanel.message = "选择保存游戏的目录和文件名"
        savePanel.allowedContentTypes = [UTType.json]
        savePanel.nameFieldStringValue = "新游戏.json"
        savePanel.allowsOtherFileTypes = false

        savePanel.begin { response in
            if response == .OK, let url = savePanel.url {
                self.gameState.currentUrl = url.path
                if self.saveChessBoardToJSON(url: url) {
                    self.gameState.unsavable = true
                    self.gameState.withFile = true
                }
            }
        }
    }

    func loadGame() {
        let openPanel = NSOpenPanel()
        openPanel.title = "读取游戏"
        openPanel.message = "选择游戏存档文件"
        openPanel.allowedContentTypes = [UTType.json]
        openPanel.allowsMultipleSelection = false

        openPanel.begin { response in
            if response == .OK, let url = openPanel.url {
                if self.loadChessBoardFromJSON(url: url) {
                    self.gameState.currentUrl = url.path
                    self.gameState.withFile = true
                    self.gameState.isPlaying = true
                    self.gameState.unsavable = true
                }
            }
        }
    }

    func saveChessBoardToJSON(url: URL) -> Bool {
        do {
            let encoder = JSONEncoder()
            // 使用新的GameData结构
            let gameData = GameData(
                chessBoard: gameState.chessBoard,
                blackRound: gameState.blackRound,
                history: gameState.history,  // 现在类型匹配了
                selectedPieceRow: gameState.selectedPiece?.0,
                selectedPieceCol: gameState.selectedPiece?.1,
                gamePhase: gameState.gamePhase,
                availableMoves: gameState.availableMoves,
                isGameOver: gameState.isGameOver,  // 新增：保存游戏结束状态
                blackStrategy: gameState.blackStrategy,
                whiteStrategy: gameState.whiteStrategy,
                roundNum: gameState.roundNum,
                mcList: gameState.mcList,
                blackControlFactor: gameState.blackControlFactor,
                blackSafetyFactor: gameState.blackSafetyFactor,
                blackSurroundFactor: gameState.blackSurroundFactor,
                blackCenterFactor: gameState.blackSafetyFactor,
                whiteControlFactor: gameState.whiteControlFactor,
                whiteSafetyFactor: gameState.whiteSafetyFactor,
                whiteSurroundFactor: gameState.whiteSurroundFactor,
                whiteCenterFactor: gameState.whiteCenterFactor
            )
            let data = try encoder.encode(gameData)
            try data.write(to: url)
            return true
        } catch {
            print("保存JSON失败: \(error)")
            return false
        }
    }

    func loadChessBoardFromJSON(url: URL) -> Bool {
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let loadedData = try decoder.decode(GameData.self, from: data)

            gameState.chessBoard = loadedData.chessBoard
            gameState.blackRound = loadedData.blackRound
            gameState.history = loadedData.history  // 现在类型匹配了

            if let row = loadedData.selectedPieceRow,
                let col = loadedData.selectedPieceCol
            {
                gameState.selectedPiece = (row, col)
            } else {
                gameState.selectedPiece = nil
            }

            gameState.gamePhase = loadedData.gamePhase
            gameState.availableMoves = loadedData.availableMoves
            gameState.isGameOver = loadedData.isGameOver  // 新增：加载游戏结束状态

            return true
        } catch {
            print("读取JSON失败: \(error)")
            return false
        }
    }
    func initializeChessBoard() {
        gameState.history = []
        gameState.selectedPiece = nil
        gameState.gamePhase = .selectPiece
        gameState.availableMoves = Array(
            repeating: Array(repeating: 0, count: 8),
            count: 8
        )
        gameState.chessBoard = Array(
            repeating: Array(repeating: 0, count: 8),
            count: 8
        )
        gameState.isGameOver = false  // 新增：重置游戏结束状态
        gameState.blackAutoOperate = false  // 重置自动下棋
        gameState.whiteAutoOperate = false  // 重置自动下棋

        // 设置初始棋子位置（根据Amazon棋规则）
        // 1黑方Amazon 2白方Amazon 3黑方障碍物 4白方障碍物
        gameState.chessBoard[0][2] = 1
        gameState.chessBoard[0][5] = 1
        gameState.chessBoard[2][0] = 1
        gameState.chessBoard[2][7] = 1
        gameState.chessBoard[7][2] = 2
        gameState.chessBoard[7][5] = 2
        gameState.chessBoard[5][0] = 2
        gameState.chessBoard[5][7] = 2
    }

    func addHistory(_ action: String) {
        let player = gameState.blackRound ? "黑方" : "白方"
        let historyEntry = HistoryEntry(
            action: "\(player) 第\(gameState.roundNum)回合 \(action)"
        )
        gameState.history.insert(historyEntry, at: 0)
    }
    // MARK: - 算法部分
    func calculateAvailableMoves(from position: (Int, Int)) {
        gameState.availableMoves = Array(
            repeating: Array(repeating: 0, count: 8),
            count: 8
        )
        let (row, col) = position
        let directions = [
            (-1, -1), (-1, 0), (-1, 1),
            (0, -1), (0, 1),
            (1, -1), (1, 0), (1, 1),
        ]

        for direction in directions {
            var currentRow = row + direction.0
            var currentCol = col + direction.1
            var foundObstacle = false

            while currentRow >= 0 && currentRow < 8 && currentCol >= 0
                && currentCol < 8
            {
                if !foundObstacle {
                    if gameState.chessBoard[currentRow][currentCol] != 0 {
                        foundObstacle = true
                        gameState.availableMoves[currentRow][currentCol] = 2
                    } else {
                        gameState.availableMoves[currentRow][currentCol] = 1
                    }
                } else {
                    gameState.availableMoves[currentRow][currentCol] = 2
                }
                currentRow += direction.0
                currentCol += direction.1
            }
        }
    }

    func getAllAvailablePieces() -> [(Int, Int)] {
        let currentPlayer = gameState.blackRound ? 1 : 2
        var availablePieces: [(Int, Int)] = []

        for row in 0..<8 {
            for col in 0..<8 {
                if gameState.chessBoard[row][col] == currentPlayer {
                    availablePieces.append((row, col))
                }
            }
        }
        return availablePieces
    }

    func getAllCounterPieces() -> [(Int, Int)] {
        let currentPlayer = gameState.blackRound ? 2 : 1
        var availablePieces: [(Int, Int)] = []

        for row in 0..<8 {
            for col in 0..<8 {
                if gameState.chessBoard[row][col] == currentPlayer {
                    availablePieces.append((row, col))
                }
            }
        }
        return availablePieces
    }

    func getAllAvailableMoves() -> [(Int, Int)] {
        var availableMovesList: [(Int, Int)] = []

        for row in 0..<8 {
            for col in 0..<8 {
                if gameState.availableMoves[row][col] == 1 {
                    availableMovesList.append((row, col))
                }
            }
        }
        return availableMovesList
    }

    func checkWinCondition() -> (isGameOver: Bool, winner: String?) {
        // 如果游戏已经结束，直接返回结果
        if gameState.isGameOver {
            let winner = gameState.blackRound ? "黑方" : "白方"
            return (true, winner)
        }

        // 获取对方所有棋子
        let opponentPieces = getAllCounterPieces()

        // 检查对方是否有任何一个棋子能移动
        for piece in opponentPieces {
            // 计算该棋子的可移动位置
            calculateAvailableMoves(from: piece)
            let availableMoves = getAllAvailableMoves()

            // 如果有任何可移动位置，游戏继续
            if !availableMoves.isEmpty {
                return (false, nil)
            }
        }

        // 对方所有棋子都无法移动，当前玩家获胜！
        let winner = gameState.blackRound ? "黑方" : "白方"
        return (true, winner)
    }
    func checkAndHandleWinCondition() -> Bool {
        let winResult = checkWinCondition()

        if winResult.isGameOver && !gameState.isGameOver {
            // 设置游戏结束状态
            gameState.isGameOver = true

            // 显示胜利消息
            addHistory("获胜")
            // 关闭双方的自动下棋
            gameState.blackAutoOperate = false
            gameState.whiteAutoOperate = false
            return true
        }
        return false
    }
    func handleSquareTap(row: Int, column: Int) {// 处理点击事件
        // 如果游戏已结束，禁止操作
        if gameState.isGameOver {
            return
        }

        let currentPlayer = gameState.blackRound ? 1 : 2
        if (gameState.blackRound && gameState.blackAutoOperate)
            || (!gameState.blackRound && gameState.whiteAutoOperate)
        {
            return
        }

        switch gameState.gamePhase {
        case .selectPiece:
            if gameState.blackRound {
                gameState.roundNum += 1
            }
            if gameState.chessBoard[row][column] == currentPlayer {
                gameState.selectedPiece = (row, column)
                calculateAvailableMoves(from: (row, column))
                gameState.gamePhase = .movePiece
                addHistory("选择了棋子 (\(row),\(column))")
            }

        case .movePiece:
            guard let selected = gameState.selectedPiece else { return }
            if gameState.availableMoves[row][column] == 1 {
                gameState.chessBoard[row][column] =
                    gameState.chessBoard[selected.0][selected.1]
                gameState.chessBoard[selected.0][selected.1] = 0
                gameState.selectedPiece = (row, column)

                calculateAvailableMoves(from: (row, column))
                gameState.gamePhase = .placeArrow
                addHistory("移动棋子到 (\(row),\(column))")

                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    if self.gameState.blackRound
                        && self.gameState.blackAutoOperate
                    {
                        self.autoOperate(player: true)
                    } else if !self.gameState.blackRound
                        && self.gameState.whiteAutoOperate
                    {
                        self.autoOperate(player: false)
                    }
                }
            } else if gameState.chessBoard[row][column] == currentPlayer {
                gameState.selectedPiece = (row, column)
                calculateAvailableMoves(from: (row, column))
                addHistory("重新选择了棋子 (\(row),\(column))")
            }

        case .placeArrow:
            if gameState.availableMoves[row][column] == 1 {
                gameState.chessBoard[row][column] = gameState.blackRound ? 3 : 4

                addHistory("放置障碍物在 (\(row),\(column))")

                // 🎯 在放置障碍物后立即检查胜利条件
                let isGameOver = checkAndHandleWinCondition()

                if isGameOver {
                    // 游戏结束，清理状态但不切换回合
                    gameState.selectedPiece = nil
                    gameState.availableMoves = Array(
                        repeating: Array(repeating: 0, count: 8),
                        count: 8
                    )
                    gameState.gamePhase = .selectPiece
                    gameState.unsavable = false
                    // 注意：这里不切换 blackRound，因为游戏已结束
                } else {
                    // 游戏继续，正常切换回合
                    gameState.selectedPiece = nil
                    gameState.availableMoves = Array(
                        repeating: Array(repeating: 0, count: 8),
                        count: 8
                    )
                    gameState.gamePhase = .selectPiece
                    gameState.blackRound.toggle()
                    gameState.unsavable = false

                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        if self.gameState.blackRound
                            && self.gameState.blackAutoOperate
                        {
                            self.autoOperate(player: true)
                        } else if !self.gameState.blackRound
                            && self.gameState.whiteAutoOperate
                        {
                            self.autoOperate(player: false)
                        }
                    }
                }
            }
        }
    }
    // MARK: - 蒙特卡洛算法实现
    private func generateMcListForCurrentTurn() {
        let currentBoard = gameState.chessBoard
        let isBlack = gameState.blackRound

        // 清空之前的列表
        gameState.mcList = []

        // 添加回合数和玩家信息
        gameState.mcList.append(gameState.roundNum)
        gameState.mcList.append(isBlack ? 1 : 0)  // 1:黑方, 0:白方

        // 生成最佳走法
        if let bestMove = findBestMoveWithMonteCarlo(
            board: currentBoard,
            isBlack: isBlack
        ) {
            // 添加走法到列表
            gameState.mcList.append(bestMove.piece.0)
            gameState.mcList.append(bestMove.piece.1)
            gameState.mcList.append(bestMove.target.0)
            gameState.mcList.append(bestMove.target.1)
            gameState.mcList.append(bestMove.arrow.0)
            gameState.mcList.append(bestMove.arrow.1)

            print(
                "\(isBlack ? "黑" : "白")方蒙特卡洛生成走法: 棋子(\(bestMove.piece)) -> 移动(\(bestMove.target)) -> 箭(\(bestMove.arrow))"
            )
        } else {
            // 如果没有找到走法，使用随机走法
            generateRandomMcList()
        }
    }
    private func findBestMoveWithMonteCarlo(board: [[Int]], isBlack: Bool) -> (
        piece: (Int, Int), target: (Int, Int), arrow: (Int, Int)
    )? {
        let allMoves = getAllPossibleMoves(board: board, isBlack: isBlack)

        if allMoves.isEmpty {
            return nil
        }

        var bestMove:
            (piece: (Int, Int), target: (Int, Int), arrow: (Int, Int))?
        var bestScore: Double = -Double.infinity
        let simulationsPerMove = 30  // 每个走法的模拟次数

        // 并行处理以提高速度
        DispatchQueue.concurrentPerform(iterations: min(10, allMoves.count)) {
            index in
            guard index < allMoves.count else { return }
            let move = allMoves[index]

            var totalScore: Double = 0.0
            for _ in 0..<simulationsPerMove {
                let score = simulateRandomGame(
                    from: move,
                    board: board,
                    isBlack: isBlack
                )
                totalScore += score
            }

            let averageScore = totalScore / Double(simulationsPerMove)
            // 使用锁保护共享资源
            if averageScore > bestScore {
                bestScore = averageScore
                bestMove = move
            }

        }
        print("\(isBlack ? "黑" : "白")方分数：\(bestScore)")
        return bestMove
    }
    private func getAllPossibleMoves(board: [[Int]], isBlack: Bool) -> [(
        piece: (Int, Int), target: (Int, Int), arrow: (Int, Int)
    )] {
        var allMoves:
            [(piece: (Int, Int), target: (Int, Int), arrow: (Int, Int))] = []
        let playerPiece = isBlack ? 1 : 2

        // 获取所有己方棋子
        for row in 0..<8 {
            for col in 0..<8 {
                if board[row][col] == playerPiece {
                    // 获取该棋子的所有移动位置
                    let movePositions = calculateSimuMoves(
                        from: (row, col),
                        board: board
                    )

                    for movePos in movePositions {
                        // 模拟移动后的棋盘
                        var tempBoard = board
                        tempBoard[movePos.0][movePos.1] = playerPiece
                        tempBoard[row][col] = 0

                        // 获取所有可能的箭位置
                        let arrowPositions = calculateSimuMoves(
                            from: movePos,
                            board: tempBoard
                        )

                        for arrowPos in arrowPositions {
                            allMoves.append(
                                (
                                    piece: (row, col), target: movePos,
                                    arrow: arrowPos
                                )
                            )
                        }
                    }
                }
            }
        }

        return allMoves
    }
    private func simulateRandomGame(
        from move: (piece: (Int, Int), target: (Int, Int), arrow: (Int, Int)),
        board: [[Int]],
        isBlack: Bool
    ) -> Double {
        // 1. 应用第一步走法
        var simBoard = board
        let playerPiece = isBlack ? 1 : 2
        let arrowType = isBlack ? 3 : 4

        // 移动棋子
        simBoard[move.target.0][move.target.1] = playerPiece
        simBoard[move.piece.0][move.piece.1] = 0

        // 放置箭
        simBoard[move.arrow.0][move.arrow.1] = arrowType

        // 2. 继续随机模拟若干步
        var currentIsBlack = !isBlack  // 切换到对方回合
        let maxSimulationSteps = 8  // 模拟8步

        for _ in 0..<maxSimulationSteps {
            // 检查游戏是否结束
            if isGameOver(board: simBoard, isBlackTurn: currentIsBlack) {
                // 游戏结束，返回分数
                return calculateFinalScore(
                    board: simBoard,
                    originalIsBlack: isBlack
                )
            }

            // 随机选择一步走法
            guard
                let randomMove = getRandomMove(
                    board: simBoard,
                    isBlack: currentIsBlack
                )
            else {
                break
            }

            // 执行随机走法
            let currentPiece = currentIsBlack ? 1 : 2
            let currentArrow = currentIsBlack ? 3 : 4

            simBoard[randomMove.target.0][randomMove.target.1] = currentPiece
            simBoard[randomMove.piece.0][randomMove.piece.1] = 0
            simBoard[randomMove.arrow.0][randomMove.arrow.1] = currentArrow

            // 切换玩家
            currentIsBlack.toggle()
        }

        // 3. 模拟结束，评估局面
        return evaluateBoard(board: simBoard, originalIsBlack: isBlack)
    }
    private func getRandomMove(board: [[Int]], isBlack: Bool) -> (
        piece: (Int, Int), target: (Int, Int), arrow: (Int, Int)
    )? {
        let allMoves = getAllPossibleMoves(board: board, isBlack: isBlack)
        return allMoves.randomElement()
    }

    // 检查游戏是否结束（模拟途中版）
    private func isGameOver(board: [[Int]], isBlackTurn: Bool) -> Bool {
        let opponentIsBlack = !isBlackTurn
        let opponentPiece = opponentIsBlack ? 1 : 2

        // 检查对方是否有棋子可以移动
        for row in 0..<8 {
            for col in 0..<8 {
                if board[row][col] == opponentPiece {
                    let moves = calculateSimuMoves(
                        from: (row, col),
                        board: board
                    )
                    if !moves.isEmpty {
                        return false  // 对方有棋子可以移动
                    }
                }
            }
        }

        return true  // 对方无法移动
    }
    private func calculateFinalScore(board: [[Int]], originalIsBlack: Bool)
        -> Double
    {
        // 如果对方无法移动，我方获胜
        let currentPlayerCanMove =
            getRandomMove(board: board, isBlack: originalIsBlack) != nil
        let opponentCanMove =
            getRandomMove(board: board, isBlack: !originalIsBlack) != nil

        if !opponentCanMove && currentPlayerCanMove {
            return 1.0  // 我方获胜
        } else if opponentCanMove && !currentPlayerCanMove {
            return -1.0  // 对方获胜
        }

        // 否则返回评估分数
        return evaluateBoard(board: board, originalIsBlack: originalIsBlack)
    }
    // 生成随机走法列表（备用）
    private func generateRandomMcList() {
        let availablePieces = getAllAvailablePieces()
        guard let randomPiece = availablePieces.randomElement() else { return }

        calculateAvailableMoves(from: randomPiece)
        let availableMovesList = getAllAvailableMoves()
        guard let randomMove = availableMovesList.randomElement() else {
            return
        }

        // 模拟移动以获取箭的位置
        var tempBoard = gameState.chessBoard
        let playerPiece = gameState.blackRound ? 1 : 2

        tempBoard[randomMove.0][randomMove.1] = playerPiece
        tempBoard[randomPiece.0][randomPiece.1] = 0

        let arrowMoves = calculateSimuMoves(from: randomMove, board: tempBoard)
        guard let randomArrow = arrowMoves.randomElement() else { return }

        // 构建mcList
        gameState.mcList = [
            gameState.roundNum,
            gameState.blackRound ? 1 : 0,
            randomPiece.0, randomPiece.1,
            randomMove.0, randomMove.1,
            randomArrow.0, randomArrow.1,
        ]
    }
    private func evaluateBoard(board: [[Int]], originalIsBlack: Bool) -> Double
    {
        // 使用你的评估函数
        let blackScore =
            controlScore(
                forBlack: true,
                board: board,
                factor: gameState.blackControlFactor
            )
            + safetyScore(
                forBlack: true,
                board: board,
                factor: gameState.blackSafetyFactor,
                factor_surround: gameState.blackSurroundFactor
            )
            + centerScore(
                forBlack: true,
                board: board,
                factor: gameState.blackCenterFactor
            )

        let whiteScore =
            controlScore(
                forBlack: false,
                board: board,
                factor: gameState.whiteControlFactor
            )
            + safetyScore(
                forBlack: false,
                board: board,
                factor: gameState.whiteSafetyFactor,
                factor_surround: gameState.whiteSurroundFactor
            )
            + centerScore(
                forBlack: false,
                board: board,
                factor: gameState.whiteCenterFactor
            )

        let diff = Double(blackScore - whiteScore)
        let normalized = tanh(diff / 100.0)  // 归一化到[-1, 1]

        return originalIsBlack ? normalized : -normalized
    }
    // 模拟移动（无GameState版）
    private func calculateSimuMoves(from: (Int, Int), board: [[Int]]) -> [(
        Int, Int
    )] {
        var moves: [(Int, Int)] = []
        let (row, col) = from
        let directions = [
            (-1, -1), (-1, 0), (-1, 1),
            (0, -1), (0, 1),
            (1, -1), (1, 0), (1, 1),
        ]

        for direction in directions {
            var currentRow = row + direction.0
            var currentCol = col + direction.1

            while currentRow >= 0 && currentRow < 8 && currentCol >= 0
                && currentCol < 8
            {
                if board[currentRow][currentCol] != 0 {
                    break  // 遇到障碍或棋子
                }
                moves.append((currentRow, currentCol))
                currentRow += direction.0
                currentCol += direction.1
            }
        }

        return moves
    }
    private func mcSelect() {
        // 如果mcList为空或者回合不匹配，重新生成
        if gameState.mcList.isEmpty || gameState.mcList[0] != gameState.roundNum
        {
            generateMcListForCurrentTurn()
        }

        // 检查mcList是否有效
        guard gameState.mcList.count >= 8,
            gameState.mcList[1] == (gameState.blackRound ? 1 : 0)
        else {
            // 如果无效，使用随机选择
            randomSelect()
            return
        }

        // 从mcList中读取棋子位置
        let pieceRow = gameState.mcList[2]
        let pieceCol = gameState.mcList[3]

        // 选择棋子
        gameState.selectedPiece = (pieceRow, pieceCol)
        calculateAvailableMoves(from: (pieceRow, pieceCol))
        gameState.gamePhase = .movePiece

        addHistory("蒙特卡洛方法 选择了棋子 (\(pieceRow),\(pieceCol))")
    }
    private func mcMove(availableMovesList: [(Int, Int)], selected: (Int, Int))
    {
        // 检查mcList是否有效
        guard gameState.mcList.count >= 8,
            gameState.mcList[0] == gameState.roundNum,
            gameState.mcList[2] == selected.0,
            gameState.mcList[3] == selected.1
        else {
            // 如果无效，使用随机移动
            randomMove(
                availableMovesList: availableMovesList,
                selected: selected
            )
            return
        }

        // 从mcList中读取目标位置
        let targetRow = gameState.mcList[4]
        let targetCol = gameState.mcList[5]

        // 检查目标位置是否合法
        if availableMovesList.contains(where: { $0 == (targetRow, targetCol) })
        {
            // 执行移动
            gameState.chessBoard[targetRow][targetCol] =
                gameState.chessBoard[selected.0][selected.1]
            gameState.chessBoard[selected.0][selected.1] = 0
            gameState.selectedPiece = (targetRow, targetCol)

            calculateAvailableMoves(from: (targetRow, targetCol))
            gameState.gamePhase = .placeArrow

            addHistory("蒙特卡洛方法 移动棋子到 (\(targetRow),\(targetCol))")
        } else {
            // 如果不合法，使用随机移动
            randomMove(
                availableMovesList: availableMovesList,
                selected: selected
            )
        }
    }
    private func mcPlace(obstacleType: Int) {
        // 检查mcList是否有效
        guard gameState.mcList.count >= 8,
            gameState.mcList[0] == gameState.roundNum
        else {
            // 如果无效，使用随机放置
            randomPlace(obstacleType: obstacleType)
            return
        }

        // 从mcList中读取箭的位置
        let arrowRow = gameState.mcList[6]
        let arrowCol = gameState.mcList[7]

        // 检查箭位置是否合法
        let availableArrows = getAllAvailableMoves()
        if availableArrows.contains(where: { $0 == (arrowRow, arrowCol) }) {
            // 放置障碍物
            gameState.chessBoard[arrowRow][arrowCol] = obstacleType
            addHistory("蒙特卡洛方法 放置障碍物在 (\(arrowRow),\(arrowCol))")
        } else {
            // 如果不合法，使用随机放置
            randomPlace(obstacleType: obstacleType)
        }

    }
    // MARK: - 自动下棋（第三版）（已修复）
    func autoOperate(player isBlack: Bool) {
        if gameState.isGameOver || isBlack != gameState.blackRound {
            return
        }
        guard
            gameState.blackRound == isBlack
                && (isBlack
                    ? gameState.blackAutoOperate : gameState.whiteAutoOperate)
        else { return }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            let obstacleType = isBlack ? 3 : 4

            switch self.gameState.gamePhase {
            case .selectPiece:
                // 根据策略执行自动下棋
                if gameState.blackRound {
                    gameState.roundNum += 1
                }
                switch isBlack
                    ? self.gameState.blackStrategy
                    : self.gameState.whiteStrategy
                {
                case "纯随机下棋":
                    randomSelect()
                case "蒙特卡洛方法":
                    mcSelect()
                default:
                    print("未选中策略，棋局冻结")
                    return
                }
                // 延迟。。
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                    // 在继续前检查是否游戏结束
                    if self.gameState.isGameOver {
                        return
                    }
                    self.autoOperate(
                        player: self.gameState.blackRound
                    )
                }

            //冗余            if !foundValidPiece {
            //                    // 这里实际上就是胜利条件，直接处理胜利
            //                    let winner = isBlack ? "白方" : "黑方"
            //                    self.addHistory("自动随机下棋 无有效棋子可移动，\(winner)胜利")// 最老的判断胜利条件
            //                    _ = self.checkAndHandleWinCondition()
            //                }

            case .movePiece:
                guard let selected = self.gameState.selectedPiece else {
                    return
                }

                let availableMovesList = self.getAllAvailableMoves()
                switch isBlack
                    ? self.gameState.blackStrategy
                    : self.gameState.whiteStrategy
                {
                case "纯随机下棋":
                    randomMove(
                        availableMovesList: availableMovesList,
                        selected: selected
                    )
                case "蒙特卡洛方法":
                    mcMove(
                        availableMovesList: availableMovesList,
                        selected: selected
                    )
                default:
                    print("未选中策略，棋局冻结")
                    return
                }
                // 延迟。。。
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    // 在继续前检查是否游戏结束
                    if self.gameState.isGameOver {
                        return
                    }
                    self.autoOperate(player: isBlack)
                }
            case .placeArrow:
                switch isBlack
                    ? self.gameState.blackStrategy
                    : self.gameState.whiteStrategy
                {
                case "纯随机下棋":
                    randomPlace(obstacleType: obstacleType)
                case "蒙特卡洛方法":
                    mcPlace(obstacleType: obstacleType)
                default:
                    print("未选中策略，棋局冻结")
                    return
                }
                // 🎯 在放置障碍物后立即检查胜利条件
                let isGameOver = self.checkAndHandleWinCondition()

                if isGameOver {
                    // 游戏结束，清理状态但不切换回合
                    self.gameState.selectedPiece = nil
                    self.gameState.availableMoves = Array(
                        repeating: Array(repeating: 0, count: 8),
                        count: 8
                    )
                    self.gameState.gamePhase = .selectPiece
                    self.gameState.unsavable = false
                    // 注意：这里不切换 blackRound，因为游戏已结束
                } else {
                    // 游戏继续，正常切换回合
                    self.gameState.selectedPiece = nil
                    self.gameState.availableMoves = Array(
                        repeating: Array(repeating: 0, count: 8),
                        count: 8
                    )
                    self.gameState.gamePhase = .selectPiece
                    self.gameState.blackRound.toggle()
                    self.gameState.unsavable = false
                    // 又是延迟。。
                    DispatchQueue.main.asyncAfter(
                        deadline: .now() + 0.3
                    ) {
                        // 在继续前检查是否游戏结束
                        if self.gameState.isGameOver {
                            return
                        }
                        if self.gameState.blackRound
                            && self.gameState.blackAutoOperate
                        {
                            self.autoOperate(player: true)
                        } else if !self.gameState.blackRound
                            && self.gameState.whiteAutoOperate
                        {
                            self.autoOperate(player: false)
                        }
                    }
                }
            }
        }
    }
    // MARK: 随机3步骤（第四版）（已修复）（稳定版）
    private func randomSelect() {
        // 注意：下方代码可替换
        let availablePieces = self.getAllAvailablePieces()
        let shuffledPieces = availablePieces.shuffled()
        for piece in shuffledPieces {
            self.calculateAvailableMoves(from: piece)
            let availableMovesList = self.getAllAvailableMoves()

            if !availableMovesList.isEmpty {
                self.gameState.selectedPiece = piece  // 必须要有！！！！！
                self.gameState.gamePhase = .movePiece  // 必须要有！！！！！
                self.addHistory(
                    "自动随机下棋 选择了棋子 (\(piece.0),\(piece.1))"
                )
                break
            }
        }
        // 注意：上方代码可替换
    }
    private func randomMove(
        availableMovesList: [(Int, Int)],
        selected: (Int, Int)
    ) {
        // 注意：以下代码可替换
        if let randomMove = availableMovesList.randomElement() {
            self.gameState.chessBoard[randomMove.0][randomMove.1] =
                self.gameState.chessBoard[selected.0][selected.1]  // 必须要有！！！！！复制棋子到目标位置
            self.gameState.chessBoard[selected.0][selected.1] = 0  // 必须要有！！！！！移除原位的棋子
            self.gameState.selectedPiece = (
                randomMove.0, randomMove.1
            )  // 必须要有！！！！！选中移动后到棋子

            self.calculateAvailableMoves(
                from: (randomMove.0, randomMove.1)
            )
            self.gameState.gamePhase = .placeArrow
            self.addHistory(
                "自动随机下棋 移动棋子到 (\(randomMove.0),\(randomMove.1))"
            )
        } else {
            self.addHistory("自动随机下棋 当前棋子无有效移动，重新选择")
            self.gameState.gamePhase = .selectPiece
            self.gameState.selectedPiece = nil
            self.gameState.availableMoves = Array(
                repeating: Array(repeating: 0, count: 8),
                count: 8
            )

        }
        // 注意：以上代码可替换
    }
    private func randomPlace(obstacleType: Int) {
        // 注意：以下代码可替换
        let availableArrows = self.getAllAvailableMoves()

        if let randomArrow = availableArrows.randomElement() {
            self.gameState.chessBoard[randomArrow.0][
                randomArrow.1
            ] =
                obstacleType

            self.addHistory(
                "自动随机下棋 放置障碍物在 (\(randomArrow.0),\(randomArrow.1))"
            )

        }
        // 注意：以上代码可替换
    }
    // MARK: 局势评估（关键）
    private func controlScore(forBlack: Bool, board: [[Int]], factor: Double)
        -> Double
    {  // 进程管理（并发运行）、设备管理、存储管理、文件管理、用户界面
        let targetPiece = forBlack ? 1 : 2
        var score = 0.0
        // 计算每个棋子的控制区域
        for row in 0..<8 {
            for col in 0..<8 {
                if board[row][col] == targetPiece {
                    score +=
                        factor
                        * Double(
                            calculateSimuMoves(from: (row, col), board: board)
                                .count
                        )
                }
            }
        }
        return score
    }
    private func safetyScore(
        forBlack: Bool,
        board: [[Int]],
        factor: Double,
        factor_surround: Double
    ) -> Double {
        let targetPiece = forBlack ? 1 : 2
        var safety = 0.0

        for row in 0..<8 {
            for col in 0..<8 {
                if board[row][col] == targetPiece {
                    // 检查棋子周围是否有出路
                    let moves = calculateSimuMoves(
                        from: (row, col),
                        board: board
                    )
                    if moves.isEmpty {
                        safety -= factor_surround  // 棋子被困住
                    } else {
                        safety += factor * Double(moves.count)
                    }

                    // 检查是否靠近棋盘边缘（不利位置）
                    let edgeDistance = min(row, 7 - row, col, 7 - col)
                    safety += factor * Double(edgeDistance)  // 距离边缘越远越安全
                }
            }
        }
        return safety
    }
    private func centerScore(forBlack: Bool, board: [[Int]], factor: Double)
        -> Double
    {
        let targetPiece = forBlack ? 1 : 2
        var centerScore = 0.0
        let centerCells = [(3, 3), (3, 4), (4, 3), (4, 4)]

        // 检查是否控制中心
        for center in centerCells {
            if board[center.0][center.1] == targetPiece {
                centerScore += factor * 5
            }
        }

        // 计算棋子距离中心的远近
        for row in 0..<8 {
            for col in 0..<8 {
                if board[row][col] == targetPiece {
                    let dx = abs(Double(row) - 3.5)
                    let dy = abs(Double(col) - 3.5)
                    let distance = sqrt(dx * dx + dy * dy)
                    centerScore += factor * (10 - distance)  // 越靠近中心得分越高
                }
            }
        }

        return centerScore
    }
}

// MARK: - 棋盘格子视图
struct ChessSquareView: View {
    let row: Int
    let column: Int
    let value: Int
    let action: () -> Void
    let blackRound: Bool
    let gamePhase: GamePhase
    let availableMoves: [[Int]]

    @State private var showContent = true

    var body: some View {
        ZStack {
            Rectangle()
                .fill(getSquareColor())
                .frame(width: 80, height: 80)
                .overlay(
                    Rectangle().stroke(Color.black.opacity(0.2), lineWidth: 1)
                )

            Button(action: action) {
                ZStack {
                    Rectangle()
                        .fill(Color.blue.opacity(0.01))
                        .frame(width: 75, height: 75)

                    if value == 1 {
                        Image(systemName: "person.fill")
                            .font(.system(size: 30))
                            .opacity(showContent ? 1 : 0)
                    } else if value == 2 {
                        Image(systemName: "person")
                            .font(.system(size: 30))
                            .opacity(showContent ? 1 : 0)
                    } else if value == 3 {
                        Image(systemName: "nosign.app.fill")
                            .font(.system(size: 30))
                            .opacity(showContent ? 1 : 0)
                    } else if value == 4 {
                        Image(systemName: "nosign.app")
                            .font(.system(size: 30))
                            .opacity(showContent ? 1 : 0)
                    }
                }
            }
            .disabled(!isInteractive())
            .buttonStyle(PlainButtonStyle())
        }
        .onChange(of: value) { _, _ in
            withAnimation(.easeInOut(duration: 0.3)) {
                showContent = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        showContent = true
                    }
                }
            }
        }
        .onAppear {
            withAnimation(.easeInOut(duration: 0.3)) {
                showContent = true
            }
        }
    }

    private func getSquareColor() -> Color {
        if availableMoves[row][column] == 1 {
            return Color.green.opacity(0.5)
        } else if availableMoves[row][column] == 2 {
            return Color.red.opacity(0.5)
        } else {
            return (row + column) % 2 == 0
                ? Color.white : Color.gray.opacity(0.3)
        }
    }

    private func isInteractive() -> Bool {
        let currentPlayer = blackRound ? 1 : 2

        switch gamePhase {
        case .selectPiece:
            return value == currentPlayer
        case .movePiece:
            return availableMoves[row][column] == 1 || value == currentPlayer
        case .placeArrow:
            return availableMoves[row][column] == 1
        }
    }
}

// MARK: - 棋盘行视图
struct ChessBoardRowView: View {
    let row: Int
    let chessBoard: [[Int]]
    let onSquareTap: (Int, Int) -> Void
    let blackRound: Bool
    let gamePhase: GamePhase
    let availableMoves: [[Int]]

    var body: some View {
        HStack(spacing: 0) {
            ForEach(0..<8, id: \.self) { column in
                ChessSquareView(
                    row: row,
                    column: column,
                    value: chessBoard[row][column],
                    action: { onSquareTap(row, column) },
                    blackRound: blackRound,
                    gamePhase: gamePhase,
                    availableMoves: availableMoves
                )
            }
        }
    }
}

// MARK: - 棋盘视图
struct ChessBoardView: View {
    let chessBoard: [[Int]]
    let onSquareTap: (Int, Int) -> Void
    let blackRound: Bool
    let gamePhase: GamePhase
    let availableMoves: [[Int]]

    var body: some View {
        VStack(spacing: 0) {
            HStack(spacing: 0) {
                Rectangle()
                    .fill(Color.clear)
                    .frame(width: 30, height: 30)

                ForEach(0..<8, id: \.self) { column in
                    Text(String(column))
                        .font(.system(size: 16, weight: .medium))
                        .frame(width: 80, height: 30)
                        .foregroundColor(.primary)
                }
            }

            HStack(spacing: 0) {
                VStack(spacing: 0) {
                    ForEach(0..<8, id: \.self) { row in
                        Text(String(row))
                            .font(.system(size: 16, weight: .medium))
                            .frame(width: 30, height: 80)
                            .foregroundColor(.primary)
                    }
                }

                VStack(spacing: 0) {
                    ForEach(0..<8, id: \.self) { row in
                        ChessBoardRowView(
                            row: row,
                            chessBoard: chessBoard,
                            onSquareTap: onSquareTap,
                            blackRound: blackRound,
                            gamePhase: gamePhase,
                            availableMoves: availableMoves
                        )
                    }
                }
                .overlay(Rectangle().stroke(Color.black, lineWidth: 2))
                .padding()
            }
        }
        .frame(width: 800)
    }
}

// MARK: - 菜单按钮视图
struct MenuButtonView: View {
    let title: String
    let icon: String
    let action: () -> Void
    let disabled: Bool

    var body: some View {
        Button(action: action) {
            Label(title, systemImage: icon)
                .font(.system(size: 15))
                .frame(maxWidth: 150)
        }
        .disabled(disabled)
        .buttonStyle(.bordered)
    }
}

// MARK: - 左侧栏视图
struct SidebarView: View {
    @EnvironmentObject var gameState: GameState

    var body: some View {
        VStack {
            if gameState.isPlaying {
                gameInfoView
            } else {
                Text("请先打开一个存档，或开启新游戏……")
                    .font(.system(size: 25, weight: .medium))
                Spacer()
            }
        }
        .frame(width: 320)
    }

    private var gameInfoView: some View {
        VStack {
            Spacer()
                .frame(height: 5)
            HStack {
                Image(systemName: "folder")
                    .font(.system(size: 10))
                    .foregroundStyle(.secondary)
                if gameState.withFile {
                    Text("当前存档文件：\(gameState.currentUrl)")
                        .font(.system(size: 10))
                        .foregroundStyle(.secondary)
                } else {
                    Text("新游戏：尚未存档")
                        .font(.system(size: 10))
                        .foregroundStyle(.secondary)
                }
            }
            HStack {
                Image(systemName: "flag.filled.and.flag.crossed")
                    .font(.system(size: 25))
                    .foregroundStyle(.primary)
                Text("当前行动方：")
                    .font(.system(size: 25, weight: .medium))
                if gameState.blackRound {
                    HStack {
                        Image(systemName: "person.fill")
                            .font(.system(size: 25))
                            .foregroundStyle(.primary)
                        Text("黑方")
                            .font(.system(size: 25, weight: .medium))
                    }
                } else {
                    HStack {
                        Image(systemName: "person")
                            .font(.system(size: 25))
                            .foregroundStyle(.primary)
                        Text("白方")
                            .font(.system(size: 25, weight: .medium))
                    }
                }
            }
            HStack {
                Image(systemName: "number")
                    .font(.system(size: 20))
                    .foregroundStyle(.primary)
                Text("当前回合数：\(gameState.roundNum)")
                    .font(.system(size: 20, weight: .medium))
            }
            HStack {
                Image(systemName: "flag.circle")
                    .font(.system(size: 15))
                Text("当前行动阶段：")
                    .font(.system(size: 15))

                switch gameState.gamePhase {
                case .selectPiece:
                    HStack {
                        if gameState.blackRound {
                            Image(systemName: "person.fill")
                                .font(.system(size: 15))
                        } else {
                            Image(systemName: "person")
                                .font(.system(size: 15))
                        }
                        Text("选择棋子")
                            .font(.system(size: 15))
                    }
                case .movePiece:
                    HStack {
                        if gameState.blackRound {
                            Image(systemName: "arrow.right.circle.fill")
                                .font(.system(size: 15))
                        } else {
                            Image(systemName: "arrow.right.circle")
                                .font(.system(size: 15))
                        }
                        Text("移动棋子")
                            .font(.system(size: 15))
                    }
                case .placeArrow:
                    HStack {
                        if gameState.blackRound {
                            Image(systemName: "nosign.app.fill")
                                .font(.system(size: 15))
                        } else {
                            Image(systemName: "nosign.app")
                                .font(.system(size: 15))
                        }
                        Text("放置障碍物")
                            .font(.system(size: 15))
                    }
                }
            }
            HStack {
                Image(systemName: "clock")
                    .font(.system(size: 15))
                Text("历史操作：")
                    .font(.system(size: 15))
            }

            // 修复List使用方式
            List(gameState.history) { entry in
                Text(entry.description)
                    .font(.system(size: 12))
            }
            .frame(height: 240)
        }
    }
}
// 独立的滑块组件，可复用
struct ParameterSliderView: View {
    @Binding var value: Double
    let title: String
    let description: String
    let range: ClosedRange<Double>
    let step: Double

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            // 标题和当前值
            HStack {
                Text(title)
                    .font(.headline)
                Spacer()
                Text("\(value, specifier: "%.1f")")
                    .font(.headline)
                    .foregroundColor(.blue)
            }

            // 滑块
            Slider(value: $value, in: range, step: step)
                .accentColor(.blue)

            // 说明文字
            Text(description)
                .font(.caption)
                .foregroundColor(.secondary)
                .fixedSize(horizontal: false, vertical: true)
        }
        .padding(.vertical, 8)
    }
}

// 游戏中的参数调节面板优化
struct ParameterSettingsView: View {
    @Binding var controlFactor: Double
    @Binding var safetyFactor: Double
    @Binding var surroundFactor: Double
    @Binding var centerFactor: Double
    let isBlack: Bool

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            // 标题
            HStack {
                Image(
                    systemName: isBlack
                        ? "arrowshape.left.arrowshape.right.fill"
                        : "arrowshape.left.arrowshape.right"
                )
                .font(.system(size: 16))
                Text("\(isBlack ? "黑方" : "白方")蒙特卡洛参数")
                    .font(.headline)
            }
            .padding(.bottom, 8)

            // 滑块组
            ParameterSliderView(
                value: $controlFactor,
                title: "控制分数倍率",
                description: "调高使算法更重视可用移动数目",
                range: 1...10,
                step: 0.1
            )

            ParameterSliderView(
                value: $safetyFactor,
                title: "安全分数倍率",
                description: "调高使算法更重视不被困住",
                range: 1...10,
                step: 0.1
            )

            ParameterSliderView(
                value: $surroundFactor,
                title: "包围惩罚系数",
                description: "棋子被完全包围时的惩罚值",
                range: 5...100,
                step: 0.5
            )

            ParameterSliderView(
                value: $centerFactor,
                title: "中心分数倍率",
                description: "调高使算法更重视占领棋盘中心",
                range: 1...10,
                step: 0.1
            )

            // 快速设置按钮
            HStack(spacing: 12) {
                Button("均衡型") {
                    controlFactor = 1.0
                    safetyFactor = 1.0
                    surroundFactor = 10.0
                    centerFactor = 1.0
                }
                .buttonStyle(.bordered)
                .font(.caption)

                Button("进攻型") {
                    controlFactor = 1.6
                    safetyFactor = 0.6
                    surroundFactor = 6.0
                    centerFactor = 1.6
                }
                .buttonStyle(.bordered)
                .font(.caption)

                Button("防守型") {
                    controlFactor = 0.6
                    safetyFactor = 1.6
                    surroundFactor = 16.0
                    centerFactor = 0.6
                }
                .buttonStyle(.bordered)
                .font(.caption)
            }
            .padding(.top, 8)
        }
        .padding()
        .background(Color.gray.opacity(0.05))
        .cornerRadius(12)
    }
}
// MARK: - 右侧栏视图
struct RightView: View {
    @EnvironmentObject var gameState: GameState
    let gameManager: GameManager
    let options = ["纯随机下棋", "蒙特卡洛方法"]

    var body: some View {
        VStack {
            Spacer()
                .frame(height: 5)

            if gameState.isPlaying {
                // 标题栏（固定）
                VStack(spacing: 8) {
                    HStack {
                        Image(systemName: "command")
                            .font(.system(size: 25))
                            .foregroundStyle(.primary)
                        Text("自动下棋控制面板")
                            .font(.system(size: 25))
                            .foregroundStyle(.primary)
                    }
                    .padding(.top, 12)

                    Divider()
                        .padding(.horizontal)
                }
                .background(Color(nsColor: .windowBackgroundColor))
                ScrollView {
                    // 黑方设置
                    HStack {
                        Image(systemName: "command.circle.fill")
                            .font(.system(size: 15))
                        Toggle("黑方自动下棋", isOn: $gameState.blackAutoOperate)
                            .disabled(gameState.isGameOver)
                            .onChange(of: gameState.blackAutoOperate) {
                                _,
                                newValue in
                                if newValue && gameState.blackRound {
                                    gameManager.autoOperate(player: true)
                                }
                            }
                    }
                    .padding(.horizontal)

                    HStack {
                        Image(systemName: "gear.circle.fill")
                            .font(.system(size: 15))
                        Picker("自动下棋策略", selection: $gameState.blackStrategy) {
                            ForEach(options, id: \.self) { option in
                                Text(option).tag(option)
                            }
                        }
                        .disabled(gameState.isGameOver)
                        .frame(maxWidth: 250)
                    }
                    .padding(.horizontal)

                    // 只有当选择蒙特卡洛时才显示参数
                    if gameState.blackStrategy == "蒙特卡洛方法"
                        && !gameState.isGameOver
                    {
                        ParameterSettingsView(
                            controlFactor: $gameState.blackControlFactor,
                            safetyFactor: $gameState.blackSafetyFactor,
                            surroundFactor: $gameState.blackSurroundFactor,
                            centerFactor: $gameState.blackCenterFactor,
                            isBlack: true
                        )
                        .padding(.horizontal)
                    }

                    Divider()
                        .padding(.horizontal)

                    // 白方设置
                    HStack {
                        Image(systemName: "command.circle")
                            .font(.system(size: 15))
                        Toggle("白方自动下棋", isOn: $gameState.whiteAutoOperate)
                            .disabled(gameState.isGameOver)
                            .onChange(of: gameState.whiteAutoOperate) {
                                _,
                                newValue in
                                if newValue && !gameState.blackRound {
                                    gameManager.autoOperate(player: false)
                                }
                            }
                    }
                    .padding(.horizontal)

                    HStack {
                        Image(systemName: "gear.circle")
                        Picker("自动下棋策略", selection: $gameState.whiteStrategy) {
                            ForEach(options, id: \.self) { option in
                                Text(option).tag(option)
                            }
                        }
                        .disabled(gameState.isGameOver)
                        .frame(maxWidth: 250)
                    }
                    .padding(.horizontal)

                    // 只有当选择蒙特卡洛时才显示参数
                    if gameState.whiteStrategy == "蒙特卡洛方法"
                        && !gameState.isGameOver
                    {
                        ParameterSettingsView(
                            controlFactor: $gameState.whiteControlFactor,
                            safetyFactor: $gameState.whiteSafetyFactor,
                            surroundFactor: $gameState.whiteSurroundFactor,
                            centerFactor: $gameState.whiteCenterFactor,
                            isBlack: false
                        )
                        .padding(.horizontal)
                    }

                    Spacer()
                }
            } else {
                Text("开始游戏以展示自动操作选项……")
                    .font(.system(size: 25, weight: .medium))
                    .foregroundColor(.secondary)
                    .multilineTextAlignment(.center)
                    .padding()
                Spacer()
            }
        }

        .frame(width: 280)
    }
}

// MARK: - 主菜单视图
struct MainMenuView: View {
    @EnvironmentObject var gameState: GameState
    let gameManager: GameManager

    @State private var showNewGameDoubleCheck = false
    @State private var showLoadDoubleCheck = false
    @State private var showQuitDoubleCheck = false

    var body: some View {
        VStack {
            Spacer()
                .frame(height: 50)
            HStack {
                Image(systemName: "list.bullet")
                    .font(.system(size: 20))
                    .foregroundStyle(.primary)

                Text("Amazon棋 菜单")
                    .font(.system(size: 20))
                    .foregroundStyle(.primary)
            }
            HStack {
                Image(systemName: "info")
                    .font(.system(size: 20))
                    .foregroundStyle(.secondary)
                VStack {
                    Text("2025秋 计算概论A 大作业")
                        .font(.system(size: 10))
                        .foregroundStyle(.secondary)
                    Text("陈奕辰 数学科学学院 2500010834")
                        .font(.system(size: 10))
                        .foregroundStyle(.secondary)
                }
            }
            Spacer()
                .frame(height: 15)

            if gameState.unsavable {
                MenuButtonView(
                    title: "新游戏",
                    icon: "plus",
                    action: newGame,
                    disabled: false
                )
            } else {
                MenuButtonView(
                    title: "新游戏",
                    icon: "plus",
                    action: { showNewGameDoubleCheck = true },
                    disabled: false
                )
            }

            Spacer().frame(height: 15)

            if gameState.isPlaying {
                if gameState.unsavable {
                    MenuButtonView(
                        title: "已保存",
                        icon: "square.and.arrow.down",
                        action: quickSave,
                        disabled: true
                    )
                } else {
                    MenuButtonView(
                        title: "快速保存",
                        icon: "square.and.arrow.down",
                        action: quickSave,
                        disabled: !gameState.withFile
                    )
                    Spacer().frame(height: 15)
                    MenuButtonView(
                        title: "另存为",
                        icon: "document.badge.plus",
                        action: saveGame,
                        disabled: false
                    )
                }
                Spacer().frame(height: 15)
            }

            if gameState.unsavable {
                MenuButtonView(
                    title: "读取",
                    icon: "folder",
                    action: loadGame,
                    disabled: false
                )
                Spacer().frame(height: 15)
                MenuButtonView(
                    title: "退出全部",
                    icon: "xmark",
                    action: quitGame,
                    disabled: false
                )
            } else {
                MenuButtonView(
                    title: "读取",
                    icon: "folder",
                    action: { showLoadDoubleCheck = true },
                    disabled: false
                )
                Spacer().frame(height: 15)
                MenuButtonView(
                    title: "退出全部",
                    icon: "xmark",
                    action: { showQuitDoubleCheck = true },
                    disabled: false
                )
            }
        }
        .alert("舍弃当前棋局并创建新棋局？", isPresented: $showNewGameDoubleCheck) {
            Button("返回", role: .cancel) {}
            Button("继续", role: .destructive) { newGame() }
        } message: {
            Text("当前棋局尚未保存，是否继续创建新游戏？")
        }
        .alert("舍弃当前棋局并读取？", isPresented: $showLoadDoubleCheck) {
            Button("返回", role: .cancel) {}
            Button("继续", role: .destructive) { loadGame() }
        } message: {
            Text("当前棋局尚未保存，是否继续读取其他棋局？")
        }
        .alert("舍弃当前棋局并退出？", isPresented: $showQuitDoubleCheck) {
            Button("返回", role: .cancel) {}
            Button("继续", role: .destructive) { quitGame() }
        } message: {
            Text("当前棋局尚未保存，是否继续退出？")
        }
        Spacer().frame(height: 20)
    }

    private func newGame() {
        gameManager.initializeChessBoard()
        gameState.isPlaying = true
        gameState.unsavable = false
        gameState.withFile = false
        gameState.blackRound = true
    }

    private func saveGame() {
        gameManager.saveGame()
    }

    private func loadGame() {
        gameManager.loadGame()
    }

    private func quickSave() {
        let url = URL(fileURLWithPath: gameState.currentUrl)
        _ = gameManager.saveChessBoardToJSON(url: url)
        gameState.unsavable = true
    }

    private func quitGame() {
        NSApplication.shared.terminate(nil)
    }
}

// MARK: - 主视图
struct ContentView: View {
    @StateObject private var gameState = GameState()
    private var gameManager: GameManager {
        GameManager(gameState: gameState)
    }

    var body: some View {
        HStack(spacing: 0) {
            VStack {
                SidebarView()
                    .environmentObject(gameState)

                Rectangle()
                    .fill(Color.gray.opacity(0.3))
                    .frame(height: 1)

                MainMenuView(gameManager: gameManager)
                    .environmentObject(gameState)
            }

            Rectangle()
                .fill(Color.gray.opacity(0.3))
                .frame(width: 1)
                .padding()

            ChessBoardView(
                chessBoard: gameState.chessBoard,
                onSquareTap: { row, column in
                    gameManager.handleSquareTap(row: row, column: column)
                },
                blackRound: gameState.blackRound,
                gamePhase: gameState.gamePhase,
                availableMoves: gameState.availableMoves
            )

            Rectangle()
                .fill(Color.gray.opacity(0.3))
                .frame(width: 1)
                .padding()
            RightView(gameManager: gameManager)
                .environmentObject(gameState)
        }
        .onAppear {
            setWindowTitle()
        }
    }

    private func setWindowTitle() {
        if let window = NSApplication.shared.windows.first {
            window.title = "这个大アサ的作者数分I期中考了25分高分"
        }
    }
}
@main
struct oasa25App:App
{
    var body: some Scene
    {
        WindowGroup
        {
            ContentView()
        }
    }
}
```
## 注
和我之前发的[计算概论A 2025年秋 大作业](http://cirrus.org.cn/p/计算概论a-2025年秋-大作业/)差不多，只是把.app文件编译成了一个单独的二进制文件。