QT C++編寫的俄羅斯方塊
https://www。zhihu。com/video/1421065640296206336
Game。pro
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
main。c
main。c:
#include
#include
#include
#include
#include
#define ROW 29 //遊戲區行數
#define COL 20 //遊戲區列數
#define DOWN 80 //方向鍵:下
#define LEFT 75 //方向鍵:左
#define RIGHT 77 //方向鍵:右
#define SPACE 32 //空格鍵
#define ESC 27 //Esc鍵
struct Face
{
int data[ROW][COL + 10]; //用於標記指定位置是否有方塊(1為有,0為無)
int color[ROW][COL + 10]; //用於記錄指定位置的方塊顏色編碼
}face;
struct Block
{
int space[4][4];
}block[7][4]; //用於儲存7種基本形狀方塊的各自的4種形態的資訊,共28種
//隱藏游標
void HideCursor();
//游標跳轉
void CursorJump(int x, int y);
//初始化介面
void InitInterface();
//初始化方塊資訊
void InitBlockInfo();
//顏色設定
void color(int num);
//畫出方塊
void DrawBlock(int shape, int form, int x, int y);
//空格覆蓋
void DrawSpace(int shape, int form, int x, int y);
//合法性判斷
int IsLegal(int shape, int form, int x, int y);
//判斷得分與結束
int JudeFunc();
//遊戲主體邏輯函式
void StartGame();
//從檔案讀取最高分
void ReadGrade();
//更新最高分到檔案
void WriteGrade();
int max, grade; //全域性變數
int main()
{
#pragma warning (disable:4996) //消除警告
max = 0, grade = 0; //初始化變數
system(“title 俄羅斯方塊”); //設定cmd視窗的名字
system(“mode con lines=29 cols=60”); //設定cmd視窗的大小
HideCursor(); //隱藏游標
ReadGrade(); //從檔案讀取最高分到max變數
InitInterface(); //初始化介面
InitBlockInfo(); //初始化方塊資訊
srand((unsigned int)time(NULL)); //設定隨機數生成的起點
StartGame(); //開始遊戲
return 0;
}
//隱藏游標
void HideCursor()
{
CONSOLE_CURSOR_INFO curInfo; //定義游標資訊的結構體變數
curInfo。dwSize = 1; //如果沒賦值的話,隱藏游標無效
curInfo。bVisible = FALSE; //將游標設定為不可見
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制檯控制代碼
SetConsoleCursorInfo(handle, &curInfo); //設定游標資訊
}
//游標跳轉
void CursorJump(int x, int y)
{
COORD pos; //定義游標位置的結構體變數
pos。X = x; //橫座標設定
pos。Y = y; //縱座標設定
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制檯控制代碼
SetConsoleCursorPosition(handle, pos); //設定游標位置
}
//初始化介面
void InitInterface()
{
color(7); //顏色設定為白色
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL + 10; j++)
{
if (j == 0 || j == COL - 1 || j == COL + 9)
{
face。data[i][j] = 1; //標記該位置有方塊
CursorJump(2 * j, i);
printf(“■”);
}
else if (i == ROW - 1)
{
face。data[i][j] = 1; //標記該位置有方塊
printf(“■”);
}
else
face。data[i][j] = 0; //標記該位置無方塊
}
}
for (int i = COL; i < COL + 10; i++)
{
face。data[8][i] = 1; //標記該位置有方塊
CursorJump(2 * i, 8);
printf(“■”);
}
CursorJump(2 * COL, 1);
printf(“下一個方塊:”);
CursorJump(2 * COL + 4, ROW - 19);
printf(“左移:←”);
CursorJump(2 * COL + 4, ROW - 17);
printf(“右移:→”);
CursorJump(2 * COL + 4, ROW - 15);
printf(“加速:↓”);
CursorJump(2 * COL + 4, ROW - 13);
printf(“旋轉:空格”);
CursorJump(2 * COL + 4, ROW - 11);
printf(“暫停: S”);
CursorJump(2 * COL + 4, ROW - 9);
printf(“退出: Esc”);
CursorJump(2 * COL + 4, ROW - 7);
printf(“重新開始:R”);
CursorJump(2 * COL + 4, ROW - 5);
printf(“最高紀錄:%d”, max);
CursorJump(2 * COL + 4, ROW - 3);
printf(“當前分數:%d”, grade);
}
//初始化方塊資訊
void InitBlockInfo()
{
//“T”形
for (int i = 0; i <= 2; i++)
block[0][0]。space[1][i] = 1;
block[0][0]。space[2][1] = 1;
//“L”形
for (int i = 1; i <= 3; i++)
block[1][0]。space[i][1] = 1;
block[1][0]。space[3][2] = 1;
//“J”形
for (int i = 1; i <= 3; i++)
block[2][0]。space[i][2] = 1;
block[2][0]。space[3][1] = 1;
for (int i = 0; i <= 1; i++)
{
//“Z”形
block[3][0]。space[1][i] = 1;
block[3][0]。space[2][i + 1] = 1;
//“S”形
block[4][0]。space[1][i + 1] = 1;
block[4][0]。space[2][i] = 1;
//“O”形
block[5][0]。space[1][i + 1] = 1;
block[5][0]。space[2][i + 1] = 1;
}
//“I”形
for (int i = 0; i <= 3; i++)
block[6][0]。space[i][1] = 1;
int temp[4][4];
for (int shape = 0; shape < 7; shape++) //7種形狀
{
for (int form = 0; form < 3; form++) //4種形態(已經有了一種,這裡每個還需增加3種)
{
//獲取第form種形態
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
temp[i][j] = block[shape][form]。space[i][j];
}
}
//將第form種形態順時針旋轉,得到第form+1種形態
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
block[shape][form + 1]。space[i][j] = temp[3 - j][i];
}
}
}
}
}
//顏色設定
void color(int c)
{
switch (c)
{
case 0:
c = 13; //“T”形方塊設定為紫色
break;
case 1:
case 2:
c = 12; //“L”形和“J”形方塊設定為紅色
break;
case 3:
case 4:
c = 10; //“Z”形和“S”形方塊設定為綠色
break;
case 5:
c = 14; //“O”形方塊設定為黃色
break;
case 6:
c = 11; //“I”形方塊設定為淺藍色
break;
default:
c = 7; //其他預設設定為白色
break;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //顏色設定
//注:SetConsoleTextAttribute是一個API(應用程式程式設計介面)
}
//畫出方塊
void DrawBlock(int shape, int form, int x, int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form]。space[i][j] == 1) //如果該位置有方塊
{
CursorJump(2 * (x + j), y + i); //游標跳轉到指定位置
printf(“■”); //輸出方塊
}
}
}
}
//空格覆蓋
void DrawSpace(int shape, int form, int x, int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form]。space[i][j] == 1) //如果該位置有方塊
{
CursorJump(2 * (x + j), y + i); //游標跳轉到指定位置
printf(“ ”); //列印空格覆蓋(兩個空格)
}
}
}
}
//合法性判斷
int IsLegal(int shape, int form, int x, int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
//如果方塊落下的位置本來就已經有方塊了,則不合法
if ((block[shape][form]。space[i][j] == 1) && (face。data[y + i][x + j] == 1))
return 0; //不合法
}
}
return 1; //合法
}
//判斷得分與結束
int JudeFunc()
{
//判斷是否得分
for (int i = ROW - 2; i > 4; i——)
{
int sum = 0; //記錄第i行的方塊個數
for (int j = 1; j < COL - 1; j++)
{
sum += face。data[i][j]; //統計第i行的方塊個數
}
if (sum == 0) //該行沒有方塊,無需再判斷其上的層次(無需再繼續判斷是否得分)
break; //跳出迴圈
if (sum == COL - 2) //該行全是方塊,可得分
{
grade += 10; //滿一行加10分
color(7); //顏色設定為白色
CursorJump(2 * COL + 4, ROW - 3); //游標跳轉到顯示當前分數的位置
printf(“當前分數:%d”, grade); //更新當前分數
for (int j = 1; j < COL - 1; j++) //清除得分行的方塊資訊
{
face。data[i][j] = 0; //該位置得分後被清除,標記為無方塊
CursorJump(2 * j, i); //游標跳轉到該位置
printf(“ ”); //列印空格覆蓋(兩個空格)
}
//把被清除行上面的行整體向下挪一格
for (int m = i; m >1; m——)
{
sum = 0; //記錄上一行的方塊個數
for (int n = 1; n < COL - 1; n++)
{
sum += face。data[m - 1][n]; //統計上一行的方塊個數
face。data[m][n] = face。data[m - 1][n]; //將上一行方塊的標識移到下一行
face。color[m][n] = face。color[m - 1][n]; //將上一行方塊的顏色編號移到下一行
if (face。data[m][n] == 1) //上一行移下來的是方塊,列印方塊
{
CursorJump(2 * n, m); //游標跳轉到該位置
color(face。color[m][n]); //顏色設定為還方塊的顏色
printf(“■”); //列印方塊
}
else //上一行移下來的是空格,列印空格
{
CursorJump(2 * n, m); //游標跳轉到該位置
printf(“ ”); //列印空格(兩個空格)
}
}
if (sum == 0) //上一行移下來的全是空格,無需再將上層的方塊向下移動(移動結束)
return 1; //返回1,表示還需呼叫該函式進行判斷(移動下來的可能還有滿行)
}
}
}
//判斷遊戲是否結束
for (int j = 1; j < COL - 1; j++)
{
if (face。data[1][j] == 1) //頂層有方塊存在(以第1行為頂層,不是第0行)
{
Sleep(1000); //留給玩家反應時間
system(“cls”); //清空螢幕
color(7); //顏色設定為白色
CursorJump(2 * (COL / 3), ROW / 2 - 3);
if (grade>max)
{
printf(“恭喜你打破最高記錄,最高記錄更新為%d”, grade);
WriteGrade();
}
else if (grade == max)
{
printf(“與最高記錄持平,加油再創佳績”, grade);
}
else
{
printf(“請繼續加油,當前與最高記錄相差%d”, max - grade);
}
CursorJump(2 * (COL / 3), ROW / 2);
printf(“GAME OVER”);
while (1)
{
char ch;
CursorJump(2 * (COL / 3), ROW / 2 + 3);
printf(“再來一局?(y/n):”);
scanf(“%c”, &ch);
if (ch == ‘y’ || ch == ‘Y’)
{
system(“cls”);
main();
}
else if (ch == ‘n’ || ch == ‘N’)
{
CursorJump(2 * (COL / 3), ROW / 2 + 5);
exit(0);
}
else
{
CursorJump(2 * (COL / 3), ROW / 2 + 4);
printf(“選擇錯誤,請再次選擇”);
}
}
}
}
return 0; //判斷結束,無需再呼叫該函式進行判斷
}
//遊戲主體邏輯函式
void StartGame()
{
int shape = rand() % 7, form = rand() % 4; //隨機獲取方塊的形狀和形態
while (1)
{
int t = 0;
int nextShape = rand() % 7, nextForm = rand() % 4; //隨機獲取下一個方塊的形狀和形態
int x = COL / 2 - 2, y = 0; //方塊初始下落位置的橫縱座標
color(nextShape); //顏色設定為下一個方塊的顏色
DrawBlock(nextShape, nextForm, COL + 3, 3); //將下一個方塊顯示在右上角
while (1)
{
color(shape); //顏色設定為當前正在下落的方塊
DrawBlock(shape, form, x, y); //將該方塊顯示在初始下落位置
if (t == 0)
{
t = 15000; //這裡t越小,方塊下落越快(可以根據此設定遊戲難度)
}
while (——t)
{
if (kbhit() != 0) //若鍵盤被敲擊,則退出迴圈
break;
}
if (t == 0) //鍵盤未被敲擊
{
if (IsLegal(shape, form, x, y + 1) == 0) //方塊再下落就不合法了(已經到達底部)
{
//將當前方塊的資訊錄入face當中
//face:記錄介面的每個位置是否有方塊,若有方塊還需記錄該位置方塊的顏色。
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form]。space[i][j] == 1)
{
face。data[y + i][x + j] = 1; //將該位置標記為有方塊
face。color[y + i][x + j] = shape; //記錄該方塊的顏色數值
}
}
}
while (JudeFunc()); //判斷此次方塊下落是否得分以及遊戲是否結束
break; //跳出當前死迴圈,準備進行下一個方塊的下落
}
else //未到底部
{
DrawSpace(shape, form, x, y); //用空格覆蓋當前方塊所在位置
y++; //縱座標自增(下一次顯示方塊時就相當於下落了一格了)
}
}
else //鍵盤被敲擊
{
char ch = getch(); //讀取keycode
switch (ch)
{
case DOWN: //方向鍵:下
if (IsLegal(shape, form, x, y + 1) == 1) //判斷方塊向下移動一位後是否合法
{
//方塊下落後合法才進行以下操作
DrawSpace(shape, form, x, y); //用空格覆蓋當前方塊所在位置
y++; //縱座標自增(下一次顯示方塊時就相當於下落了一格了)
}
break;
case LEFT: //方向鍵:左
if (IsLegal(shape, form, x - 1, y) == 1) //判斷方塊向左移動一位後是否合法
{
//方塊左移後合法才進行以下操作
DrawSpace(shape, form, x, y); //用空格覆蓋當前方塊所在位置
x——; //橫座標自減(下一次顯示方塊時就相當於左移了一格了)
}
break;
case RIGHT: //方向鍵:右
if (IsLegal(shape, form, x + 1, y) == 1) //判斷方塊向右移動一位後是否合法
{
//方塊右移後合法才進行以下操作
DrawSpace(shape, form, x, y); //用空格覆蓋當前方塊所在位置
x++; //橫座標自增(下一次顯示方塊時就相當於右移了一格了)
}
break;
case SPACE: //空格鍵
if (IsLegal(shape, (form + 1) % 4, x, y + 1) == 1) //判斷方塊旋轉後是否合法
{
//方塊旋轉後合法才進行以下操作
DrawSpace(shape, form, x, y); //用空格覆蓋當前方塊所在位置
y++; //縱座標自增(總不能原地旋轉吧)
form = (form + 1) % 4; //方塊的形態自增(下一次顯示方塊時就相當於旋轉了)
}
break;
case ESC: //Esc鍵
system(“cls”); //清空螢幕
color(7);
CursorJump(COL, ROW / 2);
printf(“ 遊戲結束 ”);
CursorJump(COL, ROW / 2 + 2);
exit(0); //結束程式
case ‘s’:
case ‘S’: //暫停
system(“pause>nul”); //暫停(按任意鍵繼續)
break;
case ‘r’:
case ‘R’: //重新開始
system(“cls”); //清空螢幕
main(); //重新執行主函式
}
}
}
shape = nextShape, form = nextForm; //獲取下一個方塊的資訊
DrawSpace(nextShape, nextForm, COL + 3, 3); //將右上角的方塊資訊用空格覆蓋
}
}
//從檔案讀取最高分
void ReadGrade()
{
FILE* pf = fopen(“俄羅斯方塊最高得分記錄。txt”, “r”); //以只讀方式開啟檔案
if (pf == NULL) //開啟檔案失敗
{
pf = fopen(“俄羅斯方塊最高得分記錄。txt”, “w”); //以只寫方式開啟檔案(檔案不存在可以自動建立該檔案)
fwrite(&grade, sizeof(int), 1, pf); //將max寫入檔案(此時max為0),即將最高歷史得分初始化為0
}
fseek(pf, 0, SEEK_SET); //使檔案指標pf指向檔案開頭
fread(&max, sizeof(int), 1, pf); //讀取檔案中的最高歷史得分到max當中
fclose(pf); //關閉檔案
pf = NULL; //檔案指標及時置空
}
//更新最高分到檔案
void WriteGrade()
{
FILE* pf = fopen(“俄羅斯方塊最高得分記錄。txt”, “w”); //以只寫方式開啟檔案
if (pf == NULL) //開啟檔案失敗
{
printf(“儲存最高得分記錄失敗\n”);
exit(0);
}
fwrite(&grade, sizeof(int), 1, pf); //將本局遊戲得分寫入檔案當中(更新最高歷史得分)
fclose(pf); //關閉檔案
pf = NULL; //檔案指標及時置空
}