-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWordPuzzle.java
321 lines (266 loc) · 11.9 KB
/
WordPuzzle.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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
//package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
/**
* Represents a character puzzle comprising a table of characters and a list of words.
* Provides utilities to parse the puzzle data from a BufferedReader,
* find occurrences of characters, and more.
*/
public class WordPuzzle implements Puzzle {
private final int nRows;
private final int nColumns;
private final String[][] data;
private final List<String> words;
public ArrayList<String> paths;
/**
* Constructs a new Puzzle instance.
*
* @param data A 2D array representing the character table of the puzzle.
* @param words A list of words associated with the puzzle.
*/
public WordPuzzle(String[][] data, List<String> words) {
this.nRows = data.length;
this.nColumns = data[0].length;
this.data = data;
this.words = words;
this.paths = new ArrayList<>();
}
/**
* Parses a BufferedReader to extract puzzle data and constructs a Puzzle object.
*
* @param reader The BufferedReader containing the puzzle data.
* @return A Puzzle object or null if the end of file is reached.
* @throws IOException if an I/O error occurs during reading.
*/
public static WordPuzzle parseChunk(BufferedReader reader) throws IOException {
// Read the first line to determine the dimensions of the puzzle
String line = reader.readLine();
if (line == null) {
return null; // No more data to read
}
// Extract number of rows and columns from the line
String[] dimensions = line.split(" ");
int nRows = Integer.parseInt(dimensions[0]);
int nColumns = Integer.parseInt(dimensions[1]);
// Initialize a 2D array to store the puzzle data
String[][] data = new String[nRows][nColumns];
// Read each row of the puzzle and populate the 2D array
for (int i = 0; i < nRows; i++) {
line = reader.readLine();
if (line != null) {
data[i] = line.split(" "); // Extract individual pieces of data from the read line
}
}
// Read the next line containing the list of words to search
line = reader.readLine();
List<String> words = new ArrayList<>();
if (line != null) {
String[] wordsArray = line.split(" ");
for (String word : wordsArray) {
words.add(word); // Populate the list with words
}
}
// Return a new Puzzle object with the parsed data and words
return new WordPuzzle(data, words);
}
/**
* Returns the list of words associated with the puzzle.
*
* @return A list of words.
*/
public List<String> getWords() {
return words;
}
/**
* Searches the puzzle data for all occurrences of a specific character.
*
* @param character The character to be located in the puzzle.
* @return A stack containing positions of the specified character.
*/
public ArrayStack<Position> findAllOccurrencesOfCharacter(String character) {
ArrayStack<Position> stack = new ArrayStack<>();
// Loop through each element in the data
for (int i = 0; i < nRows; i++) {
for (int j = 0; j < nColumns; j++) {
Position currentPosition = new Position(i, j);
// Utilize the isCharacterAtPosition method to check character equality
if (isCharacterAtPosition(currentPosition, character)) {
stack.push(currentPosition);
}
}
}
return stack;
}
/**
* Determines if the character at a specified position matches the provided character.
*
* @param position The position within the puzzle's grid to check.
* @param character The character for comparison.
* @return True if the characters match, otherwise false.
*/
public boolean isCharacterAtPosition(Position position, String character) {
// Validate the position to ensure it's within the grid boundaries
if (position.getRow() >= 0 && position.getRow() < nRows &&
position.getColumn() >= 0 && position.getColumn() < nColumns) {
// Check and return if the character at the position matches the provided character
return data[position.getRow()][position.getColumn()].equals(character);
}
// If position is out of grid boundaries, return false
return false;
}
/**
* Finds valid neighboring positions of a given position containing the specified character.
*
* @param pos The starting position for the search.
* @param character The character to identify valid neighbors.
* @return A stack containing valid neighboring positions.
*/
public ArrayStack<Position> findValidNeighbors(Position pos, String character) {
// Create a stack to hold valid neighbor positions.
ArrayStack<Position> stack = new ArrayStack<>();
// Define relative positions of all possible neighbors (self, left, right, up, down, and 4 diagonals).
// Each array represents the [rowOffset, columnOffset] relative to the original position.
int[][] neighbors = {
{0, 0}, {-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}
};
// Loop over each possible relative neighbor.
for(int[] n : neighbors) {
// Calculate the absolute position of the neighbor by adding the offsets to the original position.
int newRow = pos.getRow() + n[0];
int newCol = pos.getColumn() + n[1];
// Validate if the neighbor is within the puzzle boundaries.
// - Row should be >= 0 and < total number of rows (nRows).
// - Column should be >= 0 and < total number of columns (nColumns).
if(newRow >= 0 && newRow < nRows && newCol >= 0 && newCol < nColumns) {
// Check if the neighbor contains the desired character.
// If true, add the neighbor's position to the stack.
if(isCharacterAtPosition(new Position(newRow, newCol), character)) {
stack.push(new Position(newRow, newCol));
}
}
}
// Return the stack containing all valid neighbors.
return stack;
}
/**
* Constructs and prints a representation of the specified word using a sequence of positions.
*
* @param word The word to represent.
* @param positionsDeque The sequence of positions forming the word.
*/
public void printDequeOfPositions(String word, ArrayDeque<Position> positionsDeque) {
// Using StringBuilder for efficient string concatenation.
StringBuilder output = new StringBuilder(word + ": ");
// Initialize iterator for positionsDeque to sequentially access its items.
Iterator<Position> iterator = positionsDeque.iterator();
// Loop through the positions in the deque.
while (iterator.hasNext()) {
// Append the string representation of the next position.
output.append(iterator.next().getPositionString());
// If there's another position after the current one, append an arrow.
if (iterator.hasNext()) {
output.append("->");
}
}
// Print the concatenated string.
// System.out.println(output.toString());
// Add the concatenated string to the paths list.
this.paths.add(output.toString());
}
/**
* Sorts the paths list alphabetically.
*
* This method uses Java's built-in Collections.sort() function to
* arrange the strings inside the paths list in lexicographic order.
* It modifies the original list, so after calling this method,
* accessing the paths list will give the sorted paths.
*/
public void sortPathsAlphabetically() {
// Use Collections.sort() to sort the paths list in place
Collections.sort(this.paths);
}
/**
* Prints the paths list in lexicographic order.
*
* This method first sorts the paths list alphabetically using
* the sortPathsAlphabetically function. Then, it iterates over
* each path and prints it. This ensures that the output is always
* presented in a sorted manner.
*/
public void printSortedPaths() {
// First, sort the paths list alphabetically
sortPathsAlphabetically();
// Iterate over each path in the sorted paths list and print it
for(String path : paths) {
System.out.println(path);
}
}
/**
* Checks if the characters at the specified positions form the given word.
*
* @param positionsDeque The sequence of positions to be validated.
* @param word The target word for validation.
* @return True if the sequence of characters matches the word, otherwise false.
*/
public boolean validateWord(ArrayDeque<Position> positionsDeque, String word) {
StringBuilder constructedWord = new StringBuilder();
// Clone the positionsDeque to avoid altering the original while processing
ArrayDeque<Position> positionsDequeCopy = positionsDeque.clone();
// Temporary stack to retain the original order of positionsDeque
ArrayStack<Position> tempStack = new ArrayStack<>();
// Process each position from the deque to construct the word
while (!positionsDeque.isEmpty()) {
Position pos = positionsDeque.removeFirst();
String ch = data[pos.getRow()][pos.getColumn()];
constructedWord.append(ch);
// Push position to temp stack to restore order later
tempStack.push(pos);
}
// Restore the order in the original positionsDeque
while (!tempStack.isEmpty()) {
positionsDeque.addFirst(tempStack.pop());
}
// If the constructed word matches the provided word, print the sequence of positions
if (constructedWord.toString().equals(word)) {
printDequeOfPositions(word, positionsDequeCopy);
}
// Return whether the constructed word matches the provided word
return constructedWord.toString().equals(word);
}
/**
* Computes possible sequences of positions to form the target word.
*
* @param positionsDeque A deque containing the current sequence of positions.
* @param targetWord The word to be formed.
* @param i The current character index of the target word being processed.
*/
public void computeTreeOfWords(ArrayDeque<Position> positionsDeque, String targetWord, int i) {
// Check if the current sequence of positions forms the target word
if (positionsDeque.size() == targetWord.length()) {
boolean is_valid = validateWord(positionsDeque, targetWord);
// Note: The is_valid variable is computed, but not used in this snippet.
}
// If we've processed each character in the target word, end the recursive call
if (i == targetWord.length()) {
return;
}
// Search for valid neighboring positions that match the current character of the target word
char character = targetWord.charAt(i);
ArrayStack<Position> neighbors = findValidNeighbors(positionsDeque.getLast(), "" + character);
// If no valid neighbors are found, end the recursive call
if (neighbors.isEmpty()) {
return;
}
// For each valid neighbor, recursively try to form the remainder of the target word
while (!neighbors.isEmpty()) {
positionsDeque.addLast(neighbors.pop());
computeTreeOfWords(positionsDeque, targetWord, i + 1);
positionsDeque.removeLast();
}
}
}