Initial commit: C language learning code

This commit is contained in:
2025-07-20 16:30:56 +08:00
commit 06e24173a6
139 changed files with 9303 additions and 0 deletions
@@ -0,0 +1,473 @@
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
// !宏定义
#define MAX_BOARD_SIZE 25 //* 最大支持棋盘尺寸
int BOARD_SIZE = 15; //* 实际使用的棋盘尺寸(默认15)
#define PLAYER 1 //* 玩家棋子标识符
#define AI 2 //* AI棋子标识符
#define EMPTY 0 //* 空位置标识符
#define MAX_STEPS (MAX_BOARD_SIZE * MAX_BOARD_SIZE) //* 最大步数(棋盘总格数)
// !全局变量
int board[MAX_BOARD_SIZE][MAX_BOARD_SIZE] = {0}; //* 棋盘状态存储数组(默认棋盘全空为0)
const int direction[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; //* 四个方向:向下、向右、右下、左下
// !落子步骤结构体
typedef struct // 落子结构体
{
int player; // 落子方标识
int x, y; // 坐标位置
} Step;
// !方向信息结构体
typedef struct
{
int continuous_chess; // 连续棋子数量
bool check_start; // 序列起点方向是否开放(空位)
bool check_end; // 序列终点方向是否开放(空位)
} DirInfo;
Step steps[MAX_STEPS]; // 存储所有落子步骤
int step_count = 0; // 当前步数计数器
// !函数声明
void empty_board();
void print_board();
bool have_space(int x, int y);
bool player_move(int x, int y);
DirInfo count_specific_direction(int x, int y, int dx, int dy, int player);
bool check_win(int x, int y, int player);
int evaluate_pos(int x, int y, int player);
void ai_move();
void review_process();
// !主函数:游戏流程控制
int main()
{
// 初始化阶段:获取棋盘尺寸
printf("===== 五子棋人机对战 =====\n");
printf("通常棋盘大小分为休闲棋盘(13X13)、标准棋盘(15X15)和特殊棋盘(19X19)\n");
printf("请输入棋盘大小(5~%d)(默认为标准棋盘):", MAX_BOARD_SIZE);
scanf("%d", &BOARD_SIZE);
if (BOARD_SIZE < 5 || BOARD_SIZE > MAX_BOARD_SIZE)
{
BOARD_SIZE = 15;
printf("输入无效,使用默认标准棋盘15X15\n");
}
empty_board();
printf("===== 五子棋人机对战(%dX%d棋盘) =====", BOARD_SIZE, BOARD_SIZE);
print_board();
// 游戏主循环
while (1)
{
// 玩家回合
int x, y;
printf("\n请输入落子坐标(行 列,1~%d):", BOARD_SIZE);
scanf("%d %d", &x, &y);
// 转换为0-BOARD_SIZE索引(转为棋盘的坐标)
x--;
y--;
// 验证并执行玩家移动
if (!player_move(x, y))
{
printf("坐标无效!请重新输入。\n");
continue;
}
print_board();
// 检查玩家是否获胜
if (check_win(x, y, PLAYER))
{
printf("\n玩家获胜!\n");
review_process();
break;
}
// AI回合
printf("\nAI思考中...\n");
ai_move();
print_board();
// 检查AI是否获胜
Step last_step = steps[step_count - 1];
if (check_win(last_step.x, last_step.y, AI))
{
printf("\nAI获胜!\n");
review_process();
break;
}
// 检查平局(棋盘已满)
if (step_count == BOARD_SIZE * BOARD_SIZE)
{
printf("\n平局!\n");
review_process();
break;
}
}
return 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;
}
// !打印当前棋盘状态
void print_board()
{
// 打印列号(1-BOARD_SIZE显示)
printf("\n ");
for (int i = 0; i < BOARD_SIZE; i++)
{
printf("%2d", i + 1);
if (i + 1 == 9) // 处理两位数列号的对齐
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");
}
}
// !检查位置是否有效(在棋盘范围内且为空位)
bool have_space(int x, int y)
{
return (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE && board[x][y] == EMPTY);
}
// !玩家落子操作
bool player_move(int x, int y)
{
if (!have_space(x, y))
{
return false; // 位置无效返回失败
}
board[x][y] = PLAYER; // 更新棋盘状态
steps[step_count++] = (Step){PLAYER, x, y}; // 记录步骤
return true;
}
// !计算特定方向上连续同色棋子数量
DirInfo count_specific_direction(int x, int y, int dx, int dy, int player)
{
DirInfo info;
info.continuous_chess = 1; // 包含当前位置
info.check_start = false;
info.check_end = false;
// 检查正方向(dx, dy
int nx = x + dx, ny = y + dy;
while (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player)
{
info.continuous_chess++;
nx += dx;
ny += dy;
}
// 判断正方向端点是否开放
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE)
{
if (board[nx][ny] == EMPTY)
{
info.check_end = true;
}
}
// 检查反方向(-dx, -dy
nx = x - dx, ny = y - dy;
while (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player)
{
info.continuous_chess++;
nx -= dx;
ny -= dy;
}
// 判断反方向端点是否开放
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE)
{
if (board[nx][ny] == EMPTY)
{
info.check_start = true;
}
}
return info;
}
// !检查特定位置落子后是否获胜
bool check_win(int x, int y, int player)
{
for (int i = 0; i < 4; i++)
{
DirInfo info = count_specific_direction(x, y, direction[i][0], direction[i][1], player);
if (info.continuous_chess >= 5)
return true;
}
return false;
}
// !评估特定位置对当前玩家的价值(AI评估下一次落子位置的价值)
int evaluate_pos(int x, int y, int player)
{
// 临时模拟落子(确保评估准确)
int original = board[x][y];
board[x][y] = player;
int total_score = 0; // 总分
int line_scores[4] = {0}; // 记录四个方向的分数
// 遍历四个方向
for (int i = 0; i < 4; i++)
{
int dx = direction[i][0], dy = direction[i][1];
// 获取当前玩家的棋型信息
DirInfo info = count_specific_direction(x, y, dx, dy, player);
// 检查是否直接形成五连珠
if (info.continuous_chess >= 5)
{
board[x][y] = original; // 恢复棋盘
return 1000000; // 五连珠直接返回最高分
}
// 评估不同棋型的价值
switch (info.continuous_chess)
{
// 四连珠
case 4:
// 活四(两端开放)
if (info.check_start && info.check_end)
{
line_scores[i] = 100000; // 必胜局面
}
// 冲四(一端开放)
else if (info.check_start || info.check_end)
{
line_scores[i] = 10000; // 威
}
// 死四(两端封闭)
else
{
line_scores[i] = 500; // 低价值
}
break;
// 三连珠
case 3:
// 活三(两端开放)
if (info.check_start && info.check_end)
{
line_scores[i] = 5000; // 高价值
}
// 眠三(一端开放)
else if (info.check_start || info.check_end)
{
line_scores[i] = 1000; // 中等价值
}
// 死三(两端封闭)
else
{
line_scores[i] = 50; // 低价值
}
break;
// 二连珠
case 2:
// 活二(两端开放)
if (info.check_start && info.check_end)
{
line_scores[i] = 500; // 发展潜力大
}
// 眠二(一端开放)
else if (info.check_start || info.check_end)
{
line_scores[i] = 100; // 有限潜力
}
// 死二(两端封闭)
else
{
line_scores[i] = 10; // 几乎无价值
}
break;
// 单子
case 1:
// 单子但两端开放
if (info.check_start && info.check_end)
{
line_scores[i] = 50; // 未来潜力
}
// 单子但一端开放
else if (info.check_start || info.check_end)
{
line_scores[i] = 10; // 有限潜力
}
// 死单子
else
{
line_scores[i] = 1; // 几乎无价值
}
break;
// 五子以上(已处理)
default:
break;
}
}
// 计算总分:取最高方向分+其他方向分加权
int max_score = 0;
int sum_score = 0;
for (int i = 0; i < 4; i++)
{
if (line_scores[i] > max_score)
{
max_score = line_scores[i];
}
sum_score += line_scores[i];
}
// 主方向权重高,辅助方向权重低
total_score = max_score * 10 + sum_score;
board[x][y] = original; // 恢复棋盘
// 添加位置权重:中央位置>边缘位置
int center_x = BOARD_SIZE / 2;
int center_y = BOARD_SIZE / 2;
int distance = abs(x - center_x) + abs(y - center_y);
int position_bonus = 50 * (BOARD_SIZE - distance);
return total_score + position_bonus;
}
// !AI落子算法
void ai_move()
{
int best_score = -1, best_x = -1, best_y = -1;
// 遍历整个棋盘
for (int i = 0; i < BOARD_SIZE; i++)
{
for (int j = 0; j < BOARD_SIZE; j++)
{
// 跳过已落子位置
if (board[i][j] != EMPTY)
{
continue;
}
// 综合评估位置对AI的进攻价值和防御价值
int score = evaluate_pos(i, j, AI) + evaluate_pos(i, j, PLAYER) * 0.5;
// 更新最佳位置
if (score > best_score)
{
best_score = score;
best_x = i;
best_y = j;
}
}
}
// 在最佳位置落子
if (best_x != -1 && best_y != -1)
{
board[best_x][best_y] = AI;
steps[step_count++] = (Step){AI, best_x, best_y};
printf("AI落子于(%d, %d)\n", best_x + 1, best_y + 1);
}
}
// !复盘游戏过程
void review_process()
{
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;
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.player == PLAYER, 则打印“玩家”,反之打印“AI”
s.x + 1, s.y + 1); // 显示为(行,列)
printf("===== 五子棋人机对战(%dX%d棋盘) =====\n", BOARD_SIZE, BOARD_SIZE);
// 打印当前棋盘状态
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)
printf("x ");
else if (temp_board[row][col] == AI)
printf("");
else
printf("· ");
}
printf("\n");
}
if (i < step_count - 1)
{
printf("\n按Enter继续下一步...");
while (getchar() != '\n')
;
}
}
printf("\n复盘结束!按Enter返回...");
getchar();
}