-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGame.java
309 lines (273 loc) · 9.48 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
/* Skeleton code copyright (C) 2008, 2022 Paul N. Hilfinger and the
* Regents of the University of California. Do not distribute this or any
* derivative work without permission. */
package ataxx;
import static ataxx.PieceColor.*;
import static ataxx.GameException.error;
import static ataxx.Utils.*;
/** Main logic for playing (a) game(s) of Ataxx.
* @author P. N. Hilfinger
*/
class Game {
/** Name of resource containing help message. */
private static final String HELP = "ataxx/Help.txt";
/** A new Game that takes command/move input from INP, logs
* commands if LOGGING, displays the board using VIEW, and uses REPORTER
* for messages to the user and error messages. SEED is intended to
* seed a random number generator, if one is used in an AI.
*/
Game(CommandSource inp, View view, Reporter reporter, boolean logging) {
_inp = inp;
_view = view;
_reporter = reporter;
_logging = logging;
_seed = (long) (Math.random() * Long.MAX_VALUE);
_board = new Board();
_board.setNotifier((b) -> _view.update(b));
}
/** Returns the game board. This board is not intended to be modified
* by the caller. */
Board getBoard() {
return _board;
}
/** Return true iff the current game is not over. */
boolean gameInProgress() {
return _board.getWinner() == null;
}
/** Play a session of Ataxx. This may include multiple games,
* and proceeds until the user exits. Returns an exit code: 0 is
* normal; any positive quantity indicates an error. */
int play() {
boolean winnerAnnounced;
System.out.println("Welcome to " + Defaults.VERSION);
_board.clear();
setManual(RED);
setAuto(BLUE);
_exit = -1;
winnerAnnounced = false;
while (_exit < 0) {
String cmnd;
if (_board.getWinner() == null) {
winnerAnnounced = false;
try {
executeCommand(getPlayer(_board.whoseMove()).getMove());
} catch (GameException e) {
reportError(e.getMessage());
}
} else if (!gameInProgress()) {
if (!winnerAnnounced) {
_reporter.announceWin(_board.getWinner());
winnerAnnounced = true;
}
executeCommand(getCommand("-> "));
}
}
return _exit;
}
/** Return a suggested prompt for command input. */
private String prompt() {
if (gameInProgress()) {
return String.format("%s> ", _board.whoseMove());
} else {
return "+> ";
}
}
/** Return a command from the current source, using PROMPT as a
* prompt, if needed. */
String getCommand(String prompt) {
String cmnd = _inp.getCommand(prompt);
if (cmnd == null) {
return "quit";
} else {
return cmnd;
}
}
/** Perform the move denoted by MOVESTR, which must be legal. */
void makeMove(String moveStr) {
Move move = Move.move(moveStr);
if (_board.legalMove(move)) {
_board.makeMove(move);
} else {
throw error("illegal move");
}
if (_verbose) {
printBoard();
}
}
/** Place a block at the position PLACE (in crformat), and in its three
* reflected squares symmetrically. */
void block(String place) {
if (_board.numMoves() > 0) {
throw error("block-setting must precede first move.");
}
if (!place.matches("[a-i][1-9]")) {
throw error("invalid square designation");
}
_board.setBlock(place.charAt(0), place.charAt(1));
}
/** Undo the last move, and also the previous one, if that player is
* automatic. */
void undo() {
if (_board.numMoves() > 0) {
_board.undo();
if (_board.numMoves() > 0
&& getPlayer(_board.whoseMove()).isAuto()) {
_board.undo();
}
}
}
/** Report the move MOVE by PLAYER. */
void reportMove(Move move, PieceColor player) {
_reporter.announceMove(move, player);
}
/** Send a message to the user as determined by FORMAT and ARGS, which
* are interpreted as for String.format or PrintWriter.printf. */
void message(String format, Object... args) {
_reporter.msg(format, args);
}
/** Send announcement of winner to my user output. */
private void announceWinner() {
_reporter.msg("* %s wins.", _board.getWinner().toString());
}
/** Make the player of COLOR an AI for subsequent moves. */
private void setAuto(PieceColor color) {
setPlayer(color, new AI(this, color, _seed));
_seed += 1;
}
/** Make the player of COLOR take manual input from the user for
* subsequent moves. */
private void setManual(PieceColor color) {
setPlayer(color, new Manual(this, color));
}
/** Return the Player playing COLOR. */
private Player getPlayer(PieceColor color) {
return _players[color.ordinal()];
}
/** Set getPlayer(COLOR) to PLAYER. */
private void setPlayer(PieceColor color, Player player) {
_players[color.ordinal()] = player;
}
/** Clear the board to its initial state. */
void clear() {
_board.clear();
}
/** Print the current board using standard board-dump format. */
private void dump() {
_reporter.msg("===%n%s===", _board.toString());
}
/** Print a board with row/column numbers. */
private void printBoard() {
_reporter.msg(_board.toString(true));
}
/** Print a help message. */
private void help() {
printHelpResource(HELP, System.out);
}
/** Seed the random-number generator with SEED. */
private void setSeed(long seed) {
_seed = seed;
}
/** Execute command CMNDSTR. Throws GameException on errors. */
private void executeCommand(String cmndStr) {
Command cmnd = Command.parseCommand(cmndStr);
String[] parts = cmnd.operands();
log(cmndStr);
try {
switch (cmnd.commandType()) {
case COMMENT:
break;
case AUTO:
setAuto(parseColor(parts[0]));
break;
case BOARD:
printBoard();
break;
case DUMP:
dump();
break;
case HELP:
help();
break;
case MANUAL:
setManual(parseColor(parts[0]));
break;
case NEW:
clear();
break;
case QUIET:
_verbose = false;
break;
case QUIT:
_exit = 0;
break;
case SEED:
setSeed(toLong(parts[0]));
break;
case VERBOSE:
_verbose = true;
break;
case UNDO:
undo();
break;
case BLOCK:
block(parts[0]);
break;
case PIECEMOVE:
makeMove(parts[0]);
break;
case ERROR:
throw error("Unknown command.");
default:
break;
}
} catch (NumberFormatException excp) {
reportError("Bad number in: %s", cmnd);
} catch (ArrayIndexOutOfBoundsException excp) {
reportError("Argument(s) missing: %s", cmnd);
} catch (GameException excp) {
reportError(excp.getMessage());
}
}
/** Print a message on the logging stream, if any, appending a newline.
* The arguments FORMAT and ARGS have the same meaning as for
* String.format. */
private void log(String format, Object... args) {
if (_logging) {
System.out.printf(format + "%n", args);
}
}
/** Send an error message to the user formed from arguments FORMAT
* and ARGS, whose meanings are as for printf. */
void reportError(String format, Object... args) {
_reporter.err(format, args);
if (Main.isStrict()) {
_exit = 1;
}
}
/** Returns command input for the current game. */
private final CommandSource _inp;
/** Outlet for responses to the user. */
private final Reporter _reporter;
/** The board on which I record all moves. */
private final Board _board;
/** Displayer of boards. */
private View _view;
/** True iff we are logging commands. */
private boolean _logging;
/** True iff we should print the board after each move. */
private boolean _verbose;
/** Current pseudo-random number seed. Provided as an argument to AIs
* that use a random element in their choices. Incremented for each
* AI to which it is supplied.
*/
private long _seed;
/** When set to a non-negative value, indicates that play should terminate
* at the earliest possible point, returning _exit. When negative,
* indicates that the session is not over. */
private int _exit;
/** Current players, indexed by color (RED, BLUE). */
private final Player[] _players = new Player[PieceColor.values().length];
/** Used to return a move entered from the console. Allocated
* here to avoid allocations. */
private final int[] _move = new int[2];
}