JAVA|迷宮問題輸出最短路徑

0.背景

本學期Java課程設計選題爲迷宮遊戲。

課設的迷宮主要思路爲:設置不同關卡,每個關卡的迷宮樣式固定,每次遊戲隨機生成起點和終點。
核心問題即爲:
1、隨機生成的兩點之間是否有路徑可走?
2、最短路徑是否小於等於遊戲給出的步數限制?
3、點擊最短生成路徑按鈕顯示最短路徑。

遊戲的整體框架戳此處鏈接https://blog.csdn.net/qq_42391904/article/details/83002785
原作者代碼中並未考慮以上的三個問題,下面仔細講解。

一、遊戲整體框架

具體代碼戳上面鏈接。
主要思路爲設置兩個數組,data[][]和label[][],data[][]存放迷宮數據(邏輯層),label[][]存放迷宮圖片(圖片層)。如data[][]數組路0牆1,則label[][]覆蓋相應的圖片。

二、隨機生成兩點路徑並求最短路徑

初始化地圖數據層

// 初始化關卡地圖
	private void initMapData() {
		//讀取行列數
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("src/labyrinth/simple.txt")));
			row = 0;
		    while(reader.ready()) {
		    	row = row + 1;
		        String line = reader.readLine();
		        column = line.length();
		    }
	        System.out.println("row: " + row + " ,column: " + column);
		} catch (FileNotFoundException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		} catch (IOException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		
		//更新迷宮數組大小,邏輯層數組labyrinthData,圖形層數組labyrinthLable。行列即爲讀取的txt行列。
		labyrinthData = new int[row][column];// 邏輯上的迷宮
		labyrinthLable = new JLabel[column][row]; // 圖像顯示上的迷宮
		newData = new int[row][column]; // 定義newData2數組用於暫存移動前中的數組(用於數組是否變化)
		
		// 從文件中讀取數據保存到labyrinthData數組中
		// i(37)行j(47)列(frame中縱橫與數組相反)
		String[] line = new String[row];
		int m = 0;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src/labyrinth/simple.txt")));
			String line1;
			while ((line1 = br.readLine()) != null) {
				// System.out.println(line1);//每行輸出
				line[m] = line1;
				m++;
			} // while
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 將String轉化爲整型,放入labyrinthData數組中
		for (int j = 0; j < row; j++) {
			for (int i = 0; i < column; i++) {
				String s2 = line[j].substring(i, i + 1);
				labyrinthData[j][i] = Integer.parseInt(s2);
				labyrinthData[j][i] = Integer.valueOf(s2).intValue();
			}
		}
		
		//隨機生成起點和終點。判斷是否有路徑,且最短路徑小於initStep。
		genStartEnd(labyrinthData,row,column);
		//滿足最短路徑條件,設置該兩點爲起點和終點。
	}

隨機生成兩點,判斷是否有路,求最短路徑

private void genStartEnd(int data[][], int row,int column) {
		// 隨機產生兩個座標(出生地和終點)
		Random random = new Random();
		int sx, sy, ex, ey;	//sx.sy起點的座標;ex.ey終點的座標
		do {
		sx=random.nextInt(row);
		sy=random.nextInt(column);
		} while (data[sx][sy] == 1);
		do {
			ex=random.nextInt(row);
			ey=random.nextInt(column);
		} while (data[ex][ey] == 1);
		if(isPath(data, sx, sy, ex, ey)) {
			data[sx][sy]=2;
			data[ex][ey]=5;
		}
		else
			genStartEnd(data, row, column);
	}
	
	private boolean isPath(int data[][], int x1, int y1, int x2, int y2) {
		//將起點和終點存入兩個數組
		int begin[] = {x1, y1};
		int end[] = {x2, y2};
		//移動的四個方向
		int dx[] = {1, 0, -1, 0};
		int dy[] = {0, 1, 0, -1};
		//用來儲存距離到起始點最短路徑的二維數組
		int d[][] = new int [data.length][data[0].length];
		//儲存未進行處理的點
		Queue<int []> que = new LinkedList<int []>();
		//將所有的位置都初始化爲最大
		for(int i = 0; i < d.length; i++) {
			for(int j = 0; j < d[0].length; j++) {
				d[i][j] = Integer.MAX_VALUE;
			}
		}
		//將起始點放入隊列
		que.offer(begin);
		//將起始點最短路徑設爲0
		d[ begin[0] ][ begin[1] ] = 0;
		//一直循環直到隊列爲空
		while(!que.isEmpty()) {
			//取出隊列中最前端的點
			int current[] = que.poll();
			//如果是終點則結束
			if(current[0] == end[0] && current[1] == end[1]) break;
			//四個方向循環
			for(int i = 0; i < 4; i++) {
				//試探
				int ny = current[0] + dy[i];
				int nx = current[1] + dx[i];
				//判斷是否可以走
				if(ny >= 0 && ny < d.length && nx >= 0 && nx < d[0].length && data[ny][nx] == 0 && d[ny][nx] == Integer.MAX_VALUE) {
					//如果可以走,則將該點的距離加1
					d[ny][nx] = d[current[0]][current[1]] + 1;
					//並將該點放入隊列等待下次處理
					int[] c = {ny, nx};
					que.offer(c);				
				}
			}
		}
		//如果存在最短路徑,且最短路徑小於等於initStep。滿足條件。
		if(d[end[0]][end[1]]!=0 && d[end[0]][end[1]]<= maze_name.initStep) {
			System.out.println("最短路徑爲:"+d[end[0]][end[1]]);
			return true;
		}
		else
			return false;
	}

