16 Commits

Author SHA1 Message Date
Serendipity 17d7079c41 从GitHub仓库中移除CSV游戏记录文件\n\n- 在.gitignore中添加*.csv规则\n- 从版本控制中移除所有CSV文件\n- 本地文件保留,但不再跟踪 2025-10-08 00:57:54 +08:00
Serendipity 1abde99a68 v8.2: 完善专业安装包制作功能\n\n- 支持Inno Setup和NSIS双重安装包方案\n- 完整的软件分发体系\n- 一键安装部署和完整卸载功能\n- 更新所有文档以反映v8.2版本特性 2025-10-08 00:54:56 +08:00
Serendipity 205e943bcb 重构项目结构:将源文件和头文件分别移动到src和include目录 2025-09-22 22:53:14 +08:00
Serendipity ae0629ef6e feat: v8.2版本更新 - 编译脚本优化\n\n- 🔧 交互式编译脚本:compile_gui.bat支持选择编译控制台或GUI版本\n- 📋 用户友好界面:提供清晰的编译选项菜单和操作指引\n- 编译流程优化:统一编译参数,解决SDL3依赖问题\n- �� 多语言支持:英文界面避免编码问题,确保兼容性\n- 错误处理完善:添加无效输入处理和详细错误提示\n- 🎯 开发体验提升:简化编译流程,提高开发效率 2025-09-20 11:08:46 +08:00
Serendipity 2121831478 Merge branch 'main' of https://github.com/LHY0125/Gobang-Game 2025-09-20 10:52:28 +08:00
Serendipity a11ddeb8d1 feat: 添加Makefile编译系统和BUILD文档
- 创建完整的Makefile支持make编译
- 支持编译控制台版本和GUI版本
- 添加清理、运行、帮助等目标
- 自动处理SDL3和网络库依赖
- 创建BUILD.md编译指南文档
- 完善GUI相关全局变量管理
- 丰富函数注释和文档
2025-09-20 10:50:58 +08:00
Serendipity 39d84f38cf Update README.md 2025-09-18 19:23:18 +08:00
Serendipity 198a8a2943 Update README.md 2025-09-18 19:22:46 +08:00
Serendipity 0e53b09d9b v8.0版本更新:添加SDL3图形化界面功能和完善文档 2025-09-18 19:08:37 +08:00
Serendipity 1d8e2e25cf 重构安装脚本:将安装文件移动到installer文件夹并修复路径问题 2025-07-27 20:22:52 +08:00
Serendipity 6d4de737c4 Add NSIS installer script and update main.c 2025-07-27 10:34:49 +08:00
Serendipity b413f7254a Update main.c and add project requirements 2025-07-24 10:27:18 +08:00
Serendipity cb81105809 代码格式化和优化:统一换行符格式,优化代码结构 2025-07-23 10:09:02 +08:00
Serendipity fbe4f5273c 重构输入处理:统一使用get_integer_input函数替换scanf 2025-07-23 09:37:54 +08:00
Serendipity 59c816767b 更新项目结构:移除icon文件夹,添加图标指南文档,更新.gitignore 2025-07-21 12:43:52 +08:00
Serendipity 504868c739 优化AI参数配置:使用DEFAULT_DEFENSE_COEFFICIENT并更新相关注释 2025-07-21 00:18:11 +08:00
52 changed files with 2234 additions and 1221 deletions
+12 -1
View File
@@ -2,6 +2,7 @@
*.exe *.exe
*.out *.out
*.app *.app
*.o
# IDE配置文件 # IDE配置文件
.idea/ .idea/
@@ -39,4 +40,14 @@ Thumbs.db
# 动态库文件 # 动态库文件
*.dll *.dll
*.so *.so
*.dylib *.dylib
# 资源文件
*.res
*.ico
# 打包文件
dist/
# 临时游戏存档
*.csv
+105 -461
View File
@@ -1,461 +1,105 @@
# 🚀 五子棋AI增强指南 ## 🎯 项目优势
### ✅ 已经做得很好的方面
## 📋 概述 - 📚 代码质量优秀 :注释覆盖率30.1%,使用Doxygen格式文档
- 🏗️ 架构设计合理 :模块化设计,职责分离清晰
本文档详细介绍了提升五子棋AI水平的各种方法和实现策略,基于当前项目的代码架构,提供从简单参数调优到复杂算法改进的完整方案。 - 🎮 功能完整丰富 :支持人机对战、双人对战、网络对战、GUI界面
- 🔧 技术栈现代化 :SDL3图形库、双版本架构、专业安装包
## 🎯 当前AI分析 - 📋 文档完善 :详细的README、AI增强指南、架构重构指南
- 🌐 跨平台考虑 :已有条件编译支持Windows/Linux
### 现有优势 ## 🚀 改进建议
- ✅ 模块化设计良好 ### 1. 🧪 测试体系建设(高优先级)
- ✅ 基础Minimax + α-β剪枝算法 当前状况 :项目缺乏自动化测试
- ✅ 完整的棋型评估系统
- ✅ 威胁检测机制 建议改进
- ✅ 防御优先策略
- 添加单元测试框架(如Unity或自制简单测试框架)
### 改进空间 - 为核心模块编写测试用例:AI算法、棋盘逻辑、网络通信
- 🔄 搜索深度有限(当前3层) - 添加集成测试验证各模块协作
- 🔄 评估函数相对简单 - 创建性能基准测试
- 🔄 缺乏开局库和残局库 ### 2. 🛡️ 错误处理和安全性增强(高优先级)
- 🔄 没有学习机制 当前状况 :基础错误处理存在,但可以更完善
- 🔄 搜索效率可优化
建议改进
## 🛠️ 改进方案
- 增强输入验证,防止缓冲区溢出
### 1. 立即可实施的改进(难度:⭐) - 添加网络通信的安全验证机制
- 完善内存管理,添加内存泄漏检测
#### 1.1 参数调优 - 增加异常恢复机制
### 3. 🎯 AI算法优化(中优先级)
**修改 `config.h` 中的关键参数:** 当前状况 :基础Minimax+α-β剪枝,搜索深度3层
```c 建议改进
// 增加搜索深度
#define DEFAULT_AI_DEPTH 5 // 从3提升到5 - 实现置换表缓存系统
- 添加开局库和残局库
// 优化防守系数 - 实现迭代加深搜索
#define DEFAULT_DEFENSE_COEFFICIENT 1.5 // 从1.2提升到1.5 - 增加威胁检测优化
- 支持多线程并行搜索
// 扩大搜索范围 ### 4. 🔧 构建和部署优化(中优先级)
#define AI_NEARBY_RANGE 3 // 从2扩大到3 当前状况 :编译脚本功能完善,但可以更自动化
// 降低搜索范围限制阈值 建议改进
#define AI_SEARCH_RANGE_THRESHOLD 8 // 从10降低到8
``` - 添加CMake构建系统支持
- 实现持续集成/持续部署(CI/CD)
#### 1.2 评分系统优化 - 添加依赖管理和版本控制
- 完善跨平台构建脚本
**在 `config.h` 中添加新的评分项:** ### 5. 📊 性能监控和分析(中优先级)
建议添加
```c
// 组合棋型评分 - AI决策时间统计
#define AI_SCORE_DOUBLE_THREE 50000 // 双三 - 内存使用监控
#define AI_SCORE_FOUR_THREE 200000 // 四三 - 网络延迟分析
#define AI_SCORE_THREAT_SEQUENCE 80000 // 威胁序列 - 性能瓶颈识别工具
#define AI_SCORE_POTENTIAL_FIVE 300000 // 潜在五连 ### 6. 🎮 用户体验提升(低优先级)
``` 建议改进
### 2. 短期改进(1-2周,难度:⭐⭐) - 添加音效和动画效果
- 实现主题和皮肤系统
#### 2.1 移动排序优化 - 增加游戏统计和成就系统
- 支持自定义快捷键
**在 `ai.c` 中添加移动排序函数:** ### 7. 🌐 网络功能增强(低优先级)
建议改进
```c
// 移动排序结构 - 实现房间系统和匹配机制
typedef struct { - 添加观战功能
int x, y; - 支持断线重连
int score; - 实现聊天系统
} ScoredMove; ## 📋 具体实施建议
### 立即可实施(1-2周)
// 移动排序函数 1. 1.
int compare_moves(const void *a, const void *b) { 创建基础测试框架
ScoredMove *moveA = (ScoredMove*)a; 2. 2.
ScoredMove *moveB = (ScoredMove*)b; 添加输入验证和边界检查
return moveB->score - moveA->score; 3. 3.
} 完善错误日志记录
4. 4.
// 生成并排序候选移动 优化编译警告处理
int generate_candidate_moves(ScoredMove *moves, int player) { ### 短期目标(1-2月)
int count = 0; 1. 1.
for (int i = 0; i < BOARD_SIZE; i++) { 完善单元测试覆盖
for (int j = 0; j < BOARD_SIZE; j++) { 2. 2.
if (board[i][j] == EMPTY && is_near_stones(i, j)) { 实现置换表和开局库
moves[count].x = i; 3. 3.
moves[count].y = j; 添加性能监控
moves[count].score = evaluate_move(i, j); 4. 4.
count++; 增强网络安全性
} ### 长期规划(3-6月)
} 1. 1.
} 实现神经网络AI
qsort(moves, count, sizeof(ScoredMove), compare_moves); 2. 2.
return count; 完整跨平台支持
} 3. 3.
``` 添加数据库支持
4. 4.
#### 2.2 威胁检测增强 实现云端对战
## 🏆 总体评价
**添加多层次威胁检测:** 你的项目已经达到了 专业级别的开源项目标准 !代码架构清晰、功能完整、文档详尽。主要的改进空间在于测试覆盖和一些高级特性的添加。
```c 项目亮点
// 威胁类型枚举
typedef enum { - 从v7.0的架构重构到v8.2的编译优化,版本迭代思路清晰
THREAT_NONE = 0, - SDL3图形化界面实现现代化
THREAT_WIN = 5, // 直接获胜 - 网络对战功能完整
THREAT_FOUR = 4, // 活四/冲四 - 代码注释和文档非常专业
THREAT_THREE = 3, // 活三 这是一个非常值得继续完善和推广的优秀项目!🎉
THREAT_DOUBLE = 2, // 双威胁
THREAT_POTENTIAL = 1 // 潜在威胁
} ThreatLevel;
// 威胁检测函数
ThreatLevel detect_threat(int x, int y, int player) {
// 实现威胁检测逻辑
// ...
}
```
### 3. 中期改进(1-2月,难度:⭐⭐⭐)
#### 3.1 置换表实现
**添加置换表缓存系统:**
```c
// 置换表项
typedef struct {
uint64_t hash_key; // 棋盘哈希值
int score; // 评估分数
int depth; // 搜索深度
int best_x, best_y; // 最佳移动
int flag; // 节点类型(精确值/上界/下界)
} TranspositionEntry;
// 置换表
#define TT_SIZE 1048576 // 2^20
TranspositionEntry transposition_table[TT_SIZE];
// 棋盘哈希函数
uint64_t zobrist_hash() {
// 实现Zobrist哈希
// ...
}
```
#### 3.2 开局库系统
**创建开局库数据结构:**
```c
// 开局库项
typedef struct {
int moves[20][2]; // 开局移动序列
int move_count; // 移动数量
int win_rate; // 胜率
char name[50]; // 开局名称
} OpeningEntry;
// 开局库
OpeningEntry opening_book[] = {
// 天元开局
{{{7,7}, {6,6}, {8,8}, {6,8}, {8,6}}, 5, 65, "天元开局"},
// 花月开局
{{{7,7}, {6,7}, {8,7}, {7,6}, {7,8}}, 5, 70, "花月开局"},
// 更多开局...
};
```
#### 3.3 改进的评估函数
**实现更复杂的位置评估:**
```c
// 高级评估函数
int advanced_evaluate_pos(int x, int y, int player) {
int score = 0;
// 1. 基础棋型评分
score += basic_pattern_score(x, y, player);
// 2. 组合棋型评分
score += combination_pattern_score(x, y, player);
// 3. 位置价值评分
score += positional_value_score(x, y);
// 4. 威胁序列评分
score += threat_sequence_score(x, y, player);
// 5. 防守价值评分
score += defensive_value_score(x, y, player);
return score;
}
```
### 4. 长期改进(3-6月,难度:⭐⭐⭐⭐)
#### 4.1 蒙特卡洛树搜索(MCTS
**MCTS节点结构:**
```c
// MCTS节点
typedef struct MCTSNode {
int x, y; // 移动位置
int visits; // 访问次数
double wins; // 胜利次数
struct MCTSNode *parent; // 父节点
struct MCTSNode **children; // 子节点数组
int child_count; // 子节点数量
bool fully_expanded; // 是否完全展开
} MCTSNode;
// MCTS主函数
int mcts_search(int simulations) {
MCTSNode *root = create_node(-1, -1, NULL);
for (int i = 0; i < simulations; i++) {
MCTSNode *node = selection(root);
node = expansion(node);
double result = simulation(node);
backpropagation(node, result);
}
return best_child(root);
}
```
#### 4.2 神经网络集成
**神经网络评估接口:**
```c
// 神经网络评估函数
float neural_network_evaluate(int board[BOARD_SIZE][BOARD_SIZE]) {
// 调用训练好的神经网络模型
// 返回位置评估值
// ...
}
// 混合评估函数
int hybrid_evaluate(int x, int y, int player) {
// 传统评估
int traditional_score = evaluate_pos(x, y, player);
// 神经网络评估
float nn_score = neural_network_evaluate(board);
// 加权组合
return (int)(0.7 * traditional_score + 0.3 * nn_score * 10000);
}
```
## 📊 性能优化策略
### 1. 搜索优化
#### 1.1 迭代加深搜索
```c
int iterative_deepening_search(int max_depth, int time_limit) {
int best_move = -1;
clock_t start_time = clock();
for (int depth = 1; depth <= max_depth; depth++) {
if ((clock() - start_time) * 1000 / CLOCKS_PER_SEC > time_limit) {
break;
}
best_move = alpha_beta_search(depth);
}
return best_move;
}
```
#### 1.2 空窗搜索
```c
int null_window_search(int depth, int beta) {
return alpha_beta_search(depth, beta - 1, beta);
}
```
### 2. 内存优化
#### 2.1 位棋盘表示
```c
// 使用位操作优化棋盘表示
typedef struct {
uint64_t player1_board[4]; // 玩家1的棋盘(4个64位整数)
uint64_t player2_board[4]; // 玩家2的棋盘
} BitBoard;
```
## 🎮 实战策略
### 1. 自适应难度系统
```c
// 难度等级
typedef enum {
DIFFICULTY_EASY = 1,
DIFFICULTY_NORMAL = 2,
DIFFICULTY_HARD = 3,
DIFFICULTY_EXPERT = 4,
DIFFICULTY_MASTER = 5
} DifficultyLevel;
// 根据难度调整AI参数
void adjust_ai_parameters(DifficultyLevel level) {
switch (level) {
case DIFFICULTY_EASY:
ai_depth = 2;
defense_coefficient = 1.0;
break;
case DIFFICULTY_NORMAL:
ai_depth = 3;
defense_coefficient = 1.2;
break;
case DIFFICULTY_HARD:
ai_depth = 4;
defense_coefficient = 1.5;
break;
// ...
}
}
```
### 2. 学习系统
```c
// 对局记录
typedef struct {
int moves[MAX_STEPS][2];
int move_count;
int winner;
int ai_mistakes;
float game_quality;
} GameRecord;
// 学习函数
void learn_from_game(GameRecord *record) {
// 分析对局,更新评估参数
// 识别AI的错误决策
// 调整相关参数
}
```
## 📈 测试与评估
### 1. 性能测试
```c
// 性能测试函数
void performance_test() {
clock_t start = clock();
// 执行1000次AI决策
for (int i = 0; i < 1000; i++) {
ai_move(DEFAULT_AI_DEPTH);
// 重置棋盘
}
clock_t end = clock();
double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("平均决策时间: %.2f ms\n", time_taken);
}
```
### 2. 棋力测试
```c
// 自我对弈测试
int self_play_test(int games) {
int wins = 0;
for (int i = 0; i < games; i++) {
// AI vs AI(不同版本或参数)
int result = play_game();
if (result == 1) wins++;
}
return (wins * 100) / games; // 胜率
}
```
## 🚀 实施路线图
### 阶段1:基础优化(1周)
- [ ] 调整搜索深度和评分参数
- [ ] 优化移动排序
- [ ] 增强威胁检测
### 阶段2:算法改进(2-4周)
- [ ] 实现置换表
- [ ] 添加开局库
- [ ] 改进评估函数
### 阶段3:高级功能(1-2月)
- [ ] 实现MCTS
- [ ] 添加学习机制
- [ ] 性能优化
### 阶段4AI增强(2-3月)
- [ ] 神经网络集成
- [ ] 并行搜索
- [ ] 完整的自适应系统
## 📚 参考资源
### 算法资料
- 《人工智能:一种现代方法》- Stuart Russell
- 《游戏编程中的人工智能技术》- Mat Buckland
- AlphaGo论文系列
### 开源项目
- Stockfish(国际象棋引擎)
- Leela Zero(围棋AI
- Gomoku AI项目
### 在线资源
- Chess Programming Wiki
- Computer Olympiad
- AI游戏编程社区
## 🏗️ 代码架构优化 (v7.0新增)
### 配置管理统一化
在v7.0版本中,我们完成了重要的代码架构重构:
#### 配置参数集中管理
- **统一配置文件**:所有AI相关参数现在集中在`config.h`中定义
- **参数分类管理**:AI参数按功能分组(搜索深度、评分权重、时间限制等)
- **配置文件支持**AI参数可通过`gobang_config.ini`文件动态调整
- **运行时修改**:支持游戏过程中实时调整AI难度和参数
#### 代码模块化优化
- **清晰的模块分离**:AI逻辑与游戏逻辑完全分离
- **接口标准化**:统一的AI接口设计,便于算法替换和升级
- **全局变量管理**:AI相关全局变量集中在`globals`模块中
- **类型定义统一**:所有数据结构定义集中在`type.h`
#### 维护性提升
- **宏定义优化**:消除重复定义,提高代码一致性
- **注释规范化**:完善的代码注释和文档
- **错误处理统一**:标准化的错误处理机制
- **调试支持增强**:更好的调试信息和日志记录
这些架构优化为后续的AI算法改进奠定了坚实的基础,使得实施复杂的AI增强方案变得更加容易和可靠。
## 💡 总结
通过系统性的改进,可以将当前的五子棋AI从业余水平提升到接近专业水平。关键是要循序渐进,先实施简单的改进,再逐步引入复杂的算法。每个阶段都要进行充分的测试和评估,确保改进的有效性。
建议按照优先级顺序实施:
1. **短期目标**:参数调优 + 开局库 + 棋型优化
2. **中期目标**:搜索算法优化 + 评估函数改进
3. **长期目标**:机器学习集成 + MCTS实现
v7.0的架构重构为所有这些改进提供了更好的代码基础。
记住:**好的AI不仅要算得深,更要算得准!**
+11 -1
View File
@@ -1,7 +1,7 @@
# 🧠 五子棋AI实现详解 # 🧠 五子棋AI实现详解
## 📜 算法概述 ## 📜 算法概述
本五子棋AI采用α-β剪枝优化的极小极大算法,结合专业的棋型评估系统和多层次的威胁检测机制。支持人机对战、双人对战和网络对战多种模式。 本五子棋AI采用α-β剪枝优化的极小极大算法,结合专业的棋型评估系统和多层次的威胁检测机制。v8.0版本新增SDL3图形化界面支持,提供可视化AI决策过程和双版本架构(控制台+GUI)。支持人机对战、双人对战和网络对战多种模式。
```mermaid ```mermaid
graph TD graph TD
@@ -48,6 +48,16 @@ void network_ai_move(int depth, int player_id);
void sync_ai_decision(int x, int y); void sync_ai_decision(int x, int y);
``` ```
### 🎨 GUI界面AI支持 (v8.0新增)
```c
// GUI模式下的AI可视化决策
void gui_ai_move_with_animation(int x, int y);
// 显示AI思考过程
void show_ai_thinking_process(SDL_Renderer* renderer);
// AI决策结果的图形化展示
void render_ai_decision_info(SDL_Renderer* renderer, int score, int depth);
```
## ⚙️ 核心函数 ## ⚙️ 核心函数
### 1. ai_move(int depth) ### 1. ai_move(int depth)
+3 -1
View File
@@ -2,7 +2,7 @@
## 📋 概述 ## 📋 概述
本文档详细记录了五子棋项目在v7.0版本中进行的重大代码架构重构,包括重构的目标、实施过程、技术细节和带来的改进。 本文档详细记录了五子棋项目在v7.0-v8.0版本中进行的重大代码架构重构,包括重构的目标、实施过程、技术细节和带来的改进。v8.0版本新增了SDL3图形化界面模块,实现了双版本架构设计。
## 🎯 重构目标 ## 🎯 重构目标
@@ -12,6 +12,8 @@
3. **全局变量规范化** - 统一管理全局变量,避免散乱分布 3. **全局变量规范化** - 统一管理全局变量,避免散乱分布
4. **类型定义标准化** - 集中定义所有数据结构和类型 4. **类型定义标准化** - 集中定义所有数据结构和类型
5. **提升可维护性** - 降低代码耦合度,提高可读性和可维护性 5. **提升可维护性** - 降低代码耦合度,提高可读性和可维护性
6. **双版本架构** - (v8.0新增) 实现控制台版本和GUI版本的并行支持
7. **图形化界面集成** - (v8.0新增) 无缝集成SDL3图形库,提供现代化用户界面
### 预期收益 ### 预期收益
- 减少代码重复和冗余 - 减少代码重复和冗余
+73
View File
@@ -0,0 +1,73 @@
# 五子棋游戏编译指南
本项目现在支持使用 `make` 命令进行编译,提供了更便捷的构建方式。
## 系统要求
- MinGW64 编译器
- SDL3 开发库(用于图形界面)
- Make 工具
## 编译命令
### 查看所有可用命令
```bash
make help
```
### 编译所有版本
```bash
make all
```
这将同时编译控制台版本和GUI版本。
### 只编译控制台版本
```bash
make console
```
生成 `gobang_console.exe`
### 只编译GUI版本
```bash
make gui
```
生成 `gobang_gui.exe`
### 清理编译文件
```bash
make clean
```
删除所有目标文件(.o)和可执行文件(.exe)
### 编译并运行
```bash
# 编译并运行控制台版本
make run-console
# 编译并运行GUI版本
make run-gui
```
## 生成的文件
- `gobang_console.exe` - 控制台版本,支持所有功能包括图形界面模式
- `gobang_gui.exe` - GUI版本,与控制台版本功能相同
## 注意事项
1. 两个版本都包含完整功能,包括图形界面支持
2. 需要确保SDL3库路径正确配置在Makefile中
3. 编译时会显示一些警告,这是正常的
4. 如果遇到编译错误,请检查MinGW64和SDL3的安装路径
## 传统编译方式
如果不使用Makefile,仍可以使用传统的gcc命令:
```bash
# 控制台版本
gcc -std=c17 -o gobang.exe *.c -lws2_32
# GUI版本
gcc -std=c17 -o gobang_gui.exe *.c -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
```
+78
View File
@@ -0,0 +1,78 @@
# 为五子棋游戏添加图标指南
## 问题说明
在尝试为可执行文件添加图标时,发现icon文件夹中的图标文件格式不正确:
- `gobang_icon1.ico``gobang_icon2.ico` 实际上是HTML文件和PNG文件,而不是真正的ICO格式文件
## 解决方案
### 方法一:获取正确的ICO文件
1. **下载或创建真正的ICO文件**
- 使用在线ICO转换工具将PNG/JPG转换为ICO格式
- 推荐网站:https://www.icoconverter.com/
- 或使用GIMP、Photoshop等图像编辑软件导出ICO格式
2. **替换现有文件**
- 将正确的ICO文件保存为 `icon/gobang_icon.ico`
- 确保文件是真正的ICO格式(文件头应为 00 00 01 00)
3. **修改资源文件**
- 编辑 `gobang.rc` 文件
- 取消注释图标行:`IDI_APPLICATION ICON "icon\\gobang_icon.ico"`
4. **重新编译**
```bash
windres gobang.rc -o gobang.res
gcc -std=c17 -o gobang.exe *.c gobang.res -lws2_32
```
### 方法二:使用现有PNG文件(需要转换)
如果你有PNG格式的图标文件,可以:
1. **在线转换**
- 访问 https://convertio.co/png-ico/
- 上传PNG文件并转换为ICO格式
- 下载转换后的ICO文件
2. **使用ImageMagick(如果已安装)**
```bash
magick convert icon/your_image.png icon/gobang_icon.ico
```
### 方法三:使用Windows资源编辑器
1. 编译不带图标的exe文件(当前状态)
2. 使用Resource Hacker等工具后期添加图标
3. 下载Resource Hackerhttp://www.angusj.com/resourcehacker/
## 当前状态
- ✅ 程序可以正常编译和运行
- ✅ 包含版本信息资源
- ❌ 暂时没有应用程序图标
- ✅ 提供了完整的构建脚本
## 编译指令
### 不带图标版本(当前可用)
```bash
gcc -std=c17 -o gobang.exe *.c -lws2_32
```
### 带图标版本(需要正确的ICO文件)
```bash
windres gobang.rc -o gobang.res
gcc -std=c17 -o gobang.exe *.c gobang.res -lws2_32
```
## 验证ICO文件格式
可以使用以下PowerShell命令检查文件是否为真正的ICO格式:
```powershell
Get-Content icon/gobang_icon.ico -AsByteStream -TotalCount 4 | ForEach-Object { '{0:X2}' -f $_ }
```
正确的ICO文件应该显示:`00 00 01 00`
+16 -18
View File
@@ -2,17 +2,31 @@
## 功能概述 ## 功能概述
本项目支持网络对战功能,允许两台设备通过网络进行实时五子棋对战,支持服务器/客户端连接。 本项目支持网络对战功能,允许两台设备通过网络进行实时五子棋对战,支持服务器/客户端连接。v8.0版本新增了GUI界面的网络对战支持,提供更直观的可视化网络游戏体验。
### v8.2 (2025-10-08)
- 专业安装包制作 - 支持Inno Setup和NSIS双重安装方案
- 安装程序优化 - 完整的文件打包和快捷方式创建
- 分发体系完善 - 提供标准化的软件分发解决方案
- 用户体验提升 - 一键安装部署,支持完整卸载功能
## 编译方法 ## 编译方法
### 控制台版本
```bash ```bash
gcc -std=c17 -o gobang.exe *.c -lws2_32 gcc -std=c17 -o gobang.exe *.c -lws2_32
``` ```
### GUI版本
```bash
gcc -std=c17 -o gobang_gui.exe *.c -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" .
```
**注意:** **注意:**
- Windows系统需要添加 `-lws2_32` 链接库 - Windows系统需要添加 `-lws2_32` 链接库
- Linux系统不需要此链接库参数 - GUI版本需要SDL3库支持
- Linux系统不需要ws2_32链接库参数
## 使用方法 ## 使用方法
@@ -129,23 +143,7 @@ telnet <对方IP地址> <端口号>
- 游戏结束后及时关闭程序 - 游戏结束后及时关闭程序
- 注意保护个人网络信息 - 注意保护个人网络信息
## 更新日志
### v7.0 (2025-07-20)
- 网络配置参数统一管理 - 所有网络相关配置集中到config.h
- 消息类型定义优化 - 统一消息协议宏定义
- 代码架构重构 - 提升网络模块的可维护性
- 配置文件支持 - 网络参数可通过配置文件调整
### v6.1 (2025-07-10)
- 完善网络对战功能
- 支持TCP/IP通信
- 实现棋盘同步
- 连接状态和协议优化
- 支持多种操作
- 添加延时控制等游戏功能(延时显示、认输、悔棋等)
---
**开发者:** 刘航宇 **开发者:** 刘航宇
**联系邮箱:** 3364451258@qq.com **联系邮箱:** 3364451258@qq.com
+147
View File
@@ -0,0 +1,147 @@
# 五子棋游戏 - 图形化界面说明 (v8.0)
## 概述
v8.0版本实现了完整的双版本架构,支持两种界面模式:
- **控制台界面**:传统的文本界面,保持原有功能完整性
- **图形化界面**:基于SDL3的现代图形界面,提供可视化操作体验
## v8.0新增功能
-**SDL3图形化界面**:现代化的可视化棋盘
-**鼠标交互支持**:点击落子,直观操作
-**窗口管理优化**:自动居中,响应式设计
-**事件驱动架构**:流畅的用户交互体验
-**安装包支持**:提供专业的安装程序
-**双版本并行**:控制台和GUI版本独立运行
## 环境要求
### SDL3库配置
1. 下载SDL3-3.2.22开发库
2. 解压到:`D:\settings\SDL\SDL3-3.2.22\`
3. 确保目录结构如下:
```
D:\settings\SDL\SDL3-3.2.22\
├── x86_64-w64-mingw32\
│ ├── include\ # 头文件
│ ├── lib\ # 库文件
│ └── bin\ # DLL文件
└── ...
```
### 编译环境
- GCC编译器(MinGW-w64)位于:`D:\Program Files\mingw64`
- Windows 10/11操作系统
- 支持OpenGL的显卡驱动
## 编译方法
### 方法一:使用批处理脚本(推荐)
```bash
# 编译图形化版本
.\compile_gui.bat
```
### 方法二:手动编译
```bash
# 控制台版本
gcc -std=c17 -o gobang.exe *.c -lws2_32
# 图形化版本
gcc -std=c17 -o gobang_gui.exe *.c -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
# 复制SDL3.dll到当前目录
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" .
```
## 运行方法
### 控制台版本
```bash
.\gobang.exe
```
在主菜单选择模式1-7进行游戏
### 图形化版本
```bash
.\gobang_gui.exe
```
在主菜单选择模式8启动图形化界面
## 图形化界面功能
- **窗口大小**800x600像素
- **棋盘显示**:15x15标准五子棋棋盘,木质纹理背景
- **鼠标操作**:点击棋盘交叉点进行落子
- **键盘操作**
- `ESC`:退出图形化界面返回主菜单
- `R`:重新开始游戏
- `U`:撤销上一步(如果支持)
- **游戏状态**
- 实时显示当前玩家(黑子/白子)
- 显示游戏进度和状态
- 胜负结果提示
- **视觉效果**
- 黑白棋子带阴影效果
- 当前玩家指示器
- 平滑的图形渲染
- **胜负判定**:自动检测五子连珠并显示获胜者
## 故障排除
### 编译错误
1. **找不到SDL3头文件**
- 检查SDL3库路径是否正确
- 确保include目录存在
2. **链接错误**
- 检查lib目录是否存在
- 确保SDL3.lib文件存在
3. **运行时错误**
- 确保SDL3.dll在系统PATH中或与exe文件同目录
### 图形化界面启动失败
- 检查SDL3库是否正确安装在指定路径
- 确保SDL3.dll文件在exe同目录下
- 检查显卡驱动是否支持OpenGL
- 尝试以管理员权限运行
- 确认Windows版本兼容性
### 运行时问题
- **窗口无法显示**:检查显示器分辨率设置
- **鼠标点击无响应**:确认点击在棋盘交叉点附近
- **游戏卡顿**:关闭其他占用GPU的程序
## 使用说明
### 快速开始
1. 运行 `compile_gui.bat` 编译图形化版本
2. 运行 `gobang_gui.exe` 启动程序
3. 在主菜单选择 "8. 图形化界面"
4. 使用鼠标点击棋盘进行游戏
5. 按ESC键退出图形化界面
### 游戏规则
- 黑子先行,轮流落子
- 率先形成五子连珠者获胜
- 支持横、竖、斜四个方向的连珠判定
## 开发说明
### 文件结构
- `gui.h` - 图形化界面头文件
- `gui.c` - 图形化界面实现
- `main.c` - 主程序(已添加图形化模式)
- `compile_gui.bat` - 图形化版本编译脚本
### 扩展功能
图形化界面支持进一步扩展:
- 音效支持
- 动画效果
- 主题切换
- 网络对战界面
- AI难度可视化调节
---
**注意**:首次使用图形化界面前,请确保SDL3库已正确配置。
+80
View File
@@ -0,0 +1,80 @@
# 五子棋游戏 Makefile
# 支持编译控制台版本和GUI版本
# 编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -std=c17 -O2 -Iinclude
LDFLAGS = -lws2_32
# SDL3路径设置
SDL3_PATH = D:/settings/SDL/SDL3-3.2.22/x86_64-w64-mingw32
SDL3_INCLUDE = -I$(SDL3_PATH)/include
SDL3_LIBS = -L$(SDL3_PATH)/lib -lSDL3 -lmingw32
# 源文件
COMMON_SOURCES = src/main.c src/gobang.c src/ai.c src/config.c src/game_mode.c src/globals.c \
src/init_board.c src/network.c src/record.c src/ui.c src/gui.c
GUI_SOURCES = $(COMMON_SOURCES)
CONSOLE_SOURCES = $(COMMON_SOURCES)
# 目标文件
COMMON_OBJECTS = $(patsubst src/%.c,src/%.o,$(filter %.c,$(COMMON_SOURCES)))
# 可执行文件
CONSOLE_TARGET = gobang_console.exe
GUI_TARGET = gobang_gui.exe
# 默认目标
all: $(CONSOLE_TARGET) $(GUI_TARGET)
# 控制台版本
$(CONSOLE_TARGET): $(COMMON_OBJECTS)
$(CC) $(CFLAGS) $(SDL3_INCLUDE) -o $@ $^ $(SDL3_LIBS) $(LDFLAGS)
# GUI版本
$(GUI_TARGET): $(COMMON_OBJECTS)
$(CC) $(CFLAGS) $(SDL3_INCLUDE) -o $@ $^ $(SDL3_LIBS) $(LDFLAGS)
# 通用目标文件编译规则(包含SDL3头文件路径,因为多个文件包含gui.h)
src/%.o: src/%.c
$(CC) $(CFLAGS) $(SDL3_INCLUDE) -c -o $@ $<
# 清理规则
clean:
del /Q src\*.o *.exe 2>nul || true
# 只编译控制台版本
console: $(CONSOLE_TARGET)
# 只编译GUI版本
gui: $(GUI_TARGET)
# 安装规则(可选)
install: all
@echo Installing executables...
copy $(CONSOLE_TARGET) C:\Program Files\Gobang\ 2>nul || echo Install directory not found
copy $(GUI_TARGET) C:\Program Files\Gobang\ 2>nul || echo Install directory not found
# 运行控制台版本
run-console: $(CONSOLE_TARGET)
.\$(CONSOLE_TARGET)
# 运行GUI版本
run-gui: $(GUI_TARGET)
.\$(GUI_TARGET)
# 帮助信息
help:
@echo Available targets:
@echo all - Build both console and GUI versions
@echo console - Build console version only
@echo gui - Build GUI version only
@echo clean - Remove all object files and executables
@echo run-console - Build and run console version
@echo run-gui - Build and run GUI version
@echo install - Install executables to system directory
@echo help - Show this help message
# 声明伪目标
.PHONY: all clean console gui install run-console run-gui help
+65 -18
View File
@@ -2,28 +2,26 @@
![Build Status](https://img.shields.io/badge/build-passing-brightgreen) ![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
![License](https://img.shields.io/badge/license-MIT-blue) ![License](https://img.shields.io/badge/license-MIT-blue)
![Version](https://img.shields.io/badge/version-v7.0-blue) ![Version](https://img.shields.io/badge/version-v8.2-blue)
![Platform](https://img.shields.io/badge/platform-Windows-lightgrey) ![Platform](https://img.shields.io/badge/platform-Windows-lightgrey)
> 🎯 **最新版本 v7.0** - 代码架构重构更新,实现结构体定义集中化、配置参数统一管理、代码模块化优化等核心改进 > 🎯 **最新版本 v8.2** - 编译脚本优化更新,完善批处理脚本功能、增强用户编译体验、优化开发工作流程
## 📋 大版本更新 ## 📋 大版本更新
### v7.0 (2025-07-20) - 代码架构重构更新 ### v8.2 (2025-01-20) - 编译脚本优化更新
- 🏗️ **结构体定义集中化** - 所有数据结构统一管理在type.h中 - 🔧 **交互式编译脚本** - compile_gui.bat支持选择编译控制台或GUI版本
- ⚙️ **配置参数统一管理** - 所有配置宏定义集中在config.h中 - 📋 **用户友好界面** - 提供清晰的编译选项菜单和操作指引
- 🔧 **代码模块化优化** - 消除重复定义,提高代码可维护性 - **编译流程优化** - 统一编译参数,解决SDL3依赖问题
- 📋 **菜单选项优化** - 退出选项调整为"0. 退出游戏" - 🌐 **多语言支持** - 英文界面避免编码问题,确保兼容性
- 🎯 **类型系统完善** - 独立的type.h文件管理所有数据类型 - 📦 **安装包制作** - 提供Inno Setup和NSIS两种安装包方案
- 🌐 **网络配置重构** - 网络相关宏定义统一到config.h - **错误处理完善** - 添加无效输入处理和详细错误提示
- 📊 **全局变量管理** - 优化全局变量声明和定义结构 - 🎯 **开发体验提升** - 简化编译流程,提高开发效率
- 🔄 **头文件依赖优化** - 改进模块间依赖关系和包含结构
## 目录 ## 目录
- [C语言五子棋人机对战AI](#c语言五子棋人机对战ai) - [C语言五子棋人机对战AI](#c语言五子棋人机对战ai)
- [📋 大版本更新](#-大版本更新) - [📋 大版本更新](#-大版本更新)
- [v7.0 (2025-07-20) - 代码架构重构更新](#v70-2025-07-20---代码架构重构更新) - [v8.2 (2025-01-20) - 编译脚本优化更新](#v82-2025-01-20---编译脚本优化更新)
- [目录](#目录) - [目录](#目录)
- [项目简介](#项目简介) - [项目简介](#项目简介)
- [功能特性](#功能特性) - [功能特性](#功能特性)
@@ -34,7 +32,11 @@
- [🔧 技术特性](#-技术特性) - [🔧 技术特性](#-技术特性)
- [快速开始](#快速开始) - [快速开始](#快速开始)
- [编译项目](#编译项目) - [编译项目](#编译项目)
- [控制台版本编译](#控制台版本编译)
- [GUI版本编译(需要SDL3](#gui版本编译需要sdl3)
- [运行游戏](#运行游戏) - [运行游戏](#运行游戏)
- [控制台版本](#控制台版本)
- [GUI版本](#gui版本)
- [游戏玩法](#游戏玩法) - [游戏玩法](#游戏玩法)
- [🚀 快速开始](#-快速开始) - [🚀 快速开始](#-快速开始)
- [🎯 对局操作](#-对局操作) - [🎯 对局操作](#-对局操作)
@@ -64,7 +66,7 @@
- [🔧 技术优化](#-技术优化) - [🔧 技术优化](#-技术优化)
## 项目简介 ## 项目简介
这是一个使用C语言实现的现代化五子棋对战系统,支持人机对战、双人对战和网络对战三种模式。系统基于 Alpha-Beta 剪枝优化的 Minimax 算法,具备完整的配置管理、复盘分析、智能评分和网络通信功能。 这是一个使用C语言实现的现代化五子棋对战系统,支持人机对战、双人对战和网络对战三种模式。系统基于 Alpha-Beta 剪枝优化的 Minimax 算法,具备完整的配置管理、复盘分析、智能评分和网络通信功能。v8.0版本新增SDL3图形化界面,提供现代化的可视化游戏体验,同时保持控制台版本的完整功能。
## 功能特性 ## 功能特性
@@ -72,6 +74,8 @@
- **人机对战模式** - 与智能AI进行对弈 - **人机对战模式** - 与智能AI进行对弈
- **双人对战模式** - 支持本地双人游戏 - **双人对战模式** - 支持本地双人游戏
- **复盘模式** - 回顾和分析历史对局 - **复盘模式** - 回顾和分析历史对局
- **GUI图形化模式** - 现代化图形界面游戏体验
- **控制台模式** - 传统终端界面游戏模式
### ⚙️ 游戏设置 ### ⚙️ 游戏设置
- **自定义棋盘尺寸** - 支持5x5至25x25可调节棋盘 - **自定义棋盘尺寸** - 支持5x5至25x25可调节棋盘
@@ -88,24 +92,33 @@
- **MVP评选系统** - 自动评选对局最佳表现者 - **MVP评选系统** - 自动评选对局最佳表现者
### 💻 用户体验 ### 💻 用户体验
- **现代化UI界面** - 清晰直观的终端用户界面 - **双界面支持** - 同时提供图形化界面和终端界面
- **SDL3图形化界面** - 现代化的可视化游戏体验
- **鼠标操作支持** - 直观的点击落子操作
- **完备输入验证** - 确保所有用户输入的有效性和安全性 - **完备输入验证** - 确保所有用户输入的有效性和安全性
- **智能错误提示** - 详细的错误信息和操作指导 - **智能错误提示** - 详细的错误信息和操作指导
- **响应式设计** - 自适应窗口大小和分辨率
- **跨平台兼容** - 支持Windows系统,预留跨平台扩展 - **跨平台兼容** - 支持Windows系统,预留跨平台扩展
### 🔧 技术特性 ### 🔧 技术特性
- **模块化架构** - 清晰的代码结构,便于维护和扩展 - **模块化架构** - 清晰的代码结构,便于维护和扩展
- **SDL3图形库集成** - 现代化的跨平台图形渲染支持
- **双版本架构** - 控制台版本和GUI版本并行开发
- **结构体定义集中化** - 所有数据结构统一在type.h中管理 - **结构体定义集中化** - 所有数据结构统一在type.h中管理
- **配置参数统一管理** - 所有配置宏定义集中在config.h中 - **配置参数统一管理** - 所有配置宏定义集中在config.h中
- **全局变量统一管理** - 所有全局变量集中在globals模块中管理 - **全局变量统一管理** - 所有全局变量集中在globals模块中管理
- **内存优化管理** - 高效的内存使用和资源管理 - **内存优化管理** - 高效的内存使用和资源管理
- **配置文件支持** - INI格式配置文件自动加载保存 - **配置文件支持** - INI格式配置文件自动加载保存
- **UTF-8编码支持** - 完美支持中文显示 - **UTF-8编码支持** - 完美支持中文显示
- **事件驱动架构** - 高效的图形界面事件处理机制
- **安装包支持** - 提供专业的软件安装和分发方案
- **网络对战功能** - 完整的在线多人对战系统 - **网络对战功能** - 完整的在线多人对战系统
## 快速开始 ## 快速开始
### 编译项目 ### 编译项目
#### 控制台版本编译
```bash ```bash
gcc -std=c17 -o gobang.exe *.c -lws2_32 gcc -std=c17 -o gobang.exe *.c -lws2_32
``` ```
@@ -115,13 +128,34 @@ gcc -std=c17 -o gobang.exe *.c -lws2_32
gcc -O2 -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.c config.c globals.c network.c -lws2_32 gcc -O2 -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.c config.c globals.c network.c -lws2_32
``` ```
**注意:** Windows系统需要添加 `-lws2_32` 链接库以支持网络功能 #### GUI版本编译(需要SDL3
```bash
gcc -std=c17 -o gobang_gui.exe *.c -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" .
```
或者使用批处理文件:
```bash
.\compile_gui.bat
```
**注意:**
- Windows系统需要添加 `-lws2_32` 链接库以支持网络功能
- GUI版本需要SDL3库支持,请确保SDL3路径正确
- 编译GUI版本后需要将SDL3.dll复制到可执行文件目录
### 运行游戏 ### 运行游戏
#### 控制台版本
```bash ```bash
.\gobang.exe .\gobang.exe
``` ```
#### GUI版本
```bash
.\gobang_gui.exe
```
## 游戏玩法 ## 游戏玩法
### 🚀 快速开始 ### 🚀 快速开始
@@ -225,6 +259,7 @@ chcp 65001
- **`gobang.c/h`** - 核心游戏逻辑,包括棋盘操作、胜负判断 - **`gobang.c/h`** - 核心游戏逻辑,包括棋盘操作、胜负判断
- **`game_mode.c/h`** - 游戏模式实现 (人机对战、双人对战、复盘模式) - **`game_mode.c/h`** - 游戏模式实现 (人机对战、双人对战、复盘模式)
- **`ai.c/h`** - AI算法实现 (Minimax + Alpha-Beta剪枝) - **`ai.c/h`** - AI算法实现 (Minimax + Alpha-Beta剪枝)
- **`gui.c/h`** - SDL3图形化界面模块 (窗口管理、事件处理、图形渲染)
### 🎮 功能模块 ### 🎮 功能模块
- **`ui.c/h`** - 用户界面模块,负责所有显示和交互 - **`ui.c/h`** - 用户界面模块,负责所有显示和交互
@@ -238,7 +273,12 @@ chcp 65001
- **`gobang_config.ini`** - 游戏配置文件 (自动生成和保存) - **`gobang_config.ini`** - 游戏配置文件 (自动生成和保存)
- **`records/`** - 对局记录目录 (CSV格式存储) - **`records/`** - 对局记录目录 (CSV格式存储)
- **`type.h`** - 数据结构和类型定义集中文件 - **`type.h`** - 数据结构和类型定义集中文件
- **`compile_gui.bat`** - GUI版本编译脚本
- **`installer/`** - 安装包制作目录
- **`setup.iss`** - Inno Setup安装脚本
- **`installer.nsi`** - NSIS安装脚本
- **`MD/README.md`** - 项目说明文档 - **`MD/README.md`** - 项目说明文档
- **`MD/README_GUI.md`** - GUI版本使用指南
- **`MD/AI_Enhancement_Guide.md`** - AI算法增强指南 - **`MD/AI_Enhancement_Guide.md`** - AI算法增强指南
- **`MD/NETWORK_README.md`** - 网络功能使用说明 - **`MD/NETWORK_README.md`** - 网络功能使用说明
- **`MD/Architecture_Refactoring_Guide.md`** - 代码架构重构详细指南 - **`MD/Architecture_Refactoring_Guide.md`** - 代码架构重构详细指南
@@ -266,6 +306,11 @@ chcp 65001
## 未来计划 ## 未来计划
### ✅ 已完成功能 ### ✅ 已完成功能
- [x] **SDL3图形化界面** - 完成现代化GUI界面开发,支持可视化棋盘操作
- [x] **双版本架构** - 实现控制台版本和GUI版本并行支持
- [x] **安装包制作** - 提供Inno Setup和NSIS两种专业安装包方案
- [x] **鼠标交互支持** - 实现直观的点击落子和界面操作
- [x] **窗口管理优化** - 完善的窗口显示、居中和事件处理机制
- [x] **模块化架构设计** - 完成代码重构,实现清晰的模块分离 - [x] **模块化架构设计** - 完成代码重构,实现清晰的模块分离
- [x] **全局变量统一管理** - 所有全局变量集中在globals模块中管理 - [x] **全局变量统一管理** - 所有全局变量集中在globals模块中管理
- [x] **宏定义优化** - 消除重复定义,统一管理所有宏定义 - [x] **宏定义优化** - 消除重复定义,统一管理所有宏定义
@@ -279,9 +324,11 @@ chcp 65001
### 🚀 开发路线图 ### 🚀 开发路线图
#### 📱 用户体验提升 #### 📱 用户体验提升
- [ ] **图形用户界面 (GUI)**:使用 `SDL2``Qt` 开发现代化图形界面 - [x] **图形用户界面 (GUI)**:使用 `SDL3` 开发现代化图形界面
- [ ] **主题系统**:支持多种UI主题和棋盘样式 - [ ] **主题系统**:支持多种UI主题和棋盘样式
- [ ] **音效系统**:添加落子音效和背景音乐 - [ ] **音效系统**:添加落子音效和背景音乐
- [ ] **动画效果**:添加落子动画和界面过渡效果
- [ ] **多语言支持**:支持中英文界面切换
#### 🌐 网络功能 #### 🌐 网络功能
- [ ] **在线对战模式**:实现网络多人对战功能 - [ ] **在线对战模式**:实现网络多人对战功能
@@ -296,4 +343,4 @@ chcp 65001
#### 🔧 技术优化 #### 🔧 技术优化
- [ ] **跨平台支持**:完整支持Linux和macOS系统 - [ ] **跨平台支持**:完整支持Linux和macOS系统
- [ ] **性能优化**:多线程搜索和内存优化 - [ ] **性能优化**:多线程搜索和内存优化
- [ ] **数据库支持**:使用SQLite存储对局历史和统计 - [ ] **数据库支持**:使用SQLite存储对局历史和统计
+49 -14
View File
@@ -2,9 +2,9 @@
======================================== ========================================
项目名称:五子棋多模式对战系统 项目名称:五子棋多模式对战系统
统计时间:2025年7月20 统计时间:2025年10月8
项目版本:v7.0 项目版本:v8.2
开发语言:C语言 开发语言:C语言 + SDL3图形库
GitHub仓库:https://github.com/LHY0125/Gobang-Game.git GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
======================================== ========================================
@@ -21,7 +21,8 @@ GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
├── ui.c 204行 ├── ui.c 204行
├── config.c 331行 ├── config.c 331行
├── network.c 426行 ├── network.c 426行
── globals.c 37行 ── globals.c 37行
└── gui.c 450行 (v8.0新增)
【头文件 (.h)】 【头文件 (.h)】
├── gobang.h 101行 ├── gobang.h 101行
@@ -33,21 +34,28 @@ GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
├── config.h 170行 ├── config.h 170行
├── network.h 186行 ├── network.h 186行
├── globals.h 41行 ├── globals.h 41行
── type.h 93行 ── type.h 93行
└── gui.h 85行 (v8.0新增)
======================================== ========================================
📈 总计统计 📈 总计统计
======================================== ========================================
总代码行数:4,398行 总代码行数:4,933行 (v8.0新增535行)
文件类型分布: 文件类型分布:
• C源文件:3,527行 (80.2%) • C源文件:3,977行 (80.6%)
• 头文件:871行 (19.8%) • 头文件:956行 (19.4%)
v8.0版本新增:
• GUI图形界面模块:535行 (10.8%)
• 安装包配置文件:2个
• 编译脚本文件:1个
模块代码分布: 模块代码分布:
• 游戏模式模块:917行 (20.9%) • 游戏模式模块:917行 (18.6%)
• AI智能模块:589行 (13.4%) • AI智能模块:589行 (11.9%)
• GUI图形界面模块:535行 (10.8%) (v8.0新增)
• 记录系统模块:531行 (12.1%) • 记录系统模块:531行 (12.1%)
• 网络对战模块:426行 (9.7%) • 网络对战模块:426行 (9.7%)
• 配置管理模块:331行 (7.5%) • 配置管理模块:331行 (7.5%)
@@ -100,6 +108,12 @@ GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
✓ 跨平台支持:Windows和Linux双平台兼容 ✓ 跨平台支持:Windows和Linux双平台兼容
【技术亮点】 【技术亮点】
• SDL3图形化界面实现(v8.0新增)
• 双版本架构设计(控制台+GUI)(v8.0新增)
• 鼠标交互和事件驱动架构(v8.0新增)
• 专业安装包制作支持(v8.2新增)
• Inno Setup和NSIS双重打包方案(v8.2新增)
• 完整的软件分发体系(v8.2新增)
• 完整的网络对战功能实现 • 完整的网络对战功能实现
• 智能AI算法与评估系统 • 智能AI算法与评估系统
• 灵活的配置管理系统 • 灵活的配置管理系统
@@ -108,9 +122,9 @@ GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
• 实时计时器系统 • 实时计时器系统
• 全局变量统一管理 • 全局变量统一管理
• 跨平台网络通信支持 • 跨平台网络通信支持
• 代码架构模块化重构v7.0新增) • 代码架构模块化重构
• 配置参数集中化管理v7.0新增) • 配置参数集中化管理
• 类型定义标准化v7.0新增) • 类型定义标准化
【总体评价】 【总体评价】
这是一个非常优秀的C语言项目,代码量适中但功能完整, 这是一个非常优秀的C语言项目,代码量适中但功能完整,
@@ -120,12 +134,24 @@ GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
包括人机对战、双人对战和网络对战,功能丰富,架构清晰, 包括人机对战、双人对战和网络对战,功能丰富,架构清晰,
是C语言项目开发的优秀范例。 是C语言项目开发的优秀范例。
v8.2版本进一步完善了软件分发体系,
通过Inno Setup和NSIS双重安装包方案,
实现了专业级的软件打包和部署功能,
为用户提供了便捷的一键安装体验,
标志着项目从开发阶段向产品化阶段的重要转变。
v8.0版本的图形化界面是项目发展的重大突破,
通过SDL3图形库实现了现代化的可视化界面,
支持鼠标交互操作,大幅提升了用户体验。
双版本架构设计既保持了控制台版本的轻量特性,
又提供了GUI版本的现代化体验,满足不同用户需求。
网络对战功能的加入使得项目具备了现代化游戏的特征, 网络对战功能的加入使得项目具备了现代化游戏的特征,
支持实时在线对战,为用户提供了更丰富的游戏体验。 支持实时在线对战,为用户提供了更丰富的游戏体验。
v7.0版本的代码架构重构是项目发展的重要里程碑, v7.0版本的代码架构重构是项目发展的重要里程碑,
通过配置统一管理、全局变量规范化、类型定义标准化等措施, 通过配置统一管理、全局变量规范化、类型定义标准化等措施,
大幅提升了代码的可维护性和扩展性,为后续功能开发 大幅提升了代码的可维护性和扩展性,为v8.0的GUI功能
奠定了坚实的架构基础。 奠定了坚实的架构基础。
======================================== ========================================
@@ -136,6 +162,7 @@ v7.0版本的代码架构重构是项目发展的重要里程碑,
• main.c/gobang.c - 主程序和核心游戏逻辑 • main.c/gobang.c - 主程序和核心游戏逻辑
• game_mode.c/h - 游戏模式管理(人机/双人/网络) • game_mode.c/h - 游戏模式管理(人机/双人/网络)
• ai.c/h - AI智能算法实现 • ai.c/h - AI智能算法实现
• gui.c/h - SDL3图形化界面模块(v8.0新增)
• network.c/h - 网络对战功能 • network.c/h - 网络对战功能
• record.c/h - 游戏记录与复盘 • record.c/h - 游戏记录与复盘
• ui.c/h - 用户界面管理 • ui.c/h - 用户界面管理
@@ -146,9 +173,17 @@ v7.0版本的代码架构重构是项目发展的重要里程碑,
配置文件: 配置文件:
• gobang_config.ini - 游戏配置文件 • gobang_config.ini - 游戏配置文件
• compile_gui.bat - GUI版本编译脚本(v8.0新增)
安装包目录:
• installer/ - 安装包制作目录(v8.0新增)
• setup.iss - Inno Setup安装脚本
• installer.nsi - NSIS安装脚本
文档目录: 文档目录:
• MD/ - 项目文档目录 • MD/ - 项目文档目录
• README_GUI.md - GUI版本使用指南(v8.0新增)
• TXT/ - 文本文档目录
• records/ - 游戏记录存储目录 • records/ - 游戏记录存储目录
开发环境: 开发环境:
+20 -4
View File
@@ -1,12 +1,28 @@
/** /**
* @file 五子棋对战系统 * @file 五子棋对战系统
* @brief C语言五子棋多模式对战系统 * @brief C语言五子棋多模式对战系统
* @details 支持人机对战、双人对战、网络对战的完整五子棋游戏系统 * @details 支持人机对战、双人对战、网络对战的完整五子棋游戏系统v8.0新增SDL3图形化界面
* @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com) * @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com)
* @date 2025-07-20 * @date 2025-10-8
* @version 7.0 * @version 8.2
* @note * @note
* 1. v7.0架构重构 * 1. v8.2专业安装包
* - 📦 双重打包方案:支持Inno Setup和NSIS两种安装包制作
* - 🚀 一键安装部署:完整的软件分发解决方案
* - 🔧 安装程序优化:自动创建快捷方式和注册表项
* - 🗂️ 完整文件打包:包含所有源码、文档和依赖文件
* - 🔄 完整卸载功能:支持干净的软件卸载和清理
* - 💼 产品化部署:从开发工具向商业软件的转变
*
* 2. v8.0图形化界面:
* - 🎨 SDL3图形化界面:实现现代化可视化棋盘界面
* - 🖱️ 鼠标交互支持:直观的点击落子操作
* - 🏗️ 双版本架构:控制台版本和GUI版本并行支持
* - 🪟 窗口管理优化:自动居中、响应式设计
* - ⚡ 事件驱动架构:流畅的用户交互体验
* - 🔧 编译脚本优化:简化GUI版本编译流程
* - 🌐 GUI网络支持:图形化界面支持网络对战
* 2. v7.0架构重构:
* - 🏗️ 代码架构全面重构,实现模块化设计 * - 🏗️ 代码架构全面重构,实现模块化设计
* - 📋 配置参数统一管理,所有配置集中到config.h * - 📋 配置参数统一管理,所有配置集中到config.h
* - 🔧 全局变量规范化,统一在globals模块管理 * - 🔧 全局变量规范化,统一在globals模块管理
+69
View File
@@ -0,0 +1,69 @@
项目要求文档 - 五子棋游戏 (v8.2)
1. 项目概述
- 开发一个基于C语言的五子棋游戏,支持本地多人、AI对战和网络对战模式。
- v8.0版本实现双版本架构:支持命令行界面和SDL3图形化界面。
- v8.2版本(当前版本)完善了专业安装包制作体系,提供企业级的软件分发解决方案。
- v8.2版本完善了软件分发体系,支持Inno Setup和NSIS双重安装包制作,提供专业级的软件打包和部署解决方案。
- 包括游戏配置、记录保存、复盘功能和专业安装包。
- 提供现代化的可视化用户体验和传统控制台体验。
2. 功能需求
- **游戏模式**
- 单人模式:玩家 vs AI
- 双人模式:本地两人对战
- 网络模式:通过TCP/IP进行远程对战
- **棋盘和规则**
- 默认15x15棋盘,支持自定义大小(5-25)
- 支持禁手规则选项
- 先手黑子,五连珠获胜
- **AI功能**
- 实现AI算法,支持不同难度级别(1-5级)
- AI应能计算最佳落子位置
- 支持Alpha-Beta剪枝算法和威胁检测
- **配置管理**
- 支持修改棋盘大小、禁手规则、计时器、网络端口等
- 配置保存到INI文件
- 默认配置和重置功能
- 统一的输入验证机制
- **游戏记录**
- 自动保存对局记录到CSV文件
- 支持复盘功能,查看历史对局
- 记录包含时间戳、玩家信息、棋局步骤等
- **用户界面**
- **控制台界面**:传统命令行界面显示棋盘
- **图形化界面**(v8.0新增):基于SDL3的现代化GUI界面
- **鼠标交互**:支持点击落子操作
- **窗口管理**:自动居中、响应式设计
- **事件驱动**:流畅的用户交互体验
- 支持坐标输入落子(数字格式:行 列)
- 显示当前玩家、计时、游戏状态等信息
- 菜单系统和配置界面
- **网络功能**
- 支持服务器/客户端模式
- 实时传输棋子位置
- 超时和断线处理
- 支持端口配置
3. 技术要求
- 使用C语言开发
- Windows平台,包含Winsock网络库(-lws2_32链接)
- **SDL3图形库**v8.0新增):用于GUI界面开发
- **双版本架构**:控制台版本和GUI版本并行支持
- 模块化设计:分离游戏逻辑、AI、配置、网络、UI、GUI等模块
- **安装包制作**:支持Inno Setup专业安装程序
- 错误处理和统一的输入验证
- 支持跨平台编译(Windows/Linux
4. 非功能需求
- 性能:响应时间<1秒(AI计算除外)
- 可维护性:代码模块化,注释清晰
- 兼容性:Windows 11,支持GCC编译器
- 可扩展性:易于添加新的AI算法和游戏模式
5. 交付物
- 源代码文件(.c/.h文件)
- 配置文件(gobang_config.ini
- 记录文件夹(records/
- 文档:README.md、AI增强指南、架构重构指南、网络功能说明、图标指南等
- 代码统计报告和项目简介
+99
View File
@@ -0,0 +1,99 @@
@echo off
echo ===== Gobang Game Compile Script =====
echo.
echo Please select compile version:
echo 1. Console version (gobang_console.exe)
echo 2. GUI version (gobang_gui.exe)
echo 3. Compile all versions
echo 0. Exit
echo.
set /p choice="Please enter your choice (0-3): "
if "%choice%"=="0" goto :exit
if "%choice%"=="1" goto :console
if "%choice%"=="2" goto :gui
if "%choice%"=="3" goto :all
echo Invalid choice!
pause
exit /b 1
:console
echo.
echo Compiling console version...
echo.
gcc -std=c17 -o gobang_console.exe src/*.c -Iinclude -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
if %ERRORLEVEL% EQU 0 (
echo.
echo Compilation successful! Generated: gobang_console.exe
echo Run command: .\gobang_console.exe
echo.
) else (
echo.
echo Compilation failed! Please check source files and compiler installation
echo.
)
goto :end
:gui
echo.
echo Compiling GUI version...
echo.
REM Check if SDL3 library exists
if not exist "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include" (
echo Error: SDL3 library not found!
echo Please ensure SDL3 is extracted to: D:\settings\SDL\SDL3-3.2.22\
goto :end
)
gcc -std=c17 -o gobang_gui.exe src/*.c -Iinclude -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
if %ERRORLEVEL% EQU 0 (
echo.
echo Compilation successful! Generated: gobang_gui.exe
echo.
echo Copying SDL3.dll to current directory...
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" . >nul 2>&1
echo Run command: .\gobang_gui.exe
echo.
) else (
echo.
echo Compilation failed! Please check:
echo 1. SDL3 library path is correct
echo 2. All source files exist
echo 3. gcc compiler is installed
echo.
)
goto :end
:all
echo.
echo Compiling all versions...
echo.
echo [1/2] Compiling console version...
gcc -std=c17 -o gobang_console.exe src/*.c -Iinclude -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
if %ERRORLEVEL% EQU 0 (
echo Console version compilation successful!
) else (
echo Console version compilation failed!
)
echo.
echo [2/2] Compiling GUI version...
if not exist "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include" (
echo Error: SDL3 library not found! Skipping GUI version compilation
goto :end
)
gcc -std=c17 -o gobang_gui.exe src/*.c -Iinclude -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
if %ERRORLEVEL% EQU 0 (
echo GUI version compilation successful!
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" . >nul 2>&1
) else (
echo GUI version compilation failed!
)
echo.
echo Compilation complete! Generated files:
dir *.exe 2>nul
echo.
goto :end
:end
pause
:exit
-37
View File
@@ -1,37 +0,0 @@
/**
* @file globals.c
* @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com)
* @brief 全局变量定义和初始化文件
* @version 6.0
* @date 2025-07-10
* @note 集中管理所有全局变量的定义和初始化,提高代码可维护性
*/
#include "globals.h"
#include "config.h"
// ==================== 游戏核心变量定义 ====================
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 = DEFAULT_USE_FORBIDDEN_MOVES; // 是否启用禁手规则
int use_timer = DEFAULT_USE_TIMER; // 是否启用计时器
int time_limit = DEFAULT_TIME_LIMIT; // 每回合的时间限制(秒)
int network_port = DEFAULT_NETWORK_PORT; // 网络端口
int network_timeout = NETWORK_TIMEOUT_MS; // 网络超时时间
// ==================== AI相关变量定义 ====================
double defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT; // 防守系数
// ==================== 网络相关变量定义 ====================
NetworkGameState network_state = {0}; // 网络游戏状态
// ==================== 记录相关变量定义 ====================
int player1_final_score = 0; // 玩家1最终得分
int player2_final_score = 0; // 玩家2最终得分
int scores_calculated = 0; // 评分计算标志
char winner_info[50] = "平局或未完成"; // 存储胜负信息
+1
View File
@@ -6,6 +6,7 @@
* 2. * 2.
* 3. * 3.
*/ */
#ifndef AI_H #ifndef AI_H
#define AI_H #define AI_H
+39 -23
View File
@@ -32,36 +32,36 @@
#define INPUT_SURRENDER -4 // 认输 #define INPUT_SURRENDER -4 // 认输
//---------- 游戏设置默认值 ----------// //---------- 游戏设置默认值 ----------//
#define DEFAULT_USE_FORBIDDEN_MOVES false // 默认不启用禁手规则 #define DEFAULT_USE_FORBIDDEN_MOVES false // 默认不启用禁手规则
#define DEFAULT_USE_TIMER 0 // 默认不启用计时器 #define DEFAULT_USE_TIMER 0 // 默认不启用计时器
#define DEFAULT_TIME_LIMIT 30 // 默认时间限制为30秒(内部存储) #define DEFAULT_TIME_LIMIT 30 // 默认时间限制为30秒(内部存储)
//---------- AI参数 ----------// //---------- AI参数 ----------//
#define DEFAULT_AI_DEPTH 5 // 默认AI搜索深度(从3提升到5 #define DEFAULT_AI_DEPTH 5 // 默认AI搜索深度
#define DEFAULT_DEFENSE_COEFFICIENT 1.5 // 默认防守系数(从1.2提升到1.5 #define DEFAULT_DEFENSE_COEFFICIENT 1.5 // 默认防守系数
//---------- 网络参数 ----------// //---------- 网络参数 ----------//
#define DEFAULT_NETWORK_PORT 8888 // 默认网络端口 #define DEFAULT_NETWORK_PORT 8888 // 默认网络端口
#define MIN_NETWORK_PORT 1024 // 最小网络端口 #define MIN_NETWORK_PORT 1024 // 最小网络端口
#define MAX_NETWORK_PORT 65535 // 最大网络端口 #define MAX_NETWORK_PORT 65535 // 最大网络端口
#define NETWORK_TIMEOUT_MS 5000 // 网络超时时间(毫秒) #define NETWORK_TIMEOUT_MS 5000 // 网络超时时间(毫秒)
#define NETWORK_BUFFER_SIZE 1024 // 网络缓冲区大小 #define NETWORK_BUFFER_SIZE 1024 // 网络缓冲区大小
// 网络配置 // 网络配置
#define DEFAULT_PORT 8888 // 默认端口(与DEFAULT_NETWORK_PORT保持一致) #define DEFAULT_PORT 8888 // 默认端口(与DEFAULT_NETWORK_PORT保持一致)
#define BUFFER_SIZE 1024 // 缓冲区大小(与NETWORK_BUFFER_SIZE保持一致) #define BUFFER_SIZE 1024 // 缓冲区大小(与NETWORK_BUFFER_SIZE保持一致)
#define MAX_IP_LENGTH 16 // 最大IP地址长度 #define MAX_IP_LENGTH 16 // 最大IP地址长度
// 网络消息类型 // 网络消息类型
#define MSG_MOVE 1 // 落子消息 #define MSG_MOVE 1 // 落子消息
#define MSG_CHAT 2 // 聊天消息 #define MSG_CHAT 2 // 聊天消息
#define MSG_SURRENDER 3 // 认输消息 #define MSG_SURRENDER 3 // 认输消息
#define MSG_UNDO_REQUEST 4 // 悔棋请求 #define MSG_UNDO_REQUEST 4 // 悔棋请求
#define MSG_UNDO_RESPONSE 5 // 悔棋回应 #define MSG_UNDO_RESPONSE 5 // 悔棋回应
#define MSG_GAME_START 6 // 游戏开始 #define MSG_GAME_START 6 // 游戏开始
#define MSG_GAME_END 7 // 游戏结束 #define MSG_GAME_END 7 // 游戏结束
#define MSG_HEARTBEAT 8 // 心跳包 #define MSG_HEARTBEAT 8 // 心跳包
#define MSG_DISCONNECT 9 // 断线消息 #define MSG_DISCONNECT 9 // 断线消息
//---------- 评分参数 ----------// //---------- 评分参数 ----------//
// 棋型评分 - 用于calculate_step_score函数 // 棋型评分 - 用于calculate_step_score函数
@@ -116,7 +116,23 @@
#define TIME_WEIGHT_FACTOR 0.5 // 时间权重因子 #define TIME_WEIGHT_FACTOR 0.5 // 时间权重因子
#define WIN_BONUS 2000 // 胜利奖励分数 #define WIN_BONUS 2000 // 胜利奖励分数
// 文件路径参数 //---------- GUI界面参数 ----------//
// 窗口和棋盘配置
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define BOARD_OFFSET_X 50
#define BOARD_OFFSET_Y 50
#define CELL_SIZE 30
#define STONE_RADIUS 12
// 颜色定义
#define GUI_COLOR_BACKGROUND {240, 217, 181, 255}
#define GUI_COLOR_BOARD_LINE {0, 0, 0, 255}
#define GUI_COLOR_BLACK_STONE {0, 0, 0, 255}
#define GUI_COLOR_WHITE_STONE {255, 255, 255, 255}
#define GUI_COLOR_STONE_BORDER {100, 100, 100, 255}
//---------- 文件路径参数 ----------//
#define RECORDS_DIR "records" // 记录文件目录 #define RECORDS_DIR "records" // 记录文件目录
#define CONFIG_FILE "gobang_config.ini" // 配置文件路径 #define CONFIG_FILE "gobang_config.ini" // 配置文件路径
#define MAX_PATH_LENGTH 256 // 最大路径长度 #define MAX_PATH_LENGTH 256 // 最大路径长度
+16 -1
View File
@@ -86,8 +86,23 @@ bool handle_network_player_turn(int current_player, bool is_local_turn);
/** /**
* @brief * @brief
* @return true * @return true
* @return false 退 * @return false
*/ */
bool network_game_loop(); bool network_game_loop();
/**
* @brief
*/
void show_game_rules();
/**
* @brief
*/
void show_about_game();
/**
* @brief
*/
void run_gui_mode();
#endif // GAME_MODE_H #endif // GAME_MODE_H
+9
View File
@@ -11,6 +11,7 @@
#include "gobang.h" #include "gobang.h"
#include "network.h" #include "network.h"
#include <stdbool.h> #include <stdbool.h>
#include <SDL3/SDL.h>
// ==================== 游戏核心变量 ==================== // ==================== 游戏核心变量 ====================
extern int BOARD_SIZE; // 当前实际使用的棋盘尺寸 extern int BOARD_SIZE; // 当前实际使用的棋盘尺寸
@@ -32,6 +33,14 @@ extern double defense_coefficient; // 防守系数
// ==================== 网络相关变量 ==================== // ==================== 网络相关变量 ====================
extern NetworkGameState network_state; // 网络游戏状态 extern NetworkGameState network_state; // 网络游戏状态
// ==================== GUI相关变量 ====================
extern SDL_Window* window; // SDL窗口指针
extern SDL_Renderer* renderer; // SDL渲染器指针
extern int gui_running; // GUI运行状态标志
extern int current_player_gui; // GUI当前玩家
extern int game_over; // 游戏结束标志
extern char status_message[256]; // 状态消息
// ==================== 记录相关变量 ==================== // ==================== 记录相关变量 ====================
extern int player1_final_score; // 玩家1最终得分 extern int player1_final_score; // 玩家1最终得分
extern int player2_final_score; // 玩家2最终得分 extern int player2_final_score; // 玩家2最终得分
+1
View File
@@ -4,6 +4,7 @@
* @note * @note
* AI决策等功能 * AI决策等功能
*/ */
#ifndef GO_BANG_H #ifndef GO_BANG_H
#define GO_BANG_H #define GO_BANG_H
+154
View File
@@ -0,0 +1,154 @@
/**
* @file gui.h
* @brief 图形化用户界面头文件
* @note 使用SDL3库实现五子棋的图形化界面
* @author 刘航宇
* @date 2025-01-15
*/
#ifndef GUI_H
#define GUI_H
#include <SDL3/SDL.h>
#include "gobang.h"
#include "config.h"
#include "globals.h"
// GUI函数声明
/**
* @brief 初始化GUI
* @details 初始化SDL3图形库和游戏界面组件:
* - 初始化SDL视频子系统
* - 创建游戏窗口(可调整大小)
* - 创建SDL渲染器
* - 初始化游戏状态和棋盘数据
* @return 成功返回0,失败返回-1
* @note 窗口标题为"五子棋游戏 - SDL3版本"
* 窗口尺寸由WINDOW_WIDTH和WINDOW_HEIGHT定义
* 失败时会自动清理已创建的资源
*/
int init_gui();
/**
* @brief 清理GUI资源
* @details 按顺序释放所有SDL相关资源:
* - 销毁SDL渲染器
* - 销毁SDL窗口
* - 退出SDL子系统
* @note 函数会检查资源是否存在再进行释放
* 释放后将指针设置为NULL防止重复释放
* 程序退出时必须调用此函数避免内存泄漏
*/
void cleanup_gui();
/**
* @brief 渲染游戏画面
* @details 完整的游戏画面渲染流程:
* - 清空屏幕并设置背景色
* - 绘制棋盘网格和标记点
* - 绘制所有棋子
* - 绘制UI界面元素
* - 将渲染结果显示到屏幕
* @note 使用双缓冲技术,通过SDL_RenderPresent显示最终结果
* 背景色由GUI_COLOR_BACKGROUND定义
* 每帧都会完全重绘整个画面
*/
void render_game();
/**
* @brief 处理事件
* @details 处理所有SDL事件并执行相应操作:
* - SDL_EVENT_QUIT:用户关闭窗口
* - SDL_EVENT_KEY_DOWN:键盘按键(ESC退出)
* - SDL_EVENT_MOUSE_BUTTON_DOWN:鼠标点击落子
* @return 继续运行返回1,退出返回0
* @note 鼠标左键点击会转换为棋盘坐标并尝试落子
* 落子后会检查胜负并切换玩家
* 游戏结束后不再响应落子操作
*/
int handle_events();
/**
* @brief 绘制棋盘
* @details 绘制15x15的五子棋棋盘,包括:
* - 横竖交叉的网格线
* - 天元点(棋盘中心的标记点)
* - 四个星位(棋盘上的定位点)
* @note 使用SDL3渲染器绘制线条和填充矩形
* 棋盘线条颜色由GUI_COLOR_BOARD_LINE定义
* 天元点和星位用黑色小矩形标记
*/
void draw_board();
/**
* @brief 绘制棋子
* @details 遍历整个棋盘数组,绘制所有已落下的棋子:
* - 黑子:使用GUI_COLOR_BLACK_STONE颜色
* - 白子:使用GUI_COLOR_WHITE_STONE颜色
* - 每个棋子都有边框:使用GUI_COLOR_STONE_BORDER颜色
* @note 棋子绘制为圆形,半径由STONE_RADIUS定义
* 通过draw_circle函数实现圆形绘制
* 棋子位置根据棋盘坐标和CELL_SIZE计算屏幕坐标
*/
void draw_stones();
/**
* @brief 绘制UI元素
* @details 绘制游戏界面的用户交互元素:
* - 状态信息区域背景和边框
* - 当前玩家指示器(黑子或白子圆形)
* - 游戏状态显示区域
* @note 暂时使用简单图形代替文字显示
* 需要额外字体库支持文字渲染
* 指示器位置在棋盘右侧固定区域
*/
void draw_ui_elements();
/**
* @brief 绘制圆形
* @param center_x 圆心X坐标
* @param center_y 圆心Y坐标
* @param radius 半径
* @param color 颜色
* @details 使用像素级绘制实现圆形:
* - 遍历圆形外接矩形内的所有像素点
* - 计算每个像素到圆心的距离
* - 距离小于等于半径的像素点进行着色
* @note 采用暴力算法,性能较低但实现简单
* 适用于绘制棋子等小尺寸圆形
* SDL3没有内置圆形绘制函数,需要自实现
*/
void draw_circle(int center_x, int center_y, int radius, SDL_Color color);
/**
* @brief 屏幕坐标转棋盘坐标
* @param screen_x 屏幕X坐标
* @param screen_y 屏幕Y坐标
* @param board_x 输出棋盘X坐标
* @param board_y 输出棋盘Y坐标
* @return 转换成功返回1,失败返回0
* @details 坐标转换算法:
* - 减去棋盘偏移量得到相对坐标
* - 加上半个格子尺寸实现就近取整
* - 除以格子尺寸得到棋盘坐标
* - 检查坐标是否在有效范围内
* @note 使用就近取整算法,点击格子中心附近都会定位到该格子
* 坐标范围检查确保不会越界访问棋盘数组
*/
int screen_to_board(int screen_x, int screen_y, int *board_x, int *board_y);
/**
* @brief 显示消息
* @param message 要显示的消息
* @details 消息显示功能:
* - 将消息复制到全局状态消息缓冲区
* - 同时在控制台输出消息内容
* - 确保字符串安全复制,防止缓冲区溢出
* @note 消息会存储在status_message全局变量中
* 字符串长度限制为缓冲区大小减1
* 消息可用于游戏状态提示和错误信息显示
*/
void show_message(const char *message);
#endif // GUI_H
+1
View File
@@ -4,6 +4,7 @@
* @note * @note
* *
*/ */
#ifndef INIT_BOARD_H #ifndef INIT_BOARD_H
#define INIT_BOARD_H #define INIT_BOARD_H
View File
View File
+20 -20
View File
@@ -23,9 +23,9 @@
* @brief * @brief
*/ */
typedef struct { typedef struct {
int player; // 执行该步的玩家标识 int player; // 执行该步的玩家标识
int x; // 落子的行坐标 (0-based) int x; // 落子的行坐标 (0-based)
int y; // 落子的列坐标 (0-based) int y; // 落子的列坐标 (0-based)
} Step; } Step;
/** /**
@@ -33,9 +33,9 @@ typedef struct {
* @details * @details
*/ */
typedef struct { typedef struct {
int continuous_chess; // 连续同色棋子的数量 int continuous_chess; // 连续同色棋子的数量
bool check_start; // 棋子序列的起始端是否为空位(即是否开放) bool check_start; // 棋子序列的起始端是否为空位(即是否开放)
bool check_end; // 棋子序列的末尾端是否为空位(即是否开放) bool check_end; // 棋子序列的末尾端是否为空位(即是否开放)
} DirInfo; } DirInfo;
// ==================== AI相关数据结构 ==================== // ==================== AI相关数据结构 ====================
@@ -45,8 +45,8 @@ typedef struct {
* @details AI移动排序 * @details AI移动排序
*/ */
typedef struct { typedef struct {
int x, y; // 位置坐标 int x, y; // 位置坐标
int score; // 评估分数 int score; // 评估分数
} ScoredMove; } ScoredMove;
/** /**
@@ -54,12 +54,12 @@ typedef struct {
* @details AI威胁检测系统 * @details AI威胁检测系统
*/ */
typedef enum { typedef enum {
THREAT_NONE = 0, // 无威胁 THREAT_NONE = 0, // 无威胁
THREAT_WIN = 5, // 直接获胜 THREAT_WIN = 5, // 直接获胜
THREAT_FOUR = 4, // 活四/冲四 THREAT_FOUR = 4, // 活四/冲四
THREAT_THREE = 3, // 活三 THREAT_THREE = 3, // 活三
THREAT_DOUBLE = 2, // 双威胁 THREAT_DOUBLE = 2, // 双威胁
THREAT_POTENTIAL = 1 // 潜在威胁 THREAT_POTENTIAL = 1 // 潜在威胁
} ThreatLevel; } ThreatLevel;
// ==================== 网络相关数据结构 ==================== // ==================== 网络相关数据结构 ====================
@@ -69,11 +69,11 @@ typedef enum {
* @details * @details
*/ */
typedef struct { typedef struct {
int type; // 消息类型 int type; // 消息类型
int player_id; // 玩家ID int player_id; // 玩家ID
int x, y; // 坐标(用于落子) int x, y; // 坐标(用于落子)
char message[256]; // 消息内容(用于聊天等) char message[256]; // 消息内容(用于聊天等)
time_t timestamp; // 时间戳 time_t timestamp; // 时间戳
} NetworkMessage; } NetworkMessage;
/** /**
@@ -87,7 +87,7 @@ typedef struct {
int local_player_id; // 本地玩家ID int local_player_id; // 本地玩家ID
int remote_player_id; // 远程玩家ID int remote_player_id; // 远程玩家ID
char remote_ip[16]; // 远程IP地址(MAX_IP_LENGTH = 16 char remote_ip[16]; // 远程IP地址(MAX_IP_LENGTH = 16
int port; // 端口号 int port; // 端口号
} NetworkGameState; } NetworkGameState;
#endif // TYPE_H #endif // TYPE_H
+1
View File
@@ -4,6 +4,7 @@
* @note * @note
* *
*/ */
#ifndef UI_H #ifndef UI_H
#define UI_H #define UI_H
+56
View File
@@ -0,0 +1,56 @@
[Setup]
AppName=五子棋游戏
AppVersion=8.3
AppPublisher=LHY
AppPublisherURL=https://github.com/LHY0125/gobang.git
AppSupportURL=https://github.com/LHY0125/gobang.git
AppUpdatesURL=https://github.com/LHY0125/gobang.git
DefaultDirName={autopf}\Gobang
DefaultGroupName=五子棋游戏
AllowNoIcons=yes
LicenseFile=..\README.md
OutputDir=dist
OutputBaseFilename=Gobang_Inno_Setup
SetupIconFile=
Compression=lzma
SolidCompression=yes
WizardStyle=modern
PrivilegesRequired=lowest
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "..\gobang_console.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gobang_gui.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\SDL3.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\include\*"; DestDir: "{app}\include"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\src\*"; DestDir: "{app}\src"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\MD\*"; DestDir: "{app}\MD"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\TXT\*"; DestDir: "{app}\TXT"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\records\*"; DestDir: "{app}\records"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\installer\*"; DestDir: "{app}\installer"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\compile.bat"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\gobang_config.ini"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\Makefile"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\README.md"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\五子棋游戏(控制台版)"; Filename: "{app}\gobang_console.exe"
Name: "{group}\五子棋游戏(图形界面版)"; Filename: "{app}\gobang_gui.exe"
Name: "{group}\{cm:UninstallProgram,五子棋游戏}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\五子棋游戏"; Filename: "{app}\gobang_gui.exe"; Tasks: desktopicon
[Run]
Filename: "{app}\gobang_gui.exe"; Description: "{cm:LaunchProgram,五子棋游戏}"; Flags: nowait postinstall skipifsilent
[UninstallDelete]
Type: filesandordirs; Name: "{app}\records"
Type: filesandordirs; Name: "{app}\include"
Type: filesandordirs; Name: "{app}\src"
Type: filesandordirs; Name: "{app}\MD"
Type: filesandordirs; Name: "{app}\TXT"
Type: filesandordirs; Name: "{app}\installer"
+162
View File
@@ -0,0 +1,162 @@
; NSIS Install Script - Gobang Game
; Version: v8.3
; Author: LHY
!define PRODUCT_NAME "Gobang Game"
!define PRODUCT_VERSION "8.3"
!define PRODUCT_PUBLISHER "LHY"
!define PRODUCT_WEB_SITE "https://github.com/LHY0125/gobang.git"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\gobang_gui.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
; Include Modern UI
!include "MUI2.nsh"
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
; Welcome page
!insertmacro MUI_PAGE_WELCOME
; License page
!insertmacro MUI_PAGE_LICENSE "..\README.md"
; Components page
!insertmacro MUI_PAGE_COMPONENTS
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Install page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\gobang_gui.exe"
!insertmacro MUI_PAGE_FINISH
; Uninstall page
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "SimpChinese"
; Installer attributes
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "dist\Gobang_NSIS_Setup.exe"
InstallDir "$PROGRAMFILES\Gobang"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show
; Version information
VIProductVersion "1.0.0.0"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "Comments" "Gobang Game - Classic five-in-a-row strategy game with AI and network support"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalTrademarks" "MIT License"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalCopyright" "© 2025 ${PRODUCT_PUBLISHER}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileDescription" "${PRODUCT_NAME} Setup"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileVersion" "${PRODUCT_VERSION}"
Section "Main Program" SEC01
SectionIn RO
SetOutPath "$INSTDIR"
SetOverwrite ifnewer
File "..\gobang_console.exe"
File "..\gobang_gui.exe"
File "..\SDL3.dll"
File "..\compile.bat"
File "..\gobang_config.ini"
File "..\Makefile"
File "..\README.md"
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Gobang Console.lnk" "$INSTDIR\gobang_console.exe"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Gobang GUI.lnk" "$INSTDIR\gobang_gui.exe"
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\gobang_gui.exe"
SectionEnd
Section "Source Code" SEC02
SetOutPath "$INSTDIR\include"
File /r "..\include\*.*"
SetOutPath "$INSTDIR\src"
File /r "..\src\*.*"
SetOutPath "$INSTDIR\installer"
File /r "..\installer\*.*"
SectionEnd
Section "Game Records" SEC03
SetOutPath "$INSTDIR\records"
File /r "..\records\*.*"
SectionEnd
Section "Documentation" SEC04
SetOutPath "$INSTDIR\MD"
File /r "..\MD\*.*"
SetOutPath "$INSTDIR\TXT"
File /r "..\TXT\*.*"
SectionEnd
Section -AdditionalIcons
WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\gobang_gui.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\gobang_gui.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd
; Component descriptions
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "Install main program files. This is a required component."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "Install source code files, including headers and implementation files."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC03} "Install game records and save files."
!insertmacro MUI_DESCRIPTION_TEXT ${SEC04} "Install project documentation, including user manual and technical documents."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) has been successfully removed from your computer."
FunctionEnd
Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
Abort
FunctionEnd
Section Uninstall
Delete "$INSTDIR\${PRODUCT_NAME}.url"
Delete "$INSTDIR\uninst.exe"
Delete "$INSTDIR\gobang_console.exe"
Delete "$INSTDIR\gobang_gui.exe"
Delete "$INSTDIR\SDL3.dll"
Delete "$INSTDIR\compile.bat"
Delete "$INSTDIR\gobang_config.ini"
Delete "$INSTDIR\Makefile"
Delete "$INSTDIR\README.md"
RMDir /r "$INSTDIR\include"
RMDir /r "$INSTDIR\src"
RMDir /r "$INSTDIR\installer"
RMDir /r "$INSTDIR\records"
RMDir /r "$INSTDIR\MD"
RMDir /r "$INSTDIR\TXT"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Website.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Gobang Console.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Gobang GUI.lnk"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
RMDir "$INSTDIR"
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
SetAutoClose true
SectionEnd
-84
View File
@@ -1,84 +0,0 @@
#include "game_mode.h"
#include "ui.h"
#include "config.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#endif
/**
* @brief powershell
* gcc -std=c17 -o gobang.exe *.c -lws2_32
.\gobang.exe
* gcc network.c网络模块-lws2_32链接Windows网络库
*/
int main(int argc, char *argv[])
{
// 设置控制台编码为UTF-8
#ifdef _WIN32
system("chcp 65001 > nul"); // 设置控制台编码为UTF-8
SetConsoleOutputCP(65001); // 设置控制台输出编码
SetConsoleCP(65001); // 设置控制台输入编码
_mkdir("records");
#endif
// 加载游戏配置
load_game_config();
// 选择模式
while(1)
{
clear_screen();
display_main_menu();
int mode = get_integer_input("请输入模式(0-7): ", 0, 7);
switch (mode)
{
// 1. 人机对战
case 1:
run_ai_game();
break;
// 2. 玩家对战
case 2:
run_pvp_game();
break;
// 3. 网络对战
case 3:
run_network_game();
break;
// 4. 复盘模式
case 4:
run_review_mode();
break;
// 5. 配置管理
case 5:
config_management_menu();
break;
// 6. 游戏规则
case 6:
clear_screen();
display_game_rules();
pause_for_input("\n按任意键返回主菜单...");
break;
// 7. 关于游戏
case 7:
clear_screen();
display_about();
pause_for_input("\n按任意键返回主菜单...");
break;
// 0. 退出游戏
case 0:
save_game_config();
printf("感谢使用五子棋游戏!\n");
return 0;
default:
printf("无效的选择!\n");
pause_for_input("按任意键继续...");
break;
}
}
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,游戏结果
1,15,289,757,玩家2获胜
步数,玩家,行坐标,列坐标
1,2,8,8
2,1,3,3
3,2,4,4
4,1,5,5
5,2,4,5
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,游戏结果
2 1,15,289,757,玩家2获胜
3 步数,玩家,行坐标,列坐标
4 1,2,8,8
5 2,1,3,3
6 3,2,4,4
7 4,1,5,5
8 5,2,4,5
-18
View File
@@ -1,18 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分
1,15,1581,33119,玩家1得分
步数,玩家,行坐标,列坐标
1,1,2,2
2,2,3,3
3,1,2,3
4,2,2,4
5,1,4,4
6,2,4,2
7,1,5,5
8,2,1,5
9,1,6,6
10,2,7,7
11,1,7,6
12,2,5,6
13,1,9,9
14,2,5,1
1 游戏模式,棋盘大小,玩家1得分,玩家2得分
2 1,15,1581,33119,玩家1得分
3 步数,玩家,行坐标,列坐标
4 1,1,2,2
5 2,2,3,3
6 3,1,2,3
7 4,2,2,4
8 5,1,4,4
9 6,2,4,2
10 7,1,5,5
11 8,2,1,5
12 9,1,6,6
13 10,2,7,7
14 11,1,7,6
15 12,2,5,6
16 13,1,9,9
17 14,2,5,1
-35
View File
@@ -1,35 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分
1,15,5985,7221,玩家1得分
步数,玩家,行坐标,列坐标
1,2,8,8
2,1,4,4
3,2,5,5
4,1,7,7
5,2,7,8
6,1,3,3
7,2,6,8
8,1,5,8
9,2,9,8
10,1,10,8
11,2,8,7
12,1,8,6
13,2,9,7
14,1,9,6
15,2,7,6
16,1,6,5
17,2,10,9
18,1,6,4
19,2,5,4
20,1,6,3
21,2,6,2
22,1,6,6
23,2,6,7
24,1,5,6
25,2,4,7
26,1,5,7
27,2,4,8
28,1,5,9
29,2,5,10
30,1,1,1
31,2,11,10
1 游戏模式,棋盘大小,玩家1得分,玩家2得分
2 1,15,5985,7221,玩家1得分
3 步数,玩家,行坐标,列坐标
4 1,2,8,8
5 2,1,4,4
6 3,2,5,5
7 4,1,7,7
8 5,2,7,8
9 6,1,3,3
10 7,2,6,8
11 8,1,5,8
12 9,2,9,8
13 10,1,10,8
14 11,2,8,7
15 12,1,8,6
16 13,2,9,7
17 14,1,9,6
18 15,2,7,6
19 16,1,6,5
20 17,2,10,9
21 18,1,6,4
22 19,2,5,4
23 20,1,6,3
24 21,2,6,2
25 22,1,6,6
26 23,2,6,7
27 24,1,5,6
28 25,2,4,7
29 26,1,5,7
30 27,2,4,8
31 28,1,5,9
32 29,2,5,10
33 30,1,1,1
34 31,2,11,10
-22
View File
@@ -1,22 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
1,15,5155,2527,玩家获胜
步数,玩家,行坐标,列坐标
1,2,8,8
2,1,4,4
3,2,5,5
4,1,6,6
5,2,5,6
6,1,7,7
7,2,5,7
8,1,5,4
9,2,3,4
10,1,6,4
11,2,6,5
12,1,7,4
13,2,8,4
14,1,7,5
15,2,5,3
16,1,7,6
17,2,7,3
18,1,7,8
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 1,15,5155,2527,玩家获胜
3 步数,玩家,行坐标,列坐标
4 1,2,8,8
5 2,1,4,4
6 3,2,5,5
7 4,1,6,6
8 5,2,5,6
9 6,1,7,7
10 7,2,5,7
11 8,1,5,4
12 9,2,3,4
13 10,1,6,4
14 11,2,6,5
15 12,1,7,4
16 13,2,8,4
17 14,1,7,5
18 15,2,5,3
19 16,1,7,6
20 17,2,7,3
21 18,1,7,8
-15
View File
@@ -1,15 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2,15,2898,5601,玩家1获胜
步数,玩家,行坐标,列坐标
1,1,1,1
2,2,2,2
3,1,3,3
4,2,1,2
5,1,4,4
6,2,1,3
7,1,5,5
8,2,1,4
9,1,6,6
10,2,1,5
11,1,7,7
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 2,15,2898,5601,玩家1获胜
3 步数,玩家,行坐标,列坐标
4 1,1,1,1
5 2,2,2,2
6 3,1,3,3
7 4,2,1,2
8 5,1,4,4
9 6,2,1,3
10 7,1,5,5
11 8,2,1,4
12 9,1,6,6
13 10,2,1,5
14 11,1,7,7
-7
View File
@@ -1,7 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2,15,135,452,平局或未完成
步数,玩家,行坐标,列坐标
1,2,3,3
2,1,4,4
3,2,4,3
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 2,15,135,452,平局或未完成
3 步数,玩家,行坐标,列坐标
4 1,2,3,3
5 2,1,4,4
6 3,2,4,3
-6
View File
@@ -1,6 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2,15,110,212,平局或未完成
步数,玩家,行坐标,列坐标
1,1,3,3
2,2,6,6
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 2,15,110,212,平局或未完成
3 步数,玩家,行坐标,列坐标
4 1,1,3,3
5 2,2,6,6
-6
View File
@@ -1,6 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2,15,110,212,平局或未完成
步数,玩家,行坐标,列坐标
1,1,3,3
2,2,6,6
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 2,15,110,212,平局或未完成
3 步数,玩家,行坐标,列坐标
4 1,1,3,3
5 2,2,6,6
-14
View File
@@ -1,14 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
3,15,13500,1039,平局或未完成
步数,玩家,行坐标,列坐标
1,1,4,4
2,2,5,5
3,1,6,6
4,2,7,7
5,1,5,4
6,2,4,5
7,1,6,5
8,2,5,6
9,1,6,3
10,1,6,4
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 3,15,13500,1039,平局或未完成
3 步数,玩家,行坐标,列坐标
4 1,1,4,4
5 2,2,5,5
6 3,1,6,6
7 4,2,7,7
8 5,1,5,4
9 6,2,4,5
10 7,1,6,5
11 8,2,5,6
12 9,1,6,3
13 10,1,6,4
-5
View File
@@ -1,5 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
3,15,0,130,平局或未完成
步数,玩家,行坐标,列坐标
1,2,4,4
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 3,15,0,130,平局或未完成
3 步数,玩家,行坐标,列坐标
4 1,2,4,4
-28
View File
@@ -1,28 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
1,20,4575,8652,玩家获胜
步数,玩家,行坐标,列坐标
1,2,11,11
2,1,3,3
3,2,4,4
4,1,5,5
5,2,4,5
6,1,4,6
7,2,3,7
8,1,4,7
9,2,4,8
10,1,2,6
11,2,3,6
12,1,5,9
13,2,3,8
14,1,5,8
15,2,5,7
16,1,6,9
17,2,4,9
18,1,3,9
19,2,7,10
20,1,5,10
21,2,4,11
22,1,5,11
23,2,4,10
24,1,5,12
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 1,20,4575,8652,玩家获胜
3 步数,玩家,行坐标,列坐标
4 1,2,11,11
5 2,1,3,3
6 3,2,4,4
7 4,1,5,5
8 5,2,4,5
9 6,1,4,6
10 7,2,3,7
11 8,1,4,7
12 9,2,4,8
13 10,1,2,6
14 11,2,3,6
15 12,1,5,9
16 13,2,3,8
17 14,1,5,8
18 15,2,5,7
19 16,1,6,9
20 17,2,4,9
21 18,1,3,9
22 19,2,7,10
23 20,1,5,10
24 21,2,4,11
25 22,1,5,11
26 23,2,4,10
27 24,1,5,12
-21
View File
@@ -1,21 +0,0 @@
游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
1,15,1478,3769,AI获胜
步数,玩家,行坐标,列坐标
1,2,8,8
2,1,3,3
3,2,4,4
4,1,3,4
5,2,3,5
6,1,5,3
7,2,4,3
8,1,4,5
9,2,5,6
10,1,6,6
11,2,3,2
12,1,5,5
13,2,5,4
14,1,7,7
15,2,6,5
16,1,1,1
17,2,7,6
1 游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果
2 1,15,1478,3769,AI获胜
3 步数,玩家,行坐标,列坐标
4 1,2,8,8
5 2,1,3,3
6 3,2,4,4
7 4,1,3,4
8 5,2,3,5
9 6,1,5,3
10 7,2,4,3
11 8,1,4,5
12 9,2,5,6
13 10,1,6,6
14 11,2,3,2
15 12,1,5,5
16 13,2,5,4
17 14,1,7,7
18 15,2,6,5
19 16,1,1,1
20 17,2,7,6
+65 -56
View File
@@ -1,3 +1,12 @@
/**
* @file ai.c
* @note AI模块的函数和变量
* @note
* 1.
* 2.
* 3.
*/
#include "gobang.h" #include "gobang.h"
#include "ai.h" #include "ai.h"
#include "config.h" #include "config.h"
@@ -60,7 +69,7 @@ int evaluate_pos(int x, int y, int player)
// 直接形成五连珠为必胜 // 直接形成五连珠为必胜
if (info.continuous_chess >= 5) if (info.continuous_chess >= 5)
{ {
board[x][y] = original; // 还原棋盘 board[x][y] = original; // 还原棋盘
return SEARCH_WIN_BONUS; // 返回最大分 return SEARCH_WIN_BONUS; // 返回最大分
} }
@@ -121,8 +130,8 @@ int evaluate_pos(int x, int y, int player)
// 位置奖励:越靠近中心分数越高 // 位置奖励:越靠近中心分数越高
int center_x = BOARD_SIZE / 2; int center_x = BOARD_SIZE / 2;
int center_y = BOARD_SIZE / 2; int center_y = BOARD_SIZE / 2;
int distance = abs(x - center_x) + abs(y - center_y); // 曼哈顿距离 int distance = abs(x - center_x) + abs(y - center_y); // 曼哈顿距离
int position_bonus = AI_POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 距离中心越近奖励越高 int position_bonus = AI_POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 距离中心越近奖励越高
board[x][y] = original; // 还原棋盘状态 board[x][y] = original; // 还原棋盘状态
return total_score + position_bonus; // 返回总评估分 return total_score + position_bonus; // 返回总评估分
@@ -164,7 +173,7 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi
// 使用移动排序优化搜索效率 // 使用移动排序优化搜索效率
ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE]; ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE];
int move_count = generate_candidate_moves(candidate_moves, player); int move_count = generate_candidate_moves(candidate_moves, player);
// 限制搜索的候选移动数量以提高性能 // 限制搜索的候选移动数量以提高性能
int max_candidates = (depth >= 3) ? 15 : 25; // 深度越大,候选移动越少 int max_candidates = (depth >= 3) ? 15 : 25; // 深度越大,候选移动越少
if (move_count > max_candidates) if (move_count > max_candidates)
@@ -241,13 +250,13 @@ void ai_move(int depth)
// 1. 使用增强的威胁检测系统 // 1. 使用增强的威胁检测系统
ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE]; ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE];
int move_count = generate_candidate_moves(candidate_moves, AI); int move_count = generate_candidate_moves(candidate_moves, AI);
// 首先检查是否有直接获胜的机会 // 首先检查是否有直接获胜的机会
for (int idx = 0; idx < move_count; idx++) for (int idx = 0; idx < move_count; idx++)
{ {
int i = candidate_moves[idx].x; int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y; int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI); ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat == THREAT_WIN) if (ai_threat == THREAT_WIN)
{ {
@@ -258,13 +267,13 @@ void ai_move(int depth)
return; return;
} }
} }
// 检查是否需要阻止玩家的威胁 // 检查是否需要阻止玩家的威胁
for (int idx = 0; idx < move_count; idx++) for (int idx = 0; idx < move_count; idx++)
{ {
int i = candidate_moves[idx].x; int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y; int j = candidate_moves[idx].y;
ThreatLevel player_threat = detect_threat(i, j, PLAYER); ThreatLevel player_threat = detect_threat(i, j, PLAYER);
if (player_threat >= THREAT_FOUR) if (player_threat >= THREAT_FOUR)
{ {
@@ -275,13 +284,13 @@ void ai_move(int depth)
return; return;
} }
} }
// 检查是否需要阻止玩家的活三威胁 // 检查是否需要阻止玩家的活三威胁
for (int idx = 0; idx < move_count; idx++) for (int idx = 0; idx < move_count; idx++)
{ {
int i = candidate_moves[idx].x; int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y; int j = candidate_moves[idx].y;
ThreatLevel player_threat = detect_threat(i, j, PLAYER); ThreatLevel player_threat = detect_threat(i, j, PLAYER);
if (player_threat == THREAT_THREE) if (player_threat == THREAT_THREE)
{ {
@@ -299,7 +308,7 @@ void ai_move(int depth)
{ {
int i = candidate_moves[idx].x; int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y; int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI); ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat >= THREAT_FOUR) if (ai_threat >= THREAT_FOUR)
{ {
@@ -310,13 +319,13 @@ void ai_move(int depth)
return; return;
} }
} }
// 寻找能形成活三的位置 // 寻找能形成活三的位置
for (int idx = 0; idx < move_count; idx++) for (int idx = 0; idx < move_count; idx++)
{ {
int i = candidate_moves[idx].x; int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y; int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI); ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat == THREAT_THREE) if (ai_threat == THREAT_THREE)
{ {
@@ -327,14 +336,14 @@ void ai_move(int depth)
return; return;
} }
} }
// 3. 如果没有明显的威胁机会,选择评分最高的位置 // 3. 如果没有明显的威胁机会,选择评分最高的位置
if (move_count > 0) if (move_count > 0)
{ {
// candidate_moves已经按分数排序,直接选择第一个 // candidate_moves已经按分数排序,直接选择第一个
int best_x = candidate_moves[0].x; int best_x = candidate_moves[0].x;
int best_y = candidate_moves[0].y; int best_y = candidate_moves[0].y;
board[best_x][best_y] = AI; board[best_x][best_y] = AI;
steps[step_count++] = (Step){AI, best_x, best_y}; steps[step_count++] = (Step){AI, best_x, best_y};
printf("AI落子(%d, %d) - 最佳位置!\n", best_x + 1, best_y + 1); printf("AI落子(%d, %d) - 最佳位置!\n", best_x + 1, best_y + 1);
@@ -379,7 +388,7 @@ static int compare_moves(const void *a, const void *b)
int generate_candidate_moves(ScoredMove *moves, int player) int generate_candidate_moves(ScoredMove *moves, int player)
{ {
int count = 0; int count = 0;
for (int i = 0; i < BOARD_SIZE; i++) for (int i = 0; i < BOARD_SIZE; i++)
{ {
for (int j = 0; j < BOARD_SIZE; j++) for (int j = 0; j < BOARD_SIZE; j++)
@@ -388,51 +397,51 @@ int generate_candidate_moves(ScoredMove *moves, int player)
{ {
continue; continue;
} }
// 只考虑有意义的位置(附近有棋子) // 只考虑有意义的位置(附近有棋子)
if (step_count > AI_SEARCH_RANGE_THRESHOLD && !is_near_stones(i, j)) if (step_count > AI_SEARCH_RANGE_THRESHOLD && !is_near_stones(i, j))
{ {
continue; continue;
} }
// 计算该位置的评估分数 // 计算该位置的评估分数
moves[count].x = i; moves[count].x = i;
moves[count].y = j; moves[count].y = j;
// 结合威胁检测和位置评估 // 结合威胁检测和位置评估
ThreatLevel threat = detect_threat(i, j, player); ThreatLevel threat = detect_threat(i, j, player);
int base_score = evaluate_move(i, j); int base_score = evaluate_move(i, j);
// 根据威胁等级调整分数 // 根据威胁等级调整分数
switch (threat) switch (threat)
{ {
case THREAT_WIN: case THREAT_WIN:
moves[count].score = base_score + 10000; moves[count].score = base_score + 10000;
break; break;
case THREAT_FOUR: case THREAT_FOUR:
moves[count].score = base_score + 5000; moves[count].score = base_score + 5000;
break; break;
case THREAT_THREE: case THREAT_THREE:
moves[count].score = base_score + 2000; moves[count].score = base_score + 2000;
break; break;
case THREAT_DOUBLE: case THREAT_DOUBLE:
moves[count].score = base_score + 1000; moves[count].score = base_score + 1000;
break; break;
case THREAT_POTENTIAL: case THREAT_POTENTIAL:
moves[count].score = base_score + 500; moves[count].score = base_score + 500;
break; break;
default: default:
moves[count].score = base_score; moves[count].score = base_score;
break; break;
} }
count++; count++;
} }
} }
// 按分数降序排序 // 按分数降序排序
qsort(moves, count, sizeof(ScoredMove), compare_moves); qsort(moves, count, sizeof(ScoredMove), compare_moves);
return count; return count;
} }
@@ -471,16 +480,16 @@ ThreatLevel detect_threat(int x, int y, int player)
{ {
// 模拟落子 // 模拟落子
board[x][y] = player; board[x][y] = player;
ThreatLevel max_threat = THREAT_NONE; ThreatLevel max_threat = THREAT_NONE;
int threat_count = 0; int threat_count = 0;
// 检查四个方向 // 检查四个方向
for (int k = 0; k < 4; k++) for (int k = 0; k < 4; k++)
{ {
DirInfo info = count_specific_direction(x, y, direction[k][0], direction[k][1], player); DirInfo info = count_specific_direction(x, y, direction[k][0], direction[k][1], player);
ThreatLevel current_threat = THREAT_NONE; ThreatLevel current_threat = THREAT_NONE;
// 检查是否形成五子连珠(获胜) // 检查是否形成五子连珠(获胜)
if (info.continuous_chess >= 5) if (info.continuous_chess >= 5)
{ {
@@ -508,27 +517,27 @@ ThreatLevel detect_threat(int x, int y, int player)
{ {
current_threat = THREAT_POTENTIAL; current_threat = THREAT_POTENTIAL;
} }
if (current_threat > max_threat) if (current_threat > max_threat)
{ {
max_threat = current_threat; max_threat = current_threat;
} }
if (current_threat >= THREAT_THREE) if (current_threat >= THREAT_THREE)
{ {
threat_count++; threat_count++;
} }
} }
// 恢复棋盘 // 恢复棋盘
board[x][y] = EMPTY; board[x][y] = EMPTY;
// 如果有多个威胁,提升威胁等级 // 如果有多个威胁,提升威胁等级
if (threat_count >= 2 && max_threat >= THREAT_THREE) if (threat_count >= 2 && max_threat >= THREAT_THREE)
{ {
max_threat = THREAT_DOUBLE; max_threat = THREAT_DOUBLE;
} }
return max_threat; return max_threat;
} }
@@ -542,18 +551,18 @@ ThreatLevel detect_threat(int x, int y, int player)
int count_threats_in_direction(int x, int y, int dx, int dy, int player) int count_threats_in_direction(int x, int y, int dx, int dy, int player)
{ {
int threats = 0; int threats = 0;
// 向前搜索 // 向前搜索
for (int i = 1; i < 5; i++) for (int i = 1; i < 5; i++)
{ {
int nx = x + i * dx; int nx = x + i * dx;
int ny = y + i * dy; int ny = y + i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE)
{ {
break; break;
} }
if (board[nx][ny] == player) if (board[nx][ny] == player)
{ {
threats++; threats++;
@@ -563,18 +572,18 @@ int count_threats_in_direction(int x, int y, int dx, int dy, int player)
break; break;
} }
} }
// 向后搜索 // 向后搜索
for (int i = 1; i < 5; i++) for (int i = 1; i < 5; i++)
{ {
int nx = x - i * dx; int nx = x - i * dx;
int ny = y - i * dy; int ny = y - i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE)
{ {
break; break;
} }
if (board[nx][ny] == player) if (board[nx][ny] == player)
{ {
threats++; threats++;
@@ -584,6 +593,6 @@ int count_threats_in_direction(int x, int y, int dx, int dy, int player)
break; break;
} }
} }
return threats; return threats;
} }
+62 -139
View File
@@ -1,3 +1,9 @@
/**
* @file config.c
* @brief
* @note 便
*/
#include "config.h" #include "config.h"
#include "ui.h" #include "ui.h"
#include "game_mode.h" #include "game_mode.h"
@@ -18,13 +24,13 @@ void load_game_config()
printf("配置文件不存在,使用默认配置\n"); printf("配置文件不存在,使用默认配置\n");
return; return;
} }
char line[256]; char line[256];
while (fgets(line, sizeof(line), file)) while (fgets(line, sizeof(line), file))
{ {
// 去除换行符 // 去除换行符
line[strcspn(line, "\n")] = 0; line[strcspn(line, "\n")] = 0;
// 解析配置项 // 解析配置项
if (strncmp(line, "BOARD_SIZE=", 11) == 0) if (strncmp(line, "BOARD_SIZE=", 11) == 0)
{ {
@@ -72,7 +78,7 @@ void load_game_config()
} }
} }
} }
fclose(file); fclose(file);
printf("配置加载完成\n"); printf("配置加载完成\n");
} }
@@ -88,7 +94,7 @@ void save_game_config()
printf("无法保存配置文件\n"); printf("无法保存配置文件\n");
return; return;
} }
fprintf(file, "# 五子棋游戏配置文件\n"); fprintf(file, "# 五子棋游戏配置文件\n");
fprintf(file, "# 棋盘大小 (范围: %d-%d)\n", MIN_BOARD_SIZE, MAX_BOARD_SIZE); fprintf(file, "# 棋盘大小 (范围: %d-%d)\n", MIN_BOARD_SIZE, MAX_BOARD_SIZE);
fprintf(file, "BOARD_SIZE=%d\n", BOARD_SIZE); fprintf(file, "BOARD_SIZE=%d\n", BOARD_SIZE);
@@ -102,7 +108,7 @@ void save_game_config()
fprintf(file, "NETWORK_PORT=%d\n", network_port); fprintf(file, "NETWORK_PORT=%d\n", network_port);
fprintf(file, "\n# 网络超时时间 (毫秒)\n"); fprintf(file, "\n# 网络超时时间 (毫秒)\n");
fprintf(file, "NETWORK_TIMEOUT=%d\n", network_timeout); fprintf(file, "NETWORK_TIMEOUT=%d\n", network_timeout);
fclose(file); fclose(file);
printf("配置保存完成\n"); printf("配置保存完成\n");
} }
@@ -118,7 +124,7 @@ void reset_to_default_config()
time_limit = DEFAULT_TIME_LIMIT; time_limit = DEFAULT_TIME_LIMIT;
network_port = DEFAULT_NETWORK_PORT; network_port = DEFAULT_NETWORK_PORT;
network_timeout = NETWORK_TIMEOUT_MS; network_timeout = NETWORK_TIMEOUT_MS;
printf("已重置为默认配置\n"); printf("已重置为默认配置\n");
} }
@@ -146,27 +152,10 @@ void display_current_config()
void config_board_size() void config_board_size()
{ {
printf("\n当前棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE); printf("\n当前棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
printf("请输入新的棋盘大小 (%d-%d): ", MIN_BOARD_SIZE, MAX_BOARD_SIZE);
int new_size = get_integer_input("请输入新的棋盘大小: ", MIN_BOARD_SIZE, MAX_BOARD_SIZE);
int new_size; BOARD_SIZE = new_size;
if (scanf("%d", &new_size) == 1) printf("棋盘大小已设置为: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
{
if (new_size >= MIN_BOARD_SIZE && new_size <= MAX_BOARD_SIZE)
{
BOARD_SIZE = new_size;
printf("棋盘大小已设置为: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
}
else
{
printf("无效的棋盘大小!\n");
}
}
else
{
printf("输入格式错误!\n");
// 清除输入缓冲区
while (getchar() != '\n');
}
} }
/** /**
@@ -175,19 +164,10 @@ void config_board_size()
void config_forbidden_moves() void config_forbidden_moves()
{ {
printf("\n当前禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭"); printf("\n当前禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭");
printf("是否启用禁手规则?(1=开启, 0=关闭): ");
int choice = get_integer_input("是否启用禁手规则?(1=开启, 0=关闭): ", 0, 1);
int choice; use_forbidden_moves = (choice != 0);
if (scanf("%d", &choice) == 1) printf("禁手规则已%s\n", use_forbidden_moves ? "开启" : "关闭");
{
use_forbidden_moves = (choice != 0);
printf("禁手规则已%s\n", use_forbidden_moves ? "开启" : "关闭");
}
else
{
printf("输入格式错误!\n");
while (getchar() != '\n');
}
} }
/** /**
@@ -196,36 +176,18 @@ void config_forbidden_moves()
void config_timer() void config_timer()
{ {
printf("\n当前计时器: %s\n", use_timer ? "开启" : "关闭"); printf("\n当前计时器: %s\n", use_timer ? "开启" : "关闭");
printf("是否启用计时器?(1=开启, 0=关闭): ");
int choice = get_integer_input("是否启用计时器?(1=开启, 0=关闭): ", 0, 1);
int choice; use_timer = choice;
if (scanf("%d", &choice) == 1) if (use_timer)
{ {
use_timer = choice; int new_limit = get_integer_input("请输入时间限制(分钟): ", 1, 999);
if (use_timer) time_limit = new_limit * 60; // 转换为秒数存储
{ printf("计时器已开启,时间限制: %d 分钟\n", time_limit / 60);
printf("请输入时间限制(分钟): ");
int new_limit;
if (scanf("%d", &new_limit) == 1 && new_limit > 0)
{
time_limit = new_limit * 60; // 转换为秒数存储
printf("计时器已开启,时间限制: %d 分钟\n", time_limit / 60);
}
else
{
printf("无效的时间限制!\n");
while (getchar() != '\n');
}
}
else
{
printf("计时器已关闭\n");
}
} }
else else
{ {
printf("输入格式错误!\n"); printf("计时器已关闭\n");
while (getchar() != '\n');
} }
} }
@@ -237,46 +199,14 @@ void config_network()
printf("\n===== 网络配置 =====\n"); printf("\n===== 网络配置 =====\n");
printf("当前网络端口: %d\n", network_port); printf("当前网络端口: %d\n", network_port);
printf("当前网络超时: %d 毫秒\n", network_timeout); printf("当前网络超时: %d 毫秒\n", network_timeout);
printf("\n请输入新的网络端口 (%d-%d): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT); int new_port = get_integer_input("请输入新的网络端口: ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
int new_port; network_port = new_port;
if (scanf("%d", &new_port) == 1) printf("网络端口已设置为: %d\n", network_port);
{
if (new_port >= MIN_NETWORK_PORT && new_port <= MAX_NETWORK_PORT) int new_timeout = get_integer_input("请输入网络超时时间(毫秒, 建议1000-10000): ", 1000, 60000);
{ network_timeout = new_timeout;
network_port = new_port; printf("网络超时已设置为: %d 毫秒\n", network_timeout);
printf("网络端口已设置为: %d\n", network_port);
}
else
{
printf("无效的端口号!端口范围: %d-%d\n", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
}
}
else
{
printf("输入格式错误!\n");
while (getchar() != '\n');
}
printf("\n请输入网络超时时间(毫秒, 建议1000-10000): ");
int new_timeout;
if (scanf("%d", &new_timeout) == 1)
{
if (new_timeout > 0)
{
network_timeout = new_timeout;
printf("网络超时已设置为: %d 毫秒\n", network_timeout);
}
else
{
printf("无效的超时时间!\n");
}
}
else
{
printf("输入格式错误!\n");
while (getchar() != '\n');
}
} }
/** /**
@@ -285,47 +215,40 @@ void config_network()
void config_management_menu() void config_management_menu()
{ {
int choice; int choice;
while (1) while (1)
{ {
clear_screen(); clear_screen();
display_settings_menu(); display_settings_menu();
display_current_config(); display_current_config();
printf("请选择操作: "); choice = get_integer_input("请选择操作(0-5): ", 0, 5);
if (scanf("%d", &choice) != 1)
{
printf("输入格式错误!\n");
while (getchar() != '\n');
pause_for_input("按任意键继续...");
continue;
}
switch (choice) switch (choice)
{ {
case 1: case 1:
config_board_size(); config_board_size();
break; break;
case 2: case 2:
config_forbidden_moves(); config_forbidden_moves();
break; break;
case 3: case 3:
config_timer(); config_timer();
break; break;
case 4: case 4:
config_network(); config_network();
break; break;
case 5: case 5:
printf("AI难度设置功能开发中...\n"); printf("AI难度设置功能开发中...\n");
break; break;
case 6: case 0:
save_game_config(); save_game_config();
return; return;
default: default:
printf("无效的选择!\n"); printf("无效的选择!\n");
break; break;
} }
pause_for_input("按任意键继续..."); pause_for_input("按任意键继续...");
} }
} }
+126 -71
View File
@@ -1,3 +1,13 @@
/**
* @file game_mode.c
* @brief
* @note
* 1. AI对战模式
* 2.
* 3.
* 4.
*/
#include "game_mode.h" #include "game_mode.h"
#include "init_board.h" #include "init_board.h"
#include "gobang.h" #include "gobang.h"
@@ -6,6 +16,7 @@
#include "config.h" #include "config.h"
#include "network.h" #include "network.h"
#include "ui.h" #include "ui.h"
#include "gui.h"
#include "globals.h" #include "globals.h"
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
@@ -192,16 +203,16 @@ bool parse_network_player_input(int *x, int *y)
{ {
int steps_to_undo; int steps_to_undo;
steps_to_undo = get_integer_input("请输入要悔棋的步数(双方各退步数相同): ", 1, step_count / 2); steps_to_undo = get_integer_input("请输入要悔棋的步数(双方各退步数相同): ", 1, step_count / 2);
printf("发送悔棋请求给对方...\n"); printf("发送悔棋请求给对方...\n");
if (send_undo_request(steps_to_undo)) if (send_undo_request(steps_to_undo))
{ {
printf("悔棋请求已发送,等待对方回应...\n"); printf("悔棋请求已发送,等待对方回应...\n");
// 等待对方回应 // 等待对方回应
NetworkMessage msg; NetworkMessage msg;
time_t start_time = time(NULL); time_t start_time = time(NULL);
while (difftime(time(NULL), start_time) < 30) // 30秒超时 while (difftime(time(NULL), start_time) < 30) // 30秒超时
{ {
if (receive_network_message(&msg, 1000)) if (receive_network_message(&msg, 1000))
@@ -229,14 +240,14 @@ bool parse_network_player_input(int *x, int *y)
} }
} }
} }
if (!is_network_connected()) if (!is_network_connected())
{ {
printf("网络连接断开\n"); printf("网络连接断开\n");
return 0; return 0;
} }
} }
printf("悔棋请求超时,对方未回应。\n"); printf("悔棋请求超时,对方未回应。\n");
} }
else else
@@ -349,18 +360,18 @@ void run_ai_game()
scores_calculated = 0; scores_calculated = 0;
// AI对战模式 // AI对战模式
int AI_DEPTH = 3; int AI_DEPTH = DEFAULT_AI_DEPTH;
AI_DEPTH = get_integer_input("请选择AI难度(1~5), 数字越大越强,注意数字越大AI思考时间越长!):", 1, 5); AI_DEPTH = get_integer_input("请选择AI难度(1~5), 数字越大越强,注意数字越大AI思考时间越长!):", 1, 5);
/** /**
* @brief AI的防守系数 * @brief AI的防守系数
* @note 1~1.2 * @note 1~1.5
* 2~1.3 * 2~1.6
* 3~1.4 * 3~1.7
* 4~1.5 * 4~1.8
* 5~1.6 * 5~1.9
*/ */
defense_coefficient = 1.2 + (AI_DEPTH - 1) * 0.1; defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT + (AI_DEPTH - 1) * 0.1;
empty_board(); empty_board();
int current_player = determine_first_player(PLAYER, AI); int current_player = determine_first_player(PLAYER, AI);
@@ -558,7 +569,7 @@ void run_network_game()
{ {
// 重置评分计算标志 // 重置评分计算标志
scores_calculated = 0; scores_calculated = 0;
// 初始化网络模块 // 初始化网络模块
if (!init_network()) if (!init_network())
{ {
@@ -566,29 +577,30 @@ void run_network_game()
pause_for_input("按任意键返回主菜单..."); pause_for_input("按任意键返回主菜单...");
return; return;
} }
printf("=== 网络对战模式 ===\n"); printf("=== 网络对战模式 ===\n");
printf("1. 创建房间(作为主机)\n"); printf("1. 创建房间(作为主机)\n");
printf("2. 加入房间(连接到主机)\n"); printf("2. 加入房间(连接到主机)\n");
int choice = get_integer_input("请选择模式(1-2): ", 1, 2); int choice = get_integer_input("请选择模式(1-2): ", 1, 2);
bool connection_success = false; bool connection_success = false;
if (choice == 1) if (choice == 1)
{ {
// 服务器模式 // 服务器模式
int port = get_integer_input("请输入监听端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT); int port = get_integer_input("请输入监听端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
if (port == 0) port = network_port; if (port == 0)
port = network_port;
printf("\n正在创建房间...\n"); printf("\n正在创建房间...\n");
connection_success = create_server(port); connection_success = create_server(port);
} }
else else
{ {
// 客户端模式 // 客户端模式
char ip[MAX_IP_LENGTH]; char ip[MAX_IP_LENGTH];
// 循环直到输入有效的IP地址或用户选择退出 // 循环直到输入有效的IP地址或用户选择退出
while (1) while (1)
{ {
@@ -597,10 +609,11 @@ void run_network_game()
{ {
printf("输入错误,请重新输入。\n"); printf("输入错误,请重新输入。\n");
// 清除输入缓冲区 // 清除输入缓冲区
while (getchar() != '\n'); while (getchar() != '\n')
;
continue; continue;
} }
// 检查是否要退出 // 检查是否要退出
if (strcmp(ip, "exit") == 0 || strcmp(ip, "EXIT") == 0) if (strcmp(ip, "exit") == 0 || strcmp(ip, "EXIT") == 0)
{ {
@@ -609,14 +622,14 @@ void run_network_game()
pause_for_input("按任意键返回主菜单..."); pause_for_input("按任意键返回主菜单...");
return; return;
} }
// 简单的IP地址格式验证 // 简单的IP地址格式验证
if (strlen(ip) < 7 || strlen(ip) > 15) if (strlen(ip) < 7 || strlen(ip) > 15)
{ {
printf("IP地址格式错误!请输入有效的IP地址(如:192.168.1.100\n"); printf("IP地址格式错误!请输入有效的IP地址(如:192.168.1.100\n");
continue; continue;
} }
// 检查IP地址是否包含有效字符 // 检查IP地址是否包含有效字符
bool valid_ip = true; bool valid_ip = true;
for (int i = 0; i < strlen(ip); i++) for (int i = 0; i < strlen(ip); i++)
@@ -627,26 +640,27 @@ void run_network_game()
break; break;
} }
} }
if (!valid_ip) if (!valid_ip)
{ {
printf("IP地址格式错误!只能包含数字和点号。\n"); printf("IP地址格式错误!只能包含数字和点号。\n");
continue; continue;
} }
// 检查点号数量 // 检查点号数量
int dot_count = 0; int dot_count = 0;
for (int i = 0; i < strlen(ip); i++) for (int i = 0; i < strlen(ip); i++)
{ {
if (ip[i] == '.') dot_count++; if (ip[i] == '.')
dot_count++;
} }
if (dot_count != 3) if (dot_count != 3)
{ {
printf("IP地址格式错误!应包含3个点号(如:192.168.1.100\n"); printf("IP地址格式错误!应包含3个点号(如:192.168.1.100\n");
continue; continue;
} }
printf("输入的IP地址: %s\n", ip); printf("输入的IP地址: %s\n", ip);
int confirm = get_integer_input("确认连接到此IP?(1:是/0:否,重新输入): ", 0, 1); int confirm = get_integer_input("确认连接到此IP?(1:是/0:否,重新输入): ", 0, 1);
if (confirm) if (confirm)
@@ -655,14 +669,15 @@ void run_network_game()
} }
// 如果选择否,继续循环重新输入 // 如果选择否,继续循环重新输入
} }
int port = get_integer_input("请输入服务器端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT); int port = get_integer_input("请输入服务器端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
if (port == 0) port = network_port; if (port == 0)
port = network_port;
printf("\n正在连接到服务器 %s:%d...\n", ip, port); printf("\n正在连接到服务器 %s:%d...\n", ip, port);
connection_success = connect_to_server(ip, port); connection_success = connect_to_server(ip, port);
} }
if (!connection_success) if (!connection_success)
{ {
printf("网络连接失败!\n"); printf("网络连接失败!\n");
@@ -670,16 +685,16 @@ void run_network_game()
pause_for_input("按任意键返回主菜单..."); pause_for_input("按任意键返回主菜单...");
return; return;
} }
printf("\n网络连接成功!游戏即将开始...\n"); printf("\n网络连接成功!游戏即将开始...\n");
printf("你是玩家%d%s先手\n", printf("你是玩家%d%s先手\n",
network_state.local_player_id, network_state.local_player_id,
network_state.local_player_id == PLAYER1 ? "" : "对方"); network_state.local_player_id == PLAYER1 ? "" : "对方");
// 开始网络游戏 // 开始网络游戏
empty_board(); empty_board();
print_board(); print_board();
if (network_game_loop()) if (network_game_loop())
{ {
printf("===== 游戏结束 =====\n"); printf("===== 游戏结束 =====\n");
@@ -690,12 +705,57 @@ void run_network_game()
{ {
printf("游戏因网络错误而结束\n"); printf("游戏因网络错误而结束\n");
} }
// 清理网络连接 // 清理网络连接
disconnect_network(); disconnect_network();
pause_for_input("按任意键返回主菜单..."); pause_for_input("按任意键返回主菜单...");
} }
/**
* @brief
*/
void show_game_rules()
{
clear_screen();
display_game_rules();
pause_for_input("\n按任意键返回主菜单...");
}
/**
* @brief
*/
void show_about_game()
{
clear_screen();
display_about();
pause_for_input("\n按任意键返回主菜单...");
}
/**
* @brief
*/
void run_gui_mode()
{
if (init_gui() == 0)
{
printf("启动图形化界面...\n");
printf("图形化界面已启动,窗口应该可见\n");
printf("如果看不到窗口,请检查任务栏或按Alt+Tab切换\n");
while (gui_running && handle_events())
{
render_game();
SDL_Delay(16); // 约60FPS
}
printf("退出图形化界面\n");
cleanup_gui();
}
else
{
printf("图形化界面启动失败!请检查SDL3库是否正确安装。\n");
pause_for_input("按任意键返回主菜单...");
}
}
/** /**
* @brief * @brief
*/ */
@@ -706,17 +766,16 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
// 本地玩家回合 // 本地玩家回合
int x, y; int x, y;
time_t start_time, end_time; time_t start_time, end_time;
if (use_timer) if (use_timer)
{ {
time(&start_time); time(&start_time);
} }
while (1) while (1)
{ {
printf("\n轮到你了,请输入落子坐标(行 列,1~%d),或输入R/r悔棋,S/s认输: ", BOARD_SIZE); printf("\n轮到你了,请输入落子坐标(行 列,1~%d),或输入R/r悔棋,S/s认输: ", BOARD_SIZE);
bool input_received = false; bool input_received = false;
while (!input_received) while (!input_received)
{ {
@@ -727,10 +786,10 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
{ {
printf("\n你超时了,对方获胜!\n"); printf("\n你超时了,对方获胜!\n");
send_surrender(); // 发送认输消息 send_surrender(); // 发送认输消息
return 0; // 游戏结束 return 0; // 游戏结束
} }
} }
int parse_result = parse_network_player_input(&x, &y); int parse_result = parse_network_player_input(&x, &y);
if (parse_result == 1) // 有效坐标输入 if (parse_result == 1) // 有效坐标输入
{ {
@@ -752,9 +811,10 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
continue; continue;
} }
} }
x--; y--; // 转换为0-based坐标 x--;
y--; // 转换为0-based坐标
if (player_move(x, y, current_player)) if (player_move(x, y, current_player))
{ {
break; // 成功落子,跳出循环 break; // 成功落子,跳出循环
@@ -765,35 +825,34 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
// 继续循环,重新输入坐标 // 继续循环,重新输入坐标
} }
} }
// 发送落子消息 // 发送落子消息
if (!send_move(x, y, current_player)) if (!send_move(x, y, current_player))
{ {
printf("发送落子消息失败!\n"); printf("发送落子消息失败!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
print_board(); print_board();
if (check_win(x, y, current_player)) if (check_win(x, y, current_player))
{ {
printf("\n你获胜了!\n"); printf("\n你获胜了!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
} }
else else
{ {
// 等待对方落子 // 等待对方落子
printf("\n等待对方落子...\n"); printf("\n等待对方落子...\n");
NetworkMessage msg; NetworkMessage msg;
time_t start_time = time(NULL); time_t start_time = time(NULL);
while (1) while (1)
{ {
if (receive_network_message(&msg, 1000)) if (receive_network_message(&msg, 1000))
{ {
// 1秒超时 // 1秒超时
if (msg.type == MSG_MOVE && msg.player_id == current_player) if (msg.type == MSG_MOVE && msg.player_id == current_player)
{ {
@@ -803,41 +862,37 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
printf("收到无效的落子坐标!\n"); printf("收到无效的落子坐标!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
printf("对方落子: (%d, %d)\n", msg.x + 1, msg.y + 1); printf("对方落子: (%d, %d)\n", msg.x + 1, msg.y + 1);
print_board(); print_board();
if (check_win(msg.x, msg.y, current_player)) if (check_win(msg.x, msg.y, current_player))
{ {
printf("\n对方获胜!\n"); printf("\n对方获胜!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
break; break;
}
}
else if (msg.type == MSG_SURRENDER) else if (msg.type == MSG_SURRENDER)
{ {
printf("\n对方认输,你获胜了!\n"); printf("\n对方认输,你获胜了!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
else if (msg.type == MSG_DISCONNECT) else if (msg.type == MSG_DISCONNECT)
{ {
printf("\n对方已断开连接\n"); printf("\n对方已断开连接\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
else if (msg.type == MSG_CHAT) else if (msg.type == MSG_CHAT)
{ {
printf("[对方]: %s\n", msg.message); printf("[对方]: %s\n", msg.message);
} }
else if (msg.type == MSG_UNDO_REQUEST) else if (msg.type == MSG_UNDO_REQUEST)
{ {
int steps = msg.x; int steps = msg.x;
printf("\n对方请求悔棋 %d 步,是否同意?(1:同意/0:拒绝): ", steps); printf("\n对方请求悔棋 %d 步,是否同意?(1:同意/0:拒绝): ", steps);
int response = get_integer_input("", 0, 1); int response = get_integer_input("", 0, 1);
if (response && return_move(steps * 2)) if (response && return_move(steps * 2))
{ {
printf("同意悔棋,双方各退 %d 步\n", steps); printf("同意悔棋,双方各退 %d 步\n", steps);
@@ -854,14 +909,14 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
} }
} }
} }
// 检查超时 // 检查超时
if (use_timer && difftime(time(NULL), start_time) > time_limit) if (use_timer && difftime(time(NULL), start_time) > time_limit)
{ {
printf("\n对方超时,你获胜!\n"); printf("\n对方超时,你获胜!\n");
return 0; // 游戏结束 return 0; // 游戏结束
} }
// 检查网络连接 // 检查网络连接
if (!is_network_connected()) if (!is_network_connected())
{ {
@@ -870,7 +925,7 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
} }
} }
} }
return 1; // 正常回合完成 return 1; // 正常回合完成
} }
@@ -880,11 +935,11 @@ bool handle_network_player_turn(int current_player, bool is_local_turn)
bool network_game_loop() bool network_game_loop()
{ {
int current_player = PLAYER1; // 总是从玩家1开始 int current_player = PLAYER1; // 总是从玩家1开始
while (1) while (1)
{ {
bool is_local_turn = (current_player == network_state.local_player_id); bool is_local_turn = (current_player == network_state.local_player_id);
int turn_result = handle_network_player_turn(current_player, is_local_turn); int turn_result = handle_network_player_turn(current_player, is_local_turn);
if (turn_result == 0) // 游戏结束 if (turn_result == 0) // 游戏结束
{ {
@@ -894,17 +949,17 @@ bool network_game_loop()
{ {
continue; // 不切换玩家,重新开始当前回合 continue; // 不切换玩家,重新开始当前回合
} }
// 检查平局 // 检查平局
if (step_count == BOARD_SIZE * BOARD_SIZE) if (step_count == BOARD_SIZE * BOARD_SIZE)
{ {
printf("\n平局!\n"); printf("\n平局!\n");
return true; return true;
} }
// 切换玩家 // 切换玩家
current_player = (current_player == PLAYER1) ? PLAYER2 : PLAYER1; current_player = (current_player == PLAYER1) ? PLAYER2 : PLAYER1;
// 检查网络连接 // 检查网络连接
if (!is_network_connected()) if (!is_network_connected())
{ {
@@ -912,6 +967,6 @@ bool network_game_loop()
return false; return false;
} }
} }
return true; return true;
} }
+44
View File
@@ -0,0 +1,44 @@
/**
* @file globals.c
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @brief
* @note
*/
#include "globals.h"
#include "config.h"
#include <SDL3/SDL.h>
// ==================== 游戏核心变量定义 ====================
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 = DEFAULT_USE_FORBIDDEN_MOVES; // 是否启用禁手规则
int use_timer = DEFAULT_USE_TIMER; // 是否启用计时器
int time_limit = DEFAULT_TIME_LIMIT; // 每回合的时间限制(秒)
int network_port = DEFAULT_NETWORK_PORT; // 网络端口
int network_timeout = NETWORK_TIMEOUT_MS; // 网络超时时间
// ==================== AI相关变量定义 ====================
double defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT; // 防守系数
// ==================== 网络相关变量定义 ====================
NetworkGameState network_state = {0}; // 网络游戏状态
// ==================== GUI相关变量定义 ====================
SDL_Window *window = NULL; // SDL窗口指针
SDL_Renderer *renderer = NULL; // SDL渲染器指针
int gui_running = 1; // GUI运行状态标志
int current_player_gui = PLAYER; // GUI当前玩家
int game_over = 0; // 游戏结束标志
char status_message[256] = "五子棋游戏 - 黑子先行"; // 状态消息
// ==================== 记录相关变量定义 ====================
int player1_final_score = 0; // 玩家1最终得分
int player2_final_score = 0; // 玩家2最终得分
int scores_calculated = 0; // 评分计算标志
char winner_info[50] = "平局或未完成"; // 存储胜负信息
+10 -3
View File
@@ -1,3 +1,10 @@
/**
* @file gobang.c
* @brief
* @note
* AI决策等功能
*/
#include "game_mode.h" #include "game_mode.h"
#include "init_board.h" #include "init_board.h"
#include "gobang.h" #include "gobang.h"
@@ -258,12 +265,12 @@ int calculate_step_score(int x, int y, int player)
break; break;
} }
} }
// 位置奖励:越靠近中心分数越高 // 位置奖励:越靠近中心分数越高
int center_x = BOARD_SIZE / 2; int center_x = BOARD_SIZE / 2;
int center_y = BOARD_SIZE / 2; int center_y = BOARD_SIZE / 2;
int distance = abs(x - center_x) + abs(y - center_y); // 曼哈顿距离 int distance = abs(x - center_x) + abs(y - center_y); // 曼哈顿距离
int position_bonus = POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 距离中心越近奖励越高 int position_bonus = POSITION_BONUS_FACTOR * (BOARD_SIZE - distance); // 距离中心越近奖励越高
return step_score + position_bonus; return step_score + position_bonus;
} }
+441
View File
@@ -0,0 +1,441 @@
/**
* @file gui.c
* @brief
* @note 使SDL3库实现五子棋的图形化界面
* @author
* @date 2025-01-15
*/
#include "gui.h"
#include "ui.h"
#include "globals.h"
#include "game_mode.h"
#include "init_board.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/**
* @brief GUI
* @details SDL3图形库和游戏界面组件
* - SDL视频子系统
* -
* - SDL渲染器
* -
* @return 0-1
* @note "五子棋游戏 - SDL3版本"
* WINDOW_WIDTH和WINDOW_HEIGHT定义
*
*/
int init_gui()
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL初始化失败: %s\n", SDL_GetError());
return -1;
}
window = SDL_CreateWindow(
"五子棋游戏 - SDL3版本",
WINDOW_WIDTH, WINDOW_HEIGHT,
SDL_WINDOW_RESIZABLE);
// 设置窗口位置到屏幕中央
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
if (!window)
{
printf("窗口创建失败: %s\n", SDL_GetError());
SDL_Quit();
return -1;
}
// 显示窗口
SDL_ShowWindow(window);
renderer = SDL_CreateRenderer(window, NULL);
if (!renderer)
{
printf("渲染器创建失败: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return -1;
}
// 初始化游戏状态
// 初始化棋盘
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
{
board[i][j] = EMPTY;
}
}
current_player_gui = PLAYER;
game_over = 0;
printf("图形化界面初始化成功!\n");
printf("使用鼠标点击棋盘进行落子\n");
printf("按ESC键退出游戏\n");
return 0;
}
/**
* @brief GUI资源
* @details SDL相关资源
* - SDL渲染器
* - SDL窗口
* - 退SDL子系统
* @note
* NULL防止重复释放
* 退
*/
void cleanup_gui()
{
if (renderer)
{
SDL_DestroyRenderer(renderer);
renderer = NULL;
}
if (window)
{
SDL_DestroyWindow(window);
window = NULL;
}
SDL_Quit();
printf("图形化界面已关闭\n");
}
/**
* @brief
* @details
* -
* -
* -
* - UI界面元素
* -
* @note 使SDL_RenderPresent显示最终结果
* GUI_COLOR_BACKGROUND定义
*
*/
void render_game()
{
// 清空屏幕 - 设置背景色
SDL_Color bg_color = GUI_COLOR_BACKGROUND;
SDL_SetRenderDrawColor(renderer, bg_color.r, bg_color.g, bg_color.b, bg_color.a);
SDL_RenderClear(renderer);
// 绘制棋盘
draw_board();
// 绘制棋子
draw_stones();
// 绘制UI元素
draw_ui_elements();
// 显示渲染结果
SDL_RenderPresent(renderer);
}
/**
* @brief
* @details SDL事件并执行相应操作
* - SDL_EVENT_QUIT
* - SDL_EVENT_KEY_DOWNESC退出
* - SDL_EVENT_MOUSE_BUTTON_DOWN
* @return 1退0
* @note
*
*
*/
int handle_events()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_EVENT_QUIT:
gui_running = 0;
return 0;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE)
{
gui_running = 0;
return 0;
}
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (event.button.button == SDL_BUTTON_LEFT && !game_over)
{
int board_x, board_y;
if (screen_to_board(event.button.x, event.button.y, &board_x, &board_y))
{
if (have_space(board_x, board_y))
{
// 执行落子操作
if (player_move(board_x, board_y, current_player_gui))
{
// 检查是否获胜
if (check_win(board_x, board_y, current_player_gui))
{
game_over = 1;
if (current_player_gui == PLAYER)
{
sprintf(status_message, "游戏结束 - 黑子获胜!");
}
else
{
sprintf(status_message, "游戏结束 - 白子获胜!");
}
}
else
{
// 切换玩家
current_player_gui = (current_player_gui == PLAYER) ? AI : PLAYER;
if (current_player_gui == PLAYER)
{
sprintf(status_message, "轮到黑子下棋");
}
else
{
sprintf(status_message, "轮到白子下棋");
}
}
}
}
else
{
sprintf(status_message, "该位置已有棋子,请选择其他位置");
}
}
}
break;
}
}
return 1;
}
/**
* @brief
* @details 15x15的五子棋棋盘
* - 线
* -
* -
* @note 使SDL3渲染器绘制线条和填充矩形
* 线GUI_COLOR_BOARD_LINE定义
*
*/
void draw_board()
{
SDL_Color line_color = GUI_COLOR_BOARD_LINE;
SDL_SetRenderDrawColor(renderer, line_color.r, line_color.g, line_color.b, line_color.a);
// 绘制横线
for (int i = 0; i < BOARD_SIZE; i++)
{
int y = BOARD_OFFSET_Y + i * CELL_SIZE;
SDL_RenderLine(renderer,
BOARD_OFFSET_X, y,
BOARD_OFFSET_X + (BOARD_SIZE - 1) * CELL_SIZE, y);
}
// 绘制竖线
for (int j = 0; j < BOARD_SIZE; j++)
{
int x = BOARD_OFFSET_X + j * CELL_SIZE;
SDL_RenderLine(renderer,
x, BOARD_OFFSET_Y,
x, BOARD_OFFSET_Y + (BOARD_SIZE - 1) * CELL_SIZE);
}
// 绘制天元点和星位
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
int center = BOARD_SIZE / 2;
// 天元点
int center_x = BOARD_OFFSET_X + center * CELL_SIZE;
int center_y = BOARD_OFFSET_Y + center * CELL_SIZE;
SDL_FRect center_rect = {center_x - 2, center_y - 2, 4, 4};
SDL_RenderFillRect(renderer, &center_rect);
// 四个星位
int star_offset = 3;
int positions[][2] = {
{center - star_offset, center - star_offset},
{center + star_offset, center - star_offset},
{center - star_offset, center + star_offset},
{center + star_offset, center + star_offset}};
for (int i = 0; i < 4; i++)
{
int x = BOARD_OFFSET_X + positions[i][1] * CELL_SIZE;
int y = BOARD_OFFSET_Y + positions[i][0] * CELL_SIZE;
SDL_FRect star_rect = {x - 1, y - 1, 2, 2};
SDL_RenderFillRect(renderer, &star_rect);
}
}
/**
* @brief
* @details
* - 使GUI_COLOR_BLACK_STONE颜色
* - 使GUI_COLOR_WHITE_STONE颜色
* - 使GUI_COLOR_STONE_BORDER颜色
* @note STONE_RADIUS定义
* draw_circle函数实现圆形绘制
* CELL_SIZE计算屏幕坐标
*/
void draw_stones()
{
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
{
if (board[i][j] != EMPTY)
{
int x = BOARD_OFFSET_X + j * CELL_SIZE;
int y = BOARD_OFFSET_Y + i * CELL_SIZE;
// 设置棋子颜色
SDL_Color stone_color, border_color;
if (board[i][j] == PLAYER || board[i][j] == PLAYER1)
{
stone_color = (SDL_Color)GUI_COLOR_BLACK_STONE;
}
else
{
stone_color = (SDL_Color)GUI_COLOR_WHITE_STONE;
}
border_color = (SDL_Color)GUI_COLOR_STONE_BORDER;
// 绘制圆形棋子
draw_circle(x, y, STONE_RADIUS, stone_color);
draw_circle(x, y, STONE_RADIUS, border_color);
// 重新绘制内部
draw_circle(x, y, STONE_RADIUS - 1, stone_color);
}
}
}
}
/**
* @brief
* @param center_x X坐标
* @param center_y Y坐标
* @param radius
* @param color
* @details 使
* -
* -
* -
* @note
*
* SDL3没有内置圆形绘制函数
*/
void draw_circle(int center_x, int center_y, int radius, SDL_Color color)
{
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
for (int w = 0; w < radius * 2; w++)
{
for (int h = 0; h < radius * 2; h++)
{
int dx = radius - w;
int dy = radius - h;
if ((dx * dx + dy * dy) <= (radius * radius))
{
SDL_RenderPoint(renderer, center_x + dx, center_y + dy);
}
}
}
}
/**
* @brief UI元素
*/
void draw_ui_elements()
{
// 绘制状态信息区域背景
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
SDL_FRect info_rect = {BOARD_OFFSET_X + BOARD_SIZE * CELL_SIZE + 20, BOARD_OFFSET_Y, 200, 100};
SDL_RenderFillRect(renderer, &info_rect);
// 绘制边框
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderRect(renderer, &info_rect);
// 这里可以添加文字渲染,但SDL3需要额外的字体库
// 暂时用简单的图形表示当前玩家
int indicator_x = info_rect.x + 20;
int indicator_y = info_rect.y + 20;
if (!game_over)
{
if (current_player_gui == PLAYER)
{
// 黑子回合
draw_circle(indicator_x, indicator_y, 10, (SDL_Color){0, 0, 0, 255});
}
else
{
// 白子回合
draw_circle(indicator_x, indicator_y, 10, (SDL_Color){255, 255, 255, 255});
// 绘制当前玩家指示器(简单的矩形代替圆形)
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_FRect indicator_rect = {indicator_x - 10, indicator_y - 10, 20, 20};
SDL_RenderFillRect(renderer, &indicator_rect);
}
}
}
/**
* @brief
* @param screen_x X坐标
* @param screen_y Y坐标
* @param board_x X坐标
* @param board_y Y坐标
* @return 10
* @details
* -
* -
* -
* -
* @note 使
* 访
*/
int screen_to_board(int screen_x, int screen_y, int *board_x, int *board_y)
{
int rel_x = screen_x - BOARD_OFFSET_X;
int rel_y = screen_y - BOARD_OFFSET_Y;
*board_x = (rel_x + CELL_SIZE / 2) / CELL_SIZE;
*board_y = (rel_y + CELL_SIZE / 2) / CELL_SIZE;
return (*board_x >= 0 && *board_x < BOARD_SIZE &&
*board_y >= 0 && *board_y < BOARD_SIZE);
}
/**
* @brief
* @param message
* @details
* -
* -
* -
* @note status_message全局变量中
* 1
*
*/
void show_message(const char *message)
{
strncpy(status_message, message, sizeof(status_message) - 1);
status_message[sizeof(status_message) - 1] = '\0';
printf("%s\n", message);
}
+7
View File
@@ -1,3 +1,10 @@
/**
* @file init_board.c
* @brief
* @note
*
*/
#include "init_board.h" #include "init_board.h"
#include "gobang.h" #include "gobang.h"
#include "game_mode.h" #include "game_mode.h"
+98
View File
@@ -0,0 +1,98 @@
/**
* @file main.c
* @brief
* @note
* @brief powershell
*
* !
* gcc -std=c17 -o gobang_console.exe *.c -lws2_32
.\gobang_console.exe
*
* !SDL3
* gcc -std=c17 -o gobang_gui.exe *.c -ID:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\include -LD:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\lib -lSDL3 -lws2_32
copy "D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32\bin\SDL3.dll" .
.\gobang_gui.exe
*
* @note gcc -lws2_32链接Windows网络库
* @note SDL3 D:\settings\SDL\SDL3-3.2.22\x86_64-w64-mingw32
* @brief & "D:\Program Files (x86)\NSIS\makensis.exe" "installer\\installer.nsi"
* @brief & "D:\Program Files (x86)\Inno Setup 6\iscc.exe" installer\\installer.iss
*/
#include "game_mode.h"
#include "ui.h"
#include "config.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#endif
int main(int argc, char *argv[])
{
// 设置控制台编码为UTF-8
#ifdef _WIN32
system("chcp 65001 > nul"); // 设置控制台编码为UTF-8
SetConsoleOutputCP(65001); // 设置控制台输出编码
SetConsoleCP(65001); // 设置控制台输入编码
_mkdir("records");
#endif
// 加载游戏配置
load_game_config();
// 选择模式
while (1)
{
clear_screen();
display_main_menu();
int mode = get_integer_input("请输入模式(0-8): ", 0, 8);
switch (mode)
{
// 1. 人机对战
case 1:
run_ai_game();
break;
// 2. 玩家对战
case 2:
run_pvp_game();
break;
// 3. 网络对战
case 3:
run_network_game();
break;
// 4. 复盘模式
case 4:
run_review_mode();
break;
// 5. 配置管理
case 5:
config_management_menu();
break;
// 6. 游戏规则
case 6:
show_game_rules();
break;
// 7. 关于游戏
case 7:
show_about_game();
break;
// 8. 图形化界面
case 8:
run_gui_mode();
break;
// 0. 退出游戏
case 0:
save_game_config();
printf("感谢使用五子棋游戏!\n");
return 0;
default:
printf("无效的选择!\n");
pause_for_input("按任意键继续...");
break;
}
}
return 0;
}
+59 -61
View File
@@ -2,10 +2,6 @@
* @file network.c * @file network.c
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com) * @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @brief * @brief
* @version 6.0
* @date 2025-07-10
*
* @copyright Copyright (c) 2025
*/ */
#include "network.h" #include "network.h"
@@ -48,11 +44,11 @@ bool init_network()
return false; return false;
} }
#endif #endif
memset(&network_state, 0, sizeof(NetworkGameState)); memset(&network_state, 0, sizeof(NetworkGameState));
network_state.socket = INVALID_SOCKET; network_state.socket = INVALID_SOCKET;
network_state.port = DEFAULT_PORT; network_state.port = DEFAULT_PORT;
return true; return true;
} }
@@ -66,11 +62,11 @@ void cleanup_network()
closesocket(network_state.socket); closesocket(network_state.socket);
network_state.socket = INVALID_SOCKET; network_state.socket = INVALID_SOCKET;
} }
#ifdef _WIN32 #ifdef _WIN32
WSACleanup(); WSACleanup();
#endif #endif
network_state.is_connected = false; network_state.is_connected = false;
} }
@@ -81,7 +77,7 @@ bool create_server(int port)
{ {
struct sockaddr_in server_addr, client_addr; struct sockaddr_in server_addr, client_addr;
int addr_len = sizeof(client_addr); int addr_len = sizeof(client_addr);
// 创建套接字 // 创建套接字
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0); SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == INVALID_SOCKET) if (listen_socket == INVALID_SOCKET)
@@ -89,28 +85,28 @@ bool create_server(int port)
printf("创建套接字失败\n"); printf("创建套接字失败\n");
return false; return false;
} }
// 设置地址重用 // 设置地址重用
int opt = 1; int opt = 1;
#ifdef _WIN32 #ifdef _WIN32
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)); setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
#else #else
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif #endif
// 绑定地址 // 绑定地址
memset(&server_addr, 0, sizeof(server_addr)); memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port); server_addr.sin_port = htons(port);
if (bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) if (bind(listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
{ {
printf("绑定端口失败\n"); printf("绑定端口失败\n");
closesocket(listen_socket); closesocket(listen_socket);
return false; return false;
} }
// 开始监听 // 开始监听
if (listen(listen_socket, 1) == SOCKET_ERROR) if (listen(listen_socket, 1) == SOCKET_ERROR)
{ {
@@ -118,31 +114,31 @@ bool create_server(int port)
closesocket(listen_socket); closesocket(listen_socket);
return false; return false;
} }
char local_ip[MAX_IP_LENGTH]; char local_ip[MAX_IP_LENGTH];
if (get_local_ip(local_ip, sizeof(local_ip))) if (get_local_ip(local_ip, sizeof(local_ip)))
{ {
printf("服务器已启动,等待客户端连接...\n"); printf("服务器已启动,等待客户端连接...\n");
printf("本机IP地址: %s\n", local_ip); printf("本机IP地址: %s\n", local_ip);
printf("监听端口: %d\n", port); printf("监听端口: %d\n", port);
} }
else else
{ {
printf("服务器已启动,监听端口: %d\n", port); printf("服务器已启动,监听端口: %d\n", port);
} }
// 等待客户端连接 // 等待客户端连接
SOCKET client_socket = accept(listen_socket, (struct sockaddr*)&client_addr, &addr_len); SOCKET client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &addr_len);
if (client_socket == INVALID_SOCKET) if (client_socket == INVALID_SOCKET)
{ {
printf("接受连接失败\n"); printf("接受连接失败\n");
closesocket(listen_socket); closesocket(listen_socket);
return false; return false;
} }
// 关闭监听套接字 // 关闭监听套接字
closesocket(listen_socket); closesocket(listen_socket);
// 保存连接信息 // 保存连接信息
network_state.socket = client_socket; network_state.socket = client_socket;
network_state.is_server = true; network_state.is_server = true;
@@ -151,7 +147,7 @@ bool create_server(int port)
network_state.remote_player_id = PLAYER2; network_state.remote_player_id = PLAYER2;
network_state.port = port; network_state.port = port;
strcpy(network_state.remote_ip, inet_ntoa(client_addr.sin_addr)); strcpy(network_state.remote_ip, inet_ntoa(client_addr.sin_addr));
printf("客户端已连接: %s\n", network_state.remote_ip); printf("客户端已连接: %s\n", network_state.remote_ip);
return true; return true;
} }
@@ -159,10 +155,10 @@ bool create_server(int port)
/** /**
* @brief * @brief
*/ */
bool connect_to_server(const char* ip, int port) bool connect_to_server(const char *ip, int port)
{ {
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
// 创建套接字 // 创建套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0); SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == INVALID_SOCKET) if (client_socket == INVALID_SOCKET)
@@ -170,15 +166,15 @@ bool connect_to_server(const char* ip, int port)
printf("创建套接字失败\n"); printf("创建套接字失败\n");
return false; return false;
} }
// 设置服务器地址 // 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr)); memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); server_addr.sin_port = htons(port);
#ifdef _WIN32 #ifdef _WIN32
server_addr.sin_addr.s_addr = inet_addr(ip); server_addr.sin_addr.s_addr = inet_addr(ip);
if (server_addr.sin_addr.s_addr == INADDR_NONE) if (server_addr.sin_addr.s_addr == INADDR_NONE)
{ {
#else #else
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0) if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0)
@@ -188,17 +184,17 @@ bool connect_to_server(const char* ip, int port)
closesocket(client_socket); closesocket(client_socket);
return false; return false;
} }
printf("正在连接到服务器 %s:%d...\n", ip, port); printf("正在连接到服务器 %s:%d...\n", ip, port);
// 连接到服务器 // 连接到服务器
if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
{ {
printf("连接服务器失败\n"); printf("连接服务器失败\n");
closesocket(client_socket); closesocket(client_socket);
return false; return false;
} }
// 保存连接信息 // 保存连接信息
network_state.socket = client_socket; network_state.socket = client_socket;
network_state.is_server = false; network_state.is_server = false;
@@ -207,7 +203,7 @@ bool connect_to_server(const char* ip, int port)
network_state.remote_player_id = PLAYER1; network_state.remote_player_id = PLAYER1;
network_state.port = port; network_state.port = port;
strcpy(network_state.remote_ip, ip); strcpy(network_state.remote_ip, ip);
printf("成功连接到服务器\n"); printf("成功连接到服务器\n");
return true; return true;
} }
@@ -215,33 +211,33 @@ bool connect_to_server(const char* ip, int port)
/** /**
* @brief * @brief
*/ */
bool send_network_message(const NetworkMessage* msg) bool send_network_message(const NetworkMessage *msg)
{ {
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET) if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
{ {
return false; return false;
} }
int bytes_sent = send(network_state.socket, (const char*)msg, sizeof(NetworkMessage), 0); int bytes_sent = send(network_state.socket, (const char *)msg, sizeof(NetworkMessage), 0);
return bytes_sent == sizeof(NetworkMessage); return bytes_sent == sizeof(NetworkMessage);
} }
/** /**
* @brief * @brief
*/ */
bool receive_network_message(NetworkMessage* msg, int timeout_ms) bool receive_network_message(NetworkMessage *msg, int timeout_ms)
{ {
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET) if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
{ {
return false; return false;
} }
// 设置超时 // 设置超时
if (timeout_ms > 0) if (timeout_ms > 0)
{ {
#ifdef _WIN32 #ifdef _WIN32
DWORD timeout = timeout_ms; DWORD timeout = timeout_ms;
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
#else #else
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000; timeout.tv_sec = timeout_ms / 1000;
@@ -249,18 +245,20 @@ bool receive_network_message(NetworkMessage* msg, int timeout_ms)
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#endif #endif
} }
int bytes_received = recv(network_state.socket, (char*)msg, sizeof(NetworkMessage), 0); int bytes_received = recv(network_state.socket, (char *)msg, sizeof(NetworkMessage), 0);
if (bytes_received == sizeof(NetworkMessage)) if (bytes_received == sizeof(NetworkMessage))
{ {
return true; return true;
} else if (bytes_received == 0) }
else if (bytes_received == 0)
{ {
// 连接已关闭 // 连接已关闭
network_state.is_connected = false; network_state.is_connected = false;
printf("对方已断开连接\n"); printf("对方已断开连接\n");
} else if (bytes_received == SOCKET_ERROR) }
else if (bytes_received == SOCKET_ERROR)
{ {
#ifdef _WIN32 #ifdef _WIN32
int error = WSAGetLastError(); int error = WSAGetLastError();
@@ -274,7 +272,7 @@ bool receive_network_message(NetworkMessage* msg, int timeout_ms)
printf("网络接收错误\n"); printf("网络接收错误\n");
} }
} }
return false; return false;
} }
@@ -289,10 +287,10 @@ void disconnect_network()
msg.type = MSG_DISCONNECT; msg.type = MSG_DISCONNECT;
msg.player_id = network_state.local_player_id; msg.player_id = network_state.local_player_id;
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
send_network_message(&msg); send_network_message(&msg);
} }
cleanup_network(); cleanup_network();
} }
@@ -307,18 +305,18 @@ bool is_network_connected()
/** /**
* @brief IP地址 * @brief IP地址
*/ */
bool get_local_ip(char* ip_buffer, int buffer_size) bool get_local_ip(char *ip_buffer, int buffer_size)
{ {
#ifdef _WIN32 #ifdef _WIN32
// Windows实现 // Windows实现
char hostname[256]; char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0) if (gethostname(hostname, sizeof(hostname)) == 0)
{ {
struct hostent* host_entry = gethostbyname(hostname); struct hostent *host_entry = gethostbyname(hostname);
if (host_entry != NULL) if (host_entry != NULL)
{ {
struct in_addr addr; struct in_addr addr;
addr.s_addr = *((unsigned long*)host_entry->h_addr_list[0]); addr.s_addr = *((unsigned long *)host_entry->h_addr_list[0]);
strncpy(ip_buffer, inet_ntoa(addr), buffer_size - 1); strncpy(ip_buffer, inet_ntoa(addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0'; ip_buffer[buffer_size - 1] = '\0';
return true; return true;
@@ -333,11 +331,11 @@ bool get_local_ip(char* ip_buffer, int buffer_size)
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("8.8.8.8"); addr.sin_addr.s_addr = inet_addr("8.8.8.8");
addr.sin_port = htons(80); addr.sin_port = htons(80);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
{ {
socklen_t addr_len = sizeof(addr); socklen_t addr_len = sizeof(addr);
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == 0) if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) == 0)
{ {
strncpy(ip_buffer, inet_ntoa(addr.sin_addr), buffer_size - 1); strncpy(ip_buffer, inet_ntoa(addr.sin_addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0'; ip_buffer[buffer_size - 1] = '\0';
@@ -348,7 +346,7 @@ bool get_local_ip(char* ip_buffer, int buffer_size)
close(sock); close(sock);
} }
#endif #endif
// 默认返回本地回环地址 // 默认返回本地回环地址
strncpy(ip_buffer, "127.0.0.1", buffer_size - 1); strncpy(ip_buffer, "127.0.0.1", buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0'; ip_buffer[buffer_size - 1] = '\0';
@@ -366,21 +364,21 @@ bool send_move(int x, int y, int player_id)
msg.x = x; msg.x = x;
msg.y = y; msg.y = y;
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
return send_network_message(&msg); return send_network_message(&msg);
} }
/** /**
* @brief * @brief
*/ */
bool send_chat_message(const char* message) bool send_chat_message(const char *message)
{ {
NetworkMessage msg = {0}; NetworkMessage msg = {0};
msg.type = MSG_CHAT; msg.type = MSG_CHAT;
msg.player_id = network_state.local_player_id; msg.player_id = network_state.local_player_id;
strncpy(msg.message, message, sizeof(msg.message) - 1); strncpy(msg.message, message, sizeof(msg.message) - 1);
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
return send_network_message(&msg); return send_network_message(&msg);
} }
@@ -393,7 +391,7 @@ bool send_surrender()
msg.type = MSG_SURRENDER; msg.type = MSG_SURRENDER;
msg.player_id = network_state.local_player_id; msg.player_id = network_state.local_player_id;
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
return send_network_message(&msg); return send_network_message(&msg);
} }
@@ -407,7 +405,7 @@ bool send_undo_request(int steps)
msg.player_id = network_state.local_player_id; msg.player_id = network_state.local_player_id;
msg.x = steps; // 使用x字段存储步数 msg.x = steps; // 使用x字段存储步数
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
return send_network_message(&msg); return send_network_message(&msg);
} }
@@ -419,9 +417,9 @@ bool send_undo_response(bool accepted, int steps)
NetworkMessage msg = {0}; NetworkMessage msg = {0};
msg.type = MSG_UNDO_RESPONSE; msg.type = MSG_UNDO_RESPONSE;
msg.player_id = network_state.local_player_id; msg.player_id = network_state.local_player_id;
msg.x = steps; // 使用x字段存储步数 msg.x = steps; // 使用x字段存储步数
msg.y = accepted ? 1 : 0; // 使用y字段存储是否同意 msg.y = accepted ? 1 : 0; // 使用y字段存储是否同意
msg.timestamp = time(NULL); msg.timestamp = time(NULL);
return send_network_message(&msg); return send_network_message(&msg);
} }
+23 -18
View File
@@ -1,3 +1,10 @@
/**
* @file record.c
* @brief
* @note
*
*/
#include "record.h" #include "record.h"
#include "game_mode.h" #include "game_mode.h"
#include "gobang.h" #include "gobang.h"
@@ -43,7 +50,7 @@
void review_process(int game_mode) void review_process(int game_mode)
{ {
int review_choice = get_integer_input("是否要复盘本局比赛? (1-是, 0-否): ", 0, 1); int review_choice = get_integer_input("是否要复盘本局比赛? (1-是, 0-否): ", 0, 1);
// 如果评分尚未计算,则计算评分 // 如果评分尚未计算,则计算评分
if (!scores_calculated) if (!scores_calculated)
{ {
@@ -54,7 +61,7 @@ void review_process(int game_mode)
// 评分已从文件中加载,直接使用 // 评分已从文件中加载,直接使用
printf("从记录文件中加载评分数据\n"); printf("从记录文件中加载评分数据\n");
} }
if (review_choice == 1) if (review_choice == 1)
{ {
printf("\n===== 复盘记录(总步数:%d) =====\n", step_count); printf("\n===== 复盘记录(总步数:%d) =====\n", step_count);
@@ -108,7 +115,7 @@ void review_process(int game_mode)
for (int col = 0; col < BOARD_SIZE; col++) for (int col = 0; col < BOARD_SIZE; col++)
{ {
printf("%2d", col + 1); // 列号 printf("%2d", col + 1); // 列号
} }
printf("\n"); printf("\n");
for (int row = 0; row < BOARD_SIZE; row++) for (int row = 0; row < BOARD_SIZE; row++)
@@ -140,7 +147,7 @@ void review_process(int game_mode)
; // 等待回车 ; // 等待回车
} }
} }
// 显示胜负结果(直接使用文件中的信息) // 显示胜负结果(直接使用文件中的信息)
printf("\n===== 对局结果 ====="); printf("\n===== 对局结果 =====");
if (strcmp(winner_info, "玩家获胜") == 0) if (strcmp(winner_info, "玩家获胜") == 0)
@@ -163,7 +170,7 @@ void review_process(int game_mode)
{ {
printf("\n?? 对局平局或未完成\n"); printf("\n?? 对局平局或未完成\n");
} }
printf("\n复盘结束!按Enter查看评分..."); printf("\n复盘结束!按Enter查看评分...");
getchar(); // 等待用户按键 getchar(); // 等待用户按键
} }
@@ -188,7 +195,7 @@ void calculate_game_scores()
{ {
// 计算时间权重因子:步数越靠后,权重越大 // 计算时间权重因子:步数越靠后,权重越大
double time_weight = 1.0 + (double)i / step_count * TIME_WEIGHT_FACTOR; // 最后的步骤权重是开始步骤的(1+TIME_WEIGHT_FACTOR)倍 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) 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); player1_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight);
@@ -198,7 +205,7 @@ void calculate_game_scores()
player2_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight); player2_final_score += (int)(calculate_step_score(steps[i].x, steps[i].y, steps[i].player) * time_weight);
} }
} }
// 胜负加权:获胜方获得额外的评分奖励 // 胜负加权:获胜方获得额外的评分奖励
if (step_count > 0) if (step_count > 0)
{ {
@@ -216,7 +223,7 @@ void calculate_game_scores()
} }
} }
} }
scores_calculated = 1; // 标记评分已计算 scores_calculated = 1; // 标记评分已计算
} }
@@ -289,10 +296,8 @@ void display_game_scores(int game_mode)
*/ */
void handle_save_record(int game_mode) void handle_save_record(int game_mode)
{ {
int save_choice = 0;
printf("===== 游戏结束 =====\n"); printf("===== 游戏结束 =====\n");
printf("是否保存游戏记录? (1-是, 0-否): "); int save_choice = get_integer_input("是否保存游戏记录? (1-是, 0-否): ", 0, 1);
scanf("%d", &save_choice);
if (save_choice == 1) if (save_choice == 1)
{ {
@@ -416,14 +421,14 @@ int save_game_to_file(const char *filename, int game_mode)
} }
} }
} }
// 写入CSV文件头部 // 写入CSV文件头部
if (fprintf(file, "游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果\n%d,%d,%d,%d,%s\n\n", game_mode, BOARD_SIZE, player1_final_score, player2_final_score, winner_info) < 0) if (fprintf(file, "游戏模式,棋盘大小,玩家1得分,玩家2得分,对局结果\n%d,%d,%d,%d,%s\n\n", game_mode, BOARD_SIZE, player1_final_score, player2_final_score, winner_info) < 0)
{ {
fclose(file); fclose(file);
return 3; // 文件写入失败 return 3; // 文件写入失败
} }
// 写入CSV表头 // 写入CSV表头
if (fprintf(file, "步数,玩家,行坐标,列坐标\n") < 0) if (fprintf(file, "步数,玩家,行坐标,列坐标\n") < 0)
{ {
@@ -434,7 +439,7 @@ int save_game_to_file(const char *filename, int game_mode)
// 写入所有落子步骤(CSV格式) // 写入所有落子步骤(CSV格式)
for (int i = 0; i < step_count; i++) for (int i = 0; i < step_count; i++)
{ {
if (fprintf(file, "%d,%d,%d,%d\n", i+1, steps[i].player, steps[i].x+1, steps[i].y+1) < 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); fclose(file);
return 3; // 文件写入失败 return 3; // 文件写入失败
@@ -476,10 +481,10 @@ int load_game_from_file(const char *filename)
// 读取游戏模式、棋盘大小和评分结果 // 读取游戏模式、棋盘大小和评分结果
int game_mode, size; int game_mode, size;
// 尝试读取新格式(包含胜负信息) // 尝试读取新格式(包含胜负信息)
int read_count = fscanf(file, "%d,%d,%d,%d,%49s", &game_mode, &size, &player1_final_score, &player2_final_score, winner_info); int read_count = fscanf(file, "%d,%d,%d,%d,%49s", &game_mode, &size, &player1_final_score, &player2_final_score, winner_info);
if (read_count == 4) if (read_count == 4)
{ {
// 旧格式文件,没有胜负信息 // 旧格式文件,没有胜负信息
@@ -491,7 +496,7 @@ int load_game_from_file(const char *filename)
fclose(file); fclose(file);
return 0; return 0;
} }
if (game_mode != GAME_MODE_AI && game_mode != GAME_MODE_PVP && game_mode != GAME_MODE_NETWORK) if (game_mode != GAME_MODE_AI && game_mode != GAME_MODE_PVP && game_mode != GAME_MODE_NETWORK)
{ {
fclose(file); fclose(file);
@@ -502,7 +507,7 @@ int load_game_from_file(const char *filename)
fclose(file); fclose(file);
return false; return false;
} }
// 设置评分已计算标志 // 设置评分已计算标志
scores_calculated = 1; scores_calculated = 1;
+12 -4
View File
@@ -1,3 +1,10 @@
/**
* @file ui.c
* @brief
* @note
*
*/
#include "ui.h" #include "ui.h"
#include "gobang.h" #include "gobang.h"
#include "config.h" #include "config.h"
@@ -24,6 +31,7 @@ void display_main_menu()
printf("5. 游戏设置\n"); printf("5. 游戏设置\n");
printf("6. 游戏规则\n"); printf("6. 游戏规则\n");
printf("7. 关于游戏\n"); printf("7. 关于游戏\n");
printf("8. 图形化界面\n");
printf("0. 退出游戏\n"); printf("0. 退出游戏\n");
printf("=====================\n"); printf("=====================\n");
} }
@@ -40,7 +48,7 @@ void display_board()
printf("%2d", j); printf("%2d", j);
} }
printf("\n"); printf("\n");
// 打印棋盘内容 // 打印棋盘内容
for (int i = 0; i < BOARD_SIZE; i++) for (int i = 0; i < BOARD_SIZE; i++)
{ {
@@ -123,7 +131,7 @@ void display_settings_menu()
printf("3. 计时器设置\n"); printf("3. 计时器设置\n");
printf("4. 网络配置设置\n"); printf("4. 网络配置设置\n");
printf("5. AI难度设置\n"); printf("5. AI难度设置\n");
printf("6. 返回主菜单\n"); printf("0. 返回主菜单\n");
printf("==================\n"); printf("==================\n");
} }
@@ -143,7 +151,7 @@ void clear_screen()
* @brief * @brief
* @param prompt * @param prompt
*/ */
void pause_for_input(const char* prompt) void pause_for_input(const char *prompt)
{ {
printf("%s", prompt); printf("%s", prompt);
#ifdef _WIN32 #ifdef _WIN32
@@ -186,7 +194,7 @@ void display_about()
{ {
printf("\n===== 关于五子棋游戏 =====\n"); printf("\n===== 关于五子棋游戏 =====\n");
printf("🎮 游戏名称:五子棋人机对战\n"); printf("🎮 游戏名称:五子棋人机对战\n");
printf("📦 版本:4.0\n"); printf("📦 版本:7.0\n");
printf("👨‍💻 开发者:刘航宇\n"); printf("👨‍💻 开发者:刘航宇\n");
printf("📧 联系邮箱:3364451258@qq.com\n"); printf("📧 联系邮箱:3364451258@qq.com\n");
printf("🌐 项目主页:https://github.com/LHY0125/Gobang-Game\n\n"); printf("🌐 项目主页:https://github.com/LHY0125/Gobang-Game\n\n");