#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <vector>
#include <conio.h>
#include <utility>
#include <cstring>
#ifdef _WIN32
#include <windows.h>
#define CLEAR_SCREEN "cls"
#define SLEEP_MS(x) Sleep(x)
#else
#define CLEAR_SCREEN "clear"
#define SLEEP_MS(x) usleep(x * 1000)
#endif
using namespace std;

enum Direction { UP, DOWN, LEFT, RIGHT };

struct Bullet {
    int x, y;
    Direction dir;
    bool alive;
    Bullet(int x_, int y_, Direction dir_) : x(x_), y(y_), dir(dir_), alive(true) {}
};

class Tank {
public:
    int x, y;
    Direction dir;
    bool alive;
    char symbol;
    Tank(int x_, int y_, char sym) : x(x_), y(y_), dir(UP), alive(true), symbol(sym) {}
    bool move(Direction newDir, int mapWidth, int mapHeight, const vector<pair<int, int> >& obstacles);
};

const int MAP_WIDTH = 120;
const int MAP_HEIGHT = 26;
const int REFRESH_RATE = 100;
const int DIR[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
const int ENEMY_COUNT = 5;
const int ENEMY_DETECT_RANGE = 8;

Tank player(1, 1, 'P');
vector<Tank> enemies;
vector<Bullet> bullets;
vector<pair<int, int> > obstacles;
bool gameOver = false;
char winner[100] = "";
bool visited[MAP_HEIGHT][MAP_WIDTH];

bool isObstacle(int x, int y) {
    if (x <= 0 || x >= MAP_HEIGHT-1 || y <= 0 || y >= MAP_WIDTH-1)
        return true;
    for (vector<pair<int, int> >::const_iterator it = obstacles.begin(); it != obstacles.end(); ++it) {
        if (it->first == x && it->second == y) {
            return true;
        }
    }
    return false;
}

bool hasTankAt(int x, int y) {
    if (player.alive && player.x == x && player.y == y)
        return true;
    for (size_t i = 0; i < enemies.size(); ++i) {
        if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y)
            return true;
    }
    return false;
}

bool Tank::move(Direction newDir, int mapWidth, int mapHeight, const vector<pair<int, int> >& obstacles) {
    dir = newDir;
    int newX = x, newY = y;
    switch (newDir) {
        case UP:    newX--; break;
        case DOWN:  newX++; break;
        case LEFT:  newY--; break;
        case RIGHT: newY++; break;
    }
    if (newX >= 1 && newX <= mapHeight-2 
     && newY >= 1 && newY <= mapWidth-2 
     && !isObstacle(newX, newY) 
     && !hasTankAt(newX, newY)) {
        x = newX;
        y = newY;
        return true;
    }
    return false;
}

void dfs(int x, int y, int endX, int endY, bool& reachable) {
    if (x == endX && y == endY) {
        reachable = true;
        return;
    }
    if (x < 1 || x >= MAP_HEIGHT-1 || y < 1 || y >= MAP_WIDTH-1 || visited[x][y] || isObstacle(x, y)) {
        return;
    }
    
    visited[x][y] = true;
    for (int i = 0; i < 4 && !reachable; i++) {
        dfs(x + DIR[i][0], y + DIR[i][1], endX, endY, reachable);
    }
}

bool isMapConnected(int playerX, int playerY, const vector<Tank>& enemies) {
    for (size_t i = 0; i < enemies.size(); ++i) {
        if (!enemies[i].alive) continue;
        memset(visited, false, sizeof(visited));
        bool reachable = false;
        dfs(playerX, playerY, enemies[i].x, enemies[i].y, reachable);
        if (!reachable) return false;
    }
    return true;
}

pair<int, int> generateSuperSafePos() {
    int x, y;
    bool safe = false;
    while (!safe) {
        x = rand() % (MAP_HEIGHT - 2) + 1;
        y = rand() % (MAP_WIDTH - 2) + 1;
        if (isObstacle(x, y)) continue;
        
        int freeDir = 0;
        for (int i = 0; i < 4; i++) {
            int nx = x + DIR[i][0];
            int ny = y + DIR[i][1];
            if (nx >= 1 && nx <= MAP_HEIGHT-2 && ny >= 1 && ny <= MAP_WIDTH-2 && !isObstacle(nx, ny)) {
                freeDir++;
            }
        }
        if (freeDir >= 3) {
            safe = true;
        }
    }
    return make_pair(x, y);
}

