走迷宫(游戏)

先放几个截图:
(开局,走了几步)
在这里插入图片描述
(地图有两部分,这里位于交界口)
在这里插入图片描述
(困难部分)
在这里插入图片描述

目标

  1. 按上下左右键移动,移动时是地图平移
  2. 有自己(用红色的 O 表示)、空地(用空格表示)、墙(用白色的 # 表示)、高级墙(用白色的 K 表示)和炸弹(BOOM,用蓝色的 V 表示)。
  3. 地图大小超级大, 1000 × 1000 1000 \times 1000 1000×1000
  4. 地图随机生成。
  5. 地图分为两部分,左上角的 100 × 100 100 \times 100 100×100 区域是简单部分,出现墙的概率为 4 17 \frac{4}{17} 174,出现高级墙的概率为 4 289 \frac{4}{289} 2894,出现炸弹的概率为 4 40 \frac{4}{40} 404。其余区域为困难部分,出现墙的概率为 5 11 \frac{5}{11} 115,出现高级墙的概率为 5 121 \frac{5}{121} 1215,出现炸弹的概率为 1 125 \frac{1}{125} 1251
  6. 炸弹是用来破墙(或破高级墙)的,走到有墙或高级墙的地方后,如果炸弹足够自动使用(墙消耗 1 1 1 个炸弹,高级墙消耗 2 2 2 个炸弹),否则退回。
  7. 刚开始有 3 3 3 个炸弹,防止一开局就被困住。

实现

一、如何输出地图?

地图是 1000 × 1000 1000 \times 1000 1000×1000 的,显然不可以全部输出,所以我们输出一部分。
我们输出最靠近当前位置的 30 × 60 30 \times 60 30×60 区域,正常情况下自己的位置应该在显示的地图中间。但是上下左右有可能会不够,就需要特殊处理。
另:有颜色的输出使用 SetConsoleTextAttribute(点击查看)。
代码:

int mp[1005][1005];

void out(int x, int y)
{
	//正常情况下,(x,y)应该在中间
	int vx = x - 15, vy = y - 30; //左上角x, y
	if (vx <= 0) vx = 1; //越界。。。
	if (vy <= 0) vy = 1; //再次越界。。
	if (vx > 971) vx = 971; //越界。。。
	if (vy > 941) vy = 941; //再次越界。。
	for (int i = vx; i <= vx+29; i++)
	{
		for (int j = vy; j <= vy+59; j++)
		{
			if (x == i && y == j)
			{
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //红色
				putchar('O');
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			else if (mp[i][j] == 'V')
			{
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE); //蓝色
				putchar('V');
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			else putchar(mp[i][j]);
		}
		putchar('\n');
	}
}

二、如何识别上下左右键?

查ASCII码,发现都是两个值。当然也可以自己写个程序测试。

第一个值第二个值
− 32 -32 32 72 72 72H
− 32 -32 32 80 80 80P
− 32 -32 32 75 75 75K
− 32 -32 32 77 77 77M

没错,第一个数是个负数。

三、如何随机生成地图?

答:随机生成。
答:rand()
确实是 rand(),但是怎么根据概率生成呢?
概率为 a b \frac{a}{b} barand()%b < a。其实很好理解, a b \frac{a}{b} ba 的概率就代表有 b b b 种可能,其中有 a a a 种使条件成立,其余 b − a b-a ba 种不成立,从 b b b 中可能中均匀随机选取一种,判断条件是否成立就好。这里我们将 b b b 种可能编号为 0 , 1 , 2 , 3 , ⋯ b − 1 0, 1, 2, 3, \cdots b-1 0,1,2,3,b1,也就是 rand()%b,使条件成立的 a a a 种编号为 0 , 1 , 2 , 3 , ⋯ a − 1 0, 1, 2, 3, \cdots a-1 0,1,2,3,a1,也就是 编号 < a \text{编号} < a 编号<a 时成立。

四、可以去实现了!

(空)

五、地图刷新太慢怎么办?

通常,刷新地图是先清空,然后从头开始输出。但是其实因为地图大小始终相同,所以我们可以直接将光标移动到开头,用后面的输出覆盖前面的。
移动光标:SetConsoleCursorPosition(同样是点击查看)。
移动光标到开头:SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0, 0});,第一个参数表示句柄(这里是控制台输出句柄),第二个参数表示坐标( ( 0 , 0 ) (0, 0) (0,0),左上角)。

六、最终代码

我绝对不会告诉你最后刷新还是很慢!

#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <Windows.h>
#include <conio.h>

using namespace std;

#define getch _getch

int mp[1005][1005];

void out(int x, int y)
{
	//正常情况下,(x,y)应该在中间
	int vx = x - 15, vy = y - 30; //左上角x, y
	if (vx <= 0) vx = 1; //越界。。。
	if (vy <= 0) vy = 1; //再次越界。。
	if (vx > 971) vx = 971; //越界。。。
	if (vy > 941) vy = 941; //再次越界。。
	for (int i = vx; i <= vx+29; i++)
	{
		for (int j = vy; j <= vy+59; j++)
		{
			if (x == i && y == j)
			{
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
				putchar('O');
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			else if (mp[i][j] == 'V')
			{
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);
				putchar('V');
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			else putchar(mp[i][j]);
		}
		putchar('\n');
	}
}
bool check(int x, int y, int boom)
{
	if (x <= 0 || x > 1000 || y <= 0 || y > 1000) return false;
	if (mp[x][y] == ' ' || mp[x][y] == 'V') return true;
	if (mp[x][y] == '#' && boom >= 1) return true;
	if (mp[x][y] == 'K' && boom >= 2) return true;
	return false;
}
int main()
{
	srand(time(NULL));
	for (int i = 0; i <= 1001; i++)
	{
		for (int j = 0; j <= 1001; j++)
		{
			if (i == 0 || j == 0 || i == 1001 || j == 1001)
			{
				mp[i][j] = '#';
				continue;
			}
			int a = 11, b = 5;
			int c = 125, d = 1;
			if (i <= 100 && j <= 100)
			{
				a = 17; b = 3;
				c = 40; d = 3;
			}
			if (rand() % a < b) mp[i][j] = '#';
			else if (rand() % (a * a) < b) mp[i][j] = 'K';
			else if (rand() % c < d) mp[i][j] = 'V';
			else mp[i][j] = ' ';
		}
	}
	int x = 1, y = 1;
	int boom = 3;
	while (true)
	{
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0, 0});
		out(x, y);
		printf("剩余 %d 个BOOM!\n", boom);
		Sleep(200);
		while (_kbhit()) getch();
		char ch;
		while ((ch = getch()) != -32);
		ch = getch();
		switch (ch)
		{
		case 72: if (check(x-1, y, boom)) x--; break; //上
		case 80: if (check(x+1, y, boom)) x++; break; //下
		case 75: if (check(x, y-1, boom)) y--; break; //左
		case 77: if (check(x, y+1, boom)) y++; break; //右
		}
		if (mp[x][y] == '#')
		{
			boom--;
			mp[x][y] = ' ';
		}
		if (mp[x][y] == 'K')
		{
			boom -= 2;
			mp[x][y] = ' ';
		}
		if (mp[x][y] == 'V')
		{
			boom++;
			mp[x][y] = ' ';
		}
	}
	return 0;
}

(这里要插入效果视频,但是还没过审,过审了就插)
update:好了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值