在C语言中实现三子棋游戏的步骤
梳理的整个三子棋流程的大致思路:
需要创建两个空的.c源文件,以及一个.h头文件用于集成声明函数,以及常量。
将游戏的主体部分拆分成多个步骤并用函数来实现,以及添加一些容易读懂的说明。
下面是具体流程:
创建两个空的源文件,一个负责存放程序的主体,一个负责存放三子棋游戏内容的函数
创建一个空的头文件,用于统一存储函数的声明以及常量
创建好了之后就可以正式开始编译
首先我们需要让程序在启动的时候给人提醒,所以这个时候我们需要想办法做一个简易的游戏菜单用来引领玩家
void menu()
{
printf("press 1 play the game\n");
printf("press 0 quit the game\n");
}
由于这个函数过于简单所以我们只需要直接在main.c中进行创建,紧接着记得在int main主函数里使用,由于这个函数用到了printf函数,我们需要引入头文件,但是我们已经自己创建了一个空的集成头文件,所以我们只需要在game.h头文件里面进行引入stdio.h头文件,这样一来我们只需要在main.c里面引入game.h头文件即可,引入#include"game.h",即可表示引入我们自己的头文件game.h。
头文件里:
#pragma once
#include<stdio.h>
紧接着我们需要使用循环来实现,游戏的开始和关闭
这里我们使用do while来实现,我们还需要映入一个变量,来代表玩家输入的数字,然后来实现对游戏开始或者关闭的判断,再使用scanf函数来让玩家进行选择,通过switch分支语句来实现不同的效果,以及有必要的文字提示,这样一来,整个游戏的主界面基本就完成了
代码如下:
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("leaving the game....\n");
break;
default:
printf("error input for the command,please press 0 or 1\n");
}
} while (input);
return 0;
}
但是现在还不能实现玩游戏的过程,所以接下来我们需要编写游戏的内容
我们都知道三子棋的样子,是九个格子组成,所以我们初步知道行数为3,列数也为3,这个时候我们就需要定义一个二维数组来存放棋子。
在定义二维数组之前
我们需要定义两个常量来储存行和列,在头文件里。
#pragma once
#include<stdio.h>
#define H 3
#define L 3
这里我使用大写的H表示行,大写L表示列,在这之后我关于行和列的变量我会使用小写h和小写l表示,定义完了之后,我们继续来写游戏内容的函数
void game()
{
char board[H][L] = { 0 };
}
接下来我们需要在game.c里定义一个函数来初始化我们的棋盘,也就是将棋盘上九个格子都装上空格‘ ’,这样来实现无棋子的效果。
void IBroad(char board[H][L], int h, int l)
{
for (int i = 0; i < H; i++)
{
for (int j = 0; j < L; j++)
{
board[i][j] = ' ';
}
}
}
这里我给初始化棋盘的函数取名为IBoard,这个函数需要传递三个参数,棋盘空间对应的数组,以及行列数。最后利用数组遍历的方法来将棋盘数组内的空间存储上空格。
(一定要记得在game.h头文件里面去声明自己定义的函数)
随后将棋盘装满之后,我们需要打印出棋盘的样子,这个时候我们就需要定义一个函数,来实现打印棋盘的效果。
在game.c中
void DBoard(char board[H][L], int h, int l)
{
for (int i = 0; i < h; i++)
{
for (int j = 0; j < l; j++)
{
printf(" %c ", board[i][j]);
if (j < l - 1)
{
printf("|");
}
}
printf("\n");
if (i < h - 1)
{
for (int j = 0; j < h; j++)
{
printf("---");
if (j < h - 1)
{
printf("|");
}
}
}
printf("\n");
}
}
记得在头文件里面声明函数
这里我取名为DBoard,打印的过程不是很容易理解,下面我将对函数的内容进行解释
首先实现三子棋棋盘的分割效果,实际上我们只需要打印若干个|符号和_符号就可以实现分割的效果。
这种效果是怎么实现的呢?
首先是打印第一行,显然我们需要打印 | | 的样子,实际上就是打印很多个 |(三个空格加一个|)
由于最后一个我们不需要在结尾打印|所以我们加入一个if来判断,这个
for (int j = 0; j < l; j++)
{
printf(" %c ", board[i][j]);
if (j < l - 1)
{
printf("|");
}
}
紧接着是第二行,第二行里我们需要打印格子底部的横线来隔开,同样的最后我们不需要再打印|符号在结尾,不然右边就会封起来
if (i < h - 1)
{
for (int j = 0; j < h; j++)
{
printf("---");
if (j < h - 1)
{
printf("|");
}
}
}
但是由于整个棋盘的最底部也是无横线的状态所以我们需要让i<h-1 到最后一行之前就不生成横杠
最后把两个生成隔离符号的内容装在一起并且合理的进行换行就能实现打印棋盘的效果。
接下来我们要实现玩家下棋的效果一样需要定义一个函数去实现。
函数内容是这样的
void PlayAction(char board[H][L], int h, int l)
{
int x = 0;
int y = 0;
printf("play act>\n");
while (1)
{
printf("input location,example:x,y\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= H && y >= 1 && y <= L)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("this is not empty\n");
}
}
else
{
printf("error location\n");
}
}
}
记得在头文件里声明
这里我取名为PlayAction表示为玩家行动 ,下面会进行函数的解释
由于棋盘中的棋子是以数组的形式来储存的,以玩家的角度来说对应第一个棋盘的第一个空格
的第一个坐标应该为1 1,但是在数组中下标是从0开始的,这里的x和y为玩家输入的数字,所以在数组的下标里面我们需要对x和y进行-1操作
if (x >= 1 && x <= H && y >= 1 && y <= L)
if里面的判断是为了防止玩家把棋子下出棋盘之外
接下来我们需要定义一个函数来实现电脑下棋的操作。
在这个操作之前我们要想办法实现随机,这个时候我们就需要使用一些有关于生成随机数的函数和一些头文件,这里电脑行动的函数我命名为PCAction,然后在game.h头文件里面引入随机数函数和时间函数的头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define H 3
#define L 3
void IBroad(char board[H][L], int h, int l);
void DBoard(char board[H][L], int h, int l);
void PlayAction(char board[H][L], int h, int l);
void PCAction(char board[H][L], int h, int l);
循环前面先生成随机数的种子,用时间来作为种子数
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("leaving the game....\n");
break;
default:
printf("error input for the command,please press 0 or 1\n");
}
} while (input);
return 0;
}
在电脑行动的函数中就能实现生成随机数的效果
void PCAction(char board[H][L], int h, int l)
{
int x = 0;
int y = 0;
printf("PC act>\n");
while (1)
{
x = rand() % h;
y = rand() % l;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
由于电脑不是人类,这里坐标对应的x和y其实可以直接作为数组的下标,因此生成随机数的时候只需对行列数取余即可,取余得(0 1 2)刚好对应数组的下标 ,
然后就是简单的对棋盘进行判断,防止把棋子下到玩家下过的位置
接下来就是判断当前状态的输赢的函数,这里我取名为CheckWin,我们都知道三子棋要获胜,需要横或竖或斜着下三个棋子,如果赢了,我们就返回对应玩家或者电脑的棋子,*或者#,因此函数是带有返回值的
所以我们在if语句中加入简单的判读进行判断输赢
记得声明
char CheckWin(char board[H][L], int h, int l)
{
for (int i = 0; i < h; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
for (int i = 0; i < l; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
if (board[1][1] == board[0][0] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')
{
return board[1][1];
}
if (CheckFull(board, h, l))
{
return 'Q';
}
return 'C';
}
我们很容易发现单纯判断输赢不是最完美的结果,因为还有可能会出现棋盘下满了的情况,所以我们在这里面在定义了一个函数来检查棋盘是否已满,返回Q就表示平局,C则继续(后面会有代码呼应)
由于在用CheckWin之前需要用这个函数,所以记得把CheckFull放到CheckWin前面
int CheckFull(char board[H][L], int h, int l)
{
for (int i = 0; i < h; i++)
{
for (int j = 0; j < l; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
我们把全部结合在一起,game函数就完工了
void game()
{
char board[H][L] = { 0 };
IBroad(board, H, L);
DBoard(board, H, L);
char ret = 0;
while (1)
{
PlayAction(board, H, L);
DBoard(board, H, L);
ret = CheckWin(board, H, L);
if (ret != 'C')
{
break;
}
PCAction(board,H,L);
DBoard(board, H, L);
ret = CheckWin(board, H, L);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("player won!\n");
}
else if (ret == '#')
{
printf("PC won!\n");
}
else if (ret == 'Q')
{
printf("tie!\n");
}
}
这里引入了一个变量ret作为对于结果的判断,判断的结果都在对应的if elseif 语句里面有说明,简单易懂
最后会回一下主函数
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("leaving the game....\n");
break;
default:
printf("error input for the command,please press 0 or 1\n");
}
} while (input);
return 0;
}
开始检验成果
完美运行!
结尾:
这是本人第一次做教程类型的博客,往后会发更多教程或是笔记类的博客,如果有错误欢迎指正!