文章目录
三字棋或五字棋的代码与逻辑
先看效果图
大体思路及逻辑
编写三(五)棋我们需要创建三个文件 test.c
(游戏运行实现) game.c
(游戏功能实现文件) game.h
(game.c
文件函数头文件声明)。
我们在编写程序前,我们需要定义每个源文件的功能
test.c
:
- 游戏开始界面
- 定义棋盘数组及玩家游玩功能
- 玩家重复游戏功能
game.c
:
- 棋盘的初始化
- 棋盘打印
- 玩家下棋功能
定义两个变量作为二维数组的下标,经过下标放入 ‘O’ 作为玩家的棋子
- 电脑下棋功能
这里需要引入time.h、stdlib.h头文件,使用rand()函数来随机产生数组下标,并通过下标放入‘X’做为电脑的棋棋子
- 游戏输赢判断
game.h
:主要声明 game.c
的函数及定义棋盘规格。
代码编写
游戏开始及头文件定义
我们首先需要创建一个头文件 game.h
用来引入需要的头文件和定义棋盘规格以及游戏类型
代码如下
#include <stdio.h>
#include<stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3
#define TYPE 3
这样就初步完成了game.h
头文件的编写,接下来编写test.c
的开始界面。
创建一个函数
#include "game.h"//引入头文件
void printPlay(){
printf("************\n");
printf("***1.进入***\n");
printf("***0.退出***\n");
printf("************\n");
}
接下来我们需要在main函数中使用while循环加switch分支语句来构建玩家循环进行游戏。
int main(int argc, char** argv)
{
int i = 1;
while (i)
{
printPlay();
printf("请输入:>");
scanf("%d", &i);
switch (i)
{
case 1:
game();
break;
case 0:
break;
default:
printf("输入错误请重新输入!");
break;
}
}
return 0;
}
效果如下;
我们在switch
的case 1
分支中构建一个game
函数用于实现游戏进行逻辑。
棋盘的初始化和打印
我们需要在里面创建用于打印的棋盘数组。
void game(){
char bored[ROW][COL];
}
接下来我们给该数组进行初始化,放入空格符用作占位符然后打印。
在game.h
中声明四个函数一个用于遍历,一个用于打印、电脑和玩家下棋操作及游戏状态判断的功能。
void initBored(char bored[ROW][COL], int col, int row);//初始化数组
void printBored(char bored[ROW][COL]);//打印数组
void playBored(char bored[ROW][COL], int row, int col);//玩家下棋功能
void botBored(char bored[ROW][COL]);//电脑下棋功能
char stateBored(char bored[ROW][COL], char ch);//输赢判断
在initBored
函数中我们使用两个循环进行遍历放入占位符
void initBored(char bored[ROW][COL], int col, int row){
for (size_t i = 0; i < row; i++)
{
for (size_t j = 0; j < col; j++)
{
bored[i][j] = ' ';
}
}
}
然后因为后期玩家或电脑每进行下棋操作便需要打印一次,所以独立创建一个printBored
函数
若只打印数组则显得太过单调,我们则添加些许边框。
void printBored(char bored[ROW][COL]){
for (size_t i = 0; i < ROW; i++)
{
for (size_t j = 0; j < COL; j++)
{
printf(" %c ", bored[i][j]);
if (j < COL - 1)
{
printf("|");
}
}
printf("\n");
if (i == ROW - 1)
continue;
for (size_t j = 0; j < COL; j++)
{
printf("---");
if (j < COL - 1)
{
printf("|");
}
}
printf("\n");
}
}
打印函数完成后,遍需要进行编写玩家下棋操作。
玩家下棋操作
在编写这个函数中需要解决玩家输入的坐标是否 ”有主“,当有主并重复循环让玩家重新输入。
void playBored(char bored[ROW][COL], int row, int col){
int x, y;
while (1)
{
printf("请输入坐标空格隔开:>");
scanf("%d %d", &x, &y);
if (bored[x-1][y-1] == ' ')
{
bored[x-1][y-1] = 'O';
return;
}else{
printf("该坐标已被占用,请重新选择:");
}
}
}
电脑下棋操作
电脑下棋就比玩家下棋多了一个使用rand获取下标,当该下标存在棋子时,重新获取下标。
在使用rand()函数前我需要在test文件中的game
使用tmie
当做种子初始化rand
函数。
void game(){
srand((unsigned int)time(NULL));
char bored[ROW][COL];
}
bot下棋使用循环进行赋值操作,当遇到“空”位置时,跳出循环即可。
void botBored(char bored[ROW][COL]){
while (1)
{
printf("Bot正在下棋!\n");
int timeBot1 = rand() % ROW;
int timeBot2 = rand() % COL;
if (bored[timeBot1][timeBot2] == ' '){
bored[timeBot1][timeBot2] = 'X';
break;
}
}
}
当遍写完以上功能,我们便需要完成test
文件中的game
函数了
void game(){
srand((unsigned int)time(NULL));
char bored[ROW][COL];
initBored(bored, COL, ROW);
printBored(bored);
while (1)
{
playBored(bored,ROW,COL);
printBored(bored);
botBored(bored);
printBored(bored);
}
}
游戏状态
行列判断
游戏的结束的条件是 玩家或电脑下的三个棋字 连在一起时才能结束对局。 当前game
函是死循环的,我们还需要编写一个对于游戏状态判断的函数再搭配while循环构成循环下棋的操作,再由状态函数判断结束循环。
char stateBored(char bored[ROW][COL], char ch)
{
for (size_t i = 0; i < ROW; i++)
{
int numRow = 0;
int numCol = 0;
for (size_t j = 0; j < COL; j++)
{
if (bored[i][j] == ch)
numRow++;
else
numRow = 0;
if (bored[j][i] == ch)
numCol++;
else
numCol = 0;
}
if(numRow == TYPE || numCol == TYPE)
return ch;
}
}
游戏状态函数需要玩家或者电脑传入特征棋子变量,创建两个变量用于记录数组中特征棋子变量类型是否连贯,当出现中断时记录变量置为0;
主、副对角线
棋盘是个正方型 除了行、列还有主、副对角线等斜线。
每个方向需要遍历的线条为 n= (ROW + COL - 1) - (TYPE - 1)* 2 条,但主、副对对角线我们可以同时遍历,所以我们的while
条件则是 i < ROW - (TYPE - 1) 那么我们看下面关于斜线的逻辑。
主对角线及旁线
主对角线我们只需一个循环,条件小于行或列标即可遍历,同样需要一个变量记录值。
由图可清晰看出,主对角线右方每条斜线上的元素坐标,行标与列标成等差,差值为 1。
左放则是列标与行标成等差,差值为 1。
如 [0][1]、[1][2]、[2][3]、[3][4] 及 [1][0]、[2][1]、[3][2]、[4][3] 我们可以看出两组坐标行列互换即可,所以它两边遍历则可以在一个循环中完成。
副对角线及旁线
副对角线我们则可以使用一个变量
col == 0
当作列行标位于副对角线循环外即可完成对副角线的判断。
副角线旁我们可以根据图片得出
[0][3]、[1][2]、[2][1]、[3][0] 及 [1][4]、[2][3]、[3][2]、[4][1] 这组,记 p1 差值 1
[0][2]、[1][1]、[2][0] 及 [2][4]、[3][3]、[4][2] 这组 记p2 差值 2
我们仔细看,这差值不就是外层循环的i值吗?所以也只需要一个循环即可完成对副角线旁的判断
由以上逻辑 对角线可在
i == 0
时完成,这时遍历了主、副对角线,i > 0
时则遍历了主副对角线旁的线。
逻辑清晰,接下来看代码
int i = 0;
while (i < ROW - (TYPE - 1))
{
int num = 0;
int numCol = 0;
if (i == 0)
{
//主对角线
for (int j = 0; j < ROW; j++)
{
if (bored[j][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
{
return ch;
}
}
//副对角线
num = 0;
int col = 0;
for (int j = ROW - 1; j >= 0; j--,col++)
{
if (bored[col][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
return ch;
}
}else
{
//主对角线旁
int tmp = 0;
for (int j = i; j < ROW; j++)
{
if (bored[j][tmp] == ch)
num++;
else
num = 0;
if (bored[tmp++][j] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
}
//副对角线旁
num = 0;
tmp = 0;
for (int j = (ROW - i) - 1; j >= 0; j--)
{
if (bored[tmp][j] == ch)
num++;
else
num = 0;
if (bored[tmp + i][j + i] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
tmp++;
}
}
i++;
}
我们完成了行、列、主副对角线的判断,那么当没有数赢的时候,我们该判断是否是平局还是未完成将继续?
平局
未完成那么我们将继续游戏,我们只需要便利是否还有空位够供玩家或电脑进行下棋操作。
若无空位则平局
for (size_t i = 0; i < ROW; i++)
{
for (size_t j = 0; j < COL; j++)
{
if(bored[i][j] == ' ')
return 'P';
}
}
return 'B';
到此该函数已经完成,我们来看完整的函数
char stateBored(char bored[ROW][COL], char ch)
{
//行列
for (size_t i = 0; i < ROW; i++)
{
int numRow = 0;
int numCol = 0;
for (size_t j = 0; j < COL; j++)
{
if (bored[i][j] == ch)
numRow++;
else
numRow = 0;
if (bored[j][i] == ch)
numCol++;
else
numCol = 0;
if (numRow == TYPE || numCol == TYPE)
return ch;
}
}
//斜线
int i = 0;
while (i < ROW - (TYPE - 1))
{
int num = 0;
int numCol = 0;
if (i == 0)
{
//主对角线
for (int j = 0; j < ROW; j++)
{
if (bored[j][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
{
return ch;
}
}
//副对角线
num = 0;
int col = 0;
for (int j = ROW - 1; j >= 0; j--,col++)
{
if (bored[col][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
return ch;
}
}else
{
//主对角线旁
int tmp = 0;
for (int j = i; j < ROW; j++)
{
if (bored[j][tmp] == ch)
num++;
else
num = 0;
if (bored[tmp++][j] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
}
//副对角线旁
num = 0;
tmp = 0;
for (int j = (ROW - i) - 1; j >= 0; j--)
{
if (bored[tmp][j] == ch)
num++;
else
num = 0;
if (bored[tmp + i][j + i] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
tmp++;
}
}
i++;
}
//平局
for (size_t i = 0; i < ROW; i++)
{
for (size_t j = 0; j < COL; j++)
{
if(bored[i][j] == ' ')
return 'P';
}
}
return 'B';
}
game函数
状态函数中当有赢家时返回变量ch
,平局或继续时返回P
或B
.
回到test.c文件game函数中,我们需要一个char类型的变量tmp
用于接受game.c
文件中状态函数的值,
只需判断tmp
非P
则跳出循环,使用switch语句进行结束语句输出。
void game(){
srand((unsigned int)time(NULL));
char bored[ROW][COL];
initBored(bored, COL, ROW);
printBored(bored);
char tmp = 0;
while (1)
{
playBored(bored,ROW,COL);
printBored(bored);
tmp = stateBored(bored, 'O');
if (tmp != 'P')
{
break;
}
botBored(bored);
printBored(bored);
tmp = stateBored(bored, 'X');
if (tmp != 'P')
{
break;
}
}
switch (tmp)
{
case 'O':
printf("恭喜你你赢了!\n");
break;
case 'X' :
printf("很遗憾你输了!\n");
break;
case 'B' :
printf("平局!\n");
break;
}
}
效果
到此我们的编写完成,让我们来看效果图
到此各位看官可能会迷惑,怎么就只有三字棋?五字棋呢?
五字棋我们只需修改game.h
文件 的TYPE
、COL
和 ROW
属性即可
效果如下:
若以上代码如有bug请在评论区指出。
新人码字不容易,请各位看官点个赞,支持一下。
附上述文件源码:
game.h:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 10
#define COL 10
#define TYPE 5
void initBored(char bored[ROW][COL], int col, int row);
void printBored(char bored[ROW][COL]);
void playBored(char bored[ROW][COL], int row, int col);
void botBored(char bored[ROW][COL]);
char stateBored(char bored[ROW][COL], char ch);
game.c
#include "game.h"
void initBored(char bored[ROW][COL], int col, int row){
for (size_t i = 0; i < row; i++)
{
for (size_t j = 0; j < col; j++)
{
bored[i][j] = ' ';
}
}
}
void printBored(char bored[ROW][COL]){
for (size_t i = 0; i < ROW; i++)
{
for (size_t j = 0; j < COL; j++)
{
printf(" %c ", bored[i][j]);
if (j < COL - 1)
{
printf("|");
}
}
printf("\n");
if (i == ROW - 1)
continue;
for (size_t j = 0; j < COL; j++)
{
printf("---");
if (j < COL - 1)
{
printf("|");
}
}
printf("\n");
}
}
//玩家
void playBored(char bored[ROW][COL], int row, int col){
int x, y;
while (1)
{
printf("请输入坐标空格隔开:>");
scanf("%d %d", &x, &y);
if (bored[x-1][y-1] == ' ')
{
bored[x-1][y-1] = 'O';
return;
}else{
printf("该坐标已被占用,请重新选择:");
}
}
}
//Bot
void botBored(char bored[ROW][COL]){
while (1)
{
printf("Bot正在下棋!\n");
int timeBot1 = rand() % ROW;
int timeBot2 = rand() % ROW;
if (bored[timeBot1][timeBot2] == ' '){
bored[timeBot1][timeBot2] = 'X';
break;
}
}
}
// 状态
char stateBored(char bored[ROW][COL], char ch)
{
//行列
for (size_t i = 0; i < ROW; i++)
{
int numRow = 0;
int numCol = 0;
for (size_t j = 0; j < COL; j++)
{
if (bored[i][j] == ch)
numRow++;
else
numRow = 0;
if (bored[j][i] == ch)
numCol++;
else
numCol = 0;
if (numRow == TYPE || numCol == TYPE)
return ch;
}
}
//主对角线
int i = 0;
while (i < ROW - (TYPE - 1))
{
int num = 0;
int numCol = 0;
if (i == 0)
{
//主对角线
for (int j = 0; j < ROW; j++)
{
if (bored[j][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
{
return ch;
}
}
//副对角线
num = 0;
int col = 0;
for (int j = ROW - 1; j >= 0; j--,col++)
{
if (bored[col][j] == ch)
num++;
else
num = 0;
if (num == TYPE)
return ch;
}
}else
{
//主对角线旁
int tmp = 0;
for (int j = i; j < ROW; j++)
{
if (bored[j][tmp] == ch)
num++;
else
num = 0;
if (bored[tmp++][j] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
}
//副对角线旁
num = 0;
tmp = 0;
for (int j = (ROW - i) - 1; j >= 0; j--)
{
if (bored[tmp][j] == ch)
num++;
else
num = 0;
if (bored[tmp + i][j + i] == ch)
numCol++;
else
numCol = 0;
if (num == TYPE || numCol == TYPE)
return ch;
tmp++;
}
}
i++;
}
//平局
for (size_t i = 0; i < ROW; i++)
{
for (size_t j = 0; j < COL; j++)
{
if(bored[i][j] == ' ')
return 'P';
}
}
return 'B';
}
test.c
#include "game.h"
void printPlay(){
printf("************\n");
printf("***1.进入***\n");
printf("***0.退出***\n");
printf("************\n");
}
void game(){
srand((unsigned int)time(NULL));
char bored[ROW][COL];
initBored(bored, COL, ROW);
printBored(bored);
char tmp = 0;
while (1)
{
playBored(bored,ROW,COL);
printBored(bored);
tmp = stateBored(bored, 'O');
if (tmp != 'P')
{
break;
}
botBored(bored);
printBored(bored);
tmp = stateBored(bored, 'X');
if (tmp != 'P')
{
break;
}
}
switch (tmp)
{
case 'O':
printf("恭喜你你赢了!\n");
break;
case 'X' :
printf("很遗憾你输了!\n");
break;
case 'B' :
printf("平局!\n");
break;
}
}
int main(int argc, char** argv)
{
int i = 1;
while (i)
{
printPlay();
printf("请输入:>");
scanf("%d", &i);
switch (i)
{
case 1:
game();
break;
case 0:
break;
default:
printf("输入错误,请重新输入!");
break;
}
}
return 0;
}
`