diff --git a/README.md b/README.md index 2defef9..d799475 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,95 @@ -# C语言五子棋人机对战AI +# C璇█浜斿瓙妫嬩汉鏈哄鎴楢I ![Build Status](https://img.shields.io/badge/build-passing-brightgreen) ![License](https://img.shields.io/badge/license-MIT-blue) -## 目录 -- [C语言五子棋人机对战AI](#c语言五子棋人机对战ai) - - [目录](#目录) - - [项目简介](#项目简介) - - [功能特性](#功能特性) - - [快速开始](#快速开始) - - [编译项目](#编译项目) - - [运行游戏](#运行游戏) - - [游戏玩法](#游戏玩法) - - [环境要求](#环境要求) - - [常见问题](#常见问题) - - [权限问题](#权限问题) - - [乱码显示问题](#乱码显示问题) - - [AI 设计实现](#ai-设计实现) - - [核心算法](#核心算法) - - [棋局评估函数](#棋局评估函数) - - [项目结构](#项目结构) - - [许可证](#许可证) - - [欢迎贡献](#欢迎贡献) - - [未来计划](#未来计划) +## 鐩綍 +- [C璇█浜斿瓙妫嬩汉鏈哄鎴楢I](#c璇█浜斿瓙妫嬩汉鏈哄鎴榓i) + - [鐩綍](#鐩綍) + - [椤圭洰绠浠媇(#椤圭洰绠浠) + - [鍔熻兘鐗规(#鍔熻兘鐗规) + - [蹇熷紑濮媇(#蹇熷紑濮) + - [缂栬瘧椤圭洰](#缂栬瘧椤圭洰) + - [杩愯娓告垙](#杩愯娓告垙) + - [娓告垙鐜╂硶](#娓告垙鐜╂硶) + - [鐜瑕佹眰](#鐜瑕佹眰) + - [甯歌闂](#甯歌闂) + - [鏉冮檺闂](#鏉冮檺闂) + - [涔辩爜鏄剧ず闂](#涔辩爜鏄剧ず闂) + - [AI 璁捐瀹炵幇](#ai-璁捐瀹炵幇) + - [鏍稿績绠楁硶](#鏍稿績绠楁硶) + - [妫嬪眬璇勪及鍑芥暟](#妫嬪眬璇勪及鍑芥暟) + - [椤圭洰缁撴瀯](#椤圭洰缁撴瀯) + - [璁稿彲璇乚(#璁稿彲璇) + - [娆㈣繋璐$尞](#娆㈣繋璐$尞) + - [鏈潵璁″垝](#鏈潵璁″垝) -## 项目简介 -这是一个使用C语言实现的五子棋人机对战系统,它基于 Alpha-Beta 剪枝优化的 Minimax 算法,并支持自定义棋盘大小、游戏存档和实时悔棋。 +## 椤圭洰绠浠 +杩欐槸涓涓娇鐢–璇█瀹炵幇鐨勪簲瀛愭浜烘満瀵规垬绯荤粺锛屽畠鍩轰簬 Alpha-Beta 鍓灊浼樺寲鐨 Minimax 绠楁硶锛屽苟鏀寔鑷畾涔夋鐩樺ぇ灏忋佹父鎴忓瓨妗e拰瀹炴椂鎮旀銆 -## 功能特性 -- 人机对战模式 -- chessboard 自定义棋盘尺寸 (5x5至25x25) -- 多级AI难度 (1-5级可调) -- 自动游戏存档与加载 -- 实时对局控制系统 -- 悔棋功能 (可撤销上一步) -- 纯粹的终端界面显示 -- 完备的输入验证 (确保所有用户输入都在有效范围内) -- 可选的回合计时器 -- 自动游戏记录保存 +## 鍔熻兘鐗规 +- 浜烘満瀵规垬妯″紡 +- chessboard 鑷畾涔夋鐩樺昂瀵 (5x5鑷25x25) +- 澶氱骇AI闅惧害 (1-5绾у彲璋) +- 鑷姩娓告垙瀛樻。涓庡姞杞 +- 瀹炴椂瀵瑰眬鎺у埗绯荤粺 +- 鎮旀鍔熻兘 (鍙挙閿涓婁竴姝) +- 绾补鐨勭粓绔晫闈㈡樉绀 +- 瀹屽鐨勮緭鍏ラ獙璇 (纭繚鎵鏈夌敤鎴疯緭鍏ラ兘鍦ㄦ湁鏁堣寖鍥村唴) +- 鍙夌殑鍥炲悎璁℃椂鍣 +- 鑷姩娓告垙璁板綍淇濆瓨 +- 澶嶇洏鍔熻兘 (鏀寔淇濆瓨鍜屽洖椤惧灞璁板綍) +- 璇勫垎绯荤粺 (鍙互瀵规瘡涓姝ユ杩涜璇勫垎鍜屽垎鏋) +- 绂佹墜瑙勫垯鏀寔 (闃叉鐜╁杩涜鏃犳剰涔夌殑璧版硶) +- 鍐呭瓨绠$悊浼樺寲 (鏀硅繘浜嗗唴瀛樼鐞嗭紝鍑忓皯浜嗚祫婧愬崰鐢) +- 妯″潡鍖栬璁 (渚夸簬鍔熻兘鎵╁睍鍜岀淮鎶) +- 澧炲己閿欒鎻愮ず (甯姪鐢ㄦ埛蹇熷畾浣嶉棶棰) -## 快速开始 +## 蹇熷紑濮 -### 编译项目 +### 缂栬瘧椤圭洰 ```bash -gcc 五子棋.c gobang.c game_mode.c init_board.c record.c ai.c -o gobang.exe +gcc 浜斿瓙妫.c gobang.c game_mode.c init_board.c record.c ai.c -o gobang.exe ``` -### 运行游戏 +### 杩愯娓告垙 ```bash .\gobang.exe ``` -## 游戏玩法 -1. 启动程序后设置棋盘大小 (默认为15x15) -2. 选择AI难度等级 (1-5) -3. 轮流输入坐标进行游戏 (格式:行 列) - - 输入R/r可悔棋 -4. 游戏结束后可查看对局存档和日志 +## 娓告垙鐜╂硶 +1. 鍚姩绋嬪簭鍚庤缃鐩樺ぇ灏 (榛樿涓15x15) +2. 閫夋嫨AI闅惧害绛夌骇 (1-5) +3. 杞祦杈撳叆鍧愭爣杩涜娓告垙 (鏍煎紡:琛 鍒) + - 杈撳叆R/r鍙倲妫 +4. 娓告垙缁撴潫鍚庡彲鏌ョ湅瀵瑰眬瀛樻。鍜屾棩蹇 -## 环境要求 -- 操作系统: Windows (当前版本使用了Windows特有的 `_kbhit()` 和 `Sleep()` 函数,因此暂不跨平台) -- 编译器: GCC (MinGW-w64) -- 终端: 支持UTF-8编码的终端 +## 鐜瑕佹眰 +- 鎿嶄綔绯荤粺: Windows (褰撳墠鐗堟湰浣跨敤浜哤indows鐗规湁鐨 `_kbhit()` 鍜 `Sleep()` 鍑芥暟锛屽洜姝ゆ殏涓嶈法骞冲彴) +- 缂栬瘧鍣: GCC (MinGW-w64) +- 缁堢: 鏀寔UTF-8缂栫爜鐨勭粓绔 -> **跨平台兼容性说明:** +> **璺ㄥ钩鍙板吋瀹规ц鏄:** > -> 为了未来在Linux或macOS等其他操作系统上运行,需要将平台特定的代码(如 `_kbhit()`)替换为跨平台的实现,或使用条件编译(`#ifdef _WIN32`)进行隔离。 +> 涓轰簡鏈潵鍦↙inux鎴杕acOS绛夊叾浠栨搷浣滅郴缁熶笂杩愯锛岄渶瑕佸皢骞冲彴鐗瑰畾鐨勪唬鐮侊紙濡 `_kbhit()`锛夋浛鎹负璺ㄥ钩鍙扮殑瀹炵幇锛屾垨浣跨敤鏉′欢缂栬瘧锛坄#ifdef _WIN32`锛夎繘琛岄殧绂汇 -## 常见问题 +## 甯歌闂 -### 权限问题 -如果在保存游戏记录时提示“无法创建文件”,这通常是由于程序缺少写入权限。请尝试以下解决方案: +### 鏉冮檺闂 +濡傛灉鍦ㄤ繚瀛樻父鎴忚褰曟椂鎻愮ず鈥滄棤娉曞垱寤烘枃浠垛濓紝杩欓氬父鏄敱浜庣▼搴忕己灏戝啓鍏ユ潈闄愩傝灏濊瘯浠ヤ笅瑙e喅鏂规锛 -1. **以管理员身份运行**:右键点击 `gobang.exe` 并在管理员权限的终端中运行程序。 -2. **更改项目目录权限**:确保项目目录不在受系统保护的目录(如 `C:\Program Files`),建议将项目放在用户目录下,例如 `D:\Code`。 -3. **手动创建 `records` 目录**:如果 `records` 目录不存在,请在 `gobang.exe` 所在目录手动创建一个。 +1. **浠ョ鐞嗗憳韬唤杩愯**锛氬彸閿偣鍑 `gobang.exe` 骞跺湪绠$悊鍛樻潈闄愮殑缁堢涓繍琛岀▼搴忋 +2. **鏇存敼椤圭洰鐩綍鏉冮檺**锛氱‘淇濋」鐩洰褰曚笉鍦ㄥ彈绯荤粺淇濇姢鐨勭洰褰曪紙濡 `C:\Program Files`锛夛紝寤鸿灏嗛」鐩斁鍦ㄧ敤鎴风洰褰曚笅锛屼緥濡 `D:\Code`銆 +3. **鎵嬪姩鍒涘缓 `records` 鐩綍**锛氬鏋 `records` 鐩綍涓嶅瓨鍦紝璇峰湪 `gobang.exe` 鎵鍦ㄧ洰褰曟墜鍔ㄥ垱寤轰竴涓 -### 乱码显示问题 -如果在Windows终端中出现中文字符显示为乱码,这是由于终端代码页不匹配导致的。请在程序运行前执行以下命令: +### 涔辩爜鏄剧ず闂 +濡傛灉鍦╓indows缁堢涓嚭鐜颁腑鏂囧瓧绗︽樉绀轰负涔辩爜锛岃繖鏄敱浜庣粓绔唬鐮侀〉涓嶅尮閰嶅鑷寸殑銆傝鍦ㄧ▼搴忚繍琛屽墠鎵ц浠ヤ笅鍛戒护锛 ```bash chcp 65001 ``` -这会把当前终端的代码页切换为UTF-8,从而正确显示中文字符。为了方便,你可以创建一个批处理文件 `.bat` 来自动执行此操作。 +杩欎細鎶婂綋鍓嶇粓绔殑浠g爜椤靛垏鎹负UTF-8锛屼粠鑰屾纭樉绀轰腑鏂囧瓧绗︺備负浜嗘柟渚匡紝浣犲彲浠ュ垱寤轰竴涓壒澶勭悊鏂囦欢 `.bat` 鏉ヨ嚜鍔ㄦ墽琛屾鎿嶄綔銆 **start_game.bat** ```batch @@ -92,58 +98,58 @@ chcp 65001 .\gobang.exe ``` -## AI 设计实现 +## AI 璁捐瀹炵幇 -项目的AI主要基于以下技术实现: +椤圭洰鐨凙I涓昏鍩轰簬浠ヤ笅鎶鏈疄鐜帮細 -### 核心算法 +### 鏍稿績绠楁硶 -- **Minimax算法 (Minimax)**:作为博弈树的基础模型,为双人对弈的每一步选择最优解法。 -- **Alpha-Beta 剪枝 (Alpha-Beta Pruning)**:对Minimax算法的重大优化,通过剪掉那些不影响最终决策的树枝来提高AI的计算效率,使其能够在有限时间内达到更深的搜索深度。 -- **搜索深度**:AI的思考深度,默认为3层,可以根据难度等级进行调整。深度越大,AI预测能力越强,但计算耗时也越长。 +- **Minimax绠楁硶 (Minimax)**锛氫綔涓哄崥寮堟爲鐨勫熀纭妯″瀷锛屼负鍙屼汉瀵瑰紙鐨勬瘡涓姝ラ夋嫨鏈浼樿В娉曘 +- **Alpha-Beta 鍓灊 (Alpha-Beta Pruning)**锛氬Minimax绠楁硶鐨勯噸澶т紭鍖栵紝閫氳繃鍓帀閭d簺涓嶅奖鍝嶆渶缁堝喅绛栫殑鏍戞灊鏉ユ彁楂楢I鐨勮绠楁晥鐜囷紝浣垮叾鑳藉鍦ㄦ湁闄愭椂闂村唴杈惧埌鏇存繁鐨勬悳绱㈡繁搴︺ +- **鎼滅储娣卞害**锛欰I鐨勬濊冩繁搴︼紝榛樿涓3灞傦紝鍙互鏍规嵁闅惧害绛夌骇杩涜璋冩暣銆傛繁搴﹁秺澶э紝AI棰勬祴鑳藉姏瓒婂己锛屼絾璁$畻鑰楁椂涔熻秺闀裤 -### 棋局评估函数 +### 妫嬪眬璇勪及鍑芥暟 -为了对棋局进行价值评估,AI使用了一套复杂的评分系统,其主要依据包括: +涓轰簡瀵规灞杩涜浠峰艰瘎浼帮紝AI浣跨敤浜嗕竴濂楀鏉傜殑璇勫垎绯荤粺锛屽叾涓昏渚濇嵁鍖呮嫭锛 -- **棋型识别 (Pattern Recognition)**:能够识别并评估游戏中的关键棋型,如“连五”、“活四”、“冲四”、“活三”等,并为每种棋型赋予不同权重。 -- **位置权重 (Positional Value)**:棋盘上不同位置的战略价值不同,中心位置通常比边缘位置更有优势。评估函数会为棋盘上的落子点附加位置分。 -- **威胁检测 (Threat Detection)**:评估那些能够直接形成制胜局面的落子点,如“四三”或“活三”,并对这些点给予极高的评价值,以抓住制胜机会。 -- **双向连通性**:在评估一个点时,会同时判断其是否拥有足够的空间形成有效棋型,避免在被封锁的位置下出无效棋。 +- **妫嬪瀷璇嗗埆 (Pattern Recognition)**锛氳兘澶熻瘑鍒苟璇勪及娓告垙涓殑鍏抽敭妫嬪瀷锛屽鈥滆繛浜斺濄佲滄椿鍥涒濄佲滃啿鍥涒濄佲滄椿涓夆濈瓑锛屽苟涓烘瘡绉嶆鍨嬭祴浜堜笉鍚屾潈閲嶃 +- **浣嶇疆鏉冮噸 (Positional Value)**锛氭鐩樹笂涓嶅悓浣嶇疆鐨勬垬鐣ヤ环鍊间笉鍚岋紝涓績浣嶇疆閫氬父姣旇竟缂樹綅缃洿鏈変紭鍔裤傝瘎浼板嚱鏁颁細涓烘鐩樹笂鐨勮惤瀛愮偣闄勫姞浣嶇疆鍒嗐 +- **濞佽儊妫娴 (Threat Detection)**锛氳瘎浼伴偅浜涜兘澶熺洿鎺ュ舰鎴愬埗鑳滃眬闈㈢殑钀藉瓙鐐癸紝濡傗滃洓涓夆濇垨鈥滄椿涓夆濓紝骞跺杩欎簺鐐圭粰浜堟瀬楂樼殑璇勪环鍊硷紝浠ユ姄浣忓埗鑳滄満浼氥 +- **鍙屽悜杩為氭**锛氬湪璇勪及涓涓偣鏃讹紝浼氬悓鏃跺垽鏂叾鏄惁鎷ユ湁瓒冲鐨勭┖闂村舰鎴愭湁鏁堟鍨嬶紝閬垮厤鍦ㄨ灏侀攣鐨勪綅缃笅鍑烘棤鏁堟銆 -## 项目结构 -- `五子棋.c` - 主程序入口,负责初始化与游戏模式选择。 -- `gobang.c` - 核心游戏逻辑,包括棋盘操作、胜负判断。 -- `gobang.h` - `gobang.c` 的头文件,定义了核心数据结构和函数原型。 -- `game_mode.c` - 各种游戏模式的实现,如人机对战、双人对战和观战模式。 -- `game_mode.h` - `game_mode.c` 的头文件,声明游戏模式相关函数原型。 -- `init_board.c` - 棋盘初始化相关功能,包括设置棋盘大小、游戏选项等。 -- `init_board.h` - `init_board.c` 的头文件。 -- `record.c` - 游戏记录的保存和读取功能。 -- `record.h` - `record.c` 的头文件。 -- `ai.c` - AI算法的实现,包括Minimax和Alpha-Beta剪枝。 -- `ai.h` - `ai.c` 的头文件。 +## 椤圭洰缁撴瀯 +- `浜斿瓙妫.c` - 涓荤▼搴忓叆鍙o紝璐熻矗鍒濆鍖栦笌娓告垙妯″紡閫夋嫨銆 +- `gobang.c` - 鏍稿績娓告垙閫昏緫锛屽寘鎷鐩樻搷浣溿佽儨璐熷垽鏂 +- `gobang.h` - `gobang.c` 鐨勫ご鏂囦欢锛屽畾涔変簡鏍稿績鏁版嵁缁撴瀯鍜屽嚱鏁板師鍨嬨 +- `game_mode.c` - 鍚勭娓告垙妯″紡鐨勫疄鐜帮紝濡備汉鏈哄鎴樸佸弻浜哄鎴樺拰瑙傛垬妯″紡銆 +- `game_mode.h` - `game_mode.c` 鐨勫ご鏂囦欢锛屽0鏄庢父鎴忔ā寮忕浉鍏冲嚱鏁板師鍨嬨 +- `init_board.c` - 妫嬬洏鍒濆鍖栫浉鍏冲姛鑳斤紝鍖呮嫭璁剧疆妫嬬洏澶у皬銆佹父鎴忛夐」绛夈 +- `init_board.h` - `init_board.c` 鐨勫ご鏂囦欢銆 +- `record.c` - 娓告垙璁板綍鐨勪繚瀛樺拰璇诲彇鍔熻兘銆 +- `record.h` - `record.c` 鐨勫ご鏂囦欢銆 +- `ai.c` - AI绠楁硶鐨勫疄鐜帮紝鍖呮嫭Minimax鍜孉lpha-Beta鍓灊銆 +- `ai.h` - `ai.c` 鐨勫ご鏂囦欢銆 -## 许可证 +## 璁稿彲璇 -本项目采用 [MIT 许可证](https://opensource.org/licenses/MIT)授权。 +鏈」鐩噰鐢 [MIT 璁稿彲璇乚(https://opensource.org/licenses/MIT)鎺堟潈銆 -这意味着你可以自由地使用、复制、修改、合并、出版、分发、再授权和/或销售本软件的副本,只需在所有副本或重要部分中包含原始的版权声明和本许可声明即可。 +杩欐剰鍛崇潃浣犲彲浠ヨ嚜鐢卞湴浣跨敤銆佸鍒躲佷慨鏀广佸悎骞躲佸嚭鐗堛佸垎鍙戙佸啀鎺堟潈鍜/鎴栭攢鍞湰杞欢鐨勫壇鏈紝鍙渶鍦ㄦ墍鏈夊壇鏈垨閲嶈閮ㄥ垎涓寘鍚師濮嬬殑鐗堟潈澹版槑鍜屾湰璁稿彲澹版槑鍗冲彲銆 -## 欢迎贡献 +## 娆㈣繋璐$尞 -我们非常欢迎任何形式的反馈和贡献!如果你发现了Bug、有功能建议,或希望改进代码,请随时通过以下方式参与: +鎴戜滑闈炲父娆㈣繋浠讳綍褰㈠紡鐨勫弽棣堝拰璐$尞锛佸鏋滀綘鍙戠幇浜咮ug銆佹湁鍔熻兘寤鸿锛屾垨甯屾湜鏀硅繘浠g爜锛岃闅忔椂閫氳繃浠ヤ笅鏂瑰紡鍙備笌锛 -- **提交 Issue**:对于问题反馈或新想法,请在 [GitHub Issues](https://github.com/LHY0125/Gobang-Game/issues) 页面提交详细描述。 -- **发起 Pull Request**:如果你对源码进行了改进,欢迎提交 Pull Request。请确保你的代码风格与项目保持一致,并提供清晰的改动说明。 +- **鎻愪氦 Issue**锛氬浜庨棶棰樺弽棣堟垨鏂版兂娉曪紝璇峰湪 [GitHub Issues](https://github.com/LHY0125/Gobang-Game/issues) 椤甸潰鎻愪氦璇︾粏鎻忚堪銆 +- **鍙戣捣 Pull Request**锛氬鏋滀綘瀵规簮鐮佽繘琛屼簡鏀硅繘锛屾杩庢彁浜 Pull Request銆傝纭繚浣犵殑浠g爜椋庢牸涓庨」鐩繚鎸佷竴鑷达紝骞舵彁渚涙竻鏅扮殑鏀瑰姩璇存槑銆 -你的每一次贡献都将使这个项目变得更好! +浣犵殑姣忎竴娆¤础鐚兘灏嗕娇杩欎釜椤圭洰鍙樺緱鏇村ソ锛 -## 未来计划 +## 鏈潵璁″垝 -为了让这个项目变得更好,我们计划在未来实现以下功能: +涓轰簡璁╄繖涓」鐩彉寰楁洿濂斤紝鎴戜滑璁″垝鍦ㄦ湭鏉ュ疄鐜颁互涓嬪姛鑳斤細 -- [ ] **图形用户界面 (GUI)**:使用 `SDL2` 或 `Qt` 等库,将当前的终端界面替换为图形化界面,提升用户体验。 -- [ ] **网络对战功能**:增加一个在线对战模式,让玩家可以通过网络进行对战。 -- [ ] **棋谱库工具**:引入开局库,使AI在游戏开局阶段能够选择最优的落子方案。 -- [ ] **代码重构与优化**:持续优化现有代码,提高模块化程度和运行效率,并实现完全的跨平台兼容性。 \ No newline at end of file +- [ ] **鍥惧舰鐢ㄦ埛鐣岄潰 (GUI)**锛氫娇鐢 `SDL2` 鎴 `Qt` 绛夊簱锛屽皢褰撳墠鐨勭粓绔晫闈㈡浛鎹负鍥惧舰鍖栫晫闈紝鎻愬崌鐢ㄦ埛浣撻獙銆 +- [ ] **缃戠粶瀵规垬鍔熻兘**锛氬鍔犱竴涓湪绾垮鎴樻ā寮忥紝璁╃帺瀹跺彲浠ラ氳繃缃戠粶杩涜瀵规垬銆 +- [ ] **妫嬭氨搴撳伐鍏**锛氬紩鍏ュ紑灞搴擄紝浣緼I鍦ㄦ父鎴忓紑灞闃舵鑳藉閫夋嫨鏈浼樼殑钀藉瓙鏂规銆 +- [ ] **浠g爜閲嶆瀯涓庝紭鍖**锛氭寔缁紭鍖栫幇鏈変唬鐮侊紝鎻愰珮妯″潡鍖栫▼搴﹀拰杩愯鏁堢巼锛屽苟瀹炵幇瀹屽叏鐨勮法骞冲彴鍏煎鎬с \ No newline at end of file diff --git a/ai.c b/ai.c index 8d49da8..4665b82 100644 --- a/ai.c +++ b/ai.c @@ -1,11 +1,12 @@ #include "gobang.h" #include "ai.h" +#include "config.h" #include #include #include // 闃插畧绯绘暟 -double defense_coefficient = 1.2; +double defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT; extern int BOARD_SIZE; extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE]; extern int step_count; @@ -64,7 +65,7 @@ int evaluate_pos(int x, int y, int player) if (info.continuous_chess >= 5) { board[x][y] = original; // 杩樺師妫嬬洏 - return 1000000; // 杩斿洖鏈澶у垎 + return SEARCH_WIN_BONUS; // 杩斿洖鏈澶у垎 } // 鏍规嵁杩炵画妫嬪瓙鏁拌瘎鍒 @@ -72,38 +73,38 @@ int evaluate_pos(int x, int y, int player) { case 4: // 鍥涜繛鐝 if (info.check_start && info.check_end) // 娲诲洓(涓ょ寮鏀) - line_scores[i] = 100000; + line_scores[i] = AI_SCORE_LIVE_FOUR; else if (info.check_start || info.check_end) // 鍐插洓(涓绔紑鏀) - line_scores[i] = 10000; + line_scores[i] = AI_SCORE_RUSH_FOUR; else // 姝诲洓(涓ょ灏侀棴) - line_scores[i] = 500; + line_scores[i] = AI_SCORE_DEAD_FOUR; break; case 3: // 涓夎繛鐝 if (info.check_start && info.check_end) // 娲讳笁 - line_scores[i] = 5000; + line_scores[i] = AI_SCORE_LIVE_THREE; else if (info.check_start || info.check_end) // 鐪犱笁 - line_scores[i] = 1000; + line_scores[i] = AI_SCORE_SLEEP_THREE; else // 姝讳笁 - line_scores[i] = 50; + line_scores[i] = AI_SCORE_DEAD_THREE; break; case 2: // 浜岃繛鐝 if (info.check_start && info.check_end) // 娲讳簩 - line_scores[i] = 500; + line_scores[i] = AI_SCORE_LIVE_TWO; else if (info.check_start || info.check_end) // 鐪犱簩 - line_scores[i] = 100; + line_scores[i] = AI_SCORE_SLEEP_TWO; else // 姝讳簩 - line_scores[i] = 10; + line_scores[i] = AI_SCORE_DEAD_TWO; break; case 1: // 鍗曞瓙 if (info.check_start && info.check_end) // 寮鏀句綅缃 - line_scores[i] = 50; + line_scores[i] = AI_SCORE_LIVE_ONE; else if (info.check_start || info.check_end) // 鍗婂紑鏀句綅缃 - line_scores[i] = 10; + line_scores[i] = AI_SCORE_HALF_ONE; else // 灏侀棴浣嶇疆 - line_scores[i] = 1; + line_scores[i] = AI_SCORE_DEAD_ONE; break; } } @@ -125,7 +126,7 @@ int evaluate_pos(int x, int y, int player) int center_x = BOARD_SIZE / 2; int center_y = BOARD_SIZE / 2; int distance = abs(x - center_x) + abs(y - center_y); // 鏇煎搱椤胯窛绂 - int position_bonus = 50 * (BOARD_SIZE - distance); // 璺濈涓績瓒婅繎濂栧姳瓒婇珮 + int position_bonus = AI_POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 璺濈涓績瓒婅繎濂栧姳瓒婇珮 board[x][y] = original; // 杩樺師妫嬬洏鐘舵 return total_score + position_bonus; // 杩斿洖鎬昏瘎浼板垎 @@ -153,7 +154,7 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi // 妫鏌ュ綋鍓嶈惤瀛愭槸鍚﹁幏鑳 if (check_win(x, y, player)) { - return (player == AI) ? 1000000 + depth : -1000000 - depth; + return (player == AI) ? SEARCH_WIN_BONUS + depth : -SEARCH_WIN_BONUS - depth; } // 杈惧埌鎼滅储娣卞害鎴栧钩灞 @@ -224,11 +225,21 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi * 2. 杩涙敾闃舵锛氳嫢鏃犵揣鎬ラ槻寰¢渶姹傦紝浣跨敤璇勪及鍑芥暟閫夋嫨鏈浣宠繘鏀讳綅缃 * @note 瀹炵幇缁嗚妭锛 * - 浼樺厛澶勭悊鐜╁娲诲洓銆佸啿鍥涚瓑鍗遍櫓灞闈 - * - 姝ユ暟>10鏃剁缉灏忔悳绱㈣寖鍥村埌宸叉湁妫嬪瓙闄勮繎2鏍 + * - 姝ユ暟>AI_SEARCH_RANGE_THRESHOLD鏃剁缉灏忔悳绱㈣寖鍥村埌宸叉湁妫嬪瓙闄勮繎AI_NEARBY_RANGE鏍 * - 浣跨敤涓績浣嶇疆浼樺厛绛栫暐 */ void ai_move(int depth) { + // 濡傛灉鏄涓姝ワ紝鐩存帴涓嬪湪涓績浣嶇疆闄勮繎 + if (step_count == 0) + { + int center = BOARD_SIZE / 2; + board[center][center] = AI; + steps[step_count++] = (Step){AI, center, center}; + printf("AI钀藉瓙(%d, %d)\n", center + 1, center + 1); + return; + } + // 1. 棣栧厛妫鏌ユ槸鍚﹂渶瑕侀樆姝㈢帺瀹剁殑鍥涘瓙杩炴鎴栦笁瀛愭椿妫 for (int i = 0; i < BOARD_SIZE; i++) { @@ -277,7 +288,7 @@ void ai_move(int depth) } // 2. 濡傛灉娌℃湁闇瑕佺珛鍗抽樆姝㈢殑鎯呭喌锛屽垯姝e父璇勪及 - int best_score = -1000000; + int best_score = -SEARCH_WIN_BONUS; int best_x = -1, best_y = -1; // 閬嶅巻妫嬬洏鎵鏈夌┖浣 @@ -290,11 +301,11 @@ void ai_move(int depth) continue; } - // 鍙冭檻宸叉湁妫嬪瓙闄勮繎(2鏍艰寖鍥村唴) + // 鍙冭檻宸叉湁妫嬪瓙闄勮繎(AI_NEARBY_RANGE鏍艰寖鍥村唴) bool has_nearby_stone = false; - for (int di = -2; di <= 2; di++) + for (int di = -AI_NEARBY_RANGE; di <= AI_NEARBY_RANGE; di++) { - for (int dj = -2; dj <= 2; dj++) + for (int dj = -AI_NEARBY_RANGE; dj <= AI_NEARBY_RANGE; dj++) { int ni = i + di; int nj = j + dj; @@ -312,7 +323,7 @@ void ai_move(int depth) break; } } - if (!has_nearby_stone && step_count > 10) + if (!has_nearby_stone && step_count > AI_SEARCH_RANGE_THRESHOLD) { continue; } diff --git a/config.h b/config.h new file mode 100644 index 0000000..d45dc43 --- /dev/null +++ b/config.h @@ -0,0 +1,94 @@ +/** + * @file config.h + * @author 鍒樿埅瀹(3364451258@qq.com銆15236416560@163.com銆乴hy3364451258@outlook.com) + * @brief 浜斿瓙妫嬫父鎴忓弬鏁伴厤缃ご鏂囦欢 + * @version 1.0 + * @date 2025-07-09 + * + * @copyright Copyright (c) 2025 + * + * @note 鏈枃浠堕泦涓畾涔変簡浜斿瓙妫嬫父鎴忕殑鎵鏈夊弬鏁伴厤缃紝渚夸簬缁熶竴绠$悊鍜屼慨鏀 + */ + +#ifndef CONFIG_H +#define CONFIG_H + +//---------- 妫嬬洏鐩稿叧鍙傛暟 ----------// +#define MAX_BOARD_SIZE 25 // 鏀寔鐨勬渶澶ф鐩樺昂瀵 +#define DEFAULT_BOARD_SIZE 15 // 榛樿妫嬬洏灏哄 +#define MAX_STEPS (MAX_BOARD_SIZE * MAX_BOARD_SIZE) // 娓告垙鏈澶ф鏁 + +//---------- 鐜╁鏍囪瘑鍙傛暟 ----------// +#define EMPTY 0 // 妫嬬洏绌轰綅鏍囪瘑 +#define PLAYER 1 // 鐜╁鏍囪瘑 (鐢ㄤ簬浜烘満瀵规垬妯″紡) +#define AI 2 // AI鏍囪瘑 (鐢ㄤ簬浜烘満瀵规垬妯″紡) +#define PLAYER1 1 // 鐜╁1鏍囪瘑 (鐢ㄤ簬鍙屼汉瀵规垬妯″紡) +#define PLAYER2 2 // 鐜╁2鏍囪瘑 (鐢ㄤ簬鍙屼汉瀵规垬妯″紡) + +//---------- 鐗规畩杈撳叆鍛戒护 ----------// +#define INPUT_UNDO -1 // 鎮旀 +#define INPUT_SAVE -2 // 淇濆瓨 +#define INPUT_EXIT -3 // 閫鍑 +#define INPUT_SURRENDER -4 // 璁よ緭 + +//---------- 娓告垙璁剧疆榛樿鍊 ----------// +#define DEFAULT_USE_FORBIDDEN_MOVES false // 榛樿涓嶅惎鐢ㄧ鎵嬭鍒 +#define DEFAULT_USE_TIMER 0 // 榛樿涓嶅惎鐢ㄨ鏃跺櫒 +#define DEFAULT_TIME_LIMIT 30 // 榛樿鏃堕棿闄愬埗涓30绉 + +//---------- AI鍙傛暟 ----------// +#define DEFAULT_AI_DEPTH 3 // 榛樿AI鎼滅储娣卞害 +#define DEFAULT_DEFENSE_COEFFICIENT 1.2 // 榛樿闃插畧绯绘暟 + +//---------- 璇勫垎鍙傛暟 ----------// +// 妫嬪瀷璇勫垎 - 鐢ㄤ簬calculate_step_score鍑芥暟 +#define SCORE_FIVE 0 // 浜旇繛 +#define SCORE_LIVE_FOUR 2000 // 娲诲洓 +#define SCORE_RUSH_FOUR 1000 // 鍐插洓 +#define SCORE_DEAD_FOUR 300 // 姝诲洓 +#define SCORE_LIVE_THREE 500 // 娲讳笁 +#define SCORE_SLEEP_THREE 200 // 鐪犱笁 +#define SCORE_DEAD_THREE 80 // 姝讳笁 +#define SCORE_LIVE_TWO 100 // 娲讳簩 +#define SCORE_SLEEP_TWO 40 // 鐪犱簩 +#define SCORE_DEAD_TWO 15 // 姝讳簩 +#define SCORE_LIVE_ONE 15 // 寮鏀惧崟瀛 +#define SCORE_HALF_ONE 8 // 鍗婂紑鏀惧崟瀛 +#define SCORE_DEAD_ONE 2 // 灏侀棴鍗曞瓙 + +// 浣嶇疆濂栧姳绯绘暟 +#define POSITION_BONUS_FACTOR 10 // 浣嶇疆濂栧姳鍥犲瓙 + +// AI璇勪及鍙傛暟 - 鐢ㄤ簬evaluate_pos鍑芥暟 +#define AI_SCORE_FIVE 1000000 // AI璇勪及-浜旇繛 +#define AI_SCORE_LIVE_FOUR 100000 // AI璇勪及-娲诲洓 +#define AI_SCORE_RUSH_FOUR 10000 // AI璇勪及-鍐插洓 +#define AI_SCORE_DEAD_FOUR 500 // AI璇勪及-姝诲洓 +#define AI_SCORE_LIVE_THREE 5000 // AI璇勪及-娲讳笁 +#define AI_SCORE_SLEEP_THREE 1000 // AI璇勪及-鐪犱笁 +#define AI_SCORE_DEAD_THREE 50 // AI璇勪及-姝讳笁 +#define AI_SCORE_LIVE_TWO 500 // AI璇勪及-娲讳簩 +#define AI_SCORE_SLEEP_TWO 100 // AI璇勪及-鐪犱簩 +#define AI_SCORE_DEAD_TWO 10 // AI璇勪及-姝讳簩 +#define AI_SCORE_LIVE_ONE 50 // AI璇勪及-寮鏀惧崟瀛 +#define AI_SCORE_HALF_ONE 10 // AI璇勪及-鍗婂紑鏀惧崟瀛 +#define AI_SCORE_DEAD_ONE 1 // AI璇勪及-灏侀棴鍗曞瓙 + +// AI浣嶇疆濂栧姳绯绘暟 +#define AI_POSITION_BONUS_FACTOR 50 // AI浣嶇疆濂栧姳鍥犲瓙 + +// 鎼滅储绠楁硶鍙傛暟 +#define SEARCH_MAX_SCORE 1000000 // 鎼滅储鏈澶у垎鏁 +#define SEARCH_WIN_BONUS 1000000 // 鑾疯儨濂栧姳鍒嗘暟 +#define AI_NEARBY_RANGE 2 // AI鎼滅储鐨勯偦杩戣寖鍥 +#define AI_SEARCH_RANGE_THRESHOLD 10 // AI寮濮嬮檺鍒舵悳绱㈣寖鍥寸殑姝ユ暟闃堝 + +// 璇勫垎鏉冮噸鍙傛暟 +#define TIME_WEIGHT_FACTOR 0.5 // 鏃堕棿鏉冮噸鍥犲瓙 +#define WIN_BONUS 2000 // 鑳滃埄濂栧姳鍒嗘暟 + +// 鏂囦欢璺緞鍙傛暟 +#define RECORDS_DIR "records" // 璁板綍鏂囦欢鐩綍 +#define MAX_PATH_LENGTH 256 // 鏈澶ц矾寰勯暱搴 + +#endif // CONFIG_H \ No newline at end of file diff --git a/game_mode.c b/game_mode.c index 839c777..23d7ce3 100644 --- a/game_mode.c +++ b/game_mode.c @@ -3,10 +3,14 @@ #include "gobang.h" #include "ai.h" #include "record.h" +#include "config.h" #include #include #include #include + +// 寮曠敤record.h涓畾涔夌殑鍏ㄥ眬鍙橀噺 +extern int scores_calculated; #ifdef _WIN32 #include #include @@ -216,6 +220,9 @@ bool handle_player_turn(int current_player) */ void run_ai_game() { + // 閲嶇疆璇勫垎璁$畻鏍囧織锛岀‘淇濇瘡灞娓告垙閮戒細閲嶆柊璁$畻璇勫垎 + scores_calculated = 0; + setup_game_options(); // AI瀵规垬妯″紡 @@ -298,6 +305,9 @@ void run_ai_game() */ void run_pvp_game() { + // 閲嶇疆璇勫垎璁$畻鏍囧織锛岀‘淇濇瘡灞娓告垙閮戒細閲嶆柊璁$畻璇勫垎 + scores_calculated = 0; + setup_game_options(); // 鍙屼汉瀵规垬妯″紡 @@ -409,4 +419,8 @@ void run_review_mode() } review_process(game_mode); } + else + { + printf("鍔犺浇澶嶇洏鏂囦欢澶辫触锛佸彲鑳芥槸鏃х増鏈枃浠舵牸寮忔垨鏂囦欢鎹熷潖\n"); + } } \ No newline at end of file diff --git a/gobang.c b/gobang.c index e07862c..8bb6665 100644 --- a/gobang.c +++ b/gobang.c @@ -3,19 +3,20 @@ #include "gobang.h" #include "ai.h" #include "record.h" +#include "config.h" #include #include #include // 鍏ㄥ眬鍙橀噺瀹氫箟 -int BOARD_SIZE = 15; // 瀹為檯浣跨敤鐨勬鐩樺昂瀵(榛樿15) +int BOARD_SIZE = DEFAULT_BOARD_SIZE; // 瀹為檯浣跨敤鐨勬鐩樺昂瀵 int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE] = {0}; // 妫嬬洏鐘舵佸瓨鍌ㄦ暟缁(榛樿妫嬬洏鍏ㄧ┖涓0) Step steps[MAX_STEPS]; // 瀛樺偍鎵鏈夎惤瀛愭楠ょ殑鏁扮粍 const int direction[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; // 鍥涗釜鏂瑰悜锛氬悜涓嬨佸悜鍙炽佸彸涓嬨佸乏涓 int step_count = 0; // 褰撳墠姝ユ暟璁℃暟鍣 -bool use_forbidden_moves = false; // 榛樿涓嶅惎鐢ㄧ鎵嬭鍒 -int use_timer = 0; // 榛樿涓嶅惎鐢ㄨ鏃跺櫒 -int time_limit = 30; // 榛樿鏃堕棿闄愬埗涓30绉 +bool use_forbidden_moves = DEFAULT_USE_FORBIDDEN_MOVES; // 鏄惁鍚敤绂佹墜瑙勫垯 +int use_timer = DEFAULT_USE_TIMER; // 鏄惁鍚敤璁℃椂鍣 +int time_limit = DEFAULT_TIME_LIMIT; // 姣忓洖鍚堢殑鏃堕棿闄愬埗锛堢锛 /** * @brief 妫鏌ユ鐩(x, y)浣嶇疆鏄惁涓虹┖ @@ -177,10 +178,6 @@ bool check_win(int x, int y, int player) return false; // 鍥涗釜鏂瑰悜閮芥病鏈変簲杩炵彔 } - - - - /** * @brief 鎮旀鍔熻兘瀹炵幇 * @@ -211,12 +208,12 @@ bool return_move(int steps_to_undo) * @brief 璇勪及鐜╁鍦ㄦ暣鐩樻灞涓殑琛ㄧ幇 * @param player 瑕佽瘎浼扮殑鐜╁(PLAYER/AI) * @return int 鎬诲垎(宸茶冭檻鏂瑰悜閲嶅璁$畻) - * @note 璇勫垎鏍囧噯: - * - 浜旇繛:2500 - * - 娲诲洓:1000 鍐插洓:500 姝诲洓:250 - * - 娲讳笁:250 鐪犱笁:100 姝讳笁:50 - * - 娲讳簩:50 鐪犱簩:20 姝讳簩:10 - * - 寮鏀惧崟瀛:10 鍗婂紑鏀惧崟瀛:5 灏侀棴鍗曞瓙:1 + * @note 鏀硅繘鍚庣殑璇勫垎鏍囧噯: + * - 浜旇繛:5000 (鎻愰珮鏉冮噸锛屾洿寮鸿皟鑾疯儨) + * - 娲诲洓:2000 鍐插洓:1000 姝诲洓:300 (鎻愰珮鏉冮噸锛屽己璋冭繘鏀绘) + * - 娲讳笁:500 鐪犱笁:200 姝讳笁:80 (鎻愰珮鏉冮噸锛屽己璋冩垬鐣ヤ环鍊) + * - 娲讳簩:100 鐪犱簩:40 姝讳簩:15 (閫傚綋鎻愰珮鏉冮噸) + * - 寮鏀惧崟瀛:15 鍗婂紑鏀惧崟瀛:8 灏侀棴鍗曞瓙:2 (閫傚綋鎻愰珮鏉冮噸) * @note 瀹炵幇缁嗚妭: * 1. 閬嶅巻妫嬬洏鎵鏈変綅缃 * 2. 瀵规瘡涓瀛愭鏌ュ洓涓柟鍚 @@ -234,41 +231,48 @@ int calculate_step_score(int x, int y, int player) switch (info.continuous_chess) { case 5: - step_score += 2500; + step_score += SCORE_FIVE; break; // 浜旇繛 case 4: if (info.check_start && info.check_end) - step_score += 1000; // 娲诲洓 + step_score += SCORE_LIVE_FOUR; // 娲诲洓 else if (info.check_start || info.check_end) - step_score += 500; // 鍐插洓 + step_score += SCORE_RUSH_FOUR; // 鍐插洓 else - step_score += 250; // 姝诲洓 + step_score += SCORE_DEAD_FOUR; // 姝诲洓 break; case 3: if (info.check_start && info.check_end) - step_score += 250; // 娲讳笁 + step_score += SCORE_LIVE_THREE; // 娲讳笁 else if (info.check_start || info.check_end) - step_score += 100; // 鐪犱笁 + step_score += SCORE_SLEEP_THREE; // 鐪犱笁 else - step_score += 50; // 姝讳笁 + step_score += SCORE_DEAD_THREE; // 姝讳笁 break; case 2: if (info.check_start && info.check_end) - step_score += 50; // 娲讳簩 + step_score += SCORE_LIVE_TWO; // 娲讳簩 else if (info.check_start || info.check_end) - step_score += 20; // 鐪犱簩 + step_score += SCORE_SLEEP_TWO; // 鐪犱簩 else - step_score += 10; // 姝讳簩 + step_score += SCORE_DEAD_TWO; // 姝讳簩 break; case 1: if (info.check_start && info.check_end) - step_score += 10; // 寮鏀惧崟瀛 + step_score += SCORE_LIVE_ONE; // 寮鏀惧崟瀛 else if (info.check_start || info.check_end) - step_score += 5; // 鍗婂紑鏀惧崟瀛 + step_score += SCORE_HALF_ONE; // 鍗婂紑鏀惧崟瀛 else - step_score += 1; // 灏侀棴鍗曞瓙 + step_score += SCORE_DEAD_ONE; // 灏侀棴鍗曞瓙 break; } } - return step_score; + + // 浣嶇疆濂栧姳锛氳秺闈犺繎涓績鍒嗘暟瓒婇珮 + int center_x = BOARD_SIZE / 2; + int center_y = BOARD_SIZE / 2; + int distance = abs(x - center_x) + abs(y - center_y); // 鏇煎搱椤胯窛绂 + int position_bonus = POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 璺濈涓績瓒婅繎濂栧姳瓒婇珮 + + return step_score + position_bonus; } \ No newline at end of file diff --git a/init_board.c b/init_board.c index 73412f0..15dceed 100644 --- a/init_board.c +++ b/init_board.c @@ -1,6 +1,7 @@ #include "init_board.h" #include "game_mode.h" #include "gobang.h" +#include "config.h" #include /** diff --git a/record.c b/record.c index 5b04d14..14dce19 100644 --- a/record.c +++ b/record.c @@ -2,6 +2,7 @@ #include "game_mode.h" #include "gobang.h" #include "init_board.h" +#include "config.h" #include #include #include @@ -26,11 +27,67 @@ * - 浣跨敤鐙珛涓存椂妫嬬洏閬垮厤褰卞搷涓绘父鎴忕姸鎬 * - 鍧愭爣鏄剧ず杞崲涓1-based鏂逛究鐢ㄦ埛鐞嗚В * - 鍖呭惈杈撳叆缂撳啿鍖烘竻鐞嗛槻姝㈡剰澶栬緭鍏 - * - 璇勫垎鐜妭璋冪敤evaluate_performance()鍑芥暟 + * - 璇勫垎鐜妭璋冪敤calculate_final_score()鍑芥暟 */ + +// 鍏ㄥ眬鍙橀噺锛岀敤浜庡瓨鍌ㄥ灞璇勫垎锛岀‘淇濆鎴樼粨鏉熷拰澶嶇洏妯″紡浣跨敤鐩稿悓鐨勮瘎鍒 +int player1_final_score = 0; +int player2_final_score = 0; +int scores_calculated = 0; + void review_process(int game_mode) { int review_choice = get_integer_input("鏄惁瑕佸鐩樻湰灞姣旇禌? (1-鏄, 0-鍚): ", 0, 1); + + // 濡傛灉璇勫垎灏氭湭璁$畻锛屽垯璁$畻璇勫垎 + if (!scores_calculated) + { + // 璇勪及鍙屾柟琛ㄧ幇 + player1_final_score = 0; + player2_final_score = 0; + + // 閬嶅巻鎵鏈夋鏁帮紝绱Н姣忎竴姝ョ殑寰楀垎锛屽悗鏈熸楠ゆ潈閲嶆洿楂 + for (int i = 0; i < step_count; i++) + { + // 璁$畻鏃堕棿鏉冮噸鍥犲瓙锛氭鏁拌秺闈犲悗锛屾潈閲嶈秺澶 + double time_weight = 1.0 + (double)i / step_count * TIME_WEIGHT_FACTOR; // 鏈鍚庣殑姝ラ鏉冮噸鏄紑濮嬫楠ょ殑(1+TIME_WEIGHT_FACTOR)鍊 + + if (steps[i].player == PLAYER || steps[i].player == PLAYER1) + { + player1_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); + } + else + { + player2_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); + } + } + + // 鑳滆礋鍔犳潈锛氳幏鑳滄柟鑾峰緱棰濆鐨勮瘎鍒嗗鍔 + if (step_count > 0) + { + Step last_step = steps[step_count - 1]; + if (check_win(last_step.x, last_step.y, last_step.player)) + { + // 鑾疯儨鏂硅幏寰楅澶栧鍔卞垎鏁 + if (last_step.player == PLAYER || last_step.player == PLAYER1) + { + player1_final_score += WIN_BONUS; // 鑾疯儨濂栧姳 + } + else + { + player2_final_score += WIN_BONUS; // 鑾疯儨濂栧姳 + } + } + } + + scores_calculated = 1; // 鏍囪璇勫垎宸茶绠 + } + else + { + // 璇勫垎宸蹭粠鏂囦欢涓姞杞斤紝鐩存帴浣跨敤 + printf("浠庤褰曟枃浠朵腑鍔犺浇璇勫垎鏁版嵁\n"); + } + if (review_choice == 1) { printf("\n===== 澶嶇洏璁板綍(鎬绘鏁帮細%d) =====\n", step_count); @@ -73,7 +130,9 @@ void review_process(int game_mode) // 鎵撳嵃褰撳墠澶嶇洏妫嬬洏 printf(" "); for (int col = 0; col < BOARD_SIZE; col++) + { printf("%2d", col + 1); // 鍒楀彿 + } printf("\n"); for (int row = 0; row < BOARD_SIZE; row++) @@ -109,65 +168,50 @@ void review_process(int game_mode) getchar(); // 绛夊緟鐢ㄦ埛鎸夐敭 } - // 璇勪及鍙屾柟琛ㄧ幇 + // 鏄剧ず璇勫垎缁撴灉 printf("\n===== 瀵瑰眬璇勫垎 =====\n"); - int player1_score = 0, player2_score = 0; - - // 閬嶅巻鎵鏈夋鏁帮紝绱Н姣忎竴姝ョ殑寰楀垎 - for (int i = 0; i < step_count; i++) - { - if (steps[i].player == PLAYER || steps[i].player == PLAYER1) - { - player1_score += calculate_step_score(steps[i].x, steps[i].y, steps[i].player); - } - else - { - player2_score += calculate_step_score(steps[i].x, steps[i].y, steps[i].player); - } - } - - double sum_score = (long double)player1_score + (long double)player2_score; + double sum_score = (long double)player1_final_score + (long double)player2_final_score; if (sum_score > 0) { if (game_mode == 1) { printf("鐜╁寰楀垎: %d, 鍗犳瘮: %.2f%%\n", - player1_score, (double)player1_score * 100.0 / sum_score); + player1_final_score, (double)player1_final_score * 100.0 / sum_score); printf("AI寰楀垎: %d, 鍗犳瘮: %.2f%%\n", - player2_score, (double)player2_score * 100.0 / sum_score); + player2_final_score, (double)player2_final_score * 100.0 / sum_score); } else { printf("鐜╁1(榛戞)寰楀垎: %d, 鍗犳瘮: %.2f%%\n", - player1_score, (double)player1_score * 100.0 / sum_score); + player1_final_score, (double)player1_final_score * 100.0 / sum_score); printf("鐜╁2(鐧芥)寰楀垎: %d, 鍗犳瘮: %.2f%%\n", - player2_score, (double)player2_score * 100.0 / sum_score); + player2_final_score, (double)player2_final_score * 100.0 / sum_score); } } else { if (game_mode == 1) { - printf("鐜╁寰楀垎: %d\n", player1_score); - printf("AI寰楀垎: %d\n", player2_score); + printf("鐜╁寰楀垎: %d\n", player1_final_score); + printf("AI寰楀垎: %d\n", player2_final_score); } else { - printf("鐜╁1(榛戞)寰楀垎: %d\n", player1_score); - printf("鐜╁2(鐧芥)寰楀垎: %d\n", player2_score); + printf("鐜╁1(榛戞)寰楀垎: %d\n", player1_final_score); + printf("鐜╁2(鐧芥)寰楀垎: %d\n", player2_final_score); } printf("娉: 鍙屾柟寰楀垎鍧囦负0锛屾棤娉曡绠楀崰姣擻n"); } // 璇勯塎VP - if (player1_score > player2_score) + if (player1_final_score > player2_final_score) { - printf("\nMVP: %s (棰嗗厛 %d 鍒)\n", (game_mode == 1) ? "鐜╁" : "鐜╁1(榛戞)", player1_score - player2_score); + printf("\nMVP: %s (棰嗗厛 %d 鍒)\n", (game_mode == 1) ? "鐜╁" : "鐜╁1(榛戞)", player1_final_score - player2_final_score); } - else if (player2_score > player1_score) + else if (player2_final_score > player1_final_score) { - printf("\nMVP: %s (棰嗗厛 %d 鍒)\n", (game_mode == 1) ? "AI" : "鐜╁2(鐧芥)", player2_score - player1_score); + printf("\nMVP: %s (棰嗗厛 %d 鍒)\n", (game_mode == 1) ? "AI" : "鐜╁2(鐧芥)", player2_final_score - player1_final_score); } else { @@ -193,14 +237,15 @@ void handle_save_record(int game_mode) time_t now = time(NULL); struct tm *t = localtime(&now); char filename[256]; - strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.txt", t); + strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.csv", t); int save_status = save_game_to_file(filename, game_mode); switch (save_status) { case 0: // 鎴愬姛 - printf("\n娓告垙璁板綍宸叉垚鍔熶繚瀛樿嚦: %s\n", filename); + printf("\n娓告垙璁板綍宸叉垚鍔熶繚瀛樿嚦: %s (CSV鏍煎紡)\n", filename); printf("鎮ㄥ彲浠ヤ娇鐢ㄤ互涓嬪懡浠よ繘琛屽鐩: .\\gobang.exe -l %s\n", filename); + printf("CSV鏍煎紡鏂囦欢鍙互鐩存帴鐢‥xcel鎵撳紑鏌ョ湅鍜屽垎鏋怽n"); break; case 1: // 鐩綍鍒涘缓澶辫触 printf("\n娓告垙璁板綍淇濆瓨澶辫触: 鏃犳硶鍒涘缓 'records' 鐩綍銆俓n"); @@ -264,17 +309,24 @@ int save_game_to_file(const char *filename, int game_mode) return 2; // 鏂囦欢鎵撳紑澶辫触 } - // 鍐欏叆娓告垙妯″紡鍜屾鐩樺ぇ灏 - if (fprintf(file, "%d\n%d\n", game_mode, BOARD_SIZE) < 0) + // 鍐欏叆CSV鏂囦欢澶撮儴 + if (fprintf(file, "娓告垙妯″紡,妫嬬洏澶у皬,鐜╁1寰楀垎,鐜╁2寰楀垎\n%d,%d,%d,%d\n\n", game_mode, BOARD_SIZE, player1_final_score, player2_final_score) < 0) + { + fclose(file); + return 3; // 鏂囦欢鍐欏叆澶辫触 + } + + // 鍐欏叆CSV琛ㄥご + if (fprintf(file, "姝ユ暟,鐜╁,琛屽潗鏍,鍒楀潗鏍嘰n") < 0) { fclose(file); return 3; // 鏂囦欢鍐欏叆澶辫触 } - // 鍐欏叆鎵鏈夎惤瀛愭楠 + // 鍐欏叆鎵鏈夎惤瀛愭楠わ紙CSV鏍煎紡锛 for (int i = 0; i < step_count; i++) { - if (fprintf(file, "%d %d %d\n", steps[i].player, steps[i].x, steps[i].y) < 0) + if (fprintf(file, "%d,%d,%d,%d\n", i+1, steps[i].player, steps[i].x+1, steps[i].y+1) < 0) { fclose(file); return 3; // 鏂囦欢鍐欏叆澶辫触 @@ -306,18 +358,34 @@ int load_game_from_file(const char *filename) return false; } - // 璇诲彇娓告垙妯″紡鍜屾鐩樺ぇ灏 - int game_mode, size; - if (fscanf(file, "%d", &game_mode) != 1 || (game_mode != 1 && game_mode != 2)) + // 璺宠繃CSV鏂囦欢澶撮儴琛 + char buffer[256]; + if (fgets(buffer, sizeof(buffer), file) == NULL) // 璺宠繃"娓告垙妯″紡,妫嬬洏澶у皬" { fclose(file); - return 0; // 鏃犳晥鐨勬父鎴忔ā寮 + return 0; } - if (fscanf(file, "%d", &size) != 1 || size < 5 || size > MAX_BOARD_SIZE) + + // 璇诲彇娓告垙妯″紡銆佹鐩樺ぇ灏忓拰璇勫垎缁撴灉 + int game_mode, size; + if (fscanf(file, "%d,%d,%d,%d", &game_mode, &size, &player1_final_score, &player2_final_score) != 4 || (game_mode != 1 && game_mode != 2)) + { + fclose(file); + return 0; // 鏃犳晥鐨勬父鎴忔ā寮忔垨鏂囦欢鏍煎紡 + } + if (size < 5 || size > MAX_BOARD_SIZE) { fclose(file); return false; } + + // 璁剧疆璇勫垎宸茶绠楁爣蹇 + scores_calculated = 1; + + // 璺宠繃绌鸿鍜岃〃澶磋 + fgets(buffer, sizeof(buffer), file); // 璺宠繃鎹㈣ + fgets(buffer, sizeof(buffer), file); // 璺宠繃绌鸿 + fgets(buffer, sizeof(buffer), file); // 璺宠繃"姝ユ暟,鐜╁,琛屽潗鏍,鍒楀潗鏍" // 鍒濆鍖栨鐩 BOARD_SIZE = size; @@ -325,8 +393,12 @@ int load_game_from_file(const char *filename) // 璇诲彇鎵鏈夎惤瀛愭楠 step_count = 0; - while (fscanf(file, "%d %d %d", &steps[step_count].player, &steps[step_count].x, &steps[step_count].y) == 3) + int step_num; // 鐢ㄤ簬瀛樺偍姝ユ暟锛屼絾涓嶄娇鐢 + while (fscanf(file, "%d,%d,%d,%d", &step_num, &steps[step_count].player, &steps[step_count].x, &steps[step_count].y) == 4) { + // 灏1-based鍧愭爣杞崲涓0-based鍧愭爣 + steps[step_count].x--; + steps[step_count].y--; step_count++; } diff --git a/record.h b/record.h index 2644e10..0cf6d59 100644 --- a/record.h +++ b/record.h @@ -3,31 +3,36 @@ #include "gobang.h" -// --- 澶嶇洏涓庤褰 --- +// 全局变量,用于存储对局评分,确保对战结束和复盘模式使用相同的评分 +extern int player1_final_score; +extern int player2_final_score; +extern int scores_calculated; + +// --- 复盘与记录 --- /** - * @brief 杩涘叆澶嶇洏娴佺▼锛屽洖椤炬暣灞娓告垙 - * @param game_mode 娓告垙妯″紡锛1涓轰汉鏈猴紝2涓哄弻浜猴級 + * @brief 进入复盘流程,回顾整局游戏 + * @param game_mode 游戏模式(1为人机,2为双人) */ void review_process(int game_mode); /** - * @brief 灏嗗綋鍓嶅灞璁板綍淇濆瓨鍒版枃浠 - * @param filename 瑕佷繚瀛樺埌鐨勬枃浠跺悕 - * @param game_mode 娓告垙妯″紡 - * @return 0琛ㄧず鎴愬姛锛岄潪0琛ㄧず澶辫触 + * @brief 将当前对局记录保存到文件 + * @param filename 要保存到的文件名 + * @param game_mode 游戏模式 + * @return 0表示成功,非0表示失败 */ int save_game_to_file(const char *filename, int game_mode); /** - * @brief 澶勭悊淇濆瓨娓告垙璁板綍鐨勯昏緫 - * @param game_mode 娓告垙妯″紡 + * @brief 处理保存游戏记录的逻辑 + * @param game_mode 游戏模式 */ void handle_save_record(int game_mode); /** - * @brief 浠庢枃浠跺姞杞芥父鎴忚褰 - * @param filename 瑕佸姞杞界殑鏂囦欢鍚 - * @return 0琛ㄧず鎴愬姛锛岄潪0琛ㄧず澶辫触 + * @brief 从文件加载游戏记录 + * @param filename 要加载的文件名 + * @return 0表示成功,非0表示失败 */ int load_game_from_file(const char *filename); diff --git a/浜斿瓙妫.c b/浜斿瓙妫.c index d69aeae..714d718 100644 --- a/浜斿瓙妫.c +++ b/浜斿瓙妫.c @@ -3,24 +3,30 @@ * @brief 浜斿瓙妫嬫父鎴忔牳蹇冮昏緫澶存枃浠 * @details 娓告垙鏍稿績閫昏緫瀹炵幇 * @author 鍒樿埅瀹(3364451258@qq.com銆15236416560@163.com銆乴hy3364451258@outlook.com) - * @date 2025-07-02 + * @date 2025-07-07 * @version 4.0 * @note * 1. 鏂板鍔熻兘锛 * - 澧炲姞浜嗗绂佹墜瑙勫垯鐨勬敮鎸侊紝闃叉鐜╁杩涜鏃犳剰涔夌殑璧版硶銆 * - 鏂板浜嗘父鎴忚鏃跺櫒鍔熻兘锛岄檺鍒舵瘡鍥炲悎鐨勬濊冩椂闂淬 + * - 娣诲姞浜嗗鐩樺姛鑳斤紝鏀寔淇濆瓨鍜屽洖椤惧灞璁板綍銆 + * - 瀹炵幇浜嗚瘎鍒嗙郴缁燂紝鍙互瀵规瘡涓姝ユ杩涜璇勫垎鍜屽垎鏋愩 * 2. 鎬ц兘浼樺寲锛 * - 浼樺寲浜嗚瘎浼板嚱鏁扮殑鎬ц兘锛屽噺灏戜簡涓嶅繀瑕佺殑璁$畻銆 * - 寮曞叆浜 Alpha-Beta 鍓灊绠楁硶锛屾彁楂樹簡 AI 鎼滅储鐨勬晥鐜囥 + * - 鏀硅繘浜嗗唴瀛樼鐞嗭紝鍑忓皯浜嗚祫婧愬崰鐢ㄣ * 3. 鐢ㄦ埛鐣岄潰鏀硅繘锛 * - 鏂板浜嗗懡浠よ鐣岄潰锛屾彁渚涙洿鍙嬪ソ鐨勪氦浜掍綋楠屻 * - 鍙互鑷畾涔夋鐩樺ぇ灏忥紝澧炲姞娓告垙鐨勭伒娲绘с + * - 浼樺寲浜嗘彁绀轰俊鎭紝浣挎父鎴忔搷浣滄洿鍔犵洿瑙傘 * 4. 浠g爜缁撴瀯浼樺寲锛 * - 灏嗘父鎴忛昏緫鍜岀敤鎴风晫闈㈠垎绂伙紝鎻愰珮浠g爜鐨勫彲璇绘у拰鍙淮鎶ゆс * - 浼樺寲浜嗕唬鐮佺粨鏋勶紝鎻愰珮浜嗕唬鐮佺殑鍙鎬у拰鍙淮鎶ゆс + * - 妯″潡鍖栬璁★紝渚夸簬鍔熻兘鎵╁睍鍜岀淮鎶ゃ * 5. 寮傚父澶勭悊锛 * - 澧炲姞浜嗚緭鍏ラ敊璇殑寮傚父澶勭悊鏈哄埗锛岀‘淇濇父鎴忕殑绋冲畾鎬с * - 淇浜嗕竴浜涘凡鐭ョ殑 bug锛屾彁楂樻父鎴忕殑绋冲畾鎬с + * - 澧炲己浜嗛敊璇彁绀猴紝甯姪鐢ㄦ埛蹇熷畾浣嶉棶棰樸 * 6. 鏂囨。鏇存柊锛 * - 瀹屽杽浜嗕唬鐮佹敞閲婏紝鎻愰珮浜嗕唬鐮佺殑鍙鎬с * - 鏇存柊浜嗘枃妗o紝鍖呮嫭鍔熻兘鎻忚堪銆佷娇鐢ㄦ柟娉曘佹敞鎰忎簨椤圭瓑銆