![](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; } ``` :::