啊哈c的推箱子游戏(C语言)

啊哈c的推箱子游戏(更新附带失败判断!)

以下是原文,2020.6.28更新了推箱子失败判断,以及优化方向代码还新加了博主的无情嘲讽

因为啊哈c后面推箱子游戏未附代码,而且说实话考虑很多种状况和之前走迷宫的思路还是有区别的。并且我在网上搜的时候没有相似的参考信息,大一这学期快结束了才准备搞这个。一早上的bug ,调试了很多次才好。

说实话,网上信息对新人极不友好,我查推箱子代码,很多csdn博客连界面都写好了,对刚刚学c的我们打击好大啊。总之,我觉得有必要像啊哈c的作者啊哈磊那样写点对新生友好的内容,不然书上一堆枯燥无味的语法真的难读下去。

进入正题。二维数组画出地图,其中s表示人,o表示箱子,*表示目的地,@把箱子推入目的地。如果想换地图,只需改写二维数组和初始位置(x,y)。由于精力有限单纯好玩,所以没写出箱子推入角落等失败条件判断。源代码如下:

经提醒源代码在啊哈c 3.0版本以上运行有错,因为getch()函数头文件conio.h未添加,另外getch()函数可移植性较差,所以还是报错的话就用getchar()替代,每次输入w,a,s,d后再输入回车即可。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
int main()
{
    
    
	system("color f9");//我喜欢的言和蓝(ff9999)
					   //#是墙;s是人;o是箱子;*是目的地;
	char a[12][12]={
    
    "##########",
					"##     ###",
                    "##o###   #",
                    "# s o  o #",
                    "# **# o ##",
                    "##**#   ##",
                    "##########",
    };
    int x=3,y=2,i;//x,y是初始坐标;
    char direction;
	for(i=0;i<7;i++)
		puts(a[i]);

	while(a[4][2]!='@'||a[4][3]!='@'||a[5][2]!='@'||a[5][3]!='@')
    //所有箱子推入结束
    {
    
    
		direction=getch();
		switch(direction)
        {
    
    
        //w s a d, 方向键
			case'w':if(a[x-1][y]!='#')/*给w附注释,下面类似
									  检测前面(就是所对应方向,下同)是不是墙,可以不要但运算会变大*/
						{
    
    
                            if(a[x-1][y]==' ')//空格直接走
                            {
    
    
							a[x][y]=' ';
							x--;
							a[x][y]='s';/*我一开始写成a[x--][y]='s'
										结果不对,其实应该写成--x才能正常运行*/
							}
							else if(a[x-1][y]=='o'&&a[x-2][y]!='#')//如果是箱子,并且前面不是墙
							{
    
    
							if(a[x-2][y]==' ')//箱子前是空格推得动
								{
    
    
								a[x][y]=' ';
								x--;
                                a[x][y]='s';
                                a[x-1][y]='o';
								}
                            else if(a[x-2][y]=='*')//必须有else,否则可能多运行一步。
												   //箱子前是目的地就换成@。
                               {
    
    
							   a[x][y]=' ';
							   x--;
                                a[x][y]='s';
                                a[x-1][y]='@';
								}
                            }
                            else if(a[x-1][y]=='*')//s人想进入目的地
                            	{
    
    
								a[x][y]=' ';//空格不要紧,不会洗掉原有目的地。
											//后面有代码防止目的缺失
								x--;
                            	a[x][y]='s';
								}
                            else if(a[x-1][y]=='@')//想在目的地范围内移箱子
								{
    
    
								if(a[x-2][y]=='*')
									{
    
    
									a[x][y]=' ';
									x--;
                                    a[x][y]='*';
                                    a[x-1][y]='@';
									}
                                else if(a[x-2][y]==' ')
									{
    
    
									a[x][y]=' ';
									x--;
                                    a[x][y]='*';
                                    a[x-1][y]='o';
									}
								}
                        }break;//下面方向键类似

            case's':if(a[x+1][y]!='#')
						{
    
    
                            if(a[x+1][y]==' ')
                            {
    
    
							a[x][y]=' ';
							x++;
							a[x][y]='s';
							}
							else if(a[x+1][y]=='o'&&a[x+2][y]!='#')
							{
    
    
							if(a[x+2][y]==' ')
								{
    
    
								a[x][y]=' ';
								x++;
                                a[x][y]='s';
                                a[x+1][y]='o';
								}
                            else if(a[x+2][y]=='*')
                                {
    
    
							   a[x][y]=' ';
							   x++;
                                a[x][y]='s';
                                a[x+1][y]='@';
								}
                            }
                            else if(a[x+1][y]=='*')
                            {
    
    
							a[x][y]=' ';
							x++;
                            a[x][y]='s';
							}
                            else if(a[x+1][y]=='@')
								{
    
    
								if(a[x+2][y]=='*')
									{
    
    
									a[x][y]=' ';
									x++;
                                    a[x][y]='*';
                                    a[x+1][y]='@';
									}
                                else if(a[x+2][y]==' ')
									{
    
    
									a[x][y]=' ';
									x++;
                                    a[x][y]='*';
                                    a[x+1][y]='o';
									}
								}
                        }break;
            case'a':if(a[x][y-1]!='#')
						{
    
    
                            if(a[x][y-1]==' ')
                            {
    
    
							a[x][y]=' ';
							y--;
							a[x][y]='s';
							}
							else if(a[x][y-1]=='o'&&a[x][y-2]!='#')
							{
    
    
							if(a[x][y-2]==' ')
								{
    
    
								a[x][y]=' ';
								y--;
                                a[x][y]='s';
                                a[x][y-1]='o';
								}
                            else if(a[x][y-2]=='*')
                                {
    
    
							    a[x][y]=' ';
							    y--;
                            	a[x][y]='s';
                                a[x][y-1]='@';
								}
                            }
                            else if(a[x][y-1]=='*')
                            {
    
    
							a[x][y]=' ';
							y--;
                            a[x][y]='s';
							}
                            else if(a[x][y-1]=='@')
								{
    
    
								if(a[x][y-2]=='*')
									{
    
    
									a[x][y]=' ';
									y--;
                                    a[x][y]='*';
                                    a[x][y-1]='@';
									}
                                else if(a[x][y-2]==' ')
									{
    
    
									a[x][y]=' ';
									y--;
                                    a[x][y]='*';
                                    a[x][y-1]='o';
									}
                                }
                        }break;
            case'd':if(a[x][y+1]!='#')
						{
    
    
                            if(a[x][y+1]==' ')
                            	{
    
    
								a[x][y]=' ';
								y++;
								a[x][y]='s';
								}
							else if(a[x][y+1]=='o'&&a[x][y+2]!='#')
							{
    
    
								if(a[x][y+2]==' ')
								{
    
    
								a[x][y]=' ';
								y++;
                                a[x][y]='s';
                                a[x][y+1]='o';
								}
                             	else if(a[x][y+2]=='*')
                                {
    
    
								a[x][y]=' ';
								y++;
                                a[x][y]='s';
                                a[x][y+1]='@';
								}
                            }
                            else if(a[x][y+1]=='*')
                            	{
    
    
								a[x][y]=' ';
								y++;
                           	 	a[x][y]='s';
								}
                            else if(a[x][y+1]=='@')
								{
    
    
								if(a[x][y+2]=='*')
									{
    
    
									a[x][y]=' ';
									y++;
                                    a[x][y]='*';
                                    a[x][y+1]='@';
									}
                                else if(a[x][y+2]==' ')
									{
    
    
									a[x][y]=' ';
                                    y++;
                                    a[x][y]='*';
                                    a[x][y+1]='o';
									}
                                }
                         }break;
        }
        if(a[4][2]!='s'&&a[4][2]!='@')a[4][2]='*';
        if(a[4][3]!='s'&&a[4][3]!='@')a[4][3]='*';
        if(a[5][2]!='s'&&a[5][2]!='@')a[5][2]='*';
        if(a[5][3]!='s'&&a[5][3]!='@')a[5][3]='*';
        /*我觉得我做的最好的一步,检测目的地是不是人或者箱子,
        再加载目的地,克服换位置时目的地*号丢失*/
        system("cls");//清屏
        for(i=0;i<7;i++)
			puts(a[i]);//重新打印
    }
    system("cls");
    printf("you win!\n");
    Sleep(5000);

	return 0;
}