主要思路:從起點開始,遍歷上下左右四個點,未訪問且不是牆,則入隊列,類推。

三、輸出最短路徑

迷宮圖片層

// 定義模式主題標題
	private void initType2Frame() {// 道具模式logo
		JLabel typeShow21 = new JLabel();
		typeShow21.setIcon(smalltype2Icon);
		typeShow21.setBounds(280, 0, 240, 60);
		this.add(typeShow21);
		// 模式介紹
		JLabel typeShow22 = new JLabel();
		typeShow22.setIcon(typeInfor2Icon);
		typeShow22.setBounds(760, 60, 260, 200);
		this.add(typeShow22);
		// 模式介紹
		JLabel typeShow24 = new JLabel();
		typeShow24.setIcon(step);
		typeShow24.setBounds(760, 210, 140, 100);
		this.add(typeShow24);
		// 剩餘步數顯示
		typeShow23 = new JLabel("");
		Font font = new Font("Verdana", Font.BOLD, 40); // 創建1個字體實例Font.PLAIN
		typeShow23.setFont(font); // 設置JLabel的字體
		typeShow23.setForeground(Color.darkGray); // 設置文字的顏色
		typeShow23.setBounds(760, 230, 150, 150);
		this.add(typeShow23);
		// 道具數據初始化
		Random random = new Random();
		int x, y;
		// 5個隨機寶石(紅藍)(數據上)
		for (int i = 0; i < 5; i++) {
			do {
				x = random.nextInt(row);
				y = random.nextInt(column);
			} while (labyrinthData[x][y] == 1 || labyrinthData[x][y] == 5);// 出生地合理性(若隨機到牆或者終點,重新生成一次)
			labyrinthData[x][y] = random.nextInt(2) + 3;// 設置3,4爲道具數據(3代表藍寶石,4代表紅寶石)
		}		
		int px,py;
		for (int i = 0; i < 1; i++) {
			do {
				px = random.nextInt(row);
				py = random.nextInt(column);
			} while (labyrinthData[px][py] !=0 );// 出生地合理性
			labyrinthData[px][py] = 6;
		}
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < column; j++) {
				if (labyrinthData[i][j] == 3 || labyrinthData[i][j] == 4) {
					switch (labyrinthData[i][j]) {	//3代表藍寶石,4代表紅寶石
					case 3:
						stone = new ImageIcon("pic/stone1.png");
						break;
					case 4:
						stone = new ImageIcon("pic/stone2.png");
						break;
					}// switch
					labyrinthLable[j][i] = new JLabel(stone);
					labyrinthLable[j][i].setBounds(20 + j * 20, 50 + i * 20, 20, 20);
					this.add(labyrinthLable[j][i]);
				}
				if (labyrinthData[i][j] == 6) {
					police = new ImageIcon("pic/police.jpg");
					labyrinthLable[j][i] = new JLabel(police);
					labyrinthLable[j][i].setBounds(20 + j * 20, 50 + i * 20, 20, 20);
					this.add(labyrinthLable[j][i]);
				}
			} // for內層
		} // for外層
		//“顯示最短路徑”按鈕
		JButton btn_minPath =new JButton();
		btn_minPath.setBounds(850, 400, 150, 50);
		btn_minPath.setText("顯示最短路徑");
		btn_minPath.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				//將起點到終點的最短路徑填充小圖片
				//初始化起點和終點
				node begin = new node();
				node end = new node();
				for(int i=0; i<row; i++) {
					for(int j=0; j<column; j++) {
						if(labyrinthData[i][j] == 2) {
							begin.x = i;
							begin.y = j;
							begin.pre = null;
							begin.distance = 0;
						}
						if(labyrinthData[i][j] == 5) {
							end.x = i;
							end.y = j;
							end.pre = null;
							end.distance = 0;
						}
						
					}
				}	
	 			bfs(begin, end);
	  		    showMinPath(begin, end);
	  			btn_minPath.setFocusable(false);
			}
		});

		this.add(btn_minPath);
		this.setFocusable(true);
	}

