-
Notifications
You must be signed in to change notification settings - Fork 0
/
GameState_DC.java
303 lines (268 loc) · 9.58 KB
/
GameState_DC.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
/**
* This class keeps track of the current state of a game. It tracks the board size, grid,
* current player (1, 2), current action (move, destroy), and current move number. In
* addition, it has two players and uses these two players to get actions. The toString()
* method provides a readable string output, and the draw() method draws the board on a
* canvas using the StdDraw library.
*
* @author David Chen
* @version Java 1.8.0 - 3/17/20
*/
import java.util.ArrayList;
import java.awt.Font;
public class GameState_DC {
private int boardSize;
private String[][] grid;
private int currPlayer;
private String currAction;
private int currMoveNum;
Player_DC player1;
MinimaxAI_DC player2;
// initialize empty grid (all ""), then place two player pieces
public GameState_DC(int boardSize, Player_DC player1, MinimaxAI_DC player2, int currPlayer) {
this.boardSize = boardSize;
this.player1 = player1;
this.player2 = player2;
grid = new String[boardSize][boardSize];
for (int r = 0; r < boardSize; r++) {
for (int c = 0; c < boardSize; c++) {
grid[r][c] = "";
}
}
grid[player1.getCurrR()][player1.getCurrC()] = "1";
grid[player2.getCurrR()][player2.getCurrC()] = "2";
this.currPlayer = currPlayer;
currAction = "move";
currMoveNum = 0;
}
// used for cloning GameState
public GameState_DC(GameState_DC gameState) {
this.boardSize = gameState.boardSize;
this.grid = new String[boardSize][boardSize];
for (int r = 0; r < boardSize; r++) {
for (int c = 0; c < boardSize; c++) {
this.grid[r][c] = gameState.grid[r][c];
}
}
this.currPlayer = gameState.currPlayer;
this.currAction = gameState.currAction;
this.currMoveNum = gameState.currMoveNum;
this.player1 = new Player_DC(gameState.player1);
this.player2 = new MinimaxAI_DC(gameState.player2);
}
// place pieces in spots
public void makeMove(int[] move) {
int moveR = move[0];
int moveC = move[1];
grid[moveR][moveC] = Integer.toString(currPlayer);
if (currPlayer == 1) {
grid[player1.getCurrR()][player1.getCurrC()] = "";
player1.setCurrR(moveR);
player1.setCurrC(moveC);
}
else {
grid[player2.getCurrR()][player2.getCurrC()] = "";
player2.setCurrR(moveR);
player2.setCurrC(moveC);
}
currMoveNum++;
currAction = "destroy";
}
// destroy given spot[]
public void destroyLoc(int[] destroy) {
grid[destroy[0]][destroy[1]] = "X";
changePlayer();
currMoveNum++;
currAction = "move";
}
// swap player
public void changePlayer() {
// if (currPlayer.equals("1")) {
// currPlayer = "2";
// }
// else {
// currPlayer = "1";
// }
currPlayer = 3 - currPlayer;
}
// returns 0 if no winner, 1 or 2 if either player has won
public int gameWinner() {
int numP1Moves = getPossibleMoves(1).size();
int numP2Moves = getPossibleMoves(2).size();
if (numP1Moves == 0 && numP2Moves == 0) { // tie game
return 3;
}
else if (numP1Moves == 0) {
return 2;
}
else if (numP2Moves == 0) {
return 1;
}
return 0;
}
public ArrayList<int[]> getPossibleMoves(int playerNum) {
ArrayList<int[]> possibleMoves = new ArrayList<int[]>();
int pR, pC;
if (playerNum == 1) {
pR = player1.getCurrR();
pC = player1.getCurrC();
}
else {
pR = player2.getCurrR();
pC = player2.getCurrC();
}
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (!(i == 0 && j == 0) && 0 <= pR + i && pR + i < boardSize && 0 <= pC + j && pC + j < boardSize) {
if (grid[pR + i][pC + j].isEmpty() ) {
int move[] = {pR + i, pC + j};
possibleMoves.add(move);
}
}
}
}
return possibleMoves;
}
public ArrayList<int[]> getPossibleActions() {
if (currAction.equals("move")) {
if (currPlayer == 1) {
return getPossibleMoves(1);
}
else {
return getPossibleMoves(2);
}
}
else { // destroy
ArrayList<int[]> possibleDestroy = new ArrayList<int[]>();
// look for places to destroy in the 5x5 grid surrounding each player
// doesn't check all possible empty tiles, gives some speedup
int p1R = player1.getCurrR();
int p1C = player1.getCurrC();
int p2R = player2.getCurrR();
int p2C = player2.getCurrC();
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < boardSize; j++) {
if (grid[i][j].isEmpty()) {
if ((Math.abs(i - p1R) <= 2 && Math.abs(j - p1C) <= 2) ||
Math.abs(i - p2R) <= 2 && Math.abs(j - p2C) <= 2) {
int destroy[] = {i, j};
possibleDestroy.add(destroy);
}
}
}
}
return possibleDestroy;
}
}
// take the action in the current state -> return new state
public GameState_DC generateSuccessor(int[] action) {
GameState_DC newState = new GameState_DC(this);
if (newState.getCurrAction().equals("move")) {
newState.makeMove(action);
}
else {
newState.destroyLoc(action);
}
return newState;
}
public boolean isValidMove(Player_DC player, int[] move) {
int r = move[0];
int c = move[1];
return (Math.abs(r - player.getCurrR()) <= 1 && Math.abs(c - player.getCurrC()) <= 1 && grid[r][c].isEmpty() );
}
public boolean isValidDestroy(int[] destroy) {
int r = destroy[0];
int c = destroy[1];
return (0 <= r && r < boardSize && 0 <= c && c < boardSize && grid[r][c].isEmpty() );
}
public void stdDrawInit() {
StdDraw.setCanvasSize(100 * boardSize, 100 * (boardSize + 1));
StdDraw.setXscale(0, boardSize);
StdDraw.setYscale(0, boardSize + 1);
Font font = new Font("Sans Serif", Font.PLAIN, 30);
StdDraw.setFont(font);
StdDraw.enableDoubleBuffering();
}
// given a row and column, convert to X and Y for drawing on canvas
public double[] convertRCToXY(double r, double c) {
// in 2D array, we use rows and columns
// however, to draw we need X and Y where (0, 0) is bottom left
// we swap rows and columns and then subtract column from total height
double[] xy = {c + 0.5, boardSize - r - 0.5};
return xy;
}
public void draw() {
StdDraw.clear();
// draw horizontal and vertical gridlines
for (int i = 0; i <= boardSize; i++) {
StdDraw.line(0, i, boardSize, i);
StdDraw.line(i, 0, i, boardSize);
}
// draw game pieces and destroyed tiles
for (int r = 0; r < boardSize; r++) {
for (int c = 0; c < boardSize; c++) {
double[] xy = convertRCToXY(r, c);
double x = xy[0];
double y = xy[1];
if (grid[r][c].equals("1")) { // p1
StdDraw.setPenColor(StdDraw.GREEN);
StdDraw.filledCircle(x, y, 0.5);
StdDraw.setPenColor(StdDraw.BLACK);
}
else if (grid[r][c].equals("2")) { // p2
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.filledCircle(x, y, 0.5);
StdDraw.setPenColor(StdDraw.BLACK);
}
else if (grid[r][c].equals("X")) { // destroyed
StdDraw.filledSquare(x, y, 0.5);
}
}
}
// text info above board: 'Player 1: destroy'
String currInfo = "Player " + currPlayer + ": " + currAction;
if (gameWinner() != 0) { // game over
currInfo = "Player " + gameWinner() + " wins! Click to EXIT.";
}
StdDraw.text(boardSize / 2.0, boardSize + 0.5, currInfo);
StdDraw.show();
}
public int[] getP1Pos() {
int[] rc = {player1.getCurrR(), player1.getCurrC()};
return rc;
}
public int[] getP2Pos() {
int[] rc = {player2.getCurrR(), player2.getCurrC()};
return rc;
}
public int getCurrPlayer() {
return currPlayer;
}
public String getCurrAction() {
return currAction;
}
public int getMoveNum() {
return currMoveNum;
}
public void setMinimaxDepth(int depth) {
player2.setDepth(depth);
}
public int getMinimaxDepth() {
return player2.getDepth();
}
public String toString() {
String board = "";
for (int r = 0; r < boardSize; r++) {
for (int c = 0; c < boardSize; c++) {
if (grid[r][c].isEmpty() ) { // no pieces here
board += " -";
}
else { // if not empty then it's 1, 2, or X
board += " " + grid[r][c];
}
}
board += "\n"; // add newline to split rows
}
return board;
}
}