——————————————————假装更新线————————————————
2020.6.28 更新
如果搜到这片文章,说明你还是一个在用啊哈c学c语言的小白,而博主据写这篇文章已经有两年多更新了,快大三了,从小白变成了蒟蒻都不如的菜鸡(连萌新都不敢当,群里巨佬都在装萌新怎么办 )。
在这里插入图片描述
不过都是这么一步一步走过来的,我走了很多弯路,我想对曾经的那个小白说很多,这样我可能现在不会这么急。实在建议计算机小白们一开始就去打acm比赛,那怕最后放弃了也无所谓,只要入门了都能随便切那些一般工作的面试题(比如力扣,笑)。不知道怎么入门的可以看看我的这个回答:acm入门
直接正文吧,两年了,啊哈c2013年出版,到现在2020年了,仍然只有我在第一手更新,说多了都是泪。
上面代码200多行,而且wsad方向键其实功能差不多我硬是整了4的case然后if else套if else,哈哈哈,不愧是小白的我。
如果和我一样从啊哈c只是初学c语言,请听我唠叨这一段。对于相同的功能,比如方向键这种,只有很小的代码差别,如果只是四个方向还能多敲一下搞出来,如果要再来几个不是得重复劳动,这就叫造轮子,就是已有的东西不去用反而还要自己再设计,众所周知轮子已经是圆的了,不可能还有其他形状了(说莱洛三角形的,亲亲这边建议你去清华读数学系呢 ),你不去用还要造个方的吗?所以,把这个东西封装起来。简单在此处来说就是把这个功能变成一个函数去调用,多次调用即可,不用我们一个一个方向去再打一遍相同的内容。
下面就封装了移动函数,xx,yy表示移动距离,比如向右时xx=-1,yy=0;表示x+xx,y+yy,这样就成了原来点的(x-1,y)。有了这个函数你甚至可以定义自己的移动方向,比如用q键表示放大招把箱子斜着推,只需要让xx=1,yy=1即可,就朝右下方向推箱子了,甚至可以用e键调用dir(2,2),你可以斜着把箱子推两格
#是墙;s是人;o是箱子;米字号是目的地,对了,居然有人怀疑我这个推箱子没法过???你自己试试,我用了239步就过了(如果有更短的步数过图,请务必回复我)

