线段交叉法 构造迷宫

迷宫是算法经常用到的问题, 一般构造迷宫的方法是自己定义矩阵, 0 表示平地, 1表示墙

最近我想了一种另类的方法构造迷宫, 下面介绍一下

基本原理

构造一个"表格"


线段交叉法 构造迷宫_第1张图片



去掉表格中的某些线段,就自动生成迷宫了

线段交叉法 构造迷宫_第2张图片


下面程序生成图


线段交叉法 构造迷宫_第3张图片


如何保证迷宫一定有出路

思路就是:画出一条路径, 把交叉的代表墙的直线,全部去掉


上图中绿色就是路径, 红色是要去掉的'墙'

源代码

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

将看到令人吃惊的效果


线段交叉法 构造迷宫_第4张图片


路径生成不太合理, 此算法以后还要改进






你可能感兴趣的:(算法)