c語言——俄羅斯方塊

一、游戲效果


俄羅斯方塊


二. 游戲背景

俄羅斯方塊是久負盛名的游戲,它也和貪吃蛇,掃雷等游戲位列經典游戲的?列。

《俄羅斯方塊》(Tetris,俄文:Тетрис)是一款由俄羅斯人阿列克謝·帕基特諾夫于1984年6月發明的休閑游戲。

該游戲曾經被多家公司代理過。經過多輪訴訟后,該游戲的代理權最終被任天堂獲得。?任天堂對于俄羅斯方塊來說意義重大,因為將它與GB搭配在一起后,獲得了巨大的成功。?

《俄羅斯方塊》的基本規則是移動、旋轉和擺放游戲自動輸出的各種方塊,使之排列成完整的一行或多行并且消除得分。

三、游戲開發日志

基本邏輯結構與貪吃蛇一致?

四、游戲實現

我們有了貪吃蛇的知識儲備及了解WIN32 API,理解下面內容也就不難了。

4.1? 游戲邏輯主體

#include"tetris.h"int main()
{//修改當前地區為本地模式,為了?持中?寬字符的打印setlocale(LC_ALL, "");srand((unsigned int)time(NULL));char ch=0;do{GameInit();GameRun();GameOver();system("cls");SetPos(20, 12);printf("再來一局嗎?(Y/N):\n");SetPos(22, 15);ch = getchar();getchar();//清理\n} while (ch == 'y' || ch == 'Y');SetPos(0, 29);return 0;
}

4.2? 游戲初始化的實現

4.2.1? 設置窗口大小、名稱及隱藏光標

//設置控制臺窗?的??,30?,30列system("mode con cols=60 lines=30");//設置cmd窗?名稱system("title  俄羅斯方塊");//獲取標準輸出的句柄(?來標識不同設備的數值)HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//影藏光標操作CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, &CursorInfo);//獲取控制臺光標信息CursorInfo.bVisible = false; //隱藏控制臺光標SetConsoleCursorInfo(hOutput, &CursorInfo);//設置控制臺光標狀態

4.2.2 打印歡迎界面?

void WelcomeToGame()
{SetPos(20, 10);printf("歡迎來到俄羅斯方塊!");SetPos(20, 23);//讓按任意鍵繼續的出現的位置好看點system("pause");system("cls");SetPos(14, 10);printf("↑為變換.↓為加速.←為左移.→為右移\n");SetPos(20, 23);//讓按任意鍵繼續的出現的位置好看點system("pause");system("cls");
}

?4.2.3? 初始化地圖

void CreateMap()
{memset(IF.pos, 0, sizeof(int)*30*60);//對數組初始化(如果不初始化,FAIL后第二次循環會IF.pos會保留上次數據)int i = 0;//墻體輪廓SetPos(40, 0);for (i = 40; i < 60; i += 2){wprintf(L"%lc", WALL);}SetPos(0,29);for (i = 0; i < 60; i += 2){IF.pos[i][29] = 1;wprintf(L"%lc", WALL);}//x是0,y從1開始增?for (i = 0; i < 30; i++){IF.pos[0][i] = 1;//標記此處有方塊SetPos(0, i);wprintf(L"%c", WALL);}for (i = 0; i < 30; i++){IF.pos[38][i] = 1;SetPos(38, i);wprintf(L"%c", WALL);}for (i = 0; i < 30; i++){IF.pos[58][i] = 1;SetPos(58, i);wprintf(L"%c", WALL);}SetPos(40, 10);for (i = 40; i < 60; i += 2){IF.pos[i][10] = 1;wprintf(L"%lc", WALL);}//提示字體SetPos(40, 1);printf("下一個方塊:");SetPos(44, 12);printf("左移:←");SetPos(44, 14);printf("右移:→");SetPos(44, 16);printf("加速:↓");SetPos(44, 18);printf("變換:↑");SetPos(44, 20);printf("暫停: 空格");SetPos(44, 22);printf("退出: Esc");SetPos(44, 24);printf("重新開始:R");SetPos(44, 26);printf("當前分數:%d", grade);}

注解:?

1)一個寬字符占2個字節,我們設置?cols=60 lines=30,實際就是長寬各30個字節大小

2)如上圖所示,游戲展示區長為20,高為30,右上部分為下一個方塊提示區域,正下面為游戲操作提示區

3)橫軸為x軸,向右坐標逐漸增大,向下為y軸,坐標逐漸增大。(x,y)確定寬字符(小方塊)的位置,因為寬字符(小方塊)占2個字節寬度,所以x軸坐標沒有奇數值

4)為了方便檢測下落方塊是否靠墻、落地及碰到其它方塊,我們定義一個大數組pos[60][30],一一對應地圖上每個寬字符(小方塊)的位置,用大數組pos[60][30]記錄墻體位置及落地的方塊位置

