俄罗斯方块(Win32实现,Codeblocks+GCC编译)
缘起:
在玩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 }
嗯,后来发现 Codeblocks 可以在设置里设置不限时间地玩BYOGames。。。尬。。
程序写于2016年五一期间
随笔写于2016.5.8