开发环境:
操作系统Win10。
1.下载Java 15,提取码:soft
2.下载软件 Eclipse 2020-12,提取码:soft
下载本博客的实例工程代码,提取码:soft
前天2月9日在逛B站App时,意外看到一个很有意思的视频,Up“暗流啊暗流”通过视频讲了一个迷宫算法,看得手痒难耐,就花了一些时间写了这么一个迷宫算法最基本应用—即生成迷宫。
简单来讲,生成迷宫就是一个砸墙的过程,砸墙规则可以归纳为以下三点:
1,只有当隔壁房间没去过的时候,墙才可以砸
2,无墙可砸的时候,就传送到一个去过的房间里
3,每一个房间都要到达
有想了解迷宫算法更多应用的,可以到这里去看视频,从迷宫生成算法到创意编程
代码实现粗糙,水平有限,如果有更好的实装方案,可以精简代码,或更加灵巧可扩展,等等,欢迎批评指正,提建议。
文章目录
- 1. 工程目录结构
- 2. 代码内容
- 2.1 主启动类 StartFrame.java
- 2.2 迷宫类 Maze.java
- 2.3 迷宫房间类 MazeMember.java
- 3. 运行效果
1. 工程目录结构
2. 代码内容
2.1 主启动类 StartFrame.java
package maze.view;import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;/*** 用迷宫算法,随机生成迷宫* 思想:生成迷宫的过程就是一个在小方格里砸墙的过程* 迷宫算法规则:* 1,只有当隔壁房间没去过的时候,墙才可以砸* 2, 无墙可砸的时候,就传送到一个去过的房间里* 3, 每一个房间都要到达* @author 见瑞彬**/
public class StartFrame extends JFrame implements ActionListener {/** 序列ID */private static final long serialVersionUID = 1L;private int frameX = 0;private int frameY = 0;private int frameW = 800;private int frameH = 700;/** 迷宫类 */private Maze mazeObject;/** 迷宫区域 */private Rectangle mazeArea;/** 按钮<<重新生成迷宫>> */private JButton remakeMazeButton;private JTextField rowCountField;private JTextField colCountField;private JLabel rowCountLabel;// 居中{Dimension clientSize = Toolkit.getDefaultToolkit().getScreenSize();frameX = (int) ((clientSize.getWidth() - frameW) / 2);frameY = (int) ((clientSize.getHeight() - frameH) / 2);// 设定迷宫mazeObject = new Maze();// 设定迷宫区域mazeArea = new Rectangle(20, 80, 600, 600);}public StartFrame() {// 设定布局FlowLayout flow = new FlowLayout();flow.setAlignment(FlowLayout.LEFT);this.setLayout(flow);// 添加按钮remakeMazeButton = new JButton("重新生成迷宫");remakeMazeButton.addActionListener(this);this.add(remakeMazeButton);// 设定水平垂直的方格数rowCountLabel = new JLabel("水平方格数");this.add(rowCountLabel);rowCountField = new JTextField(6);this.add(rowCountField);// 窗体属性this.setBounds(frameX, frameY, frameW, frameH);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);this.validate();}/*** 重绘窗体*/@Overridepublic void paint(Graphics g) {super.paint(g);drawMaze(g);}/*** 画迷宫* * @param g*/private void drawMaze(Graphics g) {// 迷宫线颜色g.setColor(Color.RED);// 画迷宫区域边线g.drawRect(mazeArea.x, mazeArea.y, mazeArea.width, mazeArea.height);// 取得每个方格宽度和高度int width = mazeArea.width / mazeObject.getRowCount();int height = mazeArea.height / mazeObject.getColCount();// 绘制迷宫元素MazeMember[][] mazeTable = mazeObject.create();// 设定左上角方格为起点mazeTable[0][0].setMazeMemberId(MazeMember.ID.VISITOR);// 设定右下角方格为终点mazeTable[mazeObject.getRowCount() - 1][mazeObject.getColCount() - 1].setMazeMemberId(MazeMember.ID.TARGET);// 遍历迷宫区域的所有方格for (int i = 0; i < mazeObject.getRowCount(); i++) {for (int j = 0; j < mazeObject.getColCount(); j++) {// 迷宫水平坐标int x = mazeArea.x + width * j;// 迷宫垂直坐标int y = mazeArea.y + height * i;// 获得迷宫方格对象MazeMember mazeMember = mazeTable[i][j];// 绘制方格中表示的边,隐藏不表示的边。mazeMember.drawMazeMember(g, x, y, width, height);}}}public static void main(String[] args) {// TODO Auto-generated method stubStartFrame mf = new StartFrame();}@Overridepublic void actionPerformed(ActionEvent e) {if (e.getSource() == this.remakeMazeButton) {// 修改迷宫的水平或垂直方格数量try {mazeObject.setColCount(Integer.parseInt(rowCountField.getText()));mazeObject.setRowCount(Integer.parseInt(rowCountField.getText()));} catch (Exception e1) {if(mazeObject.getColCount() == 0) {mazeObject.setColCount(10);mazeObject.setRowCount(10);}}// 重绘迷宫this.repaint();}}}
2.2 迷宫类 Maze.java
package maze.view;import java.util.ArrayList;
import java.util.List;/*** 迷宫对象* * @author 见瑞彬**/
public class Maze {/** 迷宫成员对象数组 */private MazeMember[][] mazeTable;/** 迷宫成员对象水平数量 */private int rowCount;/** 迷宫成员对象垂直数量 */private int colCount;/** 迷宫访问者垂直方向坐标 */private int visitorVertical = 0;/** 迷宫访问者水平方向坐标 */private int visitorHorizontal = 0;public Maze() {// 初始化方格数量 5*5rowCount = 5;colCount = 5;}/*** 创建迷宫* * @return*/public MazeMember[][] create() {// 初始化迷宫的每一个成员对象mazeTable = new MazeMember[rowCount][colCount];for (int i = 0; i < rowCount; i++) {for (int j = 0; j < colCount; j++) {mazeTable[i][j] = new MazeMember();mazeTable[i][j].setTopLineShow(true);mazeTable[i][j].setBottomLineShow(true);mazeTable[i][j].setLeftLineShow(true);mazeTable[i][j].setRightLineShow(true);}}// 初始化迷宫访问者位置(0,0)visitorVertical = 0;visitorHorizontal = 0;mazeTable[0][0].setVisited(true);// 遍历所有迷宫成员对象,并进行迷宫成员对象的推墙操作while (!hasFinishMazeTable()) {// 迷宫访问者前进到下一步boolean visitorMoveSuccess = moveToNextStep(visitorVertical, visitorHorizontal);// 迷宫访问者前进失败场合if (!visitorMoveSuccess) {// 重新定位迷宫访问者的位置// 跳跃到曾经访问过的迷宫成员对象的位置updateVisitorPoint();}}// 迷宫成员对象数组return mazeTable;}/*** 迷宫访问者是否访问完所有迷宫成员对象* * @return*/private boolean hasFinishMazeTable() {for (int i = 0; i < rowCount; i++) {for (int j = 0; j < colCount; j++) {if (!mazeTable[i][j].isVisited()) {return false;}}}return true;}/*** 抹去相邻的没有访问过的成员边界,并更新访问者位置* * @param nowLocation* @return*/private boolean moveToNextStep(int vertical, int horizontal) {// 存储当前迷宫访问者位置的周围的上下左右四个位置// 避免随机到重复的位置List<Integer> locationLib = new ArrayList<>();// 遍历周围while (true) {// 所有相邻位置已经遍历完成,该成员不存在未访问过的相邻成员,终止遍历if (locationLib.size() == 4) {return false;}// 随机生成一个迷宫成员的上下左右相邻成员的序号// 0上 1下 2左 3右int locIndex = (int) (Math.random() * 4);// 下一个相邻位置if (!locationLib.contains(locIndex)) {locationLib.add(locIndex);// 上if (locIndex == 0) {// 移动到上面一个位置if (moveTop(vertical, horizontal)) {return true;}// 下} else if (locIndex == 1) {// 移动到下面一个位置if (moveBottom(vertical, horizontal)) {return true;}// 左} else if (locIndex == 2) {// 移动到左面一个位置if (moveLeft(vertical, horizontal)) {return true;}// 右} else {// 移动到右面一个位置if (moveRight(vertical, horizontal)) {return true;}}}}}/*** 防止迷宫访问者出界* * @param vertical* @param horizontal* @return*/private boolean isRightPoint(int vertical, int horizontal) {if (vertical >= 0 && vertical < rowCount && horizontal >= 0 && horizontal < colCount) {// 不出界,返回truereturn true;}// 出界返回falsereturn false;}/*** 向上移动* * @param vertical* @param horizontal* @return*/private boolean moveTop(int vertical, int horizontal) {int nextVertical = vertical - 1;int nextHorizontal = horizontal;if (isRightPoint(nextVertical, nextHorizontal)) {// 未访问过场合if (!mazeTable[nextVertical][nextHorizontal].isVisited()) {// 上面的边隐藏mazeTable[vertical][horizontal].setTopLineShow(false);// 相邻成员对象,下面的边隐藏mazeTable[nextVertical][nextHorizontal].setBottomLineShow(false);// 相邻成员对象,标记为已访问mazeTable[nextVertical][nextHorizontal].setVisited(true);// 更新迷宫访问者位置visitorVertical = nextVertical;visitorHorizontal = nextHorizontal;return true;}}return false;}/*** 向下移动* * @param vertical* @param horizontal* @return*/private boolean moveBottom(int vertical, int horizontal) {int nextVertical = vertical + 1;int nextHorizontal = horizontal;if (isRightPoint(nextVertical, nextHorizontal)) {// 未访问过场合if (!mazeTable[nextVertical][nextHorizontal].isVisited()) {// 下面的边隐藏mazeTable[vertical][horizontal].setBottomLineShow(false);// 相邻成员对象,上面的边隐藏mazeTable[nextVertical][nextHorizontal].setTopLineShow(false);// 相邻成员对象,标记为已访问mazeTable[nextVertical][nextHorizontal].setVisited(true);// 更新迷宫访问者位置visitorVertical = nextVertical;visitorHorizontal = nextHorizontal;return true;}}return false;}/*** 向左移动* * @param vertical* @param horizontal* @return*/private boolean moveLeft(int vertical, int horizontal) {int nextVertical = vertical;int nextHorizontal = horizontal - 1;if (isRightPoint(nextVertical, nextHorizontal)) {// 未访问过场合if (!mazeTable[nextVertical][nextHorizontal].isVisited()) {// 左面的边隐藏mazeTable[vertical][horizontal].setLeftLineShow(false);// 相邻成员对象,右面的边隐藏mazeTable[nextVertical][nextHorizontal].setRightLineShow(false);// 相邻成员对象,标记为已访问mazeTable[nextVertical][nextHorizontal].setVisited(true);// 更新迷宫访问者位置visitorVertical = nextVertical;visitorHorizontal = nextHorizontal;return true;}}return false;}/*** 向左移动* * @param vertical* @param horizontal* @return*/private boolean moveRight(int vertical, int horizontal) {int nextVertical = vertical;int nextHorizontal = horizontal + 1;if (isRightPoint(nextVertical, nextHorizontal)) {// 未访问过场合if (!mazeTable[nextVertical][nextHorizontal].isVisited()) {// 右面的边隐藏mazeTable[vertical][horizontal].setRightLineShow(false);// 相邻成员对象,左面的边隐藏mazeTable[nextVertical][nextHorizontal].setLeftLineShow(false);// 相邻成员对象,标记为已访问mazeTable[nextVertical][nextHorizontal].setVisited(true);// 更新迷宫访问者位置visitorVertical = nextVertical;visitorHorizontal = nextHorizontal;return true;}}return false;}/*** 迷宫访问者的位置,随机更新到曾经访问过的成员对象的位置* * @return*/private void updateVisitorPoint() {while (true) {int visitorVertical = (int) (Math.random() * rowCount);int visitorHorizontal = (int) (Math.random() * colCount);// 已访问if (mazeTable[visitorVertical][visitorHorizontal].isVisited()) {// 更新位置this.visitorVertical = visitorVertical;this.visitorHorizontal = visitorHorizontal;// 标记已访问mazeTable[visitorVertical][visitorHorizontal].setVisited(true);break;}}}/*** 取得垂直数量* * @return 垂直数量*/public int getRowCount() {return rowCount;}/*** 修改垂直数量* * @param 垂直数量*/public void setRowCount(int rowCount) {this.rowCount = rowCount;}/*** 取得水平数量* * @return 水平数量*/public int getColCount() {return colCount;}/*** 修改水平数量* * @param 水平数量*/public void setColCount(int colCount) {this.colCount = colCount;}}
2.3 迷宫房间类 MazeMember.java
package maze.view;import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;public class MazeMember {public static enum ID {// 一般NORMAL,// 起点访问者VISITOR,// 终点目标TARGET};/** 是否访问过 */private boolean visited;/** 是否显示上面的边 */private boolean topLineShow;/** 是否显示下面的边 */private boolean bottomLineShow;/** 是否显示左面的边 */private boolean leftLineShow;/** 是否显示右面的边 */private boolean rightLineShow;/** 定义成员的身份 */private ID mazeMemberId;public MazeMember() {// 默认所有成员为一般mazeMemberId = ID.NORMAL;}public void drawMazeMember(Graphics g, int x, int y, int width, int height) {// 绘画上面的边if (topLineShow) {g.drawLine(x, y, x + width, y);}// 绘画下面的边if (bottomLineShow) {g.drawLine(x, y + height, x + width, y + height);}// 绘画左面的边if (leftLineShow) {g.drawLine(x, y, x, y + height);}// 绘画右面的边if (rightLineShow) {g.drawLine(x + width, y, x + width, y + height);}// 设定字体Font font = g.getFont();g.setFont(new Font(font.getFamily(), font.getStyle(), 20));// 绘画起点图案if (this.getMazeMemberId() == ID.VISITOR) {Color c = g.getColor();g.setColor(Color.GREEN);g.fillRect(x, y, width, height);g.setColor(c);}// 绘画终点图案if (this.getMazeMemberId() == ID.TARGET) {Color c = g.getColor();g.setColor(Color.MAGENTA);g.fillRect(x, y, width, height);g.setColor(c);}// 还原字体g.setFont(font);}/*** @return the mazeMemberId*/public ID getMazeMemberId() {return mazeMemberId;}/*** @param mazeMemberId the mazeMemberId to set*/public void setMazeMemberId(ID mazeMemberId) {this.mazeMemberId = mazeMemberId;}/*** @return the visited*/public boolean isVisited() {return visited;}/*** @param visited the visited to set*/public void setVisited(boolean visited) {this.visited = visited;}/*** @param topLineShow the topLineShow to set*/public void setTopLineShow(boolean topLineShow) {this.topLineShow = topLineShow;}/*** @param bottomLineShow the bottomLineShow to set*/public void setBottomLineShow(boolean bottomLineShow) {this.bottomLineShow = bottomLineShow;}/*** @param leftLineShow the leftLineShow to set*/public void setLeftLineShow(boolean leftLineShow) {this.leftLineShow = leftLineShow;}/*** @param rightLineShow the rightLineShow to set*/public void setRightLineShow(boolean rightLineShow) {this.rightLineShow = rightLineShow;}}
3. 运行效果
运行 StartFrame.java,启动画面
更改水平方格数至更多,并点击“重新生成迷宫”按钮,效果如下