C语言之初级(保姆级别------三子棋游戏)

Posted 梦葻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言之初级(保姆级别------三子棋游戏)相关的知识,希望对你有一定的参考价值。

前言

三子棋,我相信这是一个相对普及的游戏,每个同学在无聊时候都会随便拿个废卷子和同桌比划比划,所以针对这个小游戏,综合利用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.平局案例


总结

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

以上是关于C语言之初级(保姆级别------三子棋游戏)的主要内容,如果未能解决你的问题,请参考以下文章

**入门级C语言小游戏——“三子棋” 万字保姆级教学**

一些数组知识和初级C语言,实现三子棋小游戏

C语言实现三子棋游戏

C语言实现入门级小游戏——扫雷

C语言之实现三子棋游戏 附注释

C语言游戏跟电脑battle三子棋