void dir(int xx, int yy) {
    
    //从主函数开始读是好习惯(*^▽^*)
	if (a[x +xx][y+yy] != '#')/*给w附注释,下面类似
								  检测前面(就是所对应方向,下同)是不是墙,可以不要但运算会变大*/
	{
    
    
		if (a[x +xx][y+yy] == ' ')//空格直接走
		{
    
    
			a[x][y] = ' ';
			x+=xx;
			y += yy;
			a[x][y] = 's';
		}
		else if (a[x +xx][y+yy] == 'o'&&a[x+xx+xx][y+yy+yy] != '#')//如果是箱子,并且箱子前面不是墙
		{
    
    
			if (a[x+xx+xx][y+yy+yy] == ' ')//箱子前是空格推得动
			{
    
    
				a[x][y] = ' ';//清除原位置的人
				x+=xx;
				y += yy;
				a[x][y] = 's';
				a[x +xx][y+yy] = 'o';
			}
			else if (a[x +xx+xx][y+yy+yy] == '*')//必须有else if,否则可能多运行一步。
								   //箱子前是目的地就换成@。
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = 's';
				a[x +xx][y+yy] = '@';
			}
		}
		else if (a[x+xx][y+yy] == '*')//s人想进入目的地
		{
    
    
			a[x][y] = ' ';//空格不要紧,不会洗掉原有目的地。
						//后面有代码防止目的缺失
			x+=xx;
			y += yy;
			a[x][y] = 's';
		}
		else if (a[x +xx][y+yy] == '@')//想在目的地范围内移箱子
		{
    
    
			if (a[x +xx+xx][y+yy+yy] == '*')
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = '*';
				a[x +xx][y+yy] = '@';
			}
			else if (a[x+xx+xx][y+yy+yy] == ' ')
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = '*';
				a[x +xx][y+yy] = 'o';
			}
		}
	}
}

