11package dev .abhay7 .MazeGame ;
22
3+ // Import packages to read Campaign Maps & Generate Mazes
34import java .io .InputStream ;
45import java .util .ArrayList ;
56import java .util .Scanner ;
67import java .util .Stack ;
78
89public class MapGenerator {
910
11+ // Declare 3d map array for campaign
12+ // Essentially, it is an array of 2d "maps"
1013 public Integer [][][] maps ;
1114
12- public MapGenerator () {
15+ // Constructor to read campaign maps
16+ public MapGenerator () {
17+ // Following the system in MazeGame.java, we use an input stream in order to get a steam a scanner can read from.
1318 try (InputStream inputStream = getClass ().getResourceAsStream (MazeGame .MAPS_PATH )) {
1419 Scanner scan = new Scanner (inputStream );
20+
21+ // ArrayList to hold the maps, we use Integer instead of int because ArrayLists can't take primitives
1522 ArrayList <Integer [][]> mapsList = new ArrayList <>();
1623
24+ // ArrayList to hold number of rows in each map
1725 ArrayList <Integer []> rows = new ArrayList <>();
18- int lastLength = -1 ;
26+ int lastLength = -1 ; // hold length of first line in map. Invalidly created maps will throw errors and cause issues
1927 while (scan .hasNextLine ()) {
20- String next = scan .nextLine ();
21- // System.out.println(next);
28+ String next = scan .nextLine (); // Read line
29+
30+ // If the line is ENDL, that means we read a map, and we can move on to the next one
2231 if (next .equals ("ENDL" )) {
2332 lastLength = -1 ;
33+ // Copy our arraylist data to an Integer[][]
2434 Integer [][] mapToAdd = new Integer [rows .size ()][rows .get (0 ).length ];
2535 for (int i = 0 ; i < rows .size (); i ++) {
2636 for (int j = 0 ; j < rows .get (i ).length ; j ++) {
2737 mapToAdd [i ][j ] = rows .get (i )[j ];
2838 }
2939 }
40+ // Add the map and reset our rows
3041 mapsList .add (mapToAdd );
3142 rows = new ArrayList <>();
3243 } else {
44+ // Read the line and add it's tokens (2, 1, 0) to the arraylist
3345 String [] tokens = next .split ("" );
3446 Integer [] row = new Integer [tokens .length ];
3547 if (lastLength < 0 ) lastLength = tokens .length ;
@@ -40,19 +52,23 @@ public MapGenerator() {
4052 }
4153 }
4254
55+ // Initialize the maps 3d array
4356 maps = new Integer [mapsList .size ()][][];
4457 for (int i = 0 ; i < mapsList .size (); i ++) {
4558 maps [i ] = mapsList .get (i );
4659 }
4760
61+ // Close the scanner
4862 scan .close ();
4963 } catch (Exception e ) {
5064 System .err .println ("Failed to read res/maps.txt! Levels will not be read.\n " );
51- // fnfe.printStackTrace();
65+ e .printStackTrace ();
66+
67+ // Create a default map 3d array
5268 maps = new Integer [][][] {
5369 {
5470 {1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 },
55- {1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 },
71+ {1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 1 },
5672 {1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 },
5773 {1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 },
5874 {1 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 },
@@ -77,104 +93,119 @@ public MapGenerator() {
7793 }
7894 }
7995
96+ // Return a specific level
8097 public Integer [][] getMap (int level ) {
8198 return maps [level ];
8299 }
83100
101+ // Maze Generation (Following a DFS algorithm). Makes a square maze with side lengths of a minimum size of 5
84102 public Integer [][] generateMaze (int size ) {
85103 if (size < 5 ) throw new IllegalArgumentException ("Argument " + size + " is less than 5" );
86104 Integer [][] maze = new Integer [size ][size ];
87105
106+ // By default, everything is a wall
88107 for (int i = 0 ; i < maze .length ; i ++) {
89108 for (int j = 0 ; j < maze [i ].length ; j ++) {
90109 maze [i ][j ] = 1 ;
91110 }
92111 }
93112
113+ // Create a Coord for the starting point, and make that spot empty
94114 Coord start = new Coord (1 , 1 );
95- maze [start .getY ()][start .getX ()] = 0 ;
115+ maze [start .y ][start .x ] = 0 ;
116+
117+ // Make a Stack for the DFS algorithm. Holds the visited coords that need to be rechecked
96118 Stack <Coord > coordStack = new Stack <Coord >();
97119 coordStack .add (start );
98120
121+ // Call DFS Generation, updating our maze 2d array
99122 dfsGeneration (start , coordStack , maze );
100123
124+ // Find a coordinate where I can put a finish line
101125 Coord goalCoord = new Coord (maze .length - 1 , maze [maze .length - 1 ].length - 1 );
102126
103- while (maze [goalCoord .getY ()][goalCoord .getX ()] == 1 && goalCoord .getY () > -1 && goalCoord .getX () > -1 ) {
104- if (maze [goalCoord .getY () - 1 ][goalCoord .getX ()] == 0 ) goalCoord = new Coord (goalCoord .getY () - 1 , goalCoord .getX ());
105- else if (maze [goalCoord .getY ()][goalCoord .getX () - 1 ] == 0 ) goalCoord = new Coord (goalCoord .getY (), goalCoord .getX () - 1 );
106- else if (maze [goalCoord .getY () - 1 ][goalCoord .getX () - 1 ] == 0 ) goalCoord = new Coord (goalCoord .getY () - 1 , goalCoord .getX () - 1 );
127+ // Start from the bottom right, and go up little by little to find a suitable spot for the end coordinate
128+ while (maze [goalCoord .y ][goalCoord .x ] == 1 && goalCoord .y > -1 && goalCoord .x > -1 ) {
129+ if (maze [goalCoord .y - 1 ][goalCoord .x ] == 0 ) goalCoord = new Coord (goalCoord .y - 1 , goalCoord .x );
130+ else if (maze [goalCoord .y ][goalCoord .x - 1 ] == 0 ) goalCoord = new Coord (goalCoord .y , goalCoord .x - 1 );
131+ else goalCoord = new Coord (goalCoord .y - 1 , goalCoord .x - 1 );
107132 }
108- maze [goalCoord .getY () ][goalCoord .getX () ] = 2 ;
133+ maze [goalCoord .y ][goalCoord .x ] = 2 ;
109134
110135 return maze ;
111136 }
112137
138+ // Recursive method to generate the maze
113139 private void dfsGeneration (Coord curr , Stack <Coord > coords , Integer [][] maze ) {
114- if (coords .size () == 0 ) return ;
115- ArrayList <Coord > availCoords = getAvailCoords (curr , maze );
116- if (availCoords .size () > 0 ) {
140+ if (coords .size () == 0 ) return ; // If the stack is exhausted, that means the generation is complete
141+ ArrayList <Coord > availCoords = getAvailCoords (curr , maze ); // Get the coordinates that we can move to
142+ if (availCoords .size () > 0 ) { // If there are available coordinate, randomly go to one of them, make it a 0 to designate a path, then move to that tile and check once more
117143 Coord randCoord = availCoords .get ((int )(Math .random () * availCoords .size ()));
118- maze [randCoord .getY () ][randCoord .getX () ] = 0 ;
144+ maze [randCoord .y ][randCoord .x ] = 0 ;
119145 coords .push (randCoord );
120146 dfsGeneration (randCoord , coords , maze );
121- } else {
147+ } else { // If there are no available spots, we need to recurse back down the stack
122148 Coord last = coords .pop ();
123149 dfsGeneration (last , coords , maze );
124150 }
125151 }
126152
153+ // Method to get the available coordinates the dfs can move to
127154 private ArrayList <Coord > getAvailCoords (Coord curr , Integer [][] maze ) {
128155
156+ // ArrayList to hold possible coordinates
129157 ArrayList <Coord > coords = new ArrayList <Coord >();
130158
131- int yPos = curr .getY () - 1 ;
159+ // Check above the current coordinate, and then to the left & right if there can be a wall there (there has to be walls on 3 sides around it)
160+ int yPos = curr .y - 1 ;
132161 if (yPos > 0 ) {
133- if (maze [yPos ][curr .getX () ] != 0 ) {
134- if ((yPos > 0 && maze [yPos - 1 ][curr .getX () ] == 1 ) && (curr .getX () > 0 && maze [yPos ][curr .getX () - 1 ] == 1 ) && (curr .getX () < maze [yPos ].length && maze [yPos ][curr .getX () + 1 ] == 1 )) {
135- coords .add (new Coord (yPos , curr .getX () ));
162+ if (maze [yPos ][curr .x ] != 0 ) {
163+ if ((yPos > 0 && maze [yPos - 1 ][curr .x ] == 1 ) && (curr .x > 0 && maze [yPos ][curr .x - 1 ] == 1 ) && (curr .x < maze [yPos ].length && maze [yPos ][curr .x + 1 ] == 1 )) {
164+ coords .add (new Coord (yPos , curr .x ));
136165 }
137166 }
138167 }
139168
140- yPos = curr .getY () + 1 ;
169+ // Same concept as previous, but check below
170+ yPos = curr .y + 1 ;
141171 if (yPos < maze .length ) {
142- if (maze [yPos ][curr .getX () ] != 0 ) {
143- if ((yPos < maze .length - 1 && maze [yPos + 1 ][curr .getX () ] == 1 ) && (curr .getX () > 0 && maze [yPos ][curr .getX () - 1 ] == 1 ) && (curr .getX () < maze [yPos ].length && maze [yPos ][curr .getX () + 1 ] == 1 )) {
144- coords .add (new Coord (yPos , curr .getX () ));
172+ if (maze [yPos ][curr .x ] != 0 ) {
173+ if ((yPos < maze .length - 1 && maze [yPos + 1 ][curr .x ] == 1 ) && (curr .x > 0 && maze [yPos ][curr .x - 1 ] == 1 ) && (curr .x < maze [yPos ].length && maze [yPos ][curr .x + 1 ] == 1 )) {
174+ coords .add (new Coord (yPos , curr .x ));
145175 }
146176 }
147177 }
148178
149- int xPos = curr .getX () - 1 ;
179+ // Same concept as previous, but check to the left
180+ int xPos = curr .x - 1 ;
150181 if (xPos > 0 ) {
151- if (maze [curr .getY () ][xPos ] != 0 ) {
152- if ((xPos > 0 && maze [curr .getY () ][xPos - 1 ] == 1 ) && (curr .getY () > 0 && maze [curr .getY () - 1 ][xPos ] == 1 ) && (curr .getY () < maze .length && maze [curr .getY () + 1 ][xPos ] == 1 )) {
153- coords .add (new Coord (curr .getY () , xPos ));
182+ if (maze [curr .y ][xPos ] != 0 ) {
183+ if ((xPos > 0 && maze [curr .y ][xPos - 1 ] == 1 ) && (curr .y > 0 && maze [curr .y - 1 ][xPos ] == 1 ) && (curr .y < maze .length && maze [curr .y + 1 ][xPos ] == 1 )) {
184+ coords .add (new Coord (curr .y , xPos ));
154185 }
155186 }
156187 }
157-
158- xPos = curr .getX () + 1 ;
159- if (xPos < maze [curr .getY ()].length ) {
160- if (maze [curr .getY ()][xPos ] != 0 ) {
161- if ((xPos < maze [curr .getY ()].length - 1 && maze [curr .getY ()][xPos + 1 ] == 1 ) && (curr .getY () > 0 && maze [curr .getY () - 1 ][xPos ] == 1 ) && (curr .getY () < maze .length && maze [curr .getY () + 1 ][xPos ] == 1 )) {
162- coords .add (new Coord (curr .getY (), xPos ));
188+
189+ // Same concept as previous, but check to the right
190+ xPos = curr .x + 1 ;
191+ if (xPos < maze [curr .y ].length ) {
192+ if (maze [curr .y ][xPos ] != 0 ) {
193+ if ((xPos < maze [curr .y ].length - 1 && maze [curr .y ][xPos + 1 ] == 1 ) && (curr .y > 0 && maze [curr .y - 1 ][xPos ] == 1 ) && (curr .y < maze .length && maze [curr .y + 1 ][xPos ] == 1 )) {
194+ coords .add (new Coord (curr .y , xPos ));
163195 }
164196 }
165197 }
166198
167- return coords ;
199+ return coords ; // Return the possible coordinates
168200 }
169201
202+ // Private class to hold an x/y coord
170203 private class Coord {
171204 private int y , x ;
172205 Coord (int y , int x ) {
173206 this .y = y ;
174207 this .x = x ;
175208 }
176- public int getY () { return this .y ; }
177- public int getX () { return this .x ; }
178209 }
179210
180211}
0 commit comments