从0开始学c语言-17-二维数组以及数组传参应用:三字棋

本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。

下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。

上一篇:从0开始学c语言-16-数组以及数组传参应用:冒泡排序_阿秋的阿秋不是阿秋的博客-CSDN博客

放一个链接吧

链接:https://pan.baidu.com/s/1rLHiBu8U58xowDRmrswddA?pwd=vo19 
提取码:vo19

 

目录

三字棋

三字棋是怎么玩的呀?

思路

创建文件

主函数test.c

菜单test.c

游戏头文件game.h

主函数中的游戏函数test.c

游戏头文件对应函数过程game.c

//定义函数_数组初始化

//打印棋盘

//玩家下棋

//电脑下棋

//判断输赢

//判断满否?


三字棋

不进行喂饭式的引导,讲一点思路就直接抛代码了昂。注释里写得很详细,这玩意能看懂就行,看不懂就直接问我。

三字棋是怎么玩的呀?

你看,就像这样,

 在选择游戏后,提示游戏开始并出现棋盘,随后玩家和电脑下棋,直到分出胜负。

 然后回到菜单,选择游戏或者退出。

思路

三个文件分别写需要的代码,主要想清楚棋盘如何打印,以及棋盘和二维数组的关系。理清楚玩家输入数字和数组下标关系,思考电脑怎么下棋(提示随机数设置)。

注意在test.c和game.c中引用game.h。

#include "game.h"

看好后续程序里的注释里是什么。

创建文件

 

我们在头文件中进行函数声明以及变量的定义,在test.c中书写主要程序,在game.c中书写函数的具体实现过程。

主函数test.c

int main()
{
	menu();
	printf("请选择:>");
	int input = 0;
	scanf("%d", &input); //总是忘记&符号
	switch (input)
	{
	case 1:
		printf("游戏开始");
		begin(); 
		break;
	case 2:
		printf("下次再见~");
		break;
	default:
		printf("输入错误,请重新输入");
		break;
	}
	return 0;
}

这样的主函数代码没办法实现我们玩完游戏后选择是否继续游戏,所以我们加上循环就可以了。