void generateOpenMap() {
    obstacles.clear();
    bool connected = false;
    
    while (!connected) {
        obstacles.clear();
        const int WALL_SEGMENTS = 25;
        const int MIN_WALL_LEN = 2;
        const int MAX_WALL_LEN = 4;
        
        for (int i = 0; i < WALL_SEGMENTS; i++) {
            Direction dir = (Direction)(rand() % 2);
            int len = MIN_WALL_LEN + rand() % (MAX_WALL_LEN - MIN_WALL_LEN + 1);
            
            int x, y;
            do {
                x = rand() % (MAP_HEIGHT - 2) + 1;
                y = rand() % (MAP_WIDTH - 2) + 1;
            } while (isObstacle(x, y));
            
            for (int j = 0; j < len; j++) {
                int nx = x, ny = y;
                if (dir == 0) ny += j;
                else nx += j;
                
                if (nx < 1 || nx >= MAP_HEIGHT-1 || ny < 1 || ny >= MAP_WIDTH-1 || isObstacle(nx, ny)) {
                    break;
                }
                obstacles.push_back(make_pair(nx, ny));
            }
        }
        
        pair<int, int> playerPos = generateSuperSafePos();
        player.x = playerPos.first;
        player.y = playerPos.second;
        player.alive = true;
        
        enemies.clear();
        for (int i = 0; i < ENEMY_COUNT; ++i) {
            pair<int, int> enemyPos = generateSuperSafePos();
            enemies.push_back(Tank(enemyPos.first, enemyPos.second, 'E'));
        }
        
        connected = isMapConnected(player.x, player.y, enemies);
    }
}

bool hasLineOfSight(int enemyX, int enemyY, int playerX, int playerY) {
    int dx = abs(playerX - enemyX);
    int dy = abs(playerY - enemyY);
    
    if (dx > 0 && dy > 0) {
        return false;
    }
    
    if (dx == 0) {
        int startY = min(enemyY, playerY) + 1;
        int endY = max(enemyY, playerY);
        for (int y = startY; y < endY; y++) {
            if (isObstacle(enemyX, y)) {
                return false;
            }
        }
    }
    else if (dy == 0) {
        int startX = min(enemyX, playerX) + 1;
        int endX = max(enemyX, playerX);
        for (int x = startX; x < endX; x++) {
            if (isObstacle(x, enemyY)) {
                return false;
            }
        }
    }
    return true;
}

bool canAttackPlayer(int enemyX, int enemyY) {
    if (!player.alive) return false;
    
    int dx = abs(player.x - enemyX);
    int dy = abs(player.y - enemyY);
    int distance = dx + dy;
    if (distance > ENEMY_DETECT_RANGE) {
        return false;
    }
    
    return hasLineOfSight(enemyX, enemyY, player.x, player.y);
}

void clearScreen() {
    system(CLEAR_SCREEN);
}

void drawMap() {
    clearScreen();
    
    for (int i = 0; i < MAP_HEIGHT; i++) {
        for (int j = 0; j < MAP_WIDTH; j++) {
            if (i == 0 || i == MAP_HEIGHT-1 || j == 0 || j == MAP_WIDTH-1) {
                cout << "#";
            } else if (isObstacle(i, j)) {
                cout << "X";
            } else if (i == player.x && j == player.y && player.alive) {
                cout << player.symbol;
            } else {
                bool isEnemy = false;
                for (size_t k = 0; k < enemies.size(); ++k) {
                    if (enemies[k].alive && i == enemies[k].x && j == enemies[k].y) {
                        cout << enemies[k].symbol;
                        isEnemy = true;
                        break;
                    }
                }
                if (isEnemy) continue;
                
                bool isBullet = false;
                for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ++it) {
                    if (it->alive && it->x == i && it->y == j) {
                        cout << "*";
                        isBullet = true;
                        break;
                    }
                }
                if (!isBullet) {
                    cout << " ";
                }
            }
        }
        cout << endl;
    }
    
    cout << "===== 超大开阔版坦克对战 =====" << endl;
    cout << "操作:W(上) A(左) S(下) D(右) 空格(发射) Q(退出) R(手动重开)" << endl;
    cout << "玩家:P  敌人:E  子弹:*  边界:#  障碍物:X" << endl;
    
    if (gameOver) {
        cout << "游戏结束!" << winner << "!3秒后自动重新开始..." << endl;
    }
}

void checkCollision() {
    for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ) {
        if (!it->alive) {
            it = bullets.erase(it);
            continue;
        }
        if (it->x == player.x && it->y == player.y && player.alive) {
            player.alive = false;
            gameOver = true;
            strcpy(winner, "玩家被击败,电脑获胜");
            it->alive = false;
        }
        bool hitEnemy = false;
        for (size_t k = 0; k < enemies.size(); ++k) {
            if (enemies[k].alive && it->x == enemies[k].x && it->y == enemies[k].y) {
                enemies[k].alive = false;
                it->alive = false;
                hitEnemy = true;
                break;
            }
        }
        if (hitEnemy) {
            bool allDead = true;
            for (size_t k = 0; k < enemies.size(); ++k) {
                if (enemies[k].alive) { allDead = false; break; }
            }
            if (allDead) {
                gameOver = true;
                strcpy(winner, "所有敌人被消灭,玩家获胜");
            }
            continue;
        }
        if (isObstacle(it->x, it->y) || it->x <= 0 || it->x >= MAP_HEIGHT-1 || it->y <= 0 || it->y >= MAP_WIDTH-1) {
            it->alive = false;
        } else {
            ++it;
        }
    }
}

