// ===== CodeLab: C 语言小游戏⑤:贪吃蛇(控制台) =====
// 来源: https://aoerliang.dpdns.org/articles/game-c-snake
// 以下代码片段按文章出现顺序拼接, 共 6 段

// ----- 片段 1 (c) -----
#define W 20   /* 游戏区宽度 */
#define H 12   /* 游戏区高度 */

int snake[W * H][2];   /* 蛇身坐标, snake[0] 是蛇头 */
int len;               /* 当前蛇长 */
int dir;               /* 0上 1下 2左 3右 */
int food[2];           /* 食物坐标 */

// ----- 片段 2 (c) -----
printf("\033[2J\033[H");   /* 清屏 */
for (y = 0; y < H; y++) {
    for (x = 0; x < W; x++) {
        char ch = ' ';
        if (x == food[0] && y == food[1]) ch = '*';
        for (i = 0; i < len; i++)
            if (snake[i][0] == x && snake[i][1] == y) {
                ch = (i == 0) ? '@' : '#';
                break;
            }
        putchar(ch);
    }
    putchar('\n');
}

// ----- 片段 3 (c) -----
cmd = getchar();
while (getchar() != '\n');   /* 吃掉回车 */

if (cmd == 'w' || cmd == 'W') { if (dir != 1) dir = 0; }
else if (cmd == 's' || cmd == 'S') { if (dir != 0) dir = 1; }
else if (cmd == 'a' || cmd == 'A') { if (dir != 3) dir = 2; }
else if (cmd == 'd' || cmd == 'D') { if (dir != 2) dir = 3; }
else if (cmd == 'q' || cmd == 'Q') break;

// ----- 片段 4 (c) -----
int tx = snake[len - 1][0], ty = snake[len - 1][1];  /* 记住蛇尾 */

for (i = len - 1; i > 0; i--) {      /* 身体前移 */
    snake[i][0] = snake[i - 1][0];
    snake[i][1] = snake[i - 1][1];
}
snake[0][0] = nx;
snake[0][1] = ny;

if (nx == food[0] && ny == food[1]) { /* 吃到食物 */
    snake[len][0] = tx; snake[len][1] = ty;  /* 尾巴不动 = 变长 */
    len++;
    place_food();
}

// ----- 片段 5 (c) -----
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define W 20   /* 游戏区宽度 */
#define H 12   /* 游戏区高度 */

int snake[W * H][2];   /* 蛇身坐标, snake[0] 是蛇头 */
int len;
int dir;               /* 0上 1下 2左 3右 */
int food[2];

void place_food(void) {
    int x, y, i, ok;
    while (1) {
        x = rand() % W;
        y = rand() % H;
        ok = 1;
        for (i = 0; i < len; i++)
            if (snake[i][0] == x && snake[i][1] == y) { ok = 0; break; }
        if (ok) { food[0] = x; food[1] = y; return; }
    }
}

void print_map(void) {
    int x, y, i;
    printf("\033[2J\033[H");   /* 清屏 */
    for (y = 0; y < H; y++) {
        for (x = 0; x < W; x++) {
            char ch = ' ';
            if (x == food[0] && y == food[1]) ch = '*';
            for (i = 0; i < len; i++)
                if (snake[i][0] == x && snake[i][1] == y) {
                    ch = (i == 0) ? '@' : '#';
                    break;
                }
            putchar(ch);
        }
        putchar('\n');
    }
    printf("长度: %d  分数: %d\n", len, len - 3);
}

int step(void) {
    int nx = snake[0][0], ny = snake[0][1], i;
    int tx = snake[len - 1][0], ty = snake[len - 1][1];

    if (dir == 0) ny--;
    else if (dir == 1) ny++;
    else if (dir == 2) nx--;
    else nx++;

    if (nx < 0 || nx >= W || ny < 0 || ny >= H) return 0;
    for (i = 0; i < len; i++)
        if (snake[i][0] == nx && snake[i][1] == ny) return 0;

    for (i = len - 1; i > 0; i--) {
        snake[i][0] = snake[i - 1][0];
        snake[i][1] = snake[i - 1][1];
    }
    snake[0][0] = nx;
    snake[0][1] = ny;

    if (nx == food[0] && ny == food[1]) {
        snake[len][0] = tx;
        snake[len][1] = ty;
        len++;
        place_food();
    }
    return 1;
}

int main(void) {
    char cmd;
    srand((unsigned)time(NULL));

    len = 3;
    snake[0][0] = 6;  snake[0][1] = H / 2;
    snake[1][0] = 5;  snake[1][1] = H / 2;
    snake[2][0] = 4;  snake[2][1] = H / 2;
    dir = 3;
    place_food();

    printf("贪吃蛇: W/A/S/D 转向, Q 退出。每按一次键, 蛇走一格。\n");
    while (1) {
        print_map();
        printf("输入方向: ");
        cmd = getchar();
        while (getchar() != '\n');

        if (cmd == 'w' || cmd == 'W') { if (dir != 1) dir = 0; }
        else if (cmd == 's' || cmd == 'S') { if (dir != 0) dir = 1; }
        else if (cmd == 'a' || cmd == 'A') { if (dir != 3) dir = 2; }
        else if (cmd == 'd' || cmd == 'D') { if (dir != 2) dir = 3; }
        else if (cmd == 'q' || cmd == 'Q') break;

        if (!step()) {
            print_map();
            printf("游戏结束! 你一共吃了 %d 个食物。\n", len - 3);
            break;
        }
    }
    return 0;
}

// ----- 片段 6 (bash) -----
gcc -o snake snake.c
./snake
