---
# System prepended metadata

title: Tetris 烏克蘭方塊

---

# Tetris 烏克蘭方塊
## 修改部分
### 遊戲介面及規則
* 增加遊戲封面以及遊戲結束的畫面，按下空白鍵開始遊戲或離開。
* 遊戲開始倒數畫面。
* 增加顏色顯示。
* 分數機制。

### 功能部分
* 增加方塊投影顯示的功能。
---


## 遊戲介面及規則
### 遊戲封面
```c++=
void printStartScreen()
{
    printf("\033[2J");   // Clear the screen
    printf("\033[0;0H"); // Move the cursor to (0, 0)

    printf("\033[31m___________     __         .__           ________                       \033[0m\n");
    printf("\033[33m\\__    ___/____/  |________|__| ______  /  _____/_____    _____   ____  \033[0m\n");
    printf("\033[32m  |    |_/ __ \\   __\\_  __ \\  |/  ___/ /   \\  ___\\__  \\  /     \\_/ __ \\ \033[0m\n");
    printf("\033[36m  |    |\\  ___/|  |  |  | \\/  |\\___ \\  \\    \\_\\  \\/ __ \\|  Y Y  \\  ___/ \033[0m\n");
    printf("\033[34m  |____| \\___  >__|  |__|  |__/____  >  \\______  (____  /__|_|  /\\___  >\033[0m\n");
    printf("\033[35m             \\/                    \\/          \\/     \\/      \\/     \\/  \033[0m\n");
    printf("\n");
    printf("\033[31mInstructions:\033[0m\n");
    printf("\033[33mUse the arrow keys to move and rotate the blocks.\033[0m\n");
    printf("\033[32mPress the spacebar to make the block fall instantly.\033[0m\n");
    printf("\n");
    printf("\033[37;7mPress any key to start the game...\033[0m\n");

    // Wait for the user to press any key
    system("pause");
}

```
### 結束畫面
```c++=
if (!move(canvas, state->x, state->y, state->rotate, state->x, state->y, state->rotate, state->queue[0])) // Gameover
            {
                printf("\033[2J");   // Clear the screen
                printf("\033[0;0H"); // Move the cursor to (0, 0)
                printf("\033[31m ________                        ________                     \033[0m\n");
                printf("\033[33m/  _____/_____    _____   ____   \\_____  \\___  __ ___________ \033[0m\n");
                printf("\033[32m/   \\  ___\\__  \\  /     \\_/ __ \\   /   |   \\  \\/ // __ \\_  __ \\\033[0m\n");
                printf("\033[36m\\    \\_\\  \\/ __ \\|  Y Y  \\  ___/  /    |    \\   /\\  ___/|  | \\/\033[0m\n");
                printf("\033[34m \\______  (____  /__|_|  /\\___  > \\_______  /\\_/  \\___  >__|   \033[0m\n");
                printf("\033[35m        \\/     \\/      \\/     \\/          \\/          \\/       \033[0m\n");
                printf("\n");
                printf("Your Score: %d\n", state->score);
                printf("Press any key to exit...\n");
                // Wait for the user to press any key
                system("pause");
            }
        
```
### 遊戲開始倒數畫面
```C++=
void Count()
{
    while (1)
    {
        printf("\033[2J"); // Clear the screen
        printf("\033[5;20H");
        printf("Game Started");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[31;7m3\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[33;7m2\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[32;7m1\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("GoGo!!!");
        Sleep(1000);
        system("cls");
        break;
    }
}
```
### 遊戲封面
![](https://hackmd.io/_uploads/B1RGBA2I2.png)

### 結束畫面
![](https://hackmd.io/_uploads/HkzVB0nIh.png)

### 遊戲開始倒數畫面
![](https://hackmd.io/_uploads/rk2O4Y6U2.png)
![](https://hackmd.io/_uploads/H1XcNFTI2.png)
![](https://hackmd.io/_uploads/rJ0cNKp82.png)
![](https://hackmd.io/_uploads/rJHs4KT8h.png)
![](https://hackmd.io/_uploads/BygT4tTI2.png)




### 顏色顯示範例
```c++=
printf("\033[37;7mPress any key to start the game...\033[0m\n");
```


### 分數機制
每次消完一排加10分
```C++=
state->score += clearLine(canvas)*10;
```

---
## 功能部分
## 方塊投影顯示的功能
### 顯示方塊的碰撞點位置。這可以讓玩家更容易預測方塊落地的位置。
```C++=
// Render the predict block
        int newy = state->y + 1;
        while (movea(canvas, state->x, newy, state->rotate, state->x, newy, state->rotate, state->queue[0]))
        {
            newy++;
        }
        shapeData = shapes[state->queue[0]];
        for (int j = 0; j < 4; j++)
        {
            for (int k = 0; k < 4; k++)
            {
                if (j < shapeData.size && k < shapeData.size && shapeData.rotates[state->rotate][j][k])
                {
                    printf("\033[%d;%dH\033[%dm_]\033[0m", newy + j + 1, 2 * (state->x + k) + 2, shapeData.color - 10);
                }
            }
        }

```

### 方塊投影展示畫面
![](https://hackmd.io/_uploads/rJH48K68n.png)
![](https://hackmd.io/_uploads/rJhE8KaI2.png)


---


---
## 完整程式碼
```C++
#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 score;
    int rotate;
    int fallTime;
    ShapeId queue[4];
} 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;
}

bool movea(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;
                }
            }
        }
    }
}

void printCanvas(Block canvas[CANVAS_HEIGHT][CANVAS_WIDTH], State* state)
{
    printf("\033[0;0H\n");
    for (int i = 0; i < CANVAS_HEIGHT; i++)
    {
        printf("|");
        for (int j = 0; j < CANVAS_WIDTH; j++)
        {
            printf("\033[%dm\u3000", canvas[i][j].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++)
            {
                if (j < shapeData.size && k < shapeData.size && shapeData.rotates[0][j][k])
                {
                    printf("\x1b[%dm  ", shapeData.color);
                }
                else
                {
                    printf("\x1b[0m  ");
                }
            }
        }

        // Render the predict block
        int newy = state->y + 1;
        while (movea(canvas, state->x, newy, state->rotate, state->x, newy, state->rotate, state->queue[0]))
        {
            newy++;
        }
        shapeData = shapes[state->queue[0]];
        for (int j = 0; j < 4; j++)
        {
            for (int k = 0; k < 4; k++)
            {
                if (j < shapeData.size && k < shapeData.size && shapeData.rotates[state->rotate][j][k])
                {
                    printf("\033[%d;%dH\033[%dm_]\033[0m", newy + j + 1, 2 * (state->x + k) + 2, shapeData.color - 10);
                }
            }
        }
    }
    printf("\033[%d;%dHScore: %d", 20, CANVAS_WIDTH * 2 + 5, (state->score));

    return;
}

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;

    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++;
        }
    }

    return linesCleared;
}

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);
            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])) // Gameover
            {
                printf("\033[2J");   // Clear the screen
                printf("\033[0;0H"); // Move the cursor to (0, 0)
                printf("\033[31m ________                        ________                     \033[0m\n");
                printf("\033[33m/  _____/_____    _____   ____   \\_____  \\___  __ ___________ \033[0m\n");
                printf("\033[32m/   \\  ___\\__  \\  /     \\_/ __ \\   /   |   \\  \\/ // __ \\_  __ \\\033[0m\n");
                printf("\033[36m\\    \\_\\  \\/ __ \\|  Y Y  \\  ___/  /    |    \\   /\\  ___/|  | \\/\033[0m\n");
                printf("\033[34m \\______  (____  /__|_|  /\\___  > \\_______  /\\_/  \\___  >__|   \033[0m\n");
                printf("\033[35m        \\/     \\/      \\/     \\/          \\/          \\/       \033[0m\n");
                printf("\n");
                printf("\033[37;7mYour Score: %d\033[0m\n", state->score);
                printf("Press any key to exit...\n");
                // Wait for the user to press any key
                system("pause");
                exit(0); // exit game
            }
        }
    }

    return;
}

void printStartScreen()
{
    printf("\033[2J");   // Clear the screen
    printf("\033[0;0H"); // Move the cursor to (0, 0)

    printf("\033[31m___________     __         .__           ________                       \033[0m\n");
    printf("\033[33m\\__    ___/____/  |________|__| ______  /  _____/_____    _____   ____  \033[0m\n");
    printf("\033[32m  |    |_/ __ \\   __\\_  __ \\  |/  ___/ /   \\  ___\\__  \\  /     \\_/ __ \\ \033[0m\n");
    printf("\033[36m  |    |\\  ___/|  |  |  | \\/  |\\___ \\  \\    \\_\\  \\/ __ \\|  Y Y  \\  ___/ \033[0m\n");
    printf("\033[34m  |____| \\___  >__|  |__|  |__/____  >  \\______  (____  /__|_|  /\\___  >\033[0m\n");
    printf("\033[35m             \\/                    \\/          \\/     \\/      \\/     \\/  \033[0m\n");
    printf("\n");
    printf("\033[31mInstructions:\033[0m\n");
    printf("\033[33mUse the arrow keys to move and rotate the blocks.\033[0m\n");
    printf("\033[32mPress the spacebar to make the block fall instantly.\033[0m\n");
    printf("\n");
    printf("\033[37;7mPress any key to start the game...\033[0m\n");

    // Wait for the user to press any key
    system("pause");
}

void Count()
{
    while (1)
    {
        printf("\033[2J"); // Clear the screen
        printf("\033[5;20H");
        printf("Game Started");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[31;7m3\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[33;7m2\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("\033[32;7m1\033[0m");
        Sleep(1000);
        system("cls");
        printf("\033[5;20H");
        printf("GoGo!!!");
        Sleep(1000);
        system("cls");
        break;
    }
}

int main()
{
    srand(time(NULL));
    printStartScreen();
    Count(5);
    State state = {
        .x = CANVAS_WIDTH / 2,
        .y = 0,
        .score = 0,
        .rotate = 0,
        .fallTime = 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");
    // printf("\e[?25l"); // hide cursor

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

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

## 示範影片
[連結](https://youtu.be/5LBISKta5qs)




