-
Notifications
You must be signed in to change notification settings - Fork 1
/
Maze.java
241 lines (197 loc) · 7.9 KB
/
Maze.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
Represent a Maze with an Explorer in it
A "MazeTerminal" is...
o a wall element; or
o a treasure; or
o a stepping stone.
A "Maze" is...
o a MazeTerminal; or
o a stepping stone with a Maze as any of its 4 neighbors
plus an optional explorer positioned on any element of the Maze.
*/
import java.util.Scanner;
public class Maze {
// MazeTerminal named constants
public final static int TREASURE = 0;
public final static int WALL = 1;
public final static int STEPPING_STONE = 2;
// directions that can be searched
public final static int EAST = 1;
public final static int NORTH = 2;
public final static int WEST = 4;
public final static int SOUTH = 8;
/* Values are pretty arbitrary. Values of 2^n might be useful
in the unlikely event that we ever want to add north-west, etc.:
2+4 --> 6 */
private int[][] maze;
private final static int MAX_RANKS = 64;
public int rankCount; // number of ranks in a constructed Maze
private Vector explorerPosition; // see Vector inner class, below
/**
Construct an instance from the contents of a file.
For v0, maze is rectangular, with every line having the same length.
*/
public Maze( String sourceFilename
, int explorerRank, int explorerFile
) throws java.io.FileNotFoundException {
/* Construct the maze array one rank at a time, in case
we ever allow non-rectangular mazes */
maze = new int[ MAX_RANKS][];
Scanner sc = new Scanner( new java.io.File( sourceFilename));
sc.useDelimiter(""); // Whitespaces are data, not delimiters.
// process the maze file
while( sc.hasNextLine() ) {
int rank = rankCount++;
/* So rankCount == last rank +1, as usual.
That is, rankCount is one larger than the number of
the last-used rank.
*/
String line = sc.nextLine();
// System.out.println( "|" + line + "|");
maze[ rank] = new int[ line.length()];
// Convert the input line into maze elements.
for( int file = 0; file < line.length(); file++ ) {
String inChar = line.substring( file, file+1);
int element; // value destined for maze array
if( inChar.equals("0")) element = TREASURE;
else if( inChar.equals("*")) element = STEPPING_STONE;
// spaces and unrecognised characters are walls
else element = WALL;
maze[ rank][ file] = element;
}
}
explorerPosition = new Vector().add( explorerRank, explorerFile);
// // for debugging: report explorer's location
// System.out.println( "explorer at " + explorerPosition.rank
// + ", " + explorerPosition.file);
}
/**
Copy-construct an instance.
Deep copy of all instance fields.
*/
public Maze( Maze old) {
// Copy the explorer's position (code by Holmes is asserted to work)
maze = new int[ MAX_RANKS][];
for (int i = 0; i < old.rankCount; i++) {
maze[i] = new int[old.maze[i].length];
for (int j = 0; j < old.maze[i].length; j++) {
maze[i][j] = old.maze[i][j];
}
}
rankCount = old.rankCount;
explorerPosition = new Vector().add( old.explorerPosition.rank, old.explorerPosition.file);
//throw new java.lang.RuntimeException(
//"Write code to copy the maze[][] array and rankCount.");
}
/**
@return a string representing of this instance
*/
public String toString() {
/* characters that represent elements of the maze,
indexed by the numbers used to represent elements
*/
final String outChar = "0 *"; // no explorer here
final String exOnTop = "!Ee"; /* explorer on top of
treasure, wall, stepping stone, etc. */
// build string for top and bottom separators
String aboveAndBelow = "-";
for( int file = 0; file < maze[0].length; file++)
aboveAndBelow += "-";
aboveAndBelow += "-" + System.lineSeparator();
// process maze[][], explorer
String result = aboveAndBelow;
for( int rank = 0; rank < rankCount; rank++) {
result += "|";
for( int file = 0; file < maze[ rank].length; file++) {
int elem = maze[ rank][ file];
// choose from the appropriate character set,
if( explorerPosition != null
&& explorerPosition.equals( rank, file)
)
result += exOnTop.substring( elem, elem+1);
else result += outChar.substring( elem, elem+1);
}
result += "|" + System.lineSeparator();
}
return result + aboveAndBelow;
}
/**
Move the Explorer a step in the indicated direction.
Attempting to position the explorer outside the maze means
it has no position.
@precondition: explorer starts in a valid position
*/
public void go( int direction) {
switch( direction) {
case EAST:
explorerPosition = explorerPosition.add( 0, 1);
break;
case NORTH:
explorerPosition = explorerPosition.add( -1, 0);
break;
case WEST:
explorerPosition = explorerPosition.add( 0, -1);
break;
case SOUTH:
explorerPosition = explorerPosition.add( 1, 0);
break;
}
}
/**
Modify the maze to have @mazeElement in the explorer's position.
Nix dropping treasure.
*/
public void dropA( int mazeElement) {
if( mazeElement != TREASURE)
maze[ explorerPosition.rank][ explorerPosition.file] = mazeElement;
}
/**
@return the MazeElement that the explorer is on.
When the explorer's position is null, return WALL
because the user-programmer's code is expected to benefit
from that equivalence.
*/
public int explorerIsOnA() {
if( explorerPosition == null) return WALL;
else return maze[ explorerPosition.rank][ explorerPosition.file];
}
/**
a pair of rank & file that can represent...
o a displacement from the current location
o a location in a maze, being a displacement from (0,0)
A location outside the maze is represented by a null Vector.
*/
private class Vector {
private int rank, file;
// The no-arg constructor produces [0, 0]
private Vector() {}
// copy-constructor
private Vector( Vector old) {
rank = old.rank;
file = old.file;
}
/* For other rank and file values, use add so that the Vector
will be null if the displacement exceeds the maze bounds.
There is no constructor with rank and file arguments because
a constructor cannot produce a null.
*/
private Vector add( int ranks, int files) {
rank += ranks;
file += files;
// // for debugging: report resulting position
// System.out.println( "sum: " + rank + " / " + rankCount
// + ", " + file + " / " + maze[ rank].length );
// still in bounds?
if( 0 <= rank && rank < rankCount
&& 0 <= file && file < maze[ rank].length
) return this;
else return null; // outside maze
}
/**
@return whether this Vector matches the parameters
*/
private boolean equals( int rank, int file) {
return this.rank == rank && this.file == file;
}
}
}