-
Notifications
You must be signed in to change notification settings - Fork 0
/
Board.java
332 lines (280 loc) · 7.93 KB
/
Board.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
322
323
324
325
326
327
328
329
330
// Board.java
import java.util.Arrays;
import static com.sun.tools.javac.jvm.ByteCodes.swap;
/**
CS108 Tetris Board.
Represents a Tetris board -- essentially a 2-d grid
of booleans. Supports tetris pieces and row clearing.
Has an "undo" feature that allows clients to add and remove pieces efficiently.
Does not do any drawing or have any idea of pixels. Instead,
just represents the abstract 2-d board.
*/
public class Board {
// Some ivars are stubbed out for you:
private int width;
private int height;
protected boolean[][] grid;
private boolean DEBUG = true;
boolean committed;
protected int[] heightsArr;
protected int[] widthsArr;
private int maxHeight;
//backups
private boolean[][] gridBackup;
private int[] heightsArrBackup;
private int[] widthsArrBackup;
private int maxHeightBackup;
/**
Creates an empty board of the given width and height
measured in blocks.
*/
public Board(int width, int height) {
this.width = width;
this.height = height;
grid = new boolean[width][height];
committed = true;
heightsArr = new int[width];
Arrays.fill(heightsArr, 0);
widthsArr = new int[height];
Arrays.fill(widthsArr, 0);
maxHeight = 0;
//backups
gridBackup = new boolean[width][height];
heightsArrBackup = new int[width];
Arrays.fill(heightsArrBackup, 0);
widthsArrBackup = new int[height];
Arrays.fill(widthsArrBackup, 0);
maxHeightBackup = 0;
}
/**
Returns the width of the board in blocks.
*/
public int getWidth() { return width; }
/**
Returns the height of the board in blocks.
*/
public int getHeight() { return height; }
/**
Returns the max column height present in the board.
For an empty board this is 0.
*/
public int getMaxHeight() { return maxHeight; }
/**
Checks the board for internal consistency -- used
for debugging.
*/
public void sanityCheck() {
if (!DEBUG)
return;
int[] wArr = new int[height];
int[] hArr = new int[width];
int maxH = Integer.MIN_VALUE;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (grid[i][j]) {
hArr[i] = j + 1;
wArr[j]++;
}
maxH = Math.max(maxH, hArr[i]);
}
}
if (maxH != getMaxHeight())
throw new RuntimeException("Max Height is invalid. expected: " + maxH + "; got: " + maxHeight );
if (!Arrays.equals(wArr, widthsArr))
throw new RuntimeException("Widths array is invalid");
if (!Arrays.equals(hArr, heightsArr))
throw new RuntimeException("Heights array is invalid");
}
/**
Given a piece and an x, returns the y
value where the piece would come to rest
if it were dropped straight down at that x.
<p>
Implementation: use the skirt and the col heights
to compute this fast -- O(skirt length).
*/
public int dropHeight(Piece piece, int x) {
int[] skirt = piece.getSkirt();
int res = Integer.MIN_VALUE;
for (int i = 0; i < skirt.length; i++) {
res = Math.max(res, heightsArr[x + i] - skirt[i]);
}
return res;
}
/**
Returns the height of the given column --
i.e. the y value of the highest block + 1.
The height is 0 if the column contains no blocks.
*/
public int getColumnHeight(int x) {
return heightsArr[x];
}
/**
Returns the number of filled blocks in
the given row.
*/
public int getRowWidth(int y) {
return widthsArr[y];
}
/**
Returns true if the given block is filled in the board.
Blocks outside of the valid width/height area
always return true.
*/
public boolean getGrid(int x, int y) {
return !inBounds(x, y) || grid[x][y];
}
public static final int PLACE_OK = 0;
public static final int PLACE_ROW_FILLED = 1;
public static final int PLACE_OUT_BOUNDS = 2;
public static final int PLACE_BAD = 3;
/**
Attempts to add the body of a piece to the board.
Copies the piece blocks into the board grid.
Returns PLACE_OK for a regular placement, or PLACE_ROW_FILLED
for a regular placement that causes at least one row to be filled.
<p>Error cases:
A placement may fail in two ways. First, if part of the piece may falls out
of bounds of the board, PLACE_OUT_BOUNDS is returned.
Or the placement may collide with existing blocks in the grid
in which case PLACE_BAD is returned.
In both error cases, the board may be left in an invalid
state. The client can use undo(), to recover the valid, pre-place state.
*/
public int place(Piece piece, int x, int y) {
// flag !committed problem
if (!committed) throw new RuntimeException("place commit problem");
saveState();
committed = false;
int boardX, boardY;
int res = PLACE_OK;
for (TPoint pt : piece.getBody()) {
boardX = x + pt.x;
boardY = y + pt.y;
if (!inBounds(boardX, boardY))
return PLACE_OUT_BOUNDS;
if (grid[boardX][boardY])
return PLACE_BAD;
grid[boardX][boardY] = true;
heightsArr[boardX] = Math.max (heightsArr[boardX], boardY + 1);
maxHeight = Math.max(maxHeight, heightsArr[boardX]);
if (++widthsArr[boardY] == width) {
res = PLACE_ROW_FILLED;
}
}
sanityCheck();
return res;
}
private void saveState() {
maxHeightBackup = maxHeight;
System.arraycopy(heightsArr, 0, heightsArrBackup, 0, width);
System.arraycopy(widthsArr, 0, widthsArrBackup, 0, height);
for (int i = 0; i < width; i++) {
System.arraycopy(grid[i], 0, gridBackup[i], 0, height);
}
}
private boolean inBounds(int x, int y) {
return x > -1 && x < width && y > -1 && y < height;
}
/**
Deletes rows that are filled all the way across, moving
things above down. Returns the number of rows cleared.
*/
public int clearRows() {
if (committed)
saveState();
committed = false;
int rowsCleared = 0;
int toRow = 0;
for (int i=0; i<maxHeight; i++) {
if (getRowWidth(i) != width) {
copyRow (toRow, i);
widthsArr[toRow] = getRowWidth(i);
toRow++;
} else {
rowsCleared ++;
}
}
int oldMaxHeight = maxHeight;
adjustHeightsArr(rowsCleared);
adjustFalseRows(oldMaxHeight - maxHeight);
sanityCheck();
return rowsCleared;
}
private void adjustHeightsArr(int rowsCleared) {
maxHeight = Integer.MIN_VALUE;
for (int i = 0; i<heightsArr.length; i++) {
heightsArr[i] -= rowsCleared;
while (heightsArr[i] - 1 >= 0 && !grid[i][heightsArr[i] - 1]){
heightsArr[i]--;
}
maxHeight = Math.max(maxHeight, heightsArr[i]);
}
}
private void adjustFalseRows(int rowsCleared) {
for (int i = 0; i < rowsCleared; i++) {
widthsArr[maxHeight + i] = 0;
for (int j =0; j < width; j++)
grid[j][maxHeight + i] = false;
}
}
private void copyRow(int toRow, int fromRow) {
// System.out.println("to -> " + toRow + " from -> " + fromRow);
for (int i = 0; i < width; i++) {
grid[i][toRow] = grid[i][fromRow];
}
}
/**
Reverts the board to its state before up to one place
and one clearRows();
If the conditions for undo() are not met, such as
calling undo() twice in a row, then the second undo() does nothing.
See the overview docs.
*/
public void undo() {
if (committed)
return;
Object temp = grid;
grid = gridBackup;
gridBackup = (boolean[][])temp;
temp = heightsArr;
heightsArr = heightsArrBackup;
heightsArrBackup = (int[])temp;
temp = widthsArr;
widthsArr = widthsArrBackup;
widthsArrBackup = (int[])temp;
temp = maxHeight;
maxHeight = maxHeightBackup;
maxHeightBackup = maxHeight;
committed = true;
sanityCheck();
}
/**
Puts the board in the committed state.
*/
public void commit() {
committed = true;
}
/*
Renders the board state as a big String, suitable for printing.
This is the sort of print-obj-state utility that can help see complex
state change over time.
(provided debugging utility)
*/
public String toString() {
StringBuilder buff = new StringBuilder();
for (int y = height-1; y>=0; y--) {
buff.append('|');
for (int x=0; x<width; x++) {
if (getGrid(x,y)) buff.append('+');
else buff.append(' ');
}
buff.append("|\n");
}
for (int x=0; x<width+2; x++) buff.append('-');
return(buff.toString());
}
public void setDebugMode(boolean b) {
DEBUG = b;
}
}