int main()
{
	int input = 0;
    srand((unsigned int)time(NULL)); 
//设置电脑下棋的随机数起点
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input); //总是忘记&符号
		switch (input)
		{
		case 1:
			printf("游戏开始\n");
			begin(); //游戏函数
			break;
		case 2:
			printf("下次再见~\n");
			return 0;  //break只能跳出离他最近的switch,换成这个来结束程序
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

菜单test.c

void menu()
{
	printf("  1.玩游戏\n");
	printf("  2.退出\n");
	printf("………………\n");
}

游戏头文件game.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//定义常量
#define HANG 3
#define LIE 3


//声明函数
//void zero(char aq[HANG][LIE], int HANG, int LIE) 
//这样写就不是变量而是常量了,所以我们要做个区分

//棋盘需要的函数
void zero(char aq[HANG][LIE], int hang, int lie); //初始化数据
void xian(char aq[HANG][LIE], int hang, int lie); //打印棋盘


//开始游戏

//玩家下棋
void player(char aq[HANG][LIE], int hang, int lie);
//电脑下棋
void computer(char aq[HANG][LIE], int hang, int lie);
//输赢判断
char end(char aq[HANG][LIE], int hang, int lie);

主函数中的游戏函数test.c

void begin()
{
	//第一步——创建棋盘
	// 
	//1·有二维数组来存放数据
	//2·数组初始化应该放什么
	//3·打印棋盘以及分割线

	//1·有二维数组来存放数据
	char aqiu[HANG][LIE]; //大小写的区别
	//不用数字来代表是因为我们需要这样一个变量来帮助我们完成游戏

	//2·数组初始化应该放什么-放空格
	zero(aqiu,HANG,LIE);

	//3·打印棋盘以及分割线__本质是打印数组的内容
	xian(aqiu, HANG, LIE);

	//第二步——开始玩耍并判断输赢
	//1·玩家怎么下
	//2·电脑怎么下
	//3·判断输赢——结束

	1·玩家怎么下
	//player(aqiu, HANG, LIE);
	2·电脑怎么下
	//computer(aqiu, HANG, LIE);
	现在已经实现了电脑和玩家分别下一步棋
	那么我们需要做的是,让它们可以继续下棋
	继续下棋就意味着需要循环这个下棋过程
	循环需要一个判断是否继续游戏的函数
	而是否继续游戏也和输赢判断有关
	因为输赢后,游戏也就结束了
	所以我们把游戏继续和输赢判断写在一起
	// 
	
	//3·判断输赢
	// 我们需要判断输赢的一个条件
	// 那么便需要一个返回值
	// 如下
	//3·1 玩家赢——*
	//3·2 电脑赢——#
	//3·3 平局  ——E
	//3·4 游戏继续——C
	char sign = 0; //因为返回值都是字符,所以用char返回类型
	//不能写在循环里,否则跳出循环后无法判断输赢的情况
	//循环下棋直到分出胜负为止
	while (1)
	{
		player(aqiu, HANG, LIE);
		//end(aqiu, HANG, LIE);//写成这样没办法接收这个函数的返回值,需要一个变量来储存
		sign = end(aqiu, HANG, LIE);
		//那么这里的sign会有四种情况
		//只有C字符才会允许电脑继续下棋
		//其他字符都会结束游戏
		if (sign != 'C')//意味着结束
		{
			break;
		}
		computer(aqiu, HANG, LIE);
		sign = end(aqiu, HANG, LIE);
		if (sign != 'C')//意味着结束
		{
			break;
		}
	}
	//跳出循环意味着输赢已经分出
	if (sign == '*')
	{
		printf("恭喜玩家取得胜利~\n");
	}
	else if (sign == '#')
	{
		printf("恭喜电脑取得胜利~\n");
	}
	else
	{
		printf("高手过招,点到为止,恭喜二位平局\n");
	}
}

游戏头文件对应函数过程game.c

//定义函数_数组初始化

void zero(char aq[HANG][LIE], int hang, int lie)
{
	int i = 0;  //变换行需要的变量
	int j = 0;  //变换列需要的变量
	for (i = 0; i < HANG; i++)
	{
		for (j = 0; j < LIE; j++)
		{
			aq[i][j] = ' '; //放空格
		}
	}
}

//打印棋盘

//按行来划分,分为有数组的和没数组的
void xian(char aq[HANG][LIE], int hang, int lie)
{
	int i = 0; //行
	int j = 0; //列
	for (i = 0; i < HANG ; i++)  //每一行
	{
		printf(" %c | %c | %c \n",aq[i][j],aq[i][j + 1],aq[i][j + 2]);
		if(i < HANG - 1)
		printf("---|---|---\n"); //要比数组少一行
	}
}

//这样写只能打印3*3的棋盘
//我们还需要改进

//这次我们把行和列一起看
void xian(char aq[HANG][LIE], int hang, int lie)
{
	int i = 0; //行
	int j = 0; //列
	for (i = 0; i < HANG; i++)  //每一行
	{
		for (j = 0; j < LIE-1; j++)  //每一列
		{
			printf(" %c |", aq[i][j]);
		}
		printf("\n"); //加(换)行
		for (j = 0; j < LIE; j++)  //每一列
		{
			printf("---|");
		}	
		printf("\n");
	}
}

//但是不对,没考虑到边缘区域

现在这就是我们打印棋盘的代码。

void xian(char aq[HANG][LIE], int hang, int lie)
{
	int i = 0; //行
	int j = 0; //列
	for (i = 0; i < HANG; i++)  //每一行
	{
		//打印棋子线
		for (j = 0; j < LIE; j++) //每一列
		{
			printf(" %c ", aq[i][j]);
			if (j < LIE - 1) //边缘不打
			{
				printf("|");
			}
		}
		printf("\n");  //换行
		//打印分割线
		if(i < HANG - 1)  //要找到这个条件,这分割线是比数组行少1的
		{
			for (j = 0; j < LIE; j++)  //每一列
			{
				printf("---");
				if (j < LIE - 1)  //边缘不打
				{
					printf("|");  
				}
			}	
		}
		printf("\n");//换行
	}
}

//玩家下棋

void player(char aq[HANG][LIE], int hang, int lie)
{
	
	int x = 0; //玩家输入横坐标变量
	int y = 0; //玩家输入纵坐标变量

	//考虑输入坐标和下标之间的关系
	//坐标范围1-3
	//下标范围0-2
	while (1)  //因为后续重新输入的关系,需要循环
	{
		printf("玩家下棋\n");
		printf("请输入坐标:>");
		scanf("%d %d", &x, &y); //取地址啊!又忘了
		
		//判断输入坐标是否在范围内
		if (x >= 1 && x <= hang  && y >= 1 && y <= hang )
		{
			//aq[x - 1][y - 1]='*; //换空格为棋子
			//不能直接下棋,应该判断如果输入棋子已经被占用
			if (aq[x - 1][y - 1] == ' ') //未被占用
			{
				aq[x - 1][y - 1]='*'; //换空格为棋子
				xian(aq, hang, lie); //打印棋盘与棋子
				break; //下棋成功跳出循环
			}
			else
			{
				printf("位置已有棋子,请重新输入\n");
			}
		}
		else
			printf("超出范围,请重新输入\n");  //重新输入意味有循环
	}
	//整段玩家下棋的逻辑就是,给定两个坐标让玩家输入
	//1·判断输入坐标是否在范围内——范围需要和下标比较,因为数组数据是用下标访问的
	//2·输入坐标是否被其他棋子占用——也就是判断是不是空格
	//如果输入坐标满足棋盘范围且未被占用则下棋成功,
	//跳出循环并结束玩家下棋
	//其他情况则继续让玩家输入坐标,直到下棋成功为止
}

//电脑下棋

void computer(char aq[HANG][LIE], int hang, int lie)
{
	printf("电脑下棋:>\n");  //不加上换行的话,棋盘会有一行打印在这里
	//电脑下棋如果智能的话很复杂,这里使用之前学过的随机数
	while (1)
	{
		int x = rand() % hang;
		int y = rand() % LIE;  //在game.h中进行了头文件的引用
		//上面两行代表电脑的xy坐标给定了0-2范围内的随机数
		//和玩家手动输入的坐标不同,电脑的范围正好对应数组下标范围内
		//那么我们现在应该考虑的是:棋位是否被占用
		if (aq[x][y] == ' ')
		{
			aq[x][y] = '#';  //换空格为棋子
			xian(aq, hang, lie);  //打印棋盘
			break;  //电脑下棋成功,跳出循环
		}
		//如果被占用,那我们需要重新生成随机数,于是便有了循环
	}
	//和玩家下棋不同的是,电脑的坐标和数组下标范围相同
	//不需要范围判断,只需要判断棋子是否被占用
	//其次,如果被占用,那便重新随机生成坐标
	//不需要提示被占用
	//一直随机生成到电脑下棋成功,电脑的下棋过程也就结束了
}

//判断输赢

char end(char aq[HANG][LIE], int hang, int lie)
{
	//我们设定end有四种返回符号
	//那就意味着是有分支情况的
	//赢的情况:行、列、对角线相同三种情况

	//行、列都有三行
	int x = 0;  //棋盘hang下标
	int y = 0;  //棋盘lie下标
	char re[2] = {'*','#'}; //判断字符需要
	int i = 0; //访问字符数组下标
	int flag = 0; //判断连续
	for (i = 0; i < 2; i++)
	{
		//一·判断同一行的每列是否相等,共三行
		for (x=0,y = 0; x < hang && y < lie; y++) //循环每一列 y范围0-2 
		{
			if (aq[x][y] == re[i]) //第x行的每y列都相等
				continue;
			x++; //不等于就算下一行,算到x=hang时意味着所有行都不相等
			y = -1;  //下一行y要置零,因为语句结束会去到调整部分,所以等于-1
		}
		//跳到这里有两种情况
		//1·有同一行的每列都相等
		//2·所有行的每列都不相等——这种情况的x=hang
		if (x != hang)
		{
			return re[i]; //行相等且赢
		}


		//二·判断同一列的每行是否相等,共三列
		for (x = 0, y = 0; x < hang && y < lie; x++) //循环每一行 x范围0-2 
		{
			if (aq[x][y] == re[i]) //第y列的每x行都相等
				continue;
			y++; //不等于就算下一列,算到y=lie时意味着所有行都不相等
			x = -1;  //下一行x要置零,因为语句结束会去到调整部分,所以等于-1
		}
		//跳到这里有两种情况
		//1·有同一行的每列都相等
		//2·所有行的每列都不相等——这种情况的y=lie
		if (y != lie)
		{
			return re[i]; //列相等且赢
		}


		//三·左对角线是否相等——这里默认棋盘是正方形
		for (x = 0, y = 0; x < hang && y < lie; x++, y++)
		{
			if (aq[x][y] == re[i]) //第y列的每x行都相等
				continue;
			break;
		}
		//跳到这里有两种情况
		//1·左对角线都相等且赢——此时x与y都等于hang于列
		//2·不相等——此时x小于hang,y小于lie
		if (x == hang && y == lie)
		{
			return re[i];  //左对角线相等且赢
		}


		//四·右对角线是否相等
		for (x = 0, y = lie - 1; x < hang && y >= 0; x++, y--)
		{
			if (aq[x][y] == re[i])
				continue;
			break;
		}
		//跳到这里有两种情况
		//1·右对角线都相等且赢——此时x=hang,y=-1
		//2·不相等——此时x<hang且y>=0
		if (x == hang && y == -1)
		{
			return re[i];  //右对角线相等且赢
		}	
	}
	//以上四种情况
		// 1·行相等且结束
		// 2·列相等且结束
		// 3·左对角线相等且结束
		// 4·右对角线相等且结束
		//如若都不满足的话
		//数据会来到这里
		
	//而来到这里的数据有两种情况
		//1·满棋子且输赢未分
		//2·未满棋子
	//一共有四大类情况	
	//3·1 玩家赢——*
	//3·2 电脑赢——#
	//3·3 平局  ——A
	//3·4 游戏继续——C
		//现在3·1和3·2已经判断出来
		//所以我们可以把3·3和3·4写在一起
		//一个判断是否满棋子的函数
		//如果满了,我们返回1
		//没满我们返回0
	//用变量j来储存函数返回值
	int j = full(aq,hang,lie);
	if (j == 1)
	{
		return 'A';
	}
	return 'C';
}

//判断满否?

int full(char aq[HANG][LIE], int hang, int lie)
{
	//一个判断是否满棋子的函数
	//如果满了,我们返回1
	//没满我们返回0
	int x = 0;  //hang下标
	int y = 0;  //lie下标
	for (x = 0; x < hang; x++)
	{
		for (y = 0; y < lie; y++)
		{
			if (aq[x][y] == ' ') //棋盘没满
			{
				return 0;
			}
		}
	}
	return 1;
}

难搞哦,小白还是先模仿吧,自己凭空写出来是需要点天赋的。

加油!

下一篇:

从0开始学c语言-过渡-函数递归、循环语句、数组练习_阿秋的阿秋不是阿秋的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值