typedef struct InterFace
{int pos[60][30]; //用于標記整個界面有方塊的位置(1為有,0為無)}InterFace;InterFace IF;

4.2.4 初始化方塊信息

void InitBlockInfo()
{//'T'字型int i = 0;for (i = 0; i < 3; i++){block[0][0].space[1][i] = 1;}block[0][0].space[2][1] = 1;//‘L'字型for (i = 1; i < 4; i++){block[1][0].space[i][1] = 1;}block[1][0].space[3][2] = 1;//'J'字形for (i = 1; i < 4; i++){block[2][0].space[i][2] = 1;}block[2][0].space[3][1] = 1;//’Z'字型for (i = 0; i < 2; i++){block[3][0].space[1][i] = 1;}for (i = 1; i < 3; i++){block[3][0].space[2][i] = 1;}//‘S'字型for (i = 1; i < 3; i++){block[4][0].space[1][i] = 1;}for (i = 0; i < 2; i++){block[4][0].space[2][i] = 1;}//'田’字型int j = 0;for (i = 1; i < 3; i++){for (j = 1; j < 3; j++){block[5][0].space[i][j] = 1;}}//長條型for (i = 0; i < 4; i++){block[6][0].space[i][1] = 1;}int m = 0;int n = 0;int tmp[4][4];for (i = 0; i < 7; i++){block[i][0].speed = 200;//初始化下落速度block[i][0].blockstate = NORMAL;}for (i = 0; i < LINES; i++){for (j = 0; j < COLS-1; j++){for (m = 0; m < 4; m++){for (n = 0; n < 4; n++){tmp[m][n] = block[i][j].space[m][n];}}for (m = 0; m < 4; m++){for (n = 0; n < 4; n++){block[i][j + 1].space[n][3-m] = tmp[m][n];}}block[i][j + 1].speed = 200;//初始化下落速度block[i][j + 1].blockstate =NORMAL;//初始化方塊狀態}}}

注解:?

1)我們定義結構體二位數組,用于存儲7種基本形狀方塊的各自的4種形態的信息,共28種。

#define  COLS 4
#define LINES  7
enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
typedef struct Block
{int space[4][4];int speed;enum BlockState blockstate;
}Block; 
Block block[LINES][COLS] ;//結構體二位數組,用于存儲7種基本形狀方塊的各自的4種形態的信息,共28種

?2)首先初始化7種基本形狀方塊,然后基本形狀方塊通過順時針旋轉依次得到各自剩下的三種形態。

4.3? 游戲運行的實現

void GameRun()
{int i = rand() % 7, j = rand() % 4; //隨機獲取方塊的形狀do{int x = 18;int y = 0;int next_i =  rand() % 7, next_j = rand() % 4;PrintBlock(next_i, next_j, 46, 4);//繪制提示框圖形PrintBlock(i, j, 18, 0);//繪制方塊初始位置while(IsLegal(i, j, x, y ))//不合法,說明到底了{if (KEY_PRESS(VK_UP)){block[i][j].blockstate = CHANGE;}else if (KEY_PRESS(VK_DOWN)){block[i][j].blockstate = DOWN;}else if (KEY_PRESS(VK_LEFT)){block[i][j].blockstate = LEFT;//左移}else if (KEY_PRESS(VK_RIGHT)){block[i][j].blockstate = RIGHT;//右移}else if (KEY_PRESS(VK_SPACE)){pause();//暫停}else if (KEY_PRESS(VK_ESCAPE)){state = END;//退出break;}else if (KEY_PRESS(R)){state = RESTART;//重新開始break;}else//什么都沒按{block[i][j].blockstate = NORMAL;}BlockMove(block, &i, &j, &x,&y);PrintBlock(i, j, x, y);//繪制方塊位置Sleep(block[i][j].speed);//每一幀休息的時間}i = next_i;j = next_j;PrintSpace(i,j, 46, 4);//覆蓋提示框圖形} while (state ==RUN);
}

4.3.1? 合法判斷(碰撞檢測)

