前言
三子棋,我相信这是一个相对普及的游戏,每个同学在无聊时候都会随便拿个废卷子和同桌比划比划,所以针对这个小游戏,综合利用C语言中的循环语句,条件语句,数组,函数调用,等知识对它进行一个初级的编写,(同上一期的扫雷游戏,两游戏的知识点基本一致)并且在每条代码后面都配备了注释,并且会在文章中对每一句代码进行解析,尽量让每一位初学者都能看懂,都能自己动手写出来,有疑问或错误的地方欢迎下方留言。(由于有一部分代码需要单独拿出来解析所以代码拆分有些乱,最后给大家汇总了三个文件的代码)

准备工作(初级三子棋)
(所需的知识点) 针对C语言中的循环语句,条件语句,数组,函数调用等知识点的综合应用,写出一个三子棋游戏 1.利用while for do while语句 2.利用switch if 语句 3.利用二维数组 4.随机数的利用 5.函数的调用 6.函数的声明,宏的定义及使用? ?
提示:该游戏依旧采用多文件的方式进行书写,各文件分工明确,代码简约
一、创建文件(与扫雷一致)

第一步创建两个.c?文件,text?文件用于测试,game 文件用于创建游戏所需函数?

第二步创建一个.h头文件,将前两个文件中所需要的头文件,宏以及函数声明放到此文件中,以此来使.c文件更加整洁?
二、编写
所需头文件,宏,函数声明(game.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 Playmove(char board[ROW][COL], int row, int col);
void Computermove(char board[ROW][COL], int row, int col);
char Iswin(char board[ROW][COL], int row, int col);
1.在测试文件(text.c)中引入主函数
整体代码如下(示例):
void main()//入口主函数(程序起点)
{
srand((unsigned int)time(NULL));
int input = 0;//定义由用户输入的变量
do
{
menu();//调用菜单函数,程序运行后,让玩家看到菜单
printf("请玩家依据菜单选择\n");//提示语(提示玩家输入变量)
scanf("%d", &input);//scanf函数捕获用户输入的input变量数值
switch (input)//switch 根据用户输入的数值 0/1去执行不同的语句
{
case 1://当用户输入的是1
game();//调用game函数去实现游戏
break;
case 0://当用户输入的是0
printf("Exit\n");//打印Exit退出游戏
break;
default://用户数入的不是 0/1重新回到循环,做下一次的重新输入
printf("输入的数据无法识别\n");//提示用户输入的数据有误
break;
}
} while (input);//根据用户输入的input数值 0-->终止循环退出程序 非0继续循环---->玩游戏/重新输入变量值
}
a.?
srand为生成随机数的函数
在这里我们简单介绍一下srand函数生成随机数的用法
1.首先srand函数的所需的头文件为

?2.srand的函数用法

srand函数在传参数时,参数需要的是无符号整型即(unsigned int)
所以如我们的代码所示我们需要把参数强制转换为无符号整型即(unsigned int)
3.以时间(time函数)为传入srand的参数,保证生成的数字足够随机
4.调用time函数时所需要的头文件

?5.time函数所需要的参数

可见time函数需要一个指针类型变量,所以我们在time函数内传进空指针(NULL)即可
b.
调用自定义的菜单函数(这个是自己设计的,成什么样式都是可以的)
void menu()//初始化菜单 进入界面给玩家看
{
printf("*********************************\n");//自定义菜单样式(随意)
printf("* 欢迎您来到三子棋游戏 *\n");
printf("* 1.开始游戏 *\n");
printf("* 0.退出游戏 *\n");
printf("*********************************\n");
}

?
?c.为什么使用do while 循环
do while可以无视条件循环一次,所以无论用户输入什么数值都可以进行一次循环,再根据用户输入的数值去判断是否去进行第二次的循环,或者是退出程序,或者输入的数值不对进行重新的数值输入
d.switch语句的作用
以用户输入的数值判别去执行哪一条语句
选1开始下棋

?选0退出程序

选择非0/1重新选择
?
?2.在主函数调用游戏函数(game函数)
整体代码如下(示例):
void game()
{
char board[ROW][COL];//定义一个3*3的棋盘,我们在这里引入宏这样是为了以后修改棋盘大小,直接在.h修改一个数值即可,不需要在.c文件中多次修改
InitBoard(board, ROW, COL);//调用game.c文件中的InitBoard函数对棋盘进行初始化,把定义棋盘的二维数组都设置成空格
DisPlayBoard(board, ROW, COL);//调用game.c文件中的DisPlayBoard函数,把刚刚设计好的棋盘打印出给玩家看
char ret = 0;//初始化完毕 可以开始下棋分为玩家下和电脑下
while (1)//利用循环去循环判断游戏进行进度
{
Playmove(board, ROW, COL);//玩家去下棋 玩家填写坐标
DisPlayBoard(board, ROW, COL);//将玩家填入的坐标在棋盘上打印出来
ret = Iswin(board, ROW, COL);//判断输赢
if (ret != 'C') //如果棋盘不满足继续下棋的条件终止循环
{
break;
}
Computermove(board, ROW, COL);//电脑利用随机数去生成随机坐标去下棋
DisPlayBoard(board, ROW, COL);//把电脑下棋的位置展示给玩家看
ret = Iswin(board, ROW, COL);//判断输赢
if (ret != 'C')//如果棋盘不满足继续下棋的条件终止循环
{
break;
}
}
if (ret == '*')//自己去定一个标识符作为判断输赢的标志,不一定是*和#其他的字符也都是可以的
{
printf("恭喜您获得游戏胜利!\n");
}
else if (ret == '#')
{
printf("很遗憾计算机获得游戏胜利,下次加油!\n");
}
else
{
printf("您与计算机下成平手\n");
}
}
a. 设置一个3*3的棋盘(3行3列的数组),我们在这里引用了宏的定义,这样方便我们后期对程序的改动(四子棋,五子棋的编写)直接改宏的数值就可以了
三子棋棋盘
b. 调用函数去初始化棋盘,把二维数组的各元素都置空(赋值成空格)
void InitBoard(char board[ROW][COL], int row, int col)//最基本的二维数组赋值的方法
{
int i = 0;
int j = 0;
for (j = 0; j < row; j++)
{
for (i = 0; i < col; i++)
{
board[i][j] = ' ';
}
}
}
?c. 调用函数去打印已经初始化好的三字棋的棋盘
void DisPlayBoard(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++)
{
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");
}
}
}
1.先看前半段代码?
利用循环去打印一个二维数组,打印一个空格打印一个竖线 ' | ' 我们看到的棋盘最后一列是没有竖线的,所以竖线打印到最后一列的前一列(利用if条件判断),每打印一行换一次行

