Files
Home-of-CS/content/post/dev_20251209/index.md
T
2026-04-23 13:39:28 +08:00

8.8 KiB
Raw Blame History

+++ date = '2025-12-09T23:46:30+08:00' draft = false license = 'MIT Licence' title = '计算概论A 2025年秋 大作业' tags = ['计算概论'] +++

文件下载

package.zip

基本信息

  • 程序名称:oasa25
  • 编写语言:Swift 5
  • 编译环境:
    • IDEXcode Version 26.0.1 (17A400)
    • macOSTahoe 26.0.1 (25A362)
    • 设备:MacBook Air (13英寸, M3, 2024年)
  • 借助AIDeepSeek
    • 主要用途:
      • 在原来代码的基础上进行优化;
      • 讲解Swift语法;
      • 修复语法错误;
      • 协助给程序取名;
        • "oasa"(“おアサ”)是“大きいアサインメント”(“大作业”)的缩略;
        • "25"是我的数分I期中考试成绩。
  • 程序特色:
    • 使用上:
      • 友好的GUI界面;
        • 包含分栏显示;
        • 很多图标;
      • 二次确认,防止误操作;
      • 完善的存档保存/另存为/读取功能;
      • 清晰的状态显示;
      • 支持历史记录查看;
      • 合法路径绿色高亮显示,被阻挡的路径红色高亮显示;
      • 支持自由调节蒙特卡洛算法评分参数;
      • 终端输出蒙特卡洛方法日志;
      • 利用⌘+N进行多窗口同时操作。
    • 技术上:
      • 使用SwiftUI的现成控件实现GUI界面,减少所需代码;
      • 使用多个状态变量,状态划分清晰;
      • 代码分成不同struct,分工实现不同功能;
      • 变量名、函数名等清晰易读(感谢DeepSeek);
      • 并行处理,提高运行效率;
      • 实现蒙特卡洛算法;
      • 缺点:不管怎么调,算法都有点傻傻的,只比随机算法好一点点,在Botzone上被打得毫无还手之力(期末了😭难debug)。

文件组成

  • Assets.xcassets:存储程序的图标;
    • 来自教学网上“我的成绩”界面的截图;
  • ContentView.swift:存储程序的主要源代码;
  • oasa25样本(若无法运行,则在本机编译).appmacOS的应用程序包;
  • oasa25App.swift:程序主结构(实际代码较少,仅调用ContentView(),不涉及具体实现);
  • oasa25_README.md:程序说明(本文件)。
  • oasa25.xcodeprojXcode项目文件。
  • 自动移植版.cpp:使用DeepSeek帮忙移植的C++版本,用于Botzone对局(期末周😭救命)。

下面重点解说ContentView.swift的有关内容:

程序结构

  • import部分:
    • Combine:用于实现ObservableObject类型的GameState
    • SwiftUI:提供程序的UI控件;
    • UniformTypeIdentifiers:用于存取文件时对文件类型的规定;
  • enum GamePhase::设定三个游戏阶段的值;
    • 选择棋子阶段selectPiece
    • 移动棋子阶段movePiece
    • 放置障碍物阶段placeArrow
  • struct HistoryEntry:历史记录数据结构;
    • id+字符串;
  • struct gameData:规定游戏存档的结构;
    • 棋盘数据chessBoard(二维数组);
    • 回合数据blackRoundBool值);
    • 历史记录数据historyHistoryEntry数组);
    • 选中棋子位置selectedPieceRowselectedPieceCol(整数);
    • 阶段数据gamePhase
    • 上次计算的可用路径availableMoves(二维数组);
      • 读档后无需再次计算;
    • 游戏结束状态isGameOverBool值);
    • 自动下棋选项blackStrategywhiteStrategy(字符串);
    • 当前回合数roundNum(整数);
    • 蒙特卡洛方法计算结果mcList(数组);
    • 控制面板参数值blackControlFactorblackSafetyFactorblackSurroundFactorblackCenterFactorwhiteControlFactorwhiteSafetyFactorwhiteSurroundFactorwhiteCenterFactor(均为双精度浮点类型);
  • 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 checkWinConditionfunc 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在程序启动时设置窗口标题。