void moveBullets() {
    for (vector<Bullet>::iterator it = bullets.begin(); it != bullets.end(); ++it) {
        if (!it->alive) continue;
        switch (it->dir) {
            case UP:    it->x--; break;
            case DOWN:  it->x++; break;
            case LEFT:  it->y--; break;
            case RIGHT: it->y++; break;
        }
    }
}

void enemyAI() {
    if (gameOver || !player.alive) return;
    
    for (size_t k = 0; k < enemies.size(); ++k) {
        Tank& enemy = enemies[k];
        if (!enemy.alive) continue;
        
        bool playerInRange = canAttackPlayer(enemy.x, enemy.y);
        
        if (playerInRange) {
            if (player.x < enemy.x) {
                enemy.dir = UP;
            } else if (player.x > enemy.x) {
                enemy.dir = DOWN;
            } else if (player.y < enemy.y) {
                enemy.dir = LEFT;
            } else if (player.y > enemy.y) {
                enemy.dir = RIGHT;
            }
            
            int fireRand = rand() % 5;
            if (fireRand == 0 || fireRand == 1 || fireRand == 2 || fireRand == 3) {
                int bulletX = enemy.x, bulletY = enemy.y;
                switch (enemy.dir) {
                    case UP: bulletX--; break;
                    case DOWN: bulletX++; break;
                    case LEFT: bulletY--; break;
                    case RIGHT: bulletY++; break;
                }
                bullets.push_back(Bullet(bulletX, bulletY, enemy.dir));
            }
            
            int moveRand = rand() % 4;
            if (moveRand == 0) {
                enemy.move(enemy.dir, MAP_WIDTH, MAP_HEIGHT, obstacles);
            }
        }
        else {
            int moveRand = rand() % 4;
            if (moveRand == 0) {
                Direction newDir = (Direction)(rand() % 4);
                enemy.move(newDir, MAP_WIDTH, MAP_HEIGHT, obstacles);
            }
            
            int fireRand = rand() % 30;
            if (fireRand == 0) {
                int bulletX = enemy.x, bulletY = enemy.y;
                switch (enemy.dir) {
                    case UP: bulletX--; break;
                    case DOWN: bulletX++; break;
                    case LEFT: bulletY--; break;
                    case RIGHT: bulletY++; break;
                }
                bullets.push_back(Bullet(bulletX, bulletY, enemy.dir));
            }
        }
    }
}

char getInput() {
#ifdef _WIN32
    if (kbhit()) {
        return getch();
    }
#else
    fd_set rfds;
    struct timeval tv;
    FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    if (select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv) > 0) {
        return getchar();
    }
#endif
    return 0;
}

void handleInput() {
    if (!player.alive || gameOver) return;
    char key = getInput();
    switch (key) {
        case 'w': case 'W': player.move(UP, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
        case 's': case 'S': player.move(DOWN, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
        case 'a': case 'A': player.move(LEFT, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
        case 'd': case 'D': player.move(RIGHT, MAP_WIDTH, MAP_HEIGHT, obstacles); break;
        case ' ': {
            int bulletX = player.x, bulletY = player.y;
            switch (player.dir) {
                case UP: bulletX--; break;
                case DOWN: bulletX++; break;
                case LEFT: bulletY--; break;
                case RIGHT: bulletY++; break;
            }
            bullets.push_back(Bullet(bulletX, bulletY, player.dir));
            break;
        }
        case 'r': case 'R':
            gameOver = true;
            strcpy(winner, "手动重新开始");
            break;
        case 'q': case 'Q':
            cout << "\n游戏退出!" << endl;
            exit(0);
            break;
    }
}

void resetGame() {
    player.alive = true;
    bullets.clear();
    gameOver = false;
    winner[0] = '\0';
    generateOpenMap();
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    srand((unsigned int)time(NULL));
    generateOpenMap();
    
    while (true) {
        while (!gameOver) {
            handleInput();
            enemyAI();
            moveBullets();
            checkCollision();
            drawMap();
            SLEEP_MS(REFRESH_RATE);
        }
        drawMap();
        SLEEP_MS(3000);
        resetGame();
    }
    
    return 0;
}