點擊按鈕後,效果如下,藍色圖片爲牆,小黃人爲起點,王冠爲終點,燈爲路徑。
最短路徑效果圖
自定義節點類

//定義node類,存放點座標、前驅節點、最短路徑長。x爲橫座標,y爲縱座標。
	class node{
		int x;
		int y;
		node pre;
		int distance;
	}

深搜找最短路徑

private void bfs(node begin, node end) {
		System.out.println("bigin.x:" + begin.x + ",begin.y:" + begin.y);
		System.out.println("end.x:" + end.x + ",end.y:" + end.y);
		//迷宮的四個方向
		int dx[] = {1, 0, -1, 0};
		int dy[] = {0, 1, 0, -1};
		
		//用來標記起點到終點的所有點是否訪問
		int flag[][] = new int [labyrinthData.length][labyrinthData[0].length];
		//將所有的位置都初始化爲未訪問,即0
		for(int i = 0; i < flag.length; i++) {
			for(int j = 0; j < flag[0].length; j++) {
				flag[i][j] = 0;
			}
		}
		
		//將起始點放入隊列
		Queue<node> que = new LinkedList<node>();
		que.offer(begin);
		flag[begin.x][begin.y] = 1;
		//一直循環直到隊列爲空
		while(!que.isEmpty()) {
			//取出隊列中最前端的點
			node current = que.peek();
			//System.out.println("current.x:" + current.x + ",current.y:" + current.y);
			que.poll();
			//如果是終點則結束
			if(current.x == end.x && current.y == end.y) {
				end.pre = current.pre;
				break; 
			}
			//四個方向循環
			for(int i = 0; i < 4; i++) {
				//找出下一個節點
				node next = new node();
				next.x = current.x + dx[i];
				next.y = current.y + dy[i];
				//判斷是否可以走
				if(next.x >= 0 && next.x < labyrinthData.length && next.y >= 0 && next.y < labyrinthData[0].length && labyrinthData[next.x][next.y] != 1) {
					//如果未訪問,設爲已訪問,入隊列,設置前趨節點
					if(flag[next.x][next.y] == 0) {
						flag[next.x][next.y] = 1;
						que.offer(next);
						next.pre = current;
						next.distance = current.distance + 1;
						//System.out.println("(" + next.x + "," + next.y + ")");
					}
					//如果已訪問
					else {
						if(current.distance + 1 <= next.distance) {
							next.distance = current.distance + 1;
							next.pre = current;
						}
					}
				}
			}
		}
	}

倒序輸出路徑

private void showMinPath(node begin, node end) {
		System.out.println("*******************************************************");
		System.out.println("進入showMinPath函數");
		System.out.println("倒序輸出路徑(不含起點終點)");		
		
		do {
			System.out.println(end.pre.x + "," + end.pre.y);
			end.x = end.pre.x;
			end.y = end.pre.y;
			end = end.pre;
			labyrinthLable[end.y][end.x].setIcon(helpLight);
			//labyrinthLable[end.y][end.x].setBounds(20 + end.y * 20, 50 + end.x * 20, 20, 20);
			//this.add(labyrinthLable[end.y][end.x]);
		}while(end.pre != begin);
		
		//顯示路徑後,恢復原有紅寶石和藍寶石,藍寶石3,紅寶石4
		for(int i = 0; i < row; i++) {
			for(int j = 0; j < column; j++) {
				if(labyrinthData[i][j] == 3) {	//藍寶石
					stone = new ImageIcon("pic/stone1.png");
					labyrinthLable[j][i].setIcon(stone);
					//labyrinthLable[j][i].setBounds(20 + j * 20, 50 + i * 20, 20, 20);
					//this.add(labyrinthLable[j][i]);
				}
				if(labyrinthData[i][j] == 4) {	//紅寶石
					stone = new ImageIcon("pic/stone2.png");
					labyrinthLable[j][i].setIcon(stone);
					//labyrinthLable[j][i].setBounds(20 + j * 20, 50 + i * 20, 20, 20);
					//this.add(labyrinthLable[j][i]);
				}
			}
		}

		//逆序輸出,即逆序顯示圖片
		if(end.pre == null) {
			System.out.println("end前趨節點爲空");
			return;
		}

	}

四、鹹魚最近一週學的英語……

1、BBC 6MinuteEnglish
每日英語聽力鏈接
https://dict.eudic.net/webting/play?id=f17f4f34-1824-11ea-8356-00505686c5e6&order=0
在這裏插入圖片描述
2、科學60秒
唱歌跟着別人唱更容易跑調……(鏈接找不到了
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章