三子棋游戏
一· 三子棋游戏规则:
玩家将会看到一个3X3的网格棋盘,默认玩家先下棋,电脑后下棋。
规定:先连成一条直线(3个棋子)的玩家获胜,行,列,对角线均可。若在棋盘下满时仍未分出胜负,则为平局。
二·C语言中的三子棋
我使用的是vs2019 编译器
1:工程的创建与分工
首先打开vs2019编译器,新建文件(三子棋),然后如图一样进行工程的创建和分工,要清楚自己每个文件中记录的内容。
将头文件放到test.h这种方法的好处:在编写工程是我们要分开来写实现,这样既可以分工合作提高工作效率,也可以提高代码的阅读效果。
在game1.c和test.c文件中只需要使用一个头文件#include"stdio.h" 这个头文件进行。
重点:在每次编写完一段代码,就去编译一下看有没有问题,这样可以避免我们编写代码结束后,开始测试产生一大推错误,很麻烦,也会很心累。
2:代码的编写
1:菜单的编写
首先我们的游戏开始必须要有菜单让别人眼前一亮。
void menu()
{
printf("***********************************\n");
printf("************* 1.play *************\n");
printf("************* 0.exit *************\n");
printf("***********************************\n");
}
菜单就用menu这个函数,这个实现的是1:开始游戏,0:结束游戏。
我们测试一下看打印的效果。控制台显示如下:
有菜单函数,那么我们就要进行最重要的函数,接下来就是我们必不可少的主函数。
2:主函数
int main()
{
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);
return 0;
}
这里使用do while循环,因为如果有平局或者输或者赢,都要多打印一次,所以使用do while比较好,swich不多说了,就是看做那个函数的问题,
大家看见这个不明白的代码,很奇怪,别紧张后面慢慢讲 srand((unsigned int)time(NULL));
最终要的就是我们game代码了,这个可是核心,这个是我们的重点。(以上两个代码全在test.c中)。
3.游戏代码
在这里我给大家讲个tips:我们在函数的命名格式时,不要随便命名,要有规则不要胡乱命名,到后期别人看不懂,有时候也把自己搞混乱了,还是要多讲究一些,才能走更远,得到跟多的认可。
已知#子棋是又一个3×3的表格,所以我们就要创建一个3×3的表格,用编程语言其实就是用一个二维数组去标识所以我们就可以创建一个二维数组。
char q[ROW][COL] = { 0 };
这里我们创建了一个类型为char 的二维数组q[][] ,这里我们用ROW表示数组行,COL表示数组的列,这个标记一个重点:在创建数组中行可以省略但是列不可以。
数组q[ROW][COL],我们在头文件去定义ROW,COL,给他们赋值因为如果每一步都去给他们值,会很麻烦,然后还会出现错误。如下图(该图在头文件test.h中)
在这里我们定义二维数组q的大小,这样设计有很多便利,我们可以直接在头文件上去变更数组的大小。
接下来我们正片开始:
1:game()函数大概
void game ()
{
//初始化棋盘
//显示打印的棋盘
//玩家下棋
//打印棋盘
//判断输赢
//电脑下棋
//打印棋盘
//判断输赢
}
以上是我们game()函数的大概 ,也是我们需要实现的逻辑,(该文件也是在test.c文件中,如果又在其他文件的我会标注)然后我们就要开始去一步步的编写代码,
2.初始化棋盘:
我创建的函数如下
然后开始进行初始化:
void zuoqipan(char q[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
q[i][j] = ' ';
}
}
}
(game.c文件)
这里我们就用到了开始讲的二维数组的初始化,让我们棋盘中落子的位置是空的便于后面赋值。
这里我们我们需要在头文件声明,如下:
(game.h文件)
后面如有类似操作不解释了哦。
3.显示棋盘
函数名以及声明:
(test.c文件)
声明:
(game.h文件)
来看看成品然后我们开始讲解:
从我画线的地方不难看出我们可以分为俩个板块来写\n第一个是一个空格一个竖杠一个空格一个竖杠依次循环( | |)为输入行\n第二个是三个短杠为一组在加一个竖杠依次循环(---|---|)为分割行\n但是每一行的最后和一列的最后都没有打印,我们要用到if语句来判定。
代码如下:
void dayinqipan(char q[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 ", q[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//打印下线______;
if (i < row - 1)
{
//printf("---|---|---\n");
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
(game.c文件)
4.玩家下棋函数
函数名以及声明:
(test.c文件)
(game.h文件)
函数体如下:
void playerqi(char q[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 (q[x-1][y-1] ==' ')
{
q[x-1][y-1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
break;
}
}
else
{
printf("输入坐标不合法,请重新输入\n");
}
}
(game.c文件)
在这里我们用‘*’来代替玩家下棋,x,y就是我们要输入的坐标。
开始一步步讲解:
1.我们输入的坐标x,y要合法,不然超出行或者列就没有意义。
if (x >= 1 && x <= row && y >= 1 && y <= col)
2.因为常人会把第一行或者列当成1开始,所以我们要找到二维数组中的坐标我们要对x和y进行减1操作,如图:
//在空格中落子
if (q[x-1][y-1] ==' ')
{
q[x-1][y-1] = '*';
break;
}
首先我们要判断在三子棋格子中是否为空格,如果是空格才可以落子,
3.如果该空格被占用,我们就要进行另一个步骤。
else
{
printf("坐标被占用,请重新输入\n");
break;
}
2和3在一起。
4.如果输入x,y超出我们棋盘的大小,我们就要提醒玩家。
else
{
printf("输入坐标不合法,请重新输入\n");
}
经过以上4点,我们就知道落子已经结束,但是可以发现这些操作不是一次就完成的,所以我们就要使用while 循环来进行解决问题,然后每当输入正确坐标后我们就要使用break来跳出循环。
一个tips:当有循环和swich才可以使用break来跳出循环。
5电脑下棋函数
函数名和声明:
(test.c文件)
(game.h文件)
函数体如下:
void computerqi(char q[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋>\n");
while(1)
{
x = rand() % row;
y = rand() % col;
if (q[x][y] == ' ')
{
q[x][y] = '#';
break;
}
}
}
(game.c文件)
因为是电脑下棋,值是随机的,所以我们用时间戳来表示坐标,同时我们也用到了rand函数,如图在上文已经写了。
int main()
{
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);
return 0;
}
srand((unsigned int)time(NULL))引入时间戳来让电脑下棋
时间戳所需的头文件:
现在我把初始化棋盘,打印棋盘,玩家,电脑在头文件的声明写一下,如图:
因为当双方下完棋之后不能说还是初始的棋盘,那样我们就不知道到底是什么情况,所以我们在玩家下棋函数和电脑下棋函数下面分别加上显示函数来打印我们落完子的棋盘。
这里我们已经把游戏的下棋的主要代码结束了,游戏肯定有输赢,现在玩家就差判断输赢,我先给大家测试一下我们写的代码。
这就是我们每次写完一小部分就去测试,这样子我们的错误会大大减少,现在我们就开始去判断输赢,最后一个环节。
6.判断输赢
函数名和声明:
char whowin(char q[ROW][COL], int row, int col)
(test.c文件)
//玩家赢‘*’
//电脑赢‘#’
//平局 ‘Q’
//继续 'c'
char whowin(char q[ROW][COL], int row, int col);
(game.h文件)
函数体:
char whowin(char q[ROW][COL], int row, int col)
{
//赢
//行
int i = 0;
for (i = 0; i < row; i++)
{
if (q[i][0] == q[i][1] && q[i][1] == q[i][2] && q[i][0] != ' ')
{
return q[i][0];
}
}
//列
for (i = 0; i < col; i++)
{
if (q[0][i] == q[1][i] && q[1][i] == q[2][i] && q[0][i] != ' ')
{
return q[0][i];
}
}
//对角线
if (q[0][0] == q[1][1] && q[1][1] == q[2][2] && q[1][1] != ' ')
return q[1][1];
if (q[0][2] == q[1][1] && q[1][1] == q[2][0] && q[1][1] != ' ')
return q[1][1];
if (IsFull(q, row, col) == 1)
{
return 'Q';
}
//继续
return 'c';
}
(game.c文件)
解读:既然是判断输赢,那就是三个一样连成一条直线就算赢,那么就会有四种情况:俩条对角线,横向和纵向,这样我们就分别写出来如上代码,其中我们要一个平局函数IsFull(board, row, col)重要的我都注释解读请大家多看代码块。
展示平局代码:
int IsFull(char q[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 (q[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
(game.c文件)
到现在我们已经结束了;接下来我给大家分享我写的全部代码
(test.c)
#include"test.h"
void menu()
{
printf("***********************************\n");
printf("************* 1.play *************\n");
printf("************* 0.exit *************\n");
printf("***********************************\n");
}
void game()
{
char ret=0;
char q[ROW][COL] = { 0 };
//初始化棋盘
zuoqipan(q, ROW, COL);
//打印棋盘
dayinqipan(q, ROW, COL);
while (1)
{
//玩家下棋
playerqi(q, ROW, COL);
dayinqipan(q, ROW, COL);
ret = whowin(q, ROW, COL);
if (ret != 'c')
{
break;
}
//电脑下棋
computerqi(q, ROW, COL);
dayinqipan(q, ROW, COL);
//判断输赢;
ret = whowin(q, ROW, COL);
if (ret != 'c')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else
{
printf("平局");
}
}
int main()
{
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);
return 0;
}
(game1.c)
#define _CRT_SECURE_NO_WARNINGS
#include "test.h"
void zuoqipan(char q[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
q[i][j] = ' ';
}
}
}
void dayinqipan(char q[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 ", q[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
//打印下线______;
if (i < row - 1)
{
//printf("---|---|---\n");
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
void playerqi(char q[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 (q[x-1][y-1] ==' ')
{
q[x-1][y-1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
break;
}
}
else
{
printf("输入坐标不合法,请重新输入\n");
}
}
}void computerqi(char q[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋>\n");
while(1)
{
x = rand() % row;
y = rand() % col;
if (q[x][y] == ' ')
{
q[x][y] = '#';
break;
}
}
}
int IsFull(char q[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 (q[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
char whowin(char q[ROW][COL], int row, int col)
{
//赢
//行
int i = 0;
for (i = 0; i < row; i++)
{
if (q[i][0] == q[i][1] && q[i][1] == q[i][2] && q[i][0] != ' ')
{
return q[i][0];
}
}
//列
for (i = 0; i < col; i++)
{
if (q[0][i] == q[1][i] && q[1][i] == q[2][i] && q[0][i] != ' ')
{
return q[0][i];
}
}
//对角线
if (q[0][0] == q[1][1] && q[1][1] == q[2][2] && q[1][1] != ' ')
return q[1][1];
if (q[0][2] == q[1][1] && q[1][1] == q[2][0] && q[1][1] != ' ')
return q[1][1];
if (IsFull(q, row, col) == 1)
{
return 'Q';
}
//继续
return 'c';
}
(test.h)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
void zuoqipan(char q[ROW][COL], int row, int col);
void dayinqipan(char q[ROW][COL], int row, int col);
void playerqi(char q[ROW][COL], int row, int col);
void computerqi(char q[ROW][COL], int row, int col);
//玩家赢‘*’
//电脑赢‘#’
//平局 ‘Q’
//继续 'c'
char whowin(char q[ROW][COL], int row, int col);
三.结束语
到这里就结束了,我希望大家去操作实现一下,然后自己去实现,如果觉得对你有帮助,给个赞哦。如果有没有说清楚的请大家留言。