---
# System prepended metadata

title: 烏克蘭方塊Tetris

---

![](https://hackmd.io/_uploads/S17EDQAUn.jpg)

# 烏克蘭方塊Tetris
## 新增特殊技能與功能
* 隨機開場分數
* 方塊分數
* 消滅方塊獲得分數
* 背景更改
### 隨機開場分數
說明:每次遊戲分數一開始只能從0開始，這次我們直接顛覆傳統，讓每次開場都有小分數，不過也有可能從0分開始，所以一切由開場運氣決定。~~~~畢竟運氣也是實力的一部份~~~~

![](https://hackmd.io/_uploads/BJQg96pIn.png)

![](https://hackmd.io/_uploads/rk4L9aT8h.png)
#### 新增程式碼
``````javascript=
int generateRandomScore() {
    return rand() % 9;  // 在 0 到 9 之間生成一個隨機數作為初始分數
}
``````

 ### 方塊分數
說明:讓每個方塊新增分數，可能是加分、扣分、分數不變，讓整體遊戲提升趣味性! ~~~~也是一切由運氣決定~~~~
 
{%youtube hjSBxc_U01w %}
#### 修改程式碼
```javascript=
// 修改這裡以增加特殊技能得分
bool special = specialConditionMet(state, state->score); // 檢查是否滿足特殊分數的條件
        if (special) {
                state->specialScore += 9;  // 增加特殊得分
            }
        if (special) {
                state->specialScore -= 9;  // 減少特殊得分
            }
        if (special) {
                state->specialScore == 0;  // 得分不變
            }
            state->x = CANVAS_WIDTH / 2; // 將方塊重置回水平中心位置
            state->y = 0; // 將方塊重置到頂部
            state->rotate = 0; // 將方塊的旋轉重置為初始值
            state->fallTime = 0; // 重置下落計時器
            state->queue[0] = state->queue[1]; // 更新方塊種類隊列
            state->queue[1] = state->queue[2];
            state->queue[2] = state->queue[3];
            state->queue[3] = rand() % 7;  // 隨機生成下一個方塊種類

```

### 消滅方塊獲得分數
說明:如同字面上的意思，當方塊每行成功消除時，就會得到分數

{%youtube OgiScxoJ9v0 %}
#### 修改程式碼
```javascript=
int clearLine(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH]) {
    // 將被清除的行標記為未佔據狀態
    for (int i = 0; i < CANVAS_HEIGHT; i++) {
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            if (canvas[i][j].current) {
                canvas[i][j].current = false;
            }
        }
    }
    // 此處將被清除的行的佔據狀態設置為未佔據，即將該行所有方塊的 "current" 屬性設置為 false。
    // 當一行方塊被清除時，需要將該行方塊的佔據狀態重置，以便在後續的處理中可以檢測到該行是否已滿。
    // 迴圈遍歷整個畫布，如果某個方塊的 "current" 屬性為 true，表示該方塊已佔據，將其設置為 false。

    int linesCleared = 0;  // 計算被清除的行數
    int scoreGained = 0;   // 計算獲得的分數

    // 從最底部開始檢查每一行
    for (int i = CANVAS_HEIGHT - 1; i >= 0; i--) {
        bool isFull = true;  // 標記該行是否滿行

        // 檢查該行的每一個方塊是否為空
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            if (canvas[i][j].shape == EMPTY) {
                isFull = false;
                break;
            }
        }

        if (isFull) {
            linesCleared += 1;  // 該行為滿行，增加被清除的行數

            // 將該行上方的所有方塊下移一行
            for (int j = i; j > 0; j--) {
                for (int k = 0; k < CANVAS_WIDTH; k++) {
                    // 將上方方塊的顏色、形狀和佔據狀態賦值給當前方塊
                    setBlock(&canvas[j][k], canvas[j - 1][k].color, canvas[j - 1][k].shape, false);

                    // 重置上方方塊的顏色、形狀和佔據
                    resetBlock(&canvas[j - 1][k]);
                }
            }
            i++;

            // 每次成功消除方塊增加一定的分數
            scoreGained += 100;
        }
    }

    return linesCleared;  // 返回被清除的行數
}
```

 ### 背景更改
 說明:改變整個背景與顏色，形成一個別具一格的風格
 
 {%youtube xddsTTYBbr4 %}
 #### 修改程式碼
 ```javascript=
 void printCanvas(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state)   // 將光標移動到屏幕左上角(ANSI 轉義序列)
{
    printf("\033[0;0H\n");  

    // 打印遊戲畫面
    for (int i = 0; i < CANVAS_HEIGHT; i++) {
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            int color = canvas[i][j].color;
            if (canvas[i][j].current) {
                printf("\033[%dm\u25A0", color);  // 顯示方塊字符和設定顏色
            }
            else {
                printf("\033[%dm\u25A1", color);  // 顯示空格字符和設定顏色
            }
        }
        printf("\033[0m\n");  // 重置顏色為默認值
    }

    Shape shapeData = shapes[state->queue[1]];
    printf("\033[%d;%dHNext:", 3, CANVAS_WIDTH * 2 + 5);  // 將光標移動到顯示下一個方塊的位置
    for (int i = 1; i <= 3; i++)
    {
        shapeData = shapes[state->queue[i]];
        for (int j = 0; j < 4; j++) {
            printf("\033[%d;%dH", i * 4 + j, CANVAS_WIDTH * 2 + 15);  // 將光標移動到預覽區域的每一行開始位置
            for (int k = 0; k < 4; k++) {
                int color = shapeData.color;
                if (j < shapeData.size && k < shapeData.size && shapeData.rotates[0][j][k]) {
                    printf("\033[%dm\u25A0", color);  // 顯示方塊字符和設定顏色
                }
                else {
                    printf("\033[%dm\u25A1", color);  // 顯示空格字符和設定顏色
                }
            }
            printf("\033[0m");  // 重置顏色為初始值
        }
    }

    printf("分數: %d\n", state->score + state->specialScore);  // 打印分數

    return;
}
```
## 完整程式碼
:::spoiler Code

```javascript=
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <windows.h>

#define LEFT_KEY 0x25     // The key to move left, default = 0x25 (left arrow)
#define RIGHT_KEY 0x27    // The key to move right, default = 0x27 (right arrow)
#define ROTATE_KEY 0x26   // The key to rotate, default = 0x26 (up arrow)
#define DOWN_KEY 0x28     // The key to move down, default = 0x28 (down arrow)
#define FALL_KEY 0x20     // The key to fall, default = 0x20 (spacebar)

#define FALL_DELAY 500    // The delay between each fall, default = 500
#define RENDER_DELAY 100  // The delay between each frame, default = 100

#define LEFT_FUNC() GetAsyncKeyState(LEFT_KEY) & 0x8000
#define RIGHT_FUNC() GetAsyncKeyState(RIGHT_KEY) & 0x8000
#define ROTATE_FUNC() GetAsyncKeyState(ROTATE_KEY) & 0x8000
#define DOWN_FUNC() GetAsyncKeyState(DOWN_KEY) & 0x8000
#define FALL_FUNC() GetAsyncKeyState(FALL_KEY) & 0x8000

#define CANVAS_WIDTH 10
#define CANVAS_HEIGHT 20

typedef enum {
    RED = 41,
    GREEN,
    YELLOW,
    BLUE,
    PURPLE,
    CYAN,
    WHITE,
    BLACK = 0,
}Color;

typedef enum {
    EMPTY = -1,
    I,
    J,
    L,
    O,
    S,
    T,
    Z
}ShapeId;

typedef struct {
    ShapeId shape;
    Color color;
    int size;
    char rotates[4][4][4];
}Shape;

typedef struct {
    int x;
    int y;
    int rotate;
    int queue[3];
    int specialScore;  // 特殊技能得分
    int fallTime;     // 下落时间
    int score;        // 得分
} State;



typedef struct {
    Color color;
    ShapeId shape;
    bool current;
}Block;

Shape shapes[7] = {
    {
        .shape = I,
        .color = CYAN,
        .size = 4,
        .rotates =
        {
            {
                {0, 0, 0, 0},
                {1, 1, 1, 1},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
            {
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 1, 0}
            },
            {
                {0, 0, 0, 0},
                {0, 0, 0, 0},
                {1, 1, 1, 1},
                {0, 0, 0, 0}
            },
            {
                {0, 1, 0, 0},
                {0, 1, 0, 0},
                {0, 1, 0, 0},
                {0, 1, 0, 0}
            }
        }
    },
    {
        .shape = J,
        .color = BLUE,
        .size = 3,
        .rotates =
        {
            {
                {1, 0, 0},
                {1, 1, 1},
                {0, 0, 0}
            },
            {
                {0, 1, 1},
                {0, 1, 0},
                {0, 1, 0}
            },
            {
                {0, 0, 0},
                {1, 1, 1},
                {0, 0, 1}
            },
            {
                {0, 1, 0},
                {0, 1, 0},
                {1, 1, 0}
            }
        }
    },
    {
        .shape = L,
        .color = YELLOW,
        .size = 3,
        .rotates =
        {
            {
                {0, 0, 1},
                {1, 1, 1},
                {0, 0, 0}
            },
            {
                {0, 1, 0},
                {0, 1, 0},
                {0, 1, 1}
            },
            {
                {0, 0, 0},
                {1, 1, 1},
                {1, 0, 0}
            },
            {
                {1, 1, 0},
                {0, 1, 0},
                {0, 1, 0}
            }
        }
    },
    {
        .shape = O,
        .color = WHITE,
        .size = 2,
        .rotates =
        {
            {
                {1, 1},
                {1, 1}
            },
            {
                {1, 1},
                {1, 1}
            },
            {
                {1, 1},
                {1, 1}
            },
            {
                {1, 1},
                {1, 1}
            }
        }
    },
    {
        .shape = S,
        .color = GREEN,
        .size = 3,
        .rotates =
        {
            {
                {0, 1, 1},
                {1, 1, 0},
                {0, 0, 0}
            },
            {
                {0, 1, 0},
                {0, 1, 1},
                {0, 0, 1}
            },
            {
                {0, 0, 0},
                {0, 1, 1},
                {1, 1, 0}
            },
            {
                {1, 0, 0},
                {1, 1, 0},
                {0, 1, 0}
            }
        }
    },
    {
        .shape = T,
        .color = PURPLE,
        .size = 3,
        .rotates =
        {
            {
                {0, 1, 0},
                {1, 1, 1},
                {0, 0, 0}
            },

                {{0, 1, 0},
                {0, 1, 1},
                {0, 1, 0}
            },
            {
                {0, 0, 0},
                {1, 1, 1},
                {0, 1, 0}
            },
            {
                {0, 1, 0},
                {1, 1, 0},
                {0, 1, 0}
            }
        }
    },
    {
        .shape = Z,
        .color = RED,
        .size = 3,
        .rotates =
        {
            {
                {1, 1, 0},
                {0, 1, 1},
                {0, 0, 0}
            },
            {
                {0, 0, 1},
                {0, 1, 1},
                {0, 1, 0}
            },
            {
                {0, 0, 0},
                {1, 1, 0},
                {0, 1, 1}
            },
            {
                {0, 1, 0},
                {1, 1, 0},
                {1, 0, 0}
            }
        }
    },
};


void setBlock(Block* block, Color color, ShapeId shape, bool current)
{
    block->color = color;
    block->shape = shape;
    block->current = current;
}


void resetBlock(Block* block)
{
    block->color = BLACK;
    block->shape = EMPTY;
    block->current = false;
}

bool move(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], int originalX, int originalY, int originalRotate, int newX, int newY, int newRotate, ShapeId shapeId) {
    Shape shapeData = shapes[shapeId];
    int size = shapeData.size;

    // check if the new position is valid to place the block
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            if (shapeData.rotates[newRotate][i][j]) {
                if (newX + j < 0 || newX + j >= CANVAS_WIDTH || newY + i < 0 || newY + i >= CANVAS_HEIGHT) {
                    return false;
                }
                if (!canvas[newY + i][newX + j].current && canvas[newY + i][newX + j].shape != EMPTY) {
                    return false;
                }
            }
        }
    }

    // remove the old position
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            if (shapeData.rotates[originalRotate][i][j]) {
                resetBlock(&canvas[originalY + i][originalX + j]);
            }
        }
    }

    // move the block
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            if (shapeData.rotates[newRotate][i][j]) {
                setBlock(&canvas[newY + i][newX + j], shapeData.color, shapeId, true);
            }
        }
    }

    return true;
}


void printCanvas(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state)
{
    printf("\033[0;0H\n");
    for (int i = 0; i < CANVAS_HEIGHT; i++) {
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            int color = canvas[i][j].color;
            if (canvas[i][j].current) {
                printf("\033[%dm\u25A0", color);  // 方块字符和颜色
            }
            else {
                printf("\033[%dm\u25A1", color);  // 空格字符和颜色
            }
        }
        printf("\033[0m\n");  // 重置颜色为默认值
    }

    Shape shapeData = shapes[state->queue[1]];
    printf("\033[%d;%dHNext:", 3, CANVAS_WIDTH * 2 + 5);
    for (int i = 1; i <= 3; i++)
    {
        shapeData = shapes[state->queue[i]];
        for (int j = 0; j < 4; j++) {
            printf("\033[%d;%dH", i * 4 + j, CANVAS_WIDTH * 2 + 15);
            for (int k = 0; k < 4; k++) {
                int color = shapeData.color;
                if (j < shapeData.size && k < shapeData.size && shapeData.rotates[0][j][k]) {
                    printf("\033[%dm\u25A0", color);  // 方块字符和颜色
                }
                else {
                    printf("\033[%dm\u25A1", color);  // 空格字符和颜色
                }
            }
            printf("\033[0m");  // 重置颜色为默认值
        }
    }
    printf("分數: %d\n", state->score + state->specialScore);

    return;
}


int generateRandomScore() {
    return rand() % 9;  // 在 0 到 9 之間生成一個隨機數作為初始分數
}


int clearLine(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH]) {
    for (int i = 0; i < CANVAS_HEIGHT; i++) {
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            if (canvas[i][j].current) {
                canvas[i][j].current = false;
            }
        }
    }

    int linesCleared = 0;
    int scoreGained = 0;  // 新增变量用于记录消除方块所获得的分数

    for (int i = CANVAS_HEIGHT - 1; i >= 0; i--) {
        bool isFull = true;
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            if (canvas[i][j].shape == EMPTY) {
                isFull = false;
                break;
            }
        }

        if (isFull) {
            linesCleared += 1;

            for (int j = i; j > 0; j--) {
                for (int k = 0; k < CANVAS_WIDTH; k++) {
                    setBlock(&canvas[j][k], canvas[j - 1][k].color, canvas[j - 1][k].shape, false);
                    resetBlock(&canvas[j - 1][k]);
                }
            }
            i++;

            // 每次成功消除方块增加一定的分数
            scoreGained += 100;
        }
    }

    return linesCleared;
}


void yourFunctionName(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state) {
    state->score += clearLine(canvas);
}


bool specialConditionMet(State* state, int linesCleared) {
    // 在这里实现你的特殊条件判断逻辑
    // 返回 true 表示条件满足，否则返回 false
    // 这里只是一个示例，你需要根据你的需求自行修改

    if (linesCleared >= 5 && state->score >= 10000) {
        return true;
    }
    return false;
}


void logic(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state)
{
    if (ROTATE_FUNC()) {
        int newRotate = (state->rotate + 1) % 4;
        if (move(canvas, state->x, state->y, state->rotate, state->x, state->y, newRotate, state->queue[0]))
        {
            state->rotate = newRotate;
        }
    }
    else if (LEFT_FUNC()) {
        if (move(canvas, state->x, state->y, state->rotate, state->x - 1, state->y, state->rotate, state->queue[0]))
        {
            state->x -= 1;
        }
    }
    else if (RIGHT_FUNC()) {
        if (move(canvas, state->x, state->y, state->rotate, state->x + 1, state->y, state->rotate, state->queue[0]))
        {
            state->x += 1;
        }
    }
    else if (DOWN_FUNC()) {
        state->fallTime = FALL_DELAY;
    }
    else if (FALL_FUNC()) {
        state->fallTime += FALL_DELAY * CANVAS_HEIGHT;
    }

    state->fallTime += RENDER_DELAY;

    while (state->fallTime >= FALL_DELAY) {
        state->fallTime -= FALL_DELAY;

        if (move(canvas, state->x, state->y, state->rotate, state->x, state->y + 1, state->rotate, state->queue[0])) {
            state->y++;
        }
        else {
            state->score += clearLine(canvas);

            // 修改這裡以增加特殊技能得分
            bool special = specialConditionMet(state, state->score);
            if (special) {
                state->specialScore += 9;  // 增加特殊得分
            }
            if (special) {
                state->specialScore -= 9;  // 減少特殊得分
            }
            if (special) {
                state->specialScore == 0;  // 得分不變
            }

            state->x = CANVAS_WIDTH / 2;
            state->y = 0;
            state->rotate = 0;
            state->fallTime = 0;
            state->queue[0] = state->queue[1];
            state->queue[1] = state->queue[2];
            state->queue[2] = state->queue[3];
            state->queue[3] = rand() % 7;  // 重新生成队列的第四个元素

            if (!move(canvas, state->x, state->y, state->rotate, state->x, state->y, state->rotate, state->queue[0])) {
                printf("\033[%d;%dH\x1b[41m GAME OVER \x1b[0m\033[%d;%dH", CANVAS_HEIGHT - 3, CANVAS_WIDTH * 2 + 5, CANVAS_HEIGHT + 5, 0);
                exit(0);
            }

        }
    }
    return;
}


void clearRows(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state) {
    int rowsCleared = 0;
    for (int y = CANVAS_HEIGHT - 1; y >= 0; y--) {
        bool rowFilled = true;
        for (int x = 0; x < CANVAS_WIDTH; x++) {
            if (canvas[y][x].shape == EMPTY) {
                rowFilled = false;
                break;
            }
        }
        if (rowFilled) {
            rowsCleared++;
            for (int i = y; i > 0; i--) {
                for (int j = 0; j < CANVAS_WIDTH; j++) {
                    canvas[i][j].shape = canvas[i - 1][j].shape;
                    canvas[i][j].color = canvas[i - 1][j].color;
                    canvas[i][j].current = canvas[i - 1][j].current;
                }
            }
            for (int j = 0; j < CANVAS_WIDTH; j++) {
                canvas[0][j].shape = EMPTY;
                canvas[0][j].color = BLACK;
                canvas[0][j].current = false;
            }
            y++;  // Move one step down to recheck the same row
        }
    }

    // Update score based on the number of cleared rows
    switch (rowsCleared) {
    case 1:
        state->score += 100;
        break;
    case 2:
        state->score += 200;
        break;
    case 3:
        state->score += 400;
        break;
    case 4:
        state->score += 800;
        break;
    }
}


void clearCanvas(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH]) {
    for (int i = 0; i < CANVAS_HEIGHT; i++) {
        for (int j = 0; j < CANVAS_WIDTH; j++) {
            resetBlock(&canvas[i][j]);
        }
    }
}


void init(State* state) {
    state->x = CANVAS_WIDTH / 2;
    state->y = 0;
    state->rotate = 0;
    state->specialScore = 0;  // 初始化特殊技能得分为0

    srand((unsigned int)time(NULL));
    for (int i = 0; i < 3; i++) {
        state->queue[i] = rand() % 7;
    }
}



int main()
{
    srand((unsigned int)time(NULL));
    State state = {
        .x = CANVAS_WIDTH / 2,
        .y = 0,
        .rotate = 0,
        .specialScore = 0,
        .score = 0  // 将 score 字段设置为0
    };

    for (int i = 0; i < 4; i++)
    {
        state.queue[i] = rand() % 7;
    }

    Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH];
    for (int i = 0; i < CANVAS_HEIGHT; i++)
    {
        for (int j = 0; j < CANVAS_WIDTH; j++)
        {
            resetBlock(&canvas[i][j]);
        }
    }

    system("cls");

    move(canvas, state.x, state.y, state.rotate, state.x, state.y, state.rotate, state.queue[0]);

    while (1)
    {
        logic(canvas, &state);
        clearRows(canvas, &state);
        printCanvas(canvas, &state);
        Sleep(100);
    }

    clearCanvas(canvas);

    printf("分數: %d\n", state.specialScore);  // 输出特殊分數得分
    printf("分數: %d\n", state.score + state.specialScore);  // 输出累積分数


    return 0;
}
```
:::

