1. 程式人生 > >小老鼠走迷宮

小老鼠走迷宮

程式用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");
		}
	}
}