#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <conio.h>
#include <windows.h>
#include <ctime>
using namespace std;

const int WIDTH = 80;
const int HEIGHT = 30;
const int OBSTACLE_NUM = 18;

struct SnakeNode { int x, y; };
struct Obstacle { int x, y; };

SnakeNode snake[100];
int snakeLen = 3;
int foodX, foodY;
char direction = 'r';
bool gameOver = false;
Obstacle obstacles[OBSTACLE_NUM];

void gotoxy(int x, int y) {
    COORD pos = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

void hideCursor() {
    CONSOLE_CURSOR_INFO cci = {1, FALSE};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}

void initObstacles() {
    for (int i = 0; i < OBSTACLE_NUM; i++) {
        bool invalid;
        do {
            invalid = false;
            obstacles[i].x = (rand() % (WIDTH/2 - 4) + 2) * 2;
            obstacles[i].y = rand() % (HEIGHT - 4) + 2;
            
            for (int j = 0; j < snakeLen; j++) {
                if (obstacles[i].x == snake[j].x && obstacles[i].y == snake[j].y) {
                    invalid = true; break;
                }
            }
            for (int j = 0; j < i; j++) {
                if (obstacles[i].x == obstacles[j].x && obstacles[i].y == obstacles[j].y) {
                    invalid = true; break;
                }
            }
        } while (invalid);
    }
}

void initGame() {
    snake[0] = {6, 5};
    snake[1] = {4, 5};
    snake[2] = {2, 5};
    
    srand((unsigned int)time(NULL));
    initObstacles();
    
    bool invalid;
    do {
        invalid = false;
        foodX = (rand() % (WIDTH/2 - 4) + 2) * 2;
        foodY = rand() % (HEIGHT - 4) + 2;
        
        for (int i = 0; i < snakeLen; i++) {
            if (foodX == snake[i].x && foodY == snake[i].y) { invalid = true; break; }
        }
        if (!invalid) {
            for (int i = 0; i < OBSTACLE_NUM; i++) {
                if (foodX == obstacles[i].x && foodY == obstacles[i].y) { invalid = true; break; }
            }
        }
    } while (invalid);
    
    gameOver = false;
    direction = 'r';
}

void drawMap() {
    gotoxy(0, 0);
    for (int i = 0; i < WIDTH; i++) cout << "#";
    cout << endl;
    
    for (int y = 0; y < HEIGHT; y++) {
        string line = "";
        line += "#";
        
        for (int x = 1; x < WIDTH - 1; x++) {
            bool isDraw = false;
            
            if (y == snake[0].y && x == snake[0].x) {
                line += "O";
                isDraw = true;
            }
            else for (int i = 1; i < snakeLen; i++) {
                if (y == snake[i].y && x == snake[i].x) {
                    line += "o";
                    isDraw = true;
                    break;
                }
            }
            
            if (!isDraw) for (int i = 0; i < OBSTACLE_NUM; i++) {
                if (y == obstacles[i].y && x == obstacles[i].x) {
                    line += "■";
                    isDraw = true;
                    x++; 
                    break;
                }
            }
            
            if (!isDraw && y == foodY && x == foodX) {
                line += "*";
                isDraw = true;
            }
            
            if (!isDraw) {
                line += " ";
            }
        }
        
        while (line.length() < WIDTH - 1) {
            line += " ";
        }
        
        line += "#";
        cout << line << endl;
    }
    
    for (int i = 0; i < WIDTH; i++) cout << "#";
    cout << endl;
    
    cout << "分数: " << snakeLen - 3 << " | W上 S下 A左 D右 | ESC退出 | 障碍:" << OBSTACLE_NUM << endl;
}

void handleInput() {
    if (_kbhit()) {
        switch (_getch()) {
            case 'a': case 'A': if (direction != 'r') direction = 'l'; break;
            case 'd': case 'D': if (direction != 'l') direction = 'r'; break;
            case 'w': case 'W': if (direction != 'd') direction = 'u'; break;
            case 's': case 'S': if (direction != 'u') direction = 'd'; break;
            case 27: gameOver = true; break;
        }
    }
}

void updateGame() {
    int tailX = snake[snakeLen-1].x, tailY = snake[snakeLen-1].y;
    for (int i = snakeLen-1; i > 0; i--) {
        snake[i].x = snake[i-1].x;
        snake[i].y = snake[i-1].y;
    }
    
    switch (direction) {
        case 'l': snake[0].x -= 2; break;
        case 'r': snake[0].x += 2; break;
        case 'u': snake[0].y -= 1; break;
        case 'd': snake[0].y += 1; break;
    }
    
    if (snake[0].x < 2 || snake[0].x >= WIDTH - 2 || 
        snake[0].y < 0 || snake[0].y >= HEIGHT) {
        gameOver = true;
        cout << "撞墙!游戏结束!" << endl;
        return;
    }
    
    for (int i = 0; i < OBSTACLE_NUM; i++) {
        if (snake[0].x == obstacles[i].x && snake[0].y == obstacles[i].y) {
            gameOver = true;
            cout << "撞障碍!游戏结束!" << endl;
            return;
        }
    }
    
    for (int i = 1; i < snakeLen; i++) {
        if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
            gameOver = true;
            cout << "咬自己!游戏结束!" << endl;
            return;
        }
    }
    
    if (snake[0].x == foodX && snake[0].y == foodY) {
        snakeLen++;
        snake[snakeLen-1].x = tailX;
        snake[snakeLen-1].y = tailY;
        
        bool invalid;
        do {
            invalid = false;
            foodX = (rand() % (WIDTH/2 - 4) + 2) * 2;
            foodY = rand() % (HEIGHT - 4) + 2;
            
            for (int i = 0; i < snakeLen; i++) {
                if (foodX == snake[i].x && foodY == snake[i].y) { invalid = true; break; }
            }
            if (!invalid) for (int i = 0; i < OBSTACLE_NUM; i++) {
                if (foodX == obstacles[i].x && foodY == obstacles[i].y) { invalid = true; break; }
            }
        } while (invalid);
    }
}

int main() {
    system("mode con cols=80 lines=30");
    hideCursor();
    
    initGame();
    while (!gameOver) {
        drawMap();
        handleInput();
        updateGame();
        Sleep(200);
    }
    
    gotoxy(0, HEIGHT + 3);
    cout << "游戏结束!最终分数:" << snakeLen - 3 << endl;
    system("pause");
    return 0;
}