29 Commits

Author SHA1 Message Date
Serendipity eada6d29b3 Add files via upload 2025-07-10 10:15:33 +08:00
Serendipity 22be820525 Update README.md 2025-07-10 10:14:12 +08:00
Serendipity 49b6347f72 Add files via upload 2025-07-10 10:07:55 +08:00
Serendipity aae7990cfd Add files via upload 2025-07-10 10:07:34 +08:00
Serendipity 3663352e5f Add files via upload 2025-07-09 23:00:26 +08:00
Serendipity 367f7a1709 Add files via upload 2025-07-07 18:24:02 +08:00
Serendipity 46d9993394 更新 2025-07-07 09:05:42 +08:00
Serendipity c77e32c219 更新 2025-07-07 09:02:34 +08:00
Serendipity bb252f4a4c Add files via upload 2025-07-03 09:31:48 +08:00
Serendipity 6e8b61a958 Add files via upload 2025-06-30 22:27:15 +08:00
Serendipity cf8cfceefe Add files via upload 2025-06-30 21:02:13 +08:00
Serendipity 0970c3fd82 Add files via upload 2025-06-30 18:19:53 +08:00
Serendipity b05d03c614 Add files via upload 2025-06-29 22:51:33 +08:00
Serendipity 680787ab7a Update README.md 2025-06-29 22:48:27 +08:00
Serendipity 2eab60e2ad Update README.md 2025-06-24 18:25:28 +08:00
Serendipity e09827a10b Update gobang.h 2025-06-24 18:24:59 +08:00
Serendipity 1bcd64eaec Update gobang.c 2025-06-24 18:24:28 +08:00
Serendipity 978280be85 Update 五子棋.c 2025-06-24 18:23:49 +08:00
Serendipity ed222473af Update README.md 2025-06-24 18:18:06 +08:00
Serendipity d60b31eb9f Update README.md 2025-06-24 18:17:33 +08:00
Serendipity 2f021752ee Update gobang.h 2025-06-24 18:16:56 +08:00
Serendipity ec72db8385 Update gobang.c 2025-06-24 18:16:23 +08:00
Serendipity 474d7c6004 Update 五子棋.c 2025-06-24 18:15:40 +08:00
Serendipity 8c8f52f081 Add files via upload 2025-06-23 20:43:58 +08:00
Serendipity 46048b25ce gobang.h 2025-06-23 20:37:24 +08:00
Serendipity 19c4ddbf3e Add files via upload 2025-06-23 20:34:10 +08:00
Serendipity 160aebc0db Add files via upload 2025-06-23 20:33:43 +08:00
Serendipity 4edba6e7ce Add files via upload 2025-06-23 20:33:14 +08:00
Serendipity 246ecc007b Initial commit 2025-06-16 18:41:43 +08:00
42 changed files with 1811 additions and 4726 deletions
-42
View File
@@ -1,42 +0,0 @@
# 编译生成的可执行文件
*.exe
*.out
*.app
# IDE配置文件
.idea/
.vscode/
.vs/
# Trae AI配置文件
.trae/
# 临时文件
*.tmp
*.temp
*.log
# 系统文件
.DS_Store
Thumbs.db
# 备份文件
*.bak
*.backup
# 调试文件
*.pdb
*.ilk
# 对象文件
*.o
*.obj
# 静态库文件
*.lib
*.a
# 动态库文件
*.dll
*.so
*.dylib
+1 -17
View File
@@ -1,7 +1,7 @@
# 🧠 五子棋AI实现详解
## 📜 算法概述
本五子棋AI采用α-β剪枝优化的极小极大算法,结合专业的棋型评估系统和多层次的威胁检测机制。支持人机对战、双人对战和网络对战多种模式。
本五子棋AI采用α-β剪枝优化的极小极大算法,结合专业的棋型评估系统和多层次的威胁检测机制。
```mermaid
graph TD
@@ -40,14 +40,6 @@ typedef struct {
} DirInfo;
```
### 🌐 网络对战支持
```c
// 网络模式下的AI决策
void network_ai_move(int depth, int player_id);
// 同步AI决策到网络对手
void sync_ai_decision(int x, int y);
```
## ⚙️ 核心函数
### 1. ai_move(int depth)
@@ -104,11 +96,6 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi
- 基础难度: 3层
- 最高难度: 5层
4. **网络优化**:
- 异步AI计算,避免网络延迟
- 决策结果实时同步
- 支持断线重连后状态恢复
## 🎯 典型场景
### 必胜局面处理
@@ -136,12 +123,9 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi
1. **调试技巧**:
- 启用`DEBUG_MODE`查看搜索过程
- 使用`print_board()`可视化评估
- 网络模式下使用`network_debug()`监控通信
2. **扩展方向**:
- 添加开局库
- 实现并行搜索
- 优化评估函数
- 增强网络对战AI适应性
- 支持AI难度动态调整
```
-461
View File
@@ -1,461 +0,0 @@
# 🚀 五子棋AI增强指南
## 📋 概述
本文档详细介绍了提升五子棋AI水平的各种方法和实现策略,基于当前项目的代码架构,提供从简单参数调优到复杂算法改进的完整方案。
## 🎯 当前AI分析
### 现有优势
- ✅ 模块化设计良好
- ✅ 基础Minimax + α-β剪枝算法
- ✅ 完整的棋型评估系统
- ✅ 威胁检测机制
- ✅ 防御优先策略
### 改进空间
- 🔄 搜索深度有限(当前3层)
- 🔄 评估函数相对简单
- 🔄 缺乏开局库和残局库
- 🔄 没有学习机制
- 🔄 搜索效率可优化
## 🛠️ 改进方案
### 1. 立即可实施的改进(难度:⭐)
#### 1.1 参数调优
**修改 `config.h` 中的关键参数:**
```c
// 增加搜索深度
#define DEFAULT_AI_DEPTH 5 // 从3提升到5
// 优化防守系数
#define DEFAULT_DEFENSE_COEFFICIENT 1.5 // 从1.2提升到1.5
// 扩大搜索范围
#define AI_NEARBY_RANGE 3 // 从2扩大到3
// 降低搜索范围限制阈值
#define AI_SEARCH_RANGE_THRESHOLD 8 // 从10降低到8
```
#### 1.2 评分系统优化
**在 `config.h` 中添加新的评分项:**
```c
// 组合棋型评分
#define AI_SCORE_DOUBLE_THREE 50000 // 双三
#define AI_SCORE_FOUR_THREE 200000 // 四三
#define AI_SCORE_THREAT_SEQUENCE 80000 // 威胁序列
#define AI_SCORE_POTENTIAL_FIVE 300000 // 潜在五连
```
### 2. 短期改进(1-2周,难度:⭐⭐)
#### 2.1 移动排序优化
**在 `ai.c` 中添加移动排序函数:**
```c
// 移动排序结构
typedef struct {
int x, y;
int score;
} ScoredMove;
// 移动排序函数
int compare_moves(const void *a, const void *b) {
ScoredMove *moveA = (ScoredMove*)a;
ScoredMove *moveB = (ScoredMove*)b;
return moveB->score - moveA->score;
}
// 生成并排序候选移动
int generate_candidate_moves(ScoredMove *moves, int player) {
int count = 0;
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == EMPTY && is_near_stones(i, j)) {
moves[count].x = i;
moves[count].y = j;
moves[count].score = evaluate_move(i, j);
count++;
}
}
}
qsort(moves, count, sizeof(ScoredMove), compare_moves);
return count;
}
```
#### 2.2 威胁检测增强
**添加多层次威胁检测:**
```c
// 威胁类型枚举
typedef enum {
THREAT_NONE = 0,
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不仅要算得深,更要算得准!**
-268
View File
@@ -1,268 +0,0 @@
# 五子棋项目代码架构重构指南
## 📋 概述
本文档详细记录了五子棋项目在v7.0版本中进行的重大代码架构重构,包括重构的目标、实施过程、技术细节和带来的改进。
## 🎯 重构目标
### 主要目标
1. **代码模块化** - 实现清晰的模块分离和职责划分
2. **配置统一管理** - 集中管理所有配置参数和宏定义
3. **全局变量规范化** - 统一管理全局变量,避免散乱分布
4. **类型定义标准化** - 集中定义所有数据结构和类型
5. **提升可维护性** - 降低代码耦合度,提高可读性和可维护性
### 预期收益
- 减少代码重复和冗余
- 提高开发效率和调试便利性
- 增强代码的可扩展性和可移植性
- 为后续功能开发奠定坚实基础
## 🏗️ 重构实施
### 1. 配置参数统一管理
#### 重构前状态
- 配置参数散落在多个头文件中
- 存在重复定义和不一致的问题
- 网络相关配置分散在`network.h`
- 缺乏统一的配置管理机制
#### 重构措施
- **集中到config.h**:将所有配置宏定义迁移到`config.h`文件
- **分类管理**:按功能模块对配置参数进行分组
- **消除重复**:移除重复的宏定义,确保唯一性
- **标准化命名**:统一配置参数的命名规范
#### 配置分类结构
```c
// 棋盘配置
#define BOARD_SIZE 15
#define MIN_BOARD_SIZE 5
#define MAX_BOARD_SIZE 25
// 游戏模式配置
#define MODE_HUMAN_VS_AI 1
#define MODE_HUMAN_VS_HUMAN 2
#define MODE_NETWORK_BATTLE 3
// AI参数配置
#define DEFAULT_AI_DEPTH 3
#define MAX_AI_DEPTH 6
#define AI_TIMEOUT_MS 5000
// 网络配置
#define DEFAULT_PORT 8888
#define BUFFER_SIZE 1024
#define MAX_IP_LENGTH 16
// 评分参数
#define SCORE_FIVE 100000
#define SCORE_LIVE_FOUR 10000
#define SCORE_RUSH_FOUR 1000
```
### 2. 全局变量统一管理
#### 重构前状态
- 全局变量分散在各个源文件中
- 缺乏统一的声明和定义管理
- 变量作用域不清晰
- 初始化逻辑分散
#### 重构措施
- **创建globals模块**:新建`globals.h``globals.c`文件
- **集中声明**:在`globals.h`中统一声明所有全局变量
- **集中定义**:在`globals.c`中统一定义和初始化
- **访问规范化**:通过包含`globals.h`访问全局变量
#### 全局变量分类
```c
// 游戏状态变量
extern int current_board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
extern int current_player;
extern int game_over;
// 配置变量
extern GameConfig game_config;
extern AIConfig ai_config;
extern NetworkConfig network_config;
// 统计变量
extern GameStats game_stats;
extern int total_games_played;
```
### 3. 类型定义标准化
#### 重构前状态
- 结构体定义分散在各个头文件中
- 类型定义不统一
- 缺乏标准的数据结构规范
#### 重构措施
- **创建type.h**:集中定义所有数据结构和类型
- **标准化命名**:采用统一的命名规范
- **逻辑分组**:按功能对类型进行分组
- **文档化**:为每个类型添加详细注释
#### 类型定义结构
```c
// 基础类型定义
typedef enum {
PLAYER_NONE = 0,
PLAYER_BLACK = 1,
PLAYER_WHITE = 2
} PlayerType;
// 游戏配置结构
typedef struct {
int board_size;
int ai_level;
int enable_forbidden;
int time_limit;
} GameConfig;
// 网络消息结构
typedef struct {
int type;
int x, y;
int player;
char data[256];
} NetworkMessage;
```
### 4. 网络配置重构
#### 具体实施
1. **迁移宏定义**:将`network.h`中的配置宏移动到`config.h`
2. **统一命名**:规范网络相关宏的命名
3. **添加引用**:在`network.h`中添加`#include "config.h"`
4. **消息类型统一**:将消息类型定义集中管理
#### 迁移的配置项
```c
// 从network.h迁移到config.h
#define DEFAULT_PORT 8888
#define BUFFER_SIZE 1024
#define MAX_IP_LENGTH 16
#define MSG_MOVE 1
#define MSG_CHAT 2
#define MSG_SURRENDER 3
#define MSG_DISCONNECT 4
```
## 📊 重构效果评估
### 代码质量提升
- **模块耦合度降低**:各模块职责更加清晰
- **代码重复减少**:消除了重复的宏定义和类型定义
- **可读性增强**:统一的命名规范和代码结构
- **维护性提高**:集中管理使得修改更加便捷
### 开发效率提升
- **配置修改便捷**:只需在一个地方修改配置参数
- **调试更容易**:全局变量集中管理,状态更清晰
- **扩展更简单**:标准化的接口和数据结构
- **错误减少**:统一管理避免了不一致性错误
### 性能影响
- **编译时间**:略有增加(由于更多的头文件包含)
- **运行时性能**:无显著影响
- **内存使用**:无显著变化
- **整体评估**:性能影响微乎其微,收益远大于成本
## 🔧 技术细节
### 文件结构变化
#### 新增文件
- `type.h` - 类型定义集中文件
- `globals.h` - 全局变量声明文件
- `globals.c` - 全局变量定义文件
#### 修改文件
- `config.h` - 扩展为完整的配置管理文件
- `network.h` - 移除配置定义,添加config.h引用
- 所有源文件 - 更新头文件包含关系
### 编译依赖关系
```
type.h (基础类型)
config.h (配置参数)
globals.h (全局变量声明)
各功能模块头文件
源文件实现
```
### 包含关系规范
1. **type.h**:被所有需要类型定义的文件包含
2. **config.h**:被所有需要配置参数的文件包含
3. **globals.h**:被所有需要访问全局变量的文件包含
4. **功能模块头文件**:按需包含上述基础头文件
## 📝 最佳实践
### 配置管理
1. **新增配置参数**:统一添加到`config.h`的相应分组中
2. **命名规范**:使用描述性的宏名称,避免缩写
3. **分组管理**:按功能模块对配置进行逻辑分组
4. **文档注释**:为每个配置参数添加清晰的注释
### 全局变量管理
1. **声明规范**:在`globals.h`中使用extern声明
2. **定义规范**:在`globals.c`中进行实际定义和初始化
3. **访问规范**:通过包含`globals.h`访问,避免重复声明
4. **初始化管理**:在`globals.c`中集中进行初始化
### 类型定义管理
1. **命名规范**:使用PascalCase命名结构体和枚举
2. **分组管理**:按功能对类型进行逻辑分组
3. **文档化**:为每个类型和字段添加详细注释
4. **版本兼容**:考虑结构体的向后兼容性
## 🚀 未来扩展
### 短期计划
1. **配置文件增强**:支持更多配置项的动态加载
2. **类型安全增强**:添加更多的类型检查和验证
3. **模块接口标准化**:定义标准的模块接口规范
### 长期规划
1. **插件架构**:基于当前架构实现插件系统
2. **配置热重载**:支持运行时配置的动态更新
3. **跨平台适配**:利用统一架构实现跨平台支持
## 📚 参考资料
### 相关文档
- [C语言编程规范](https://www.kernel.org/doc/html/latest/process/coding-style.html)
- [软件架构设计原则](https://en.wikipedia.org/wiki/Software_architecture)
- [模块化编程最佳实践](https://en.wikipedia.org/wiki/Modular_programming)
### 工具推荐
- **静态分析**:使用cppcheck进行代码质量检查
- **格式化**:使用clang-format统一代码格式
- **文档生成**:使用Doxygen生成API文档
## 📈 总结
v7.0版本的代码架构重构是一次重要的技术升级,通过系统性的重构实现了:
**配置参数的统一管理** - 提高了配置的一致性和可维护性
**全局变量的规范化** - 降低了代码的复杂度和耦合度
**类型定义的标准化** - 增强了代码的可读性和类型安全
**模块结构的优化** - 为后续功能扩展奠定了坚实基础
这次重构不仅解决了当前的技术债务,更为项目的长期发展提供了良好的架构基础。后续的功能开发将能够更加高效和稳定地进行。
---
*本文档将随着项目的发展持续更新,记录架构演进的每一个重要节点。*
-152
View File
@@ -1,152 +0,0 @@
# 五子棋网络对战使用说明
## 功能概述
本项目支持网络对战功能,允许两台设备通过网络进行实时五子棋对战,支持服务器/客户端连接。
## 编译方法
```bash
gcc -std=c17 -o gobang.exe *.c -lws2_32
```
**注意:**
- Windows系统需要添加 `-lws2_32` 链接库
- Linux系统不需要此链接库参数
## 使用方法
### 1. 启动游戏
运行编译后的程序
```bash
.\gobang.exe
```
### 2. 选择网络对战模式
在主菜单中选择 `3. 网络对战`
### 3. 选择连接模式
#### 模式1:创建房间(作为服务器)
- 选择 `1. 创建房间(作为服务器)`
- 输入监听端口(默认8888,建议使用1024-65535范围内的端口)
- 程序会显示本机IP地址,将此IP告知对方
- 等待对方连接
#### 模式2:加入房间(连接到服务器)
- 选择 `2. 加入房间(连接到服务器)`
- 输入服务器IP地址
- 输入服务器的端口号(与服务器设置的端口一致)
- 连接到服务器
### 4. 开始游戏
- 连接成功后游戏自动开始
- 服务器为玩家1(黑棋),客户端为玩家2(白棋)
- 玩家1先手
## 游戏操作
### 基本操作
- **落子**:输入坐标 `行号 列号`,例如:`7 7`
- **认输**:输入 `S``s`
- **悔棋**:输入 `R``r`(需要对方同意)
### 网络功能
- **自动同步**:落子操作会自动同步给对方
- **连接检测**:自动检测网络连接状态
- **延时显示**:支持回合延时显示,避免过快操作
- **悔棋协商**:悔棋需要对方同意才能生效
## 网络配置
### 端口设置
- 默认端口:8888
- 可设端口范围:1024-65535
- 确保防火墙允许选定端口的通信
### IP地址
- **局域网**:使用内网IP地址,例如:192.168.1.100
- **广域网**:使用公网IP地址,可能需要路由器端口转发
### 防火墙设置
如果连接失败,请检查防火墙设置:
#### Windows防火墙
1. 打开Windows安全中心
2. 选择"防火墙和网络保护"
3. 选择"允许应用通过防火墙"
4. 添加gobang.exe到允许列表
#### 路由器设置(用于广域网对战)
如果通过互联网对战,可能需要:
1. 在路由器中设置端口转发
2. 将选定端口转发到服务器内网IP
3. 将路由器的公网IP告知对方
## 故障排除
### 常见问题
1. **连接失败**
- 检查IP地址和端口是否正确
- 确认防火墙设置
- 确保两台设备网络连通
2. **游戏中断**
- 检查网络连接稳定性
- 重新启动游戏重新连接
3. **端口被占用**
- 更换其他端口号
- 关闭占用端口的其他程序
### 网络测试
可以使用以下命令测试网络连通性:
```bash
# 测试网络连通
ping <对方IP地址>
# 测试端口(需要telnet客户端)
telnet <对方IP地址> <端口号>
```
## 技术细节
- **协议**TCP/IP
- **消息格式**:自定义二进制协议
- **支持功能**
- 棋盘同步
- 悔棋处理
- 认输协商
- 连接检测
- 延时控制
## 安全注意事项
1. **局域网使用**:建议安全的家庭或办公环境
2. **广域网使用**
- 不要使用默认端口8888
- 游戏结束后及时关闭程序
- 注意保护个人网络信息
## 更新日志
### v7.0 (2025-07-20)
- 网络配置参数统一管理 - 所有网络相关配置集中到config.h
- 消息类型定义优化 - 统一消息协议宏定义
- 代码架构重构 - 提升网络模块的可维护性
- 配置文件支持 - 网络参数可通过配置文件调整
### v6.1 (2025-07-10)
- 完善网络对战功能
- 支持TCP/IP通信
- 实现棋盘同步
- 连接状态和协议优化
- 支持多种操作
- 添加延时控制等游戏功能(延时显示、认输、悔棋等)
---
**开发者:** 刘航宇
**联系邮箱:** 3364451258@qq.com
**项目主页:** https://github.com/LHY0125/Gobang-Game
+26 -52
View File
@@ -2,33 +2,31 @@
![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
![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-v5.0-blue)
![Platform](https://img.shields.io/badge/platform-Windows-lightgrey)
> 🎯 **最新版本 v7.0** - 代码架构重构更新,实现结构体定义集中化、配置参数统一管理、代码模块化优化等核心改进
> 🎯 **最新版本 v5.0** - 全面重构,新增配置管理、复盘分析、评分系统等核心功能
## 📋 版本更新
### v7.0 (2025-07-20) - 代码架构重构更新
- 🏗️ **结构体定义集中化** - 所有数据结构统一管理在type.h中
- ⚙️ **配置参数统一管理** - 所有配置宏定义集中在config.h中
- 🔧 **代码模块化优化** - 消除重复定义,提高代码可维护性
- 📋 **菜单选项优化** - 退出选项调整为"0. 退出游戏"
- 🎯 **类型系统完善** - 独立的type.h文件管理所有数据类型
- 🌐 **网络配置重构** - 网络相关宏定义统一到config.h
- 📊 **全局变量管理** - 优化全局变量声明和定义结构
- 🔄 **头文件依赖优化** - 改进模块间依赖关系和包含结构
## 📋 版本更新
### v5.0 (2025-07-10) - 重大更新
-**新增配置管理系统** - 支持INI配置文件持久化
- 📊 **完整复盘功能** - 逐步回放对局并提供专业评分
- 🧠 **智能评分系统** - 每步棋评分分析和MVP评选
- 💻 **现代化UI界面** - 重新设计的用户交互界面
- 🔧 **模块化重构** - 清晰的代码架构,便于维护扩展
-**禁手规则支持** - 可选启用标准五子棋禁手规则
- ⏱️ **计时器功能** - 支持每回合时间限制设置
## 目录
- [C语言五子棋人机对战AI](#c语言五子棋人机对战ai)
- [📋 版本更新](#-版本更新)
- [v7.0 (2025-07-20) - 代码架构重构更新](#v70-2025-07-20---代码架构重构更新)
- [📋 版本更新](#-版本更新)
- [v5.0 (2025-07-10) - 重大更新](#v50-2025-07-10---重大更新)
- [目录](#目录)
- [项目简介](#项目简介)
- [功能特性](#功能特性)
- [🎮 游戏模式](#-游戏模式)
- [⚙️ 游戏设置](#-游戏设置)
- [⚙️ 游戏设置](#-游戏设置)
- [🎯 游戏功能](#-游戏功能)
- [💻 用户体验](#-用户体验)
- [🔧 技术特性](#-技术特性)
@@ -38,9 +36,8 @@
- [游戏玩法](#游戏玩法)
- [🚀 快速开始](#-快速开始)
- [🎯 对局操作](#-对局操作)
- [⚙️ 配置管理](#-配置管理)
- [⚙️ 配置管理](#-配置管理)
- [📊 复盘功能](#-复盘功能)
- [🌐 网络对战功能](#-网络对战功能)
- [环境要求](#环境要求)
- [常见问题](#常见问题)
- [权限问题](#权限问题)
@@ -64,7 +61,7 @@
- [🔧 技术优化](#-技术优化)
## 项目简介
这是一个使用C语言实现的现代化五子棋对战系统,支持人机对战、双人对战和网络对战三种模式。系统基于 Alpha-Beta 剪枝优化的 Minimax 算法,具备完整的配置管理、复盘分析、智能评分和网络通信功能
这是一个使用C语言实现的五子棋人机对战系统,它基于 Alpha-Beta 剪枝优化的 Minimax 算法,并支持自定义棋盘大小、游戏存档和实时悔棋
## 功能特性
@@ -95,28 +92,22 @@
### 🔧 技术特性
- **模块化架构** - 清晰的代码结构,便于维护和扩展
- **结构体定义集中化** - 所有数据结构统一在type.h中管理
- **配置参数统一管理** - 所有配置宏定义集中在config.h中
- **全局变量统一管理** - 所有全局变量集中在globals模块中管理
- **内存优化管理** - 高效的内存使用和资源管理
- **配置文件支持** - INI格式配置文件自动加载保存
- **UTF-8编码支持** - 完美支持中文显示
- **网络对战功能** - 完整的在线多人对战系统
## 快速开始
### 编译项目
```bash
gcc -std=c17 -o gobang.exe *.c -lws2_32
gcc -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.c config.c
```
或者使用优化编译:
```bash
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
```
**注意:** Windows系统需要添加 `-lws2_32` 链接库以支持网络功能
### 运行游戏
```bash
.\gobang.exe
@@ -127,14 +118,13 @@ gcc -O2 -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.
### 🚀 快速开始
1. **启动游戏**:运行 `gobang.exe` 进入主菜单
2. **选择模式**
- `1` - **人机对战模式** - 与AI智能对手进行五子棋对战
- `2` - **双人对战模式** - 两名玩家轮流对弈的本地对战
- `3` - **网络对战模式** - 通过网络与远程玩家实时对战
- `4` - **复盘模式** - 回放历史对局并查看详细分析
- `5` - **配置管理** - 自定义游戏设置和参数调整
- `6` - **游戏规则** - 查看五子棋游戏规则和操作说明
- `7` - **关于信息** - 查看项目版本和开发者信息
- `0` - **退出游戏** - 安全退出程序
- `1` - 人机对战模式
- `2` - 双人对战模式
- `3` - 复盘模式
- `4` - 配置管理
- `5` - 游戏规则
- `6` - 关于信息
- `7` - 退出游戏
### 🎯 对局操作
- **落子**:输入坐标 (格式: `行 列`,如 `8 8`)
@@ -157,14 +147,6 @@ gcc -O2 -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.
- **MVP评选**:自动评选本局最佳表现者
- **胜负统计**:完整的对局结果记录
### 🌐 网络对战功能
- **服务器模式**:创建游戏房间等待其他玩家加入
- **客户端模式**:连接到指定服务器进行对战
- **实时同步**:棋盘状态和游戏进度实时同步
- **连接管理**:自动处理网络连接和断线重连
- **延迟显示**:实时显示网络延迟状态
- **安全验证**:基本的数据验证和防作弊检测
## 环境要求
- 操作系统: Windows (当前版本使用了Windows特有的 `_kbhit()``Sleep()` 函数,因此暂不跨平台)
- 编译器: GCC (MinGW-w64)
@@ -231,17 +213,12 @@ chcp 65001
- **`record.c/h`** - 游戏记录系统 (保存、加载、复盘、评分)
- **`init_board.c/h`** - 棋盘初始化和游戏设置
- **`config.c/h`** - 配置管理系统 (参数设置、文件读写)
- **`globals.c/h`** - 全局变量统一管理模块
- **`network.c/h`** - 网络功能模块 (为未来网络对战预留)
### 📄 配置和文档
- **`gobang_config.ini`** - 游戏配置文件 (自动生成和保存)
- **`records/`** - 对局记录目录 (CSV格式存储)
- **`type.h`** - 数据结构和类型定义集中文件
- **`MD/README.md`** - 项目说明文档
- **`MD/AI_Enhancement_Guide.md`** - AI算法增强指南
- **`MD/NETWORK_README.md`** - 网络功能使用说明
- **`MD/Architecture_Refactoring_Guide.md`** - 代码架构重构详细指南
- **`MD/AI_function.md`** - AI算法详细说明
### 🔧 开发工具
- **`.vscode/`** - VS Code 配置文件
@@ -267,14 +244,11 @@ chcp 65001
### ✅ 已完成功能
- [x] **模块化架构设计** - 完成代码重构,实现清晰的模块分离
- [x] **全局变量统一管理** - 所有全局变量集中在globals模块中管理
- [x] **宏定义优化** - 消除重复定义,统一管理所有宏定义
- [x] **配置管理系统** - 实现INI配置文件的自动加载和保存
- [x] **完整复盘功能** - 支持对局记录、回放和专业评分分析
- [x] **用户界面优化** - 实现现代化的终端UI界面
- [x] **智能评分系统** - 完成每步棋的评分和MVP评选功能
- [x] **禁手规则支持** - 添加标准五子棋禁手规则选项
- [x] **网络模块预留** - 为未来网络对战功能预留完整接口
### 🚀 开发路线图
-158
View File
@@ -1,158 +0,0 @@
五子棋对战系统 - 代码统计报告
========================================
项目名称:五子棋多模式对战系统
统计时间:2025年7月20日
项目版本:v7.0
开发语言:C语言
GitHub仓库:https://github.com/LHY0125/Gobang-Game.git
========================================
📊 代码行数统计
========================================
【C源文件 (.c)】
├── main.c 85行
├── gobang.c 269行
├── game_mode.c 917行
├── ai.c 589行
├── record.c 531行
├── init_board.c 118行
├── ui.c 204行
├── config.c 331行
├── network.c 426行
└── globals.c 37行
【头文件 (.h)】
├── gobang.h 101行
├── game_mode.h 99行
├── ai.h 39行
├── record.h 45行
├── init_board.h 35行
├── ui.h 62行
├── config.h 170行
├── network.h 186行
├── globals.h 41行
└── type.h 93行
========================================
📈 总计统计
========================================
总代码行数:4,398行
文件类型分布:
• C源文件:3,527行 (80.2%)
• 头文件:871行 (19.8%)
模块代码分布:
• 游戏模式模块:917行 (20.9%)
• AI智能模块:589行 (13.4%)
• 记录系统模块:531行 (12.1%)
• 网络对战模块:426行 (9.7%)
• 配置管理模块:331行 (7.5%)
• 核心游戏模块:269行 (6.1%)
• 用户界面模块:204行 (4.6%)
• 配置参数模块:170行 (3.9%)
• 棋盘初始化模块:118行 (2.7%)
• 类型定义模块:93行 (2.1%)
• 主程序模块:84行 (1.9%)
• 全局变量模块:41行 (0.9%)
========================================
💬 注释统计
========================================
【注释统计】
总注释行数:1,248行
注释覆盖率:30.1%
注释类型分布:
• 函数说明注释:498行 (39.9%)
• 代码逻辑注释:425行 (34.1%)
• 文件头注释:325行 (26.0%)
注释质量分析:
• 文件头注释:每个文件都有详细的文档头
• 函数注释:使用Doxygen格式的完整函数文档
• 行内注释:关键逻辑的解释说明
• 分块注释:代码段落的功能说明
估算注释字数:约8,500-10,000字
注释内容包括:
• 详细的函数参数和返回值说明
• 算法逻辑的中文解释
• 代码块的功能描述
• 重要变量和常量的用途说明
• 网络协议和数据结构的详细文档
========================================
🏆 代码质量评价
========================================
【优秀特点】
✓ 注释覆盖率高:几乎每个函数都有详细文档
✓ 代码结构清晰:模块化设计,职责分离明确
✓ 命名规范:变量和函数名具有良好的可读性
✓ 文档完整:包含完整的API文档和使用说明
✓ 架构合理:网络模块、AI模块、UI模块分离
✓ 跨平台支持:Windows和Linux双平台兼容
【技术亮点】
• 完整的网络对战功能实现
• 智能AI算法与评估系统
• 灵活的配置管理系统
• 详细的游戏记录与复盘功能
• 规范的禁手规则实现
• 实时计时器系统
• 全局变量统一管理
• 跨平台网络通信支持
• 代码架构模块化重构(v7.0新增)
• 配置参数集中化管理(v7.0新增)
• 类型定义标准化(v7.0新增)
【总体评价】
这是一个非常优秀的C语言项目,代码量适中但功能完整,
注释详尽,体现了良好的编程习惯和专业素养!
项目从单一的五子棋游戏发展为支持多种对战模式的完整系统,
包括人机对战、双人对战和网络对战,功能丰富,架构清晰,
是C语言项目开发的优秀范例。
网络对战功能的加入使得项目具备了现代化游戏的特征,
支持实时在线对战,为用户提供了更丰富的游戏体验。
v7.0版本的代码架构重构是项目发展的重要里程碑,
通过配置统一管理、全局变量规范化、类型定义标准化等措施,
大幅提升了代码的可维护性和扩展性,为后续功能开发
奠定了坚实的架构基础。
========================================
📋 项目文件结构
========================================
核心模块:
• main.c/gobang.c - 主程序和核心游戏逻辑
• game_mode.c/h - 游戏模式管理(人机/双人/网络)
• ai.c/h - AI智能算法实现
• network.c/h - 网络对战功能
• record.c/h - 游戏记录与复盘
• ui.c/h - 用户界面管理
• config.c/h - 配置文件管理
• init_board.c/h - 棋盘初始化
• globals.c/h - 全局变量统一管理
• type.h - 数据结构和类型定义集中文件(v7.0新增)
配置文件:
• gobang_config.ini - 游戏配置文件
文档目录:
• MD/ - 项目文档目录
• records/ - 游戏记录存储目录
开发环境:
• .vscode/ - VS Code配置
• .idea/ - IntelliJ IDEA配置
========================================
-87
View File
@@ -1,87 +0,0 @@
/**
* @file 五子棋对战系统
* @brief C语言五子棋多模式对战系统
* @details 支持人机对战、双人对战、网络对战的完整五子棋游戏系统
* @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com)
* @date 2025-07-20
* @version 7.0
* @note
* 1. v7.0架构重构:
* - 🏗️ 代码架构全面重构,实现模块化设计
* - 📋 配置参数统一管理,所有配置集中到config.h
* - 🔧 全局变量规范化,统一在globals模块管理
* - 📝 类型定义标准化,集中在type.h中定义
* - 🌐 网络配置重构,从network.h迁移到config.h
* - 🔄 消除重复定义,提高代码一致性
* - 📚 完善文档体系,新增架构重构指南
* 2. v6.1完善功能:
* - 🌐 完善的网络对战模式,支持服务器/客户端架构
* - 🔗 实时数据同步,支持落子、悔棋、认输等网络功能
* - 🛡️ 网络安全验证和连接状态管理
* - 📡 跨平台网络支持(Windows/Linux
* - 🔧 全局变量统一管理,优化代码结构
* - 📋 宏定义统一管理,消除重复定义
* - 🔄 网络协议优化,改进通信稳定性
* 2. 核心游戏功能:
* - 增加了对禁手规则的支持,防止玩家进行无意义的走法。
* - 新增了游戏计时器功能,限制每回合的思考时间。
* - 添加了复盘功能,支持保存和回顾对局记录。
* - 实现了评分系统,可以对每一步棋进行评分和分析。
* 3. 性能优化:
* - 🚀 优化了AI算法,使用Alpha-Beta剪枝提高搜索效率
* - 🎨 改进了棋盘渲染算法,减少了不必要的重绘操作
* - 💾 增加了内存管理优化,避免内存泄漏问题
* - ⚡ 网络通信优化,支持异步消息处理
* - 🔍 智能评分算法优化,提升AI决策质量
* 4. 用户界面改进:
* - 🎮 美化了游戏界面,增加了更多的视觉效果
* - ⌨️ 改进了用户交互体验,增加了快捷键支持
* - 🔊 添加了音效和背景音乐,提升游戏沉浸感
* - 💬 网络对战聊天界面,支持实时交流
* - 📊 游戏状态显示优化,清晰展示连接状态
* 5. 代码结构优化:
* - 🏗️ 重构了代码架构,提高了代码的可读性和可维护性
* - 📝 增加了详细的注释和文档,便于理解和修改
* - 🧩 采用了模块化设计,各功能模块相对独立
* - 🌍 新增网络模块,完整的网络通信架构
* - 🔧 全局状态统一管理,消除代码重复
* - 📋 配置文件标准化,支持灵活配置
* 6. 异常处理:
* - 🛡️ 增加了输入错误的异常处理机制,确保游戏的稳定性
* - 💡 优化了错误提示信息,帮助用户快速定位问题
* - 🔄 增加了程序崩溃恢复功能,提高游戏的可靠性
* - 🌐 网络连接异常处理,自动重连和超时管理
* - 📡 消息传输错误处理,确保数据完整性
* 7. 文档更新:
* - 📚 更新了README文件,提供详细的安装和使用说明
* - 💬 增加了代码注释,提高代码的可读性
* - 👨‍💻 添加了开发者文档,便于后续的功能扩展
* - 🌐 新增网络对战使用指南和配置说明
* - 🔧 API文档完善,支持二次开发
* 8. 版本控制:
* - 📦 使用Git进行版本控制,便于代码管理和协作开发
* - 🚀 建立了清晰的版本发布流程,确保代码质量
* - 🏷️ v7.0版本更新,代码架构全面重构
- 🏷️ v6.1版本更新,网络功能完善优化
* - 📋 完整的变更日志,追踪功能演进
* 9. 测试:
* - ✅ 进行了全面的功能测试,确保各项功能正常运行
* - 🧪 增加了单元测试,提高代码的可靠性
* - ⚡ 进行了性能测试,优化了程序的运行效率
* - 🌐 网络功能压力测试,确保多人对战稳定性
* - 🔒 安全性测试,验证网络通信安全
* - 🔄 协议兼容性测试,确保通信协议稳定
* 10. 开源协议:
* - 📄 选择了MIT开源协议,允许用户自由使用、修改和分发代码
* - 🤝 欢迎社区贡献,共同完善项目
* 11. 贡献者:
* - 👨‍💻 感谢所有为项目做出贡献的开发者和用户
* - 🌟 特别感谢网络功能开发、测试和优化的贡献者
* 12. 联系信息:
* - 📧 如有问题或建议,请联系开发者:
* - 3364451258@qq.com
* - 15236416560@163.com
* - lhy3364451258@outlook.com
* - 🐛 Bug报告和功能建议欢迎通过邮件反馈
* - 💡 网络对战相关问题请详细描述网络环境和连接状态
*/
+130 -368
View File
@@ -1,13 +1,17 @@
#include "gobang.h"
#include "ai.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// ==================== 辅助函数声明 ====================
static int compare_moves(const void *a, const void *b);
// 防守系数
double defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT;
extern int BOARD_SIZE;
extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
extern int step_count;
extern const int direction[4][2];
extern Step steps[MAX_STEPS];
/**
* @brief 评估一个落子位置的综合得分(结合进攻和防守)
@@ -161,55 +165,53 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi
int best_score = is_maximizing ? -1000000 : 1000000;
// 使用移动排序优化搜索效率
ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE];
int move_count = generate_candidate_moves(candidate_moves, player);
// 限制搜索的候选移动数量以提高性能
int max_candidates = (depth >= 3) ? 15 : 25; // 深度越大,候选移动越少
if (move_count > max_candidates)
// 遍历所有可能落子位置
for (int i = 0; i < BOARD_SIZE; i++)
{
move_count = max_candidates;
}
// 遍历排序后的候选移动
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
// 模拟当前玩家落子
board[i][j] = player;
step_count++;
// 递归搜索(切换玩家和搜索深度)
int current_score = dfs(i, j, (player == AI) ? PLAYER : AI, depth - 1, alpha, beta, !is_maximizing);
// 撤销落子
board[i][j] = EMPTY;
step_count--;
// 极大值玩家(AI)逻辑
if (is_maximizing)
for (int j = 0; j < BOARD_SIZE; j++)
{
best_score = (current_score > best_score) ? current_score : best_score;
alpha = (best_score > alpha) ? best_score : alpha;
// α剪枝
if (beta <= alpha)
if (board[i][j] != EMPTY)
{
break;
continue;
}
// 模拟当前玩家落子
board[i][j] = player;
step_count++;
// 递归搜索(切换玩家和搜索深度)
int current_score = dfs(i, j, (player == AI) ? PLAYER : AI, depth - 1, alpha, beta, !is_maximizing);
// 撤销落子
board[i][j] = EMPTY;
step_count--;
// 极大值玩家(AI)逻辑
if (is_maximizing)
{
best_score = (current_score > best_score) ? current_score : best_score;
alpha = (best_score > alpha) ? best_score : alpha;
// α剪枝
if (beta <= alpha)
{
break;
}
}
// 极小值玩家(人类)逻辑
else
{
best_score = (current_score < best_score) ? current_score : best_score;
beta = (best_score < beta) ? best_score : beta;
// β剪枝
if (beta <= alpha)
{
break;
}
}
}
// 极小值玩家(人类)逻辑
else
if ((is_maximizing && best_score >= beta) || (!is_maximizing && best_score <= alpha))
{
best_score = (current_score < best_score) ? current_score : best_score;
beta = (best_score < beta) ? best_score : beta;
// β剪枝
if (beta <= alpha)
{
break;
}
break; // 提前退出外层循环
}
}
@@ -238,148 +240,7 @@ void ai_move(int depth)
return;
}
// 1. 使用增强的威胁检测系统
ScoredMove candidate_moves[BOARD_SIZE * BOARD_SIZE];
int move_count = generate_candidate_moves(candidate_moves, AI);
// 首先检查是否有直接获胜的机会
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat == THREAT_WIN)
{
// 直接获胜
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 获胜!\n", i + 1, j + 1);
return;
}
}
// 检查是否需要阻止玩家的威胁
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
ThreatLevel player_threat = detect_threat(i, j, PLAYER);
if (player_threat >= THREAT_FOUR)
{
// 必须阻止玩家的四子威胁
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 防守!\n", i + 1, j + 1);
return;
}
}
// 检查是否需要阻止玩家的活三威胁
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
ThreatLevel player_threat = detect_threat(i, j, PLAYER);
if (player_threat == THREAT_THREE)
{
// 阻止玩家的活三
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 阻止活三!\n", i + 1, j + 1);
return;
}
}
// 2. 寻找最佳进攻位置
// 优先考虑能形成威胁的位置
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat >= THREAT_FOUR)
{
// 形成四子威胁
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 形成威胁!\n", i + 1, j + 1);
return;
}
}
// 寻找能形成活三的位置
for (int idx = 0; idx < move_count; idx++)
{
int i = candidate_moves[idx].x;
int j = candidate_moves[idx].y;
ThreatLevel ai_threat = detect_threat(i, j, AI);
if (ai_threat == THREAT_THREE)
{
// 形成活三
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 形成活三!\n", i + 1, j + 1);
return;
}
}
// 3. 如果没有明显的威胁机会,选择评分最高的位置
if (move_count > 0)
{
// candidate_moves已经按分数排序,直接选择第一个
int best_x = candidate_moves[0].x;
int best_y = candidate_moves[0].y;
board[best_x][best_y] = AI;
steps[step_count++] = (Step){AI, best_x, best_y};
printf("AI落子(%d, %d) - 最佳位置!\n", best_x + 1, best_y + 1);
}
else
{
// 备用方案:如果没有候选移动,随机选择一个位置
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
{
if (board[i][j] == EMPTY)
{
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d) - 备用位置!\n", i + 1, j + 1);
return;
}
}
}
}
}
// ==================== AI增强:辅助函数实现 ====================
/**
* @brief 比较函数,用于移动排序(按分数降序)
*/
static int compare_moves(const void *a, const void *b)
{
const ScoredMove *move_a = (const ScoredMove *)a;
const ScoredMove *move_b = (const ScoredMove *)b;
return move_b->score - move_a->score; // 降序排列
}
/**
* @brief 生成候选移动并按评估分数排序
* @param moves 存储候选移动的数组
* @param player 当前玩家
* @return 候选移动数量
*/
int generate_candidate_moves(ScoredMove *moves, int player)
{
int count = 0;
// 1. 首先检查是否需要阻止玩家的四子连棋或三子活棋
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
@@ -389,201 +250,102 @@ int generate_candidate_moves(ScoredMove *moves, int player)
continue;
}
// 只考虑有意义的位置(附近有棋子)
if (step_count > AI_SEARCH_RANGE_THRESHOLD && !is_near_stones(i, j))
// 模拟玩家在此位置落子
board[i][j] = PLAYER;
bool need_block = false;
// 检查四个方向
for (int k = 0; k < 4; k++)
{
DirInfo info = count_specific_direction(i, j, direction[k][0], direction[k][1], PLAYER);
// 如果玩家能形成四子连棋且至少一端开放
if (info.continuous_chess >= 4 && (info.check_start || info.check_end))
{
need_block = true;
break;
}
// 如果玩家能形成三子活棋且两端开放
if (info.continuous_chess == 3 && info.check_start && info.check_end)
{
need_block = true;
break;
}
}
board[i][j] = EMPTY; // 恢复棋盘
if (need_block)
{
// 必须在此位置落子阻止
board[i][j] = AI;
steps[step_count++] = (Step){AI, i, j};
printf("AI落子(%d, %d)\n", i + 1, j + 1);
return;
}
}
}
// 2. 如果没有需要立即阻止的情况,则正常评估
int best_score = -SEARCH_WIN_BONUS;
int best_x = -1, best_y = -1;
// 遍历棋盘所有空位
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
{
if (board[i][j] != EMPTY)
{
continue;
}
// 计算该位置的评估分数
moves[count].x = i;
moves[count].y = j;
// 结合威胁检测和位置评估
ThreatLevel threat = detect_threat(i, j, player);
int base_score = evaluate_move(i, j);
// 根据威胁等级调整分数
switch (threat)
// 只考虑已有棋子附近(AI_NEARBY_RANGE格范围内)
bool has_nearby_stone = false;
for (int di = -AI_NEARBY_RANGE; di <= AI_NEARBY_RANGE; di++)
{
case THREAT_WIN:
moves[count].score = base_score + 10000;
break;
case THREAT_FOUR:
moves[count].score = base_score + 5000;
break;
case THREAT_THREE:
moves[count].score = base_score + 2000;
break;
case THREAT_DOUBLE:
moves[count].score = base_score + 1000;
break;
case THREAT_POTENTIAL:
moves[count].score = base_score + 500;
break;
default:
moves[count].score = base_score;
break;
}
count++;
}
}
// 按分数降序排序
qsort(moves, count, sizeof(ScoredMove), compare_moves);
return count;
}
/**
* @brief 检查位置是否在已有棋子附近
* @param x, y 要检查的位置
* @return 如果附近有棋子返回true
*/
bool is_near_stones(int x, int y)
{
for (int di = -AI_NEARBY_RANGE; di <= AI_NEARBY_RANGE; di++)
{
for (int dj = -AI_NEARBY_RANGE; dj <= AI_NEARBY_RANGE; dj++)
{
int ni = x + di;
int nj = y + dj;
if (ni >= 0 && ni < BOARD_SIZE && nj >= 0 && nj < BOARD_SIZE)
{
if (board[ni][nj] != EMPTY)
for (int dj = -AI_NEARBY_RANGE; dj <= AI_NEARBY_RANGE; dj++)
{
return true;
int ni = i + di;
int nj = j + dj;
if (ni >= 0 && ni < BOARD_SIZE && nj >= 0 && nj < BOARD_SIZE)
{
if (board[ni][nj] != EMPTY)
{
has_nearby_stone = true;
break;
}
}
}
if (has_nearby_stone)
{
break;
}
}
}
}
return false;
}
/**
* @brief 检测在指定位置落子的威胁等级
* @param x, y 落子位置
* @param player 落子玩家
* @return 威胁等级
*/
ThreatLevel detect_threat(int x, int y, int player)
{
// 模拟落子
board[x][y] = player;
ThreatLevel max_threat = THREAT_NONE;
int threat_count = 0;
// 检查四个方向
for (int k = 0; k < 4; k++)
{
DirInfo info = count_specific_direction(x, y, direction[k][0], direction[k][1], player);
ThreatLevel current_threat = THREAT_NONE;
// 检查是否形成五子连珠(获胜)
if (info.continuous_chess >= 5)
{
current_threat = THREAT_WIN;
}
// 检查是否形成活四或冲四
else if (info.continuous_chess == 4)
{
if (info.check_start && info.check_end)
if (!has_nearby_stone && step_count > AI_SEARCH_RANGE_THRESHOLD)
{
current_threat = THREAT_FOUR; // 活四
continue;
}
else if (info.check_start || info.check_end)
// 使用评估函数获取综合得分
int current_score = evaluate_move(i, j);
// 更新最佳位置
if (current_score > best_score)
{
current_threat = THREAT_FOUR; // 冲四
best_score = current_score;
best_x = i;
best_y = j;
}
}
// 检查是否形成活三
else if (info.continuous_chess == 3 && info.check_start && info.check_end)
{
current_threat = THREAT_THREE;
}
// 检查潜在威胁
else if (info.continuous_chess >= 2)
{
current_threat = THREAT_POTENTIAL;
}
if (current_threat > max_threat)
{
max_threat = current_threat;
}
if (current_threat >= THREAT_THREE)
{
threat_count++;
}
}
// 恢复棋盘
board[x][y] = EMPTY;
// 如果有多个威胁,提升威胁等级
if (threat_count >= 2 && max_threat >= THREAT_THREE)
// 执行最佳落子
if (best_x != -1 && best_y != -1)
{
max_threat = THREAT_DOUBLE;
board[best_x][best_y] = AI;
steps[step_count++] = (Step){AI, best_x, best_y};
printf("AI落子(%d, %d)\n", best_x + 1, best_y + 1);
}
return max_threat;
}
/**
* @brief 计算指定方向的威胁数量
* @param x, y 起始位置
* @param dx, dy 方向向量
* @param player 玩家
* @return 威胁数量
*/
int count_threats_in_direction(int x, int y, int dx, int dy, int player)
{
int threats = 0;
// 向前搜索
for (int i = 1; i < 5; i++)
{
int nx = x + i * dx;
int ny = y + i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE)
{
break;
}
if (board[nx][ny] == player)
{
threats++;
}
else if (board[nx][ny] != EMPTY)
{
break;
}
}
// 向后搜索
for (int i = 1; i < 5; i++)
{
int nx = x - i * dx;
int ny = y - i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE)
{
break;
}
if (board[nx][ny] == player)
{
threats++;
}
else if (board[nx][ny] != EMPTY)
{
break;
}
}
return threats;
}
+3 -43
View File
@@ -1,16 +1,10 @@
/**
* @file ai.h
* @note 本文件定义了AI模块的函数和变量
* @note 包括:
* 1. 评估一个落子位置的综合得分(结合进攻和防守)
* 2. 评估指定位置的价值
* 3. 评估棋盘价值
*/
#ifndef AI_H
#define AI_H
#include "gobang.h"
#include "type.h"
// 防守系数
extern double defense_coefficient;
/**
* @brief 评估一个落子位置的综合得分(结合进攻和防守)
@@ -45,38 +39,4 @@ int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximi
*/
void ai_move(int depth);
// ==================== AI增强:新增函数声明 ====================
/**
* @brief 生成候选移动并按评估分数排序
* @param moves 存储候选移动的数组
* @param player 当前玩家
* @return 候选移动数量
*/
int generate_candidate_moves(ScoredMove *moves, int player);
/**
* @brief 检查位置是否在已有棋子附近
* @param x, y 要检查的位置
* @return 如果附近有棋子返回true
*/
bool is_near_stones(int x, int y);
/**
* @brief 检测在指定位置落子的威胁等级
* @param x, y 落子位置
* @param player 落子玩家
* @return 威胁等级
*/
ThreatLevel detect_threat(int x, int y, int player);
/**
* @brief 计算指定方向的威胁数量
* @param x, y 起始位置
* @param dx, dy 方向向量
* @param player 玩家
* @return 威胁数量
*/
int count_threats_in_direction(int x, int y, int dx, int dy, int player);
#endif // AI_H
+4 -80
View File
@@ -1,11 +1,12 @@
#include "config.h"
#include "ui.h"
#include "game_mode.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 配置文件路径
#define CONFIG_FILE "gobang_config.ini"
/**
* @brief 加载游戏配置
*/
@@ -46,22 +47,6 @@ void load_game_config()
{
time_limit = atoi(line + 11);
}
else if (strncmp(line, "NETWORK_PORT=", 13) == 0)
{
int port = atoi(line + 13);
if (port >= MIN_NETWORK_PORT && port <= MAX_NETWORK_PORT)
{
network_port = port;
}
}
else if (strncmp(line, "NETWORK_TIMEOUT=", 16) == 0)
{
int timeout = atoi(line + 16);
if (timeout > 0)
{
network_timeout = timeout;
}
}
else if (strncmp(line, "AI_DIFFICULTY=", 14) == 0)
{
int difficulty = atoi(line + 14);
@@ -98,10 +83,6 @@ void save_game_config()
fprintf(file, "USE_TIMER=%d\n", use_timer);
fprintf(file, "\n# 时间限制 (分钟)\n");
fprintf(file, "TIME_LIMIT=%d\n", time_limit);
fprintf(file, "\n# 网络端口 (范围: %d-%d)\n", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
fprintf(file, "NETWORK_PORT=%d\n", network_port);
fprintf(file, "\n# 网络超时时间 (毫秒)\n");
fprintf(file, "NETWORK_TIMEOUT=%d\n", network_timeout);
fclose(file);
printf("配置保存完成\n");
@@ -116,8 +97,6 @@ void reset_to_default_config()
use_forbidden_moves = DEFAULT_USE_FORBIDDEN_MOVES;
use_timer = DEFAULT_USE_TIMER;
time_limit = DEFAULT_TIME_LIMIT;
network_port = DEFAULT_NETWORK_PORT;
network_timeout = NETWORK_TIMEOUT_MS;
printf("已重置为默认配置\n");
}
@@ -135,8 +114,6 @@ void display_current_config()
{
printf("时间限制: %d 分钟\n", time_limit / 60);
}
printf("网络端口: %d\n", network_port);
printf("网络超时: %d 毫秒\n", network_timeout);
printf("=====================\n");
}
@@ -229,56 +206,6 @@ void config_timer()
}
}
/**
* @brief 配置网络参数
*/
void config_network()
{
printf("\n===== 网络配置 =====\n");
printf("当前网络端口: %d\n", network_port);
printf("当前网络超时: %d 毫秒\n", network_timeout);
printf("\n请输入新的网络端口 (%d-%d): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
int new_port;
if (scanf("%d", &new_port) == 1)
{
if (new_port >= MIN_NETWORK_PORT && new_port <= MAX_NETWORK_PORT)
{
network_port = new_port;
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');
}
}
/**
* @brief 配置管理主菜单
*/
@@ -313,12 +240,9 @@ void config_management_menu()
config_timer();
break;
case 4:
config_network();
break;
case 5:
printf("AI难度设置功能开发中...\n");
break;
case 6:
case 5:
save_game_config();
return;
default:
+53 -87
View File
@@ -1,6 +1,12 @@
/**
* @file config.h
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @brief
* @version 5.0
* @date 2025-07-10
*
* @copyright Copyright (c) 2025
*
* @note 便
*/
@@ -13,23 +19,18 @@
#define DEFAULT_BOARD_SIZE 15 // 默认棋盘尺寸
#define MAX_STEPS (MAX_BOARD_SIZE * MAX_BOARD_SIZE) // 游戏最大步数
//---------- 游戏模式参数 ----------//
#define GAME_MODE_AI 1 // 人机对战模式
#define GAME_MODE_PVP 2 // 双人对战模式
#define GAME_MODE_NETWORK 3 // 网络对战模式
//---------- 玩家标识参数 ----------//
#define EMPTY 0 // 棋盘空位标识
#define PLAYER 1 // 玩家标识 (用于人机对战模式)
#define AI 2 // AI标识 (用于人机对战模式)
#define PLAYER1 1 // 玩家1标识 (用于双人对战模式)
#define PLAYER2 2 // 玩家2标识 (用于双人对战模式)
#define EMPTY 0 // 棋盘空位标识
#define PLAYER 1 // 玩家标识 (用于人机对战模式)
#define AI 2 // AI标识 (用于人机对战模式)
#define PLAYER1 1 // 玩家1标识 (用于双人对战模式)
#define PLAYER2 2 // 玩家2标识 (用于双人对战模式)
//---------- 特殊输入命令 ----------//
#define INPUT_UNDO -1 // 悔棋
#define INPUT_SAVE -2 // 保存
#define INPUT_EXIT -3 // 退出
#define INPUT_SURRENDER -4 // 认输
#define INPUT_UNDO -1 // 悔棋
#define INPUT_SAVE -2 // 保存
#define INPUT_EXIT -3 // 退出
#define INPUT_SURRENDER -4 // 认输
//---------- 游戏设置默认值 ----------//
#define DEFAULT_USE_FORBIDDEN_MOVES false // 默认不启用禁手规则
@@ -37,89 +38,59 @@
#define DEFAULT_TIME_LIMIT 30 // 默认时间限制为30秒(内部存储)
//---------- AI参数 ----------//
#define DEFAULT_AI_DEPTH 5 // 默认AI搜索深度(从3提升到5
#define DEFAULT_DEFENSE_COEFFICIENT 1.5 // 默认防守系数(从1.2提升到1.5
//---------- 网络参数 ----------//
#define DEFAULT_NETWORK_PORT 8888 // 默认网络端口
#define MIN_NETWORK_PORT 1024 // 最小网络端口
#define MAX_NETWORK_PORT 65535 // 最大网络端口
#define NETWORK_TIMEOUT_MS 5000 // 网络超时时间(毫秒)
#define NETWORK_BUFFER_SIZE 1024 // 网络缓冲区大小
// 网络配置
#define DEFAULT_PORT 8888 // 默认端口(与DEFAULT_NETWORK_PORT保持一致)
#define BUFFER_SIZE 1024 // 缓冲区大小(与NETWORK_BUFFER_SIZE保持一致)
#define MAX_IP_LENGTH 16 // 最大IP地址长度
// 网络消息类型
#define MSG_MOVE 1 // 落子消息
#define MSG_CHAT 2 // 聊天消息
#define MSG_SURRENDER 3 // 认输消息
#define MSG_UNDO_REQUEST 4 // 悔棋请求
#define MSG_UNDO_RESPONSE 5 // 悔棋回应
#define MSG_GAME_START 6 // 游戏开始
#define MSG_GAME_END 7 // 游戏结束
#define MSG_HEARTBEAT 8 // 心跳包
#define MSG_DISCONNECT 9 // 断线消息
#define DEFAULT_AI_DEPTH 3 // 默认AI搜索深度
#define DEFAULT_DEFENSE_COEFFICIENT 1.2 // 默认防守系数
//---------- 评分参数 ----------//
// 棋型评分 - 用于calculate_step_score函数
#define SCORE_FIVE 0 // 五连
#define SCORE_LIVE_FOUR 2000 // 活四
#define SCORE_RUSH_FOUR 1000 // 冲四
#define SCORE_DEAD_FOUR 300 // 死四
#define SCORE_LIVE_THREE 500 // 活三
#define SCORE_SLEEP_THREE 200 // 眠三
#define SCORE_DEAD_THREE 80 // 死三
#define SCORE_LIVE_TWO 100 // 活二
#define SCORE_SLEEP_TWO 40 // 眠二
#define SCORE_DEAD_TWO 15 // 死二
#define SCORE_LIVE_ONE 15 // 开放单子
#define SCORE_HALF_ONE 8 // 半开放单子
#define SCORE_DEAD_ONE 2 // 封闭单子
#define SCORE_FIVE 0 // 五连
#define SCORE_LIVE_FOUR 2000 // 活四
#define SCORE_RUSH_FOUR 1000 // 冲四
#define SCORE_DEAD_FOUR 300 // 死四
#define SCORE_LIVE_THREE 500 // 活三
#define SCORE_SLEEP_THREE 200 // 眠三
#define SCORE_DEAD_THREE 80 // 死三
#define SCORE_LIVE_TWO 100 // 活二
#define SCORE_SLEEP_TWO 40 // 眠二
#define SCORE_DEAD_TWO 15 // 死二
#define SCORE_LIVE_ONE 15 // 开放单子
#define SCORE_HALF_ONE 8 // 半开放单子
#define SCORE_DEAD_ONE 2 // 封闭单子
// 位置奖励系数
#define POSITION_BONUS_FACTOR 10 // 位置奖励因子
#define POSITION_BONUS_FACTOR 10 // 位置奖励因子
// AI评估参数 - 用于evaluate_pos函数
#define AI_SCORE_FIVE 1000000 // AI评估-五连
#define AI_SCORE_LIVE_FOUR 100000 // AI评估-活四
#define AI_SCORE_RUSH_FOUR 10000 // AI评估-冲四
#define AI_SCORE_DEAD_FOUR 500 // AI评估-死四
#define AI_SCORE_LIVE_THREE 5000 // AI评估-活三
#define AI_SCORE_SLEEP_THREE 1000 // AI评估-眠三
#define AI_SCORE_DEAD_THREE 50 // AI评估-死三
#define AI_SCORE_LIVE_TWO 500 // AI评估-活二
#define AI_SCORE_SLEEP_TWO 100 // AI评估-眠二
#define AI_SCORE_DEAD_TWO 10 // AI评估-死二
#define AI_SCORE_LIVE_ONE 50 // AI评估-开放单子
#define AI_SCORE_HALF_ONE 10 // AI评估-半开放单子
#define AI_SCORE_DEAD_ONE 1 // AI评估-封闭单子
#define AI_SCORE_FIVE 1000000 // AI评估-五连
#define AI_SCORE_LIVE_FOUR 100000 // AI评估-活四
#define AI_SCORE_RUSH_FOUR 10000 // AI评估-冲四
#define AI_SCORE_DEAD_FOUR 500 // AI评估-死四
#define AI_SCORE_LIVE_THREE 5000 // AI评估-活三
#define AI_SCORE_SLEEP_THREE 1000 // AI评估-眠三
#define AI_SCORE_DEAD_THREE 50 // AI评估-死三
#define AI_SCORE_LIVE_TWO 500 // AI评估-活二
#define AI_SCORE_SLEEP_TWO 100 // AI评估-眠二
#define AI_SCORE_DEAD_TWO 10 // AI评估-死二
#define AI_SCORE_LIVE_ONE 50 // AI评估-开放单子
#define AI_SCORE_HALF_ONE 10 // AI评估-半开放单子
#define AI_SCORE_DEAD_ONE 1 // AI评估-封闭单子
// AI位置奖励系数
#define AI_POSITION_BONUS_FACTOR 50 // AI位置奖励因子
#define AI_POSITION_BONUS_FACTOR 50 // AI位置奖励因子
// 搜索算法参数
#define SEARCH_MAX_SCORE 1000000 // 搜索最大分数
#define SEARCH_WIN_BONUS 1000000 // 获胜奖励分数
#define AI_NEARBY_RANGE 3 // AI搜索的邻近范围
#define AI_SEARCH_RANGE_THRESHOLD 8 // AI开始限制搜索范围的步数阈值
// 组合棋型评分 - 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 // 潜在五连
#define SEARCH_MAX_SCORE 1000000 // 搜索最大分数
#define SEARCH_WIN_BONUS 1000000 // 获胜奖励分数
#define AI_NEARBY_RANGE 2 // AI搜索的邻近范围
#define AI_SEARCH_RANGE_THRESHOLD 10 // AI开始限制搜索范围的步数阈值
// 评分权重参数
#define TIME_WEIGHT_FACTOR 0.5 // 时间权重因子
#define WIN_BONUS 2000 // 胜利奖励分数
#define TIME_WEIGHT_FACTOR 0.5 // 时间权重因子
#define WIN_BONUS 2000 // 胜利奖励分数
// 文件路径参数
#define RECORDS_DIR "records" // 记录文件目录
#define CONFIG_FILE "gobang_config.ini" // 配置文件路径
#define MAX_PATH_LENGTH 256 // 最大路径长度
#define RECORDS_DIR "records" // 记录文件目录
#define MAX_PATH_LENGTH 256 // 最大路径长度
//---------- 配置管理函数声明 ----------//
/**
@@ -157,11 +128,6 @@ void config_forbidden_moves();
*/
void config_timer();
/**
* @brief
*/
void config_network();
/**
* @brief
*/
+36 -526
View File
@@ -4,16 +4,13 @@
#include "ai.h"
#include "record.h"
#include "config.h"
#include "network.h"
#include "ui.h"
#include "globals.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <ctype.h>
// 全局变量现在在globals.c中定义
// 引用record.h中定义的全局变量
extern int scores_calculated;
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
@@ -96,7 +93,7 @@ bool parse_player_input(int *x, int *y)
{
printf("无法悔棋!\n");
}
return 0; // 特殊命令已处理
return false; // 特殊命令
}
else if (*x == INPUT_SAVE)
{
@@ -108,10 +105,10 @@ bool parse_player_input(int *x, int *y)
// ... 处理退出 ...
return false; // 特殊命令
}
printf("无效输入,请输入两个数字坐标。\n");
printf("无效输入,请输入两个数字坐标。");
while (getchar() != '\n')
;
return 0; // 无效输入
return false; // 无效输入
}
}
else
@@ -139,7 +136,7 @@ bool parse_player_input(int *x, int *y)
if (confirm)
{
printf("玩家选择认输!\n");
return 1; // 正常回合完成 // 返回认输命令
return true; // 返回认输命令
}
else
{
@@ -147,121 +144,7 @@ bool parse_player_input(int *x, int *y)
return false; // 取消认输
}
}
printf("无效输入,请输入数字坐标、'r'悔棋或's'认输。\n");
return 0; // 无效输入
}
return 1; // 有效坐标
}
/**
* @brief
* @param x
* @param y
* @return true
* @return false
*/
bool parse_network_player_input(int *x, int *y)
{
char input[10];
while (1)
{
if (_kbhit())
{
scanf("%s", input);
break;
}
Sleep(100); // 短暂延迟以防止CPU占用过高
}
if (sscanf(input, "%d", x) == 1)
{
// 成功解析第一个数字,现在解析第二个
if (scanf("%d", y) != 1)
{
printf("无效输入,请输入两个数字坐标。\n");
while (getchar() != '\n')
;
return false; // 无效输入
}
}
else
{
// sscanf失败,检查特殊命令
if (input[0] == 'r' || input[0] == 'R')
{
int steps_to_undo;
steps_to_undo = get_integer_input("请输入要悔棋的步数(双方各退步数相同): ", 1, step_count / 2);
printf("发送悔棋请求给对方...\n");
if (send_undo_request(steps_to_undo))
{
printf("悔棋请求已发送,等待对方回应...\n");
// 等待对方回应
NetworkMessage msg;
time_t start_time = time(NULL);
while (difftime(time(NULL), start_time) < 30) // 30秒超时
{
if (receive_network_message(&msg, 1000))
{
if (msg.type == MSG_UNDO_RESPONSE && msg.x == steps_to_undo)
{
if (msg.y == 1) // 对方同意
{
if (return_move(steps_to_undo * 2))
{
printf("对方同意悔棋,双方各退 %d 步!\n", steps_to_undo);
print_board();
return 2; // 悔棋成功,需要重新开始回合
}
else
{
printf("悔棋失败!\n");
return 0; // 悔棋失败,继续当前回合
}
}
else // 对方拒绝
{
printf("对方拒绝了悔棋请求。\n");
return 0; // 悔棋被拒绝,继续当前回合
}
}
}
if (!is_network_connected())
{
printf("网络连接断开\n");
return 0;
}
}
printf("悔棋请求超时,对方未回应。\n");
}
else
{
printf("发送悔棋请求失败!\n");
}
return 0; // 特殊命令已处理
}
else if (input[0] == 's' || input[0] == 'S')
{
*x = INPUT_SURRENDER;
int confirm = get_integer_input("确认认输?(1:是/0:否): ", 0, 1);
if (confirm)
{
printf("你选择认输!\n");
*x = INPUT_SURRENDER;
return -1; // 返回认输命令
}
else
{
printf("取消认输!\n");
return 0; // 取消认输
}
}
printf("无效输入,请输入数字坐标、'r'悔棋或's'认输。\n");
printf("无效输入,请输入数字坐标、'r'悔棋或's'认输。");
return false; // 无效输入
}
return true; // 有效坐标
@@ -281,52 +164,44 @@ bool handle_player_turn(int current_player)
time(&start_time);
}
printf("\n玩家%d, 请输入落子坐标(行 列,1~%d),或输入R/r悔棋,S/s认输:", current_player, BOARD_SIZE);
while (1)
{
printf("\n玩家%d, 请输入落子坐标(行 列,1~%d),或输入R/r悔棋,S/s认输:", current_player, BOARD_SIZE);
bool input_received = false;
while (!input_received)
if (use_timer)
{
if (use_timer)
time(&end_time);
if (difftime(end_time, start_time) > time_limit)
{
time(&end_time);
if (difftime(end_time, start_time) > time_limit)
{
printf("\n玩家%d超时, 对方获胜!\n", current_player);
return false; // Timeout
}
}
if (parse_player_input(&x, &y))
{
if (x == INPUT_SURRENDER)
{
printf("\n玩家%d选择认输,对方获胜!\n", current_player);
return false; // 游戏结束,认输
}
input_received = true;
}
else
{
// 已处理特殊命令或无效输入,继续循环
return true;
printf("\n玩家%d超时, 对方获胜!\n", current_player);
return false; // Timeout
}
}
x--;
y--;
if (player_move(x, y, current_player))
if (parse_player_input(&x, &y))
{
break; // 成功落子,跳出循环
if (x == INPUT_SURRENDER)
{
printf("\n玩家%d选择认输,对方获胜!\n", current_player);
return false; // 游戏结束,认输
}
break; // 收到有效输入
}
else
{
printf("坐标无效!请重新输入。\n");
// 继续循环,重新输入坐标
// 已处理特殊命令或无效输入,继续循环
return true;
}
}
x--;
y--;
if (!player_move(x, y, current_player))
{
printf("坐标无效!请重新输入。\n");
return true; // 坐标无效,但回合继续
}
print_board();
if (check_win(x, y, current_player))
@@ -424,8 +299,8 @@ void run_ai_game()
}
}
printf("===== 游戏结束 =====\n");
review_process(GAME_MODE_AI); // AI对战模式
handle_save_record(GAME_MODE_AI); // AI对战模式
review_process(1); // 1 for AI mode
handle_save_record(1); // 1 for AI mode
}
/**
@@ -462,8 +337,8 @@ void run_pvp_game()
}
}
printf("===== 游戏结束 =====\n");
review_process(GAME_MODE_PVP); // 双人对战模式
handle_save_record(GAME_MODE_PVP); // 双人对战模式
review_process(2); // 2 for PvP mode
handle_save_record(2); // 2 for PvP mode
}
/**
@@ -550,368 +425,3 @@ void run_review_mode()
printf("加载复盘文件失败!可能是旧版本文件格式或文件损坏\n");
}
}
/**
* @brief
*/
void run_network_game()
{
// 重置评分计算标志
scores_calculated = 0;
// 初始化网络模块
if (!init_network())
{
printf("网络初始化失败!\n");
pause_for_input("按任意键返回主菜单...");
return;
}
printf("=== 网络对战模式 ===\n");
printf("1. 创建房间(作为主机)\n");
printf("2. 加入房间(连接到主机)\n");
int choice = get_integer_input("请选择模式(1-2): ", 1, 2);
bool connection_success = false;
if (choice == 1)
{
// 服务器模式
int port = get_integer_input("请输入监听端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
if (port == 0) port = network_port;
printf("\n正在创建房间...\n");
connection_success = create_server(port);
}
else
{
// 客户端模式
char ip[MAX_IP_LENGTH];
// 循环直到输入有效的IP地址或用户选择退出
while (1)
{
printf("请输入服务器IP地址 (输入'exit'退出): ");
if (scanf("%s", ip) != 1)
{
printf("输入错误,请重新输入。\n");
// 清除输入缓冲区
while (getchar() != '\n');
continue;
}
// 检查是否要退出
if (strcmp(ip, "exit") == 0 || strcmp(ip, "EXIT") == 0)
{
printf("取消连接,返回主菜单。\n");
cleanup_network();
pause_for_input("按任意键返回主菜单...");
return;
}
// 简单的IP地址格式验证
if (strlen(ip) < 7 || strlen(ip) > 15)
{
printf("IP地址格式错误!请输入有效的IP地址(如:192.168.1.100\n");
continue;
}
// 检查IP地址是否包含有效字符
bool valid_ip = true;
for (int i = 0; i < strlen(ip); i++)
{
if (!(isdigit(ip[i]) || ip[i] == '.'))
{
valid_ip = false;
break;
}
}
if (!valid_ip)
{
printf("IP地址格式错误!只能包含数字和点号。\n");
continue;
}
// 检查点号数量
int dot_count = 0;
for (int i = 0; i < strlen(ip); i++)
{
if (ip[i] == '.') dot_count++;
}
if (dot_count != 3)
{
printf("IP地址格式错误!应包含3个点号(如:192.168.1.100\n");
continue;
}
printf("输入的IP地址: %s\n", ip);
int confirm = get_integer_input("确认连接到此IP?(1:是/0:否,重新输入): ", 0, 1);
if (confirm)
{
break; // 确认IP地址,跳出循环
}
// 如果选择否,继续循环重新输入
}
int port = get_integer_input("请输入服务器端口(默认8888): ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
if (port == 0) port = network_port;
printf("\n正在连接到服务器 %s:%d...\n", ip, port);
connection_success = connect_to_server(ip, port);
}
if (!connection_success)
{
printf("网络连接失败!\n");
cleanup_network();
pause_for_input("按任意键返回主菜单...");
return;
}
printf("\n网络连接成功!游戏即将开始...\n");
printf("你是玩家%d%s先手\n",
network_state.local_player_id,
network_state.local_player_id == PLAYER1 ? "" : "对方");
// 开始网络游戏
empty_board();
print_board();
if (network_game_loop())
{
printf("===== 游戏结束 =====\n");
review_process(GAME_MODE_NETWORK); // 网络对战模式的复盘
handle_save_record(GAME_MODE_NETWORK); // 保存为网络对战模式记录
}
else
{
printf("游戏因网络错误而结束\n");
}
// 清理网络连接
disconnect_network();
pause_for_input("按任意键返回主菜单...");
}
/**
* @brief
*/
bool handle_network_player_turn(int current_player, bool is_local_turn)
{
if (is_local_turn)
{
// 本地玩家回合
int x, y;
time_t start_time, end_time;
if (use_timer)
{
time(&start_time);
}
while (1)
{
printf("\n轮到你了,请输入落子坐标(行 列,1~%d),或输入R/r悔棋,S/s认输: ", BOARD_SIZE);
bool input_received = false;
while (!input_received)
{
if (use_timer)
{
time(&end_time);
if (difftime(end_time, start_time) > time_limit)
{
printf("\n你超时了,对方获胜!\n");
send_surrender(); // 发送认输消息
return 0; // 游戏结束
}
}
int parse_result = parse_network_player_input(&x, &y);
if (parse_result == 1) // 有效坐标输入
{
input_received = true;
}
else if (parse_result == -1) // 认输命令
{
printf("\n你选择认输,对方获胜!\n");
send_surrender();
return 0; // 游戏结束
}
else if (parse_result == 2) // 悔棋成功
{
return 2; // 悔棋发生,需要重新开始回合
}
else // parse_result == 0, 特殊命令已处理或无效输入
{
// 继续等待输入
continue;
}
}
x--; y--; // 转换为0-based坐标
if (player_move(x, y, current_player))
{
break; // 成功落子,跳出循环
}
else
{
printf("坐标无效!请重新输入。\n");
// 继续循环,重新输入坐标
}
}
// 发送落子消息
if (!send_move(x, y, current_player))
{
printf("发送落子消息失败!\n");
return 0; // 游戏结束
}
print_board();
if (check_win(x, y, current_player))
{
printf("\n你获胜了!\n");
return 0; // 游戏结束
}
}
else
{
// 等待对方落子
printf("\n等待对方落子...\n");
NetworkMessage msg;
time_t start_time = time(NULL);
while (1)
{
if (receive_network_message(&msg, 1000))
{
// 1秒超时
if (msg.type == MSG_MOVE && msg.player_id == current_player)
{
// 收到落子消息
if (!player_move(msg.x, msg.y, current_player))
{
printf("收到无效的落子坐标!\n");
return 0; // 游戏结束
}
printf("对方落子: (%d, %d)\n", msg.x + 1, msg.y + 1);
print_board();
if (check_win(msg.x, msg.y, current_player))
{
printf("\n对方获胜!\n");
return 0; // 游戏结束
}
break;
}
else if (msg.type == MSG_SURRENDER)
{
printf("\n对方认输,你获胜了!\n");
return 0; // 游戏结束
}
else if (msg.type == MSG_DISCONNECT)
{
printf("\n对方已断开连接\n");
return 0; // 游戏结束
}
else if (msg.type == MSG_CHAT)
{
printf("[对方]: %s\n", msg.message);
}
else if (msg.type == MSG_UNDO_REQUEST)
{
int steps = msg.x;
printf("\n对方请求悔棋 %d 步,是否同意?(1:同意/0:拒绝): ", steps);
int response = get_integer_input("", 0, 1);
if (response && return_move(steps * 2))
{
printf("同意悔棋,双方各退 %d 步\n", steps);
send_undo_response(true, steps);
print_board();
// 悔棋后需要重新开始当前回合,不改变current_player
return 2; // 悔棋发生,需要重新开始回合
}
else
{
printf("拒绝悔棋\n");
send_undo_response(false, steps);
// 继续等待对方落子
}
}
}
// 检查超时
if (use_timer && difftime(time(NULL), start_time) > time_limit)
{
printf("\n对方超时,你获胜!\n");
return 0; // 游戏结束
}
// 检查网络连接
if (!is_network_connected())
{
printf("\n网络连接断开\n");
return 0; // 游戏结束
}
}
}
return 1; // 正常回合完成
}
/**
* @brief
*/
bool network_game_loop()
{
int current_player = PLAYER1; // 总是从玩家1开始
while (1)
{
bool is_local_turn = (current_player == network_state.local_player_id);
int turn_result = handle_network_player_turn(current_player, is_local_turn);
if (turn_result == 0) // 游戏结束
{
return true;
}
else if (turn_result == 2) // 悔棋发生,重新开始当前回合
{
continue; // 不切换玩家,重新开始当前回合
}
// 检查平局
if (step_count == BOARD_SIZE * BOARD_SIZE)
{
printf("\n平局!\n");
return true;
}
// 切换玩家
current_player = (current_player == PLAYER1) ? PLAYER2 : PLAYER1;
// 检查网络连接
if (!is_network_connected())
{
printf("\n网络连接断开\n");
return false;
}
}
return true;
}
+14 -35
View File
@@ -1,18 +1,28 @@
/**
* @file game_mode.h
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @brief
* @note
* @version 4.0
* @date 2025-07-02
*
* @copyright Copyright (c) 2025
*
* @note
* 1. AI对战模式
* 2.
* 3.
* 4.
* 3.
*/
#ifndef GAME_MODE_H
#define GAME_MODE_H
#include "gobang.h"
#include "config.h"
// 特殊输入命令
#define INPUT_UNDO -1 // 悔棋
#define INPUT_SAVE -2 // 保存
#define INPUT_EXIT -3 // 退出
#define INPUT_SURRENDER -4 // 认输
/**
* @brief
@@ -34,15 +44,6 @@ int get_integer_input(const char *prompt, int min, int max);
*/
bool parse_player_input(int *x, int *y);
/**
* @brief
* @param x
* @param y
* @return true
* @return false
*/
bool parse_network_player_input(int *x, int *y);
/**
* @brief AI回合
*
@@ -68,26 +69,4 @@ void run_pvp_game();
*/
void run_review_mode();
/**
* @brief
* 线
*/
void run_network_game();
/**
* @brief
* @param current_player
* @param is_local_turn
* @return true
* @return false
*/
bool handle_network_player_turn(int current_player, bool is_local_turn);
/**
* @brief
* @return true
* @return false 退
*/
bool network_game_loop();
#endif // GAME_MODE_H
-37
View File
@@ -1,37 +0,0 @@
/**
* @file globals.c
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@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] = "平局或未完成"; // 存储胜负信息
-41
View File
@@ -1,41 +0,0 @@
/**
* @file globals.h
* @brief
* @note
*/
#ifndef GLOBALS_H
#define GLOBALS_H
#include "type.h"
#include "gobang.h"
#include "network.h"
#include <stdbool.h>
// ==================== 游戏核心变量 ====================
extern int BOARD_SIZE; // 当前实际使用的棋盘尺寸
extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE]; // 棋盘状态存储数组
extern Step steps[MAX_STEPS]; // 存储所有落子步骤的数组
extern const int direction[4][2]; // 四个方向:向下、向右、右下、左下
extern int step_count; // 当前步数计数器
// ==================== 游戏配置变量 ====================
extern bool use_forbidden_moves; // 是否启用禁手规则的标志
extern int use_timer; // 是否启用计时器的标志
extern int time_limit; // 每回合的时间限制(秒,内部存储)
extern int network_port; // 网络端口
extern int network_timeout; // 网络超时时间
// ==================== AI相关变量 ====================
extern double defense_coefficient; // 防守系数
// ==================== 网络相关变量 ====================
extern NetworkGameState network_state; // 网络游戏状态
// ==================== 记录相关变量 ====================
extern int player1_final_score; // 玩家1最终得分
extern int player2_final_score; // 玩家2最终得分
extern int scores_calculated; // 评分计算标志
extern char winner_info[50]; // 存储胜负信息
#endif // GLOBALS_H
+10 -1
View File
@@ -4,11 +4,20 @@
#include "ai.h"
#include "record.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.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; // 每回合的时间限制(秒)
/**
* @brief (x, y)
* @param x (0-base)
+43 -8
View File
@@ -1,23 +1,58 @@
/**
* @file gobang.h
* @brief
* @note
* AI决策等功能
*/
#ifndef GO_BANG_H
#define GO_BANG_H
#include <stdio.h>
#include <stdbool.h>
#include "config.h"
#include "type.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
// 宏定义
#define MAX_BOARD_SIZE 25 // 支持的最大棋盘尺寸
#define PLAYER 1 // 玩家标识 (用于人机对战模式)
#define AI 2 // AI标识 (用于人机对战模式)
#define PLAYER1 1 // 玩家1标识 (用于双人对战模式)
#define PLAYER2 2 // 玩家2标识 (用于双人对战模式)
#define EMPTY 0 // 棋盘空位标识
#define MAX_STEPS (MAX_BOARD_SIZE * MAX_BOARD_SIZE) // 游戏最大步数
// 全局变量
extern int BOARD_SIZE; // 当前实际使用的棋盘尺寸
extern int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE]; // 存储棋盘状态的二维数组
extern int step_count; // 当前游戏的总步数
extern bool use_forbidden_moves; // 是否启用禁手规则的标志
extern int use_timer; // 是否启用计时器的标志
extern int time_limit; // 每回合的时间限制(秒,内部存储)
extern const int direction[4][2]; // 定义四个基本搜索方向:水平、垂直、左斜、右斜
// 数据结构
/**
* @brief
*/
typedef struct
{
int player; // 执行该步的玩家标识
int x; // 落子的行坐标 (0-based)
int y; // 落子的列坐标 (0-based)
} Step;
extern Step steps[MAX_STEPS]; // 用于存储游戏中每一步棋的数组
/**
* @brief
* @details
*/
typedef struct
{
int continuous_chess; // 连续同色棋子的数量
bool check_start; // 棋子序列的起始端是否为空位(即是否开放)
bool check_end; // 棋子序列的末尾端是否为空位(即是否开放)
} DirInfo;
// 函数原型
// --- 游戏核心逻辑 ---
/**
* @brief
+5 -11
View File
@@ -1,18 +1,12 @@
# 五子棋游戏配置文件
# 棋盘大小 (范围: 5-25)
# 五子棋游戏配置文件
# 棋盘大小 (范围: 5-25)
BOARD_SIZE=15
# 禁手规则 (0=关闭, 1=开启)
# 禁手规则 (0=关闭, 1=开启)
USE_FORBIDDEN_MOVES=1
# 计时器 (0=关闭, 1=开启)
# 计时器 (0=关闭, 1=开启)
USE_TIMER=1
# 时间限制 (分钟)
# 时间限制 (分钟)
TIME_LIMIT=60
# 网络端口 (范围: 1024-65535)
NETWORK_PORT=8888
# 网络超时时间 (毫秒)
NETWORK_TIMEOUT=5000
+1 -4
View File
@@ -1,11 +1,8 @@
#include "init_board.h"
#include "gobang.h"
#include "game_mode.h"
#include "gobang.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief
-6
View File
@@ -1,9 +1,3 @@
/**
* @file init_board.h
* @brief
* @note
*
*/
#ifndef INIT_BOARD_H
#define INIT_BOARD_H
+54 -19
View File
@@ -1,3 +1,48 @@
/**
* @file .c
* @brief
* @details
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @date 2025-07-10
* @version 5.0
* @note
* 1.
* -
* -
* -
* -
* 2.
* -
* - Alpha-Beta AI
* -
* 3.
* -
* -
* - 使
* 4.
* -
* -
* - 便
* 5.
* -
* - bug
* -
* 6.
* -
* - 使
* 7.
* - 使 Git 便
* 8.
* -
* 9.
* - MIT 使
* 10.
* -
* 11.
* - [https://github.com/LHY0125/Gobang-Game]
* - [3364451258@qq.com][15236416560@163.com][lhy3364451258@outlook.com]
*/
#include "game_mode.h"
#include "ui.h"
#include "config.h"
@@ -9,9 +54,10 @@
/**
* @brief powershell
* gcc -std=c17 -o gobang.exe *.c -lws2_32
.\gobang.exe
* gcc network.c网络模块-lws2_32链接Windows网络库
* gcc -o gobang.exe main.c gobang.c game_mode.c ai.c record.c init_board.c ui.c config.c
* gcc .c gobang.c game_mode.c output/
* @brief powershell
* .\gobang.exe
*/
int main(int argc, char *argv[])
@@ -32,44 +78,33 @@ int main(int argc, char *argv[])
{
clear_screen();
display_main_menu();
int mode = get_integer_input("请输入模式(0-7): ", 0, 7);
int mode = get_integer_input("请输入模式(1-7): ", 1, 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:
case 4:
config_management_menu();
break;
// 6. 游戏规则
case 6:
case 5:
clear_screen();
display_game_rules();
pause_for_input("\n按任意键返回主菜单...");
break;
// 7. 关于游戏
case 7:
case 6:
clear_screen();
display_about();
pause_for_input("\n按任意键返回主菜单...");
break;
// 0. 退出游戏
case 0:
case 7:
save_game_config();
printf("感谢使用五子棋游戏!\n");
return 0;
-427
View File
@@ -1,427 +0,0 @@
/**
* @file network.c
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @brief
* @version 6.0
* @date 2025-07-10
*
* @copyright Copyright (c) 2025
*/
#include "network.h"
#include "gobang.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket close
typedef int SOCKET;
#endif
/**
* @brief
*/
bool init_network()
{
#ifdef _WIN32
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup failed: %d\n", result);
return false;
}
#endif
memset(&network_state, 0, sizeof(NetworkGameState));
network_state.socket = INVALID_SOCKET;
network_state.port = DEFAULT_PORT;
return true;
}
/**
* @brief
*/
void cleanup_network()
{
if (network_state.socket != INVALID_SOCKET)
{
closesocket(network_state.socket);
network_state.socket = INVALID_SOCKET;
}
#ifdef _WIN32
WSACleanup();
#endif
network_state.is_connected = false;
}
/**
* @brief
*/
bool create_server(int port)
{
struct sockaddr_in server_addr, client_addr;
int addr_len = sizeof(client_addr);
// 创建套接字
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == INVALID_SOCKET)
{
printf("创建套接字失败\n");
return false;
}
// 设置地址重用
int opt = 1;
#ifdef _WIN32
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
#else
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
// 绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
if (bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
{
printf("绑定端口失败\n");
closesocket(listen_socket);
return false;
}
// 开始监听
if (listen(listen_socket, 1) == SOCKET_ERROR)
{
printf("监听失败\n");
closesocket(listen_socket);
return false;
}
char local_ip[MAX_IP_LENGTH];
if (get_local_ip(local_ip, sizeof(local_ip)))
{
printf("服务器已启动,等待客户端连接...\n");
printf("本机IP地址: %s\n", local_ip);
printf("监听端口: %d\n", port);
}
else
{
printf("服务器已启动,监听端口: %d\n", port);
}
// 等待客户端连接
SOCKET client_socket = accept(listen_socket, (struct sockaddr*)&client_addr, &addr_len);
if (client_socket == INVALID_SOCKET)
{
printf("接受连接失败\n");
closesocket(listen_socket);
return false;
}
// 关闭监听套接字
closesocket(listen_socket);
// 保存连接信息
network_state.socket = client_socket;
network_state.is_server = true;
network_state.is_connected = true;
network_state.local_player_id = PLAYER1;
network_state.remote_player_id = PLAYER2;
network_state.port = port;
strcpy(network_state.remote_ip, inet_ntoa(client_addr.sin_addr));
printf("客户端已连接: %s\n", network_state.remote_ip);
return true;
}
/**
* @brief
*/
bool connect_to_server(const char* ip, int port)
{
struct sockaddr_in server_addr;
// 创建套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == INVALID_SOCKET)
{
printf("创建套接字失败\n");
return false;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
#ifdef _WIN32
server_addr.sin_addr.s_addr = inet_addr(ip);
if (server_addr.sin_addr.s_addr == INADDR_NONE)
{
#else
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0)
{
#endif
printf("无效的IP地址: %s\n", ip);
closesocket(client_socket);
return false;
}
printf("正在连接到服务器 %s:%d...\n", ip, port);
// 连接到服务器
if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
{
printf("连接服务器失败\n");
closesocket(client_socket);
return false;
}
// 保存连接信息
network_state.socket = client_socket;
network_state.is_server = false;
network_state.is_connected = true;
network_state.local_player_id = PLAYER2;
network_state.remote_player_id = PLAYER1;
network_state.port = port;
strcpy(network_state.remote_ip, ip);
printf("成功连接到服务器\n");
return true;
}
/**
* @brief
*/
bool send_network_message(const NetworkMessage* msg)
{
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
{
return false;
}
int bytes_sent = send(network_state.socket, (const char*)msg, sizeof(NetworkMessage), 0);
return bytes_sent == sizeof(NetworkMessage);
}
/**
* @brief
*/
bool receive_network_message(NetworkMessage* msg, int timeout_ms)
{
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
{
return false;
}
// 设置超时
if (timeout_ms > 0)
{
#ifdef _WIN32
DWORD timeout = timeout_ms;
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
#else
struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#endif
}
int bytes_received = recv(network_state.socket, (char*)msg, sizeof(NetworkMessage), 0);
if (bytes_received == sizeof(NetworkMessage))
{
return true;
} else if (bytes_received == 0)
{
// 连接已关闭
network_state.is_connected = false;
printf("对方已断开连接\n");
} else if (bytes_received == SOCKET_ERROR)
{
#ifdef _WIN32
int error = WSAGetLastError();
if (error != WSAETIMEDOUT)
{
#else
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
#endif
network_state.is_connected = false;
printf("网络接收错误\n");
}
}
return false;
}
/**
* @brief
*/
void disconnect_network()
{
if (network_state.is_connected)
{
NetworkMessage msg = {0};
msg.type = MSG_DISCONNECT;
msg.player_id = network_state.local_player_id;
msg.timestamp = time(NULL);
send_network_message(&msg);
}
cleanup_network();
}
/**
* @brief
*/
bool is_network_connected()
{
return network_state.is_connected && network_state.socket != INVALID_SOCKET;
}
/**
* @brief IP地址
*/
bool get_local_ip(char* ip_buffer, int buffer_size)
{
#ifdef _WIN32
// Windows实现
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0)
{
struct hostent* host_entry = gethostbyname(hostname);
if (host_entry != NULL)
{
struct in_addr addr;
addr.s_addr = *((unsigned long*)host_entry->h_addr_list[0]);
strncpy(ip_buffer, inet_ntoa(addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
return true;
}
}
#else
// Linux实现
struct sockaddr_in addr;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1)
{
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("8.8.8.8");
addr.sin_port = htons(80);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0)
{
socklen_t addr_len = sizeof(addr);
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == 0)
{
strncpy(ip_buffer, inet_ntoa(addr.sin_addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
close(sock);
return true;
}
}
close(sock);
}
#endif
// 默认返回本地回环地址
strncpy(ip_buffer, "127.0.0.1", buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
return false;
}
/**
* @brief
*/
bool send_move(int x, int y, int player_id)
{
NetworkMessage msg = {0};
msg.type = MSG_MOVE;
msg.player_id = player_id;
msg.x = x;
msg.y = y;
msg.timestamp = time(NULL);
return send_network_message(&msg);
}
/**
* @brief
*/
bool send_chat_message(const char* message)
{
NetworkMessage msg = {0};
msg.type = MSG_CHAT;
msg.player_id = network_state.local_player_id;
strncpy(msg.message, message, sizeof(msg.message) - 1);
msg.timestamp = time(NULL);
return send_network_message(&msg);
}
/**
* @brief
*/
bool send_surrender()
{
NetworkMessage msg = {0};
msg.type = MSG_SURRENDER;
msg.player_id = network_state.local_player_id;
msg.timestamp = time(NULL);
return send_network_message(&msg);
}
/**
* @brief
*/
bool send_undo_request(int steps)
{
NetworkMessage msg = {0};
msg.type = MSG_UNDO_REQUEST;
msg.player_id = network_state.local_player_id;
msg.x = steps; // 使用x字段存储步数
msg.timestamp = time(NULL);
return send_network_message(&msg);
}
/**
* @brief
*/
bool send_undo_response(bool accepted, int steps)
{
NetworkMessage msg = {0};
msg.type = MSG_UNDO_RESPONSE;
msg.player_id = network_state.local_player_id;
msg.x = steps; // 使用x字段存储步数
msg.y = accepted ? 1 : 0; // 使用y字段存储是否同意
msg.timestamp = time(NULL);
return send_network_message(&msg);
}
-144
View File
@@ -1,144 +0,0 @@
/**
* @file network.h
* @brief
* @note
* 1.
* 2.
* 3.
*/
#ifndef NETWORK_H
#define NETWORK_H
#include "gobang.h"
#include "type.h"
#include "config.h"
#include <stdbool.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket close
#endif
// 函数声明
/**
* @brief
* @return true
* @return false
*/
bool init_network();
/**
* @brief
*/
void cleanup_network();
/**
* @brief
* @param port
* @return true
* @return false
*/
bool create_server(int port);
/**
* @brief
* @param ip IP地址
* @param port
* @return true
* @return false
*/
bool connect_to_server(const char* ip, int port);
/**
* @brief
* @param msg
* @return true
* @return false
*/
bool send_network_message(const NetworkMessage* msg);
/**
* @brief
* @param msg
* @param timeout_ms 0
* @return true
* @return false
*/
bool receive_network_message(NetworkMessage* msg, int timeout_ms);
/**
* @brief
*/
void disconnect_network();
/**
* @brief
* @return true
* @return false
*/
bool is_network_connected();
/**
* @brief IP地址
* @param ip_buffer IP地址的缓冲区
* @param buffer_size
* @return true
* @return false
*/
bool get_local_ip(char* ip_buffer, int buffer_size);
/**
* @brief
* @param x
* @param y
* @param player_id ID
* @return true
* @return false
*/
bool send_move(int x, int y, int player_id);
/**
* @brief
* @param message
* @return true
* @return false
*/
bool send_chat_message(const char* message);
/**
* @brief
* @return true
* @return false
*/
bool send_surrender();
/**
* @brief
* @param steps
* @return true
* @return false
*/
bool send_undo_request(int steps);
/**
* @brief
* @param accepted
* @param steps
* @return true
* @return false
*/
bool send_undo_response(bool accepted, int steps);
#endif // NETWORK_H
+17 -49
View File
@@ -2,22 +2,11 @@
#include "game_mode.h"
#include "gobang.h"
#include "init_board.h"
#include "ui.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#endif
/**
* @brief
@@ -40,6 +29,13 @@
* -
* - calculate_final_score()
*/
// 全局变量,用于存储对局评分,确保对战结束和复盘模式使用相同的评分
int player1_final_score = 0;
int player2_final_score = 0;
int scores_calculated = 0;
char winner_info[50] = "平局或未完成"; // 存储胜负信息
void review_process(int game_mode)
{
int review_choice = get_integer_input("是否要复盘本局比赛? (1-是, 0-否): ", 0, 1);
@@ -75,7 +71,7 @@ void review_process(int game_mode)
// 打印当前步骤信息
// 根据游戏模式显示不同的标题和玩家信息
if (game_mode == GAME_MODE_AI)
if (game_mode == 1)
{
// 人机对战
printf("\n===== 五子棋人机对战(%dX%d棋盘) =====", BOARD_SIZE, BOARD_SIZE);
@@ -84,7 +80,7 @@ void review_process(int game_mode)
(s.player == PLAYER) ? "玩家" : "AI",
s.x + 1, s.y + 1);
}
else if (game_mode == GAME_MODE_PVP)
else
{
// 双人对战
printf("\n===== 五子棋双人对战(%dX%d棋盘) =====", BOARD_SIZE, BOARD_SIZE);
@@ -93,15 +89,6 @@ void review_process(int game_mode)
(s.player == PLAYER1) ? "玩家1(黑棋)" : "玩家2(白棋)",
s.x + 1, s.y + 1);
}
else if (game_mode == GAME_MODE_NETWORK)
{
// 网络对战
printf("\n===== 五子棋网络对战(%dX%d棋盘) =====", BOARD_SIZE, BOARD_SIZE);
printf("\n 第%d步/%d步: %s 落子于(%d, %d)\n",
i + 1, step_count,
(s.player == PLAYER1) ? "玩家1(黑棋)" : "玩家2(白棋)",
s.x + 1, s.y + 1);
}
// 打印当前复盘棋盘
printf(" ");
@@ -231,21 +218,14 @@ void display_game_scores(int game_mode)
if (sum_score > 0)
{
if (game_mode == GAME_MODE_AI)
if (game_mode == 1)
{
printf("玩家得分: %d, 占比: %.2f%%\n",
player1_final_score, (double)player1_final_score * 100.0 / sum_score);
printf("AI得分: %d, 占比: %.2f%%\n",
player2_final_score, (double)player2_final_score * 100.0 / sum_score);
}
else if (game_mode == GAME_MODE_PVP)
{
printf("玩家1(黑棋)得分: %d, 占比: %.2f%%\n",
player1_final_score, (double)player1_final_score * 100.0 / sum_score);
printf("玩家2(白棋)得分: %d, 占比: %.2f%%\n",
player2_final_score, (double)player2_final_score * 100.0 / sum_score);
}
else if (game_mode == GAME_MODE_NETWORK)
else
{
printf("玩家1(黑棋)得分: %d, 占比: %.2f%%\n",
player1_final_score, (double)player1_final_score * 100.0 / sum_score);
@@ -255,7 +235,7 @@ void display_game_scores(int game_mode)
}
else
{
if (game_mode == GAME_MODE_AI)
if (game_mode == 1)
{
printf("玩家得分: %d\n", player1_final_score);
printf("AI得分: %d\n", player2_final_score);
@@ -271,11 +251,11 @@ void display_game_scores(int game_mode)
// 评选MVP
if (player1_final_score > player2_final_score)
{
printf("\nMVP: %s (领先 %d 分)\n", (game_mode == GAME_MODE_AI) ? "玩家" : "玩家1(黑棋)", player1_final_score - player2_final_score);
printf("\nMVP: %s (领先 %d 分)\n", (game_mode == 1) ? "玩家" : "玩家1(黑棋)", player1_final_score - player2_final_score);
}
else if (player2_final_score > player1_final_score)
{
printf("\nMVP: %s (领先 %d 分)\n", (game_mode == GAME_MODE_AI) ? "AI" : "玩家2(白棋)", player2_final_score - player1_final_score);
printf("\nMVP: %s (领先 %d 分)\n", (game_mode == 1) ? "AI" : "玩家2(白棋)", player2_final_score - player1_final_score);
}
else
{
@@ -378,7 +358,7 @@ int save_game_to_file(const char *filename, int game_mode)
Step last_step = steps[step_count - 1];
if (check_win(last_step.x, last_step.y, last_step.player))
{
if (game_mode == GAME_MODE_AI)
if (game_mode == 1)
{
// 人机对战
if (last_step.player == PLAYER)
@@ -390,7 +370,7 @@ int save_game_to_file(const char *filename, int game_mode)
strcpy(winner_info, "AI获胜");
}
}
else if (game_mode == GAME_MODE_PVP)
else
{
// 双人对战
if (last_step.player == PLAYER1)
@@ -402,18 +382,6 @@ int save_game_to_file(const char *filename, int game_mode)
strcpy(winner_info, "玩家2获胜");
}
}
else if (game_mode == GAME_MODE_NETWORK)
{
// 网络对战
if (last_step.player == PLAYER1)
{
strcpy(winner_info, "玩家1获胜");
}
else
{
strcpy(winner_info, "玩家2获胜");
}
}
}
}
@@ -492,7 +460,7 @@ int load_game_from_file(const char *filename)
return 0;
}
if (game_mode != GAME_MODE_AI && game_mode != GAME_MODE_PVP && game_mode != GAME_MODE_NETWORK)
if (game_mode != 1 && game_mode != 2)
{
fclose(file);
return 0; // 无效的游戏模式
+6 -6
View File
@@ -1,14 +1,14 @@
/**
* @file record.h
* @brief
* @note
*
*/
#ifndef RECORD_H
#define RECORD_H
#include "gobang.h"
// 全局变量,用于存储对局评分,确保对战结束和复盘模式使用相同的评分
extern int player1_final_score; // 玩家1最终得分
extern int player2_final_score; // 玩家2最终得分
extern int scores_calculated; // 评分计算标志
extern char winner_info[50]; // 存储胜负信息
// --- 复盘与记录功能 ---
/**
* @brief
-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
-93
View File
@@ -1,93 +0,0 @@
/**
* @file type.h
* @brief
* @note 使
* @author
*/
#ifndef TYPE_H
#define TYPE_H
#include <stdbool.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#define SOCKET int
#endif
// ==================== 游戏核心数据结构 ====================
/**
* @brief
*/
typedef struct {
int player; // 执行该步的玩家标识
int x; // 落子的行坐标 (0-based)
int y; // 落子的列坐标 (0-based)
} Step;
/**
* @brief
* @details
*/
typedef struct {
int continuous_chess; // 连续同色棋子的数量
bool check_start; // 棋子序列的起始端是否为空位(即是否开放)
bool check_end; // 棋子序列的末尾端是否为空位(即是否开放)
} DirInfo;
// ==================== AI相关数据结构 ====================
/**
* @brief
* @details AI移动排序
*/
typedef struct {
int x, y; // 位置坐标
int score; // 评估分数
} ScoredMove;
/**
* @brief
* @details AI威胁检测系统
*/
typedef enum {
THREAT_NONE = 0, // 无威胁
THREAT_WIN = 5, // 直接获胜
THREAT_FOUR = 4, // 活四/冲四
THREAT_THREE = 3, // 活三
THREAT_DOUBLE = 2, // 双威胁
THREAT_POTENTIAL = 1 // 潜在威胁
} ThreatLevel;
// ==================== 网络相关数据结构 ====================
/**
* @brief
* @details
*/
typedef struct {
int type; // 消息类型
int player_id; // 玩家ID
int x, y; // 坐标(用于落子)
char message[256]; // 消息内容(用于聊天等)
time_t timestamp; // 时间戳
} NetworkMessage;
/**
* @brief
* @details
*/
typedef struct {
SOCKET socket; // 套接字
bool is_server; // 是否为服务器
bool is_connected; // 是否已连接
int local_player_id; // 本地玩家ID
int remote_player_id; // 远程玩家ID
char remote_ip[16]; // 远程IP地址(MAX_IP_LENGTH = 16
int port; // 端口号
} NetworkGameState;
#endif // TYPE_H
+30 -35
View File
@@ -1,7 +1,5 @@
#include "ui.h"
#include "gobang.h"
#include "config.h"
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
@@ -19,12 +17,11 @@ void display_main_menu()
printf("===== 五子棋游戏 =====\n");
printf("1. AI模式\n");
printf("2. 玩家比赛\n");
printf("3. 网络对战\n");
printf("4. 复盘模式\n");
printf("5. 游戏设置\n");
printf("6. 游戏规则\n");
printf("7. 关于游戏\n");
printf("0. 退出游戏\n");
printf("3. 复盘模式\n");
printf("4. 游戏设置\n");
printf("5. 游戏规则\n");
printf("6. 关于游戏\n");
printf("7. 退出游戏\n");
printf("=====================\n");
}
@@ -89,26 +86,26 @@ void display_game_status(int current_player, int step_count)
*/
void display_winner(int winner)
{
printf("\n游戏结束!\n");
printf("\n? 游戏结束!\n");
if (winner == PLAYER)
{
printf("玩家获胜!\n");
printf("? 玩家获胜!\n");
}
else if (winner == AI)
{
printf("AI获胜!\n");
printf("? AI获胜!\n");
}
else if (winner == PLAYER1)
{
printf("玩家1获胜!\n");
printf("? 玩家1获胜!\n");
}
else if (winner == PLAYER2)
{
printf("玩家2获胜!\n");
printf("? 玩家2获胜!\n");
}
else
{
printf("平局!\n");
printf("? 平局!\n");
}
}
@@ -121,9 +118,8 @@ void display_settings_menu()
printf("1. 棋盘大小设置\n");
printf("2. 禁手规则设置\n");
printf("3. 计时器设置\n");
printf("4. 网络配置设置\n");
printf("5. AI难度设置\n");
printf("6. 返回主菜单\n");
printf("4. AI难度设置\n");
printf("5. 返回主菜单\n");
printf("==================\n");
}
@@ -159,24 +155,24 @@ void pause_for_input(const char* prompt)
void display_game_rules()
{
printf("\n===== 五子棋游戏规则 =====\n");
printf("1. 🎮 游戏目标:\n");
printf("1. 游戏目标:\n");
printf(" 在棋盘上连成五个同色棋子(横、竖、斜均可)\n\n");
printf("2. 🔄 游戏流程:\n");
printf(" - 黑棋先行,双方轮流落子\n");
printf(" - 📍 输入坐标格式:行号 列号(如:7 7)\n");
printf(" - 棋子落在棋盘交叉点上\n\n");
printf("3. 🏆 胜负判定:\n");
printf(" - 🎉 率先连成五子者获胜\n");
printf(" - 🤝 棋盘下满无人获胜则为平局\n\n");
printf("4. 🚫 禁手规则(可选):\n");
printf(" - 三三禁手:同时形成两个活三\n");
printf(" - 四四禁手:同时形成两个冲四\n");
printf(" - 长连禁手:连成六子或以上\n\n");
printf("5. 🛠️ 特殊功能:\n");
printf(" - ↩️ 悔棋:输入 'R' 或 'r' 可悔棋\n");
printf(" - 💾 保存:游戏结束后可保存对局记录\n");
printf(" - 📖 复盘:可加载历史对局进行复盘\n");
printf("============================\n");
printf("2. 游戏流程:\n");
printf(" - 黑棋先行,双方轮流落子\n");
printf(" - 输入坐标格式:行号 列号(如:7 7)\n");
printf(" - 棋子落在棋盘交叉点上\n\n");
printf("3. 胜负判定:\n");
printf(" - 率先连成五子者获胜\n");
printf(" - 棋盘下满无人获胜则为平局\n\n");
printf("4. 禁手规则(可选):\n");
printf(" - 三三禁手:同时形成两个活三\n");
printf(" - 四四禁手:同时形成两个冲四\n");
printf(" - 长连禁手:连成六子或以上\n\n");
printf("5. 特殊功能:\n");
printf(" - 悔棋:输入 'R' 或 'r' 可悔棋\n");
printf(" - 保存:游戏结束后可保存对局记录\n");
printf(" - 复盘:可加载历史对局进行复盘\n");
printf("========================\n");
}
/**
@@ -193,7 +189,6 @@ void display_about()
printf("✨ 主要特性:\n");
printf(" 🤖 智能AI对战(支持多种难度)\n");
printf(" 👥 双人对战模式\n");
printf(" 🌐 网络对战(局域网/互联网)\n");
printf(" 📝 对局记录与复盘\n");
printf(" 🚫 禁手规则支持\n");
printf(" ⏱️ 计时器功能\n");
-6
View File
@@ -1,9 +1,3 @@
/**
* @file ui.h
* @brief
* @note
*
*/
#ifndef UI_H
#define UI_H
+101
View File
@@ -0,0 +1,101 @@
/**
* @file .c
* @brief
* @details
* @author (3364451258@qq.com15236416560@163.comlhy3364451258@outlook.com)
* @date 2025-07-07
* @version 4.0
* @note
* 1.
* -
* -
* -
* -
* 2.
* -
* - Alpha-Beta AI
* -
* 3.
* -
* -
* - 使
* 4.
* -
* -
* - 便
* 5.
* -
* - bug
* -
* 6.
* -
* - 使
* 7.
* - 使 Git 便
* 8.
* -
* 9.
* - MIT 使
* 10.
* -
* 11.
* - [https://github.com/LHY0125/Gobang-Game]
* - [3364451258@qq.com][15236416560@163.com][lhy3364451258@outlook.com]
*/
#include "game_mode.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#endif
/**
* @brief powershell
* gcc -o gobang.exe gobang.c ai.c game_mode.c init_board.c record.c .c
* gcc .c gobang.c game_mode.c output/
* @brief powershell
* .\gobang.exe
*/
int main(int argc, char *argv[])
{
// 设置控制台编码为UTF-8
#ifdef _WIN32
system("chcp 65001 > nul"); // 设置控制台编码为UTF-8
SetConsoleOutputCP(65001); // 设置控制台输出编码
SetConsoleCP(65001); // 设置控制台输入编码
_mkdir("records");
#endif
// 选择模式
while(1)
{
printf("===== 五子棋游戏 =====\n");
printf("1. AI模式\n");
printf("2. 玩家比赛\n");
printf("3. 复盘模式\n");
printf("4. 退出游戏\n");
int mode = get_integer_input("请输入模式(1/2/3/4): ", 1, 4);
if (mode == 1)
{
run_ai_game();
}
else if (mode == 2)
{
run_pvp_game();
}
else if (mode == 3)
{
run_review_mode();
}
else if (mode == 4)
{
printf("感谢使用五子棋游戏!\n");
break;
}
}
return 0;
}