mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-05-09 18:09:46 +08:00
refactor: 重构项目为纯GUI版本并清理冗余代码
- 移除控制台版本相关代码,包括game_mode、ui、init_board等模块 - 将empty_board函数移至gobang.c核心模块 - 简化main.c仅保留GUI启动逻辑 - 更新Makefile仅构建GUI版本 - 清理过时文档和配置文件 - 优化GUI菜单和游戏窗口交互逻辑 - 添加AI难度配置支持
This commit is contained in:
@@ -1,105 +0,0 @@
|
|||||||
## 🎯 项目优势
|
|
||||||
### ✅ 已经做得很好的方面
|
|
||||||
- 📚 代码质量优秀 :注释覆盖率30.1%,使用Doxygen格式文档
|
|
||||||
- 🏗️ 架构设计合理 :模块化设计,职责分离清晰
|
|
||||||
- 🎮 功能完整丰富 :支持人机对战、双人对战、网络对战、GUI界面
|
|
||||||
- 🔧 技术栈现代化 :SDL3图形库、双版本架构、专业安装包
|
|
||||||
- 📋 文档完善 :详细的README、AI增强指南、架构重构指南
|
|
||||||
- 🌐 跨平台考虑 :已有条件编译支持Windows/Linux
|
|
||||||
## 🚀 改进建议
|
|
||||||
### 1. 🧪 测试体系建设(高优先级)
|
|
||||||
当前状况 :项目缺乏自动化测试
|
|
||||||
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 添加单元测试框架(如Unity或自制简单测试框架)
|
|
||||||
- 为核心模块编写测试用例:AI算法、棋盘逻辑、网络通信
|
|
||||||
- 添加集成测试验证各模块协作
|
|
||||||
- 创建性能基准测试
|
|
||||||
### 2. 🛡️ 错误处理和安全性增强(高优先级)
|
|
||||||
当前状况 :基础错误处理存在,但可以更完善
|
|
||||||
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 增强输入验证,防止缓冲区溢出
|
|
||||||
- 添加网络通信的安全验证机制
|
|
||||||
- 完善内存管理,添加内存泄漏检测
|
|
||||||
- 增加异常恢复机制
|
|
||||||
### 3. 🎯 AI算法优化(中优先级)
|
|
||||||
当前状况 :基础Minimax+α-β剪枝,搜索深度3层
|
|
||||||
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 实现置换表缓存系统
|
|
||||||
- 添加开局库和残局库
|
|
||||||
- 实现迭代加深搜索
|
|
||||||
- 增加威胁检测优化
|
|
||||||
- 支持多线程并行搜索
|
|
||||||
### 4. 🔧 构建和部署优化(中优先级)
|
|
||||||
当前状况 :编译脚本功能完善,但可以更自动化
|
|
||||||
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 添加CMake构建系统支持
|
|
||||||
- 实现持续集成/持续部署(CI/CD)
|
|
||||||
- 添加依赖管理和版本控制
|
|
||||||
- 完善跨平台构建脚本
|
|
||||||
### 5. 📊 性能监控和分析(中优先级)
|
|
||||||
建议添加 :
|
|
||||||
|
|
||||||
- AI决策时间统计
|
|
||||||
- 内存使用监控
|
|
||||||
- 网络延迟分析
|
|
||||||
- 性能瓶颈识别工具
|
|
||||||
### 6. 🎮 用户体验提升(低优先级)
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 添加音效和动画效果
|
|
||||||
- 实现主题和皮肤系统
|
|
||||||
- 增加游戏统计和成就系统
|
|
||||||
- 支持自定义快捷键
|
|
||||||
### 7. 🌐 网络功能增强(低优先级)
|
|
||||||
建议改进 :
|
|
||||||
|
|
||||||
- 实现房间系统和匹配机制
|
|
||||||
- 添加观战功能
|
|
||||||
- 支持断线重连
|
|
||||||
- 实现聊天系统
|
|
||||||
## 📋 具体实施建议
|
|
||||||
### 立即可实施(1-2周)
|
|
||||||
1. 1.
|
|
||||||
创建基础测试框架
|
|
||||||
2. 2.
|
|
||||||
添加输入验证和边界检查
|
|
||||||
3. 3.
|
|
||||||
完善错误日志记录
|
|
||||||
4. 4.
|
|
||||||
优化编译警告处理
|
|
||||||
### 短期目标(1-2月)
|
|
||||||
1. 1.
|
|
||||||
完善单元测试覆盖
|
|
||||||
2. 2.
|
|
||||||
实现置换表和开局库
|
|
||||||
3. 3.
|
|
||||||
添加性能监控
|
|
||||||
4. 4.
|
|
||||||
增强网络安全性
|
|
||||||
### 长期规划(3-6月)
|
|
||||||
1. 1.
|
|
||||||
实现神经网络AI
|
|
||||||
2. 2.
|
|
||||||
完整跨平台支持
|
|
||||||
3. 3.
|
|
||||||
添加数据库支持
|
|
||||||
4. 4.
|
|
||||||
实现云端对战
|
|
||||||
## 🏆 总体评价
|
|
||||||
你的项目已经达到了 专业级别的开源项目标准 !代码架构清晰、功能完整、文档详尽。主要的改进空间在于测试覆盖和一些高级特性的添加。
|
|
||||||
|
|
||||||
项目亮点 :
|
|
||||||
|
|
||||||
- 从v7.0的架构重构到v8.2的编译优化,版本迭代思路清晰
|
|
||||||
- SDL3图形化界面实现现代化
|
|
||||||
- 网络对战功能完整
|
|
||||||
- 代码注释和文档非常专业
|
|
||||||
这是一个非常值得继续完善和推广的优秀项目!🎉
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
# 🧠 五子棋AI实现详解
|
|
||||||
|
|
||||||
## 📜 算法概述
|
|
||||||
本五子棋AI采用α-β剪枝优化的极小极大算法,结合专业的棋型评估系统和多层次的威胁检测机制。v8.0版本新增SDL3图形化界面支持,提供可视化AI决策过程和双版本架构(控制台+GUI)。支持人机对战、双人对战和网络对战多种模式。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD
|
|
||||||
A[AI决策开始] --> B{威胁检测}
|
|
||||||
B -->|有威胁| C[防御性落子]
|
|
||||||
B -->|无威胁| D[α-β剪枝搜索]
|
|
||||||
D --> E[评估候选位置]
|
|
||||||
E --> F[选择最优落子]
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔢 数据结构
|
|
||||||
|
|
||||||
### 🎲 棋盘表示
|
|
||||||
```c
|
|
||||||
int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE]; // 25x25最大棋盘
|
|
||||||
```
|
|
||||||
- `0` 空位
|
|
||||||
- `1` 玩家(✖)
|
|
||||||
- `2` AI(◯)
|
|
||||||
|
|
||||||
### 📝 步数记录
|
|
||||||
```c
|
|
||||||
typedef struct {
|
|
||||||
int player; // 1=玩家, 2=AI
|
|
||||||
int x, y; // 坐标(0-based)
|
|
||||||
} Step;
|
|
||||||
Step steps[MAX_STEPS]; // 最大步数记录
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🧭 方向分析
|
|
||||||
```c
|
|
||||||
typedef struct {
|
|
||||||
int continuous_chess; // 连续同色棋子数
|
|
||||||
bool check_start; // 起始方向开放
|
|
||||||
bool check_end; // 结束方向开放
|
|
||||||
} DirInfo;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🌐 网络对战支持
|
|
||||||
```c
|
|
||||||
// 网络模式下的AI决策
|
|
||||||
void network_ai_move(int depth, int player_id);
|
|
||||||
// 同步AI决策到网络对手
|
|
||||||
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)
|
|
||||||
```c
|
|
||||||
void ai_move(int depth);
|
|
||||||
```
|
|
||||||
**执行流程**:
|
|
||||||
1. 🔍 扫描棋盘检测威胁
|
|
||||||
2. 🛡️ 优先防御关键威胁
|
|
||||||
3. 🔎 使用α-β剪枝搜索最佳位置
|
|
||||||
4. ✅ 执行最优落子
|
|
||||||
|
|
||||||
### 2. dfs() - α-β剪枝核心
|
|
||||||
```c
|
|
||||||
int dfs(int x, int y, int player, int depth, int alpha, int beta, bool is_maximizing);
|
|
||||||
```
|
|
||||||
**剪枝条件**:
|
|
||||||
- 极大节点: α ≥ β
|
|
||||||
- 极小节点: β ≤ α
|
|
||||||
|
|
||||||
### 3. evaluate_pos() - 位置评估
|
|
||||||
**评分标准**:
|
|
||||||
| 棋型 | 图示 | 分数 |
|
|
||||||
|------|------|------|
|
|
||||||
| 活四 | ○○○○● | 100000 |
|
|
||||||
| 冲四 | ○○○○■ | 10000 |
|
|
||||||
| 活三 | ○○○●● | 5000 |
|
|
||||||
|
|
||||||
## 🏆 评估系统
|
|
||||||
|
|
||||||
### 棋型评分表
|
|
||||||
| 棋型 | 分数 | 示例 |
|
|
||||||
|------|------|------|
|
|
||||||
| 活四 | 100000 | `-----○-----` |
|
|
||||||
| 冲四 | 10000 | `----○■----` |
|
|
||||||
| 活三 | 5000 | `---○●●---` |
|
|
||||||
|
|
||||||
### 位置权重计算
|
|
||||||
```python
|
|
||||||
权重 = 50 * (BOARD_SIZE - |x-center| - |y-center|)
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⚡ 性能优化
|
|
||||||
|
|
||||||
1. **评估缓存**:
|
|
||||||
- 哈希表存储重复位置评估
|
|
||||||
- 命中率: ~85%
|
|
||||||
|
|
||||||
2. **搜索优化**:
|
|
||||||
- 局部搜索范围: 2格
|
|
||||||
- 平均剪枝率: 65%
|
|
||||||
|
|
||||||
3. **典型搜索深度**:
|
|
||||||
- 基础难度: 3层
|
|
||||||
- 最高难度: 5层
|
|
||||||
|
|
||||||
4. **网络优化**:
|
|
||||||
- 异步AI计算,避免网络延迟
|
|
||||||
- 决策结果实时同步
|
|
||||||
- 支持断线重连后状态恢复
|
|
||||||
|
|
||||||
## 🎯 典型场景
|
|
||||||
|
|
||||||
### 必胜局面处理
|
|
||||||
```
|
|
||||||
局面: ○○○○_
|
|
||||||
决策: 立即落子形成五连
|
|
||||||
```
|
|
||||||
|
|
||||||
### 双活三防御
|
|
||||||
```
|
|
||||||
威胁: 玩家有两个活三
|
|
||||||
应对: 必须阻挡关键交叉点
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 性能基准
|
|
||||||
|
|
||||||
| 指标 | 15x15棋盘 | 19x19棋盘 |
|
|
||||||
|------|-----------|-----------|
|
|
||||||
| 平均决策时间 | 120ms | 350ms |
|
|
||||||
| 最大搜索节点 | 8,200 | 24,500 |
|
|
||||||
| 平均剪枝率 | 68% | 62% |
|
|
||||||
|
|
||||||
## 🛠️ 开发建议
|
|
||||||
|
|
||||||
1. **调试技巧**:
|
|
||||||
- 启用`DEBUG_MODE`查看搜索过程
|
|
||||||
- 使用`print_board()`可视化评估
|
|
||||||
- 网络模式下使用`network_debug()`监控通信
|
|
||||||
|
|
||||||
2. **扩展方向**:
|
|
||||||
- 添加开局库
|
|
||||||
- 实现并行搜索
|
|
||||||
- 优化评估函数
|
|
||||||
- 增强网络对战AI适应性
|
|
||||||
- 支持AI难度动态调整
|
|
||||||
```
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
# 五子棋项目代码架构重构指南
|
|
||||||
|
|
||||||
## 📋 概述
|
|
||||||
|
|
||||||
本文档详细记录了五子棋项目在v7.0-v8.0版本中进行的重大代码架构重构,包括重构的目标、实施过程、技术细节和带来的改进。v8.0版本新增了SDL3图形化界面模块,实现了双版本架构设计。
|
|
||||||
|
|
||||||
## 🎯 重构目标
|
|
||||||
|
|
||||||
### 主要目标
|
|
||||||
1. **代码模块化** - 实现清晰的模块分离和职责划分
|
|
||||||
2. **配置统一管理** - 集中管理所有配置参数和宏定义
|
|
||||||
3. **全局变量规范化** - 统一管理全局变量,避免散乱分布
|
|
||||||
4. **类型定义标准化** - 集中定义所有数据结构和类型
|
|
||||||
5. **提升可维护性** - 降低代码耦合度,提高可读性和可维护性
|
|
||||||
6. **双版本架构** - (v8.0新增) 实现控制台版本和GUI版本的并行支持
|
|
||||||
7. **图形化界面集成** - (v8.0新增) 无缝集成SDL3图形库,提供现代化用户界面
|
|
||||||
|
|
||||||
### 预期收益
|
|
||||||
- 减少代码重复和冗余
|
|
||||||
- 提高开发效率和调试便利性
|
|
||||||
- 增强代码的可扩展性和可移植性
|
|
||||||
- 为后续功能开发奠定坚实基础
|
|
||||||
|
|
||||||
## 🏗️ 重构实施
|
|
||||||
|
|
||||||
### 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版本的代码架构重构是一次重要的技术升级,通过系统性的重构实现了:
|
|
||||||
|
|
||||||
✅ **配置参数的统一管理** - 提高了配置的一致性和可维护性
|
|
||||||
✅ **全局变量的规范化** - 降低了代码的复杂度和耦合度
|
|
||||||
✅ **类型定义的标准化** - 增强了代码的可读性和类型安全
|
|
||||||
✅ **模块结构的优化** - 为后续功能扩展奠定了坚实基础
|
|
||||||
|
|
||||||
这次重构不仅解决了当前的技术债务,更为项目的长期发展提供了良好的架构基础。后续的功能开发将能够更加高效和稳定地进行。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*本文档将随着项目的发展持续更新,记录架构演进的每一个重要节点。*
|
|
||||||
-83
@@ -1,83 +0,0 @@
|
|||||||
# 五子棋游戏编译指南
|
|
||||||
|
|
||||||
本项目现在支持使用 `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
|
|
||||||
```
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
# 为五子棋游戏添加图标指南
|
|
||||||
|
|
||||||
## 问题说明
|
|
||||||
|
|
||||||
在尝试为可执行文件添加图标时,发现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 Hacker:http://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`
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
# 五子棋网络对战使用说明
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
本项目支持网络对战功能,允许两台设备通过网络进行实时五子棋对战,支持服务器/客户端连接。v8.0版本新增了GUI界面的网络对战支持,提供更直观的可视化网络游戏体验。
|
|
||||||
|
|
||||||
### v8.2 (2025-10-08)
|
|
||||||
- 专业安装包制作 - 支持Inno Setup和NSIS双重安装方案
|
|
||||||
- 安装程序优化 - 完整的文件打包和快捷方式创建
|
|
||||||
- 分发体系完善 - 提供标准化的软件分发解决方案
|
|
||||||
- 用户体验提升 - 一键安装部署,支持完整卸载功能
|
|
||||||
|
|
||||||
## 编译方法
|
|
||||||
|
|
||||||
### 控制台版本
|
|
||||||
```bash
|
|
||||||
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` 链接库
|
|
||||||
- GUI版本需要SDL3库支持
|
|
||||||
- Linux系统不需要ws2_32链接库参数
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 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
|
|
||||||
- 游戏结束后及时关闭程序
|
|
||||||
- 注意保护个人网络信息
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**开发者:** 刘航宇
|
|
||||||
**联系邮箱:** 3364451258@qq.com
|
|
||||||
**项目主页:** https://github.com/LHY0125/Gobang-Game
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
# 五子棋游戏 - 图形化界面说明 (v8.3)
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
v8.3版本引入了基于IUP的现代化图形界面,实现了完整的双版本架构:
|
|
||||||
|
|
||||||
- **控制台界面**:传统的文本界面,保持原有功能完整性
|
|
||||||
- **图形化界面**:基于IUP的现代图形界面,提供可视化操作体验
|
|
||||||
|
|
||||||
## v8.3 新增功能
|
|
||||||
|
|
||||||
- ✅ **IUP图形化界面**:原生控件风格,轻量高效
|
|
||||||
- ✅ **鼠标交互支持**:点击落子,直观操作
|
|
||||||
- ✅ **窗口管理优化**:自动居中,响应式设计
|
|
||||||
- ✅ **事件驱动架构**:流畅的用户交互体验
|
|
||||||
- ✅ **安装包支持**:提供专业的安装程序
|
|
||||||
- ✅ **双版本并行**:控制台和GUI版本独立运行
|
|
||||||
|
|
||||||
## 环境要求
|
|
||||||
|
|
||||||
### IUP库配置
|
|
||||||
|
|
||||||
项目已内置IUP库(位于 `libs/iup-3.31_Win64_dllw6_lib`),无需额外安装。
|
|
||||||
|
|
||||||
### 编译环境
|
|
||||||
|
|
||||||
- GCC编译器(MinGW-w64)
|
|
||||||
- Make工具(mingw32-make)
|
|
||||||
- Windows 10/11操作系统
|
|
||||||
|
|
||||||
## 编译方法
|
|
||||||
|
|
||||||
### 方法一:使用Makefile(推荐)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 编译图形化版本
|
|
||||||
mingw32-make gui
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方法二:手动编译
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# GUI版本
|
|
||||||
gcc -std=c17 -o bin/gobang_gui.exe src/*.c -Iinclude -Ilibs/iup-3.31_Win64_dllw6_lib/include -Llibs/iup-3.31_Win64_dllw6_lib -liup -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -lws2_32
|
|
||||||
|
|
||||||
# 复制IUP动态库到bin目录
|
|
||||||
copy libs\iup-3.31_Win64_dllw6_lib\iup.dll bin\
|
|
||||||
```
|
|
||||||
|
|
||||||
## 运行方法
|
|
||||||
|
|
||||||
### 图形化版本
|
|
||||||
|
|
||||||
```bash
|
|
||||||
.\bin\gobang_gui.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
或者在主菜单选择模式8启动图形化界面。
|
|
||||||
|
|
||||||
## 图形化界面功能
|
|
||||||
|
|
||||||
- **窗口大小**:800x600像素
|
|
||||||
- **棋盘显示**:15x15标准五子棋棋盘
|
|
||||||
- **鼠标操作**:点击棋盘交叉点进行落子
|
|
||||||
- **键盘操作**:
|
|
||||||
- `ESC`:退出图形化界面返回主菜单
|
|
||||||
- **游戏状态**:
|
|
||||||
- 实时显示当前玩家(黑子/白子)
|
|
||||||
- 显示游戏进度和状态
|
|
||||||
- 胜负结果提示
|
|
||||||
- **视觉效果**:
|
|
||||||
- 清晰的黑白棋子
|
|
||||||
- 当前玩家指示器
|
|
||||||
- 平滑的图形渲染
|
|
||||||
- **胜负判定**:自动检测五子连珠并显示获胜者
|
|
||||||
|
|
||||||
## 故障排除
|
|
||||||
|
|
||||||
### 编译错误
|
|
||||||
|
|
||||||
1. **找不到IUP头文件**
|
|
||||||
- 检查Makefile中的`IUP_PATH`配置是否正确指向`libs`目录
|
|
||||||
|
|
||||||
2. **链接错误**
|
|
||||||
- 确保链接了所有必要的Windows系统库(`-lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32`)
|
|
||||||
|
|
||||||
3. **运行时错误**
|
|
||||||
- 确保`iup.dll`位于可执行文件同级目录或系统PATH中
|
|
||||||
|
|
||||||
### 图形化界面启动失败
|
|
||||||
|
|
||||||
- 确保`iup.dll`文件在exe同目录下
|
|
||||||
- 尝试以管理员权限运行
|
|
||||||
- 确认Windows版本兼容性
|
|
||||||
|
|
||||||
### 运行时问题
|
|
||||||
|
|
||||||
- **中文乱码**:确保代码中已启用UTF-8模式 `IupSetGlobal("UTF8MODE", "YES");`
|
|
||||||
- **鼠标点击无响应**:确认点击在棋盘交叉点附近
|
|
||||||
|
|
||||||
## 使用说明
|
|
||||||
|
|
||||||
### 快速开始
|
|
||||||
|
|
||||||
1. 运行 `mingw32-make gui` 编译图形化版本
|
|
||||||
2. 运行 `bin\gobang_gui.exe` 启动程序
|
|
||||||
3. 在主菜单选择 "8. 图形化界面"
|
|
||||||
4. 使用鼠标点击棋盘进行游戏
|
|
||||||
5. 按ESC键退出图形化界面
|
|
||||||
|
|
||||||
### 游戏规则
|
|
||||||
|
|
||||||
- 黑子先行,轮流落子
|
|
||||||
- 率先形成五子连珠者获胜
|
|
||||||
- 支持横、竖、斜四个方向的连珠判定
|
|
||||||
|
|
||||||
## 开发说明
|
|
||||||
|
|
||||||
### 文件结构
|
|
||||||
|
|
||||||
- `include/gui.h` - 图形化界面头文件
|
|
||||||
- `src/gui.c` - 图形化界面实现(基于IUP)
|
|
||||||
- `src/main.c` - 主程序(已添加图形化模式)
|
|
||||||
- `Makefile` - 构建脚本
|
|
||||||
|
|
||||||
### 扩展功能
|
|
||||||
|
|
||||||
图形化界面支持进一步扩展:
|
|
||||||
|
|
||||||
- 更多IUP控件集成
|
|
||||||
- 主题切换
|
|
||||||
- 网络对战界面优化
|
|
||||||
- AI难度可视化调节
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**注意**:首次使用图形化界面前,请确保IUP动态库已正确复制。
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# 五子棋游戏 Makefile
|
# 五子棋游戏 Makefile
|
||||||
# 支持编译控制台版本和GUI版本 (IUP)
|
# 支持编译GUI版本 (IUP)
|
||||||
|
|
||||||
# 编译器设置
|
# 编译器设置
|
||||||
CC = gcc
|
CC = gcc
|
||||||
@@ -22,35 +22,27 @@ OBJ_DIR = obj
|
|||||||
BIN_DIR = bin
|
BIN_DIR = bin
|
||||||
|
|
||||||
# 源文件
|
# 源文件
|
||||||
COMMON_SOURCES = $(SRC_DIR)/main.c $(SRC_DIR)/gobang.c $(SRC_DIR)/ai.c $(SRC_DIR)/config.c \
|
COMMON_SOURCES = $(SRC_DIR)/gobang.c $(SRC_DIR)/ai.c $(SRC_DIR)/config.c \
|
||||||
$(SRC_DIR)/game_mode.c $(SRC_DIR)/globals.c $(SRC_DIR)/init_board.c \
|
$(SRC_DIR)/globals.c \
|
||||||
$(SRC_DIR)/network.c $(SRC_DIR)/record.c $(SRC_DIR)/ui.c $(SRC_DIR)/gui.c \
|
$(SRC_DIR)/network.c $(SRC_DIR)/record.c $(SRC_DIR)/gui.c \
|
||||||
$(SRC_DIR)/gui_menu.c
|
$(SRC_DIR)/gui_menu.c
|
||||||
|
|
||||||
GUI_SOURCES = $(COMMON_SOURCES)
|
|
||||||
CONSOLE_SOURCES = $(COMMON_SOURCES)
|
|
||||||
|
|
||||||
# 目标文件 (src/xxx.c -> obj/xxx.o)
|
# 目标文件 (src/xxx.c -> obj/xxx.o)
|
||||||
COMMON_OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(COMMON_SOURCES))
|
COMMON_OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(COMMON_SOURCES))
|
||||||
|
|
||||||
# 可执行文件
|
# 可执行文件
|
||||||
CONSOLE_TARGET = $(BIN_DIR)/gobang_console.exe
|
|
||||||
GUI_TARGET = $(BIN_DIR)/gobang_gui.exe
|
GUI_TARGET = $(BIN_DIR)/gobang_gui.exe
|
||||||
|
|
||||||
# 默认目标
|
# 默认目标
|
||||||
all: directories $(CONSOLE_TARGET) $(GUI_TARGET)
|
all: directories $(GUI_TARGET)
|
||||||
|
|
||||||
# 创建目录 (PowerShell 语法)
|
# 创建目录 (PowerShell 语法)
|
||||||
directories:
|
directories:
|
||||||
if (!(Test-Path "$(OBJ_DIR)")) { New-Item -ItemType Directory -Path "$(OBJ_DIR)" | Out-Null }
|
if (!(Test-Path "$(OBJ_DIR)")) { New-Item -ItemType Directory -Path "$(OBJ_DIR)" | Out-Null }
|
||||||
if (!(Test-Path "$(BIN_DIR)")) { New-Item -ItemType Directory -Path "$(BIN_DIR)" | Out-Null }
|
if (!(Test-Path "$(BIN_DIR)")) { New-Item -ItemType Directory -Path "$(BIN_DIR)" | Out-Null }
|
||||||
|
|
||||||
# 控制台版本
|
|
||||||
$(CONSOLE_TARGET): $(COMMON_OBJECTS)
|
|
||||||
$(CC) $(CFLAGS) $(IUP_INCLUDE) -o $@ $^ $(IUP_LIBS) $(LDFLAGS)
|
|
||||||
|
|
||||||
# GUI版本
|
# GUI版本
|
||||||
$(GUI_TARGET): $(COMMON_OBJECTS)
|
$(GUI_TARGET): $(COMMON_OBJECTS) $(OBJ_DIR)/main.o
|
||||||
$(CC) $(CFLAGS) $(IUP_INCLUDE) -o $@ $^ $(IUP_LIBS) $(LDFLAGS)
|
$(CC) $(CFLAGS) $(IUP_INCLUDE) -o $@ $^ $(IUP_LIBS) $(LDFLAGS)
|
||||||
Copy-Item -Path "$(subst /,\,$(IUP_PATH))\iup.dll" -Destination "$(BIN_DIR)" -Force
|
Copy-Item -Path "$(subst /,\,$(IUP_PATH))\iup.dll" -Destination "$(BIN_DIR)" -Force
|
||||||
|
|
||||||
@@ -58,27 +50,23 @@ $(GUI_TARGET): $(COMMON_OBJECTS)
|
|||||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
$(CC) $(CFLAGS) $(IUP_INCLUDE) -c -o $@ $<
|
$(CC) $(CFLAGS) $(IUP_INCLUDE) -c -o $@ $<
|
||||||
|
|
||||||
|
# 编译 main.c
|
||||||
|
$(OBJ_DIR)/main.o: $(SRC_DIR)/main.c
|
||||||
|
$(CC) $(CFLAGS) $(IUP_INCLUDE) -c -o $@ $<
|
||||||
|
|
||||||
# 清理规则 (PowerShell 语法)
|
# 清理规则 (PowerShell 语法)
|
||||||
clean:
|
clean:
|
||||||
if (Test-Path "$(OBJ_DIR)") { Remove-Item -Path "$(OBJ_DIR)" -Recurse -Force }
|
if (Test-Path "$(OBJ_DIR)") { Remove-Item -Path "$(OBJ_DIR)" -Recurse -Force }
|
||||||
if (Test-Path "$(BIN_DIR)") { Remove-Item -Path "$(BIN_DIR)" -Recurse -Force }
|
if (Test-Path "$(BIN_DIR)") { Remove-Item -Path "$(BIN_DIR)" -Recurse -Force }
|
||||||
|
|
||||||
# 只编译控制台版本
|
# 编译GUI版本
|
||||||
console: directories $(CONSOLE_TARGET)
|
|
||||||
|
|
||||||
# 只编译GUI版本
|
|
||||||
gui: directories $(GUI_TARGET)
|
gui: directories $(GUI_TARGET)
|
||||||
|
|
||||||
# 安装规则(可选)
|
# 安装规则(可选)
|
||||||
install: all
|
install: all
|
||||||
Write-Host "Installing executables..."
|
Write-Host "Installing executables..."
|
||||||
Copy-Item -Path "$(CONSOLE_TARGET)" -Destination "C:\Program Files\Gobang\" -Force
|
|
||||||
Copy-Item -Path "$(GUI_TARGET)" -Destination "C:\Program Files\Gobang\" -Force
|
Copy-Item -Path "$(GUI_TARGET)" -Destination "C:\Program Files\Gobang\" -Force
|
||||||
|
|
||||||
# 运行控制台版本
|
|
||||||
run-console: $(CONSOLE_TARGET)
|
|
||||||
& ".\$(CONSOLE_TARGET)"
|
|
||||||
|
|
||||||
# 运行GUI版本
|
# 运行GUI版本
|
||||||
run-gui: $(GUI_TARGET)
|
run-gui: $(GUI_TARGET)
|
||||||
& ".\$(GUI_TARGET)"
|
& ".\$(GUI_TARGET)"
|
||||||
@@ -86,14 +74,12 @@ run-gui: $(GUI_TARGET)
|
|||||||
# 帮助信息
|
# 帮助信息
|
||||||
help:
|
help:
|
||||||
@echo Available targets:
|
@echo Available targets:
|
||||||
@echo all - Build both console and GUI versions
|
@echo all - Build GUI version
|
||||||
@echo console - Build console version only
|
@echo gui - Build GUI version
|
||||||
@echo gui - Build GUI version only
|
|
||||||
@echo clean - Remove all object files and executables
|
@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 run-gui - Build and run GUI version
|
||||||
@echo install - Install executables to system directory
|
@echo install - Install executables to system directory
|
||||||
@echo help - Show this help message
|
@echo help - Show this help message
|
||||||
|
|
||||||
# 声明伪目标
|
# 声明伪目标
|
||||||
.PHONY: all clean console gui install run-console run-gui help directories
|
.PHONY: all clean gui install run-gui help directories
|
||||||
|
|||||||
-193
@@ -1,193 +0,0 @@
|
|||||||
五子棋对战系统 - 代码统计报告
|
|
||||||
========================================
|
|
||||||
|
|
||||||
项目名称:五子棋多模式对战系统
|
|
||||||
统计时间:2025年10月8日
|
|
||||||
项目版本:v8.2
|
|
||||||
开发语言:C语言 + SDL3图形库
|
|
||||||
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行
|
|
||||||
└── gui.c :450行 (v8.0新增)
|
|
||||||
|
|
||||||
【头文件 (.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行
|
|
||||||
└── gui.h :85行 (v8.0新增)
|
|
||||||
|
|
||||||
========================================
|
|
||||||
📈 总计统计
|
|
||||||
========================================
|
|
||||||
|
|
||||||
总代码行数:4,933行 (v8.0新增535行)
|
|
||||||
|
|
||||||
文件类型分布:
|
|
||||||
• C源文件:3,977行 (80.6%)
|
|
||||||
• 头文件:956行 (19.4%)
|
|
||||||
|
|
||||||
v8.0版本新增:
|
|
||||||
• GUI图形界面模块:535行 (10.8%)
|
|
||||||
• 安装包配置文件:2个
|
|
||||||
• 编译脚本文件:1个
|
|
||||||
|
|
||||||
模块代码分布:
|
|
||||||
• 游戏模式模块:917行 (18.6%)
|
|
||||||
• AI智能模块:589行 (11.9%)
|
|
||||||
• GUI图形界面模块:535行 (10.8%) (v8.0新增)
|
|
||||||
• 记录系统模块: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双平台兼容
|
|
||||||
|
|
||||||
【技术亮点】
|
|
||||||
• SDL3图形化界面实现(v8.0新增)
|
|
||||||
• 双版本架构设计(控制台+GUI)(v8.0新增)
|
|
||||||
• 鼠标交互和事件驱动架构(v8.0新增)
|
|
||||||
• 专业安装包制作支持(v8.2新增)
|
|
||||||
• Inno Setup和NSIS双重打包方案(v8.2新增)
|
|
||||||
• 完整的软件分发体系(v8.2新增)
|
|
||||||
• 完整的网络对战功能实现
|
|
||||||
• 智能AI算法与评估系统
|
|
||||||
• 灵活的配置管理系统
|
|
||||||
• 详细的游戏记录与复盘功能
|
|
||||||
• 规范的禁手规则实现
|
|
||||||
• 实时计时器系统
|
|
||||||
• 全局变量统一管理
|
|
||||||
• 跨平台网络通信支持
|
|
||||||
• 代码架构模块化重构
|
|
||||||
• 配置参数集中化管理
|
|
||||||
• 类型定义标准化
|
|
||||||
|
|
||||||
【总体评价】
|
|
||||||
这是一个非常优秀的C语言项目,代码量适中但功能完整,
|
|
||||||
注释详尽,体现了良好的编程习惯和专业素养!
|
|
||||||
|
|
||||||
项目从单一的五子棋游戏发展为支持多种对战模式的完整系统,
|
|
||||||
包括人机对战、双人对战和网络对战,功能丰富,架构清晰,
|
|
||||||
是C语言项目开发的优秀范例。
|
|
||||||
|
|
||||||
v8.2版本进一步完善了软件分发体系,
|
|
||||||
通过Inno Setup和NSIS双重安装包方案,
|
|
||||||
实现了专业级的软件打包和部署功能,
|
|
||||||
为用户提供了便捷的一键安装体验,
|
|
||||||
标志着项目从开发阶段向产品化阶段的重要转变。
|
|
||||||
|
|
||||||
v8.0版本的图形化界面是项目发展的重大突破,
|
|
||||||
通过SDL3图形库实现了现代化的可视化界面,
|
|
||||||
支持鼠标交互操作,大幅提升了用户体验。
|
|
||||||
双版本架构设计既保持了控制台版本的轻量特性,
|
|
||||||
又提供了GUI版本的现代化体验,满足不同用户需求。
|
|
||||||
|
|
||||||
网络对战功能的加入使得项目具备了现代化游戏的特征,
|
|
||||||
支持实时在线对战,为用户提供了更丰富的游戏体验。
|
|
||||||
|
|
||||||
v7.0版本的代码架构重构是项目发展的重要里程碑,
|
|
||||||
通过配置统一管理、全局变量规范化、类型定义标准化等措施,
|
|
||||||
大幅提升了代码的可维护性和扩展性,为v8.0的GUI功能
|
|
||||||
奠定了坚实的架构基础。
|
|
||||||
|
|
||||||
========================================
|
|
||||||
📋 项目文件结构
|
|
||||||
========================================
|
|
||||||
|
|
||||||
核心模块:
|
|
||||||
• main.c/gobang.c - 主程序和核心游戏逻辑
|
|
||||||
• game_mode.c/h - 游戏模式管理(人机/双人/网络)
|
|
||||||
• ai.c/h - AI智能算法实现
|
|
||||||
• gui.c/h - SDL3图形化界面模块(v8.0新增)
|
|
||||||
• 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 - 游戏配置文件
|
|
||||||
• compile_gui.bat - GUI版本编译脚本(v8.0新增)
|
|
||||||
|
|
||||||
安装包目录:
|
|
||||||
• installer/ - 安装包制作目录(v8.0新增)
|
|
||||||
• setup.iss - Inno Setup安装脚本
|
|
||||||
• installer.nsi - NSIS安装脚本
|
|
||||||
|
|
||||||
文档目录:
|
|
||||||
• MD/ - 项目文档目录
|
|
||||||
• README_GUI.md - GUI版本使用指南(v8.0新增)
|
|
||||||
• TXT/ - 文本文档目录
|
|
||||||
• records/ - 游戏记录存储目录
|
|
||||||
|
|
||||||
开发环境:
|
|
||||||
• .vscode/ - VS Code配置
|
|
||||||
• .idea/ - IntelliJ IDEA配置
|
|
||||||
|
|
||||||
========================================
|
|
||||||
-108
@@ -1,108 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file 五子棋对战系统
|
|
||||||
* @brief C语言五子棋多模式对战系统
|
|
||||||
* @details 支持人机对战、双人对战、网络对战的完整五子棋游戏系统,v8.3新增IUP图形化界面
|
|
||||||
* @author 刘航宇(3364451258@qq.com、15236416560@163.com、lhy3364451258@outlook.com)
|
|
||||||
* @date 2026-03-16
|
|
||||||
* @version 8.3
|
|
||||||
* @note
|
|
||||||
* 1. v8.3 图形界面重构:
|
|
||||||
* - 🎨 IUP图形化界面:迁移至轻量级IUP库,无需复杂配置即可运行
|
|
||||||
* - 🏗️ 构建系统优化:引入Makefile,实现标准化编译流程
|
|
||||||
* - 📂 目录结构重构:分离src、obj、bin目录,项目结构更清晰
|
|
||||||
* - 🌐 本地化资源:集成第三方库到libs目录,实现开箱即用
|
|
||||||
*
|
|
||||||
* 2. v8.2专业安装包:
|
|
||||||
* - 📦 双重打包方案:支持Inno Setup和NSIS两种安装包制作
|
|
||||||
* - 🚀 一键安装部署:完整的软件分发解决方案
|
|
||||||
* - 🔧 安装程序优化:自动创建快捷方式和注册表项
|
|
||||||
* - 🗂️ 完整文件打包:包含所有源码、文档和依赖文件
|
|
||||||
* - 🔄 完整卸载功能:支持干净的软件卸载和清理
|
|
||||||
* - 💼 产品化部署:从开发工具向商业软件的转变
|
|
||||||
*
|
|
||||||
* 3. v8.0图形化界面:
|
|
||||||
* - 🎨 SDL3图形化界面:实现现代化可视化棋盘界面(已废弃,迁移至IUP)
|
|
||||||
* - 🖱️ 鼠标交互支持:直观的点击落子操作
|
|
||||||
* - 🏗️ 双版本架构:控制台版本和GUI版本并行支持
|
|
||||||
* - 🪟 窗口管理优化:自动居中、响应式设计
|
|
||||||
* - ⚡ 事件驱动架构:流畅的用户交互体验
|
|
||||||
* - 🌐 GUI网络支持:图形化界面支持网络对战
|
|
||||||
* 4. v7.0架构重构:
|
|
||||||
* - 🏗️ 代码架构全面重构,实现模块化设计
|
|
||||||
* - 📋 配置参数统一管理,所有配置集中到config.h
|
|
||||||
* - 🔧 全局变量规范化,统一在globals模块管理
|
|
||||||
* - 📝 类型定义标准化,集中在type.h中定义
|
|
||||||
* - 🌐 网络配置重构,从network.h迁移到config.h
|
|
||||||
* - 🔄 消除重复定义,提高代码一致性
|
|
||||||
* - 📚 完善文档体系,新增架构重构指南
|
|
||||||
* 5. v6.1完善功能:
|
|
||||||
* - 🌐 完善的网络对战模式,支持服务器/客户端架构
|
|
||||||
* - 🔗 实时数据同步,支持落子、悔棋、认输等网络功能
|
|
||||||
* - 🛡️ 网络安全验证和连接状态管理
|
|
||||||
* - 📡 跨平台网络支持(Windows/Linux)
|
|
||||||
* - 🔧 全局变量统一管理,优化代码结构
|
|
||||||
* - 📋 宏定义统一管理,消除重复定义
|
|
||||||
* - 🔄 网络协议优化,改进通信稳定性
|
|
||||||
* 6. 核心游戏功能:
|
|
||||||
* - 增加了对禁手规则的支持,防止玩家进行无意义的走法。
|
|
||||||
* - 新增了游戏计时器功能,限制每回合的思考时间。
|
|
||||||
* - 添加了复盘功能,支持保存和回顾对局记录。
|
|
||||||
* - 实现了评分系统,可以对每一步棋进行评分和分析。
|
|
||||||
* 7. 性能优化:
|
|
||||||
* - 🚀 优化了AI算法,使用Alpha-Beta剪枝提高搜索效率
|
|
||||||
* - 🎨 改进了棋盘渲染算法,减少了不必要的重绘操作
|
|
||||||
* - 💾 增加了内存管理优化,避免内存泄漏问题
|
|
||||||
* - ⚡ 网络通信优化,支持异步消息处理
|
|
||||||
* - 🔍 智能评分算法优化,提升AI决策质量
|
|
||||||
* 8. 用户界面改进:
|
|
||||||
* - 🎮 美化了游戏界面,增加了更多的视觉效果
|
|
||||||
* - ⌨️ 改进了用户交互体验,增加了快捷键支持
|
|
||||||
* - 🔊 添加了音效和背景音乐,提升游戏沉浸感
|
|
||||||
* - 💬 网络对战聊天界面,支持实时交流
|
|
||||||
* - 📊 游戏状态显示优化,清晰展示连接状态
|
|
||||||
* 9. 代码结构优化:
|
|
||||||
* - 🏗️ 重构了代码架构,提高了代码的可读性和可维护性
|
|
||||||
* - 📝 增加了详细的注释和文档,便于理解和修改
|
|
||||||
* - 🧩 采用了模块化设计,各功能模块相对独立
|
|
||||||
* - 🌍 新增网络模块,完整的网络通信架构
|
|
||||||
* - 🔧 全局状态统一管理,消除代码重复
|
|
||||||
* - 📋 配置文件标准化,支持灵活配置
|
|
||||||
* 10. 异常处理:
|
|
||||||
* - 🛡️ 增加了输入错误的异常处理机制,确保游戏的稳定性
|
|
||||||
* - 💡 优化了错误提示信息,帮助用户快速定位问题
|
|
||||||
* - 🔄 增加了程序崩溃恢复功能,提高游戏的可靠性
|
|
||||||
* - 🌐 网络连接异常处理,自动重连和超时管理
|
|
||||||
* - 📡 消息传输错误处理,确保数据完整性
|
|
||||||
* 11. 文档更新:
|
|
||||||
* - 📚 更新了README文件,提供详细的安装和使用说明
|
|
||||||
* - 💬 增加了代码注释,提高代码的可读性
|
|
||||||
* - 👨💻 添加了开发者文档,便于后续的功能扩展
|
|
||||||
* - 🌐 新增网络对战使用指南和配置说明
|
|
||||||
* - 🔧 API文档完善,支持二次开发
|
|
||||||
* 12. 版本控制:
|
|
||||||
* - 📦 使用Git进行版本控制,便于代码管理和协作开发
|
|
||||||
* - 🚀 建立了清晰的版本发布流程,确保代码质量
|
|
||||||
* - 🏷️ v7.0版本更新,代码架构全面重构
|
|
||||||
* - 🏷️ v6.1版本更新,网络功能完善优化
|
|
||||||
* - 📋 完整的变更日志,追踪功能演进
|
|
||||||
* 13. 测试:
|
|
||||||
* - ✅ 进行了全面的功能测试,确保各项功能正常运行
|
|
||||||
* - 🧪 增加了单元测试,提高代码的可靠性
|
|
||||||
* - ⚡ 进行了性能测试,优化了程序的运行效率
|
|
||||||
* - 🌐 网络功能压力测试,确保多人对战稳定性
|
|
||||||
* - 🔒 安全性测试,验证网络通信安全
|
|
||||||
* - 🔄 协议兼容性测试,确保通信协议稳定
|
|
||||||
* 14. 开源协议:
|
|
||||||
* - 📄 选择了MIT开源协议,允许用户自由使用、修改和分发代码
|
|
||||||
* - 🤝 欢迎社区贡献,共同完善项目
|
|
||||||
* 15. 贡献者:
|
|
||||||
* - 👨💻 感谢所有为项目做出贡献的开发者和用户
|
|
||||||
* - 🌟 特别感谢网络功能开发、测试和优化的贡献者
|
|
||||||
* 16. 联系信息:
|
|
||||||
* - 📧 如有问题或建议,请联系开发者:
|
|
||||||
* - 3364451258@qq.com
|
|
||||||
* - 15236416560@163.com
|
|
||||||
* - lhy3364451258@outlook.com
|
|
||||||
* - 🐛 Bug报告和功能建议欢迎通过邮件反馈
|
|
||||||
* - 💡 网络对战相关问题请详细描述网络环境和连接状态
|
|
||||||
*/
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
项目要求文档 - 五子棋游戏 (v8.3)
|
|
||||||
|
|
||||||
1. 项目概述
|
|
||||||
- 开发一个基于C语言的五子棋游戏,支持本地多人、AI对战和网络对战模式。
|
|
||||||
- v8.3版本(当前版本)引入了IUP图形库,实现了轻量级、跨平台的图形界面。
|
|
||||||
- v8.2版本完善了专业安装包制作体系,提供企业级的软件分发解决方案。
|
|
||||||
- v8.0版本实现双版本架构:支持命令行界面和GUI图形化界面。
|
|
||||||
- 包括游戏配置、记录保存、复盘功能和专业安装包。
|
|
||||||
- 提供现代化的可视化用户体验和传统控制台体验。
|
|
||||||
|
|
||||||
2. 功能需求
|
|
||||||
- **游戏模式**:
|
|
||||||
- 单人模式:玩家 vs AI
|
|
||||||
- 双人模式:本地两人对战
|
|
||||||
- 网络模式:通过TCP/IP进行远程对战
|
|
||||||
- **棋盘和规则**:
|
|
||||||
- 默认15x15棋盘,支持自定义大小(5-25)
|
|
||||||
- 支持禁手规则选项
|
|
||||||
- 先手黑子,五连珠获胜
|
|
||||||
- **AI功能**:
|
|
||||||
- 实现AI算法,支持不同难度级别(1-5级)
|
|
||||||
- AI应能计算最佳落子位置
|
|
||||||
- 支持Alpha-Beta剪枝算法和威胁检测
|
|
||||||
- **配置管理**:
|
|
||||||
- 支持修改棋盘大小、禁手规则、计时器、网络端口等
|
|
||||||
- 配置保存到INI文件
|
|
||||||
- 默认配置和重置功能
|
|
||||||
- 统一的输入验证机制
|
|
||||||
- **游戏记录**:
|
|
||||||
- 自动保存对局记录到CSV文件
|
|
||||||
- 支持复盘功能,查看历史对局
|
|
||||||
- 记录包含时间戳、玩家信息、棋局步骤等
|
|
||||||
- **用户界面**:
|
|
||||||
- **控制台界面**:传统命令行界面显示棋盘
|
|
||||||
- **图形化界面**(v8.3新增IUP):基于IUP的轻量级GUI界面
|
|
||||||
- **鼠标交互**:支持点击落子操作
|
|
||||||
- **窗口管理**:自动居中、响应式设计
|
|
||||||
- **事件驱动**:流畅的用户交互体验
|
|
||||||
- 支持坐标输入落子(数字格式:行 列)
|
|
||||||
- 显示当前玩家、计时、游戏状态等信息
|
|
||||||
- 菜单系统和配置界面
|
|
||||||
- **网络功能**:
|
|
||||||
- 支持服务器/客户端模式
|
|
||||||
- 实时传输棋子位置
|
|
||||||
- 超时和断线处理
|
|
||||||
- 支持端口配置
|
|
||||||
|
|
||||||
3. 技术要求
|
|
||||||
- 使用C语言开发
|
|
||||||
- Windows平台,包含Winsock网络库(-lws2_32链接)
|
|
||||||
- **IUP图形库**(v8.3新增):用于GUI界面开发,替代原SDL3方案
|
|
||||||
- **双版本架构**:控制台版本和GUI版本并行支持
|
|
||||||
- 模块化设计:分离游戏逻辑、AI、配置、网络、UI、GUI等模块
|
|
||||||
- **安装包制作**:支持Inno Setup和NSIS专业安装程序
|
|
||||||
- **构建系统**:使用Makefile管理编译流程
|
|
||||||
- 错误处理和统一的输入验证
|
|
||||||
- 支持跨平台编译(Windows/Linux)
|
|
||||||
|
|
||||||
4. 非功能需求
|
|
||||||
- 性能:响应时间<1秒(AI计算除外)
|
|
||||||
- 可维护性:代码模块化,注释清晰
|
|
||||||
- 兼容性:Windows 11,支持GCC编译器
|
|
||||||
- 可扩展性:易于添加新的AI算法和游戏模式
|
|
||||||
- 部署便捷:依赖库本地化,开箱即用
|
|
||||||
|
|
||||||
5. 交付物
|
|
||||||
- 源代码文件(.c/.h文件)
|
|
||||||
- 配置文件(gobang_config.ini)
|
|
||||||
- 记录文件夹(records/)
|
|
||||||
- 文档:README.md、AI增强指南、架构重构指南、网络功能说明、图标指南等
|
|
||||||
- 代码统计报告和项目简介
|
|
||||||
Binary file not shown.
BIN
Binary file not shown.
@@ -16,3 +16,6 @@ NETWORK_PORT=8888
|
|||||||
|
|
||||||
# 网络超时时间 (毫秒)
|
# 网络超时时间 (毫秒)
|
||||||
NETWORK_TIMEOUT=5000
|
NETWORK_TIMEOUT=5000
|
||||||
|
|
||||||
|
# AI难度 (1-5)
|
||||||
|
AI_DIFFICULTY=3
|
||||||
|
|||||||
@@ -153,34 +153,4 @@ void save_game_config();
|
|||||||
*/
|
*/
|
||||||
void reset_to_default_config();
|
void reset_to_default_config();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示当前配置
|
|
||||||
*/
|
|
||||||
void display_current_config();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置棋盘大小
|
|
||||||
*/
|
|
||||||
void config_board_size();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置禁手规则
|
|
||||||
*/
|
|
||||||
void config_forbidden_moves();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置计时器
|
|
||||||
*/
|
|
||||||
void config_timer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置网络参数
|
|
||||||
*/
|
|
||||||
void config_network();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置管理主菜单
|
|
||||||
*/
|
|
||||||
void config_management_menu();
|
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file game_mode.h
|
|
||||||
* @brief 五子棋游戏框架头文件
|
|
||||||
* @note 本文件定义了五子棋游戏的四种主要模式:
|
|
||||||
* 1. AI对战模式
|
|
||||||
* 2. 双人对战模式
|
|
||||||
* 3. 网络对战模式
|
|
||||||
* 4. 复盘模式
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GAME_MODE_H
|
|
||||||
#define GAME_MODE_H
|
|
||||||
|
|
||||||
#include "gobang.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从用户获取整数输入
|
|
||||||
*
|
|
||||||
* @param prompt 提示信息
|
|
||||||
* @param min 最小值
|
|
||||||
* @param max 最大值
|
|
||||||
* @return int 输入的整数
|
|
||||||
*/
|
|
||||||
int get_integer_input(const char *prompt, int min, int max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理玩家回合
|
|
||||||
*
|
|
||||||
* @param x 玩家输入的横坐标
|
|
||||||
* @param y 玩家输入的纵坐标
|
|
||||||
* @return true 输入有效
|
|
||||||
* @return false 输入无效
|
|
||||||
*/
|
|
||||||
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回合
|
|
||||||
*
|
|
||||||
* @param current_player 当前玩家
|
|
||||||
*/
|
|
||||||
bool handle_player_turn(int current_player);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief AI对战模式
|
|
||||||
* 实现玩家与AI的对战逻辑
|
|
||||||
*/
|
|
||||||
void run_ai_game();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 双人对战模式
|
|
||||||
* 实现两个玩家之间的对战逻辑
|
|
||||||
*/
|
|
||||||
void run_pvp_game();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 复盘模式
|
|
||||||
* 加载并重现历史对局
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏规则
|
|
||||||
*/
|
|
||||||
void show_game_rules();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示关于游戏信息
|
|
||||||
*/
|
|
||||||
void show_about_game();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启动图形化界面
|
|
||||||
*/
|
|
||||||
void run_gui_mode();
|
|
||||||
|
|
||||||
#endif // GAME_MODE_H
|
|
||||||
@@ -20,6 +20,11 @@
|
|||||||
// 函数原型
|
// 函数原型
|
||||||
|
|
||||||
// --- 游戏核心逻辑 ---
|
// --- 游戏核心逻辑 ---
|
||||||
|
/**
|
||||||
|
* @brief 初始化棋盘,将所有位置设置为空(EMPTY)
|
||||||
|
*/
|
||||||
|
void empty_board();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查指定坐标是否为有效落子点(在棋盘内且为空)
|
* @brief 检查指定坐标是否为有效落子点(在棋盘内且为空)
|
||||||
* @param x 待检查的行坐标 (0-based)
|
* @param x 待检查的行坐标 (0-based)
|
||||||
|
|||||||
@@ -81,4 +81,16 @@ void start_pve_game_gui();
|
|||||||
*/
|
*/
|
||||||
void start_replay_gui();
|
void start_replay_gui();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 启动图形化界面模式
|
||||||
|
* @note 替代原来的 main 函数中的 GUI 分支逻辑
|
||||||
|
*/
|
||||||
|
int init_gui(); // Already declared
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 运行图形化界面模式
|
||||||
|
* @details 主循环处理事件、渲染画面和更新状态
|
||||||
|
*/
|
||||||
|
void run_gui_mode();
|
||||||
|
|
||||||
#endif // GUI_H
|
#endif // GUI_H
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file init_board.h
|
|
||||||
* @brief 初始化游戏棋盘头文件
|
|
||||||
* @note 本文件定义了初始化游戏棋盘的相关函数和全局变量。
|
|
||||||
* 它负责设置游戏的初始状态,包括棋盘大小、玩家标识、游戏规则等。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INIT_BOARD_H
|
|
||||||
#define INIT_BOARD_H
|
|
||||||
|
|
||||||
#include "gobang.h"
|
|
||||||
|
|
||||||
// --- 游戏初始化 ---
|
|
||||||
/**
|
|
||||||
* @brief 初始化棋盘,将所有位置设置为空(EMPTY)
|
|
||||||
*/
|
|
||||||
void empty_board();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 将当前棋盘状态打印到控制台
|
|
||||||
*/
|
|
||||||
void print_board();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置当前游戏的棋盘大小
|
|
||||||
*/
|
|
||||||
void setup_board_size();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置游戏选项,如是否启用禁手、计时器等
|
|
||||||
*/
|
|
||||||
void setup_game_options();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 决定先手玩家
|
|
||||||
* @param player1 玩家1的标识
|
|
||||||
* @param player2 玩家2的标识
|
|
||||||
* @return 返回先手玩家的标识
|
|
||||||
*/
|
|
||||||
int determine_first_player(int player1, int player2);
|
|
||||||
|
|
||||||
#endif // INIT_H
|
|
||||||
@@ -10,12 +10,6 @@
|
|||||||
#include "gobang.h"
|
#include "gobang.h"
|
||||||
|
|
||||||
// --- 复盘与记录功能 ---
|
// --- 复盘与记录功能 ---
|
||||||
/**
|
|
||||||
* @brief 进入复盘流程,回顾整局游戏
|
|
||||||
* @param game_mode 游戏模式(1为人机对战,2为双人对战)
|
|
||||||
*/
|
|
||||||
void review_process(int game_mode);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将当前对局记录保存到文件
|
* @brief 将当前对局记录保存到文件
|
||||||
* @param filename 要保存到的文件名
|
* @param filename 要保存到的文件名
|
||||||
@@ -24,12 +18,6 @@ void review_process(int game_mode);
|
|||||||
*/
|
*/
|
||||||
int save_game_to_file(const char *filename, int game_mode);
|
int save_game_to_file(const char *filename, int game_mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理保存游戏记录的逻辑
|
|
||||||
* @param game_mode 游戏模式
|
|
||||||
*/
|
|
||||||
void handle_save_record(int game_mode);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 从文件加载游戏记录
|
* @brief 从文件加载游戏记录
|
||||||
* @param filename 要加载的文件名
|
* @param filename 要加载的文件名
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file ui.h
|
|
||||||
* @brief
|
|
||||||
* @note 本文件定义了用户界面相关的函数和数据结构。
|
|
||||||
* 它负责处理用户输入、显示游戏界面、提示信息等与用户交互的功能。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef UI_H
|
|
||||||
#define UI_H
|
|
||||||
|
|
||||||
#include "gobang.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief UI模块 - 用户界面相关功能
|
|
||||||
* @author 刘航宇
|
|
||||||
* @date 2025-07-10
|
|
||||||
* @version 5.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏主菜单
|
|
||||||
*/
|
|
||||||
void display_main_menu();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示棋盘
|
|
||||||
*/
|
|
||||||
void display_board();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏状态信息
|
|
||||||
* @param current_player 当前玩家
|
|
||||||
* @param step_count 当前步数
|
|
||||||
*/
|
|
||||||
void display_game_status(int current_player, int step_count);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示获胜信息
|
|
||||||
* @param winner 获胜者
|
|
||||||
*/
|
|
||||||
void display_winner(int winner);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏设置菜单
|
|
||||||
*/
|
|
||||||
void display_settings_menu();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清屏函数
|
|
||||||
*/
|
|
||||||
void clear_screen();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 暂停等待用户输入
|
|
||||||
* @param prompt 提示信息
|
|
||||||
*/
|
|
||||||
void pause_for_input(const char* prompt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏规则
|
|
||||||
*/
|
|
||||||
void display_game_rules();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示关于信息
|
|
||||||
*/
|
|
||||||
void display_about();
|
|
||||||
|
|
||||||
#endif // UI_H
|
|
||||||
-127
@@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "ui.h"
|
|
||||||
#include "game_mode.h"
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -132,128 +130,3 @@ void reset_to_default_config()
|
|||||||
|
|
||||||
printf("已重置为默认配置\n");
|
printf("已重置为默认配置\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示当前配置
|
|
||||||
*/
|
|
||||||
void display_current_config()
|
|
||||||
{
|
|
||||||
printf("\n===== 当前游戏配置 =====\n");
|
|
||||||
printf("棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
|
|
||||||
printf("禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭");
|
|
||||||
printf("计时器: %s\n", use_timer ? "开启" : "关闭");
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
printf("时间限制: %d 分钟\n", time_limit / 60);
|
|
||||||
}
|
|
||||||
printf("网络端口: %d\n", network_port);
|
|
||||||
printf("网络超时: %d 毫秒\n", network_timeout);
|
|
||||||
printf("=====================\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置棋盘大小
|
|
||||||
*/
|
|
||||||
void config_board_size()
|
|
||||||
{
|
|
||||||
printf("\n当前棋盘大小: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
|
|
||||||
|
|
||||||
int new_size = get_integer_input("请输入新的棋盘大小: ", MIN_BOARD_SIZE, MAX_BOARD_SIZE);
|
|
||||||
BOARD_SIZE = new_size;
|
|
||||||
printf("棋盘大小已设置为: %d x %d\n", BOARD_SIZE, BOARD_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置禁手规则
|
|
||||||
*/
|
|
||||||
void config_forbidden_moves()
|
|
||||||
{
|
|
||||||
printf("\n当前禁手规则: %s\n", use_forbidden_moves ? "开启" : "关闭");
|
|
||||||
|
|
||||||
int choice = get_integer_input("是否启用禁手规则?(1=开启, 0=关闭): ", 0, 1);
|
|
||||||
use_forbidden_moves = (choice != 0);
|
|
||||||
printf("禁手规则已%s\n", use_forbidden_moves ? "开启" : "关闭");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置计时器
|
|
||||||
*/
|
|
||||||
void config_timer()
|
|
||||||
{
|
|
||||||
printf("\n当前计时器: %s\n", use_timer ? "开启" : "关闭");
|
|
||||||
|
|
||||||
int choice = get_integer_input("是否启用计时器?(1=开启, 0=关闭): ", 0, 1);
|
|
||||||
use_timer = choice;
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
int new_limit = get_integer_input("请输入时间限制(分钟): ", 1, 999);
|
|
||||||
time_limit = new_limit * 60; // 转换为秒数存储
|
|
||||||
printf("计时器已开启,时间限制: %d 分钟\n", time_limit / 60);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("计时器已关闭\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置网络参数
|
|
||||||
*/
|
|
||||||
void config_network()
|
|
||||||
{
|
|
||||||
printf("\n===== 网络配置 =====\n");
|
|
||||||
printf("当前网络端口: %d\n", network_port);
|
|
||||||
printf("当前网络超时: %d 毫秒\n", network_timeout);
|
|
||||||
|
|
||||||
int new_port = get_integer_input("请输入新的网络端口: ", MIN_NETWORK_PORT, MAX_NETWORK_PORT);
|
|
||||||
network_port = new_port;
|
|
||||||
printf("网络端口已设置为: %d\n", network_port);
|
|
||||||
|
|
||||||
int new_timeout = get_integer_input("请输入网络超时时间(毫秒, 建议1000-10000): ", 1000, 60000);
|
|
||||||
network_timeout = new_timeout;
|
|
||||||
printf("网络超时已设置为: %d 毫秒\n", network_timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置管理主菜单
|
|
||||||
*/
|
|
||||||
void config_management_menu()
|
|
||||||
{
|
|
||||||
int choice;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
clear_screen();
|
|
||||||
display_settings_menu();
|
|
||||||
display_current_config();
|
|
||||||
|
|
||||||
choice = get_integer_input("请选择操作(0-5): ", 0, 5);
|
|
||||||
|
|
||||||
switch (choice)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
config_board_size();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
config_forbidden_moves();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
config_timer();
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
config_network();
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
printf("AI难度设置功能开发中...\n");
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
save_game_config();
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
printf("无效的选择!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pause_for_input("按任意键继续...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-971
@@ -1,971 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file game_mode.c
|
|
||||||
* @brief 五子棋游戏框架源文件
|
|
||||||
* @note 本文件定义了五子棋游戏的四种主要模式:
|
|
||||||
* 1. AI对战模式
|
|
||||||
* 2. 双人对战模式
|
|
||||||
* 3. 网络对战模式
|
|
||||||
* 4. 复盘模式
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "game_mode.h"
|
|
||||||
#include "init_board.h"
|
|
||||||
#include "gobang.h"
|
|
||||||
#include "ai.h"
|
|
||||||
#include "record.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "ui.h"
|
|
||||||
#include "gui.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
// 全局变量现在在globals.c中定义
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <direct.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从用户获取整数输入
|
|
||||||
*
|
|
||||||
* @param prompt 提示信息
|
|
||||||
* @param min 最小值
|
|
||||||
* @param max 最大值
|
|
||||||
* @return int 用户输入的整数
|
|
||||||
*/
|
|
||||||
int get_integer_input(const char *prompt, int min, int max)
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
int result;
|
|
||||||
char ch;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
printf("%s", prompt);
|
|
||||||
result = scanf("%d", &value);
|
|
||||||
|
|
||||||
if (result == 1 && value >= min && value <= max)
|
|
||||||
{
|
|
||||||
// 清除输入缓冲区中剩余的字符
|
|
||||||
while ((ch = getchar()) != '\n' && ch != EOF)
|
|
||||||
;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 清除无效输入
|
|
||||||
while ((ch = getchar()) != '\n' && ch != EOF)
|
|
||||||
;
|
|
||||||
printf("输入无效,请输入一个介于 %d 和 %d 之间的整数。\n", min, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理玩家回合
|
|
||||||
*
|
|
||||||
* @param current_player
|
|
||||||
* @return true
|
|
||||||
* @return false
|
|
||||||
*/
|
|
||||||
bool parse_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)
|
|
||||||
{
|
|
||||||
// 如果第二个数字不可用,则检查特殊命令
|
|
||||||
if (*x == INPUT_UNDO)
|
|
||||||
{
|
|
||||||
int steps_to_undo;
|
|
||||||
steps_to_undo = get_integer_input("请输入要悔棋的步数(双方各退步数相同): ", 1, step_count / 2);
|
|
||||||
if (return_move(steps_to_undo * 2))
|
|
||||||
{
|
|
||||||
printf("成功悔棋,双方各退 %d 步!\n", steps_to_undo);
|
|
||||||
print_board();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("无法悔棋!\n");
|
|
||||||
}
|
|
||||||
return 0; // 特殊命令已处理
|
|
||||||
}
|
|
||||||
else if (*x == INPUT_SAVE)
|
|
||||||
{
|
|
||||||
// ... 处理保存 ...
|
|
||||||
return false; // 特殊命令
|
|
||||||
}
|
|
||||||
else if (*x == INPUT_EXIT)
|
|
||||||
{
|
|
||||||
// ... 处理退出 ...
|
|
||||||
return false; // 特殊命令
|
|
||||||
}
|
|
||||||
printf("无效输入,请输入两个数字坐标。\n");
|
|
||||||
while (getchar() != '\n')
|
|
||||||
;
|
|
||||||
return 0; // 无效输入
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// sscanf失败,检查特殊命令
|
|
||||||
if (input[0] == 'r' || input[0] == 'R')
|
|
||||||
{
|
|
||||||
int steps_to_undo;
|
|
||||||
steps_to_undo = get_integer_input("请输入要悔棋的步数(双方各退步数相同): ", 1, step_count / 2);
|
|
||||||
if (return_move(steps_to_undo * 2))
|
|
||||||
{
|
|
||||||
printf("成功悔棋,双方各退 %d 步!\n", steps_to_undo);
|
|
||||||
print_board();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("无法悔棋!\n");
|
|
||||||
}
|
|
||||||
return false; // 特殊命令
|
|
||||||
}
|
|
||||||
else if (input[0] == 's' || input[0] == 'S')
|
|
||||||
{
|
|
||||||
*x = INPUT_SURRENDER;
|
|
||||||
int confirm = get_integer_input("确认认输?(1:是/0:否): ", 0, 1);
|
|
||||||
if (confirm)
|
|
||||||
{
|
|
||||||
printf("玩家选择认输!\n");
|
|
||||||
return 1; // 正常回合完成 // 返回认输命令
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("取消认输!\n");
|
|
||||||
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");
|
|
||||||
return false; // 无效输入
|
|
||||||
}
|
|
||||||
return true; // 有效坐标
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理玩家回合
|
|
||||||
* @param current_player 当前玩家
|
|
||||||
* @return true
|
|
||||||
*/
|
|
||||||
bool handle_player_turn(int current_player)
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
time_t start_time, end_time;
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
time(&start_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x--;
|
|
||||||
y--;
|
|
||||||
|
|
||||||
if (player_move(x, y, current_player))
|
|
||||||
{
|
|
||||||
break; // 成功落子,跳出循环
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("坐标无效!请重新输入。\n");
|
|
||||||
// 继续循环,重新输入坐标
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print_board();
|
|
||||||
|
|
||||||
if (check_win(x, y, current_player))
|
|
||||||
{
|
|
||||||
printf("\n玩家%d获胜!\n", current_player);
|
|
||||||
return false; // 游戏结束
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // 成功落子
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 运行AI游戏
|
|
||||||
* @note 从文件中加载历史记录并进行复盘
|
|
||||||
* @param AI_DEPTH AI的搜索深度
|
|
||||||
*/
|
|
||||||
void run_ai_game()
|
|
||||||
{
|
|
||||||
// 重置评分计算标志,确保每局游戏都会重新计算评分
|
|
||||||
scores_calculated = 0;
|
|
||||||
|
|
||||||
// AI对战模式
|
|
||||||
int AI_DEPTH = DEFAULT_AI_DEPTH;
|
|
||||||
AI_DEPTH = get_integer_input("请选择AI难度(1~5), 数字越大越强,注意数字越大AI思考时间越长!):", 1, 5);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief AI的防守系数,系数越大越倾向于防守
|
|
||||||
* @note 1~1.5
|
|
||||||
* 2~1.6
|
|
||||||
* 3~1.7
|
|
||||||
* 4~1.8
|
|
||||||
* 5~1.9
|
|
||||||
*/
|
|
||||||
defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT + (AI_DEPTH - 1) * 0.1;
|
|
||||||
|
|
||||||
empty_board();
|
|
||||||
int current_player = determine_first_player(PLAYER, AI);
|
|
||||||
print_board();
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
if (current_player == PLAYER)
|
|
||||||
{
|
|
||||||
int old_step_count = step_count;
|
|
||||||
if (!handle_player_turn(current_player))
|
|
||||||
{
|
|
||||||
break; // 游戏结束或超时
|
|
||||||
}
|
|
||||||
if (step_count > old_step_count)
|
|
||||||
{
|
|
||||||
// 检查玩家是否获胜
|
|
||||||
Step last_step = steps[step_count - 1];
|
|
||||||
if (check_win(last_step.x, last_step.y, PLAYER))
|
|
||||||
{
|
|
||||||
printf("\n玩家获胜!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current_player = AI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("\nAI思考中...\n");
|
|
||||||
time_t start_time, end_time;
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
time(&start_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
ai_move(AI_DEPTH);
|
|
||||||
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
time(&end_time);
|
|
||||||
if (difftime(end_time, start_time) > time_limit)
|
|
||||||
{
|
|
||||||
printf("\nAI超时, 玩家获胜!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print_board();
|
|
||||||
Step last_step = steps[step_count - 1];
|
|
||||||
if (check_win(last_step.x, last_step.y, AI))
|
|
||||||
{
|
|
||||||
printf("\nAI获胜!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current_player = PLAYER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (step_count == BOARD_SIZE * BOARD_SIZE)
|
|
||||||
{
|
|
||||||
printf("\n平局!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("===== 游戏结束 =====\n");
|
|
||||||
review_process(GAME_MODE_AI); // AI对战模式
|
|
||||||
handle_save_record(GAME_MODE_AI); // AI对战模式
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 运行双人对战模式
|
|
||||||
* @note 从文件中加载历史记录并进行复盘
|
|
||||||
*/
|
|
||||||
void run_pvp_game()
|
|
||||||
{
|
|
||||||
// 重置评分计算标志,确保每局游戏都会重新计算评分
|
|
||||||
scores_calculated = 0;
|
|
||||||
|
|
||||||
// 双人对战模式
|
|
||||||
empty_board();
|
|
||||||
int current_player = determine_first_player(PLAYER1, PLAYER2);
|
|
||||||
print_board();
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
int old_step_count = step_count;
|
|
||||||
if (!handle_player_turn(current_player))
|
|
||||||
{
|
|
||||||
break; // 游戏结束或超时
|
|
||||||
}
|
|
||||||
|
|
||||||
if (step_count == BOARD_SIZE * BOARD_SIZE)
|
|
||||||
{
|
|
||||||
printf("\n平局!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (step_count > old_step_count)
|
|
||||||
{
|
|
||||||
current_player = (current_player == PLAYER1) ? PLAYER2 : PLAYER1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("===== 游戏结束 =====\n");
|
|
||||||
review_process(GAME_MODE_PVP); // 双人对战模式
|
|
||||||
handle_save_record(GAME_MODE_PVP); // 双人对战模式
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 运行复盘模式
|
|
||||||
* @note 从文件中加载历史记录并进行复盘
|
|
||||||
*/
|
|
||||||
void run_review_mode()
|
|
||||||
{
|
|
||||||
char filename[100];
|
|
||||||
char record_files[100][100];
|
|
||||||
int file_count = 0;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
WIN32_FIND_DATA ffd;
|
|
||||||
HANDLE hFind = FindFirstFile("records\\*", &ffd);
|
|
||||||
if (hFind != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
||||||
{
|
|
||||||
strcpy(record_files[file_count++], ffd.cFileName);
|
|
||||||
}
|
|
||||||
} while (FindNextFile(hFind, &ffd) != 0);
|
|
||||||
FindClose(hFind);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (file_count > 0)
|
|
||||||
{
|
|
||||||
printf("发现以下复盘文件:\n");
|
|
||||||
for (int i = 0; i < file_count; i++)
|
|
||||||
{
|
|
||||||
printf("%d. %s\n", i + 1, record_files[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char prompt[150];
|
|
||||||
sprintf(prompt, "请输入复盘文件编号(1-%d),或输入0以手动输入文件名: ", file_count);
|
|
||||||
int choice = get_integer_input(prompt, 0, file_count);
|
|
||||||
|
|
||||||
if (choice > 0)
|
|
||||||
{
|
|
||||||
strcpy(filename, record_files[choice - 1]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("请输入完整文件名: ");
|
|
||||||
scanf("%s", filename);
|
|
||||||
int c;
|
|
||||||
while ((c = getchar()) != '\n' && c != EOF)
|
|
||||||
;
|
|
||||||
|
|
||||||
int possible_choice = atoi(filename);
|
|
||||||
if (possible_choice > 0 && possible_choice <= file_count)
|
|
||||||
{
|
|
||||||
strcpy(filename, record_files[possible_choice - 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("未找到任何复盘文件,请输入复盘文件地址: ");
|
|
||||||
scanf("%s", filename);
|
|
||||||
int c;
|
|
||||||
while ((c = getchar()) != '\n' && c != EOF)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
int game_mode = load_game_from_file(filename);
|
|
||||||
if (game_mode != 0)
|
|
||||||
{
|
|
||||||
if (game_mode == 1)
|
|
||||||
{
|
|
||||||
printf("加载AI对战模式复盘文件成功!\n");
|
|
||||||
}
|
|
||||||
else if (game_mode == 2)
|
|
||||||
{
|
|
||||||
printf("加载双人对战模式复盘文件成功!\n");
|
|
||||||
}
|
|
||||||
review_process(game_mode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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 显示游戏规则
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
printf("退出图形化界面\n");
|
|
||||||
cleanup_gui();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("图形化界面启动失败!请检查SDL3库是否正确安装。\n");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
+17
-2
@@ -5,8 +5,6 @@
|
|||||||
* 它包含了游戏棋盘的表示、玩家操作、规则检查以及AI决策等功能。
|
* 它包含了游戏棋盘的表示、玩家操作、规则检查以及AI决策等功能。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "game_mode.h"
|
|
||||||
#include "init_board.h"
|
|
||||||
#include "gobang.h"
|
#include "gobang.h"
|
||||||
#include "ai.h"
|
#include "ai.h"
|
||||||
#include "record.h"
|
#include "record.h"
|
||||||
@@ -16,6 +14,23 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化棋盘为全空状态并重置步数计数器
|
||||||
|
* 清空棋盘数组并将所有位置设为EMPTY,同时将step_count重置为0
|
||||||
|
*/
|
||||||
|
void empty_board()
|
||||||
|
{
|
||||||
|
// 初始化棋盘状态为全空
|
||||||
|
for (int i = 0; i < BOARD_SIZE; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < BOARD_SIZE; j++)
|
||||||
|
{
|
||||||
|
board[i][j] = EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step_count = 0; // 重置步数计数器
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查棋盘(x, y)位置是否为空
|
* @brief 检查棋盘(x, y)位置是否为空
|
||||||
* @param x 行坐标(0-base)
|
* @param x 行坐标(0-base)
|
||||||
|
|||||||
@@ -9,9 +9,7 @@
|
|||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include <iup.h>
|
#include <iup.h>
|
||||||
#include <iupdraw.h>
|
#include <iupdraw.h>
|
||||||
#include "ui.h"
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "init_board.h"
|
|
||||||
#include "gobang.h"
|
#include "gobang.h"
|
||||||
#include "gui_menu.h"
|
#include "gui_menu.h"
|
||||||
#include "ai.h"
|
#include "ai.h"
|
||||||
@@ -26,13 +24,15 @@ static Ihandle *lbl_player = NULL;
|
|||||||
static Ihandle *lbl_status = NULL;
|
static Ihandle *lbl_status = NULL;
|
||||||
static int gui_loop_running = 0;
|
static int gui_loop_running = 0;
|
||||||
static int gui_game_mode = 0; // 0: PvP, 1: PvE, 2: Replay
|
static int gui_game_mode = 0; // 0: PvP, 1: PvE, 2: Replay
|
||||||
static int replay_total_steps = 0; // For Replay Mode
|
static int replay_total_steps = 0; // 为了复盘而记录的总步数
|
||||||
|
|
||||||
// 回调函数
|
// 回调函数
|
||||||
static int action_cb(Ihandle *ih);
|
static int action_cb(Ihandle *ih);
|
||||||
static int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status);
|
static int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status);
|
||||||
static int k_any_cb(Ihandle *ih, int c);
|
static int k_any_cb(Ihandle *ih, int c);
|
||||||
void create_game_window(); // 移除前向声明
|
void create_game_window(); // 移除前向声明
|
||||||
|
static int btn_replay_sel_ok_cb(Ihandle *ih);
|
||||||
|
static int btn_replay_sel_cancel_cb(Ihandle *ih);
|
||||||
|
|
||||||
// 辅助函数:设置绘图颜色
|
// 辅助函数:设置绘图颜色
|
||||||
static void set_draw_color(Ihandle *ih, unsigned char r, unsigned char g, unsigned char b)
|
static void set_draw_color(Ihandle *ih, unsigned char r, unsigned char g, unsigned char b)
|
||||||
@@ -123,7 +123,7 @@ static void draw_stones_iup(Ihandle *ih)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 标记最后落子位置 (红色小点)
|
// 标记最后落子位置 (红色小点)
|
||||||
if (step_count > 0)
|
if (step_count > 0 && step_count <= MAX_STEPS)
|
||||||
{
|
{
|
||||||
// 绘制最后一步的标记
|
// 绘制最后一步的标记
|
||||||
// 最后一步的坐标是 steps[step_count-1]
|
// 最后一步的坐标是 steps[step_count-1]
|
||||||
@@ -179,6 +179,17 @@ static void update_ui_labels()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 显示消息
|
||||||
|
*/
|
||||||
|
void show_message(const char *message)
|
||||||
|
{
|
||||||
|
strncpy(status_message, message, sizeof(status_message) - 1);
|
||||||
|
status_message[sizeof(status_message) - 1] = '\0';
|
||||||
|
update_ui_labels();
|
||||||
|
printf("%s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
// ACTION 回调:负责重绘
|
// ACTION 回调:负责重绘
|
||||||
static int action_cb(Ihandle *ih)
|
static int action_cb(Ihandle *ih)
|
||||||
{
|
{
|
||||||
@@ -294,7 +305,7 @@ static int btn_replay_prev_cb(Ihandle *ih)
|
|||||||
static int btn_replay_next_cb(Ihandle *ih)
|
static int btn_replay_next_cb(Ihandle *ih)
|
||||||
{
|
{
|
||||||
(void)ih;
|
(void)ih;
|
||||||
if (step_count < replay_total_steps)
|
if (step_count < replay_total_steps && step_count >= 0) // Ensure step_count is valid
|
||||||
{
|
{
|
||||||
Step s = steps[step_count];
|
Step s = steps[step_count];
|
||||||
board[s.x][s.y] = s.player;
|
board[s.x][s.y] = s.player;
|
||||||
@@ -310,12 +321,29 @@ static int btn_replay_next_cb(Ihandle *ih)
|
|||||||
static int btn_back_cb(Ihandle *ih)
|
static int btn_back_cb(Ihandle *ih)
|
||||||
{
|
{
|
||||||
(void)ih;
|
(void)ih;
|
||||||
|
printf("DEBUG: Back to Menu clicked\n");
|
||||||
|
|
||||||
|
// 1. Show main menu FIRST
|
||||||
|
show_main_menu();
|
||||||
|
printf("DEBUG: Main menu shown\n");
|
||||||
|
|
||||||
|
// 2. Destroy game window
|
||||||
|
// Important: We must destroy the game window to free resources and potentially stop its event loop contribution.
|
||||||
|
// But we must NOT close the application.
|
||||||
|
// If we only Hide, it stays in memory.
|
||||||
|
// If we Destroy, it's gone.
|
||||||
|
|
||||||
if (dlg)
|
if (dlg)
|
||||||
{
|
{
|
||||||
IupHide(dlg);
|
// IupHide(dlg); // Hiding is safer if Destroy causes loop exit
|
||||||
show_main_menu();
|
// printf("DEBUG: Hiding game window\n");
|
||||||
|
Ihandle *old_dlg = dlg;
|
||||||
|
dlg = NULL; // Clear global pointer first
|
||||||
|
IupDestroy(old_dlg);
|
||||||
|
printf("DEBUG: Destroyed game window\n");
|
||||||
}
|
}
|
||||||
return IUP_DEFAULT;
|
|
||||||
|
return IUP_IGNORE; // Return IUP_IGNORE to prevent default processing (like closing if CLOSE_CB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 鼠标点击回调
|
// 鼠标点击回调
|
||||||
@@ -421,14 +449,27 @@ static int k_any_cb(Ihandle *ih, int c)
|
|||||||
// 创建游戏窗口
|
// 创建游戏窗口
|
||||||
void create_game_window()
|
void create_game_window()
|
||||||
{
|
{
|
||||||
|
printf("DEBUG: create_game_window start\n");
|
||||||
|
// if (dlg)
|
||||||
|
// {
|
||||||
|
// IupDestroy(dlg); // 销毁旧窗口
|
||||||
|
// dlg = NULL;
|
||||||
|
// }
|
||||||
|
// Only destroy if it exists and is not the current dialog?
|
||||||
|
// Actually, creating a new dialog while old one is valid is fine, but we should destroy old one to save memory.
|
||||||
|
// However, if destroying dlg causes main loop to exit because it was the last visible window (if main menu was hidden)...
|
||||||
|
|
||||||
if (dlg)
|
if (dlg)
|
||||||
{
|
{
|
||||||
IupDestroy(dlg); // 销毁旧窗口
|
IupDestroy(dlg);
|
||||||
dlg = NULL;
|
dlg = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建Canvas (Board)
|
// 创建Canvas (Board)
|
||||||
board_canvas = IupCanvas(NULL);
|
board_canvas = IupCanvas(NULL);
|
||||||
|
if (!board_canvas)
|
||||||
|
printf("ERROR: Failed to create board_canvas\n");
|
||||||
|
|
||||||
IupSetAttribute(board_canvas, "ACTION", "action_cb");
|
IupSetAttribute(board_canvas, "ACTION", "action_cb");
|
||||||
IupSetCallback(board_canvas, "ACTION", (Icallback)action_cb);
|
IupSetCallback(board_canvas, "ACTION", (Icallback)action_cb);
|
||||||
IupSetCallback(board_canvas, "BUTTON_CB", (Icallback)button_cb);
|
IupSetCallback(board_canvas, "BUTTON_CB", (Icallback)button_cb);
|
||||||
@@ -443,10 +484,10 @@ void create_game_window()
|
|||||||
|
|
||||||
// 创建标签 (玩家信息和游戏状态)
|
// 创建标签 (玩家信息和游戏状态)
|
||||||
lbl_player = IupLabel("当前玩家: 黑子");
|
lbl_player = IupLabel("当前玩家: 黑子");
|
||||||
IupSetAttribute(lbl_player, "FONT", "SimHei, 14");
|
// IupSetAttribute(lbl_player, "FONT", "SimHei, 14"); // Comment out potentially problematic font setting
|
||||||
|
|
||||||
lbl_status = IupLabel("准备开始");
|
lbl_status = IupLabel("准备开始");
|
||||||
IupSetAttribute(lbl_status, "FONT", "SimHei, 12");
|
// IupSetAttribute(lbl_status, "FONT", "SimHei, 12");
|
||||||
|
|
||||||
Ihandle *vbox_controls;
|
Ihandle *vbox_controls;
|
||||||
|
|
||||||
@@ -509,10 +550,14 @@ void create_game_window()
|
|||||||
|
|
||||||
// 创建Dialog
|
// 创建Dialog
|
||||||
dlg = IupDialog(hbox_main);
|
dlg = IupDialog(hbox_main);
|
||||||
|
if (!dlg)
|
||||||
|
printf("ERROR: Failed to create dialog\n");
|
||||||
|
|
||||||
IupSetAttribute(dlg, "TITLE", "五子棋 - IUP版本");
|
IupSetAttribute(dlg, "TITLE", "五子棋 - IUP版本");
|
||||||
IupSetAttribute(dlg, "RESIZE", "NO");
|
IupSetAttribute(dlg, "RESIZE", "NO");
|
||||||
|
|
||||||
IupMap(dlg);
|
IupMap(dlg);
|
||||||
|
printf("DEBUG: create_game_window end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_pvp_game_gui()
|
void start_pvp_game_gui()
|
||||||
@@ -533,6 +578,7 @@ void start_pvp_game_gui()
|
|||||||
|
|
||||||
void start_pve_game_gui()
|
void start_pve_game_gui()
|
||||||
{
|
{
|
||||||
|
printf("DEBUG: start_pve_game_gui start\n");
|
||||||
gui_game_mode = 1;
|
gui_game_mode = 1;
|
||||||
// ai_difficulty is global
|
// ai_difficulty is global
|
||||||
empty_board();
|
empty_board();
|
||||||
@@ -540,67 +586,162 @@ void start_pve_game_gui()
|
|||||||
game_over = 0;
|
game_over = 0;
|
||||||
|
|
||||||
create_game_window();
|
create_game_window();
|
||||||
|
printf("DEBUG: create_game_window returned\n");
|
||||||
|
|
||||||
|
if (dlg)
|
||||||
|
{
|
||||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||||
sprintf(status_message, "人机对战模式 - 玩家执黑先行");
|
printf("DEBUG: IupShowXY called\n");
|
||||||
update_ui_labels();
|
}
|
||||||
if (board_canvas)
|
else
|
||||||
IupUpdate(board_canvas);
|
{
|
||||||
|
printf("ERROR: dlg is NULL in start_pve_game_gui\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_replay_gui()
|
sprintf(status_message, "人机对战模式 - 玩家执黑先行");
|
||||||
|
update_ui_labels();
|
||||||
|
printf("DEBUG: update_ui_labels returned\n");
|
||||||
|
|
||||||
|
// Force initial draw
|
||||||
|
if (board_canvas)
|
||||||
{
|
{
|
||||||
// Open file dialog
|
IupUpdate(board_canvas);
|
||||||
Ihandle *file_dlg = IupFileDlg();
|
// IupFlush(); // Comment out to check if this caused the crash
|
||||||
IupSetAttribute(file_dlg, "DIALOGTYPE", "OPEN");
|
}
|
||||||
IupSetAttribute(file_dlg, "TITLE", "选择复盘文件");
|
printf("DEBUG: start_pve_game_gui end\n");
|
||||||
IupSetAttribute(file_dlg, "FILTER", "*.csv");
|
}
|
||||||
IupSetAttribute(file_dlg, "FILTERINFO", "CSV Files");
|
|
||||||
|
|
||||||
IupPopup(file_dlg, IUP_CENTER, IUP_CENTER);
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
if (IupGetInt(file_dlg, "STATUS") != -1)
|
// 选择复盘文件
|
||||||
|
static void select_replay_file_gui()
|
||||||
{
|
{
|
||||||
char *filename = IupGetAttribute(file_dlg, "VALUE");
|
// List files in records/ directory
|
||||||
|
char record_files[100][100];
|
||||||
|
int file_count = 0;
|
||||||
|
|
||||||
char *base_name = strrchr(filename, '\\');
|
#ifdef _WIN32
|
||||||
if (!base_name)
|
WIN32_FIND_DATA ffd;
|
||||||
base_name = strrchr(filename, '/');
|
HANDLE hFind = FindFirstFile("records\\*.csv", &ffd);
|
||||||
if (base_name)
|
if (hFind != INVALID_HANDLE_VALUE)
|
||||||
base_name++;
|
{
|
||||||
else
|
do
|
||||||
base_name = filename;
|
{
|
||||||
|
if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
{
|
||||||
|
strcpy(record_files[file_count++], ffd.cFileName);
|
||||||
|
if (file_count >= 100)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (FindNextFile(hFind, &ffd) != 0);
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (load_game_from_file(base_name)) // returns game_mode (non-zero) on success
|
if (file_count == 0)
|
||||||
|
{
|
||||||
|
IupMessage("提示", "未找到复盘记录文件 (records/*.csv)");
|
||||||
|
show_main_menu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建列表框
|
||||||
|
Ihandle *list = IupList(NULL);
|
||||||
|
IupSetAttribute(list, "EXPAND", "YES");
|
||||||
|
IupSetAttribute(list, "VISIBLELINES", "10");
|
||||||
|
|
||||||
|
for (int i = 0; i < file_count; i++)
|
||||||
|
{
|
||||||
|
IupSetAttributeId(list, "", i + 1, record_files[i]);
|
||||||
|
}
|
||||||
|
IupSetAttribute(list, "VALUE", "1");
|
||||||
|
|
||||||
|
// 创建确定和取消按钮
|
||||||
|
Ihandle *btn_ok = IupButton("确定", NULL);
|
||||||
|
IupSetCallback(btn_ok, "ACTION", (Icallback)btn_replay_sel_ok_cb);
|
||||||
|
IupSetAttribute(btn_ok, "SIZE", "60x30");
|
||||||
|
|
||||||
|
Ihandle *btn_cancel = IupButton("取消", NULL);
|
||||||
|
IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_replay_sel_cancel_cb);
|
||||||
|
IupSetAttribute(btn_cancel, "SIZE", "60x30");
|
||||||
|
|
||||||
|
Ihandle *vbox = IupVbox(
|
||||||
|
IupLabel("请选择复盘文件:"),
|
||||||
|
list,
|
||||||
|
IupHbox(btn_ok, btn_cancel, NULL),
|
||||||
|
NULL);
|
||||||
|
IupSetAttribute(vbox, "MARGIN", "10x10");
|
||||||
|
IupSetAttribute(vbox, "GAP", "10");
|
||||||
|
IupSetAttribute(vbox, "ALIGNMENT", "ACENTER");
|
||||||
|
|
||||||
|
Ihandle *dlg_sel = IupDialog(vbox);
|
||||||
|
IupSetAttribute(dlg_sel, "TITLE", "选择复盘文件");
|
||||||
|
IupSetAttribute(dlg_sel, "MINBOX", "NO");
|
||||||
|
IupSetAttribute(dlg_sel, "MAXBOX", "NO");
|
||||||
|
IupSetAttribute(dlg_sel, "RESIZE", "NO");
|
||||||
|
|
||||||
|
// 存储列表框句柄到对话框属性
|
||||||
|
IupSetAttribute(dlg_sel, "MY_LIST", (char *)list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btn_replay_sel_ok_cb(Ihandle *ih)
|
||||||
|
{
|
||||||
|
Ihandle *dlg = IupGetDialog(ih);
|
||||||
|
Ihandle *list = (Ihandle *)IupGetAttribute(dlg, "MY_LIST");
|
||||||
|
char *filename = IupGetAttribute(list, "VALUE"); // Returns index
|
||||||
|
int index = atoi(filename);
|
||||||
|
char *selected_file = IupGetAttributeId(list, "", index);
|
||||||
|
|
||||||
|
if (selected_file)
|
||||||
|
{
|
||||||
|
if (load_game_from_file(selected_file))
|
||||||
{
|
{
|
||||||
replay_total_steps = step_count;
|
replay_total_steps = step_count;
|
||||||
step_count = 0;
|
step_count = 0;
|
||||||
// load_game_from_file already cleared board when reading steps, but steps were read into array
|
|
||||||
// wait, load_game_from_file calls empty_board() then reads steps.
|
|
||||||
// But it doesn't set board array.
|
|
||||||
// So board is empty.
|
|
||||||
|
|
||||||
gui_game_mode = 2; // Replay
|
gui_game_mode = 2; // Replay
|
||||||
create_game_window();
|
create_game_window();
|
||||||
|
IupShowXY(dlg, IUP_CENTER, IUP_CENTER); // 展示
|
||||||
|
|
||||||
|
Ihandle *dlg_sel = dlg;
|
||||||
|
IupHide(dlg_sel);
|
||||||
|
IupDestroy(dlg_sel);
|
||||||
|
|
||||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||||
sprintf(status_message, "复盘模式 - %s", base_name);
|
|
||||||
|
sprintf(status_message, "复盘模式 - %s", selected_file);
|
||||||
update_ui_labels();
|
update_ui_labels();
|
||||||
if (board_canvas)
|
if (board_canvas)
|
||||||
IupUpdate(board_canvas);
|
IupUpdate(board_canvas);
|
||||||
|
|
||||||
|
return IUP_DEFAULT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IupMessage("错误", "无法加载复盘文件");
|
IupMessage("错误", "无法加载复盘文件");
|
||||||
show_main_menu();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
show_main_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
IupDestroy(file_dlg);
|
IupHide(dlg);
|
||||||
|
IupDestroy(dlg);
|
||||||
|
show_main_menu();
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btn_replay_sel_cancel_cb(Ihandle *ih)
|
||||||
|
{
|
||||||
|
Ihandle *dlg = IupGetDialog(ih);
|
||||||
|
IupHide(dlg);
|
||||||
|
IupDestroy(dlg);
|
||||||
|
show_main_menu();
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_replay_gui()
|
||||||
|
{
|
||||||
|
select_replay_file_gui();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -691,12 +832,14 @@ void draw_ui_elements()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 显示消息
|
* @brief 运行图形化界面模式
|
||||||
|
* @note 包含初始化、主循环和清理
|
||||||
*/
|
*/
|
||||||
void show_message(const char *message)
|
void run_gui_mode()
|
||||||
{
|
{
|
||||||
strncpy(status_message, message, sizeof(status_message) - 1);
|
if (init_gui() == 0)
|
||||||
status_message[sizeof(status_message) - 1] = '\0';
|
{
|
||||||
update_ui_labels();
|
IupMainLoop(); // 使用IUP的主循环
|
||||||
printf("%s\n", message);
|
cleanup_gui();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-9
@@ -11,16 +11,20 @@ static Ihandle *menu_dlg = NULL;
|
|||||||
static int btn_pvp_cb(Ihandle *ih)
|
static int btn_pvp_cb(Ihandle *ih)
|
||||||
{
|
{
|
||||||
(void)ih;
|
(void)ih;
|
||||||
hide_main_menu();
|
printf("DEBUG: Starting PvP Game\n");
|
||||||
|
// hide_main_menu(); // DO NOT HIDE MAIN MENU YET
|
||||||
start_pvp_game_gui();
|
start_pvp_game_gui();
|
||||||
|
IupHide(menu_dlg); // Hide main menu manually AFTER game window created
|
||||||
return IUP_DEFAULT;
|
return IUP_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btn_pve_cb(Ihandle *ih)
|
static int btn_pve_cb(Ihandle *ih)
|
||||||
{
|
{
|
||||||
(void)ih;
|
(void)ih;
|
||||||
hide_main_menu();
|
printf("DEBUG: Starting PvE Game\n");
|
||||||
|
// hide_main_menu(); // DO NOT HIDE MAIN MENU YET
|
||||||
start_pve_game_gui();
|
start_pve_game_gui();
|
||||||
|
IupHide(menu_dlg); // Hide main menu manually AFTER game window created
|
||||||
return IUP_DEFAULT;
|
return IUP_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,22 +49,28 @@ static int btn_save_settings_cb(Ihandle *ih)
|
|||||||
|
|
||||||
// Update globals
|
// Update globals
|
||||||
int new_size = IupGetInt(txt_board_size, "VALUE");
|
int new_size = IupGetInt(txt_board_size, "VALUE");
|
||||||
if (new_size < MIN_BOARD_SIZE) new_size = MIN_BOARD_SIZE;
|
if (new_size < MIN_BOARD_SIZE)
|
||||||
if (new_size > MAX_BOARD_SIZE) new_size = MAX_BOARD_SIZE;
|
new_size = MIN_BOARD_SIZE;
|
||||||
|
if (new_size > MAX_BOARD_SIZE)
|
||||||
|
new_size = MAX_BOARD_SIZE;
|
||||||
BOARD_SIZE = new_size;
|
BOARD_SIZE = new_size;
|
||||||
|
|
||||||
use_forbidden_moves = IupGetInt(tgl_forbidden, "VALUE");
|
use_forbidden_moves = IupGetInt(tgl_forbidden, "VALUE");
|
||||||
|
|
||||||
use_timer = IupGetInt(tgl_timer, "VALUE");
|
use_timer = IupGetInt(tgl_timer, "VALUE");
|
||||||
if (use_timer) {
|
if (use_timer)
|
||||||
|
{
|
||||||
int minutes = IupGetInt(txt_time_limit, "VALUE");
|
int minutes = IupGetInt(txt_time_limit, "VALUE");
|
||||||
if (minutes < 1) minutes = 1;
|
if (minutes < 1)
|
||||||
|
minutes = 1;
|
||||||
time_limit = minutes * 60;
|
time_limit = minutes * 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ai_level = IupGetInt(lst_ai, "VALUE");
|
int ai_level = IupGetInt(lst_ai, "VALUE");
|
||||||
if (ai_level < 1) ai_level = 1;
|
if (ai_level < 1)
|
||||||
if (ai_level > 5) ai_level = 5;
|
ai_level = 1;
|
||||||
|
if (ai_level > 5)
|
||||||
|
ai_level = 5;
|
||||||
ai_difficulty = ai_level;
|
ai_difficulty = ai_level;
|
||||||
defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT + (ai_difficulty - 1) * 0.1;
|
defense_coefficient = DEFAULT_DEFENSE_COEFFICIENT + (ai_difficulty - 1) * 0.1;
|
||||||
|
|
||||||
@@ -197,7 +207,9 @@ static int btn_exit_cb(Ihandle *ih)
|
|||||||
|
|
||||||
void create_main_menu()
|
void create_main_menu()
|
||||||
{
|
{
|
||||||
if (menu_dlg) return;
|
if (menu_dlg)
|
||||||
|
return;
|
||||||
|
printf("DEBUG: create_main_menu\n");
|
||||||
|
|
||||||
Ihandle *lbl_title = IupLabel("五子棋 (Gobang)");
|
Ihandle *lbl_title = IupLabel("五子棋 (Gobang)");
|
||||||
IupSetAttribute(lbl_title, "FONT", "SimHei, 24");
|
IupSetAttribute(lbl_title, "FONT", "SimHei, 24");
|
||||||
@@ -248,13 +260,19 @@ void create_main_menu()
|
|||||||
|
|
||||||
// 设置对话框关闭回调 (点X关闭程序)
|
// 设置对话框关闭回调 (点X关闭程序)
|
||||||
IupSetCallback(menu_dlg, "CLOSE_CB", (Icallback)btn_exit_cb);
|
IupSetCallback(menu_dlg, "CLOSE_CB", (Icallback)btn_exit_cb);
|
||||||
|
IupMap(menu_dlg); // Map immediately
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_main_menu()
|
void show_main_menu()
|
||||||
{
|
{
|
||||||
|
printf("DEBUG: show_main_menu start\n");
|
||||||
if (!menu_dlg)
|
if (!menu_dlg)
|
||||||
|
{
|
||||||
|
printf("DEBUG: Creating main menu\n");
|
||||||
create_main_menu();
|
create_main_menu();
|
||||||
|
}
|
||||||
IupShowXY(menu_dlg, IUP_CENTER, IUP_CENTER);
|
IupShowXY(menu_dlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
printf("DEBUG: show_main_menu end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void hide_main_menu()
|
void hide_main_menu()
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file init_board.c
|
|
||||||
* @brief 初始化游戏棋盘源文件
|
|
||||||
* @note 本文件定义了初始化游戏棋盘的相关函数。
|
|
||||||
* 它负责设置游戏的初始状态,包括棋盘大小、玩家标识、游戏规则等。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "init_board.h"
|
|
||||||
#include "gobang.h"
|
|
||||||
#include "game_mode.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化棋盘为全空状态并重置步数计数器
|
|
||||||
* 清空棋盘数组并将所有位置设为EMPTY,同时将step_count重置为0
|
|
||||||
*/
|
|
||||||
void empty_board()
|
|
||||||
{
|
|
||||||
// 初始化棋盘状态为全空
|
|
||||||
for (int i = 0; i < BOARD_SIZE; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < BOARD_SIZE; j++)
|
|
||||||
{
|
|
||||||
board[i][j] = EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
step_count = 0; // 重置步数计数器
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 打印当前棋盘状态
|
|
||||||
* 以可读格式输出棋盘,包括行列号和棋子状态
|
|
||||||
* 玩家棋子显示为'x',AI棋子显示为'○',空位显示为'·'
|
|
||||||
*/
|
|
||||||
void print_board()
|
|
||||||
{
|
|
||||||
// 打印列号(1-BOARD_SIZE显示)
|
|
||||||
printf("\n ");
|
|
||||||
for (int i = 0; i < BOARD_SIZE; i++)
|
|
||||||
{
|
|
||||||
printf("%2d", i + 1);
|
|
||||||
if (i + 1 == 9) // 处理列号9和10+的对齐
|
|
||||||
{
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// 逐行打印棋盘内容
|
|
||||||
for (int i = 0; i < BOARD_SIZE; i++)
|
|
||||||
{
|
|
||||||
printf("%2d ", i + 1); // 打印行号(1-BOARD_SIZE)
|
|
||||||
for (int j = 0; j < BOARD_SIZE; j++)
|
|
||||||
{
|
|
||||||
if (board[i][j] == PLAYER)
|
|
||||||
{
|
|
||||||
printf("x "); // 玩家棋子
|
|
||||||
}
|
|
||||||
else if (board[i][j] == AI)
|
|
||||||
{
|
|
||||||
printf("○ "); // AI棋子(使用○显示)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("· "); // 空位
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n"); // 每行结束换行
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 配置棋盘大小
|
|
||||||
*
|
|
||||||
* @param player1 玩家1
|
|
||||||
* @param player2 玩家2
|
|
||||||
*/
|
|
||||||
void setup_board_size()
|
|
||||||
{
|
|
||||||
printf("通常棋盘大小分为休闲棋盘(13X13)、标准棋盘(15X15)和特殊棋盘(19X19)\n");
|
|
||||||
char prompt[100];
|
|
||||||
sprintf(prompt, "请输入棋盘大小(5~%d)(默认为标准棋盘):\n", MAX_BOARD_SIZE);
|
|
||||||
BOARD_SIZE = get_integer_input(prompt, 5, MAX_BOARD_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the up game options object
|
|
||||||
* 配置游戏选项,包括禁手规则、计时器和时间限制
|
|
||||||
*/
|
|
||||||
void setup_game_options()
|
|
||||||
{
|
|
||||||
use_forbidden_moves = get_integer_input("是否启用禁手规则 (1-是, 0-否): ", 0, 1);
|
|
||||||
|
|
||||||
use_timer = get_integer_input("是否启用计时器 (1-是, 0-否): ", 0, 1);
|
|
||||||
if (use_timer)
|
|
||||||
{
|
|
||||||
time_limit = get_integer_input("请输入每回合的时间限制 (1~60分钟): ", 1, 60) * 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 确定先手玩家
|
|
||||||
*
|
|
||||||
* @param player1
|
|
||||||
* @param player2
|
|
||||||
* @return int player1 or player2
|
|
||||||
*/
|
|
||||||
int determine_first_player(int player1, int player2)
|
|
||||||
{
|
|
||||||
char prompt[100];
|
|
||||||
sprintf(prompt, "请选择先手方 (1 for Player %d, 2 for Player %d): ", player1, player2);
|
|
||||||
int first_player_choice = get_integer_input(prompt, 1, 2);
|
|
||||||
if (first_player_choice == 1)
|
|
||||||
{
|
|
||||||
return player1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return player2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-59
@@ -4,22 +4,16 @@
|
|||||||
* @note 本文件包含了游戏的主循环、模式选择和游戏初始化等功能
|
* @note 本文件包含了游戏的主循环、模式选择和游戏初始化等功能
|
||||||
* @brief 将以下指令复制到powershell
|
* @brief 将以下指令复制到powershell
|
||||||
*
|
*
|
||||||
* !控制台版本编译:
|
|
||||||
* mingw32-make console
|
|
||||||
.\bin\gobang_console.exe
|
|
||||||
*
|
|
||||||
* !图形化版本编译(需要IUP库):
|
* !图形化版本编译(需要IUP库):
|
||||||
* mingw32-make gui
|
* mingw32-make gui
|
||||||
.\bin\gobang_gui.exe
|
.\bin\gobang_gui.exe
|
||||||
*
|
*
|
||||||
* @note gcc 为编译器,添加了-lws2_32链接Windows网络库
|
* @note gcc 为编译器,添加了-lws2_32链接Windows网络库
|
||||||
* @note IUP 的路径:libs\iup-3.31_Win64_dllw6_lib
|
* @note IUP 的路径:libs\iup-3.31_Win64_dllw6_lib
|
||||||
* @brief & "D:\Program Files (x86)\NSIS\makensis.exe" "installer\\installer.nsi"
|
|
||||||
* @brief & "D:\Program Files (x86)\Inno Setup 6\iscc.exe" installer\\installer.iss
|
* @brief & "D:\Program Files (x86)\Inno Setup 6\iscc.exe" installer\\installer.iss
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "game_mode.h"
|
#include "gui.h"
|
||||||
#include "ui.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -40,58 +34,7 @@ int main(int argc, char *argv[])
|
|||||||
// 加载游戏配置
|
// 加载游戏配置
|
||||||
load_game_config();
|
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();
|
run_gui_mode();
|
||||||
break;
|
|
||||||
// 0. 退出游戏
|
|
||||||
case 0:
|
|
||||||
save_game_config();
|
|
||||||
printf("感谢使用五子棋游戏!\n");
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
printf("无效的选择!\n");
|
|
||||||
pause_for_input("按任意键继续...");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
+1
-185
@@ -6,10 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "record.h"
|
#include "record.h"
|
||||||
#include "game_mode.h"
|
|
||||||
#include "gobang.h"
|
#include "gobang.h"
|
||||||
#include "init_board.h"
|
|
||||||
#include "ui.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -47,139 +44,7 @@
|
|||||||
* - 包含输入缓冲区清理防止意外输入
|
* - 包含输入缓冲区清理防止意外输入
|
||||||
* - 评分环节调用calculate_final_score()函数
|
* - 评分环节调用calculate_final_score()函数
|
||||||
*/
|
*/
|
||||||
void review_process(int game_mode)
|
void calculate_game_scores();
|
||||||
{
|
|
||||||
int review_choice = get_integer_input("是否要复盘本局比赛? (1-是, 0-否): ", 0, 1);
|
|
||||||
|
|
||||||
// 如果评分尚未计算,则计算评分
|
|
||||||
if (!scores_calculated)
|
|
||||||
{
|
|
||||||
calculate_game_scores();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 评分已从文件中加载,直接使用
|
|
||||||
printf("从记录文件中加载评分数据\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (review_choice == 1)
|
|
||||||
{
|
|
||||||
printf("\n===== 复盘记录(总步数:%d) =====\n", step_count);
|
|
||||||
// 清空输入缓冲区
|
|
||||||
int c;
|
|
||||||
while ((c = getchar()) != '\n' && c != EOF)
|
|
||||||
;
|
|
||||||
|
|
||||||
// 创建临时复盘棋盘
|
|
||||||
int temp_board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
|
|
||||||
memset(temp_board, EMPTY, sizeof(temp_board)); // 初始化为空棋盘
|
|
||||||
|
|
||||||
// 逐步重现游戏过程
|
|
||||||
for (int i = 0; i < step_count; i++)
|
|
||||||
{
|
|
||||||
Step s = steps[i]; // 获取当前步骤
|
|
||||||
temp_board[s.x][s.y] = s.player; // 在临时棋盘上落子
|
|
||||||
|
|
||||||
// 打印当前步骤信息
|
|
||||||
// 根据游戏模式显示不同的标题和玩家信息
|
|
||||||
if (game_mode == GAME_MODE_AI)
|
|
||||||
{
|
|
||||||
// 人机对战
|
|
||||||
printf("\n===== 五子棋人机对战(%dX%d棋盘) =====", BOARD_SIZE, BOARD_SIZE);
|
|
||||||
printf("\n 第%d步/%d步: %s 落子于(%d, %d)\n",
|
|
||||||
i + 1, step_count,
|
|
||||||
(s.player == PLAYER) ? "玩家" : "AI",
|
|
||||||
s.x + 1, s.y + 1);
|
|
||||||
}
|
|
||||||
else if (game_mode == GAME_MODE_PVP)
|
|
||||||
{
|
|
||||||
// 双人对战
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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(" ");
|
|
||||||
for (int col = 0; col < BOARD_SIZE; col++)
|
|
||||||
{
|
|
||||||
printf("%2d", col + 1); // 列号
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
for (int row = 0; row < BOARD_SIZE; row++)
|
|
||||||
{
|
|
||||||
printf("%2d ", row + 1); // 行号
|
|
||||||
for (int col = 0; col < BOARD_SIZE; col++)
|
|
||||||
{
|
|
||||||
if (temp_board[row][col] == PLAYER || temp_board[row][col] == PLAYER1)
|
|
||||||
{
|
|
||||||
printf("x ");
|
|
||||||
}
|
|
||||||
else if (temp_board[row][col] == AI || temp_board[row][col] == PLAYER2)
|
|
||||||
{
|
|
||||||
printf("○ ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("· ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n"); // 行结束换行
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果不是最后一步,等待用户按键继续
|
|
||||||
if (i < step_count - 1)
|
|
||||||
{
|
|
||||||
printf("\n按Enter继续下一步...");
|
|
||||||
while (getchar() != '\n')
|
|
||||||
; // 等待回车
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示胜负结果(直接使用文件中的信息)
|
|
||||||
printf("\n===== 对局结果 =====");
|
|
||||||
if (strcmp(winner_info, "玩家获胜") == 0)
|
|
||||||
{
|
|
||||||
printf("\n? 恭喜!玩家获胜!\n");
|
|
||||||
}
|
|
||||||
else if (strcmp(winner_info, "AI获胜") == 0)
|
|
||||||
{
|
|
||||||
printf("\n? AI获胜!\n");
|
|
||||||
}
|
|
||||||
else if (strcmp(winner_info, "玩家1获胜") == 0)
|
|
||||||
{
|
|
||||||
printf("\n? 恭喜!玩家1(黑棋)获胜!\n");
|
|
||||||
}
|
|
||||||
else if (strcmp(winner_info, "玩家2获胜") == 0)
|
|
||||||
{
|
|
||||||
printf("\n? 恭喜!玩家2(白棋)获胜!\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("\n?? 对局平局或未完成\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n复盘结束!按Enter查看评分...");
|
|
||||||
getchar(); // 等待用户按键
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示评分结果
|
|
||||||
display_game_scores(game_mode);
|
|
||||||
|
|
||||||
getchar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算游戏评分
|
* @brief 计算游戏评分
|
||||||
@@ -290,57 +155,8 @@ void display_game_scores(int game_mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理游戏结束后的记录保存
|
|
||||||
* @return int 保存状态码(0-成功, 1-目录创建失败, 2-文件打开失败, 3-文件写入失败)
|
|
||||||
*/
|
|
||||||
void handle_save_record(int game_mode)
|
|
||||||
{
|
|
||||||
printf("===== 游戏结束 =====\n");
|
|
||||||
int save_choice = get_integer_input("是否保存游戏记录? (1-是, 0-否): ", 0, 1);
|
|
||||||
|
|
||||||
if (save_choice == 1)
|
|
||||||
{
|
|
||||||
time_t now = time(NULL);
|
|
||||||
struct tm *t = localtime(&now);
|
|
||||||
char filename[256];
|
|
||||||
strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.csv", t);
|
|
||||||
|
|
||||||
int save_status = save_game_to_file(filename, game_mode);
|
|
||||||
switch (save_status)
|
|
||||||
{
|
|
||||||
case 0: // 成功
|
|
||||||
printf("\n游戏记录已成功保存至: %s (CSV格式)\n", filename);
|
|
||||||
printf("您可以使用以下命令进行复盘: .\\gobang.exe -l %s\n", filename);
|
|
||||||
printf("CSV格式文件可以直接用Excel打开查看和分析\n");
|
|
||||||
break;
|
|
||||||
case 1: // 目录创建失败
|
|
||||||
printf("\n游戏记录保存失败: 无法创建 'records' 目录。\n");
|
|
||||||
printf("请检查程序是否具有足够的写入权限或磁盘空间是否充足。\n");
|
|
||||||
break;
|
|
||||||
case 2: // 文件打开失败
|
|
||||||
printf("\n游戏记录保存失败: 无法在路径 '%s' 创建文件。\n", filename);
|
|
||||||
printf("请检查路径是否有效以及程序是否具有写入权限。\n");
|
|
||||||
break;
|
|
||||||
case 3: // 文件写入失败
|
|
||||||
printf("\n游戏记录保存失败: 写入文件时发生错误。\n");
|
|
||||||
printf("请检查磁盘空间是否已满。\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("\n游戏记录保存失败: 发生未知错误。\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将当前游戏记录保存到文件
|
* @brief 将当前游戏记录保存到文件
|
||||||
* @param filename 要保存的文件名
|
|
||||||
* @return int 错误码:
|
|
||||||
* 0: 成功
|
|
||||||
* 1: 目录创建失败
|
|
||||||
* 2: 文件打开失败
|
|
||||||
* 3: 文件写入失败
|
|
||||||
*/
|
*/
|
||||||
int save_game_to_file(const char *filename, int game_mode)
|
int save_game_to_file(const char *filename, int game_mode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file ui.c
|
|
||||||
* @brief
|
|
||||||
* @note 本文件定义了用户界面相关的函数和数据结构。
|
|
||||||
* 它负责处理用户输入、显示游戏界面、提示信息等与用户交互的功能。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ui.h"
|
|
||||||
#include "gobang.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏主菜单
|
|
||||||
*/
|
|
||||||
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("8. 图形化界面\n");
|
|
||||||
printf("0. 退出游戏\n");
|
|
||||||
printf("=====================\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示棋盘
|
|
||||||
*/
|
|
||||||
void display_board()
|
|
||||||
{
|
|
||||||
printf("\n ");
|
|
||||||
// 打印列号
|
|
||||||
for (int j = 0; j < BOARD_SIZE; j++)
|
|
||||||
{
|
|
||||||
printf("%2d", j);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// 打印棋盘内容
|
|
||||||
for (int i = 0; i < BOARD_SIZE; i++)
|
|
||||||
{
|
|
||||||
printf("%2d", i); // 打印行号
|
|
||||||
for (int j = 0; j < BOARD_SIZE; j++)
|
|
||||||
{
|
|
||||||
if (board[i][j] == EMPTY)
|
|
||||||
{
|
|
||||||
printf(" ·"); // 空位用点表示
|
|
||||||
}
|
|
||||||
else if (board[i][j] == PLAYER || board[i][j] == PLAYER1)
|
|
||||||
{
|
|
||||||
printf(" ●"); // 玩家1用实心圆表示
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf(" ○"); // 玩家2/AI用空心圆表示
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏状态信息
|
|
||||||
* @param current_player 当前玩家
|
|
||||||
* @param step_count 当前步数
|
|
||||||
*/
|
|
||||||
void display_game_status(int current_player, int step_count)
|
|
||||||
{
|
|
||||||
printf("当前步数: %d\n", step_count);
|
|
||||||
if (current_player == PLAYER || current_player == PLAYER1)
|
|
||||||
{
|
|
||||||
printf("当前玩家: ●\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("当前玩家: ○\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示获胜信息
|
|
||||||
* @param winner 获胜者
|
|
||||||
*/
|
|
||||||
void display_winner(int winner)
|
|
||||||
{
|
|
||||||
printf("\n游戏结束!\n");
|
|
||||||
if (winner == PLAYER)
|
|
||||||
{
|
|
||||||
printf("玩家获胜!\n");
|
|
||||||
}
|
|
||||||
else if (winner == AI)
|
|
||||||
{
|
|
||||||
printf("AI获胜!\n");
|
|
||||||
}
|
|
||||||
else if (winner == PLAYER1)
|
|
||||||
{
|
|
||||||
printf("玩家1获胜!\n");
|
|
||||||
}
|
|
||||||
else if (winner == PLAYER2)
|
|
||||||
{
|
|
||||||
printf("玩家2获胜!\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("平局!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏设置菜单
|
|
||||||
*/
|
|
||||||
void display_settings_menu()
|
|
||||||
{
|
|
||||||
printf("\n===== 游戏设置 =====\n");
|
|
||||||
printf("1. 棋盘大小设置\n");
|
|
||||||
printf("2. 禁手规则设置\n");
|
|
||||||
printf("3. 计时器设置\n");
|
|
||||||
printf("4. 网络配置设置\n");
|
|
||||||
printf("5. AI难度设置\n");
|
|
||||||
printf("0. 返回主菜单\n");
|
|
||||||
printf("==================\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清屏函数
|
|
||||||
*/
|
|
||||||
void clear_screen()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
system("cls");
|
|
||||||
#else
|
|
||||||
system("clear");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 暂停等待用户输入
|
|
||||||
* @param prompt 提示信息
|
|
||||||
*/
|
|
||||||
void pause_for_input(const char *prompt)
|
|
||||||
{
|
|
||||||
printf("%s", prompt);
|
|
||||||
#ifdef _WIN32
|
|
||||||
_getch();
|
|
||||||
#else
|
|
||||||
getchar();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示游戏规则
|
|
||||||
*/
|
|
||||||
void display_game_rules()
|
|
||||||
{
|
|
||||||
printf("\n===== 五子棋游戏规则 =====\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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 显示关于信息
|
|
||||||
*/
|
|
||||||
void display_about()
|
|
||||||
{
|
|
||||||
printf("\n===== 关于五子棋游戏 =====\n");
|
|
||||||
printf("🎮 游戏名称:五子棋人机对战\n");
|
|
||||||
printf("📦 版本:7.0\n");
|
|
||||||
printf("👨💻 开发者:刘航宇\n");
|
|
||||||
printf("📧 联系邮箱:3364451258@qq.com\n");
|
|
||||||
printf("🌐 项目主页:https://github.com/LHY0125/Gobang-Game\n\n");
|
|
||||||
printf("✨ 主要特性:\n");
|
|
||||||
printf(" 🤖 智能AI对战(支持多种难度)\n");
|
|
||||||
printf(" 👥 双人对战模式\n");
|
|
||||||
printf(" 🌐 网络对战(局域网/互联网)\n");
|
|
||||||
printf(" 📝 对局记录与复盘\n");
|
|
||||||
printf(" 🚫 禁手规则支持\n");
|
|
||||||
printf(" ⏱️ 计时器功能\n");
|
|
||||||
printf(" 📏 自定义棋盘大小\n");
|
|
||||||
printf(" 📊 评分系统\n\n");
|
|
||||||
printf("🙏 感谢使用!\n");
|
|
||||||
printf("========================\n");
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user