C语言三子棋实战:从文件划分到完整逻辑实现
利用C语言编写三子棋游戏是初学者理解模块化编程和二维数组操作的经典案例。核心在于将代码划分为头文件声明(game.h)、功能实现(game.c)和主流程测试(test.c)。本教程通过完整的代码示例,展示了如何通过结构体或数组管理棋盘状态,并实现了包含随机落子的电脑AI及完整的输赢平局判定逻辑。
开发目标与项目结构
三子棋的核心玩法是在 3x3 的棋盘上,通过轮流落子争取先连成一线。为了保持代码整洁和可维护性,我们需要采用模块化开发思路,将不同功能的代码分散在不同的文件中:
game.h:用于放置宏定义(如行列数)和游戏相关函数的声明。game.c:用于实现具体的游戏逻辑,包括棋盘初始化、打印、玩家移动、电脑移动及状态判断。test.c:作为程序的入口,包含main函数、菜单显示及游戏的主循环控制。
在IDE中创建这三个文件时,注意 game.h 和 game.c 属于实现部分,test.c 属于测试逻辑部分。确保它们在同一个工程目录下以便互相引用。
第1步:定义接口与常量 (game.h)
首先,在 game.h 中定义游戏所需的常量和全局函数原型。使用预处理指令 #pragma once 防止重复包含错误。
#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
// 初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
// 打印棋盘
void Displayboard(char board[ROW][COL], int row, int col);
// 玩家下棋
void player_move(char board[ROW][COL], int row, int col);
// 电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
// 判断输赢:' * '玩家赢, '# '电脑赢, ' Q '平局, ' C '继续
char is_win(char board[ROW][COL], int row, int col);
注意:ROW 和 COL 是常量宏,而函数参数中的 row 和 col 是变量,不可直接替换。引入标准库是为了支持输入输出、随机数生成和时间种子。
第2步:实现基础游戏逻辑 (game.c)
在 game.c 中,我们逐步实现各个功能模块。首先需要引入 "game.h" 头文件。
第1小步:棋盘初始化
游戏开始前,需要将 3x3 的二维字符数组初始化为空格,表示空位。这通常使用双重循环完成。
void Initboard(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
第2小步:打印棋盘界面
棋盘打印需要兼顾数据行和分割线。为了避免硬编码行列索引,需根据参数动态打印分隔符。
void Displayboard(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
// 打印每一行的棋子
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
// 打印行之间的分割线(最后一行不打印)
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
第3小步:处理玩家与电脑落子
玩家操作: 需要验证输入的坐标是否在合法范围内(1-3),且对应位置未被占用。若非法则提示重试。
void player_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
printf("请输入坐标:->");
scanf("%d %d", &x, &y);
// 检查范围并修正为数组下标(坐标转下标减1)
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标被占用,请重新输入!\n");
}
}
else
{
printf("坐标非法\n");
}
}
}
电脑操作: 这里实现一个简单的随机AI,使用 rand() 生成随机坐标,直到找到空位为止。
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
printf("电脑下棋\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
第4小步:判断游戏状态
这是最复杂的逻辑部分。每走一步后,需依次检查: 1. 是否有获胜者(横向、纵向、对角线)。 2. 棋盘是否已满(平局)。 3. 游戏是否继续。
辅助函数 is_full 用于检测平局:
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
主判断函数 is_win 返回 char 类型标识结果:
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
// 检查行
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
return board[i][0];
}
// 检查列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
return board[0][i];
}
// 检查对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[0][0];
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
return board[1][1];
// 检查平局
if (is_full(board, row, col))
return 'Q';
return 'C';
}
第3步:构建主游戏循环 (test.c)
在 test.c 中组合所有功能。使用 do-while 循环确保至少执行一次,并通过 srand(time(NULL)) 设置随机种子以保证电脑落子的随机性。
#include "game.h"
void menu()
{
printf("***************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("***************************\n");
}
void game()
{
char board[ROW][COL] = { 0 };
char ret = 0;
// 1. 初始化
Initboard(board, ROW, COL);
// 2. 打印初始棋盘
Displayboard(board, ROW, COL);
// 3. 核心游戏循环
while (1)
{
player_move(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C') break;
computer_move(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C') break;
}
// 4. 显示结果
if (ret == '*') printf("恭喜你,取得胜利!\n");
else if (ret == 'Q') printf("平局\n");
else printf("你输了\n");
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:->");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
在此结构中,switch 语句配合 break 和 default 处理用户输入;menu 提供清晰的操作指引。游戏结束后自动回到主菜单,体现了良好的用户体验。
关键细节与排错
- 坐标系转换:用户输入通常为 1-based 索引(1,2,3),而数组访问是 0-based 索引(0,1,2),因此在写入棋盘前必须执行
x-1, y-1转换。 - 内存重置:如果希望重复玩游戏时棋盘清空,务必调用
Initboard。若在局部变量中分配数组,每次进入game()都会重新初始化。 - 头文件保护:确保所有 .c 文件都包含了正确的
game.h,且编译时将这三个文件链接在一起。
总结
这个三子棋项目涵盖了C语言程序开发的完整生命周期,包括文件结构设计、二维数组操作、循环控制流以及条件分支逻辑。虽然这里的电脑AI是基于简单的随机算法,但通过完善 computer_move 函数中的博弈逻辑,可以进一步提升游戏的挑战性。以上就是基于C语言实现三子棋游戏的详细内容,更多关于C语言三子棋的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









