package nl.tudelft.bw4t.environmentstore.editor.model; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; import nl.tudelft.bw4t.map.Zone; import nl.tudelft.bw4t.map.Zone.Type; public class RandomMapCreator { public RandomMapCreator() { } /** * Create the entire random grid as a model for a randomized map. * @param rows The amount of rows in the table. * @param cols The amount of columns in the table. * @param roomCount The amount of rooms that need to be in the map. * @return A 2D array of nodes representing the randomized map. */ public static ZoneModel[][] createRandomGrid(int rows, int cols, int roomCount) { if (rows <= 0) { throw new IllegalArgumentException("The amount of rows has to be positive and nonzero."); } if (cols <= 0) { throw new IllegalArgumentException("The amount of columns has to be positive and nonzero."); } Node[][] grid = new Node[rows][cols]; initGrid(grid); createRooms(grid); int amountPossibleRooms = maxRoomsPossible(rows, cols); if (amountPossibleRooms < roomCount) { throw new IllegalArgumentException("The amount of rooms wanted" + " cannot be higher than the amount of rooms possible."); } if (roomCount < 1) { throw new IllegalArgumentException("The amount of rooms has to be at least one."); } List<Node> allRooms = getRooms(grid); List<Node> allCorridors = getCorridors(grid); randomizeCorridors(allCorridors); randomizeRooms(allRooms, amountPossibleRooms - roomCount); randomizeDoorDirs(allRooms); ZoneModel[][] newGrid = convertToZoneModelGrid(grid); selectRandomStartPoint(newGrid); selectRandomDropZone(newGrid); return newGrid; } /** * Convert a node grid to a zone model grid. * @param grid The node grid. * @return The representing node model grid. */ private static ZoneModel[][] convertToZoneModelGrid(Node[][] grid) { ZoneModel[][] newGrid = new ZoneModel[grid.length][grid[0].length]; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { newGrid[i][j] = new ZoneModel(grid[i][j]); newGrid[i][j].generateNameFromPosition(i, j); } } return newGrid; } /** * Calculates the max. amount of rooms in a map (use only for randomized maps). * @param r The amount of rows. * @param c The amount of columns. * @return The maximum amount of rooms in a map of this size. */ public static int maxRoomsPossible(int r, int c) { return (c - 2)*(r/2); } /** * Uses a list of nodes classified as corridors and has a 1% chance * to reclassify a node as a charge zone. * @param corridors The list of corridors to be randomized. */ private static void randomizeCorridors(List<Node> corridors) { Random r = new Random(System.currentTimeMillis()); Node stdCharge = corridors.get(r.nextInt(corridors.size())); stdCharge.setType(Type.CHARGINGZONE); for (Node n : corridors) { if (r.nextDouble() < 0.01) { n.setType(Zone.Type.CHARGINGZONE); } } } private static void selectRandomStartPoint(ZoneModel[][] grid) { List<ZoneModel> list = getCorridors(grid); Random r = new Random(System.currentTimeMillis()); int index = r.nextInt(list.size()); list.get(index).setStartZone(true); } /** * Returns a list with all corridors, used in randomization. * @param grid The grid containing the corridors. * @return A list containing all the nodes classified as a corridor in the given grid. */ private static List<ZoneModel> getCorridors(ZoneModel[][] grid) { List<ZoneModel> l = new LinkedList<ZoneModel>(); for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j].getType() == Zone.Type.CORRIDOR) { l.add(grid[i][j]); } } } return l; } /** * Returns a list with all corridors, used in randomization. * @param grid The grid containing the corridors. * @return A list containing all the nodes classified as a corridor in the given grid. */ private static List<Node> getCorridors(Node[][] grid) { List<Node> l = new LinkedList<Node>(); for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j].getType() == Zone.Type.CORRIDOR) { l.add(grid[i][j]); } } } return l; } /** * Randomize the door directions for all rooms in such a way that * completing the map is still possible. * @param rooms The rooms that need a random door direction. */ private static void randomizeDoorDirs(List<Node> rooms) { for (Node n : rooms) { Random r = new Random(System.currentTimeMillis()); n.setDir(n.getFreeDirs().get(r.nextInt(n.getFreeDirs().size()))); } } /** * Reduce the amount of rooms by the given integer to get a desired map, * by randomly selecting rooms from the list and reclassifying them. * @param rooms The list of rooms. * @param changedRooms The amount of rooms that need to be reclassified. */ private static void randomizeRooms(List<Node> rooms, int changedRooms) { List<Zone.Type> typeList = new ArrayList<Zone.Type>(); typeList.add(Zone.Type.CORRIDOR); typeList.add(Zone.Type.CORRIDOR); typeList.add(Zone.Type.CORRIDOR); typeList.add(Zone.Type.CORRIDOR); typeList.add(Zone.Type.CHARGINGZONE); typeList.add(Zone.Type.BLOCKADE); typeList.add(Zone.Type.BLOCKADE); typeList.add(Zone.Type.BLOCKADE); typeList.add(Zone.Type.BLOCKADE); typeList.add(Zone.Type.BLOCKADE); Random typeSelector = new Random(System.currentTimeMillis()); for (int i = 0; i < changedRooms; i++) { Random r = new Random(System.currentTimeMillis()); Node n = rooms.remove(r.nextInt(rooms.size())); n.setType(typeList.get(typeSelector.nextInt(10))); } } private static void selectRandomDropZone(ZoneModel[][] grid) { List<ZoneModel> list = getRooms(grid); Random r = new Random(System.currentTimeMillis()); int index = r.nextInt(list.size()); list.get(index).setDropZone(true); } /** * Returns a list of all the rooms in the grid. * @param grid The grid from which the rooms have to be extracted. * @return A list containing every tile in the grid classified as a room. */ private static List<ZoneModel> getRooms(ZoneModel[][] grid) { List<ZoneModel> l = new LinkedList<ZoneModel>(); for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j].getType() == Zone.Type.ROOM) { l.add(grid[i][j]); } } } return l; } /** * Returns a list of all the rooms in the grid. * @param grid The grid from which the rooms have to be extracted. * @return A list containing every tile in the grid classified as a room. */ private static List<Node> getRooms(Node[][] grid) { List<Node> l = new LinkedList<Node>(); for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j].getType() == Zone.Type.ROOM) { l.add(grid[i][j]); } } } return l; } /** * Create the initial rooms according to the standard model * of room creation (upper row no rooms, next row has rooms on * all tiles except for the leftmost and rightmost ones, next row has * no rooms, etc). * @param grid The grid in question. */ private static void createRooms(Node[][] grid) { int lastCol = grid[0].length - 1; int lastRow = grid.length; for (int i = 1; i < lastRow; i++) { for (int j = 1; j < lastCol; j++) { if (i % 2 == 1) { grid[i][j].setType(Zone.Type.ROOM); } } } } /** * Initialize the grid by creating a node object * for every square and then connecting adjacent node objects. * @param grid The grid to be initialized. */ private static void initGrid(Node[][] grid) { for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { grid[i][j] = new Node(Zone.Type.CORRIDOR); } } connectTiles(grid); } /** * Connect all tiles of a grid to their adjacent tiles * in the grid. * @param grid The grid of tiles that need to be connected. */ private static void connectTiles(Node[][] grid) { configureCorners(grid); configureBorders(grid); configureInnerNodes(grid); } /** * Set the tiles that would be out of bound to null. * The corner nodes are connected to their adjacent grid tiles later. * @param grid The grid containing the corner tiles to be configured. */ private static void configureCorners(Node[][] grid) { int lastCol = grid[0].length - 1; int lastRow = grid.length - 1; grid[0][0].setNorth(null); grid[0][0].setWest(null); grid[lastRow][0].setSouth(null); grid[lastRow][0].setWest(null); grid[0][lastCol].setEast(null); grid[0][lastCol].setNorth(null); grid[lastRow][lastCol].setEast(null); grid[lastRow][lastCol].setSouth(null); } /** * Connect all the borders, setting the tiles out of bound to null. * @param grid The grid in question. */ private static void configureBorders(Node[][] grid) { int lastCol = grid[0].length - 1; int lastRow = grid.length - 1; // Connect upper and lower rows. for (int i = 1; i < lastCol; i++) { grid[0][i].setNorth(null); grid[0][i].setEast(grid[0][i + 1]); grid[0][i + 1].setWest(grid[0][i]); grid[0][i].setWest(grid[0][i - 1]); grid[0][i - 1].setEast(grid[0][i]); grid[0][i].setSouth(grid[1][i]); grid[1][i].setNorth(grid[0][i]); grid[lastRow][i].setSouth(null); grid[lastRow][i].setEast(grid[lastRow][i + 1]); grid[lastRow][i + 1].setWest(grid[lastRow][i]); grid[lastRow][i].setWest(grid[lastRow][i - 1]); grid[lastRow][i - 1].setEast(grid[lastRow][i]); grid[lastRow][i].setNorth(grid[lastRow][i - 1]); grid[lastRow][i - 1].setSouth(grid[lastRow][i]); } // Connect leftmost and rightmost columns. for (int i = 1; i < lastRow; i++) { grid[i][0].setWest(null); grid[i][0].setNorth(grid[i - 1][0]); grid[i - 1][0].setSouth(grid[i][0]); grid[i][0].setSouth(grid[i + 1][0]); grid[i + 1][0].setNorth(grid[i][0]); grid[i][0].setEast(grid[i][1]); grid[i][1].setWest(grid[i][0]); grid[i][lastCol].setEast(null); grid[i][lastCol].setNorth(grid[i - 1][lastCol]); grid[i - 1][lastCol].setSouth(grid[i][lastCol]); grid[i][lastCol].setSouth(grid[i + 1][lastCol]); grid[i + 1][lastCol].setNorth(grid[i][lastCol]); grid[i][lastCol].setWest(grid[i][lastCol - 1]); grid[i][lastCol - 1].setEast(grid[i][lastCol]); } } /** * Connect all the nodes within the outer areas to their adjacent * tiles and vice versa. * @param grid The grid in question. */ private static void configureInnerNodes(Node[][] grid) { int lastCol = grid[0].length - 1; int lastRow = grid.length - 1; for (int i = 1; i < lastRow; i++) { for (int j = 1; j < lastCol; j++) { grid[i][j].setNorth(grid[i - 1][j]); grid[i - 1][j].setSouth(grid[i][j]); grid[i][j].setEast(grid[i][j + 1]); grid[i][j + 1].setWest(grid[i][j]); grid[i][j].setSouth(grid[i + 1][j]); grid[i + 1][j].setNorth(grid[i][j]); grid[i][j].setWest(grid[i][j - 1]); grid[i][j - 1].setEast(grid[i][j]); } } } }