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


#### 新增程式碼
``````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;
}
```
:::