然后就是判断失败函数,说实话,写的很简单,判断箱子有没有进死角,进死角就说明gme over。
有一个箱子两面相邻贴墙的话肯定没法推了,推箱子不是拉箱子,你难道把他挤出来到目的地吗?(笑)

//类似这种
#########
#      o#
# *     #
#########

代码如下:

bool fail() {
    
    
	int i, j;
	for (i = 0; i < 7; i++) {
    
    
		for (j= 0; j < 10; j++) {
    
    
			if (a[i][j] == 'o') {
    
    //遇到箱子判断一下
				if (a[i + 1][j] == '#' && (a[i][j + 1] == '#' || a[i][j-1] == '#')) {
    
    //下面是墙,如果左右有墙就卡死
					return false;
				}
				if (a[i -1][j] == '#' && (a[i][j + 1] == '#' || a[i][j-1] == '#')) {
    
    //上面是墙,如果左右有墙就卡死
					return false;
				}
			}
		}
	}
	return true;
}

可以看出来非常粗糙,两个for循环判断,时间复杂度O(n2),还好地图小,实际上应该用四个点来记录箱子位置,这样就不用每次都去两个for循环找这个箱子在哪了。不过这样就是代码将变得完全不一样了。而实际上这种判断失败条件太简单了,漏了很多条件,比如下面这种,只有一面贴墙却失败了:

#######
#     #####
##    o   #
#  *   ####
############

可以看出了虽然只有上面贴墙,人在外面的话但是没法从o的右边推箱子,因为右边那三个空格人是进不去的。我百度了下没有代码来专门判断推箱子失败条件的,我一向讨厌造轮子,不过这次搜不出来,就和我写这个推箱子一样,之前从没有人发过思路或者代码。
实际上我的思路很简单,就是先用二维数组记录箱子坐标,通过深度搜索或者广度搜索来判断那些空格可以到达,能到达标1,不能标为0,然后枚举所有箱子是否进“死角”。

下面是精简后代码,当然没有实现上面说的判断方法,因为答主太懒了 。这个版本方向可以随便修改,步数也能修改,你想让这个人走“日”字步(比如一次操作向左走三步,向下走两步)也是可以的,只需要调用函数dir(2,3)即可。
另外添加了失败判断条件,game over.,最后,如果你的步数超过239步以上,还会遭到博主的无情嘲笑,如果你过这个图少于239步请务必私聊或者评论我,答主应该是最少步数了。
代码如下:

