小老鼠走迷宮
阿新 • • 發佈:2019-01-05
程式用C語言編寫,在Visual Studio中除錯。要求在規定時間內完成遊戲小鼠走迷宮,找到糧食,小鼠形象可辨認,可以在迷宮中上下左右移動,可以手動生成迷宮,編輯迷宮,並實現牆變路、路變牆、顯示所有路徑、顯示最短路徑。
輸入的形式:通過輸入字元“w 、s 、 a、 d”來控制小鼠的上下左右移動,“0、1、2、3”代表通路、老鼠、牆、糧。0~7共八個操作編號。
輸出的形式:符號 “▉” 和 “の” 分別表示牆體和可通路徑,老鼠和糧食用漢字“鼠”,“糧”表示
程式所能達到的功能:可進行規定時間內老鼠走迷宮,老鼠形態動態可辨認;可以手動生成迷宮,修改迷宮,儲存迷宮,顯示新建迷宮;可以顯示最短路徑,所有路徑。
主要模組
遊戲模組:void game()
新建地圖模組:void create_plat()
檢視地圖模組:void show_plat()
修改地圖模組:void alter_plat()
讀取地圖模組:void read_plat()
最短路徑模組:void path_best(int p, int q, int l)
所有路徑模組:void path_result(int p, int q)
時間模組:time(null)
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<time.h> #define M 9 #define N 9 //0代表通路,2代表牆體,1是老鼠位置,3是糧倉位置 int maze[M][N] = { { 2,2,2,2,2,2,2,2,2 }, { 2,0,0,0,2,0,0,0,2 }, { 2,0,2,0,0,0,2,2,2 }, { 2,0,0,2,2,0,0,0,2 }, { 2,0,0,0,1,2,2,0,2 }, { 2,0,2,0,2,0,0,0,2 }, { 2,0,2,0,2,0,0,2,2 }, { 2,0,0,0,0,0,0,3,2 }, { 2,2,2,2,2,2,2,2,2 } }; struct best //用於記錄最短路徑的結構體 { int min; int b[M][N]; }Best; int m = 4, n = 4; //老鼠的初始位置 int p = 4, q = 4; //老鼠位置二次定位 int x = 7, y = 7; //糧倉初始位置 int g, answer; //g:統計有效路徑,防止路徑過多,造成迴圈過度; //answer:統計該地圖的所有可能性; void game() //走迷宮遊戲,包含時間統計,以及對老鼠的移動控制,遊戲模組 { int i, j, v = 0, t = 0; //t為初始時間 //v代表移動成功後,v加1,否則加0 char str; //存放字元w,s,a,d time_t start, end; start = time(NULL); maze[x][y] = 3; //放糧倉的位置 m = p; n = q; maze[m][n] = 1; //重新定位老鼠位置 printf("顯示迷宮:\n"); //地圖初始化 for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { if (maze[i][j] == 2) { printf("▉"); } else if (maze[i][j] == 1) { printf("鼠"); } else if (maze[i][j] == 3) { printf("糧"); } else { printf(" "); } } printf("\n"); } printf("(按w↑s↓a← d→移動)\n請在60秒內通關\n"); //初始化地圖 while (1) { printf("時間:%d\r", t); if (kbhit()) //輸入控制 //如果有輸入就返回非零值,否則返回0. { str = getch(); //接受使用者從鍵盤輸入的字元,並存入str中 if (str == 'w') //上 { if (maze[m - 1][n] == 0) { maze[m - 1][n] = 1; maze[m][n] = 0; m = m - 1; } else if (maze[m - 1][n] == 3) { maze[m][n] = 0; v = 1; } } else if (str == 's') //下 { if (maze[m + 1][n] == 0) { maze[m + 1][n] = 1; maze[m][n] = 0; m = m + 1; } else if (maze[m + 1][n] == 3) { maze[m][n] = 0; v = 1; } } else if (str == 'a') //左 { if (maze[m][n - 1] == 0) { maze[m][n - 1] = 1; maze[m][n] = 0; n = n - 1; } else if (maze[m][n - 1] == 3) { maze[m][n] = 0; v = 1; } } else if (str == 'd') //右 { if (maze[m][n + 1] == 0) { maze[m][n + 1] = 1; maze[m][n] = 0; n = n + 1; } else if (maze[m][n + 1] == 3) { maze[m][n] = 0; v = 1; } } else; system("cls"); //cls是為了清屏 printf("顯示迷宮:\n"); //顯示遊戲地圖 for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { if (maze[i][j] == 2) { printf("▉"); } else if (maze[i][j] == 1) { printf("鼠"); } else if (maze[i][j] == 3) { printf("糧"); } else { printf(" "); } } printf("\n"); } printf("(按w↑s↓a← d→移動)\n請在60秒內通關\n"); } else; if (v == 1) //判斷是否通關 { printf("\n恭喜通關!\n"); system("pause"); break; } if (t>60) //規定時間到後結束遊戲 { printf("\n未在規定時間內通關,遊戲失敗。\n"); maze[m][n] = 0; //清除最後所在位置 system("pause"); break; } end = time(NULL); t = difftime(end, start); } } void create_plat() //新建地圖,並存入檔案的模組 { int i, j, s; cc: printf("請輸入0和2,0代表通路,2代表牆體(數字用空格隔開),輸入規格是%dx%d,有邊緣牆體無需再次輸入\n", M - 2, M - 2); for (i = 1; i <= M - 2; i++) { printf("第%d行:", i); for (j = 1; j <= N - 2; j++) { scanf("%d", &s); if (s == 0 || s == 2) maze[i][j] = s; else { system("cls"); //清屏 printf("輸入錯誤請重新輸入!\n"); goto cc; //跳轉到cc去做執行 } } } aa: printf("請設定老鼠的初始位置x,y即行列(1~%d,1~%d):\n", M - 2, N - 2); for (i = 0; i<30; i++) fflush(stdin); //清除來自鍵盤的30多個快取字元,防止死迴圈bug scanf("%d,%d", &p, &q); if (p <= (M - 2) && q <= (M - 2) && p>0 && q>0) maze[p][q] = 1; else { system("cls"); //清屏 printf("輸入錯誤,請重新輸入,在%dx%d的範圍內。\n", M - 2, N - 2); goto aa; } bb: printf("請設定糧倉的位置x,y:\n"); //清除來自鍵盤的30多個快取字元,防止死迴圈bug for (i = 0; i<30; i++) fflush(stdin); scanf("%d,%d", &x, &y); if (x <= (M - 2) && y <= (N - 2) && x>0 && y>0 && (x != p || y != q)) maze[x][y] = 3; else { system("cls"); printf("輸入錯誤,請重新輸入,在%dx%d的範圍內。\n", M - 2, N - 2); goto bb; } //檔案儲存地圖 FILE *fp; fp = fopen("plat.txt", "w"); for (i = 0; i<M; i++) { for (j = 0; j<N; j++) fprintf(fp, "%d\t", maze[i][j]); fprintf(fp, "%c", '\n'); } fprintf(fp, "%d\t", p); fprintf(fp, "%d\t", q); fprintf(fp, "%d\t", x); fprintf(fp, "%d\t", y); fclose(fp); printf("地圖新建完成,並儲存成功!\n"); system("pause"); } void show_plat() //地圖展示模組 { int i, j; system("cls"); maze[x][y] = 3; //糧倉位置 m = p; n = q; maze[m][n] = 1; //老鼠位置 printf("顯示迷宮:\n"); //顯示遊戲地圖 for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { if (maze[i][j] == 2) { printf("▉"); } else if (maze[i][j] == 1) { printf("鼠"); } else if (maze[i][j] == 3) { printf("糧"); } else { printf(" "); } } printf("\n"); } printf("已顯示地圖\n"); system("pause"); } void alter_plat() //修改地圖模組 { int i, j, select, a, b; FILE *fp; while (1) { system("cls"); m = p; n = q; maze[m][n] = 1; printf("顯示迷宮:\n"); //顯示遊戲地圖 for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { if (maze[i][j] == 2) { printf("▉"); } else if (maze[i][j] == 1) { printf("鼠"); } else if (maze[i][j] == 3) { printf("糧"); } else { printf(" "); } } printf("\n"); } printf(" =============----修改地圖------===================\n"); printf(" |請選擇: |\n"); printf(" | 1.修改為牆體; |\n"); printf(" | 2.修改為通路; |\n"); printf(" | 3.儲存修改地圖; |\n"); printf(" | 0.退出修改功能; |\n"); printf(" ===========(請輸入相應數字執行其功能)===========\n"); fflush(stdin); //清除鍵入的快取 scanf("%d", &select); if (select<4 && select >= 0) { switch (select) { case 1: printf("圍牆內為修改範圍,範圍是%dx%d\n", M - 2, N - 2); printf("請輸入座標x,y(行,列)修改地圖:\n"); //清除來自鍵盤的30多個快取字元,防止死迴圈bug for (i = 0; i<30; i++) fflush(stdin); scanf("%d,%d", &a, &b); if (a <= (M - 2) && b <= (N - 2) && a>0 && b>0 && maze[a][b] != 1 && maze[a][b] != 3) maze[a][b] = 2; else { printf("輸入錯誤請重新輸入,不能在圍牆、糧倉和老鼠的位置修改!\n"); system("pause"); } break; case 2: printf("圍牆內為修改範圍,範圍是%dx%d\n", M - 2, N - 2); printf("請輸入座標x,y(行,列)修改地圖:\n"); //清除來自鍵盤的30多個快取字元,防止死迴圈bug for (i = 0; i<30; i++) fflush(stdin); scanf("%d,%d", &a, &b); if (a <= (M - 2) && b <= (N - 2) && a>0 && b>0 && maze[a][b] != 1 && maze[a][b] != 3) maze[a][b] = 0; else { printf("輸入錯誤請重新輸入,不能在圍牆、糧倉和老鼠的位置修改!\n"); system("pause"); } break; case 3: { //檔案形式儲存修改後地圖 fp = fopen("plat.txt", "w"); for (i = 0; i<M; i++) { for (j = 0; j<N; j++) fprintf(fp, "%d\t", maze[i][j]); fprintf(fp, "%c", '\n'); } fprintf(fp, "%d\t", p); fprintf(fp, "%d\t", q); fprintf(fp, "%d\t", x); fprintf(fp, "%d\t", y); fclose(fp); printf("地圖修改完成,並儲存成功!\n"); system("pause"); } break; case 0: break; } } else { printf("請按規定輸入!\n"); system("pause"); } if (select == 0) break; } } void read_plat() //讀取檔案裡的地圖模組 { int i, j; FILE *fp; fp = fopen("plat.txt", "r"); if (!fp) { printf("檔案不存在,請重新開啟!\n"); system("pause"); } else { for (i = 0; i<M; i++) { for (j = 0; j<N; j++) fscanf(fp, "%d\t", &maze[i][j]); fscanf(fp, "\n"); } fscanf(fp, "%d\t", &p); fscanf(fp, "%d\t", &q); //讀取老鼠的位置 fscanf(fp, "%d\t", &x); fscanf(fp, "%d\t", &y); //讀取糧倉的位置 printf("讀取成功,請檢視新地圖!\n"); system("pause"); } fclose(fp); } void path_result(int p, int q) //統計地圖的所有可能出現的路徑結果,並把次數賦給全域性變數answer //並在path_result函式中引用變數,作為遞迴終止和顯示最優解的條件 { int i, j; maze[p][q] = 1; if (maze[p][q + 1] == 3 || maze[p][q - 1] == 3 || maze[p + 1][q] == 3 || maze[p - 1][q] == 3) //判斷當前位置上下左右是否有糧倉 { ++answer; //統計一個地圖的所有可能性的次數 printf("已計算%d次,若超過1 h,請重啟\r", answer); } // 遞迴部分,即尋找路徑的部分 if (maze[p][q + 1] == 0) path_result(p, q + 1); //右 if (maze[p + 1][q] == 0) path_result(p + 1, q); //下 if (maze[p][q - 1] == 0) path_result(p, q - 1); //左 if (maze[p - 1][q] == 0) path_result(p - 1, q); //上 maze[p][q] = 0; } void path_best(int p, int q, int l) //顯示最優路徑的模組 { int i, j, c, k; maze[p][q] = 1; if (maze[p][q + 1] == 3 || maze[p][q - 1] == 3 || maze[p + 1][q] == 3 || maze[p - 1][q] == 3) //判斷當前位置上下左右是否有糧倉 { if (Best.min>l) { Best.min = l; for (i = 0; i < M; i++) for (j = 0; j< N; j++) Best.b[i][j] = maze[i][j]; } g++; k = ((double)g / answer) / 2 * 100 + 50; //計算後面一半完成的百分比情況 printf("已完成%d%%,請繼續等待\r", k); c = g; if (c == answer) //防止過度迴圈,比較answer條路徑。 { printf("已列舉最優解!%d個方案中的最優解\n", answer); for (i = 0; i < M; i++) { for (j = 0; j< N; j++) if (Best.b[i][j] == 2) { printf("█"); } else if (Best.b[i][j] == 1) printf("の"); else if (Best.b[i][j] == 3) printf("糧"); else printf(" "); printf("\n"); } printf("已顯示最優解,方案如上!\n"); maze[p][q] = 0; return; exit(0); } } ++l; if (g != answer) { if (maze[p][q + 1] == 0) path_best(p, q + 1, l); //右 if (maze[p + 1][q] == 0) path_best(p + 1, q, l); //下 if (maze[p][q - 1] == 0) path_best(p, q - 1, l); //左 if (maze[p - 1][q] == 0) path_best(p - 1, q, l); //上 } maze[p][q] = 0; } void path_find(int p, int q) //尋找通關路徑的模組 { int i, j, c; maze[p][q] = 1; c = g; if (c == 15) //防止過度迴圈,暫時只能顯示15條路徑,可調節。 { printf("已列舉10種可行方案!!方案太多可能列舉不完,所以最多列舉15個\r"); maze[p][q] = 0; return; // exit(0); } if (maze[p][q + 1] == 3 || maze[p][q - 1] == 3 || maze[p + 1][q] == 3 || maze[p - 1][q] == 3) //判斷當前位置上下左右是否有糧倉 { printf("顯示路徑: \n"); for (i = 0; i < M; i++) { for (j = 0; j< N; j++) { if (maze[i][j] == 2) { printf("█"); } else if (maze[i][j] == 1) printf("の"); else if (maze[i][j] == 3) printf("糧"); else printf(" "); } printf("\n"); } printf("已顯示方案,如上!\n"); g++; } if (g != 15) { if (maze[p][q + 1] == 0) path_find(p, q + 1); //右 if (maze[p + 1][q] == 0) path_find(p + 1, q); //下 if (maze[p][q - 1] == 0) path_find(p, q - 1); //左 if (maze[p - 1][q] == 0) path_find(p - 1, q); //上 } maze[p][q] = 0; } void main() //主函式,選單控制介面 { //全域性變數:m,n老鼠位置,p,q老鼠初始位置x,y糧倉的初始位置 //g統計所有有效路徑的次數,最多十次,防止因為路徑過多而死迴圈; int select, k; time_t start, end; int hour, minute, second, t; //時間變數 while (1) { system("cls"); printf(" =============老鼠走迷宮遊戲 n.0===================\n"); printf(" |請選擇: |\n"); printf(" | 1.開始遊戲; |\n"); printf(" | 2.新建地圖; |\n"); printf(" | 3.檢視地圖; |\n"); printf(" | 4.修改地圖; |\n"); printf(" | 5.讀取地圖; |\n"); printf(" | 6.顯示最短路徑; |\n"); printf(" | 7.顯示通關所有路徑; |\n"); printf(" | 0.退出系統; |\n"); printf(" ===========(請輸入相應數字執行其功能)===========\n"); printf(" 注意事項:\n 地圖不要全空,否則不要執行6,7選項,會運算十億次花費很長時間,儘量把通關所有路徑設定在一億種以內!\n"); for (k = 0; k<30; k++) fflush(stdin); //清除鍵盤輸入的scanf快取,防止死迴圈 scanf("%d", &select); if (select >= 0 && select<8) //鍵盤輸入檢錯 { switch (select) { case 0: exit(0); case 1: system("cls"); //清除選單 game(); //開始遊戲 break; case 2: create_plat(); //新建地圖 break; case 3: show_plat(); //顯示地圖 break; case 4: alter_plat(); //修改地圖 break; case 5: read_plat(); //在檔案裡讀取地圖 break; case 6: g = 0; Best.min = 999; answer = 0; start = time(NULL); path_result(p, q); //統計地圖所有解的個數 path_best(p, q, 1); //尋找最短的路徑 end = time(NULL); t = difftime(end, start); second = t % 60; //求出秒 minute = t / 60; //總分數 hour = minute / 60; //求出小時 minute = minute % 60; //求出分 printf("花費時間:%dh %dm %ds", hour, minute, second); //顯示尋找最短路徑所花費的時間 system("pause"); break; case 7: g = 0; path_find(p, q); //尋找所有通關路徑 printf("已顯示所有路徑! \n"); system("pause"); break; default: break; } } else { printf("請按規定輸入!\n"); system("pause"); } } }