线段交叉法 构造迷宫

来源:互联网 发布:游开服表数据 编辑:程序博客网 时间:2024/04/30 07:04

迷宫是算法经常用到的问题, 一般构造迷宫的方法是自己定义矩阵, 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) { // paralif ( 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;} elsereturn 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 LineArrayList<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 linesclass 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 linesgoffScreen.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 frameLine 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);elsenextY += 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);}elsei++;}}}public void show() {generateLines();calculateCrossPoints();dynamicInit(); // cut line for gen the Mazeframe = 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

将看到令人吃惊的效果




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






原创粉丝点击