在玩Codeblocks自帶的俄羅斯方塊時覺得不錯,然而有時間限制。所以想自己再寫一個。

程序中有一個board數組,其中有要顯示的部分,也有不顯示的部分,不顯示的部分都存儲1。
如下圖:

shape采用4*4數組(shape)保存。如:
0 0 0 0
0 1 0 0
1 1 1 0
0 0 0 0
另外用變量row和column保存shape數組左上角在board中的位置。
每次下落或左右移動,先對row和column做出改變,然後檢測當前row和column下,shape是否重合了為1的格子,如果有重合,就說明shape出界了或者到達下落最低點,則要恢復row和column值。另外,如果是下落,還要將shape放在board上,並產生新的shape。
旋轉時,先對shape數組進行旋轉操作,然後檢測重合,如果有重合,則反向旋轉回來。
1 #if defined(UNICODE) && !defined(_UNICODE)
2 #define _UNICODE
3 #elif defined(_UNICODE) && !defined(UNICODE)
4 #define UNICODE
5 #endif
6
7 #include <tchar.h>
8 #include <windows.h>
9 #include <pthread.h>
10 #include <stdio.h>
11 #include <time.h>
12 /*-----------------宏定義--------------------------------------------------------*/
13 #define WIDTH 180
14 #define HEIGHT 400
15 #define LONG_SLEEP 300
16 #define BKCOLOR RGB(238,238,238)//背景色
17 /*-----------------變量----------------------------------------------------------*/
18 static int shapes[7][4][4];//存儲7個形狀
19 static int high_score[4]= {0,0,0,0};//前三個元素存儲最高分,最後一個元素存儲此次得分
20 static int **shape;//當前形狀
21 static int **board;
22 static int M=15;//顯示的列數
23 static int N=30;//顯示的行數
24 static int MM=M+8;//board的列數
25 static int NN=N+4;//board的行數
26 static int LEFT=4;//顯示的最左一列
27 static int RIGHT=LEFT+M-1;//顯示的最右一列
28 static int TOP=0;//顯示的最上一列
29 static int BOTTOM=N-1;//顯示的最下一列
30 static int score=0;
31 static int row=0;//形狀所在行
32 static int column=MM/2;//形狀坐在列
33 static bool is_pause=false;
34 static HBRUSH grey_brush =CreateSolidBrush (RGB(210,210,210));
35 static HBRUSH white_brush =CreateSolidBrush (RGB(130,130,130));
36 static HBRUSH bk_brush =CreateSolidBrush (BKCOLOR);
37 static HPEN hPen = CreatePen(PS_SOLID,1,RGB(147,155,166));
38 static int lattices_top=40;//上面留白
39 static int lattices_left=20;//左側留白
40 static int width=WIDTH/M;//每個格子的寬度
41 static int height=(HEIGHT-lattices_top)/N;//每個格子的高度
42 /*-----------------函數-----------------------------------------------------------*/
43 void add_score() ;
44 bool check_is_lose() ;
45 void clear_up() ;//消除沒有空格子的行
46 void* down_thread_function(void * args) ;//形狀下落進程要執行的函數
47 void exit_game(HWND hwnd) ;
48 void give_new_shape() ;//隨機生成一個新形狀
49 int handle_key(HWND hwnd,WPARAM wParam) ;
50 int init_down_thread(HWND hwnd) ;//初始化形狀下落進程
51 int init_game(HWND hwnd) ;//初始化游戲程序
52 void init_play() ;//初始化游戲數據
53 bool is_legel() ;//檢測形狀在當前位置是否合法(即是否重合了非空的格子)
54 int load_scores(int* a) ;//讀取游戲最高分數據
55 int load_shape() ;//從文件中加載7個形狀
56 void lose_game(HWND hwnd) ;
57 int move_down(HWND hwnd) ;//形狀下落
58 int move_lr(HWND hwnd,int lr) ;//形狀左右移動
59 void paint_lattice(HDC hdc,int x,int y,int color) ;//顯示一個格子
60 void paint_UI(HDC hdc) ;//畫界面
61 void reset_rc() ;
62 void rerotate_matrix(int mn) ;//順時針旋轉一個行列數為mn的方陣
63 void rotate_matrix(int mn) ;//逆時針旋轉一個行列數為mn的方陣
64 int rotate_shape(HWND hwnd) ;//旋轉當前形狀並更新界面
65 bool save_score(HWND hwnd) ;//保存最高分數據
66 void shape_to_ground() ;//當前形狀落地之後,更新board
67 bool sort_scores(int* a) ;//對最高分和此次得分排序,若創造新紀錄則返回true
68 void update_UI(HWND hwnd) ;//更新界面,僅更新Rect區域(形狀所在的那幾行)內
69 void update_UI_all(HWND hwnd) ;//更新界面,更新整個界面
70 int write_scores(int* a) ;//寫最高分數據
71
72
73
74
75 /* Declare Windows procedure */
76 LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
77
78 /* Make the class name into a global variable */
79 TCHAR szClassName[ ] = _T("Tris");
80
81 int WINAPI WinMain (HINSTANCE hThisInstance,
82 HINSTANCE hPrevInstance,
83 LPSTR lpszArgument,
84 int nCmdShow) {
85 HWND hwnd; /* This is the handle for our window */
86 MSG messages; /* Here messages to the application are saved */
87 WNDCLASSEX wincl; /* Data structure for the windowclass */
88
89 /* The Window structure */
90 wincl.hInstance = hThisInstance;
91 wincl.lpszClassName = szClassName;
92 wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
93 wincl.style = CS_DBLCLKS; /* Catch double-clicks */
94 wincl.cbSize = sizeof (WNDCLASSEX);
95
96 /* Use default icon and mouse-pointer */
97 wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
98 wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
99 wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
100 wincl.lpszMenuName = NULL; /* No menu */
101 wincl.cbClsExtra = 0; /* No extra bytes after the window class */
102 wincl.cbWndExtra = 0; /* structure or the window instance */
103 /* Use Windows's default colour as the background of the window */
104 wincl.hbrBackground =bk_brush;
105 /* Register the window class, and if it fails quit the program */
106 if (!RegisterClassEx (&wincl))
107 return 0;
108
109 /* The class is registered, let's create the program*/
110 hwnd = CreateWindowEx (
111 0, /* Extended possibilites for variation */
112 szClassName, /* Classname */
113 _T("Tris"), /* Title Text */
114 WS_OVERLAPPEDWINDOW, /* default window */
115 CW_USEDEFAULT, /* Windows decides the position */
116 CW_USEDEFAULT, /* where the window ends up on the screen */
117 WIDTH+200, /* The programs width */
118 HEIGHT+70, /* and height in pixels */
119 HWND_DESKTOP, /* The window is a child-window to desktop */
120 NULL, /* No menu */
121 hThisInstance, /* Program Instance handler */
122 NULL /* No Window Creation data */
123 );
124
125 /* Make the window visible on the screen */
126 ShowWindow (hwnd, nCmdShow);
127
128 /* Run the message loop. It will run until GetMessage() returns 0 */
129 while (GetMessage (&messages, NULL, 0, 0)) {
130 /* Translate virtual-key messages into character messages */
131 TranslateMessage(&messages);
132 /* Send message to WindowProcedure */
133 DispatchMessage(&messages);
134 }
135
136 /* The program return-value is 0 - The value that PostQuitMessage() gave */
137 return messages.wParam;
138 }
139 //從文件中加載7個形狀
140 int load_shape() {
141 FILE* f=fopen("shapes.txt","rb");
142 if(f==NULL) {
143 return -1;
144 }
145 for(int i=0; i<7; i++) {
146 for(int j=0; j<4; j++) {
147 for(int k=0; k<4; k++) {
148 if(fscanf(f,"%d",&shapes[i][j][k])!=1) {
149 return -1;
150 }
151 }
152 }
153 }
154 fclose(f);
155 return 0;
156 }
157 //隨機生成一個新形狀
158 void give_new_shape() {
159 int shape_num=rand()%7;
160 for(int i=0; i<4; i++) {
161 for(int j=0; j<4; j++) {
162 shape[i][j]=shapes[shape_num][i][j];
163 }
164 }
165 }
166 void add_score() {
167 score+=100;
168 }
169 //消除沒有空格子的行
170 void clear_up() {
171 for(int i=row; i<=row+3; i++) {
172 if(i>BOTTOM)continue;
173 bool there_is_blank=false;
174 for(int j=LEFT; j<=RIGHT; j++) {
175 if(board[i][j]==0) {
176 there_is_blank=true;
177 break;
178 }
179 }
180 if(!there_is_blank) {
181 add_score();
182 for(int r=i; r>=1; r--) {
183 for(int c=LEFT; c<=RIGHT; c++) {
184 board[r][c]=board[r-1][c];
185 }
186 }
187 }
188 }
189 }
190 //檢測形狀在當前位置是否合法(即是否重合了非空的格子)
191 bool is_legel() {
192 for(int i=0; i<4; i++) {
193 for(int j=0; j<4; j++) {
194 if(shape[i][j]==1&&board[row+i][column+j]==1) {
195 return false;
196 }
197 }
198 }
199 return true;
200 }
201 //逆時針旋轉一個行列數為mn的方陣
202 void rotate_matrix(int mn) {
203 int** a=shape;
204 int s=0;
205 for(int n=mn; n>=1; n-=2) {
206 for(int i=0; i<n-1; i++) {
207 int t=a[s+i][s];
208 a[s+i][s]=a[s][s+n-i-1];
209 a[s][s+n-i-1]=a[s+n-i-1][s+n-1];
210 a[s+n-i-1][s+n-1]=a[s+n-1][s+i];
211 a[s+n-1][s+i]=t;
212 }
213 s++;
214 }
215 }
216 //順時針旋轉一個行列數為mn的方陣
217 void rerotate_matrix(int mn) {
218 int** a=shape;
219 int s=0;
220 for(int n=mn; n>=1; n-=2) {
221 for(int i=0; i<n-1; i++) {
222 int t=a[s+i][s];
223 a[s+i][s]=a[s+n-1][s+i];
224 a[s+n-1][s+i]=a[s+n-i-1][s+n-1];
225 a[s+n-i-1][s+n-1]=a[s][s+n-i-1];
226 a[s][s+n-i-1]=t;
227 }
228 s++;
229 }
230 }
231 //顯示一個格子
232 void paint_lattice(HDC hdc,int x,int y,int color) {
233 if(x<TOP||x>BOTTOM||y<LEFT||y>RIGHT) {
234 return ;
235 }
236 x-=TOP;
237 y-=LEFT;
238 int left=lattices_left+y*width;
239 int right=lattices_left+y*width+width;
240 int top=lattices_top+x*height;
241 int bottom=lattices_top+x*height+height;
242 MoveToEx (hdc,left,top, NULL) ;
243 LineTo (hdc,right,top) ;
244 MoveToEx (hdc,left,top, NULL) ;
245 LineTo (hdc,left,bottom) ;
246 MoveToEx (hdc,left,bottom, NULL) ;
247 LineTo (hdc,right,bottom) ;
248 MoveToEx (hdc,right,top, NULL) ;
249 LineTo (hdc,right,bottom) ;
250 SelectObject(hdc, grey_brush);
251 if(color==0) {
252 SelectObject(hdc, white_brush);
253 }
254 Rectangle(hdc,left,top,right,bottom);
255 }
256 //更新界面,僅更新Rect區域(形狀所在的那幾行)內
257 void update_UI(HWND hwnd) {
258 static RECT rect;
259 rect.left=lattices_left;
260 rect.right=lattices_left+M*width+width;
261 rect.top=lattices_top+(row-1)*height;
262 rect.bottom=lattices_top+(row+4)*height;
263 InvalidateRect (hwnd,&rect, false) ;
264 }
265 //更新界面,更新整個界面
266 void update_UI_all(HWND hwnd) {
267 InvalidateRect (hwnd,NULL, false) ;
268 }
269 //畫界面
270 void paint_UI(HDC hdc) {
271 SetBkColor(hdc,BKCOLOR);
272 SelectObject(hdc,hPen); //選用畫筆
273 char score_str[20];
274 sprintf(score_str,"Score:%d",score);
275 TextOut(hdc,10,10,score_str,strlen(score_str));
276 sprintf(score_str,"Highest Scores:");
277 TextOut(hdc,WIDTH+50,50,score_str,strlen(score_str));
278 for(int i=0; i<3; i++) {
279 sprintf(score_str,"%d",high_score[i]);
280 TextOut(hdc,WIDTH+50,50+(i+1)*20,score_str,strlen(score_str));
281 }
282 for(int i=TOP; i<=BOTTOM; i++) {
283 for(int j=LEFT; j<=RIGHT; j++) {
284 paint_lattice(hdc,i,j,board[i][j]);
285 }
286 }
287 for(int i=0; i<4; i++) {
288 for(int j=0; j<4; j++) {
289 if(shape[i][j]==1)
290 paint_lattice(hdc,row+i,column+j,shape[i][j]);
291 }
292 }
293 }
294 //旋轉當前形狀並更新界面
295 int rotate_shape(HWND hwnd) {
296 int mn=4;
297 rotate_matrix(mn);
298 if(!is_legel()) {
299 rerotate_matrix(mn);
300 }
301 update_UI(hwnd);
302 }
303 void reset_rc() {
304 row=0;
305 column=MM/2-2;
306 }
307 //讀取游戲最高分數據
308 int load_scores(int* a) {
309 FILE* f=fopen("scores.txt","r");
310 if(f==NULL)return -1;
311 fscanf(f,"%d%d%d",&a[0],&a[1],&a[2]);
312 return 0;
313 }
314 //初始化游戲數據
315 void init_play() {
316 load_scores(high_score);
317 for(int i=0; i<NN; i++) {
318 for(int j=0; j<MM; j++) {
319 board[i][j]=0;
320 }
321 }
322 for(int i=0; i<N; i++) {
323 for(int j=0; j<LEFT; j++) {
324 board[i][j]=1;
325 }
326 }
327 for(int i=0; i<N; i++) {
328 for(int j=RIGHT+1; j<MM; j++) {
329 board[i][j]=1;
330 }
331 }
332 for(int i=BOTTOM+1; i<NN; i++) {
333 for(int j=0; j<MM; j++) {
334 board[i][j]=1;
335 }
336 }
337 reset_rc();
338 score=0;
339 give_new_shape();
340 is_pause=false;
341 return ;
342 }
343 bool check_is_lose() {
344 if(row==0)return true;
345 return false;
346 }
347 //對最高分和此次得分排序,若創造新紀錄則返回true
348 bool sort_scores(int* a) {
349 int temp=a[3];
350 for(int i=0; i<4; i++) {
351 for(int j=0; j<3; j++) {
352 if(a[j]<a[j+1]) {
353 int t=a[j];
354 a[j]=a[j+1];
355 a[j+1]=t;
356 }
357 }
358 }
359 if(temp>a[3])return true;
360 return false;
361 }
362 //寫最高分數據
363 int write_scores(int* a) {
364 FILE* f=fopen("scores.txt","w");
365 if(f==NULL)return -1;
366 fprintf(f,"%d\n%d\n%d\n",a[0],a[1],a[2]);
367 return 0;
368 }
369 //保存最高分數據
370 bool save_score(HWND hwnd) {
371 high_score[3]=score;
372 bool made_record=sort_scores(high_score);
373 if(write_scores(high_score)!=0) {
374 MessageBox(hwnd,"Write file error.Program will exit.","Error",NULL);
375 DestroyWindow(hwnd);
376 }
377 return made_record;
378 }
379 void lose_game(HWND hwnd) {
380 if(is_pause)return ;
381 is_pause=true;
382 char message[200]="You lose the Game.\n";
383 char title[50]="Game Over";
384 if(save_score(hwnd)) {
385 strcat(message,"You have made a new record.\n");
386 char score_str[100];
387 sprintf(score_str,"The Highest Scores:\n%d\n%d\n%d\n",high_score[0],high_score[1],high_score[2]);
388 strcat(message,score_str);
389 }
390 strcat(message,"\nPlay again?\n");
391 if(MessageBox(hwnd,message,title,MB_YESNO)==IDYES) {
392 init_play();
393 update_UI_all(hwnd);
394 } else {
395 exit(0);
396 }
397 }
398 void exit_game(HWND hwnd) {
399 is_pause=true;
400 char message[200]="";
401 char title[50]="Exit";
402 if(save_score(hwnd)) {
403 strcat(message,"You have made a new record.\n");
404 char score_str[100];
405 sprintf(score_str,"The Highest Scores:\n%d\n%d\n%d\n",high_score[0],high_score[1],high_score[2]);
406 strcat(message,score_str);
407 MessageBox(hwnd,message,title,NULL);
408 }
409 exit(0);
410 }
411 //當前形狀落地之後,更新board
412 void shape_to_ground() {
413 for(int i=0; i<4; i++) {
414 for(int j=0; j<4; j++) {
415 board[row+i][column+j]=shape[i][j]==1?1:board[row+i][column+j];
416 }
417 }
418 }
419 //形狀下落
420 int move_down(HWND hwnd) {
421 row++;
422 if(!is_legel()) {
423 row--;
424 if(check_is_lose()) {
425 lose_game(hwnd);
426 return 0;
427 }
428 shape_to_ground();
429 clear_up();
430 update_UI_all(hwnd);
431 reset_rc();
432 give_new_shape();
433 }
434 update_UI(hwnd);
435 }
436 //進程參數結構體
437 struct thread_arg {
438 HWND arg_hwnd;
439 };
440 //形狀下落進程要執行的函數
441 void* down_thread_function(void * args) {
442 thread_arg *arg=(thread_arg*)args;
443 HWND dhwnd=arg->arg_hwnd;
444 while(true) {
445 if(is_pause) {
446 Sleep(300);
447 continue;
448 }
449 move_down(dhwnd);
450 Sleep(LONG_SLEEP);
451 }
452 }
453 //初始化形狀下落進程
454 int init_down_thread(HWND hwnd) {
455 int ret;
456 pthread_t t;
457 thread_arg *argp=new thread_arg;
458 argp->arg_hwnd=hwnd;
459 ret=pthread_create(&t,NULL,down_thread_function,argp);
460 delete argp;
461 if(ret!=0) {
462 return -1;
463 }
464 return 0;
465 }
466 //初始化游戲程序
467 int init_game(HWND hwnd) {
468 board=new int*[NN];
469 for(int i=0; i<NN; i++) {
470 board[i]=new int[MM];
471 }
472 shape=new int*[4];
473 for(int i=0; i<4; i++) {
474 shape[i]=new int[4];
475 }
476 srand(time(0));
477 if(load_shape()!=0) {
478 MessageBox(hwnd,"Read file error.Program will exit.","Error",NULL);
479 exit(-1);
480 }
481 init_play();
482 update_UI_all(hwnd);
483 if(init_down_thread(hwnd)!=0) {
484 MessageBox(hwnd,"Thread error.Program will exit.","Error",NULL);
485 exit(-1);
486 }
487 return 0;
488 }
489 //形狀左右移動
490 int move_lr(HWND hwnd,int lr) {
491 int temp=column;
492 if(lr==0)column--;
493 else {
494 column++;
495 }
496 if(!is_legel()) {
497 column=temp;
498 }
499 update_UI(hwnd);
500 }
501 int handle_key(HWND hwnd,WPARAM wParam) {
502 if(wParam==VK_ESCAPE) {//ESC退出
503 exit_game(hwnd);
504 }
505 if(wParam==VK_SPACE) {//空格暫停
506 is_pause=!is_pause;
507 }
508 if(is_pause==true) {
509 Sleep(300);
510 return 0;
511 }
512 if(wParam==VK_UP) {
513 rotate_shape(hwnd);
514 }
515 if(wParam==VK_DOWN) {
516 move_down(hwnd);
517 }
518 if(wParam==VK_LEFT) {
519 move_lr(hwnd,0);
520 }
521 if(wParam==VK_RIGHT) {
522 move_lr(hwnd,1);
523 }
524 return 0;
525 }
526 /* This function is called by the Windows function DispatchMessage() */
527 HWND hwnd;
528 LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
529 static HDC hdc;
530 static HDC hdcBuffer;
531 static HBITMAP hBitMap;
532 static PAINTSTRUCT ps ;
533 switch (message) { /* handle the messages */
534 case WM_CREATE:
535 init_game(hwnd);
536 break;
537 case WM_KEYDOWN:
538 handle_key(hwnd,wParam);
539 break;
540 case WM_DESTROY:
541 exit_game(hwnd);
542 PostQuitMessage (0); /* send a WM_QUIT to the message queue */
543 break;
544 case WM_PAINT:
545 hdc = BeginPaint (hwnd, &ps) ;
546 paint_UI(hdc);
547 EndPaint (hwnd, &ps) ;
548 break;
549 default: /* for messages that we don't deal with */
550 return DefWindowProc (hwnd, message, wParam, lParam);
551 }
552 return 0;
553 }
程序寫於2016年五一期間
隨筆寫於2016.5.8