随机迷宫生成
随机Prim方法
package com.company; import java.util.Random; class Maze { // 初始化一个地图 默认所有路不通 //最终产生的二维数组大小实际为(2width+1) * (2height+1) private static int width; private static int height; public static int[][] map;// 存放迷宫的数组 private static int r; private static int c; Maze(int r0, int c0) { width = r0; height = c0; r = 2 * width + 1; c = 2 * height + 1; map = new int[r][c]; } public static int[][] Init() { for (int i = 0; i < r; i++) // 将所有格子都设为墙 for (int j = 0; j < c; j++) map[i][j] = 0;// 0 为墙 1为路 // 中间格子放为1 for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) map[2 * i + 1][2 * j + 1] = 1;// 0 为墙 1为路 // 普里姆算法 rdPrime(); return map; } public static void rdPrime() { // ok存放已访问队列,not存放没有访问队列 int[] ok, not; int sum = width * height; int count = 0;// 记录访问过点的数量 ok = new int[sum]; not = new int[sum]; // width上各方向的偏移 height各方向的偏移 0左 1右 3上 2下 int[] offR = {-1, 1, 0, 0}; int[] offC = {0, 0, 1, -1}; // 四个方向的偏移 左右上下 int[] offS = {-1, 1, width, -width}; // 向上向下移动都是变化一行 // 初始化 ok中0代表未访问,not中0代表未访问 for (int i = 0; i < sum; i++) { ok[i] =0; not[i] = 0; } // 起点 Random rd = new Random(); ok[0] = rd.nextInt(sum);// 起始点 int pos = ok[0]; // 第一个点存入 not[pos] = 1; while (count < sum) { // 取出现在的点 int x = pos % width; int y = pos / width;// 该点的坐标 int offpos = -1; int w = 0; // 四个方向都尝试一遍 直到挖通为止 while (++w < 5) { // 随机访问最近的点 int point = rd.nextInt(4); // 0-3 int repos; int move_x, move_y; // 计算出移动方位 repos = pos + offS[point];// 移动后的下标 move_x = x + offR[point];// 移动后的方位 move_y = y + offC[point]; if (move_y >= 0 && move_x >= 0 && move_x < width && move_y < height && repos >= 0 && repos < sum && not[repos] != 1) { not[repos] = 1;// 把该点标记为已访问 ok[++count] = repos;// ++count代表第几个已经访问的点,repos代表该点的下标 pos = repos;// 把该点作为起点 offpos = point; // 相邻的格子中间的位置放1 map[2 * x + 1 + offR[point]][2 * y + 1 + offC[point]] = 1; break; } else { if (count == sum - 1) return; } } if (offpos < 0) {// 周边没有找到能走的路了 从走过的路里重新找个起点 pos = ok[rd.nextInt(count + 1)]; } } }
}
A*算法
package com.company; import java.util.ArrayList; import java.util.List; class AStar { public static int[][] NODES;//定义一个迷宫单元数组 public int STEP = 10;//设每一步的权值为10 private ArrayListopenList = new ArrayList ();//维护一个开放列表 private ArrayList closeList = new ArrayList ();//维护一个关闭列表 AStar(int[][] map) { NODES=map;//初始化迷宫单元为新生成的对应地图,把Maze类里面生成的地图传给NODES,再在此地图基础上用A*算法寻路径 Node startNode = new Node(1, 1);//起点 Node endNode = new Node(map.length-2, map.length-2);//终点 Node parent = findPath(startNode, endNode); //父节点 ArrayList arrayList = new ArrayList (); while (parent != null) { arrayList.add(new Node(parent.x, parent.y)); parent = parent.parent; } //打印有路径的地图,在控制台输出查看 System.out.println("\n"+"打印有路径的地图:"); for (int i = 0; i < NODES.length; i++) { for (int j = 0; j < NODES.length; j++) { if (exists(arrayList, i, j)) { NODES[i][j]=2;//标记关闭列表里的方格为2,为了方便后面在界面画系统寻路路径 } //System.out.print(NODES[i][j] + " "); } //System.out.println(); } } public static int[][] ans(){ return NODES; } //寻找开放列表里F值最小的节点的方法 public Node findMinFNodeInOpneList() { Node tempNode = openList.get(0); for (Node node : openList) { if (node.F < tempNode.F) { tempNode = node; } } return tempNode; } //遍历当前节点上下左右四个邻居的方法, public ArrayList findNeighborNodes(Node currentNode) { ArrayList arrayList = new ArrayList (); // 只考虑上下左右,不考虑斜对角 int topX = currentNode.x; int topY = currentNode.y - 1; if (canReach(topX, topY) && !exists(closeList, topX, topY)) { arrayList.add(new Node(topX, topY)); } int bottomX = currentNode.x; int bottomY = currentNode.y + 1; if (canReach(bottomX, bottomY) && !exists(closeList, bottomX, bottomY)) { arrayList.add(new Node(bottomX, bottomY)); } int leftX = currentNode.x - 1; int leftY = currentNode.y; if (canReach(leftX, leftY) && !exists(closeList, leftX, leftY)) { arrayList.add(new Node(leftX, leftY)); } int rightX = currentNode.x + 1; int rightY = currentNode.y; if (canReach(rightX, rightY) && !exists(closeList, rightX, rightY)) { arrayList.add(new Node(rightX, rightY)); } return arrayList; } //判断此处坐标是否可达,若超界或者是墙则不可达 public boolean canReach(int x, int y) { if (x >=0 && x < NODES.length && y >=0 && y < NODES.length && NODES[x][y]==1) { return true; } return false; } //A*寻路过程 public Node findPath(Node startNode, Node endNode) { openList.add(startNode);// 把起点加入 open list while (openList.size() > 0) { Node currentNode = findMinFNodeInOpneList();// 遍历 open list ,查找 F值最小的节点,把它作为当前要处理的节点 openList.remove(currentNode);// 从open list中移除 closeList.add(currentNode);// 把这个节点移到 close list ArrayList neighborNodes = findNeighborNodes(currentNode); for (Node node : neighborNodes) {//遍历四个邻居 if (exists(openList, node)) { foundPoint(currentNode, node); } else { notFoundPoint(currentNode, endNode, node); } } if (find(openList, endNode) != null) { return find(openList, endNode);//找到终点了并返回 } } return find(openList, endNode); } //在列表里可以找到节点后的情况 private void foundPoint(Node tempStart, Node node) { int G = calcG(tempStart, node); if (G < node.G) { node.parent = tempStart; node.G = G; node.calcF(); } } //在节点里找不到节点的情况 private void notFoundPoint(Node tempStart, Node end, Node node) { node.parent = tempStart; node.G = calcG(tempStart, node); node.H = calcH(end, node); node.calcF(); openList.add(node); } //计算G值的方法 private int calcG(Node start, Node node) { int G = STEP; int parentG = node.parent != null ? node.parent.G : 0; return G + parentG; } //计算H值的方法 private int calcH(Node end, Node node) { int step = Math.abs(node.x - end.x) + Math.abs(node.y - end.y); return step * STEP; } //找到终点的方法 public static Node find(List nodes, Node point) { for (Node n : nodes) if ((n.x == point.x) && (n.y == point.y)) { return n; } return null; } //下面两个是exist方法的重载,判断不同参数情况时节点是否在列表里 public static boolean exists(List nodes, Node node) { for (Node n : nodes) { if ((n.x == node.x) && (n.y == node.y)) { return true; } } return false; } public static boolean exists(List nodes, int x, int y) { for (Node n : nodes) { if ((n.x == x) && (n.y == y)) { return true; } } return false; } //节点类,定义了每一个节点的属性 public static class Node { public Node(int x, int y) { this.x = x; this.y = y; } public int x; public int y; public int F; public int G; public int H; public void calcF() { this.F = this.G + this.H; } public Node parent; } }
MazePanel类
package com.company; import javax.swing.*; import java.awt.*; import java.awt.event.*; class MazePanel extends JPanel implements KeyListener,ActionListener { public final static int WALL = 0;//障碍物 public final static int ROAD = 1;//通路 public static int width; //地图规模 public static int height; public final static int current = 2; //标识当前位置图例 public final static int MAXSTEPS = 200; //游戏的最大步数 static int mx = 1; //当前位置(curX,curY) static int my = 1; static int score = 0; //游戏得分及步数 static int steps = 0; // 步数 static int level = 3;//难度 static int[][] map; //储存迷宫 private JButton ans = new JButton("显示路径"); //按键和键盘方式,未实现,直接全用键盘操作了 private JButton remake = new JButton("重置迷宫"); private JPanel jp = new JPanel(); private JButton hide = new JButton("隐藏路径"); private JButton exit = new JButton("退出游戏"); private JButton start = new JButton("开始游戏"); public static int Difficult(int level) { //游戏难度,初始为10,难度+1,地图+2 int number = 10; for (int i = 0; i < level; i++) { number += 2; } return number; } public static void MapRandom() { //随机迷宫 width = Difficult(level); height = Difficult(level); com.company.Maze maze = new com.company.Maze(width, height); //初始化迷宫 map = com.company.Maze.Init(); //继承迷宫 map[1][1] = 2; //设置迷宫的入口及出口 mx = 1; my = 1; //map[map.length - 2][map.length - 2] = 2; } //游戏界面初始化和设计 public void paintComponent(Graphics g) { super.paintComponent(g); this.addKeyListener(this); //count = Difficult(level); g.setColor(new Color(0,139,139));//添加RGB背景色 g.fillRect(0, 0, 760, 807); //Date.location.paintIcon(this,g,0,50); //g.setColor(new Color(224, 238, 238)); for (int i = 0; i < map.length; i++) { //填充颜色的方法画出迷宫的wall和load for (int j = 0; j < map.length; j++) { if(i==map.length-2&&j==map.length-2){ g.setColor(Color.RED); g.fillRect(10 * i + 10, 10 * j + 50, 10, 10); }else if (map[i][j] == 0) { g.setColor(Color.BLACK); g.fillRect(10 * i + 10, 10 * j + 50, 10, 10); } else if (map[i][j] == 1) { g.setColor(Color.GRAY); g.fillRect(10 * i + 10, 10 * j + 50, 10, 10); } else if (map[i][j] == 2) { g.setColor(Color.RED); g.fillRect(10 * i + 10, 10 * j + 50, 10, 10); } } g.setColor(new Color(188, 250, 250)); g.fillRect(0, 0, 1000, 40); g.setColor(Color.BLACK); g.setFont(new Font("仿宋", Font.BOLD, 20)); g.drawString("重置游戏(X)", 10, 25); g.drawString("寻路(Y)", 150, 25); g.drawString("当前步数:" + steps, 250, 25); g.drawString("难度:"+level,400,25); g.drawString("W:难度+ S:难度-",500,25); if (MazePanel.isSuccess()) { g.setFont(new Font("宋体", Font.BOLD, 50)); g.drawString("恭喜通关", 500, 400); } } } //将A*算法得出的路径返回给map public void ans() { com.company.AStar aStart = new com.company.AStar(map); map = com.company.AStar.ans(); } public MazePanel() { MapRandom(); this.setFocusable(true); this.addKeyListener(this); } //判断是否走出迷宫 public static boolean isSuccess() { //score += MazePanel.MAXSTEPS - steps; return map.length - 2 == mx && map.length - 2 == my; } @Override public void actionPerformed(ActionEvent e) { } static int i=0; @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); int x = mx, y = my; //游戏移动方式和限制,下一步不能为墙且单次监听,不加i=0会产生bug if(!isSuccess()&&steps=3){ //减少迷宫难度 level--;i++; MapRandom(); } } repaint(); //刷新游戏界面 } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { i=0; //键盘监听限制,不加限制会一次移动好几格 } } Main类
package com.company; import javax.swing.*; public class Main { public static void main(String[] args) { JFrame frame = new JFrame(); //生成窗口 frame.setSize(770,825); //设置窗口大小 com.company.MazePanel maze = new com.company.MazePanel(); frame.add(maze); //添加游戏面板 frame.setLocationRelativeTo(null);//显示屏幕中央 frame.setResizable(false);//固定屏幕 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭指令 frame.setVisible(true); //窗口可视化 } }