迷宫是算法经常用到的问题, 一般构造迷宫的方法是自己定义矩阵, 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
路径生成不太合理, 此算法以后还要改进