【C语言】通过C语言实现简单的三子棋
文章目录
前言
本篇文章是对我半个月来C语言学习的总结,通过一个简单的三子棋程序,来加深对分支-循环-数组相关内容的理解。
欢迎各位大牛来给予意见,有什么不对的地方拜托指出。
一、本次小小小小项目前提
1.模块化编程
本程序可以初步认识进行模块化编程,体验模块化编程的优点和相关内容。可以先对本程序做几个简单的要求。
(1)程序的逻辑执行放在test.c这个源文件中 。
(2)程序里的函数声明、头文件创建和引用,放在tpoc.h头文件中。
(3)三子棋使用的相关函数放在tpoc.h这个源文件中。
二、游戏简易菜单
1.菜单打印
这个很简单,直接使用printf来打印出一个简易的菜单就行,菜单是什么样子的可以自由发挥。
比如这样的:
代码如下(示例):
printf("***************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("***************************\n");
或者是这样的:
printf("====================\n");
printf("| 1.开始游戏 |\n");
printf("| 0.退出游戏 |\n");
printf("====================\n");
具体是什么样子可以自由发挥,喜欢什么样子搞什么样子,自己认为好看就行。 这里我就选择第一个。
然后我们把他放在一个函数meun()里
2.游戏菜单的逻辑执行
代码如下(示例):
#include "tpoc.h"
int main()
{
int input = 0;
do
{
meun();//打印菜单
printf("请选择:"); //提示玩家选择开始/退出 游戏。
scanf("%d", & input);//玩家选择1或者0
switch(input)//通过一个switch循环函数,来判断玩家输入的值是否合法,是开始游戏还是退出游戏。
{
case 1:
printf("开始游戏\n");
tpoc();//三子棋游戏
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("输入错误,请重新输入\n");//提示玩家输入的数字不合法。
break;
}
} while (input);//通过一个do-whlie函数来进行游戏的循环,同时由input进行判断,如果玩家输入0,则为假,退出游戏,不再进行循环。
return 0;
}
注意我这里使用的头文件名是tpco.h,这个是我们自己创建的头文件,里面包含了<stdio.h>
在tpoc.h里引用<stdio.h>,包括后续要使用的头文件我们都放在tpco.h里,这样的话我们test.c的文件里面只需要引用tpco.h头文件就可以了。
使用自定义头文件的时候要用 " " 来引用。
当前头文件tpco.h里面的代码
#include<stdio.h>
到这里,我们的简易游戏菜单内容就完成了。
运行测试一下,输入 1 2 0 没有问题。
三、三子棋游戏
1.初始化棋盘
下面就正式开始我们的三子棋游戏了,既然是下棋,那么我们首先要有一个棋盘。我们要先打印一个棋盘出来。
我们知道,三子棋的内容本质上是一个二维数组,所以我们这边要先创建一个二维数组borad,然后把这个数组打印出来,就是我们的一个三子棋了。
void tpoc()
{
char board[3][3];
}
这里创建了一个数组,但是可以发现一点问题,假如我们以后不想玩三子棋了,想玩五子棋了。我们还需要在一个个的修改,我们可以通过两个全局变量,来确定这个二维数组的内容,这个变量我们可以放在头文件tpco.h里面,因为后续还有其他的地方需要用到。
当前头文件tpco.h里面的代码
当前头文件tpco.h里面的代码`
#include<stdio.h>
#define ROW 3
#define COL 3
但打印棋盘之前我们需要进行一个初始化,主要是为了对棋盘进行清空。初始化棋盘很简单,进行一次遍历,把内容全部初始化为‘ ’(空格)即可。这里我们创建一个名为InitBoard的函数,放在tpco.h里进行声明。
当前tpco.h里的代码:
#include<stdio.h>
#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL], int row, int col);
> 然后我们在tpco.c文件里进行函数的实现
void InitBoard(char board[ROW][COL], int row, int col)
{
for(int i = 0; i < row ; i++)
{
for(int j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
这里后面的函数我就不展示tpco.h里面的代码了,逻辑是一样的。
2.打印棋盘
初始化完成之后我们就可以进行棋盘的打印了,直接进行一次遍历打印出数组内容并且进行分割就可以了。这里我们使用DisplayBoard函数来实现打印棋盘的功能。
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)//这里if的作用是保证最后一列不打印
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)这里if的作用是保证最后一行不打印
{
printf("|");
}
}
printf("\n");
}
}
我们来看一下
3.玩家下棋
棋盘出来了,下面我们就可以开始下棋了,这里我们先默认是玩家先开始下棋。这里我们用一个PlayerMove函数来进行玩家下棋这一步。
void PlayerMove(char board[ROW][COL], int row, int col)
{
while (1)
{
int x, y;
printf("玩家走:");
scanf("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)//先判断玩家输入坐标的合法性。
{
if (board[x - 1][y - 1] == ' ')//这里判断玩家输入的坐标有没有被占用。
{
board[x - 1][y - 1] = '*';//没有被占用,放入符号*
break;
}
else
{
printf("坐标已被占用,请重新输入\n");//如果被占用了,提示被占用了,重新输入
}
}
else
{
printf("超过棋盘范围,请重新输入\n");
}
}
}
在玩家走这一步我们要注意几个点:
(1)玩家输入的坐标是从1开始,而数组的下标是从0开始。
(2)判断玩家输入坐标的合法性,如果超过范围要提示并重新输入。
(3)判断玩家输入坐标的位置有没有棋,如果有了,提示并重新输入。
(4)当玩家输入正确的坐标后,跳出循环。
测试一下:
4.电脑下棋
电脑下棋相比玩家下棋不用判断输入的合法性,但是有三个需要注意的点
(1)需要用到rand函数来随机。
(2)同样需要判断坐标是否被占用,如果被占用再随机一个坐标进去。
(3)rand函数需要使用srand函数,我们需要在tese.c文件中用srand函数,并且配合时间戳来进行一个数的随机。
(4)srand和time需要引头文件<stdlib.h>和
void AI_Move(char board[ROW][COL], int row, int col)
{
int x, y;
printf("电脑走:\n");
while (1)
{
x = rand() % row; //生成在棋盘范围内的随机数
y = rand() % col;
if (board[x][y] == ' ') //判断坐标是否被占用
{
board[x][y] = '#';
break; //没被占用则跳出循环,被占用继续随机。
}
}
}
srand((unsigned int)time(NULL));//放在main函数的前面
5.判断游戏状态
每当玩家或电脑下棋后我们需要进行一次游戏状态的判定,这个时候会有四种情况,我们为这四种情况来进行一个返回值,并且用一个Is_Win函数来判断这四种情况。
(1)玩家赢了 -返回 *(2)电脑赢了 -返回 #
(3)棋盘满了-平局 -返回 Q
(4)棋盘还有位置-游戏继续 -返回 C
确定了返回值我们开始写这个函数,我们可以通过判断行 判断列 判断对角来返回相应的字符,平局需要通过另外一个函数来进行一次遍历来判断数组内还有没有空格就能判断棋盘是否满了。如果以上几种情况都没有,那可以返回C,游戏继续。
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][1];
}
}
for (i = 0; i < col; i++)//判断列
{
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
{
return board[1][i];
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//判断对角
{
return board[1][1];
}
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) == 1)//通过一个函数确认棋盘有没有满,来判断是否是平局。
{
return 'Q';
}
return 'C';
}
Is_Full代码的实现
int Is_Full(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;//棋盘如果未满返回 0
}
}
}
return 1;//棋盘满了返回 1
}
下面我们回到test.c,来理清tpco函数的逻辑
大概的逻辑顺序为:1.初始化棋盘——打印棋盘——循环(玩家下棋并且判断——电脑下棋并且判断)——出结果了跳出循环,并判断结果。
void tpoc()
{
char board[ROW][COL];//存放棋盘的数组
char ret;//用来判断返回内容的字符
InitBoard(board, ROW, COL);//初始化棋盘
DisplayBoard(board, ROW, COL);//打印棋盘
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//玩家下完棋打印
ret = Is_Win(board, ROW, COL);//判断游戏状态
if (ret != 'C')//不为C说明出结果了,跳出循环,进行判断。
{
break;
}
AI_Move(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//电脑下完棋打印
ret = Is_Win(board, ROW, COL);//判断游戏状态
if (ret != 'C')//不为C说明出结果了,跳出循环,进行判断。
{
break;
}
}
if (ret == '*')//返回*说明玩家赢了
{
printf("我恭喜你发财了\n");
}
else if (ret == '#')
{
printf("回家养猪去吧\n");//这种弱智电脑都玩不过,可以回去养猪了(bushi)返回 # 电脑赢了
}
else
{
printf("平局\n");//仅剩一种情况了,那就是平局。
}
}
最后让我们看看全部的代码:
test.c
//test.c
#include "tpoc.h"
void meun()
{
printf("****************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("****************************\n");
}
void tpoc()
{
char board[ROW][COL];//存放棋盘的数组
char ret;//用来判断返回内容的字符
InitBoard(board, ROW, COL);//初始化棋盘
DisplayBoard(board, ROW, COL);//打印棋盘
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//玩家下完棋打印
ret = Is_Win(board, ROW, COL);//判断游戏状态
if (ret != 'C')//不为C说明出结果了,跳出循环,进行判断。
{
break;
}
AI_Move(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//电脑下完棋打印
ret = Is_Win(board, ROW, COL);//判断游戏状态
if (ret != 'C')//不为C说明出结果了,跳出循环,进行判断。
{
break;
}
}
if (ret == '*')//返回*说明玩家赢了
{
printf("我恭喜你发财了\n");
}
else if (ret == '#')
{
printf("回家养猪去吧\n");//这种弱智电脑都玩不过,可以回去养猪了(bushi)返回 # 电脑赢了
}
else
{
printf("平局\n");//仅剩一种情况了,那就是平局。
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
meun();//打印菜单
printf("请选择:"); //提示玩家选择开始/退出 游戏。
scanf("%d", & input);//玩家选择1或者0
switch(input)//通过一个switch循环函数,来判断玩家输入的值是否合法,是开始游戏还是退出游戏。
{
case 1:
printf("开始游戏\n");
tpoc();//三子棋游戏
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("输入错误,请重新输入\n");//提示玩家输入的数字不合法。
break;
}
} while (input);//通过一个do-whlie函数来进行游戏的循环,同时由input进行判断,如果玩家输入0,则为假,退出游戏,不再进行循环。
return 0;
}
> tpoc.c
```c
#include "tpoc.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int 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++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
while (1)
{
int x, y;
printf("玩家走:");
scanf("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)//先判断玩家输入坐标的合法性。
{
if (board[x - 1][y - 1] == ' ')//这里判断玩家输入的坐标有没有被占用。
{
board[x - 1][y - 1] = '*';//没有被占用,放入符号*
break;
}
else
{
printf("坐标已被占用,请重新输入\n");//如果被占用了,提示被占用了,重新输入
}
}
else
{
printf("超过棋盘范围,请重新输入\n");
}
}
}
void AI_Move(char board[ROW][COL], int row, int col)
{
int x, y;
printf("电脑走:\n");
while (1)
{
x = rand() % row; //生成在棋盘范围内的随机数
y = rand() % col;
if (board[x][y] == ' ') //判断坐标是否被占用
{
board[x][y] = '#';
break; //没被占用则跳出循环,被占用继续随机。
}
}
}
int Is_Full(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;//棋盘如果未满返回 0
}
}
}
return 1;//棋盘满了返回 1
}
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][1];
}
}
for (i = 0; i < col; i++)//判断列
{
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
{
return board[1][i];
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//判断对角
{
return board[1][1];
}
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) == 1)//通过一个函数确认棋盘有没有满,来判断是否是平局。
{
return 'Q';
}
return 'C';
}
tpoc.h
#include <stdio.h>
#include <stdlib.h>
#include <time.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 PlayerMove(char board[ROW][COL], int row, int col);
void AI_Move(char board[ROW][COL], int row, int col);
char Is_Win(char board[ROW][COL], int row, int col);
最后运行我就不贴出来了,毕竟输一把电脑挺难的。我的水平目前只能让电脑是这个智商了。