迷宫是算法经常用到的问题, 一般构造迷宫的方法是自己定义矩阵, 0 表示平地, 1表示墙
最近我想了一种另类的方法构造迷宫, 下面介绍一下
基本原理
构造一个"表格"
去掉表格中的某些线段,就自动生成迷宫了

下面程序生成图

如何保证迷宫一定有出路
思路就是:画出一条路径, 把交叉的代表墙的直线,全部去掉
上图中绿色就是路径, 红色是要去掉的'墙'
源代码
package com.pnp.maze;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
public class Maze {
static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "<"+x+":"+y+">";
}
}
static class Line {
Point p1;
Point p2;
double k;
double b;
ArrayList<Point> crossPoints;
public Line( Point p1, Point p2 ) {
this.p1 = p1;
this.p2 = p2;
canclulateKB();
}
public String toString() {
return "<"+p1.x+":"+p1.y+">-<"+p2.x+":"+p2.y+">";
}
void canclulateKB(){
if ( p2.x == p1.x ) {
k = 0x7FFFFFFF;
} else {
k = (p2.y - p1.y) / ( p2.x - p1.x);
b = p1.y - k * p1.x;
}
}
public boolean isVertical() {
return p1.x == p2.x;
}
public boolean isHorizontal() {
return p1.y == p2.y;
}
public void addCrossPoint(Point p) {
if ( crossPoints == null)
crossPoints = new ArrayList<Point>();
crossPoints.add(p);
}
static public Point getCrossPoint(Line l1, Line l2) {
if ( l2.k == l1.k) { // paral
if ( l1.b == l2.b) {
if ( l1.k == 0x7FFFFFFF ) {
if ( l1.p1.x != l2.p1.x)
return null;
else if ( Math.abs(( l1.p2.y + l1.p1.y ) /2 - ( l2.p2.y + l2.p1.y )/2) <= ( l1.p2.y - l1.p1.y ) /2 + ( l2.p2.y - l2.p1.y )/2)
return l1.p1;
} else if ( Math.abs(( l1.p2.x + l1.p1.x ) /2 - ( l2.p2.x + l2.p1.x )/2) <= ( l1.p2.x - l1.p1.x ) /2 + ( l2.p2.x - l2.p1.x )/2)
return l1.p1;
}
else
return null;
}
int x,y;
if ( l1.k == 0x7FFFFFFF ) {
x = l1.p1.x;
y = (int) (l2.k * x + l2.b);
} else if ( l2.k == 0x7FFFFFFF ) {
x = l2.p1.x;
y = (int) (l1.k * x + l1.b);
} else {
x = (int)(-(l2.b - l1.b)/(l2.k-l1.k));
y = (int)(l1.k * x + l1.b);
}
if ( x >= l1.p1.x && x <= l1.p2.x && y >= l1.p1.y && y <= l1.p2.y &&
x >= l2.p1.x && x <= l2.p2.x && y >= l2.p1.y && y <= l2.p2.y )
return new Point(x,y);
return null;
}
static public void addCrossPointToLines(Line l1, Line l2) {
Point p = getCrossPoint(l1,l2);
if (p == null)
return;
l1.addCrossPoint(p);
l2.addCrossPoint(p);
}
} // end class Line
ArrayList<Line> verticalLines = new ArrayList<Line>();
ArrayList<Line> HorizontalLines = new ArrayList<Line>();
ArrayList<Line> shortLines = new ArrayList<Line>();
ArrayList<Line> cutPaths = new ArrayList<Line>();
ArrayList<Line> removedLines = new ArrayList<Line>();
Point point1 = new Point(MAZE_PATH_EDGE,MAZE_PATH_EDGE);
Point point2 = new Point(WIDTH - MAZE_PATH_EDGE,HEIGHT - MAZE_PATH_EDGE);
MyJFrame frame;
final static int WIDTH = 800;
final static int HEIGHT = 600;
final static int MAZE_PATH_EDGE = 20;
final static int MAX_LINE_LEN = 50;
final static int MIN_LINE_LEN = 10;
final static int BREAK_LINE_FACTOR = 50; // ( 0-100, the bigger, the more possible to break lines
class MyJFrame extends JFrame {
Image offScreenImage = null;
public void paint(Graphics g) {
if (offScreenImage == null)
offScreenImage = this.createImage(this.getWidth(),
this.getHeight());
Graphics goffScreen = offScreenImage.getGraphics();
goffScreen.setColor(Color.white);
goffScreen.fillRect(0, 0, this.getWidth(), this.getHeight());
goffScreen.translate(50, 50);
// goffScreen.setColor( Color.white);
// goffScreen.fillRect(0, 0, this.getWidth(),this.getHeight());
// draw lines
goffScreen.setColor(Color.black);
for (int i = 0; i < shortLines.size(); i++) {
Line l = shortLines.get(i);
goffScreen.drawLine(l.p1.x, l.p1.y, l.p2.x, l.p2.y);
}
goffScreen.setColor(Color.green);
for (int i = 0; i < cutPaths.size(); i++) {
Line l = cutPaths.get(i);
goffScreen.drawLine(l.p1.x, l.p1.y, l.p2.x, l.p2.y);
}
goffScreen.setColor(Color.RED);
for (int i = 0; i < removedLines.size(); i++) {
Line l = removedLines.get(i);
goffScreen.drawLine(l.p1.x, l.p1.y, l.p2.x, l.p2.y);
}
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(offScreenImage, 0, 0, null);
}
}
private void dynamicInit() {
genShortLines();
generatePaths2();
cutPathsFromMaze();
shortLines.add(new Line(new Point(0,0), new Point(WIDTH,0)));
shortLines.add(new Line(new Point(0,HEIGHT), new Point(WIDTH,HEIGHT)));
shortLines.add(new Line(new Point(0,0), new Point(0,HEIGHT)));
shortLines.add(new Line(new Point(WIDTH,0), new Point(WIDTH,HEIGHT)));
}
private void generateLines() {
for (int x = 0; x < WIDTH;) {
Point p1 = new Point(x, 0);
Point p2 = new Point(x, HEIGHT);
verticalLines.add(new Line(p1, p2));
if ( x == WIDTH-1 )
break;
double inter;
while ((inter = Math.random() * MAX_LINE_LEN) < MIN_LINE_LEN)
;
x += inter;
if ( WIDTH - x < MIN_LINE_LEN)
x = WIDTH-1;
}
for (int y = 0; y < HEIGHT;) {
Point p1 = new Point(0, y);
Point p2 = new Point(WIDTH, y);
HorizontalLines.add(new Line(p1, p2));
if ( y == HEIGHT-1 )
break;
double inter;
while ((inter = Math.random() * MAX_LINE_LEN) < MIN_LINE_LEN)
;
y += inter;
if ( HEIGHT - y < MIN_LINE_LEN)
y = HEIGHT-1;
}
}
void calculateCrossPoints() {
for (int i = 0; i < verticalLines.size(); i++) {
Line l1 = verticalLines.get(i);
for (int j = 0; j < HorizontalLines.size(); j++) {
Line l2 = HorizontalLines.get(j);
Line.addCrossPointToLines(l1, l2);
}
}
}
void breakLine(ArrayList<Line> lines) {
for (int i = 1; i < lines.size() - 1; i++) {
// won't consider the first
// and last, since it
// form the frame
Line l = lines.get(i);
ArrayList<Point> points = l.crossPoints;
if ( points == null) {
//System.out.println("crossPoints for "+l+" is null");
break;
}
for (int j = 0; j < points.size() - 1; j++) {
Point p1 = points.get(j);
if ((Math.random() * 100) > BREAK_LINE_FACTOR) {
Point p2 = points.get(j + 1);
shortLines.add(new Line(p1, p2));
}
}
}
}
void genShortLines() {
shortLines.clear();
breakLine(verticalLines);
breakLine(HorizontalLines);
}
private void generatePaths1() {
for (int x = MAZE_PATH_EDGE; x < WIDTH - MAZE_PATH_EDGE;) {
Point p1 = new Point(x, 1);
Point p2 = new Point(x, HEIGHT - 1);
cutPaths.add(new Line(p1, p2));
double inter;
while ((inter = Math.random() * 200) < 100)
;
x += inter;
if ( WIDTH - x < MAZE_PATH_EDGE)
break;
}
for (int y = MAZE_PATH_EDGE; y <= HEIGHT - MAZE_PATH_EDGE;) {
Point p1 = new Point(1, y);
Point p2 = new Point(WIDTH - 1, y);
cutPaths.add(new Line(p1, p2));
double inter;
while ((inter = Math.random() * 200) < 100)
;
y += inter;
if ( HEIGHT - y < MAZE_PATH_EDGE)
break;
}
}
private void generatePaths2() {
int beginX = MAZE_PATH_EDGE;
int beginY = MAZE_PATH_EDGE;
int endX = WIDTH - MAZE_PATH_EDGE;
int endY = HEIGHT - MAZE_PATH_EDGE;
cutPaths.clear();
int x = beginX;
int y = beginY;
while ( x < endX || y < endY ) {
int nextX = x;
int nextY = y;
int flag;
if ( (int)(Math.random() * 100) % 3 == 0 )
flag = -1;
else
flag = 1;
double rate = (double)WIDTH / (WIDTH + HEIGHT);
if ( Math.random() < rate )
nextX += (int)(Math.random() * MAX_LINE_LEN*3);
else
nextY += flag* (int)(Math.random() * MAX_LINE_LEN*3);
if ( nextX > endX ) nextX = endX;
if ( nextX < beginX) nextX = beginX;
if ( nextY > endY ) nextY = endY;
if ( nextY < beginY) nextY = beginY;
Point p1 = new Point(x, y);
Point p2 = new Point(nextX, nextY );
Line l = new Line(p1, p2);
/*
int crossFlag = 0;
for( int i= 0; i < cutPaths.size()-1; i++){
Line ll = cutPaths.get(i);
if ( Line.getCrossPoint(l, ll) != null ) {
crossFlag = 1;
break;
}
}
if( crossFlag == 0 ) {
cutPaths.add(l);
x = nextX;
y = nextY;
}
*/
cutPaths.add(l);
x = nextX;
y = nextY;
}
}
static int ct = 0;
private void cutPathsFromMaze() {
removedLines.clear();
for (int j = 0; j < cutPaths.size(); j++) {
Line path = cutPaths.get(j);
for (int i = 0; i < shortLines.size(); ) {
Line l = shortLines.get(i);
Point p = Line.getCrossPoint(l, path);
if (p != null ) {
shortLines.remove(l);
removedLines.add(l);
}
else
i++;
}
}
}
public void show() {
generateLines();
calculateCrossPoints();
dynamicInit(); // cut line for gen the Maze
frame = new MyJFrame();
frame.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent event) {
dynamicInit();
}
});
frame.setTitle("Maze");
frame.setBounds(0, 0, WIDTH + 100, HEIGHT + 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
/*
for( int i=0; i < 10 ; i++)
System.out.println(Math.random()*100);
*/
(new Maze()).show();
}
}
生成参数
通过调节图的生成参数
final static int MAX_LINE_LEN = 5;
final static int MIN_LINE_LEN = 3;
final static int BREAK_LINE_FACTOR = 50; // ( 0-100, the bigger, the more possible to break lines
将看到令人吃惊的效果

路径生成不太合理, 此算法以后还要改进
本文介绍了一种使用线段交叉法构造迷宫的创新方法。基本原理是构建表格并移除特定线段形成迷宫。为了确保迷宫总有出路,作者通过绘制路径并删除与路径交叉的墙线。提供的源代码和生成参数可以进一步调整以优化算法,目前该算法仍有改进空间。
925

被折叠的 条评论
为什么被折叠?



