先放几个截图:
(开局,走了几步)
(地图有两部分,这里位于交界口)
(困难部分)
目标
- 按上下左右键移动,移动时是地图平移。
- 有自己(用红色的
O
表示)、空地(用空格表示)、墙(用白色的#
表示)、高级墙(用白色的K
表示)和炸弹(BOOM,用蓝色的V
表示)。 - 地图大小超级大, 1000 × 1000 1000 \times 1000 1000×1000。
- 地图随机生成。
- 地图分为两部分,左上角的 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。
- 炸弹是用来破墙(或破高级墙)的,走到有墙或高级墙的地方后,如果炸弹足够自动使用(墙消耗 1 1 1 个炸弹,高级墙消耗 2 2 2 个炸弹),否则退回。
- 刚开始有 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
72(H ) |
下 | − 32 -32 −32 |
80
80
80(P ) |
左 | − 32 -32 −32 |
75
75
75(K ) |
右 | − 32 -32 −32 |
77
77
77(M ) |
没错,第一个数是个负数。
三、如何随机生成地图?
答:随机生成。
答:rand()
确实是 rand()
,但是怎么根据概率生成呢?
概率为
a
b
\frac{a}{b}
ba:rand()%b < a
。其实很好理解,
a
b
\frac{a}{b}
ba 的概率就代表有
b
b
b 种可能,其中有
a
a
a 种使条件成立,其余
b
−
a
b-a
b−a 种不成立,从
b
b
b 中可能中均匀随机选取一种,判断条件是否成立就好。这里我们将
b
b
b 种可能编号为
0
,
1
,
2
,
3
,
⋯
b
−
1
0, 1, 2, 3, \cdots b-1
0,1,2,3,⋯b−1,也就是 rand()%b
,使条件成立的
a
a
a 种编号为
0
,
1
,
2
,
3
,
⋯
a
−
1
0, 1, 2, 3, \cdots a-1
0,1,2,3,⋯a−1,也就是
编号
<
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:好了