#include <iostream>
#include <vector>
#include <string>
#include <conio.h>
#include <windows.h>
#include <random>
#include <chrono>
#include <algorithm>
#include <thread>
#include <queue>
using namespace std;
// 地图大小
const int WIDTH = 60;
const int HEIGHT = 30;
// 地图元素
const char EMPTY = ' ';
const char WALL = '#';
const char PLAYER = 'P';
const char NPC = 'N';
const char EXIT = 'E';
// 方向
enum Dir { UP, DOWN, LEFT, RIGHT };
// 坐标
struct Point {
int x, y;
Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
bool operator!=(const Point& other) const {
return !(*this == other);
}
};
// 双缓冲
class DoubleBuffer {
private:
HANDLE hConsole;
CHAR_INFO* buffer;
int w, h;
public:
DoubleBuffer(int _w, int _h) : w(_w), h(_h) {
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
buffer = new CHAR_INFO[w * h];
CONSOLE_CURSOR_INFO ci;
GetConsoleCursorInfo(hConsole, &ci);
ci.bVisible = FALSE;
SetConsoleCursorInfo(hConsole, &ci);
}
~DoubleBuffer() { delete[] buffer; }
void clear() {
for (int i = 0; i < w * h; i++) {
buffer[i].Char.AsciiChar = ' ';
buffer[i].Attributes = 7;
}
}
void setChar(int x, int y, char ch, int color = 7) {
if (x >= 0 && x < w && y >= 0 && y < h) {
int idx = y * w + x;
buffer[idx].Char.AsciiChar = ch;
buffer[idx].Attributes = color;
}
}
void render() {
COORD pos = { 0, 0 };
SMALL_RECT rect = { 0, 0, (SHORT)(w - 1), (SHORT)(h - 1) };
WriteConsoleOutputA(hConsole, buffer, { (SHORT)w, (SHORT)h }, pos, &rect);
}
};
// 随机地图生成器 - 带分叉和死路
class MapGenerator {
private:
random_device rd;
mt19937 gen;
int width, height;
vector<string> map;
vector<vector<bool>> visited;
public:
MapGenerator() : gen(rd()) {}
// 检查位置是否可通行(用于BFS)
bool isWalkable(int x, int y) {
if (x <= 0 || x >= width - 1 || y <= 0 || y >= height - 1) return false;
return map[y][x] != WALL;
}
// 获取邻居(用于迷宫生成)
vector<Point> getNeighbors(int x, int y, int step) {
vector<Point> neighbors;
int dx[] = {0, 0, step, -step};
int dy[] = {step, -step, 0, 0};
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1 && !visited[ny][nx]) {
neighbors.push_back(Point(nx, ny));
}
}
return neighbors;
}
// 使用递归回溯生成基础迷宫
void generateBaseMaze() {
// 初始化地图为墙
map.assign(height, string(width, WALL));
visited.assign(height, vector<bool>(width, false));
// 起点
Point start(1, 1);
map[1][1] = EMPTY;
visited[1][1] = true;
vector<Point> stack;
stack.push_back(start);
while (!stack.empty()) {
Point cur = stack.back();
auto neighbors = getNeighbors(cur.x, cur.y, 2);
if (!neighbors.empty()) {
// 随机选择一个邻居
uniform_int_distribution<> dis(0, neighbors.size() - 1);
Point next = neighbors[dis(gen)];
// 打通墙壁
map[(cur.y + next.y) / 2][(cur.x + next.x) / 2] = EMPTY;
map[next.y][next.x] = EMPTY;
visited[next.y][next.x] = true;
stack.push_back(next);
} else {
stack.pop_back();
}
}
}
// 添加分叉和死路
void addBranchesAndDeadEnds() {
uniform_int_distribution<> dis(0, 100);
uniform_int_distribution<> xDis(2, width - 3);
uniform_int_distribution<> yDis(2, height - 3);
// 收集所有空地
vector<Point> emptySpaces;
for (int y = 2; y < height - 2; y++) {
for (int x = 2; x < width - 2; x++) {
if (map[y][x] == EMPTY) {
emptySpaces.push_back(Point(x, y));
}
}
}
// 随机选择一些空地,开辟新的分支
shuffle(emptySpaces.begin(), emptySpaces.end(), gen);
int branchesToAdd = emptySpaces.size() / 8; // 大约12.5%的空地变为分支点
for (int i = 0; i < min(branchesToAdd, (int)emptySpaces.size()); i++) {
Point p = emptySpaces[i];
// 检查周围是否有墙可以打通
vector<int> dirs = {0, 1, 2, 3};
shuffle(dirs.begin(), dirs.end(), gen);
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
for (int d : dirs) {
int nx = p.x + dx[d] * 2;
int ny = p.y + dy[d] * 2;
int mx = p.x + dx[d];
int my = p.y + dy[d];
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1 &&
map[ny][nx] == EMPTY && map[my][mx] == WALL) {
// 打通墙壁,创建分支
map[my][mx] = EMPTY;
break;
}
}
}
// 创建死路 - 在一些分支末端添加死胡同
for (int i = 0; i < branchesToAdd / 2; i++) {
Point p = emptySpaces[dis(gen) % emptySpaces.size()];
// 找到这个点的一个方向,可以挖出一条死路
vector<int> dirs = {0, 1, 2, 3};
shuffle(dirs.begin(), dirs.end(), gen);
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
for (int d : dirs) {
int nx = p.x + dx[d] * 3;
int ny = p.y + dy[d] * 3;
int mx1 = p.x + dx[d];
int my1 = p.y + dy[d];
int mx2 = p.x + dx[d] * 2;
int my2 = p.y + dy[d] * 2;
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1 &&
map[ny][nx] == WALL && map[my2][mx2] == WALL) {
// 挖一条死路
map[my1][mx1] = EMPTY;
map[my2][mx2] = EMPTY;
break;
}
}
}
}
// 创建循环路径(增加多个出口路径)
void addLoops() {
uniform_int_distribution<> dis(0, 100);
// 找一些空地,创建循环
vector<Point> emptySpaces;
for (int y = 2; y < height - 2; y++) {
for (int x = 2; x < width - 2; x++) {
if (map[y][x] == EMPTY) {
emptySpaces.push_back(Point(x, y));
}
}
}
shuffle(emptySpaces.begin(), emptySpaces.end(), gen);
int loopsToAdd = emptySpaces.size() / 10;
for (int i = 0; i < min(loopsToAdd, (int)emptySpaces.size()); i++) {
Point p = emptySpaces[i];
// 检查是否可以创建一个2x2的循环
if (p.x + 1 < width - 1 && p.y + 1 < height - 1) {
if (map[p.y][p.x + 1] == WALL && map[p.y + 1][p.x] == WALL &&
map[p.y + 1][p.x + 1] == EMPTY) {
map[p.y][p.x + 1] = EMPTY;
map[p.y + 1][p.x] = EMPTY;
} else if (map[p.y][p.x + 1] == EMPTY && map[p.y + 1][p.x] == WALL &&
map[p.y + 1][p.x + 1] == WALL) {
map[p.y + 1][p.x] = EMPTY;
map[p.y + 1][p.x + 1] = EMPTY;
}
}
}
}
// BFS检查路径是否存在
bool hasPath(Point start, Point target) {
if (!isWalkable(start.x, start.y) || !isWalkable(target.x, target.y)) {
return false;
}
vector<vector<bool>> visited2(height, vector<bool>(width, false));
queue<Point> q;
q.push(start);
visited2[start.y][start.x] = true;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
while (!q.empty()) {
Point cur = q.front();
q.pop();
if (cur.x == target.x && cur.y == target.y) {
return true;
}
for (int i = 0; i < 4; i++) {
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1 &&
!visited2[ny][nx] && map[ny][nx] != WALL) {
visited2[ny][nx] = true;
q.push(Point(nx, ny));
}
}
}
return false;
}
// BFS找多条路径(用于验证)
int countPaths(Point start, Point target) {
if (!isWalkable(start.x, start.y) || !isWalkable(target.x, target.y)) {
return 0;
}
vector<vector<int>> ways(height, vector<int>(width, 0));
queue<Point> q;
q.push(start);
ways[start.y][start.x] = 1;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int pathCount = 0;
while (!q.empty()) {
Point cur = q.front();
q.pop();
if (cur.x == target.x && cur.y == target.y) {
pathCount += ways[cur.y][cur.x];
continue;
}
for (int i = 0; i < 4; i++) {
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
if (nx > 0 && nx < width - 1 && ny > 0 && ny < height - 1 &&
map[ny][nx] != WALL) {
if (ways[ny][nx] == 0) {
q.push(Point(nx, ny));
}
ways[ny][nx] += ways[cur.y][cur.x];
}
}
}
return pathCount;
}
vector<string> generate(int w, int h) {
width = w;
height = h;
// 生成基础迷宫
generateBaseMaze();
// 添加分叉
addBranchesAndDeadEnds();
// 添加循环
addLoops();
// 确保边界是墙
for (int y = 0; y < height; y++) {
map[y][0] = WALL;
map[y][width - 1] = WALL;
}
for (int x = 0; x < width; x++) {
map[0][x] = WALL;
map[height - 1][x] = WALL;
}
// 开入口和出口
map[1][0] = EMPTY;
map[height - 2][width - 1] = EMPTY;
// 确保入口和出口可通行
Point start(1, 1);
Point exitPos(width - 2, height - 2);
// 如果出口被堵,清空周围
if (map[exitPos.y][exitPos.x] == WALL) {
map[exitPos.y][exitPos.x] = EMPTY;
map[exitPos.y - 1][exitPos.x] = EMPTY;
map[exitPos.y][exitPos.x - 1] = EMPTY;
}
// 验证是否有路径,如果没有,强制开一条
if (!hasPath(start, exitPos)) {
// 简单方案:从起点到终点直线打通
for (int y = start.y; y <= exitPos.y; y++) {
map[y][start.x] = EMPTY;
}
for (int x = start.x; x <= exitPos.x; x++) {
map[exitPos.y][x] = EMPTY;
}
}
return map;
}
// 在安全距离外找空位
Point findSafePosition(const vector<string>& map, Point playerPos, int minDist = 8) {
uniform_int_distribution<> xDis(2, width - 3);
uniform_int_distribution<> yDis(2, height - 3);
vector<Point> candidates;
for (int y = 2; y < height - 2; y++) {
for (int x = 2; x < width - 2; x++) {
if (map[y][x] == EMPTY) {
int dist = abs(x - playerPos.x) + abs(y - playerPos.y);
if (dist >= minDist) {
// 检查这个位置周围是否开阔(避免NPC卡在死路)
int openCount = 0;
if (map[y-1][x] == EMPTY) openCount++;
if (map[y+1][x] == EMPTY) openCount++;
if (map[y][x-1] == EMPTY) openCount++;
if (map[y][x+1] == EMPTY) openCount++;
if (openCount >= 2) {
candidates.push_back(Point(x, y));
}
}
}
}
}
if (candidates.empty()) {
return Point(3, 3);
}
uniform_int_distribution<> idxDis(0, candidates.size() - 1);
return candidates[idxDis(gen)];
}
};
// 游戏主类
class Game {
private:
vector<string> map;
Point player;
vector<Point> npcs;
Point exitPos;
bool gameOver;
bool levelComplete;
int moves;
int score;
DoubleBuffer* display;
MapGenerator generator;
chrono::steady_clock::time_point lastNPCMove;
random_device rd;
mt19937 gen;
const int COLOR_PLAYER = 10;
const int COLOR_NPC = 12;
const int COLOR_WALL = 8;
const int COLOR_EXIT = 14;
const int COLOR_UI = 15;
public:
Game() : gameOver(false), levelComplete(false), moves(0), score(0), gen(rd()) {
display = new DoubleBuffer(WIDTH + 2, HEIGHT + 10);
generateNewMap();
lastNPCMove = chrono::steady_clock::now();
}
~Game() { delete display; }
void generateNewMap() {
// 生成带分叉和死路的迷宫
map = generator.generate(WIDTH, HEIGHT);
// 放置玩家在入口
player = Point(1, 1);
// 放置出口
exitPos = Point(WIDTH - 2, HEIGHT - 2);
map[exitPos.y][exitPos.x] = EXIT;
// 生成NPC - 确保不在玩家附近
npcs.clear();
int npcCount = 6 + gen() % 7; // 6-12个NPC
for (int i = 0; i < npcCount * 3 && (int)npcs.size() < npcCount; i++) {
Point pos = generator.findSafePosition(map, player, 8);
if (pos == exitPos) continue;
bool overlap = false;
for (auto& npc : npcs) {
if (npc == pos) {
overlap = true;
break;
}
}
if (overlap) continue;
npcs.push_back(pos);
}
gameOver = false;
levelComplete = false;
moves = 0;
score++;
}
void draw() {
display->clear();
// UI
string title = "========== PARKOUR ==========";
for (int i = 0; i < (int)title.length(); i++) {
display->setChar(i, 0, title[i], COLOR_UI);
}
string info = "Score: " + to_string(score) + " Moves: " + to_string(moves) +
" NPCs: " + to_string(npcs.size()) + " [Infinite Mode]";
for (int i = 0; i < (int)info.length(); i++) {
display->setChar(i, 1, info[i], COLOR_UI);
}
string controls = "WASD:Move R:Restart Q:Quit Reach E to advance!";
for (int i = 0; i < (int)controls.length(); i++) {
display->setChar(i, 2, controls[i], COLOR_UI);
}
string divider(WIDTH, '=');
for (int i = 0; i < WIDTH; i++) {
display->setChar(i, 3, divider[i], COLOR_UI);
}
// 地图
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int sx = x;
int sy = y + 4;
if (player.x == x && player.y == y) {
display->setChar(sx, sy, PLAYER, COLOR_PLAYER);
} else if (map[y][x] == WALL) {
display->setChar(sx, sy, WALL, COLOR_WALL);
} else if (map[y][x] == EXIT) {
display->setChar(sx, sy, EXIT, COLOR_EXIT);
} else {
bool hasNPC = false;
for (auto& npc : npcs) {
if (npc.x == x && npc.y == y) {
hasNPC = true;
break;
}
}
if (hasNPC) {
display->setChar(sx, sy, NPC, COLOR_NPC);
} else {
display->setChar(sx, sy, EMPTY, 7);
}
}
}
}
// 底部信息
for (int i = 0; i < WIDTH; i++) {
display->setChar(i, HEIGHT + 4, '=', COLOR_UI);
}
if (gameOver) {
string msg = "GAME OVER! Press R to restart, Q to quit Score: " + to_string(score);
for (int i = 0; i < (int)msg.length(); i++) {
display->setChar(i, HEIGHT + 5, msg[i], COLOR_NPC);
}
} else if (levelComplete) {
string msg = "LEVEL COMPLETE! New map generated! Score: " + to_string(score);
for (int i = 0; i < (int)msg.length(); i++) {
display->setChar(i, HEIGHT + 5, msg[i], COLOR_EXIT);
}
} else {
string msg = "Navigate through branches & dead ends! Avoid N, reach E";
for (int i = 0; i < (int)msg.length(); i++) {
display->setChar(i, HEIGHT + 5, msg[i], COLOR_UI);
}
}
display->render();
}
void movePlayer(Dir dir) {
if (gameOver) return;
int dx = 0, dy = 0;
switch(dir) {
case UP: dy = -1; break;
case DOWN: dy = 1; break;
case LEFT: dx = -1; break;
case RIGHT: dx = 1; break;
}
int nx = player.x + dx;
int ny = player.y + dy;
if (nx < 0 || nx >= WIDTH || ny < 0 || ny >= HEIGHT) return;
if (map[ny][nx] == WALL) return;
player.x = nx;
player.y = ny;
moves++;
if (map[ny][nx] == EXIT) {
levelComplete = true;
generateNewMap();
return;
}
for (auto& npc : npcs) {
if (npc.x == player.x && npc.y == player.y) {
gameOver = true;
return;
}
}
}
void updateNPCs() {
if (gameOver || levelComplete) return;
uniform_int_distribution<> dirDis(0, 3);
uniform_int_distribution<> chanceDis(0, 100);
for (int i = 0; i < (int)npcs.size(); i++) {
Point& npc = npcs[i];
int dist = abs(npc.x - player.x) + abs(npc.y - player.y);
bool chase = false;
if (dist < 25) {
int chaseChance = max(20, 85 - dist * 2);
if (chanceDis(gen) < chaseChance) {
chase = true;
}
}
int dx = 0, dy = 0;
if (chase) {
if (npc.x < player.x) dx = 1;
else if (npc.x > player.x) dx = -1;
if (npc.y < player.y) dy = 1;
else if (npc.y > player.y) dy = -1;
if (dx != 0 && dy != 0) {
if (chanceDis(gen) % 2 == 0) dx = 0;
else dy = 0;
}
if (chanceDis(gen) < 8) {
dx = -dx;
dy = -dy;
}
} else {
int dir = dirDis(gen);
if (dir == 0) dy = -1;
else if (dir == 1) dy = 1;
else if (dir == 2) dx = -1;
else dx = 1;
}
int nx = npc.x + dx;
int ny = npc.y + dy;
if (nx > 0 && nx < WIDTH - 1 && ny > 0 && ny < HEIGHT - 1 &&
map[ny][nx] != WALL && map[ny][nx] != EXIT) {
bool occupied = false;
for (int j = 0; j < (int)npcs.size(); j++) {
if (i == j) continue;
if (npcs[j].x == nx && npcs[j].y == ny) {
occupied = true;
break;
}
}
if (!occupied) {
if (nx == player.x && ny == player.y) {
gameOver = true;
return;
}
npc.x = nx;
npc.y = ny;
}
}
}
}
void restart() {
generateNewMap();
lastNPCMove = chrono::steady_clock::now();
}
bool isGameOver() const { return gameOver; }
bool isLevelComplete() const { return levelComplete; }
int getScore() const { return score; }
void run() {
auto lastUpdate = chrono::steady_clock::now();
while (true) {
draw();
if (gameOver) {
if (_kbhit()) {
char key = _getch();
if (key == 'r' || key == 'R') {
restart();
} else if (key == 'q' || key == 'Q') {
break;
}
}
this_thread::sleep_for(chrono::milliseconds(50));
continue;
}
if (_kbhit()) {
char key = _getch();
switch(key) {
case 'w': case 'W': movePlayer(UP); break;
case 's': case 'S': movePlayer(DOWN); break;
case 'a': case 'A': movePlayer(LEFT); break;
case 'd': case 'D': movePlayer(RIGHT); break;
case 'r': case 'R': restart(); break;
case 'q': case 'Q': return;
}
}
auto now = chrono::steady_clock::now();
auto elapsed = chrono::duration_cast<chrono::milliseconds>(now - lastUpdate);
if (elapsed.count() > 130) {
updateNPCs();
lastUpdate = now;
}
this_thread::sleep_for(chrono::milliseconds(10));
}
}
};
int main() {
system("mode con: cols=70 lines=45");
system("title Parkour Game - Complex Maze");
Game game;
game.run();
return 0;
}