# ===== CodeLab: C 小游戏⑧:俄罗斯方块(控制台) =====
# 来源: https://aoerliang.dpdns.org/articles/game-c-tetris
# 以下代码片段按文章出现顺序拼接, 共 4 段

# ----- 片段 1 (c) -----
#define COLS 10
#define ROWS 20
int board[ROWS][COLS];

int shapes[7][4][4] = {
    {{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},  // I
    {{1,1},{1,1}},                              // O
    {{0,1,0},{1,1,1},{0,0,0},{0,0,0}},          // T
    {{0,1,1},{1,1,0},{0,0,0},{0,0,0}},          // S
    {{1,1,0},{0,1,1},{0,0,0},{0,0,0}},          // Z
    {{1,0,0},{1,1,1},{0,0,0},{0,0,0}},          // J
    {{0,0,1},{1,1,1},{0,0,0},{0,0,0}},          // L
};

int cur[4][4];   // 当前方块矩阵
int cur_r, cur_c;

# ----- 片段 2 (c) -----
void rotate_clock(int mat[4][4]) {
    int tmp[4][4];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            tmp[j][3 - i] = mat[i][j];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            mat[i][j] = tmp[i][j];
}

int can_place(int mat[4][4], int r, int c) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (mat[i][j]) {
                int rr = r + i, cc = c + j;
                if (rr < 0 || rr >= ROWS || cc < 0 || cc >= COLS || board[rr][cc])
                    return 0;
            }
    return 1;
}

# ----- 片段 3 (c) -----
void lock(void) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (cur[i][j]) board[cur_r + i][cur_c + j] = 1;
}

int clear_lines(void) {
    int cleared = 0;
    for (int r = ROWS - 1; r >= 0; ) {
        int full = 1;
        for (int c = 0; c < COLS; c++)
            if (!board[r][c]) { full = 0; break; }
        if (full) {
            for (int rr = r; rr > 0; rr--)
                for (int c = 0; c < COLS; c++)
                    board[rr][c] = board[rr - 1][c];
            for (int c = 0; c < COLS; c++) board[0][c] = 0;
            cleared++;
        } else r--;
    }
    return cleared;
}

# ----- 片段 4 (c) -----
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define COLS 10
#define ROWS 20

int board[ROWS][COLS];
int shapes[7][4][4] = {
    {{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
    {{1,1},{1,1}},
    {{0,1,0},{1,1,1},{0,0,0},{0,0,0}},
    {{0,1,1},{1,1,0},{0,0,0},{0,0,0}},
    {{1,1,0},{0,1,1},{0,0,0},{0,0,0}},
    {{1,0,0},{1,1,1},{0,0,0},{0,0,0}},
    {{0,0,1},{1,1,1},{0,0,0},{0,0,0}},
};
int cur[4][4];
int cur_r, cur_c;
int score = 0;

void rotate_clock(int mat[4][4]) {
    int tmp[4][4];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            tmp[j][3 - i] = mat[i][j];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            mat[i][j] = tmp[i][j];
}

int can_place(int mat[4][4], int r, int c) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (mat[i][j]) {
                int rr = r + i, cc = c + j;
                if (rr < 0 || rr >= ROWS || cc < 0 || cc >= COLS || board[rr][cc]) return 0;
            }
    return 1;
}

void lock(void) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (cur[i][j]) board[cur_r + i][cur_c + j] = 1;
}

int clear_lines(void) {
    int cleared = 0;
    for (int r = ROWS - 1; r >= 0; ) {
        int full = 1;
        for (int c = 0; c < COLS; c++)
            if (!board[r][c]) { full = 0; break; }
        if (full) {
            for (int rr = r; rr > 0; rr--)
                for (int c = 0; c < COLS; c++)
                    board[rr][c] = board[rr - 1][c];
            for (int c = 0; c < COLS; c++) board[0][c] = 0;
            cleared++;
        } else r--;
    }
    return cleared;
}

void spawn(void) {
    int type = rand() % 7;
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            cur[i][j] = shapes[type][i][j];
    cur_r = 0; cur_c = 3;
}

void draw(void) {
    system("clear");   // Windows 改成 system("cls")
    for (int r = 0; r < ROWS; r++) {
        printf("|");
        for (int c = 0; c < COLS; c++) {
            int shown = board[r][c];
            for (int i = 0; i < 4 && !shown; i++)
                for (int j = 0; j < 4 && !shown; j++)
                    if (cur[i][j] && r == cur_r + i && c == cur_c + j) shown = 1;
            printf("%s", shown ? "[]" : "  ");
        }
        printf("|\n");
    }
    printf("+--------------------+\n分数: %d   a左 d右 s下 w旋转 空格落底 q退出\n", score);
}

int main(void) {
    srand((unsigned)time(NULL));
    spawn();
    char key;
    while (1) {
        draw();
        scanf(" %c", &key);
        if (key == 'q') break;
        if (key == 'a' && can_place(cur, cur_r, cur_c - 1)) cur_c--;
        if (key == 'd' && can_place(cur, cur_r, cur_c + 1)) cur_c++;
        if (key == 's' && can_place(cur, cur_r + 1, cur_c)) cur_r++;
        if (key == 'w') {
            int tmp[4][4];
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++) tmp[i][j] = cur[i][j];
            rotate_clock(tmp);
            if (can_place(tmp, cur_r, cur_c))
                for (int i = 0; i < 4; i++)
                    for (int j = 0; j < 4; j++) cur[i][j] = tmp[i][j];
        }
        if (key == ' ') {
            while (can_place(cur, cur_r + 1, cur_c)) cur_r++;
        }
        if (!can_place(cur, cur_r + 1, cur_c)) {
            lock();
            int n = clear_lines();
            score += (n == 1 ? 100 : n == 2 ? 300 : n == 3 ? 500 : n == 4 ? 800 : 0);
            spawn();
            if (!can_place(cur, cur_r, cur_c)) { printf("游戏结束,分数: %d\n", score); break; }
        }
    }
    return 0;
}