#include<conio.h>//getch()函数头文件
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int x = 3, y = 2;//x,y是初始坐标;
char a[12][12] = {
    
     "##########",//存图,注意人不能推动一个以上的箱子
					"##     ###",
					"##o###   #",
					"# s o  o #",
					"# **# o ##",
					"##**#   ##",
					"##########",
};
void dir(int xx, int yy) {
    
    //从主函数开始读是好习惯(*^▽^*)
	if (a[x +xx][y+yy] != '#')/*给w附注释,下面类似
								  检测前面(就是所对应方向,下同)是不是墙,可以不要但运算会变大*/
	{
    
    
		if (a[x +xx][y+yy] == ' ')//空格直接走
		{
    
    
			a[x][y] = ' ';
			x+=xx;
			y += yy;
			a[x][y] = 's';
		}
		else if (a[x +xx][y+yy] == 'o'&&a[x+xx+xx][y+yy+yy] != '#')//如果是箱子,并且箱子前面不是墙
		{
    
    
			if (a[x+xx+xx][y+yy+yy] == ' ')//箱子前是空格推得动
			{
    
    
				a[x][y] = ' ';//清除原位置的人
				x+=xx;
				y += yy;
				a[x][y] = 's';
				a[x +xx][y+yy] = 'o';
			}
			else if (a[x +xx+xx][y+yy+yy] == '*')//必须有else if,否则可能多运行一步。
								   //箱子前是目的地就换成@。
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = 's';
				a[x +xx][y+yy] = '@';
			}
		}
		else if (a[x+xx][y+yy] == '*')//s人想进入目的地
		{
    
    
			a[x][y] = ' ';//空格不要紧,不会洗掉原有目的地。
						//后面有代码防止目的缺失
			x+=xx;
			y += yy;
			a[x][y] = 's';
		}
		else if (a[x +xx][y+yy] == '@')//想在目的地范围内移箱子
		{
    
    
			if (a[x +xx+xx][y+yy+yy] == '*')
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = '*';
				a[x +xx][y+yy] = '@';
			}
			else if (a[x+xx+xx][y+yy+yy] == ' ')
			{
    
    
				a[x][y] = ' ';
				x+=xx;
				y += yy;
				a[x][y] = '*';
				a[x +xx][y+yy] = 'o';
			}
		}
	}
}
bool fail() {
    
    
	int i, j;
	for (i = 0; i < 7; i++) {
    
    
		for (j= 0; j < 10; j++) {
    
    
			if (a[i][j] == 'o') {
    
    //遇到箱子判断一下
				if (a[i + 1][j] == '#' && (a[i][j + 1] == '#' || a[i][j-1] == '#')) {
    
    //下面是墙,如果左右有墙就卡死
					return false;
				}
				if (a[i -1][j] == '#' && (a[i][j + 1] == '#' || a[i][j-1] == '#')) {
    
    //上面是墙,如果左右有墙就卡死
					return false;
				}
			}
		}
	}
	return true;
}
int main()
{
    
    
	system("color f9");//我喜欢的言和蓝(ff9999)
					   //#是墙;s是人;o是箱子;*是目的地;
	int i,cnt=0;//cnt统计步数
	char direction;
	for (i = 0; i < 7; i++)
		puts(a[i]);

	while (a[4][2] != '@' || a[4][3] != '@' || a[5][2] != '@' || a[5][3] != '@')
		//所有箱子推入结束
	{
    
    
		cnt++;//操作一次步数加一次撞墙了也算哦!
		direction = getch();//w s a d, 方向键
							//注意啊哈c下载的编译器支持getch(),如果vs想运行尽量用getchar()函数

		if (direction == 'w'||direction == 'W')//大小写都支持
			dir(-1, 0);//相比之前是不是好了很多,只需要行函数调用即可
		else if (direction == 's' || direction == 'S')dir(1, 0);
		else if (direction == 'a' || direction == 'A')dir(0, -1);
		else if (direction == 'd' || direction == 'D')dir(0, 1);
		
		if (a[4][2] != 's'&&a[4][2] != '@')a[4][2] = '*';
		if (a[4][3] != 's'&&a[4][3] != '@')a[4][3] = '*';
		if (a[5][2] != 's'&&a[5][2] != '@')a[5][2] = '*';
		if (a[5][3] != 's'&&a[5][3] != '@')a[5][3] = '*';
		/*我觉得我做的最好的一步,检测目的地是不是人或者箱子,
		再加载目的地,克服换位置时目的地*号丢失*/
		system("cls");//清屏
		for (i = 0; i < 7; i++)
			puts(a[i]);//重新打印
		if (!fail()) {
    
    //判断这个状态是否失败
			printf("you lose\n");
			for (i = 0; i <= 10; i++)printf("Game over!!!!\n");
			Sleep(5000);
			return 0;
		}

	}
	system("cls");
	if(cnt>239)printf("虽然你赢了但你用了%d步\n博主只用了239步",cnt);
	else printf("Congratulation!you win!\n");
	Sleep(5000);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43305312/article/details/85699758