拼图游戏(逆序数曼哈顿距离,c语言实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38192568/article/details/77630747

时间:2017年8月28日

呼,感觉小游戏里拼图(puzzle)游戏算是最简单的几个游戏之一了吧

代码比较简单,因为今天是七夕,所以用了单身狗为主题,学c挺好,单身到老啊!

哈哈,各位码农节日快乐

编程语言:c语言

编译环境:vs,easyx图形库

基本操作:方向键,WASD键控制方向,esc退出游戏

github:点击打开链接

1.找到一张图,用ps分成3*3的小方格,标记为1-9.jpg(方便函数调用),

这里我选取了一张273*300的图片(完整的原图),可以分成9张91*100的图片

另外加了一张91*100的白色图片

准备了一首音乐,前段时间挺火的single dog……微笑

这样放入exe目录下的编辑好的文件夹里,素材就准备好了

2.有这样几个函数,有几个是类似于推箱子游戏的,相信做过推箱子游戏的会比较了解

这里先放一下函数声明

#include<conio.h>
#include<stdio.h>
#include"graphics.h"
#include<time.h>
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")

void GameInit();
void draw();
void game();
int judge();
void menu();

IMAGE img[11];//封面图1张,3*3拼图的9小张,1张空白小图
int p[3][3];//拼图的数组
int gn= 0;//变为空白小图的地方
HWND hWnd;//窗口句柄


重点说一下这里的逆序数和曼哈顿距离运用,

我们用1-9数值代表拼图的图片

百度上关于逆序数的介绍如下

点击打开链接

曼哈顿距离

点击打开链接

总状态数=行数*列数-1+gn到左下角数组的曼哈顿距离(gn是当前空白图)

拼图的当前状态数=逆序数+空白图片到左下角数组(这里是[2][2])的曼哈顿距离

逆序数为0(拼好了);

状态数与总状态数奇偶性一致,才能顺利拼图(排除不能还原拼图的情况)

在3*3拼图里,

总状态数为3*3-1=8,为偶数

所以有

状态数为奇数,不可以拼图;

状态数为偶数,可以拼图,但尚未拼好;

int judge()
{
	int i, j,x=0;
	int s;
	s= 0;
	int manha=0;
	int b[N*N];

	for (i = 0; i < N; i++)
	for (j = 0; j < N; j++)
	{
		if (p[i][j])
			b[x++] = p[i][j];
		else b[x++] = gn;
	}

	for (i = 0; i < (N*N-1);i++)
	for (j = (N*N-1); j>i; j--)
	{
		if (b[i]>b[j])s++;
	}



	int m, n;
	for (i = 0; i < N; i++)
	for (j = 0; j < N; j++)
	{
		if (p[i][j] == 0)
		{
			m = i; n = j; break;
		}
	}
	manha = N-1 - m + N-1 - n;

	if (s==0)
	return 2;
	if ((s + manha) % 2 == (N * N - 1 + 2 - (gn - 1) / N
		+ 2 - gn + ((gn - 1) / N) * N + 1) % 2)
		return 0;
	return 1;

}

根据这个judge函数,最终返回值可以为

0,可以拼图;

1,无法拼图;

2,拼图按拼好的顺序,1-9依次排列,游戏胜利。

GameInit函数里,初始化函数时,结合judge函数打乱了一下拼图

void GameInit()
{
	int i, j, x = 1;
	for (i = 0; i < 3; i++)
	for (j = 0; j < 3; j++)
	{
		p[i][j] = x++;
	}
	srand((unsigned)time(NULL));
	int temp;

	while (gn== 0)gn= rand() % 10;
	p[(gn- 1) / 3][2 - gn % 3] = 0;


	while (judge() != 0)                 //取状态为偶数,而且未还原的状态
	{for (int t = 0; t < 2; t++)
	{
		temp = p[t][t];
		 p[t][t] =p[t][t+1];
		 p[t][t + 1] = temp;
	}
	for (int t = 0; t < 2; t++)
	{
		temp = p[t][t];
		p[t][t] = p[t + 1][t ];
		p[t + 1][t] = temp;
	}
	}
}
while (judge() != 0)                 //取状态为偶数,而且未还原的状态
	{for (int t = 0; t < 2; t++)
	{
		temp = p[t][t];
		 p[t][t] =p[t][t+1];
		 p[t][t + 1] = temp;
	}
	for (int t = 0; t < 2; t++)
	{
		temp = p[t][t];
		p[t][t] = p[t + 1][t ];
		p[t + 1][t] = temp;
	}
	}
}

这里int了gn(gn是全局变量),

用rand()对它进行取值,使gn的值在1-9之间

(使得9张图中一张变为空白,进行游戏)

<span style="color:#330000">p[(gn- 1) / 3][gn-((gn-1)/3+1)*3] = 0;</span>

用这一段使gn数值的方块变为空白图,也就是文件夹里的0.jpg空白图

<span style="color:#330000">(gn- 1) / 3</span>

为gn的行数

 
gn-((gn-1)/3+1)*3

为gn的列数

红色的代码部分有待争议,

因为是我自己临时编的打乱拼图顺序的代码,

有建议更改的欢迎指出

之后是和推箱子游戏等类似的draw画图函数,和利用getch()读取输入

的game()游戏函数

放一段w,和方向键向上控制的代码

void game()
{
	char ch;
	int m, n;
	int i, j;
	for (i = 0; i < 3; i++)
	for (j = 0; j < 3; j++)
	{
		if (p[i][j] == 0)
		{
			m = i; n = j;//保留下0空白位置的数组下标
		}
	}


	ch = getch();
	switch (ch)
	{
	case 'w':
	case 'W':
	case 72:if (m != 2){	
		p[m][n] = p[m + 1][n];
		p[m + 1][n] = 0;}
			break;


其余的A,S,D的情况就有劳大家自行脑补了,哈哈

外加一个开始的menu()函数

做出这样的效果:

放置一张封面图,并给出messagebox,提示是否开始游戏

这里放一下播放音乐的代码

需要嵌入相应的头文件

mciSendString(L"open ./res/单身狗之歌.mp3 alias bgm", 0, 0, 0);
mciSendString(L"play bgm repeat", 0, 0, 0);

主函数里主要的部分为

	menu();

	GameInit();

	while (1)
	{
		draw();
		game();
		if (judge()==2)break;
	}
	hWnd=GetForegroundWindow();
	MessageBoxA(hWnd, "恭喜通关,节日快乐!", "恭喜通关", MB_OK);
	return 0;

之后再拼好图,judge会返回2,循环也就break掉,

游戏结束,会出现messagebox的弹窗,

算是一点节日的小创意吧

以上就是所谓的“单身狗拼图游戏”了,逆序数的想法是之前看的网上的

代码上传到了github

点击打开链接

谢谢你的观看,欢迎评论互相学习,互相进步。

猜你喜欢

转载自blog.csdn.net/qq_38192568/article/details/77630747
今日推荐