QT C++編寫的俄羅斯方塊(可畢業設計)

QT C++編寫的俄羅斯方塊

https://www。zhihu。com/video/1421065640296206336

QT C++編寫的俄羅斯方塊(可畢業設計)

QT C++編寫的俄羅斯方塊(可畢業設計)

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; //檔案指標及時置空

}