C++實現掃雷經典小遊戲
阿新 • • 發佈:2020-03-17
用C++復現經典掃雷,供大家參考,具體內容如下
主要是dfs實現開啟一片的操作,數字帶有顏色,很真實。
windows掃雷中滑鼠左右鍵同時按也實現了,即試探。
先上圖,詳見下面程式碼:
程式碼中有詳細註釋,編譯無任何錯誤警告。
Ps.有bug請評論指出,謝謝啦~
另外我覺得程式碼比較臃腫,有什麼可以優化的也請提出~
#include<cstdio> #include<cstring> #include<algorithm> #include<conio.h> #include<windows.h> #include<cstdlib> #include<ctime> using namespace std; #define MAXN 35 #define MIDX 10 #define MIDY 40 #define CG 25 #define CK 80 int G,K,Lnum,Wnum;//G為地圖高,K為地圖,Lnum為地圖中的雷數,Wnum為剩餘的小旗數 int nx,ny;//現在游標所在的位置 bool QR=0,Lose=0,is_flag_true[MAXN][MAXN];//QR為確認模式是否開啟,Lose為是否輸,第三個是這個位置上的旗是否放對 char map[MAXN][MAXN],tmap[MAXN][MAXN];//第一個是隻有雷和空地的地圖,第二個是玩家能看到的地圖 int map1[MAXN][MAXN],mapc[MAXN][MAXN];//map1為數字的地圖,其中0代表空地,-1為雷,1-8為周圍雷的個數 //mapc為當前格子的顏色 int col[10]={240,249,242,252,241,244,243,240,248};//col[i]表示windows掃雷中i的顏色,col[0]為空格的顏色 int d[10][4]={{0},{0,1},{1,0},-1},{-1,-1}};//8個方向 bool ZB;//作弊是否開啟 /*各種函式*/ void color(int);//顏色 void gto(int,int);//游標位置 void make();//製作隨機地圖 void print();//列印地圖等 bool check(int,int);//判斷座標是否合法 bool is_win();//判斷是否贏 bool is_lose();//是否輸 void dfs(int,int);//用深搜來開啟方塊 void st(int,int);//試探,即windows掃雷中的左右鍵同時按 void flag(int,int);//小旗 void bj(int,int);//標記 void swt();//確認模式 void again();//重新開始 void zb();//作弊模式 void mainmain();//主函式 void print_real_map();//列印最終的地圖 void begin();//各種操作 int main() { mainmain(); } void color(int a){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a);} void gto(int x,int y) { COORD pos;pos.X=y;pos.Y=x; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos); } void make() { for(int i=1;i<=G;i++) for(int j=1;j<=K;j++) map[i][j]='#';//初始化 for(int i=1;i<=Lnum;i++) { int x=rand()%G+1,y=rand()%K+1; while(map[x][y]=='O') x=rand()%G+1,y=rand()%K+1; map[x][y]='O'; }//隨機放雷 for(int i=1;i<=G;i++) for(int j=1;j<=K;j++) { if(map[i][j]=='O')map1[i][j]=-1,mapc[i][j]=240;//如果是雷 else { for(int k=1;k<=8;k++) if(map[i+d[k][0]][j+d[k][1]]=='O') map1[i][j]++;//計算周圍雷的個數 mapc[i][j]=col[map1[i][j]];//根據格子上的數設定顏色 } } for(int i=1;i<=G;i++) for(int j=1;j<=K;j++) if(mapc[i][j]==0)//空地 mapc[i][j]=240; } void print() { system("cls"); gto(0,MIDY-4); color(233); printf("掃雷"); color(240); gto(1,MIDY); for(int i=2;i<=G+1;i++) { gto(i,0); for(int j=1;j<=K;j++) printf("#"),tmap[i-1][j]='#';//初始化玩家所看到的地圖 } gto(2,0); nx=2,ny=0; color(15); printf("@"); color(15); gto(2,2*K+5);printf("-----規則-----"); gto(3,2*K+5);printf("wasd:選擇位置"); gto(4,2*K+5);printf("空格:開啟"); gto(5,2*K+5);printf("1鍵:試探周圍8個方塊,如果其中有雷則不會開啟,無"); gto(6,2*K+5);printf(" 雷或旗幟標對了則會將周圍無雷的位置開啟,"); gto(7,2*K+5);printf(" 如果試探時周圍有標錯的旗幟,則會遊戲失敗"); gto(8,2*K+5);printf(" 必須額外確認一次,以便檢視周圍被試探的區域"); gto(9,2*K+5);printf("2鍵:放置/取消小旗(F)"); gto(10,2*K+5);printf("3鍵:放置/取消標記(?)"); gto(11,2*K+5);printf("4鍵:開啟/關閉確認模式,即每次操作需再按一次確認"); gto(12,2*K+5);printf("5鍵:開啟/關閉作弊模式,即顯示原本地圖"); gto(13,2*K+5);printf("0鍵:重新開始");//列印規則 gto(G+4,0);printf("-----操作提示-----\n"); printf("請選擇方塊進行操作"); gto(1,2*K+10);color(12);printf("剩餘小旗數:%d",Wnum=Lnum); } bool check(int x,int y){return y>=0&&y<K&&x>=2&&x<G+2;} //因為地圖是從2行0列開始打的,而地圖是從1行1列開始存的,所以gto(x,y)走到的是map[x-1][y+1] bool is_win() { int cnt=0; for(int i=1;i<=G;i++) for(int j=1;j<=K;j++) if(map[i][j]=='#'&&map1[i][j]==-1) cnt++; if(cnt==Lnum) return 1; //所有沒被開啟的方塊都是雷=>勝利 for(int i=1;i<=G;i++) for(int j=1;j<=K;j++) if((tmap[i][j]!='F'&&map1[i][j]==-1)||(tmap[i][j]=='F'&&map1[i][j]!=-1)) return 0; return 1; //所有雷都標有旗 } bool is_lose(){return Lose;} void dfs(int x,int y) { if(map1[x-1][y+1]>0)//只要邊界全部是數字就return { gto(x,y),color(mapc[x-1][y+1]),printf("%d",map1[x-1][y+1]); tmap[x-1][y+1]=map1[x-1][y+1]+'0'; return; } gto(x,y);color(255); tmap[x-1][y+1]=' '; printf(" ");//因為下面判斷了雷,上面判斷了數字,這裡就一定是空地 for(int i=1;i<=8;i++) { int xx=x+d[i][0]-1,yy=y+d[i][1]+1;//這裡的xx和yy是在map中的,而不是gto中的 if(check(xx+1,yy-1)&&tmap[xx][yy]=='#'&&map1[xx][yy]!=-1)//所以check和dfs的引數要變化 dfs(xx+1,yy-1); } } void st(int x,int y) { for(int i=1;i<=8;i++) { int xx=x+d[i][0],yy=y+d[i][1]; if(check(xx,yy)) { gto(xx,yy); if(tmap[xx-1][yy+1]!='#') color(mapc[xx-1][yy+1]-128);//減去128使周圍的8個格子的背景顏色變為灰色 else color(112);//這裡特判一下'#',應該可以不用 printf("%c",tmap[xx-1][yy+1]); } } gto(G+5,0),color(15),printf("請確認 "); //試探必須額外確認一次,規則上有說 char c=getch(); if(c=='1') { for(int i=1;i<=8;i++) { int xx=x+d[i][0],yy=y+d[i][1]; if(check(xx,yy)) if(tmap[xx-1][yy+1]=='F'&&map1[xx-1][yy+1]!=-1)//試探時有格子的小旗標錯了=>失敗 { Lose=1; return; } } for(int i=1;i<=8;i++) { int xx=x+d[i][0],yy)) if(map1[xx-1][yy+1]==-1&&tmap[xx-1][yy+1]!='F')//試探是有格子為雷=>取消開啟 return; } for(int i=1;i<=8;i++) { int xx=x+d[i][0],yy)&&tmap[xx-1][yy+1]=='#')//開啟周圍8個格子 dfs(xx,yy); } } } void flag(int x,int y) { x-=1,y+=1; if(tmap[x][y]=='F')//原本為小旗=>取消小旗 { tmap[x][y]='#';mapc[x][y]=240; gto(x+1,y-1),color(240),printf("#"); Wnum++;//更新小旗數 } else//否則就放置小旗 { is_flag_true[x][y]=map1[x][y]==-1;//判斷小旗是否放對 tmap[x][y]='F';mapc[x][y]=253; gto(x+1,color(253),printf("F"); Wnum--;//更新小旗數 } gto(1,2*K+10);color(12);printf("剩餘小旗數: "); gto(1,2*K+22);printf("%d",Wnum);//更新小旗數 } void bj(int x,int y)//和放小旗差不多,只是不用更新is_flag_true { x-=1,y+=1; if(tmap[x][y]=='?') { gto(x+1,printf("#"); tmap[x][y]='#';mapc[x][y]=240; } else { if(tmap[x][y]=='F')//如果原本這個位置上是小旗,而你把它變為了標記,就要更新小旗數 { Wnum++; gto(1,2*K+10);color(12);printf("剩餘小旗數: "); gto(1,Wnum); } gto(x+1,printf("?"); tmap[x][y]='?';mapc[x][y]=240; } } void swt(){QR=!QR;} void zb() { if(ZB)//如果本來作弊打開了就把作弊地圖清除 { for(int i=1;i<=G;i++) { gto(i+1,K+2); for(int j=1;j<=K;j++) color(15),printf(" "); } ZB=0; } else//否則列印作弊地圖 { for(int i=1;i<=G;i++) { gto(i+1,K+2); for(int j=1;j<=K;j++) { color(mapc[i][j]); if(map1[i][j]==-1) printf("O"); else if(map1[i][j]>0) printf("%d",map1[i][j]); else printf(" "); } } ZB=1; } } void again() { G=K=Lnum=nx=ny=Lose=ZB=0; QR=0; memset(is_flag_true,sizeof(is_flag_true)); memset(map,sizeof(map)); memset(tmap,sizeof(tmap)); memset(map1,sizeof(map1)); memset(mapc,sizeof(mapc)); color(15); system("cls");//初始化 mainmain(); } void begin()//各種操作 { char c=getch(); gto(G+5,printf("請選擇方塊進行操作"); color(240); if(c=='w'&&check(nx-1,ny)) { gto(nx,ny); if(tmap[nx-1][ny+1]!='#'||tmap[nx-1][ny+1]==' ') color(mapc[nx-1][ny+1]); printf("%c",tmap[nx-1][ny+1]); gto(nx-=1,ny);color(15);printf("@"); } else if(c=='s'&&check(nx+1,ny);if(tmap[nx-1][ny+1]!='#'||tmap[nx-1][ny+1]==' ')color(mapc[nx-1][ny+1]);printf("%c",tmap[nx-1][ny+1]); gto(nx+=1,ny);color(15);printf("@"); } else if(c=='a'&&check(nx,ny-1)) { gto(nx,tmap[nx-1][ny+1]); gto(nx,ny-=1);color(15);printf("@"); } else if(c=='d'&&check(nx,ny+1)) { gto(nx,ny+=1);color(15);printf("@"); } //上下左右移動 else { if(c==' '&&(!(tmap[nx-1][ny+1]<='9'&&tmap[nx-1][ny+1]>='0'))&&tmap[nx-1][ny+1]!='F') { mapc[nx-1][ny+1]=col[map1[nx-1][ny+1]];//如果本來放了標記,mapc[nx-1][ny+1]的顏色為黑色,在開啟時裡面的顏色卻不一定是黑色 if(QR) { gto(G+5,printf("請確認 "); if(getch()==' ') { if(map1[nx-1][ny+1]==-1) {Lose=1;return;} dfs(nx,ny); } } else { if(map1[nx-1][ny+1]==-1) {Lose=1;return;} dfs(nx,ny); } } else if(c=='1') { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='1') st(nx,ny); } else st(nx,ny); for(int i=1;i<=8;i++) { int xx=nx+d[i][0],yy=ny+d[i][1]; if(check(xx,yy)) { gto(xx,yy); if(tmap[xx-1][yy+1]!='#') color(mapc[xx-1][yy+1]); else color(240); printf("%c",tmap[xx-1][yy+1]); } } } else if(c=='2'&&(tmap[nx-1][ny+1]>'9'||tmap[nx-1][ny+1]<'1')) { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='2') flag(nx,ny); } else flag(nx,ny); } else if(c=='3'&&(tmap[nx-1][ny+1]>'9'||tmap[nx-1][ny+1]<'1')) { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='3') bj(nx,ny); } else bj(nx,ny); } else if(c=='4') { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='4') swt(); } else swt(); } else if(c=='5') { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='5') zb(); } else zb(); } else if(c=='0') { if(QR) { gto(G+5,printf("請確認 "); if(getch()=='0') again(); } else again(); } } } void mainmain() { system("mode con cols=120 lines=35");//設定視窗大小 srand((unsigned)time(NULL)); int mode; printf("1.初級\n2.中級\n3.高階\n4.自定義\n"); scanf("%d",&mode);if(mode>4) mode=4; if(mode==1) G=9,K=9,Lnum=10; else if(mode==2) G=16,K=16,Lnum=40; else if(mode==3) G=16,K=30,Lnum=99;//三種等級的引數 else { printf("請輸入雷區高度:");scanf("%d",&G); printf("請輸入雷區寬度:");scanf("%d",&K); printf("請輸入雷個數(建議不超過總大小的三分之一):");scanf("%d",&Lnum); if(G>24) G=24;if(K>30) K=30; if(G<9) G=9;if(K<9) K=9; if(Lnum<10) Lnum=10;if(Lnum>G*K*9/10) Lnum=G*K*9/10; //控制引數的範圍,最後一個if是雷的數量不超過地圖大小的9/10 } make(); print(); while(1) { begin(); bool f1=is_win(),f2=is_lose(); if(f1||f2) { gto(0,0); if(f1) color(202),gto(0,printf("你 贏 了!!是否重來?(y/n)"); if(f2) color(137),printf("你 輸 了!!是否重來?(y/n)");//輸贏 print_real_map(); char c=getch(); if(c=='y'||c=='Y') again(); else { color(15); system("cls"); gto(MIDX,MIDY-5); printf("歡迎下次再來"); return; } } } } void print_real_map() { color(240); for(int i=1;i<=G;i++) { gto(i+1,0); for(int j=1;j<=K;j++) { if(tmap[i][j]=='F'&&is_flag_true[i][j]==0)//如果旗標錯了顯示紅色的X color(252),printf("X"); else if(map1[i][j]==-1)//雷為黑色O color(240),printf("O"); else if(map1[i][j]==0)//空 color(240),printf(" "); else//數字 color(mapc[i][j]),map1[i][j]); } } }
更多精彩遊戲小程式碼,請點選《遊戲專題》閱讀
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。