-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGame.java
313 lines (258 loc) · 9.15 KB
/
Game.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
import java.util.*;
public class Game {
final int boardSize;
final int centerLocation;
Node goal;
Node root;
Map<Long, Long> nodeMap;
ArrayList<Node> solutionPath;
Stack<Long> stackFrontier;
LinkedList<Node> queueFrontier;
Map<Long,Boolean> endBoards;
long rootReached;
//a map of possible jumps that can be made to any given
//spot on the
Map<Integer, ArrayList<Integer>> jumps;
public Game() {
rootReached = -1;
stackFrontier = new Stack<>();
endBoards = new HashMap<Long, Boolean>();
queueFrontier = new LinkedList<>();
boardSize = 33;
centerLocation = 16;
solutionPath = new ArrayList<>();
nodeMap = new HashMap<>();
goal = new Node(generateGoalNode());
root = new Node(generateRootNode());
nodeMap.put(root.getCode(),rootReached );
jumps = new HashMap<>(33);
initializeJumps();
}
public void playGame() {
//start at a node
//generate adjacent nodes
//add adjacent nodes to stack
//start again
//if goal node reached stop
//generate solution path
int nodesExpanded = 0;
stackFrontier.push(root.getCode());
while (!stackFrontier.isEmpty()) {
long curr = stackFrontier.pop();
nodesExpanded++;
//if goal reached then HALT
if (curr == goal.getCode()) {
printResult(nodesExpanded);
break;
}
//otherwise continue to traverse
exploreAdjacentNodes(curr);
}
}
private void printResult(int nodesExpanded) {
System.out.println("Solution found. " + nodesExpanded + " nodes expanded... Wow thats alot of nodes man.");
System.out.println("Here is the solution path: ");
printSolutionPath();
System.out.println("DFS failed " + (endBoards.size() - 1) + " times at peg Solitaire Before finding the solution");
printEndBoards();
}
public void printEndBoards() {
for (long code: endBoards.keySet()) {
Node node = convertCodeToNode(code);
if(node.numPegs == 1)
node.printGameBoard();
}
}
private void printSolutionPath() {
long currCode = goal.getCode();
System.out.println("Solution Path: ");
Stack<Node> solStack = new Stack<>();
while(currCode != rootReached) {
solStack.push(convertCodeToNode(currCode));
currCode = nodeMap.get(currCode);
}
while(!solStack.isEmpty()) {
solStack.pop().printGameBoard();
}
}
//Converts game board code to a Node
public Node convertCodeToNode(long currCode) {
Stack<Byte> stack = new Stack<>();
for(int i = 0; i < boardSize; i++) {
stack.push((byte) (currCode & 1));
currCode = currCode >> 1;
}
byte[] arr = new byte[boardSize];
for(int i = 0; i < boardSize; i ++) {
if(!stack.isEmpty()) {
arr[i] = stack.pop();
} else {
arr[i] = 0;
}
}
return new Node(arr);
}
//bug found where I was not appending zeros to gameBoard array so it had empty spots
/*
* Generates and adds adjacent nodes to stack
* */
public void exploreAdjacentNodes(long currCode) {
Node node = convertCodeToNode(currCode);
byte[] currBoard = node.getGameBoard();
boolean isEndBoard = true;
for (int i = 0; i < boardSize; i++) {
//find empty spot on game board
if (currBoard[i] == 0) {
//get coordinates of spaces on board 2 away from empty spot
ArrayList<Integer> currJumps = jumps.get(i);
//find out if those jumps are possible with current board
for (int j = 0; j < currJumps.size(); j++) {
//if jumps possible then create a new node and add it to stack
if (currBoard[currJumps.get(j)] == 1 && currBoard[findNewEmp(i,currJumps.get(j))] == 1 ) {
isEndBoard = false;
Node adjNode = makeAdjNode(currBoard, i, currJumps.get(j));
//add parent to adjNode
//check board not already seen
if(!nodeMap.containsKey(adjNode.getCode())) {
stackFrontier.push(adjNode.getCode());
nodeMap.put(adjNode.getCode(), node.getCode());
}
}
}
}
}
if(isEndBoard && !endBoards.containsKey(currCode))
endBoards.put(currCode,true);
}
//Bug causing game to keep jumping around infinitely and never reach end
//
/*
parentBoard == game of parent node
newFill == previously empty spot ball moved to
newEmp == spot ball was moved from now empty
We use findNewEmp to calculate the spot that is now a 0
*/
public Node makeAdjNode(byte[] parentBoard, int newFill, int newEmp) {
byte[] newBoard = new byte[boardSize];
for (int i = 0; i < boardSize; i++) {
newBoard[i] = parentBoard[i];
}
newBoard[newEmp] = 0;
newBoard[newFill] = 1;
newBoard[findNewEmp(newFill, newEmp)] = 0;
return new Node(newBoard);
}
//returns the index of the ball that was jumped over from newEmp to newFill
public int findNewEmp(int newFill, int newEmp) {
//case 1a: horizontal jump from left to right
if (newFill - newEmp == 2) {
return newFill - 1;
}
//case 1b: horizontal right to left jump
if (newEmp - newFill == 2) {
return newEmp - 1;
}
//case 2a: vertical jump down
if (newFill - newEmp > 0) {
return gridToIndex(indexToGrid(newFill) - 7);
}
//case 2b: vertical jump up
if (newFill - newEmp < 0) {
return gridToIndex(indexToGrid(newFill) + 7);
}
return -69;
}
//initializes map of all possible jumps (spaces on board two away orthogonally
public void initializeJumps() {
for (int i = 0; i < boardSize; i++) {
jumps.put(i, generateJumps(i));
}
}
//generate list of all possible jumps to index
public ArrayList<Integer> generateJumps(int boardIndex) {
ArrayList<Integer> jumpIndexes = new ArrayList<>();
int ind = indexToGrid(boardIndex);
//case 1 index is too close to left wall so has no left jump
if (ind % 7 == 0 || ind % 7 == 1) {
jumpIndexes.add(ind - 14);
jumpIndexes.add(ind + 2);
jumpIndexes.add(ind + 14);
}
//case 2 index is too close to right wall so has no right jump
else if (ind % 7 == 6 || ind % 7 == 5) {
jumpIndexes.add(ind - 14);
jumpIndexes.add(ind - 2);
jumpIndexes.add(ind + 14);
}
//case 3 all jumps can be made
else {
jumpIndexes.add(ind - 14);
jumpIndexes.add(ind - 2);
jumpIndexes.add(ind + 14);
jumpIndexes.add(ind + 2);
}
jumpIndexes = sanitize(jumpIndexes);
return jumpIndexes;
}
//Convert an index of the game board one of a 7*7 grid numbered
// with top left == 0 and bottom right == 48
public int indexToGrid(int index) {
if (index < 3) {
return index + 2;
} else if (index < 6) {
return index + 6;
} else if (index < 27) {
return index + 8;
} else if (index < 30) {
return index + 10;
} else {
return index + 14;
}
}
//converts grid coords to index on gameboard
//returns -1 if coords not on game board
public int gridToIndex(int grid) {
if (grid < 2) {
return -1;
}
if (grid < 5) {
return grid - 2;
} else if (grid < 12 && grid > 8) {
return grid - 6;
} else if (grid < 35 && grid > 13) {
return grid - 8;
} else if (grid < 40 && grid > 36) {
return grid - 10;
} else if (grid < 47 && grid > 43) {
return grid - 14;
} else {
return -1;
}
}
//Array of grid indexes converted to an array of valid game board indexes
public ArrayList<Integer> sanitize(ArrayList<Integer> arr) {
for (int i = 0; i < arr.size(); i++) {
int curr = gridToIndex(arr.get(i));
if (curr > 33 || curr < 0) {
arr.remove(i);
i--;
} else
arr.set(i, curr);
}
return arr;
}
private byte[] generateGoalNode() {
byte[] goalBoard = new byte[boardSize];
for (int i = 0; i < boardSize; i++)
goalBoard[i] = 0;
goalBoard[16] = 1;
return goalBoard;
}
private byte[] generateRootNode() {
byte[] rootBoard = new byte[boardSize];
for (int i = 0; i < boardSize; i++)
rootBoard[i] = 1;
rootBoard[16] = 0;
return rootBoard;
}
}