//碰撞檢測
int IsLegal(int i, int j, int x, int y)
{for (int m = 0; m < 4; m++){for (int n = 0; n< 4; n++){//如果方塊落下的位置本來就已經有方塊了,則不合法if ((block[i][j].space[m][n] == 1) && (IF.pos[x + n * 2][y + m] == 1))return 0; //不合法}}return 1; //合法
}

4.3.2? 覆蓋方塊

void  PrintSpace(int i, int j, int  x, int y)//覆蓋指定位置方塊
{for (int m = 0; m< 4; m++){for (int n = 0; n < 4; n++){if (block[i][j].space[m][n] == 1){SetPos(x+2*n,y+m); //光標跳轉到指定位置printf("  "); //打印空格覆蓋(兩個空格)}}}}

4.3.3? 繪制方塊

void PrintBlock(int i, int j, int x, int y)
{
int m = 0;
int n = 0;
for (m = 0; m < 4; m++)
{for (n = 0; n < 4; n++){if (block[i][j].space[m][n] == 1){//IF.pos[x+n*2][y+m] = 1;//記錄該位置有方塊SetPos(x + n * 2, y + m);wprintf(L"%c", BIOCK);}}
}
}

?4.3.4?? 判斷滿行消除

int JudeIsErase()
{int sign = 0;int i = 0;for (i = 28; i > 3; i--){int count = 0;for (int j = 2; j < 38; j += 2){if (IF.pos[j][i] == 1){count++;}}if (count == 18)//整行全是方塊{sign = 1;grade += 10;SetPos(44, 26);printf("當前分數:%d", grade);//更新當前分數for (int a = 2; a < 38; a += 2){SetPos(a, i);printf("  ");IF.pos[a][i] = 0;}//將該行以上全部下移一格for (int m = i; m > 3; m--){for (int n = 2; n < 38; n += 2){IF.pos[n][m] = IF.pos[n][m - 1]; //將上一行方塊移到下一行if (IF.pos[n][m]==1) //上一行移下來的是方塊,打印方塊{SetPos(n, m); //光標跳轉到該位置wprintf(L"%c", BIOCK); //打印方塊}else //上一行移下來的是空格,打印空格{SetPos(n, m); //光標跳轉到該位置printf("  "); //打印空格(兩個空格)}}}}}return sign;}

注:

1)用大數組pos[60][30],從下往上判斷是否滿行,如果滿行,則將將該行以上全部下移一格,這樣就達到消行的目的

2)若第十四第十三行都滿行了,當檢測到第十四行滿行,將第十三行及以上全部下移一格,再次循環,從現十三行(原十二行)開始檢測,此時會漏掉現十四行(原十三行),所以引進標識變量sign,只要有消行,則變為1,當sign=0時,表示全部方塊沒有滿行了

4.4.5???判斷游戲失敗

void JudeIsOver()
{for (int i = 2; i < 38; i+=2){if (IF.pos[i][1] == 1) //第一行有方塊存在{state = FAIL;}}}

?

4.4.6? 方塊移動

?

void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
{switch (block[*i][*j].blockstate){case CHANGE:if (IsLegal(*i, ((*j) + 1) % 4, *x, (*y )+1) == 1) //判斷方塊變化后(變化的同時下降一格)是否碰撞{//方塊旋轉后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*j) = ((*j) + 1) % 4;(*y)++;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;}//else //方塊再下落就碰撞了(已經到達底部)//{//	//將當前方塊的信息錄入IF當中//	for (int m = 0; m< 4; m++)//	{//		for (int n = 0; n< 4; n++)//		{//			if (block[*i][*j].space[m][n] == 1)//			{//				IF.pos[(*x )+n*2][(*y )+m] = 1; //將該位置標記為有方塊//				//			}//		}//	}//	while (JudeIsErase()); //判斷此次方塊下落是否得分//	JudeIsOver();//}break;case LEFT:if (IsLegal(*i, *j,(*x )-2, *y ) == 1) //判斷方塊左移后是否合法{//方塊左移后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*x) = (*x) - 2;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊}break;case RIGHT:if (IsLegal(*i, *j, (*x) + 2, *y) == 1) //判斷方塊右移后是否合法{//方塊右移后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*x) = (*x) + 2;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊}break;case DOWN:if (IsLegal(*i, *j,* x , (*y) + 1) == 1) //判斷方塊下移后是否合法{//方塊下移后合法才進行以下操作PrintSpace(*i,* j, *x,* y); //用空格覆蓋當前方塊所在位置(*y)++;block[*i][*j].blockstate = NORMAL;if (block[*i][*j].speed>50)//避免連續按,變成負數,會出現bug{block[*i][*j].speed -= 50;}}else //碰撞{//將當前方塊的信息錄入IF當中for (int m = 0; m < 4; m++){for (int n = 0; n < 4; n++){if (block[*i][*j].space[m][n] == 1){IF.pos[(*x) + n * 2][(*y) + m] = 1; //將該位置標記為有方塊}}}while (JudeIsErase()); //判斷此次方塊下落是否得分JudeIsOver();}break;case NORMAL:if (IsLegal(*i, *j,*x, (*y) + 1) == 1) //方塊再下落合法{//方塊右移后合法才進行以下操作PrintSpace(*i, *j, *x,* y); //用空格覆蓋當前方塊所在位置(*y)++;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed=200;}else //碰撞{//將當前方塊的信息錄入IF當中for (int m = 0; m < 4; m++){for (int n = 0; n < 4; n++){if (block[*i][*j].space[m][n] == 1){IF.pos[(*x) + n * 2][(*y) + m] = 1; //將該位置標記為有方塊}}}while (JudeIsErase()); //判斷此次方塊下落是否得分JudeIsOver();}break;}}

注:

1)這里我們根據方塊狀態判斷方塊的下一步位置和形狀,需要改變參數,所以傳的是地址

2)只有在正常下落和加速下落才需要描述不合法時(碰撞時)狀況,因為此時已經到底了。變換方塊時不需要描述不合法(碰撞時)狀況(因為'L'型,在變換時容易與邊界發生碰撞,若此時將方塊位置記錄在pos[60][30]的大數組中,導致跳出該循環,進行下一個方塊判斷,導致該方塊懸浮在半空的BUG)

4.4? ?游戲結束的實現

enum GameState{ RUN = 1,END,FAIL,RESTART };
enum GameState state;void GameOver()
{switch (state){case END:SetPos(14, 15);printf("您主動退出游戲\n");SetPos(14, 18);system("pause");break;case FAIL:Sleep(1000); //留給玩家反應時間system("cls"); //清空屏幕SetPos(20, 10);printf("小垃圾,游戲結束!\n");SetPos(20, 14);printf("你的最終得分為: %d ", grade);SetPos(20, 18);system("pause");break;case RESTART:system("cls"); //清空屏幕SetPos(26, 10);printf("重新開始!\n");SetPos(22, 14);printf("小垃圾,你準備好了嗎?\n");Sleep(2000);main();break;}
}

注:

1)對游戲的三個狀態分別進行描述。

五、完整代碼

5.1?? tetris.h

#include<Windows.h>
#include <locale.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define WALL  L'□'
#define BIOCK  L'■' 
#define  COLS 4
#define LINES  7
#define R 0x52	
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
enum GameState{ RUN = 1,END,FAIL,RESTART };
enum GameState state;enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
int grade;
typedef struct InterFace
{int pos[60][30]; //用于標記整個界面有方塊的位置(1為有,0為無)}InterFace;InterFace IF;
typedef struct Block
{int space[4][4];int speed;enum BlockState blockstate;
}Block; 
Block block[LINES][COLS] ;//結構體二位數組,用于存儲7種基本形狀方塊的各自的4種形態的信息,共28種
void GameInit();
void CreateMap();
void InitBlockInfo();
void GameRun();
void PrintBlock(int i,int j,int x,int y);//ij 為方塊形狀,x,y為打印坐標
void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y);
void  PrintSpace(int i, int j, int  x, int y);
int JudeIsErase();
void JudeIsOver();
void GameOver();

5.2? ?tetris.c

#include"tetris.h"//設置光標的坐標
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//獲取標準輸出的句柄(?來標識不同設備的數值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//設置標準輸出上光標的位置為posSetConsoleCursorPosition(hOutput, pos);}
void WelcomeToGame()
{SetPos(20, 10);printf("歡迎來到俄羅斯方塊!");SetPos(20, 23);//讓按任意鍵繼續的出現的位置好看點system("pause");system("cls");SetPos(14, 10);printf("↑為變換.↓為加速.←為左移.→為右移\n");SetPos(20, 23);//讓按任意鍵繼續的出現的位置好看點system("pause");system("cls");
}
void CreateMap()
{memset(IF.pos, 0, sizeof(int)*30*60);//對數組初始化(如果不初始化,FAIL后第二次循環會IF.pos會保留上次數據)int i = 0;//墻體輪廓SetPos(40, 0);for (i = 40; i < 60; i += 2){wprintf(L"%lc", WALL);}SetPos(0,29);for (i = 0; i < 60; i += 2){IF.pos[i][29] = 1;wprintf(L"%lc", WALL);}//x是0,y從1開始增?for (i = 0; i < 30; i++){IF.pos[0][i] = 1;//標記此處有方塊SetPos(0, i);wprintf(L"%c", WALL);}for (i = 0; i < 30; i++){IF.pos[38][i] = 1;SetPos(38, i);wprintf(L"%c", WALL);}for (i = 0; i < 30; i++){IF.pos[58][i] = 1;SetPos(58, i);wprintf(L"%c", WALL);}SetPos(40, 10);for (i = 40; i < 60; i += 2){IF.pos[i][10] = 1;wprintf(L"%lc", WALL);}//提示字體SetPos(40, 1);printf("下一個方塊:");SetPos(44, 12);printf("左移:←");SetPos(44, 14);printf("右移:→");SetPos(44, 16);printf("加速:↓");SetPos(44, 18);printf("變換:↑");SetPos(44, 20);printf("暫停: 空格");SetPos(44, 22);printf("退出: Esc");SetPos(44, 24);printf("重新開始:R");/*SetPos(44, 26);printf("最高紀錄:%d", max);*/SetPos(44, 26);printf("當前分數:%d", grade);/*getchar();*/}
void InitBlockInfo()
{//'T'字型int i = 0;for (i = 0; i < 3; i++){block[0][0].space[1][i] = 1;}block[0][0].space[2][1] = 1;//‘L'字型for (i = 1; i < 4; i++){block[1][0].space[i][1] = 1;}block[1][0].space[3][2] = 1;//'J'字形for (i = 1; i < 4; i++){block[2][0].space[i][2] = 1;}block[2][0].space[3][1] = 1;//’Z'字型for (i = 0; i < 2; i++){block[3][0].space[1][i] = 1;}for (i = 1; i < 3; i++){block[3][0].space[2][i] = 1;}//‘S'字型for (i = 1; i < 3; i++){block[4][0].space[1][i] = 1;}for (i = 0; i < 2; i++){block[4][0].space[2][i] = 1;}//'田’字型int j = 0;for (i = 1; i < 3; i++){for (j = 1; j < 3; j++){block[5][0].space[i][j] = 1;}}//長條型for (i = 0; i < 4; i++){block[6][0].space[i][1] = 1;}int m = 0;int n = 0;int tmp[4][4];for (i = 0; i < 7; i++){block[i][0].speed = 200;//初始化下落速度block[i][0].blockstate = NORMAL;}for (i = 0; i < LINES; i++){for (j = 0; j < COLS-1; j++){for (m = 0; m < 4; m++){for (n = 0; n < 4; n++){tmp[m][n] = block[i][j].space[m][n];}}for (m = 0; m < 4; m++){for (n = 0; n < 4; n++){block[i][j + 1].space[n][3 - m] = tmp[m][n];}}block[i][j + 1].speed = 200;//初始化下落速度block[i][j + 1].blockstate =NORMAL;//初始化方塊狀態}}}void GameInit()
{//設置控制臺窗?的??,30?,30列system("mode con cols=60 lines=30");//設置cmd窗?名稱system("title  俄羅斯方塊");//獲取標準輸出的句柄(?來標識不同設備的數值)HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//影藏光標操作CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, &CursorInfo);//獲取控制臺光標信息CursorInfo.bVisible = false; //隱藏控制臺光標SetConsoleCursorInfo(hOutput, &CursorInfo);//設置控制臺光標狀態//打印歡迎界?WelcomeToGame();//初始化地圖CreateMap();//初始化方塊信息InitBlockInfo();//初始化游戲狀態state = RUN;
}
void PrintBlock(int i, int j, int x, int y)
{
int m = 0;
int n = 0;
for (m = 0; m < 4; m++)
{for (n = 0; n < 4; n++){if (block[i][j].space[m][n] == 1){//IF.pos[x+n*2][y+m] = 1;//記錄該位置有方塊SetPos(x + n * 2, y + m);wprintf(L"%c", BIOCK);}}
}
}
void pause()//暫停
{while (1){Sleep(300);if (KEY_PRESS(VK_SPACE)){break;}}
}
//碰撞檢測
int IsLegal(int i, int j, int x, int y)
{for (int m = 0; m < 4; m++){for (int n = 0; n< 4; n++){//如果方塊落下的位置本來就已經有方塊了,則不合法if ((block[i][j].space[m][n] == 1) && (IF.pos[x + n * 2][y + m] == 1))return 0; //不合法}}return 1; //合法
}
void  PrintSpace(int i, int j, int  x, int y)//覆蓋指定位置方塊
{for (int m = 0; m< 4; m++){for (int n = 0; n < 4; n++){if (block[i][j].space[m][n] == 1){SetPos(x+2*n,y+m); //光標跳轉到指定位置printf("  "); //打印空格覆蓋(兩個空格)}}}}
int JudeIsErase()
{int sign = 0;int i = 0;for (i = 28; i > 3; i--){int count = 0;for (int j = 2; j < 38; j += 2){if (IF.pos[j][i] == 1){count++;}}if (count == 18)//整行全是方塊{sign = 1;grade += 10;SetPos(44, 26);printf("當前分數:%d", grade);//更新當前分數for (int a = 2; a < 38; a += 2){SetPos(a, i);printf("  ");IF.pos[a][i] = 0;}//將該行以上全部下移一格for (int m = i; m > 3; m--){for (int n = 2; n < 38; n += 2){IF.pos[n][m] = IF.pos[n][m - 1]; //將上一行方塊移到下一行if (IF.pos[n][m]==1) //上一行移下來的是方塊,打印方塊{SetPos(n, m); //光標跳轉到該位置wprintf(L"%c", BIOCK); //打印方塊}else //上一行移下來的是空格,打印空格{SetPos(n, m); //光標跳轉到該位置printf("  "); //打印空格(兩個空格)}}}}}return sign;}
void JudeIsOver()
{for (int i = 2; i < 38; i+=2){if (IF.pos[i][1] == 1) //第一行有方塊存在{state = FAIL;}}}void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
{switch (block[*i][*j].blockstate){case CHANGE:if (IsLegal(*i, ((*j) + 1) % 4, *x, (*y )+1) == 1) //判斷方塊變化后(變化的同時下降一格)是否碰撞{//方塊旋轉后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*j) = ((*j) + 1) % 4;(*y)++;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;}//else //方塊再下落就碰撞了(已經到達底部)//{//	//將當前方塊的信息錄入IF當中//	for (int m = 0; m< 4; m++)//	{//		for (int n = 0; n< 4; n++)//		{//			if (block[*i][*j].space[m][n] == 1)//			{//				IF.pos[(*x )+n*2][(*y )+m] = 1; //將該位置標記為有方塊//				//			}//		}//	}//	while (JudeIsErase()); //判斷此次方塊下落是否得分//	JudeIsOver();//}break;case LEFT:if (IsLegal(*i, *j,(*x )-2, *y ) == 1) //判斷方塊左移后是否合法{//方塊左移后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*x) = (*x) - 2;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊}break;case RIGHT:if (IsLegal(*i, *j, (*x) + 2, *y) == 1) //判斷方塊右移后是否合法{//方塊右移后合法才進行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆蓋當前方塊所在位置(*x) = (*x) + 2;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed = 200;//PrintBlock(i, j, x, y);//繪制下一個變化的方塊}break;case DOWN:if (IsLegal(*i, *j,* x , (*y) + 1) == 1) //判斷方塊下移后是否合法{//方塊下移后合法才進行以下操作PrintSpace(*i,* j, *x,* y); //用空格覆蓋當前方塊所在位置(*y)++;block[*i][*j].blockstate = NORMAL;if (block[*i][*j].speed>50)//避免連續按,變成負數,會出現bug{block[*i][*j].speed -= 50;}}else //碰撞{//將當前方塊的信息錄入IF當中for (int m = 0; m < 4; m++){for (int n = 0; n < 4; n++){if (block[*i][*j].space[m][n] == 1){IF.pos[(*x) + n * 2][(*y) + m] = 1; //將該位置標記為有方塊}}}while (JudeIsErase()); //判斷此次方塊下落是否得分JudeIsOver();}break;case NORMAL:if (IsLegal(*i, *j,*x, (*y) + 1) == 1) //方塊再下落合法{//方塊右移后合法才進行以下操作PrintSpace(*i, *j, *x,* y); //用空格覆蓋當前方塊所在位置(*y)++;block[*i][*j].blockstate = NORMAL;block[*i][*j].speed=200;}else //碰撞{//將當前方塊的信息錄入IF當中for (int m = 0; m < 4; m++){for (int n = 0; n < 4; n++){if (block[*i][*j].space[m][n] == 1){IF.pos[(*x) + n * 2][(*y) + m] = 1; //將該位置標記為有方塊}}}while (JudeIsErase()); //判斷此次方塊下落是否得分JudeIsOver();}break;}}void GameRun()
{int i = rand() % 7, j = rand() % 4; //隨機獲取方塊的形狀do{int x = 18;int y = 0;int next_i =  rand() % 7, next_j = rand() % 4;PrintBlock(next_i, next_j, 46, 4);//繪制提示框圖形PrintBlock(i, j, 18, 0);//繪制方塊初始位置while(IsLegal(i, j, x, y ))//不合法,說明到底了{if (KEY_PRESS(VK_UP)){block[i][j].blockstate = CHANGE;}else if (KEY_PRESS(VK_DOWN)){block[i][j].blockstate = DOWN;}else if (KEY_PRESS(VK_LEFT)){block[i][j].blockstate = LEFT;//左移}else if (KEY_PRESS(VK_RIGHT)){block[i][j].blockstate = RIGHT;//右移}else if (KEY_PRESS(VK_SPACE)){pause();//暫停}else if (KEY_PRESS(VK_ESCAPE)){state = END;//退出break;}else if (KEY_PRESS(R)){state = RESTART;//重新開始break;}else//什么都沒按{block[i][j].blockstate = NORMAL;}BlockMove(block, &i, &j, &x,&y);PrintBlock(i, j, x, y);//繪制方塊位置Sleep(block[i][j].speed);//每一幀休息的時間}i = next_i;j = next_j;PrintSpace(i,j, 46, 4);//覆蓋提示框圖形} while (state ==RUN);
}
void GameOver()
{switch (state){case END:SetPos(14, 15);printf("您主動退出游戲\n");SetPos(14, 18);system("pause");break;case FAIL:Sleep(1000); //留給玩家反應時間system("cls"); //清空屏幕SetPos(20, 10);printf("小垃圾,游戲結束!\n");SetPos(20, 14);printf("你的最終得分為: %d ", grade);SetPos(20, 18);system("pause");break;case RESTART:system("cls"); //清空屏幕SetPos(26, 10);printf("重新開始!\n");SetPos(22, 14);printf("小垃圾,你準備好了嗎?\n");Sleep(2000);main();break;}
}

5.3? test.c

#include"tetris.h"int main()
{//修改當前地區為本地模式,為了?持中?寬字符的打印setlocale(LC_ALL, "");srand((unsigned int)time(NULL));char ch=0;do{GameInit();GameRun();GameOver();system("cls");SetPos(20, 12);printf("再來一局嗎?(Y/N):\n");SetPos(22, 15);ch = getchar();getchar();//清理\n} while (ch == 'y' || ch == 'Y');SetPos(0, 29);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161664.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161664.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161664.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java http

超文本傳輸協議 超文本/html 工作方式 get / url 請求獲取相應報文 http://xxxxxxxxxxxx.com/user?xxx xxx 協議類型 - 服務器地址 -路徑 path 請求格式: head / body path路徑進行處理資源 等同于報文請求: GET: /users HTTP/1.1 Host:api.github.com 響應報文 請求方式…

京東數據分析平臺(京東運營數據采集):2023年10月京東白酒品牌銷售排行榜

鯨參謀監測的京東平臺10月份白酒市場銷售數據已出爐&#xff01; 鯨參謀數據顯示&#xff0c;10月份&#xff0c;京東平臺上白酒的銷量為340萬&#xff0c;環比增長約16%&#xff0c;同比增長約37%&#xff1b;銷售額為28億&#xff0c;環比增長約20%&#xff0c;同比增長約122…

educoder中Hive綜合應用案例 — 學生成績查詢

第1關:計算每個班的語文總成績和數學總成績 ---------- 禁止修改 ----------drop database if exists mydb cascade;set hive.auto.convert.join = false; set hive.ignore.mapjoin.hint=false; ---------- 禁止修改 ---------- ---------- begin ---------- ---創建mydb數據…

如何在Ubuntu的Linux系統中安裝MySQL5.7數據庫

前往MySQL數據庫官網鏈接地址下載5.7數據庫。 MySQL :: Download MySQL Community Server (Archived Versions)使用ssh的可視化工具將下載的mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz文件上傳到Linux服務器&#xff0c;并解壓文件 tar -zxvf mysql-5.7.40-linux-glibc2.12-x…

總結vue框架中的鉤子函數

vue2.x生命周期鉤子函數 組件的生命周期分為3個階段: 掛載階段:beforeCreate、created、beforeMount、mounted,更新階段:beforeUpdate、updated,銷毀階段:beforeDestroy、destroyed beforeCreate beforeCreate() {// 初始化數據&#xff0c;并通過Object.defineProperty()和…

基于蛇優化算法優化概率神經網絡PNN的分類預測 - 附代碼

基于蛇優化算法優化概率神經網絡PNN的分類預測 - 附代碼 文章目錄 基于蛇優化算法優化概率神經網絡PNN的分類預測 - 附代碼1.PNN網絡概述2.變壓器故障診街系統相關背景2.1 模型建立 3.基于蛇優化優化的PNN網絡5.測試結果6.參考文獻7.Matlab代碼 摘要&#xff1a;針對PNN神經網絡…

大數據預處理技術

文章目錄 前言 大數據技術成為前沿專業 也是現在甚至未來的朝陽產業&#xff0c;大數據有分別是 數據預處理 數據存儲 大數據處理和分析 數據可視化 部分組成 &#xff0c;大數據行業有數據則稱王&#xff0c;大數據的核心是數據本身 怎么獲取有價值的數據呢&#xff1f;本章講…

android 9 adb安裝過程學習(二)

一、PackageInstalllerService流程分析 下面來分析下 PackageInstallerService 中的邏輯&#xff0c;我們先來看看 PackageInstallerService 的創建&#xff0c;當然&#xff0c;這部分的邏輯是在開機的時候&#xff0c;這里我們再回顧下&#xff1a; 位置&#xff1a;./frame…

Cent OS 8.2 安裝 自定義硬盤 固定IP VMware

時間&#xff1a;20231122 環境&#xff1a;win11 、VMware 16 pro、Cent OS 8.2 說明&#xff1a;自定義安裝方法、自定義硬盤分區、固定IP且能聯網 1、使用自定義的方式安裝虛擬機 此處選擇典型&#xff0c;則會自動安裝系統&#xff0c;無法自定義硬件以及配置信息 選擇…

CCF CSP認證 歷年題目自練Day49

題目一 此題用暴力枚舉做過&#xff08;80分&#xff09;現如今終于用二維前綴和做到滿分。 試題編號&#xff1a; 202309-2 試題名稱&#xff1a; 坐標變換&#xff08;其二&#xff09; 時間限制&#xff1a; 2.0s 內存限制&#xff1a; 512.0MB 問題描述&#xff1a; 問題…

【Axure視頻教程】中繼器首行函數

今天教大家在Axure里如何使用中繼器首行函數&#xff0c;本視頻教程會先從中繼器首行函數的基礎講起&#xff0c;然后通過計算合計數、統計選中數、兩個中繼器選項聯動這3個案例更加深入的講解這這個函數的應用。注&#xff1a;該教程主要講解中繼器首行函數的用法&#xff0c;…

NFC:應用場景廣泛的短距離通信技術

NFC&#xff1a;應用場景廣泛的短距離通信技術 一、NFC 技術介紹1.1 NFC 技術應用場景1.2 NFC 技術優點1.3 NFC 工作原理 二、NFC 開發2.1 NFC 應用開發流程2.2 NFC 讀取和寫入2.3 NFC 讀寫功能示例 三、總結 一、NFC 技術介紹 NFC &#xff08;Near-field communication&…

SM系列國密算法

一、概述 國產密碼算法&#xff08;國密算法&#xff09;是指國家密碼局認定的國產商用密碼算法&#xff0c;國密算法是提升國家密碼安全和數據安全的關鍵技術。 為了保障商用密碼的安全性&#xff0c;國家密碼局制定了一系列密碼標準&#xff0c;包括&#xff1a;SM1、SM2、S…

分類預測 | Matlab實現基于PSO-PNN粒子群算法優化概率神經網絡的數據分類預測

分類預測 | Matlab實現基于PSO-PNN粒子群算法優化概率神經網絡的數據分類預測 目錄 分類預測 | Matlab實現基于PSO-PNN粒子群算法優化概率神經網絡的數據分類預測分類效果基本描述程序設計參考資料 分類效果 基本描述 1.Matlab實現基于PSO-PNN粒子群算法優化概率神經網絡的數據…

hadoop在本地創建文件,然后將文件拷貝/上傳到HDFS

1.要$cd {對應目錄}進入到對應目錄&#xff0c;一般為 cd /usr/local/hadoop/ 2.創建文件&#xff0c;$sudo gedit {文件名}&#xff0c;例 sudo gedit test.txt 然后在彈出的txt文件輸入內容&#xff0c;點擊右上角的保存之后&#xff0c;關閉即可。 3.拷貝本地文件到HDF…

RPG項目_UI登錄

首先創建一個項目 將資源包導進Resources文件夾 創建一個Scripts腳本文件夾 然后再對Scripts腳本文件夾分門別類 導入UI資源包 創建一個Image 按住Alt 選擇右下角 image就會覆蓋整個面板 修改image名字為BG 將image圖片放置背景欄 再創建一個image 改名為MainMenu 修改MainMenu…

屏幕坐標轉換場景坐標并進行物體檢測

在 OpenSceneGraph 中&#xff0c;要將屏幕坐標轉換為當前場景坐標&#xff0c;并過濾出屏幕顯示范圍內的節點&#xff0c;可以通過以下步驟實現&#xff1a; 獲取屏幕坐標&#xff1a; 當用戶點擊或交互時&#xff0c;獲取鼠標點擊的屏幕坐標。 轉換屏幕坐標為世界坐標&#…

Linux上通過SSL/TLS和start tls連接到LDAP服務器

一&#xff0c;大致流程。 1.首先在Linux上搭建一個LDAP服務器 2.在LDAP服務器上安裝CA證書&#xff0c;服務器證書&#xff0c;因為SSL/TLS&#xff0c;start tls都屬于機密通信&#xff0c;需要客戶端和服務器都存在一個相同的證書認證雙方的身份。3.安裝phpldapadmin工具&am…

一點DETR學習

DETR: 主要是為了學習query。 主要從兩個方面&#xff1a;加偏好和縮短序列長度

〖大前端 - 基礎入門三大核心之JS篇?〗- DOM事件傳播和事件監聽方法addEventListener()

說明&#xff1a;該文屬于 大前端全棧架構白寶書專欄&#xff0c;目前階段免費&#xff0c;如需要項目實戰或者是體系化資源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作經驗, 從事過全棧研發、產品經理等工作&#xf…