C++制作俄羅斯方塊
緣起:
在玩Codeblocks自帶的俄羅斯方塊時覺得不錯,然而有時間限制。所以想自己再寫一個。
程序效果:

主要內(nèi)容:
程序中有一個board數(shù)組,其中有要顯示的部分,也有不顯示的部分,不顯示的部分都存儲1。
如下圖:

shape采用4*4數(shù)組(shape)保存。如:
0 0 0 0
0 1 0 0
1 1 1 0
0 0 0 0
另外用變量row和column保存shape數(shù)組左上角在board中的位置。
每次下落或左右移動,先對row和column做出改變,然后檢測當(dāng)前row和column下,shape是否重合了為1的格子,如果有重合,就說明shape出界了或者到達(dá)下落最低點(diǎn),則要恢復(fù)row和column值。另外,如果是下落,還要將shape放在board上,并產(chǎn)生新的shape。
旋轉(zhuǎn)時,先對shape數(shù)組進(jìn)行旋轉(zhuǎn)操作,然后檢測重合,如果有重合,則反向旋轉(zhuǎn)回來。
代碼:
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
/*-----------------宏定義--------------------------------------------------------*/
#define WIDTH 180
#define HEIGHT 400
#define LONG_SLEEP 300
#define BKCOLOR RGB(238,238,238)//背景色
/*-----------------變量----------------------------------------------------------*/
static int shapes[7][4][4];//存儲7個形狀
static int high_score[4]= {0,0,0,0};//前三個元素存儲最高分,最后一個元素存儲此次得分
static int **shape;//當(dāng)前形狀
static int **board;
static int M=15;//顯示的列數(shù)
static int N=30;//顯示的行數(shù)
static int MM=M+8;//board的列數(shù)
static int NN=N+4;//board的行數(shù)
static int LEFT=4;//顯示的最左一列
static int RIGHT=LEFT+M-1;//顯示的最右一列
static int TOP=0;//顯示的最上一列
static int BOTTOM=N-1;//顯示的最下一列
static int score=0;
static int row=0;//形狀所在行
static int column=MM/2;//形狀坐在列
static bool is_pause=false;
static HBRUSH grey_brush =CreateSolidBrush (RGB(210,210,210));
static HBRUSH white_brush =CreateSolidBrush (RGB(130,130,130));
static HBRUSH bk_brush =CreateSolidBrush (BKCOLOR);
static HPEN hPen = CreatePen(PS_SOLID,1,RGB(147,155,166));
static int lattices_top=40;//上面留白
static int lattices_left=20;//左側(cè)留白
static int width=WIDTH/M;//每個格子的寬度
static int height=(HEIGHT-lattices_top)/N;//每個格子的高度
/*-----------------函數(shù)-----------------------------------------------------------*/
void add_score() ;
bool check_is_lose() ;
void clear_up() ;//消除沒有空格子的行
void* down_thread_function(void * args) ;//形狀下落進(jìn)程要執(zhí)行的函數(shù)
void exit_game(HWND hwnd) ;
void give_new_shape() ;//隨機(jī)生成一個新形狀
int handle_key(HWND hwnd,WPARAM wParam) ;
int init_down_thread(HWND hwnd) ;//初始化形狀下落進(jìn)程
int init_game(HWND hwnd) ;//初始化游戲程序
void init_play() ;//初始化游戲數(shù)據(jù)
bool is_legel() ;//檢測形狀在當(dāng)前位置是否合法(即是否重合了非空的格子)
int load_scores(int* a) ;//讀取游戲最高分?jǐn)?shù)據(jù)
int load_shape() ;//從文件中加載7個形狀
void lose_game(HWND hwnd) ;
int move_down(HWND hwnd) ;//形狀下落
int move_lr(HWND hwnd,int lr) ;//形狀左右移動
void paint_lattice(HDC hdc,int x,int y,int color) ;//顯示一個格子
void paint_UI(HDC hdc) ;//畫界面
void reset_rc() ;
void rerotate_matrix(int mn) ;//順時針旋轉(zhuǎn)一個行列數(shù)為mn的方陣
void rotate_matrix(int mn) ;//逆時針旋轉(zhuǎn)一個行列數(shù)為mn的方陣
int rotate_shape(HWND hwnd) ;//旋轉(zhuǎn)當(dāng)前形狀并更新界面
bool save_score(HWND hwnd) ;//保存最高分?jǐn)?shù)據(jù)
void shape_to_ground() ;//當(dāng)前形狀落地之后,更新board
bool sort_scores(int* a) ;//對最高分和此次得分排序,若創(chuàng)造新紀(jì)錄則返回true
void update_UI(HWND hwnd) ;//更新界面,僅更新Rect區(qū)域(形狀所在的那幾行)內(nèi)
void update_UI_all(HWND hwnd) ;//更新界面,更新整個界面
int write_scores(int* a) ;//寫最高分?jǐn)?shù)據(jù)
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("Tris");
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow) {
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground =bk_brush;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
_T("Tris"), /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
WIDTH+200, /* The programs width */
HEIGHT+70, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0)) {
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
//從文件中加載7個形狀
int load_shape() {
FILE* f=fopen("shapes.txt","rb");
if(f==NULL) {
return -1;
}
for(int i=0; i<7; i++) {
for(int j=0; j<4; j++) {
for(int k=0; k<4; k++) {
if(fscanf(f,"%d",&shapes[i][j][k])!=1) {
return -1;
}
}
}
}
fclose(f);
return 0;
}
//隨機(jī)生成一個新形狀
void give_new_shape() {
int shape_num=rand()%7;
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
shape[i][j]=shapes[shape_num][i][j];
}
}
}
void add_score() {
score+=100;
}
//消除沒有空格子的行
void clear_up() {
for(int i=row; i<=row+3; i++) {
if(i>BOTTOM)continue;
bool there_is_blank=false;
for(int j=LEFT; j<=RIGHT; j++) {
if(board[i][j]==0) {
there_is_blank=true;
break;
}
}
if(!there_is_blank) {
add_score();
for(int r=i; r>=1; r--) {
for(int c=LEFT; c<=RIGHT; c++) {
board[r][c]=board[r-1][c];
}
}
}
}
}
//檢測形狀在當(dāng)前位置是否合法(即是否重合了非空的格子)
bool is_legel() {
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
if(shape[i][j]==1&&board[row+i][column+j]==1) {
return false;
}
}
}
return true;
}
//逆時針旋轉(zhuǎn)一個行列數(shù)為mn的方陣
void rotate_matrix(int mn) {
int** a=shape;
int s=0;
for(int n=mn; n>=1; n-=2) {
for(int i=0; i<n-1; i++) {
int t=a[s+i][s];
a[s+i][s]=a[s][s+n-i-1];
a[s][s+n-i-1]=a[s+n-i-1][s+n-1];
a[s+n-i-1][s+n-1]=a[s+n-1][s+i];
a[s+n-1][s+i]=t;
}
s++;
}
}
//順時針旋轉(zhuǎn)一個行列數(shù)為mn的方陣
void rerotate_matrix(int mn) {
int** a=shape;
int s=0;
for(int n=mn; n>=1; n-=2) {
for(int i=0; i<n-1; i++) {
int t=a[s+i][s];
a[s+i][s]=a[s+n-1][s+i];
a[s+n-1][s+i]=a[s+n-i-1][s+n-1];
a[s+n-i-1][s+n-1]=a[s][s+n-i-1];
a[s][s+n-i-1]=t;
}
s++;
}
}
//顯示一個格子
void paint_lattice(HDC hdc,int x,int y,int color) {
if(x<TOP||x>BOTTOM||y<LEFT||y>RIGHT) {
return ;
}
x-=TOP;
y-=LEFT;
int left=lattices_left+y*width;
int right=lattices_left+y*width+width;
int top=lattices_top+x*height;
int bottom=lattices_top+x*height+height;
MoveToEx (hdc,left,top, NULL) ;
LineTo (hdc,right,top) ;
MoveToEx (hdc,left,top, NULL) ;
LineTo (hdc,left,bottom) ;
MoveToEx (hdc,left,bottom, NULL) ;
LineTo (hdc,right,bottom) ;
MoveToEx (hdc,right,top, NULL) ;
LineTo (hdc,right,bottom) ;
SelectObject(hdc, grey_brush);
if(color==0) {
SelectObject(hdc, white_brush);
}
Rectangle(hdc,left,top,right,bottom);
}
//更新界面,僅更新Rect區(qū)域(形狀所在的那幾行)內(nèi)
void update_UI(HWND hwnd) {
static RECT rect;
rect.left=lattices_left;
rect.right=lattices_left+M*width+width;
rect.top=lattices_top+(row-1)*height;
rect.bottom=lattices_top+(row+4)*height;
InvalidateRect (hwnd,&rect, false) ;
}
//更新界面,更新整個界面
void update_UI_all(HWND hwnd) {
InvalidateRect (hwnd,NULL, false) ;
}
//畫界面
void paint_UI(HDC hdc) {
SetBkColor(hdc,BKCOLOR);
SelectObject(hdc,hPen); //選用畫筆
char score_str[20];
sprintf(score_str,"Score:%d",score);
TextOut(hdc,10,10,score_str,strlen(score_str));
sprintf(score_str,"Highest Scores:");
TextOut(hdc,WIDTH+50,50,score_str,strlen(score_str));
for(int i=0; i<3; i++) {
sprintf(score_str,"%d",high_score[i]);
TextOut(hdc,WIDTH+50,50+(i+1)*20,score_str,strlen(score_str));
}
for(int i=TOP; i<=BOTTOM; i++) {
for(int j=LEFT; j<=RIGHT; j++) {
paint_lattice(hdc,i,j,board[i][j]);
}
}
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
if(shape[i][j]==1)
paint_lattice(hdc,row+i,column+j,shape[i][j]);
}
}
}
//旋轉(zhuǎn)當(dāng)前形狀并更新界面
int rotate_shape(HWND hwnd) {
int mn=4;
rotate_matrix(mn);
if(!is_legel()) {
rerotate_matrix(mn);
}
update_UI(hwnd);
}
void reset_rc() {
row=0;
column=MM/2-2;
}
//讀取游戲最高分?jǐn)?shù)據(jù)
int load_scores(int* a) {
FILE* f=fopen("scores.txt","r");
if(f==NULL)return -1;
fscanf(f,"%d%d%d",&a[0],&a[1],&a[2]);
return 0;
}
//初始化游戲數(shù)據(jù)
void init_play() {
load_scores(high_score);
for(int i=0; i<NN; i++) {
for(int j=0; j<MM; j++) {
board[i][j]=0;
}
}
for(int i=0; i<N; i++) {
for(int j=0; j<LEFT; j++) {
board[i][j]=1;
}
}
for(int i=0; i<N; i++) {
for(int j=RIGHT+1; j<MM; j++) {
board[i][j]=1;
}
}
for(int i=BOTTOM+1; i<NN; i++) {
for(int j=0; j<MM; j++) {
board[i][j]=1;
}
}
reset_rc();
score=0;
give_new_shape();
is_pause=false;
return ;
}
bool check_is_lose() {
if(row==0)return true;
return false;
}
//對最高分和此次得分排序,若創(chuàng)造新紀(jì)錄則返回true
bool sort_scores(int* a) {
int temp=a[3];
for(int i=0; i<4; i++) {
for(int j=0; j<3; j++) {
if(a[j]<a[j+1]) {
int t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
if(temp>a[3])return true;
return false;
}
//寫最高分?jǐn)?shù)據(jù)
int write_scores(int* a) {
FILE* f=fopen("scores.txt","w");
if(f==NULL)return -1;
fprintf(f,"%d\n%d\n%d\n",a[0],a[1],a[2]);
return 0;
}
//保存最高分?jǐn)?shù)據(jù)
bool save_score(HWND hwnd) {
high_score[3]=score;
bool made_record=sort_scores(high_score);
if(write_scores(high_score)!=0) {
MessageBox(hwnd,"Write file error.Program will exit.","Error",NULL);
DestroyWindow(hwnd);
}
return made_record;
}
void lose_game(HWND hwnd) {
if(is_pause)return ;
is_pause=true;
char message[200]="You lose the Game.\n";
char title[50]="Game Over";
if(save_score(hwnd)) {
strcat(message,"You have made a new record.\n");
char score_str[100];
sprintf(score_str,"The Highest Scores:\n%d\n%d\n%d\n",high_score[0],high_score[1],high_score[2]);
strcat(message,score_str);
}
strcat(message,"\nPlay again?\n");
if(MessageBox(hwnd,message,title,MB_YESNO)==IDYES) {
init_play();
update_UI_all(hwnd);
} else {
exit(0);
}
}
void exit_game(HWND hwnd) {
is_pause=true;
char message[200]="";
char title[50]="Exit";
if(save_score(hwnd)) {
strcat(message,"You have made a new record.\n");
char score_str[100];
sprintf(score_str,"The Highest Scores:\n%d\n%d\n%d\n",high_score[0],high_score[1],high_score[2]);
strcat(message,score_str);
MessageBox(hwnd,message,title,NULL);
}
exit(0);
}
//當(dāng)前形狀落地之后,更新board
void shape_to_ground() {
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
board[row+i][column+j]=shape[i][j]==1?1:board[row+i][column+j];
}
}
}
//形狀下落
int move_down(HWND hwnd) {
row++;
if(!is_legel()) {
row--;
if(check_is_lose()) {
lose_game(hwnd);
return 0;
}
shape_to_ground();
clear_up();
update_UI_all(hwnd);
reset_rc();
give_new_shape();
}
update_UI(hwnd);
}
//進(jìn)程參數(shù)結(jié)構(gòu)體
struct thread_arg {
HWND arg_hwnd;
};
//形狀下落進(jìn)程要執(zhí)行的函數(shù)
void* down_thread_function(void * args) {
thread_arg *arg=(thread_arg*)args;
HWND dhwnd=arg->arg_hwnd;
while(true) {
if(is_pause) {
Sleep(300);
continue;
}
move_down(dhwnd);
Sleep(LONG_SLEEP);
}
}
//初始化形狀下落進(jìn)程
int init_down_thread(HWND hwnd) {
int ret;
pthread_t t;
thread_arg *argp=new thread_arg;
argp->arg_hwnd=hwnd;
ret=pthread_create(&t,NULL,down_thread_function,argp);
delete argp;
if(ret!=0) {
return -1;
}
return 0;
}
//初始化游戲程序
int init_game(HWND hwnd) {
board=new int*[NN];
for(int i=0; i<NN; i++) {
board[i]=new int[MM];
}
shape=new int*[4];
for(int i=0; i<4; i++) {
shape[i]=new int[4];
}
srand(time(0));
if(load_shape()!=0) {
MessageBox(hwnd,"Read file error.Program will exit.","Error",NULL);
exit(-1);
}
init_play();
update_UI_all(hwnd);
if(init_down_thread(hwnd)!=0) {
MessageBox(hwnd,"Thread error.Program will exit.","Error",NULL);
exit(-1);
}
return 0;
}
//形狀左右移動
int move_lr(HWND hwnd,int lr) {
int temp=column;
if(lr==0)column--;
else {
column++;
}
if(!is_legel()) {
column=temp;
}
update_UI(hwnd);
}
int handle_key(HWND hwnd,WPARAM wParam) {
if(wParam==VK_ESCAPE) {//ESC退出
exit_game(hwnd);
}
if(wParam==VK_SPACE) {//空格暫停
is_pause=!is_pause;
}
if(is_pause==true) {
Sleep(300);
return 0;
}
if(wParam==VK_UP) {
rotate_shape(hwnd);
}
if(wParam==VK_DOWN) {
move_down(hwnd);
}
if(wParam==VK_LEFT) {
move_lr(hwnd,0);
}
if(wParam==VK_RIGHT) {
move_lr(hwnd,1);
}
return 0;
}
/* This function is called by the Windows function DispatchMessage() */
HWND hwnd;
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static HDC hdc;
static HDC hdcBuffer;
static HBITMAP hBitMap;
static PAINTSTRUCT ps ;
switch (message) { /* handle the messages */
case WM_CREATE:
init_game(hwnd);
break;
case WM_KEYDOWN:
handle_key(hwnd,wParam);
break;
case WM_DESTROY:
exit_game(hwnd);
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
paint_UI(hdc);
EndPaint (hwnd, &ps) ;
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
相關(guān)文章
C++實(shí)現(xiàn)LeetCode(67.二進(jìn)制數(shù)相加)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(67.二進(jìn)制數(shù)相加),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
c++動態(tài)內(nèi)存管理詳解(new/delete)
作為一名編程初學(xué)者,通常學(xué)習(xí)中,發(fā)生內(nèi)存錯誤是件非常麻煩的事情,下面這篇文章主要給大家介紹了關(guān)于c++動態(tài)內(nèi)存管理new/delete的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
關(guān)于C++智能指針shared_ptr和unique_ptr能否互轉(zhuǎn)問題
C++中的智能指針最常用的是shared_ptr和unique_ptr,C++新手最常問的問題是我從一個函數(shù)中拿到unique_ptr,但要轉(zhuǎn)成shared_ptr才能使用,要怎么轉(zhuǎn)換?同理是否能將shared_ptr轉(zhuǎn)換成unique_ptr,面對這些問題,跟隨小編一起看看吧2022-05-05
C++標(biāo)準(zhǔn)庫bitset類型的簡單使用方法介紹
這篇文章主要介紹了C++標(biāo)準(zhǔn)庫bitset類型的簡單使用方法,需要的朋友可以參考下2017-07-07

