今天这篇博客就来讲一下C语言版简单三子棋的实现。
目录
1.项目的总体设计
?2.game模块的具体内容
2.1初始化棋盘函数模块 ——?InitBoard(board, ROW, COL);
2.2打印棋盘函数模块 ——?DisplayBoard(board, ROW, COL);
2.3玩家下棋函数模块 ——?PlayerMove(board, ROW, COL);
2.4输赢判断函数模块 ——?IsWin(board, ROW, COL);
2.5 电脑下棋函数模块 ——? ComputerMove(board, ROW, COL);
1.项目的总体设计
? ? ? ?首先创建一个工程,然后再工程里可以创建两个.c文件? —— test.c(这个用来实现总体设计),game.c(这个就用来实现三子棋游戏的具体步骤函数)。当然了,既然创建了game.c,所以也该有它的一个头文件来存放自定义函数申明和其他的一些东西。
下面就是我所创建的工程文件:
? ? ? ? 然后,设计test.c原文件的具体内容。这里开头时,可以给一个用户界面进行提示,当然,由于这个游戏,我们玩完了一把,还想继续下一把,所以这里就要使用 do ... while 的循环结构,使整个程序至少循环一次,然后根据界面提示来判断是不是进行下一把。
#include<stdio.h>
//打印菜单
void menu()
{
printf("**************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("**************************\n");
}
int main()
{
int input = 0; //用来存储输入选项
do
{
menu(); //做一个用户界面进行提示
printf("请选择》");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋游戏!\n"); //具体的游戏模块
break;
case 0:
printf("退出游戏\n"); //提示退出游戏
break;
default:
printf("输入错误,请重新选择》\n");
break;
}
} while (input); //判断输入的是不是退出游戏,不是,就继续
return 0;
}
完成了以上的内容,我们就可以来测试一下:
?发现以上的代码已经基本实现我们想要的效果,只差当我们选择1时,不是打印三子棋游戏,而是真正开始游戏的模块内容了。
?2.game模块的具体内容
这时,我们可以用一个二维数组来当一个棋盘,用来存放其中下棋的数据,它的行和列的话,为了不直接写死棋盘的大小,我们可以使用符号常量来确定,所以最好就在 game.h文件中定义两个符号常量:
?这里需要注意的就是:我们是在game.h文件中定义的 ROW 和 COL,所以在 test.c文件中使用时,需要引我们自己的头文件 #include "game.h"。
?完成以上操作后,创建一个棋盘
char board[ROW][COL] = { 0 };
接下来就是具体的一个一个的函数模块:
2.1初始化棋盘函数模块 ——?InitBoard(board, ROW, COL);
? ? ? ?首先,我们需要思考给棋盘的内容初始化成什么。我们知道三子棋是游戏是给用户玩的,所以必须打印棋盘,用户才能下棋。故选择空格初始化棋盘的内容,这样在打印的时候,给用户的感觉就像一个空的棋盘。
具体实现代码:
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++) //行
{
int j = 0;
for (j = 0; j < col; j++) //列
{
board[i][j] = ' '; //每个元素初始化成' '
}
}
}
上面这是在game.c文件中具体实现初始化模块的内容。(也就是该函数的定义)
除了这以外,我们还需要在game.h头文件中对它进行声明,才可以真正使用它:
2.2打印棋盘函数模块 ——?DisplayBoard(board, ROW, COL);
当用户在玩游戏时,用户和电脑每下一个子,就需要将棋盘打印出来给用户查看。
同样,先声明:
?然后实现具体的内容:
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
//打印行数
int i = 0;
for (i = 0; i < row; i++) //所需打印的行数
{
//打印数据行
int j = 0;
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"); //换行
}
}
}
2.3玩家下棋函数模块 ——?PlayerMove(board, ROW, COL);
由于下棋阶段是你走一步,我走一步,所以可以看出这里是一个循环的过程,所以可以使用while循环,在while循环中完成玩家下棋和电脑下棋。
玩家下棋声明:
?具体实现的定义:
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("--------------------------\n");
printf("玩家下棋》\n");
printf("--------------------------\n"); //用来装饰一下界面,让人看的更清楚
while (1)
{
printf("玩家走:");
scanf("%d %d", &x, &y);
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");
}
}
}
2.4输赢判断函数模块 ——?IsWin(board, ROW, COL);
功能:返回一个字符,'*' —— 玩家赢,'#' —— 电脑赢 , '3' —— 平局 , '4' —— 继续
函数声明:
?函数定义:
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int flag = 1; //判断对角线时用的标记
//判断每一行
for (i = 0; i < row; i++)
{
int temp = 1; //判断行和列时用的标记
int j = 0;
for (j = 0; j < col - 1; j++) //col为3时,j只需要判断等于0 , 1时的情况
{
if (board[i][j] != board[i][j + 1]) //判断是否有一行不相等
{
temp = 0; //标记为0,并结束本次循环
break;
}
}
if (temp == 1 && board[i][j] != ' ') //若标记没有被修改,并且该行已经落子
{
return board[i][j]; //返回这行其中的任意一个元素
}
}
//判断每一列
for (i = 0; i < row; i++)
{
int temp = 1;
int j = 0;
for (j = 0; j < col - 1; j++) //同上,col为3时,j只需要判断等于0 , 1时的情况
{
if (board[j][i] != board[j + 1][i]) //判断是否有一列不相等
{
temp = 0; //只要有,标记为0,并退出本次循环
break;
}
}
if (temp == 1 && board[j][i] != ' ') //若标记没有被修改,并且该行已经落子
{
return board[j][i]; //返回这行其中的任意一个元素
}
}
//判断对角线
for (i = 1; i < row; i++)
{
if (board[0][0] != board[i][i])
{
flag = 0;
break;
}
}
if (flag == 1 && board[0][0] != ' ')
{
return board[0][0];
}
flag = 1;
for (i = 1; i < col; i++)
{
if (board[0][col - 1] != board[i][i])
{
flag = 0;
break;
}
}
if (flag == 1 && board[0][col - 1] != ' ')
{
return board[0][col - 1];
}
//判断是否为平局
if (IsFull(board,ROW,COL)) //自定义判断是否为平局函数模块
{
return '3';
}
//继续
return '4';
}
这里要注意,需要用到一个判断平局的函数模块。
判断平局函数声明:?
判断平局函数定义:
//判断是否平局
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0; //棋盘没有占满,返回0
}
}
return 1; //棋盘已满,返回1
}
2.5 电脑下棋函数模块 ——? ComputerMove(board, ROW, COL);
声明:
?定义:
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
x = rand() % row; //生成x的随机值
y = rand() % col; //生成y的随机值
if (board[x][y] == ' ') //该坐标没有被占用时,下棋
{
board[x][y] = '#';
break;
}
}
}
?注意:在这个函数模块中,我们需要使用到生成随机值的库函数(rand函数),但在使用它之前需要使用srand函数生成一个起始值,而srand函数有需要一个随机值作为参数,所以这时我们就可以使用time.h库中的time函数,使用时间戳来设置起始点。
具体写法如下:
srand((unsigned int)time(NULL)); //程序在运行中只需要设置一次起始点,所以放到main函数前面
各个模块所需要负责的内容就介绍完了,接下来就是主要的连接在一起,当然,这个在连接时,为了能有更好的游戏体验,所以我们可以在代码中加上一些延时(Sleep)和清屏(system("cls"))函数等。这样看起来能更简洁明了。
最后附上源代码:
下面是 test.c 中的内容:
#include"game.h"
//打印菜单
void menu()
{
printf("**************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("**************************\n");
}
void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != '4')
{
system("cls"); //清下屏幕再打印
//打印棋盘
DisplayBoard(board, ROW, COL);
break;
}
//电脑下棋
ComputerMove(board, ROW, COL);
system("cls"); //清下屏幕再打印
//打印棋盘
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != '4')
{
break;
}
}
if (ret == '#')
{
printf("电脑赢\n");
printf("5秒后将重新开始!");
Sleep(5000); //显示5秒结果
system("cls"); //清屏重新一把
}
else if (ret == '*')
{
printf("玩家赢\n");
printf("5秒后将重新开始!");
Sleep(5000); //显示5秒结果
system("cls"); //清屏重新一把
}
else
{
printf("平局\n");
printf("5秒后将重新开始!");
Sleep(5000); //显示5秒结果
system("cls"); //清屏重新一把
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择》");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls"); //清一下屏幕
game();
break;
case 0:
system("cls");
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新选择》\n");
Sleep(500);
system("cls");
break;
}
} while (input);
return 0;
}
下面是 game.h 的内容:
#pragma once //防止重复引用
#include<stdio.h>
#include<stdlib.h>
#include<time.h> // time 函数的头文件
#include<windows.h> // system 函数的头文件
#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - '3'
//继续 - '4'
char IsWin(char board[ROW][COL], int row, int col);
//判断是否平局
int IsFull(char board[ROW][COL], int row, int col);
最后是 game.c 中的内容:
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
//打印行数
int i = 0;
for (i = 0; i < row; i++)
{
//打印数据行
int j = 0;
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"); //换行
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("--------------------------\n");
printf("玩家下棋》\n");
printf("--------------------------\n");
while (1)
{
printf("玩家走:");
scanf("%d %d", &x, &y);
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");
}
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断是否平局
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0; //没有占满
}
}
return 1;
}
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - '3'
//继续 - '4'
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int flag = 1;
//判断每一行
for (i = 0; i < row; i++)
{
int temp = 1; //用来标记
int j = 0;
for (j = 0; j < col - 1; j++)
{
if (board[i][j] != board[i][j + 1])
{
temp = 0;
break;
}
}
if (temp == 1 && board[i][j] != ' ')
{
return board[i][j];
}
}
//判断每一列
for (i = 0; i < row; i++)
{
int temp = 1;
int j = 0;
for (j = 0; j < col - 1; j++)
{
if (board[j][i] != board[j + 1][i])
{
temp = 0;
break;
}
}
if (temp == 1 && board[j][i] != ' ')
{
return board[j][i];
}
}
//判断对角线
for (i = 1; i < row; i++)
{
if (board[0][0] != board[i][i])
{
flag = 0;
break;
}
}
if (flag == 1 && board[0][0] != ' ')
{
return board[0][0];
}
flag = 1;
for (i = 1; i < col; i++)
{
if (board[0][col - 1] != board[i][i])
{
flag = 0;
break;
}
}
if (flag == 1 && board[0][col - 1] != ' ')
{
return board[0][col - 1];
}
//判断是否为平局
if (IsFull(board,ROW,COL))
{
return '3';
}
//继续
return '4';
}
|