C语言简单游戏编程学习入门之九宫格拼图

运行结果预览图!

在这里插入图片描述
经过玩家操作通关时
在这里插入图片描述

源程序代码

/*头文件引用*/
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<time.h>

/*定义全局变量*/
#define maxsize 9				//地图的最大边框 
int row=3;						//地图的行数
int col=3;						//地图的列数 
int map[maxsize]={0};			//地图(实际上是一个一维数组) 

/*定义程序所需函数*/
void mapToZero();				//地图清0 
void initMap();					//初始化地图(随机生成9个数字,按位置依次将0到8填入地图) 
void printMap();				//打印地图,除了8其他的都打印出来
int isWin();					//判断拼图是否已经完成 
void turn();					//玩家按键时相应操作 
int hasSolution();				//判断随机生成的地图是否有解 

/*主函数*/ 
int main()
{
	/*初始化地图(直到保证有解)*/
	while(1)
	{
		initMap();
		if(hasSolution())
		{
			break;	
		} 
	}
	/*在没有通关前进行死循环*/
	while(1)
	{
		printMap();			//打印地图
		if(isWin()==1)		//完成拼图时退出循环 
		{
			printf("success!\n"); 
			break;
		}
		turn();				//接收玩家的移动请求 
		system("cls");		//清空屏幕 
	}
	return 0;
} 

void mapToZero()			//地图清0
{
	int i;
	for(i=0;i<row*col;i++)
	{
		map[i]=0;
	}
}

/*函数实现*/
void initMap()				//初始化地图(随机生成row*col个数字,按位置依次将0到row*col-1填入地图)
{
	int i,j;
	int number;
	srand(time(0));
	mapToZero();
	for(i=0;i<row*col;i++)
	{
		number=rand()%(row*col);
		if(map[number]==0)
		{
			map[number]=i;
		}
		else
		{
			for(j=number;;j++)
			{
				if(j==row*col)
				{
					j=0;
				}
				if(map[j]==0)
				{
					map[j]=i;
					break;
				}
			}
		}
	}
}
 
void printMap()				//打印地图,除了最大值其他的都打印出来
{
	int i;
	for(i=0;i<row*col;i++)
	{
		if(map[i]==row*col-1)
		{
			printf("%2s "," ");
		}
		else 
		{
			printf("%2d ",map[i]+1);
		}
		if(i%col==col-1)
		{
			printf("\n");
		}
	}	
	printf("\n");
}

int isWin()					//判断拼图是否已经完成 
{
	int i;
	for(i=0;i<row*col-1;i++)
	{
		if(map[i]>map[i+1])
		{
			return 0;
		}
	}
	return 1;
}

void turn()					//玩家按键时响应操作 
{
	char c;
	int i,j;	
	c=getch();
	switch(c)
	{
		case 'a':	//左 
		case 'A':
			for(i=0;i<row*col;i++)
			{
				if(map[i]==row*col-1&&i%col!=col-1)
				{
					map[i]=map[i+1];
					map[i+1]=row*col-1;
					break;
				}
			}
			break;
		case 'w':	//上 
		case 'W':
			for(i=0;i<row*col-col;i++)
			{
				if(map[i]==row*col-1)
				{
					map[i]=map[i+col];
					map[i+col]=row*col-1;
					break;
				}
			}
			break;
		case 's':	//下 
		case 'S':
			for(i=col;i<row*col;i++)
			{
				if(map[i]==row*col-1)
				{
					map[i]=map[i-col];
					map[i-col]=row*col-1;
					break;
				}
			}
			break;
		case 'd':	//右 
		case 'D':
			for(i=0;i<row*col;i++)
			{
				if(map[i]==row*col-1&&i%col!=0)
				{
					map[i]=map[i-1];
					map[i-1]=row*col-1;
					break;
				}
			}
			break;
	}
}

int hasSolution()				//判断随机生成的地图是否有解 
{
	int reverse=0;				//逆序数(每个元素(最大的那个数除外)比后面元素大的个数的总和,如123456789,没有数字比后面的大,则逆序数为0;再如153276984,5后面有3 2 4比它小,3后有2,7后有6 4,6后有4,9是最大的数字不算在内,8后有4,故此时逆序数为它们的个数总和是8。逆序数为奇数时无解,为偶数时有解) 
	int i,j;
	for(i=0;i<row*col-1;i++)
	{
		for(j=i+1;j<row*col;j++)
		{
			if(map[i]!=row*col-1&&map[i]>map[j])//不是最大的数且又比后面的数字大,逆序数加1 
			{
				reverse++;
			}
		}
	}
	if(reverse%2==0)							//逆序数为偶数,拼图有解 
	{
		return 1;
	}
	else
	{
		return 0;
	}
} 

代码讲解

我们需要展示1到最大值的一个九宫格,所以先定义一个数组map[9]存储这些数字。
然后,我们需要把九宫格展示出来,于是就需要一个打印地图的函数printMap,其实现原理是,每打印三个数字便打印一个换行符以实现二维视觉。其中,我们不打印最大的那个数字,也就是9,而是遇到它时打印空格,留下一个位置来给玩家移动。
接下来我们要实现数字的移动。当玩家请求往上移时,我们从前面6个中便可寻找有没有数值是9的格子,有的话则将它下面的格子的数字移上去,再将下面的格子填上9;当玩家请求左移时,我们首先看一个二维的图
map[0] map[1] map[2]
map[3] map[4] map[5]
map[6] map[7] map[8]
经过观察可以看到,只有下标除3余2的格子为空格时,地图才不需要移动元素。于是我们便可以对整个地图进行遍历,并加上条件,当那一格的数字是9且它的格子下标不是对3余2时,便把右边的格子的数字赋予它,同时右边的格子变成9。那么玩家请求向下移动和向右移动和上述是有共通的地方的,在此就不多做赘述了,留给读者思考。
那么怎么获取玩家的请求呢,我们都知道scanf函数需要加上一个回车键,这样对于需要一直移动元素的玩家来说十分不方便,所以我们需要用到getch函数,它是一个无参函数,返回值为按键的字符值,例如char ch=getch(),我再从键盘上按下p键,则ch的值为‘p’。接下来就好定义操作啦,跟大部分的移动游戏类似,a左,s下,d右,w上。
我们还需要让程序自动产生一个地图,我们只需要让系统产生9个随机数,然后再跟据这些随机数在地图上按顺序依次填上1到8便可。
游戏完成了,试一下。结果发现有很多种情况根本想破脑子都弄不出来,百度一下后才发现原来存在无解的情况,我们在生成随机地图时需要对其进行有无解判断,没有的话再重新生成,直到有解为止。
好了,这样我们的设计就到此完成了,经过实验,可以正常运行并且保证了有解。

开发总结

经过一轮思考和实践,我又收获了不少新的知识点。二维地图有时候可以用一位数组来存储其元素而且不会增加程序的设计复杂度。对C语言程序函数的理解加深,可以自己定义一些函数来使用。通过getch函数获取用户的按键,其在conio头文件中。有时候需要考虑问题的无解性,这引申到生活中,我们不能盲目地和一个问题死磕,那样无疑在浪费时间和生命,而应该抱着怀疑的态度去面对,对于无解的问题我们要积极探讨其无解的原因和判别的方式,再次遇到时可以很好地面对和处理。
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值