?
?
?
?
2.后半段代码
?
打印出来的棋盘样式
?
?d.棋盘都设置好了,可以开始下棋

?我们规定C为继续下棋,Q为平局
void Playmove(char board[ROW][COL], int row, int col)
{
printf("现在进行请玩家下棋\n");
int x = 0;//定义两个变量 由用户输入 作为棋盘的坐标
int y = 0;
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 Computermove(char board[ROW][COL], int row, int col)//电脑下棋得我
{
printf("现在进行请计算机下棋\n");
int x = 0;//由电脑生成的随机坐标
int y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')//电脑生成随机坐标对应元素是空格电脑就可以在对应坐标位置下棋
{
board[x][y] = '#';//电脑下棋的标识利用#表示
break;//结束循环 进行下一轮玩家下棋
}
}
}
1.
这里为什么要由用户输入的坐标减去1,因为作为玩家并不知道数组的下标是从0开始的,所以当玩家输入1 1坐标时其实应该对应我们二维数组的 [0][0]下标所对应元素
2.
?这里我们用rand去调用我们刚刚定义的随机数,但是随机数相当的大,并不满足我们棋盘的坐标需求,所以我们利用我们生成的随机数去取余行或者列,这样剩下的余数,一定是小于我们行列坐标的数值,这样我们的电脑生成的随机坐标就可以在记录在棋盘内了
e.双方都下好棋,我们可以对双方的输赢进行判断
int IsFull(char board[ROW][COL], int row, int col)//判断是否平局的函数
{
int i = 0;
int j = 0;
for (j = 0; j < row; j++)//访问二维数组的各个元素
{
for (i = 0; i < col; i++)
{
if (board[j][i] == ' ')//看看棋盘是否满了
{
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][0] != ' ')//判断一行三个是否相等而且一行不能有空格
{
return board[i][0]; //返回* 玩家赢 返回#电脑赢 主要看board[0][i]对应的元素
}
}
//判断一列连出来三个的情况
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//判断一列三个是否相等而且一行不能有空格
{
return board[0][i];//返回*玩家赢 返回#电脑赢 主要看board[0][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 (IsFull(board, row, col))
{
return 'Q';
}
return 'C';//都不是继续游戏
}

?f.整体三个文件的代码
text.c
#pragma warning(disable : 4996)//VS 2019需要关闭scanf等函数的C4996警告
#include "game.h"//调用.h文件中text.c所需要的头文件,宏,函数声明
void menu()//初始化菜单 进入界面给玩家看
{
printf("*********************************\n");//自定义菜单样式(随意)
printf("* 欢迎您来到三子棋游戏 *\n");
printf("* 1.开始游戏 *\n");
printf("* 0.退出游戏 *\n");
printf("*********************************\n");
}
void game()
{
char board[ROW][COL];//定义一个3*3的棋盘,我们在这里引入宏这样是为了以后修改棋盘大小,直接在.h修改一个数值即可,不需要在.c文件中多次修改
InitBoard(board, ROW, COL);//调用game.c文件中的InitBoard函数对棋盘进行初始化,把定义棋盘的二维数组都设置成空格
DisPlayBoard(board, ROW, COL);//调用game.c文件中的DisPlayBoard函数,把刚刚设计好的棋盘打印出给玩家看
char ret = 0;//初始化完毕 可以开始下棋分为玩家下和电脑下
while (1)//利用循环去循环判断游戏进行进度
{
Playmove(board, ROW, COL);//玩家去下棋 玩家填写坐标
DisPlayBoard(board, ROW, COL);//将玩家填入的坐标在棋盘上打印出来
ret = Iswin(board, ROW, COL);//判断输赢
if (ret != 'C') //如果棋盘不满足继续下棋的条件终止循环
{
break;
}
Computermove(board, ROW, COL);//电脑利用随机数去生成随机坐标去下棋
DisPlayBoard(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 main()//入口主函数(程序起点)
{
srand((unsigned int)time(NULL));
int input = 0;//定义由用户输入的变量
do
{
menu();//调用菜单函数,程序运行后,让玩家看到菜单输入变量的数值
printf("请玩家依据菜单选择\n");//提示语(提示玩家输入变量)
scanf("%d", &input);//scanf函数捕获用户输入的input变量数值
switch (input)//switch 根据用户输入的数值 0/1去执行不同的语句
{
case 1://当用户输入的是1
game();//调用game函数去实现游戏
break;
case 0://当用户输入的是0
printf("Exit\n");//打印Exit退出游戏
break;
default://用户数入的不是 0/1重新回到循环,做下一次的重新输入
printf("输入的数据无法识别\n");//提示用户输入的数据有误
break;
}
} while (input);//根据用户输入的input数值 0-->终止循环退出程序 非0继续循环---->玩游戏/重新输入变量值
}
?game.c
#pragma warning(disable : 4996)
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (j = 0; j < row; j++)
{
for (i = 0; i < col; i++)
{
board[i][j] = ' ';
}
}
}
void DisPlayBoard(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++)
{
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 Playmove(char board[ROW][COL], int row, int col)
{
printf("现在进行请玩家下棋\n");
int x = 0;//定义两个变量 由用户输入 作为棋盘的坐标
int y = 0;
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 Computermove(char board[ROW][COL], int row, int col)
{
printf("现在进行请计算机下棋\n");
int x = 0;//由电脑生成的随机坐标
int y = 0;
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 (j = 0; j < row; j++)
{
for (i = 0; i < col; i++)
{
if (board[j][i] == ' ')
{
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][0] != ' ')//判断一行三个是否相等而且一行不能有空格
{
return board[i][0]; //返回* 玩家赢 返回#电脑赢 主要看board[0][i]对应的元素
}
}
//判断一列连出来三个的情况
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//判断一列三个是否相等而且一行不能有空格
{
return board[0][i];//返回*玩家赢 返回#电脑赢 主要看board[0][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 (IsFull(board, row, col))
{
return 'Q';
}
return 'C';//都不是继续游戏
}
?game.h
#pragma once
#pragma once
#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 Playmove(char board[ROW][COL], int row, int col);
void Computermove(char board[ROW][COL], int row, int col);
char Iswin(char board[ROW][COL], int row, int col);
?g.成果展示
1.玩家赢(一行案列)

2.电脑赢(副对角线案列)
?
?3.玩家赢(一列案例)

?
?4.玩家赢(主对角案例)

?5.平局案例
?
?
?
总结
三子棋同扫雷游戏,知识点几乎一致,希望大家对所学的知识进行一个汇总,学会的同时,尽可能自己写一下,有错误或者说得不清楚的地方欢迎下方留言
|