r4lovets

Говноcode

Oct 1st, 2019
433
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.55 KB | None | 0 0
  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.Comparator;
  4. import java.util.Random;
  5.  
  6. public class MyMaze {
  7.   private int dimensionX, dimensionY; // dimension of maze
  8.   private int gridDimensionX, gridDimensionY; // dimension of output grid
  9.   private char[][] grid; // output grid
  10.   private Cell[][] cells; // 2d array of Cells
  11.   private Random random = new Random(); // The random object
  12.  
  13.   // initialize with x and y the same
  14.   public MyMaze(int aDimension) {
  15.       // Initialize
  16.       this(aDimension, aDimension);
  17.   }
  18.   // constructor
  19.   public MyMaze(int xDimension, int yDimension) {
  20.       dimensionX = xDimension;
  21.       dimensionY = yDimension;
  22.       gridDimensionX = xDimension * 4 + 1;
  23.       gridDimensionY = yDimension * 2 + 1;
  24.       grid = new char[gridDimensionX][gridDimensionY];
  25.       init();
  26.       generateMaze();
  27.   }
  28.  
  29.   private void init() {
  30.       // create cells
  31.       cells = new Cell[dimensionX][dimensionY];
  32.       for (int x = 0; x < dimensionX; x++) {
  33.           for (int y = 0; y < dimensionY; y++) {
  34.               cells[x][y] = new Cell(x, y, false); // create cell (see Cell constructor)
  35.           }
  36.       }
  37.   }
  38.  
  39.   // inner class to represent a cell
  40.   private class Cell {
  41.     int x, y; // coordinates
  42.     // cells this cell is connected to
  43.     ArrayList<Cell> neighbors = new ArrayList<>();
  44.     // solver: if already used
  45.     boolean visited = false;
  46.     // solver: the Cell before this one in the path
  47.     Cell parent = null;
  48.     // solver: if used in last attempt to solve path
  49.     boolean inPath = false;
  50.     // solver: distance travelled this far
  51.     double travelled;
  52.     // solver: projected distance to end
  53.     double projectedDist;
  54.     // impassable cell
  55.     boolean wall = true;
  56.     // if true, has yet to be used in generation
  57.     boolean open = true;
  58.     // construct Cell at x, y
  59.     Cell(int x, int y) {
  60.         this(x, y, true);
  61.     }
  62.     // construct Cell at x, y and with whether it isWall
  63.     Cell(int x, int y, boolean isWall) {
  64.         this.x = x;
  65.         this.y = y;
  66.         this.wall = isWall;
  67.     }
  68.     // add a neighbor to this cell, and this cell as a neighbor to the other
  69.     void addNeighbor(Cell other) {
  70.         if (!this.neighbors.contains(other)) { // avoid duplicates
  71.             this.neighbors.add(other);
  72.         }
  73.         if (!other.neighbors.contains(this)) { // avoid duplicates
  74.             other.neighbors.add(this);
  75.         }
  76.     }
  77.     // used in updateGrid()
  78.     boolean isCellBelowNeighbor() {
  79.         return this.neighbors.contains(new Cell(this.x, this.y + 1));
  80.     }
  81.     // used in updateGrid()
  82.     boolean isCellRightNeighbor() {
  83.         return this.neighbors.contains(new Cell(this.x + 1, this.y));
  84.     }
  85.     // useful Cell representation
  86.     @Override
  87.     public String toString() {
  88.         return String.format("Cell(%s, %s)", x, y);
  89.     }
  90.     // useful Cell equivalence
  91.     @Override
  92.     public boolean equals(Object other) {
  93.         if (!(other instanceof Cell)) return false;
  94.         Cell otherCell = (Cell) other;
  95.         return (this.x == otherCell.x && this.y == otherCell.y);
  96.     }
  97.     // should be overridden with equals
  98.     @Override
  99.     public int hashCode() {
  100.         // random hash code method designed to be usually unique
  101.         return this.x + this.y * 256;
  102.     }
  103.   }
  104.   // generate from upper left (In computing the y increases down often)
  105.   private void generateMaze() {
  106.       generateMaze(0, 0);
  107.   }
  108.   // generate the maze from coordinates x, y
  109.   private void generateMaze(int x, int y) {
  110.       generateMaze(getCell(x, y)); // generate from Cell
  111.   }
  112.   private void generateMaze(Cell startAt) {
  113.       // don't generate from cell not there
  114.       if (startAt == null) return;
  115.       startAt.open = false; // indicate cell closed for generation
  116.       ArrayList<Cell> cells = new ArrayList<>();
  117.       cells.add(startAt);
  118.  
  119.       while (!cells.isEmpty()) {
  120.           Cell cell;
  121.           // this is to reduce but not completely eliminate the number
  122.           //   of long twisting halls with short easy to detect branches
  123.           //   which results in easy mazes
  124.           if (random.nextInt(10)==0)
  125.               cell = cells.remove(random.nextInt(cells.size()));
  126.           else cell = cells.remove(cells.size() - 1);
  127.           // for collection
  128.           ArrayList<Cell> neighbors = new ArrayList<>();
  129.           // cells that could potentially be neighbors
  130.           Cell[] potentialNeighbors = new Cell[]{
  131.               getCell(cell.x + 1, cell.y),
  132.               getCell(cell.x, cell.y + 1),
  133.               getCell(cell.x - 1, cell.y),
  134.               getCell(cell.x, cell.y - 1)
  135.           };
  136.           for (Cell other : potentialNeighbors) {
  137.               // skip if outside, is a wall or is not opened
  138.               if (other==null || other.wall || !other.open) continue;
  139.               neighbors.add(other);
  140.           }
  141.           if (neighbors.isEmpty()) continue;
  142.           // get random cell
  143.           Cell selected = neighbors.get(random.nextInt(neighbors.size()));
  144.           // add as neighbor
  145.           selected.open = false; // indicate cell closed for generation
  146.           cell.addNeighbor(selected);
  147.           cells.add(cell);
  148.           cells.add(selected);
  149.       }
  150.   }
  151.   // used to get a Cell at x, y; returns null out of bounds
  152.   public Cell getCell(int x, int y) {
  153.       try {
  154.           return cells[x][y];
  155.       } catch (ArrayIndexOutOfBoundsException e) { // catch out of bounds
  156.           return null;
  157.       }
  158.   }
  159.  
  160.   public void solve() {
  161.       // default solve top left to bottom right
  162.       this.solve(0, 0, dimensionX - 1, dimensionY -1);
  163.   }
  164.   // solve the maze starting from the start state (A-star algorithm)
  165.   public void solve(int startX, int startY, int endX, int endY) {
  166.       // re initialize cells for path finding
  167.       for (Cell[] cellrow : this.cells) {
  168.           for (Cell cell : cellrow) {
  169.               cell.parent = null;
  170.               cell.visited = false;
  171.               cell.inPath = false;
  172.               cell.travelled = 0;
  173.               cell.projectedDist = -1;
  174.           }
  175.       }
  176.       // cells still being considered
  177.       ArrayList<Cell> openCells = new ArrayList<>();
  178.       // cell being considered
  179.       Cell endCell = getCell(endX, endY);
  180.       if (endCell == null) return; // quit if end out of bounds
  181.       { // anonymous block to delete start, because not used later
  182.           Cell start = getCell(startX, startY);
  183.           if (start == null) return; // quit if start out of bounds
  184.           start.projectedDist = getProjectedDistance(start, 0, endCell);
  185.           start.visited = true;
  186.           openCells.add(start);
  187.       }
  188.       boolean solving = true;
  189.       while (solving) {
  190.           if (openCells.isEmpty()) return; // quit, no path
  191.           // sort openCells according to least projected distance
  192.           Collections.sort(openCells, new Comparator<Cell>(){
  193.               @Override
  194.               public int compare(Cell cell1, Cell cell2) {
  195.                   double diff = cell1.projectedDist - cell2.projectedDist;
  196.                   if (diff > 0) return 1;
  197.                   else if (diff < 0) return -1;
  198.                   else return 0;
  199.               }
  200.           });
  201.           Cell current = openCells.remove(0); // pop cell least projectedDist
  202.           if (current == endCell) break; // at end
  203.           for (Cell neighbor : current.neighbors) {
  204.               double projDist = getProjectedDistance(neighbor,
  205.                       current.travelled + 1, endCell);
  206.               if (!neighbor.visited || // not visited yet
  207.                       projDist < neighbor.projectedDist) { // better path
  208.                   neighbor.parent = current;
  209.                   neighbor.visited = true;
  210.                   neighbor.projectedDist = projDist;
  211.                   neighbor.travelled = current.travelled + 1;
  212.                   if (!openCells.contains(neighbor))
  213.                       openCells.add(neighbor);
  214.               }
  215.           }
  216.       }
  217.       // create path from end to beginning
  218.       Cell backtracking = endCell;
  219.       backtracking.inPath = true;
  220.       while (backtracking.parent != null) {
  221.           backtracking = backtracking.parent;
  222.           backtracking.inPath = true;
  223.       }
  224.   }
  225.   // get the projected distance
  226.   // (A star algorithm consistent)
  227.   public double getProjectedDistance(Cell current, double travelled, Cell end) {
  228.       return travelled + Math.abs(current.x - end.x) +
  229.               Math.abs(current.y - current.x);
  230.   }
  231.  
  232.   // draw the maze
  233.   public void updateGrid() {
  234.       char backChar = ' ', wallChar = 'X', cellChar = ' ', pathChar = '*';
  235.       // fill background
  236.       for (int x = 0; x < gridDimensionX; x ++) {
  237.           for (int y = 0; y < gridDimensionY; y ++) {
  238.               grid[x][y] = backChar;
  239.           }
  240.       }
  241.       // build walls
  242.       for (int x = 0; x < gridDimensionX; x ++) {
  243.           for (int y = 0; y < gridDimensionY; y ++) {
  244.               if (x % 4 == 0 || y % 2 == 0)
  245.                   grid[x][y] = wallChar;
  246.           }
  247.       }
  248.       // make meaningful representation
  249.       for (int x = 0; x < dimensionX; x++) {
  250.           for (int y = 0; y < dimensionY; y++) {
  251.               Cell current = getCell(x, y);
  252.               int gridX = x * 4 + 2, gridY = y * 2 + 1;
  253.               if (current.inPath) {
  254.                   grid[gridX][gridY] = pathChar;
  255.                   if (current.isCellBelowNeighbor())
  256.                       if (getCell(x, y + 1).inPath) {
  257.                           grid[gridX][gridY + 1] = pathChar;
  258.                           grid[gridX + 1][gridY + 1] = backChar;
  259.                           grid[gridX - 1][gridY + 1] = backChar;
  260.                       } else {
  261.                           grid[gridX][gridY + 1] = cellChar;
  262.                           grid[gridX + 1][gridY + 1] = backChar;
  263.                           grid[gridX - 1][gridY + 1] = backChar;
  264.                       }
  265.                   if (current.isCellRightNeighbor())
  266.                       if (getCell(x + 1, y).inPath) {
  267.                           grid[gridX + 2][gridY] = pathChar;
  268.                           grid[gridX + 1][gridY] = pathChar;
  269.                           grid[gridX + 3][gridY] = pathChar;
  270.                       } else {
  271.                           grid[gridX + 2][gridY] = cellChar;
  272.                           grid[gridX + 1][gridY] = cellChar;
  273.                           grid[gridX + 3][gridY] = cellChar;
  274.                       }
  275.               } else {
  276.                   grid[gridX][gridY] = cellChar;
  277.                   if (current.isCellBelowNeighbor()) {
  278.                       grid[gridX][gridY + 1] = cellChar;
  279.                       grid[gridX + 1][gridY + 1] = backChar;
  280.                       grid[gridX - 1][gridY + 1] = backChar;
  281.                   }
  282.                   if (current.isCellRightNeighbor()) {
  283.                       grid[gridX + 2][gridY] = cellChar;
  284.                       grid[gridX + 1][gridY] = cellChar;
  285.                       grid[gridX + 3][gridY] = cellChar;
  286.                   }
  287.               }
  288.           }
  289.       }
  290.   }
  291.  
  292.   // simply prints the map
  293.   public void draw() {
  294.       System.out.print(this);
  295.   }
  296.   // forms a meaningful representation
  297.   @Override
  298.   public String toString() {
  299.       updateGrid();
  300.       String output = "";
  301.       for (int y = 0; y < gridDimensionY; y++) {
  302.           for (int x = 0; x < gridDimensionX; x++) {
  303.               output += grid[x][y];
  304.           }
  305.           output += "\n";
  306.       }
  307.       return output;
  308.   }
  309.  
  310.   // run it
  311.   public static void main(String[] args) {
  312.       MyMaze maze = new MyMaze(20);
  313.       maze.solve();
  314.       maze.draw();
  315.   }
  316. }
Add Comment
Please, Sign In to add comment