想必大家儿时都玩过:#字棋
如图,即一个3x3的格子,玩家需要在横、竖、斜三条线上布下自己的棋子
先连成一条线上3个棋子的玩家获胜
如果格子填满后还未有胜者,即为平局
今天就让我们用c语言代码来完成一个基础的三子棋小游戏
涉及知识点:
二维数组、循环语句、自定义函数、自定义头文件
关联博客:扫雷游戏
本次三子棋代码实现中,我们需要使用到自定义头文件
使用自定义函数前,需要先对函数进行声明
头文件就是一堆声明的集合
问:为什么不把函数声明直接main函数前面?
答:因为这样更方便团队进行同一个项目的分工制作
如何创建自定义头文件?
在vs编译器的“解决资源管理器”里,我们能看到头文件的文件夹
右键它,新建项
选择头文件进行新建,后缀是.h
创建完后,我们可以在另外一个源文件中引用头文件
#include "game.h"
另外,因为我们使用的两个源文件(下面会提到)都需要使用c语言的库函数
所以我们可以在自定义头文件中引用库函数的头文件
这样只用在其他源文件中引用我们的自定义头文件
就无需再引用C语言的库函数头文件了
在三子棋代码中,我们需要编写两个源文件
在实际项目合作中,这种方式可以将不同的自定义函数(实现不同功能)的内容分工给每个人
同样是为了方便团队合作
在两个源文件中编写自定义函数的方式和在一个源文件中编写是一样的
如在头文件game.h中声明了初始化棋盘的函数
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col);
在实现自定义函数的game.c文件中只需要引用头文件game.h
就能正常编写自定义函数的实现部分
#include "game.h"
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col)
{
}
游戏嘛,肯定需要一个最基本的初始菜单了
玩家通过输入1或0来选择进入游戏还是退出游戏
void menu()
{
printf("**********************\n");
printf("********1.play********\n");
printf("********0.exit********\n");
printf("**********************\n");
}
接下来我们需要弄一个初始化的函数
三子棋一共是3x3的格子,所以我们需要一个二维数组来存放棋盘
然后要先把这个棋盘中的每一个棋子都初始化为空格
game.h
#define ROW 3//行
#define COL 3//列
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col);
test.c
char ret = 0;
//存储数据-二维数组
char board[ROW][COL] = { 0 };
//初始化棋盘 - 初始化空格
Intboard(board, ROW, COL);//自定义函数传参
game.c
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//将每个棋盘都初始化为空格
}
}
}
玩家/电脑每走一步,游戏都需要打印一次棋盘,告诉玩家自己下在了哪里,以及对手(电脑)落子的位置
和简单打印二维数组不同
我们需要在每个元素之间打印一个分割线,达到以下效果
// | |
//---|---|---
// | |
//---|---|---
// | |
game.h和test.c文件中写法同上,重点来看game.c文件中函数的实现
//打印棋盘
void Display(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("|");//竖分割线
}//效果 a | b | c
printf("\n");//换行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");//横分割线
if (j < col - 1)
printf("|");//竖分割线
}//效果---|---|---
printf("\n");
}
}
}
这样我们就能输出一个如图所示的棋盘
你可能注意到,这里多了两行提示
这是我们test.c函数中的内容
三子棋游戏需要以函数返回值来判断是否胜利(平局)
不过这里的test函数主要用于玩家最开始1和0的选择(是否开始游戏)
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;
}
这里的srand以及time函数是用来生成随机数,作为电脑落子坐标的
在猜数字游戏的博客里也有提到这两个函数 [链接]
srand((unsigned int)time(NULL));
打印完棋盘后,开始编写玩家落子、电脑落子以及判断谁最终胜利的函数
在日常编写代码的时候,我们也最好使用这种方式
test.c
void game()
{
char ret = 0;
//存储数据-二维数组
char board[ROW][COL] = { 0 };
//初始化棋盘 - 初始化空格
Intboard(board, ROW, COL);
//打印棋盘
Display(board, ROW, COL);
while (1)
{
//玩家走
PlayerMove(board, ROW, COL);
Display(board, ROW, COL);
//判断玩家是否胜利
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑走
BotMove(board, ROW, COL);
Display(board, ROW, COL);
//判断电脑是否胜利
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
//判断状态
if (ret == '*')
{
printf("玩家赢了\n");
}
else if (ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
}
代码中的Iswin函数用于判断谁胜利
因为每走一步的结果有4种情况
所以这里我们不再使用数字(也是避免和原有menu选择弄混)
以字符的形式来表达这4种情况
//在游戏进行的过程中--Iswin判断
//1.玩家赢了 -*
//2.电脑赢了 -#
//3.平局 -Q
//4.继续 -C
玩家走棋的时候,我们需要注意以下几点
//玩家走
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
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");
}
}
}
细心的你肯定发现了,这里的玩家落子使用的是*,也正是玩家胜利时的返回字符
下面电脑落子的时候用的就是#
电脑落子比玩家落子相对较简单
我们只需要让生成的两个随机数在1-3之间即可
void BotMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑走: \n");//提示玩家此处是电脑落子
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
上面提到,每走一步,我们都需要用Iswin函数判断是否有人胜利
三子棋胜利有3种方式:行、列、对角线
这里我们需要分别予以判断,是否存在某一个行、列、对角线上的子全为玩家或电脑所下
//判断胜利
char Iswin(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][1] != ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][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[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
//判断平局
//满了返回1,不满返回2
if (1 == IsFull(board, row, col))
{
return 'Q';
}
//继续
return 'C';
}
这里又出现了一个新的函数,IsFull
当上面的判断胜利的语句走完后,若还没出现返回的情况
我们就要判断是否是平局,即判断棋盘的9个棋子是否都已落下
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;//棋盘满了
}
如果的判断胜利和平局的语句都走完后,还没出现返回的情况
即说明游戏并未停止,我们需要继续游戏
到这里,我们的三子棋游戏就完成了!
跑一下看看效果吧!
这里贴出三子棋函数的完整代码供大家参考
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void Display(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");
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 BotMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑走: \n");
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;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;//棋盘满了
}
//判断胜利
char Iswin(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][1] != ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][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[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
//判断平局
//满了返回1,不满返回2
if (1 == IsFull(board, row, col))
{
return 'Q';
}
//继续
return 'C';
}
#define _CRT_SECURE_NO_WARNINGS 1
#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 };
//初始化棋盘 - 初始化空格
Intboard(board, ROW, COL);
//打印棋盘
Display(board, ROW, COL);
while (1)
{
//玩家走
PlayerMove(board, ROW, COL);
Display(board, ROW, COL);
//判断玩家是否胜利
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑走
BotMove(board, ROW, COL);
Display(board, ROW, COL);
//判断电脑是否胜利
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
//判断状态
if (ret == '*')
{
printf("玩家赢了\n");
}
else if (ret == '#')
{
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;
}
#include
#include
#include
#define ROW 3
#define COL 3
//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col);
//打印棋盘
void Display(char board[ROW][COL], int row, int col);
//玩家走
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑走
void BotMove(char board[ROW][COL], int row, int col);
//判断是否胜利
char Iswin(char board[ROW][COL], int row, int col);
//判断是否有空位
#pragma once
本篇三子棋的博客到此结束了
感谢你看到最后!
下篇博客会教大家,如何打包我们的代码为dll文件(此文件无法编译)
点个赞再走吧,球球了!