// ===== CodeLab: C 语言小游戏④:2048 控制台版 =====
// 来源: https://aoerliang.dpdns.org/articles/game-c-2048
// 以下代码片段按文章出现顺序拼接, 共 7 段

// ----- 片段 1 (c) -----
int board[4][4];   /* 0 表示空格 */
int score = 0;

if (board[i][j] == 0) {        /* 收集空格 */
    empty[n][0] = i; empty[n][1] = j; n++;
}
i = rand() % n;                /* 随机挑一个 */
board[empty[i][0]][empty[i][1]] = (rand() % 10 < 9) ? 2 : 4;

// ----- 片段 2 (c) -----
printf("\n+----+----+----+----+\n");
for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++)
        printf("|%4d", board[i][j]);
    printf("|\n+----+----+----+----+\n");
}

// ----- 片段 3 (c) -----
j = 0;                                 /* 压缩: 数字靠左 */
for (i = 0; i < 4; i++)
    if (row[i] != 0) row[j++] = row[i];
for (; j < 4; j++) row[j] = 0;

for (i = 0; i < 3; i++) {              /* 合并相邻相同 */
    if (row[i] != 0 && row[i] == row[i + 1]) {
        row[i] *= 2;
        score += row[i];
        for (k = i + 1; k < 3; k++) row[k] = row[k + 1];
        row[3] = 0;
    }
}

// ----- 片段 4 (c) -----
for (i = 0; i < 4; i++)               /* 转置: 行列互换 */
    for (j = 0; j < 4; j++)
        tmp[i][j] = board[j][i];

/* 上移 = 转置 → 左移 → 转置; 右移 = 镜像 → 左移 → 镜像 */

// ----- 片段 5 (c) -----
if (board[i][j] == 0) return 0;                        /* 还有空格 */
if (j < 3 && board[i][j] == board[i][j + 1]) return 0;  /* 横向可合并 */
if (i < 3 && board[i][j] == board[i + 1][j]) return 0;  /* 纵向可合并 */

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

int board[4][4];   /* 0 表示空格 */
int score = 0;

/* 随机空格生成 2(90%)或 4(10%) */
void spawn(void) {
    int empty[16][2], n = 0, i, j;
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
            if (board[i][j] == 0) { empty[n][0] = i; empty[n][1] = j; n++; }
    if (n == 0) return;
    i = rand() % n;
    board[empty[i][0]][empty[i][1]] = (rand() % 10 < 9) ? 2 : 4;
}

/* 单行左滑: 压缩 + 合并, 返回是否变化 */
int slide_left_row(int row[4]) {
    int orig[4], i, j, k;
    for (i = 0; i < 4; i++) orig[i] = row[i];
    j = 0;
    for (i = 0; i < 4; i++)
        if (row[i] != 0) row[j++] = row[i];
    for (; j < 4; j++) row[j] = 0;
    for (i = 0; i < 3; i++) {
        if (row[i] != 0 && row[i] == row[i + 1]) {
            row[i] *= 2;
            score += row[i];
            for (k = i + 1; k < 3; k++) row[k] = row[k + 1];
            row[3] = 0;
        }
    }
    for (i = 0; i < 4; i++)
        if (row[i] != orig[i]) return 1;
    return 0;
}

void transpose(void) {
    int tmp[4][4], i, j;
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) tmp[i][j] = board[j][i];
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) board[i][j] = tmp[i][j];
}

void reverse_rows(void) {
    int tmp[4][4], i, j;
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) tmp[i][j] = board[i][3 - j];
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) board[i][j] = tmp[i][j];
}

/* dir: 0左 1右 2上 3下 */
int move(int dir) {
    int changed = 0, i;
    if (dir == 1) reverse_rows();
    if (dir == 2) transpose();
    if (dir == 3) { transpose(); reverse_rows(); }
    for (i = 0; i < 4; i++)
        changed |= slide_left_row(board[i]);
    if (dir == 1) reverse_rows();
    if (dir == 2) transpose();
    if (dir == 3) { reverse_rows(); transpose(); }
    return changed;
}

void print_board(void) {
    int i, j;
    printf("\n+----+----+----+----+\n");
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++)
            printf("|%4d", board[i][j]);
        printf("|\n+----+----+----+----+\n");
    }
    printf("得分: %d\n", score);
}

int has_2048(void) {
    int i, j;
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
            if (board[i][j] >= 2048) return 1;
    return 0;
}

int game_over(void) {
    int i, j;
    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++) {
            if (board[i][j] == 0) return 0;
            if (j < 3 && board[i][j] == board[i][j + 1]) return 0;
            if (i < 3 && board[i][j] == board[i + 1][j]) return 0;
        }
    return 1;
}

int main(void) {
    char cmd;
    int dir;
    srand((unsigned)time(NULL));
    spawn();
    spawn();
    while (1) {
        print_board();
        printf("W/A/S/D 移动, Q 退出: ");
        scanf(" %c", &cmd);
        switch (cmd) {
        case 'a': case 'A': dir = 0; break;
        case 'd': case 'D': dir = 1; break;
        case 'w': case 'W': dir = 2; break;
        case 's': case 'S': dir = 3; break;
        case 'q': case 'Q': return 0;
        default: continue;
        }
        if (move(dir)) spawn();
        if (has_2048()) { print_board(); printf("太棒了! 你合成了 2048!\n"); break; }
        if (game_over()) { print_board(); printf("游戏结束: 没有可移动的方块了。\n"); break; }
    }
    return 0;
}

// ----- 片段 7 (bash) -----
gcc -o game2048 game2048.c
./game2048
