C++實現俄羅斯方塊源碼
本文實例為大傢分享瞭C++實現俄羅斯方塊的具體代碼,供大傢參考,具體內容如下
先是效果圖:
主菜單:
遊戲:
設置:
錯誤處理:
代碼:
#include <iostream.h> #include <conio.h> #include <stdio.h> #include <windows.h> #include <fstream.h> #include <time.h> #include <cstring> #pragma comment( lib,"winmm.lib" ) //定義 //方塊 #define NO 0 #define SQR 1 //碰撞檢測 #define OK 0 #define CANTMOVE 1 //方向 #define UP 0 #define DOWN 1 #define LEFT 2 #define RIGHT 3 //錯誤碼 #define no_enough_memory 0 #define set_no_found 1 #define dat_no_found 2 #define error_argument 3 //函數聲明 //模塊 void play();//開始遊戲 void sets();//設置 void highscores();//排行榜 void copyright();//作者 //功能 void mapsetup();//準備地圖 bool newsqr();//放置方塊,返回是否遊戲結束 int move(int direction);//移動方塊,返回定義表 void movetomap();//把當前方塊移動到地圖上 int wholeline();//檢查是否組成瞭一層,返回層數,-1表示沒有 void deleteline(int which);//刪除一行 void endup();//結束遊戲,清理內存 //顯示 void show();//刷新畫面 void showmenu(char* menu);//顯示菜單 //文件 void loadset();//加載設置 void saveset();//保存設置 void loadhs();//加載排行榜 bool addscores(int score,char name[50]);//增加一個分數,返回是否是高分 void savehs();//保存排行榜 //坐標變換 int get(int x,int y); void set(int x,int y,int date); //結構 //設置 struct{ int xs,ys;//屏幕大小 int speed;//速度 char sqr[3],no[3],frame[3];//方塊、空白處、邊框的樣式 }gameset; //排行榜 struct{ char name[50]; int score; }rating[10]; //全局變量 //變量 int* map=NULL;//地圖 bool now[4][4];//當前方塊 int xnow,ynow;//當前位置 int guide;//分數 //常量 const bool shap[7][4][4]={//形狀 {\ 0,0,0,0,\ 0,0,0,0,\ 1,1,1,1,\ 0,0,0,0,\ },\ {\ 0,0,0,0,\ 0,1,1,0,\ 0,1,1,0,\ 0,0,0,0,\ },\ {\ 0,0,0,0,\ 0,1,1,1,\ 0,0,1,0,\ 0,0,0,0,\ },\ {\ 0,0,0,0,\ 1,0,0,0,\ 1,1,1,0,\ 0,0,0,0,\ },\ {\ 0,0,0,0,\ 0,0,0,1,\ 0,1,1,1,\ 0,0,0,0,\ },\ {\ 0,1,0,0,\ 0,1,1,0,\ 0,0,1,0,\ 0,0,0,0,\ },\ {\ 0,0,1,0,\ 0,1,1,0,\ 0,1,0,0,\ 0,0,0,0,\ }\ }; const char errword[4][50]={"程序沒能取得足夠的內存","無法打開或找不到設置文件set.ini","無法打開或找不到排行榜數據highscore.dat","您設置的參數太大或者太小"}; //控制臺 HANDLE hout;//控制臺句柄 COORD curpos={0,0};//光標坐標 //主函數 int main() { start1: try { hout = GetStdHandle(STD_OUTPUT_HANDLE);//獲取控制臺句柄,以便移動光標 srand(time(0));//用當前時間初始化隨機數生成器 loadset();//加載 loadhs(); start2: while(1) { showmenu("俄羅斯方塊\n請選擇菜單:\n1.開始遊戲\n2.設置\n3.排行榜\n4.幫助\n5.保存並退出\n"); switch(getch()) { case '1': system("cls");//play函數覆蓋界面而不是清屏,所以需要先清屏 play(); break; case '2': sets(); break; case '3': highscores(); break; case '4': copyright(); break; case '5': savehs();//保存數據 saveset(); return 0; } } } catch(int errnum)//錯誤處理 { system("cls"); printf("o(>﹏<)o 出錯啦!\n程序收到瞭一條錯誤信息,錯誤碼是:%d(%s)\n您可以聯系我們解決這個問題。\n",errnum,errword[errnum]); printf("\n你可以選擇以下操作:\n1.重啟程序\n2.以默認設置重啟程序\n3.向設置和數據文件寫入默認設置然後重啟\n4.退出\n"); switch(getch()) { case '1': goto start1; case '2': gameset.xs=20; gameset.ys=20; gameset.speed=100; strcpy(gameset.sqr,"[]");//無法直接給數組復制數據 strcpy(gameset.no," "); strcpy(gameset.frame,"::"); int i; for(i=0;i<10;i++) strcpy(rating[i].name,"未命名"),rating[i].score=0; goto start2; case '3': { ofstream fout; fout.open("set.ini"); fout<<"20\n20\n100[]\n \n::\n"; fout.close(); fout.clear(); fout.open("highscore.dat"); int j; for(j=0;j<10;j++) fout<<"未命名\n0\n"; goto start1; } default: return -1;//返回異常退出 } } return 0; } void play() { mapsetup();//初始化 /*for(int i=0;i<20;i++) set(i,19,SQR);*/ while(newsqr())//不斷新建方塊,直到返回NO { while(move(DOWN)!=CANTMOVE)//每次向下移動方塊,直到不能移動 { guide+=1;//向下移動一次加1分 show();//顯示 while(kbhit())//不斷處理鍵盤,直到沒有按鍵 { switch(getch())//獲取按鍵 { case 'w': move(UP); break; case 's': move(DOWN); break; case 'a': move(LEFT); break; case 'd': move(RIGHT); break; } } Sleep(gameset.speed);//延時 } movetomap();//退出循環時無法向下移動,把當前方塊移動到地圖上 int line; while((line=wholeline())!=-1);//不斷檢查是否出現整行,直到沒有 deleteline(line);//刪除整行 } endup();//無法新建方塊,遊戲結束 return;//結束 } //函數定義 void mapsetup() { map=new int[gameset.xs*gameset.ys];//申請內存 if(!map)//如果申請到0 throw no_enough_memory;//拋出異常 //初始化地圖 int i,j; for(i=0;i<gameset.xs;i++) { for(j=0;j<gameset.ys;j++) { set(i,j,NO); } } guide=0;//分數清零 return; } int get(int x,int y) { if(y<0)//上方虛擬為空 return NO; if(x>=0&&x<gameset.xs&&y>=0&&y<gameset.ys)//是否在地圖范圍內 return *(map+y*gameset.xs+x);//提取數據 else return SQR;//虛擬地圖側面和底部有方塊 } void set(int x,int y,int date) { if(x>=0&&x<gameset.xs&&y>=0&&y<gameset.ys)//if(x>0&&x<gameset.xs&&y>0&&y<gameset.ys)//是否在地圖范圍內 *(map+y*gameset.xs+x)=date;//寫入 return; } bool newsqr(){ int i,j; for(i=0;i<4;i++)//檢查下一個方塊要出現的地方是否有方塊 if(get(gameset.xs/2+i,0)==SQR) return false;//有方塊,創建失敗 int which=rand()%7;//隨機選擇形狀 for(i=0;i<4;i++) { for(j=0;j<4;j++) { now[i][j]=shap[which][i][j];//復制形狀 } } for(i=rand()%4;i>0;i--)//旋轉隨機0-3次 move(UP); xnow=gameset.xs/2;//設置坐標 ynow=-4; return true; } int move(int direction){ int x,y;//儲存坐標偏移量 int i,j; switch(direction) { case UP://上鍵是旋轉 bool newshap[4][4];//儲存旋轉後的圖形 for(i=0;i<4;i++) { for(j=0;j<4;j++) { newshap[i][j]=now[j][3-i];//坐標變換 } } for(i=0;i<4;i++) { for(j=0;j<4;j++) { if(newshap[i][j]==true&&get(xnow+i,ynow+j)==SQR)//對新圖形碰撞檢測 return CANTMOVE;//不能旋轉 } } for(i=0;i<4;i++) { for(j=0;j<4;j++) { now[i][j]=newshap[i][j];//檢測完畢,復制形狀 } } return OK; case DOWN://先記錄坐標的偏移量,確定沒有碰撞以後移動 x=0,y=1; break; case LEFT: x=-1;y=0; break; case RIGHT: x=1,y=0; break; } for(i=0;i<4;i++) { for(j=0;j<4;j++) { if(now[i][j]==true&&get(i+x+xnow,j+y+ynow)==SQR)//如果和地圖上的方塊重合(邊緣以外get函數也返回SQR,不必單獨處理)//if(get(i+x,j+y)==SQR)//if(now[i+x][j+y]==SQR) { return CANTMOVE;//無法移動 } } } xnow+=x;//檢測完畢,更改坐標 ynow+=y; return OK; } void movetomap(){ guide+=10;//成功放置方塊,加10分 int i,j; for(i=0;i<4;i++) { for(j=0;j<4;j++) { if(now[i][j]==true) set(xnow+i,ynow+j,SQR);//復制方塊到地圖 } } return; } int wholeline(){ int i,j; bool whole;//儲存是否是整行 for(j=0;j<gameset.ys;j++)//for(i=0;i<gameset.ys;i++) { whole=true;//假設是整行 for(i=0;i<gameset.xs;i++)//for(j=0;j<gameset.xs;j++) { if(get(i,j)==NO) whole=false;//有空,不是整行 } if(whole) return j;//是整行,返回 } return -1;//沒找到整行,返回 } void deleteline(int which){ int i,j; guide+=1000;//消方塊,獎勵分數 for(i=which;i>=0;i--) { for(j=0;j<gameset.xs;j++) { set(j,i,get(j,i-1));//移動上面的所有方塊,覆蓋這一行。最上面虛擬成瞭空,不必特殊處理 } } return; } void endup(){ delete map;//清理內存 system("cls"); while(kbhit())//清除所有未處理的按鍵 getchar(); showmenu("遊戲結束,請輸入您的姓名:"); char name[50]="noname"; scanf("%s",&name[0]);//輸入 char word[1000];//儲存格式化以後的字符串 sprintf(&word[0],"遊戲結束!\n\n您(%s)的積分是:%d\n\n%s\n\n請按任意鍵繼續···\n",name,guide,((addscores(guide,name))?"你進入瞭排行榜":"你沒有進入排行榜")); showmenu(&word[0]); getch(); highscores();//顯示排行榜 savehs();//保存排行榜 return; } void show(){ int i,j; SetConsoleCursorPosition(hout,curpos);//system("cls");//光標移至左上角,覆蓋之前的圖案 printf("當前積分:%d\n",guide); for(i=0;i<gameset.xs+2;i++)//輸出上邊框 printf(gameset.frame); printf("\n"); for(j=0;j<gameset.ys;j++) { printf(gameset.frame);//左邊框 for(i=0;i<gameset.xs;i++) { if(i>=xnow&&i<(xnow+4)&&j>=ynow&&j<(ynow+4))//if(i>=xnow&&i<(xnow+1)&&j>=ynow&&j<(ynow+1))//在當前方塊范圍內 { if(now[i-xnow][j-ynow]==true)//如果有方塊 printf(gameset.sqr); else if(get(i,j)==SQR)//如果地圖有方塊 printf(gameset.sqr); else//否則,空白 printf(gameset.no); } else//不在當前方塊范圍內,輸出地圖 { if(get(i,j)==SQR)//有方塊 printf(gameset.sqr); else//否則,沒方塊 printf(gameset.no); } } printf("::\n");//右邊框和換行 } for(i=0;i<gameset.xs+2;i++)//下邊框 printf(gameset.frame); printf("\n"); return; } void showmenu(char* menu) { int i,j; char output[100];//儲存本行的文字 system("cls"); for(i=0;i<gameset.xs;i++) printf(gameset.frame);//輸出上邊框 printf("\n"); i=0,j=0; while(*menu!='\0') { printf(gameset.frame);//左邊框 for(i=0,j=0;*(menu+i)!='\n'&&*(menu+i)!='\0';i++,j++)//復制本行 { if(*(menu+i)=='\t')//如果是制表符,輸出空格直到列數是6的倍數 { for(;j%6!=5;j++) output[j]=gameset.no[0]; j--; } else output[j]=*(menu+i);//直接復制 } menu=menu+i+1;//移動指針到下一行 for(;j<gameset.xs*2-6;j++)//用空格填充本行的後面 output[j]=gameset.no[0]; output[j]='\0';//結束標記 printf(gameset.no);//行首空格,讓界面更好看 printf(output);//輸出內容 printf(gameset.frame);//右邊框 printf("\n");//換行 Sleep(100);//延時,顯示漸漸出現的效果 } for(i=0;i<gameset.xs;i++) printf(gameset.frame);//輸出下邊框 printf("\n"); return; } void sets() { char word[1000];//要顯示的文字 while(1) {//使用符號'\'告訴編譯器下一行應該和本行連起來再編譯 sprintf(&word[0],"\ 設置菜單\n\ 請選擇你要更改的選項:\n\ 屏幕大小:\n\ \t1.寬度:%d\n\ \t2.高度:%d\n\ 速度:\n\ \t3.方塊下落速度:%d\n\ 顯示:\n\ \t4.方塊形狀:\"%s\"\n\ \t5.空白區域形狀:\"%s\"\n\ \t6.邊框形狀:\"%s\"\n\ 7.返回\n\ ",gameset.xs,gameset.ys,gameset.speed,gameset.sqr,gameset.no,gameset.frame); showmenu(&word[0]); char choice=getch(); showmenu("請輸入改變後的參數:");//顯示提示 switch(choice)//分情況輸入 { case '1': scanf("%d",&gameset.xs); if(gameset.xs<15||gameset.xs>70) throw error_argument; break; case '2': scanf("%d",&gameset.ys); if(gameset.ys<15||gameset.ys>70) throw error_argument; break; case '3': scanf("%d",&gameset.speed); if(gameset.speed<0) throw error_argument; break; case '4': cin.getline(&gameset.sqr[0],3);//scanf("%s",&gameset.sqr[0]); cout<<endl; //gameset.sqr[3]='\0'; break; case '5': cin.getline(&gameset.no[0],3);//scanf("%s",&gameset.no[0]); cout<<endl; //gameset.no[3]='\0'; break; case '6': cin.getline(&gameset.frame[0],3);//scanf("%s",&gameset.frame[0]); cout<<endl; //gameset.frame[3]='\0'; break; case '7': saveset();//保存設置並返回 return; } } } void highscores() { int i; char word[1000]="排行榜\n排名\t姓名\t積分\n\0";//抬頭 for(i=0;i<10;i++) { sprintf(&word[0],"%s%d\t%s\t%d\n",&word[0],i+1,rating[i].name,rating[i].score);//追加名單 } sprintf(&word[0],"%s請按任意鍵繼續···\n",&word[0]);//追加提示 showmenu(&word[0]); getch();//等待按鍵 return; } void copyright() { showmenu("\ 請使用a,s,d,w鍵,\n\ a,s,d分別為\n\ 向左下右移動,\n\ w為旋轉\n\ \n\ 移動速度是越小越快\n\ \n\ 請按任意鍵繼續···\n\ "); getch(); return; } void loadset() { ifstream fin; fin.open("set.ini",ios::in|ios::nocreate);//輸入文件流 if(!fin) throw set_no_found; fin>>gameset.xs>>gameset.ys>>gameset.speed; fin.getline(gameset.sqr,4);//獲取整行,因為可能有空格 fin.getline(gameset.no,4); fin.getline(gameset.frame,4); return; } void saveset() { ofstream fout;//輸出文件流 fout.open("set.ini",ifstream::out|ios::nocreate); if(!fout) throw set_no_found; fout<<gameset.xs<<'\n'<<gameset.ys<<'\n'<<gameset.speed<<gameset.sqr<<'\n'<<gameset.no<<'\n'<<gameset.frame<<'\n'; return; } void loadhs() { int i; ifstream fin; fin.open("highscore.dat",ifstream::in|ios::nocreate);//打開文件,不存在則錯誤 if(!fin)//如果錯誤 throw dat_no_found; for(i=0;i<10;i++)//讀取文件 fin>>rating[i].name>>rating[i].score; return; } bool addscores(int score,char name[50]) { int i,j; for(i=0;i<10;i++)//枚舉 { if(rating[i].score<score)//如果排行榜的積分比新積分小 { for(j=9;j>i;j--)//移動數據空出位置 { for(int k=0;k<50;k++) rating[j].name[k]=rating[j-1].name[k]; rating[j].score=rating[j-1].score; } rating[i].score=score;//插入數據 strcpy(rating[i].name,name); return true;//返回進入排行 } } return false;//返回沒有進入 } void savehs() { int i; ofstream fout; fout.open("highscore.dat",ifstream::out|ios::nocreate); if(!fout) throw dat_no_found; for(i=0;i<10;i++) fout<<rating[i].name<<'\n'<<rating[i].score<<'\n'; return; }
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。