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秒
唱歌跟着別人唱更容易跑調……(鏈接找不到了