#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;
}