001 /**
002 * A TextUI is an interactive text interface to play Antichess through the
003 * command-line.
004 *
005 *
006 * @specfield event_handlers_active : boolean // action listeners that allow a
007 * human to move. [Deprecated]
008 */
009
010 package ui;
011
012 import ai.*;
013 //import games.*;
014
015 import java.io.*;
016 import java.util.*;
017
018 import core.*;
019
020 public class TextUI {
021
022 private Game game;
023 private boolean gameRunning; // false until a game has been started/loaded
024 private Move nextMove; // a move the AI has calculated but not yet made
025 private AIFactory aiFactory;
026
027 BufferedReader input;
028 PrintStream output;
029
030 /**
031 * Abstraction function AF(r) = t
032 *
033 * t.event_handlers_active = r.waiting_for_human
034 */
035
036 /**
037 * This main method creates a new TextUI object and begins its interactive
038 * loop.
039 */
040 public static void main(String[] args) {
041 TextUI textui = new TextUI();
042
043 try {
044 textui.interactiveLoop();
045 }
046 catch (IOException e) {
047 System.out.flush();
048 System.err.println(e.toString());
049 e.printStackTrace(System.err);
050 }
051 }
052
053 public TextUI() {
054 this(new BufferedReader(new InputStreamReader(System.in)),
055 System.out);
056 aiFactory = new AIFactory();
057 }
058
059 public TextUI(BufferedReader in, PrintStream out) {
060 gameRunning = false;
061
062 input = in;
063 output = out;
064 }
065
066 /**
067 * The interactive loop that drives the text interface. The user enters
068 * commands (as specified in the documentation) to begin and play a game of
069 * Antichess. [NB: The loop trims whitespace from the beginning and end of
070 * user input; this is not required in the specification.]
071 */
072 public void interactiveLoop()
073 throws IOException {
074
075 String inputString;
076
077 while (true) {
078 // Get user input
079 inputString = input.readLine();
080 if (inputString == null)
081 break;
082 List<String> inputList = parse(inputString.trim());
083
084 // Figure out what command has been entered and run it
085 String command = inputList.get(0);
086 List<String> args = inputList.subList(1, inputList.size());
087 if (command.equals("StartNewGame")) {
088 startNewGame(args);
089 }
090 else if (command.equals("LoadGame")) {
091 loadGame(args);
092 }
093 else if (game == null) {
094 // Commands that can be run after a game finishes.
095 // come after this.
096 output.println("Input error");
097 }
098 else if (command.equals("GetTime")) {
099 getTime(args);
100 }
101 else if (command.equals("QuitGame")) {
102 output.println("Exiting game");
103 break;
104 }
105 else if (command.equals("PrintBoard")) {
106 printBoard();
107 }
108 else if (! gameRunning) {
109 // After this are commands that can't
110 // be run after a game finishes.
111 output.println("Input error");
112 }
113 else if (command.equals("SaveGame")) {
114 saveGame(args);
115 }
116 else if (command.equals("GetNextMove")) {
117 getNextMove();
118 }
119 else if (command.equals("MakeNextMove")) {
120 makeNextMove();
121 }
122 else if (command.equals("MakeMove")) {
123 makeMove(args);
124 }
125 else if (command.equals("IsLegalMove")) {
126 isLegalMove(args);
127 }
128 else if (command.equals("PrintAllMoves")) {
129 printAllMoves();
130 }
131 else {
132 output.println("Input error");
133 }
134 }
135 }
136
137 // Methods to execute all the user commands
138
139 // StartNewGame [player] [time] [player] [time]
140 // [player] arg is "human" or "computer"
141 // [time] arg is in milliseconds
142 // If either [time] arg is 0, the game is untimed
143 private void startNewGame(List<String> args) {
144 // Make sure there are 4 arguments
145 if (args.size() != 4) {
146 output.println("Input error");
147 return;
148 }
149
150 Player whitePlayer;
151 Player blackPlayer;
152 int whiteTime;
153 int blackTime;
154
155 // Parse the times
156 try {
157 whiteTime = Integer.parseInt(args.get(1));
158 blackTime = Integer.parseInt(args.get(3));
159 }
160 catch (NumberFormatException x) {
161 output.println("Input error");
162 return;
163 }
164
165 // Create white player
166 if (args.get(0).equals("human")) {
167 whitePlayer = new HumanPlayer(Color.WHITE);
168 }
169 else if (args.get(0).equals("computer")) {
170 whitePlayer = aiFactory.createPlayer(true, whiteTime,
171 blackTime, null);
172 }
173 else {
174 output.println("Input error");
175 return;
176 }
177
178 // Create black player
179 if (args.get(2).equals("human")) {
180 blackPlayer = new HumanPlayer(Color.BLACK);
181 }
182 else if (args.get(2).equals("computer")) {
183 blackPlayer = aiFactory.createPlayer(false, whiteTime,
184 blackTime, null);
185 }
186 else {
187 output.println("Input error");
188 return;
189 }
190
191 //start the game
192 game = new Game(whitePlayer, whiteTime, blackPlayer, blackTime);
193 gameRunning = true;
194 nextMove = null;
195 output.println("New game started");
196 }
197
198 private void loadGame(List<String> args) {
199 // Verify that there is one argument
200 if (args.size() != 1) {
201 output.println("Input error");
202 return;
203 }
204
205 // Create the player objects
206 Player whitePlayer;
207 Player blackPlayer;
208
209 // No game in progress: Default to human-vs-human game
210 if (! gameRunning) {
211 whitePlayer = new HumanPlayer(Color.WHITE);
212 blackPlayer = new HumanPlayer(Color.BLACK);
213 }
214 // Use player types of current game
215 else {
216 if (game.getPlayerByColor(Color.WHITE) instanceof HumanPlayer) {
217 whitePlayer = new HumanPlayer(Color.WHITE);
218 }
219 else {
220 whitePlayer = aiFactory.createPlayer(true, 0, 0, null);
221 }
222
223 if (game.getPlayerByColor(Color.BLACK) instanceof HumanPlayer) {
224 blackPlayer = new HumanPlayer(Color.BLACK);
225 }
226 else {
227 blackPlayer = aiFactory.createPlayer(false, 0, 0, null);
228 }
229 }
230
231 File file = new File(args.get(0));
232 try {
233 game = Game.loadGame(whitePlayer, blackPlayer, file);
234
235 // Update any players
236 whitePlayer.update(game);
237 blackPlayer.update(game);
238
239 gameRunning = true;
240 nextMove = null;
241 output.println("Game loaded");
242 }
243 catch (GameLoadException e) {
244 output.println("Corrupt file");
245 }
246 }
247
248 private void saveGame(List<String> args) {
249 // Verify that there is one argument
250 if (args.size() != 1) {
251 output.println("Input error");
252 return;
253 }
254
255 File file = new File(args.get(0));
256 String xml = game.getXML();
257 try {
258 PrintWriter fileOutput = new PrintWriter(new FileWriter(file));
259 fileOutput.print(xml);
260 fileOutput.close();
261 output.println("Game saved");
262 }
263 catch (IOException x) {
264 output.println("Error: Your game could not be saved.");
265 }
266 }
267
268 private void getNextMove() {
269 // It's the human's turn
270 if (game.getCurrentPlayer() instanceof HumanPlayer) {
271 output.println("Human turn");
272 return;
273 }
274
275 // GetNextMove has already been called this turn
276 else if (nextMove != null) {
277 output.println(nextMove.toString());
278 }
279
280 // Ask the AI player for a move
281 // This action is timed, and that time is subtracted from the AI's clock
282 else {
283 // Determine the previous move
284 Move lastMove;
285
286 // If the game just started, give the AI player a pass move
287 if (game.getMoveHistory().size() == 0) {
288 lastMove = new Move();
289 }
290 else {
291 lastMove= game.getMoveHistory().get(game.getMoveHistory().size()
292 - 1);
293 }
294
295 // Time the askForMove call
296 long startTime = System.currentTimeMillis();
297 nextMove = game.getCurrentPlayer().askForMove(lastMove);
298 long endTime = System.currentTimeMillis();
299
300 // Adjust the clock and output the move
301 int time = (int) (endTime - startTime);
302 game.subtractTimeLeftForPlayer(game.getCurrentPlayer(), time);
303 output.println(nextMove.toString());
304 }
305 }
306
307 private void makeNextMove() {
308 // It's the human's turn
309 if (game.getCurrentPlayer() instanceof HumanPlayer) {
310 output.println("Please specify human move");
311 return;
312 }
313
314 // The user hasn't used GetNextMove yet
315 else if (nextMove == null) {
316 output.println("First GetNextMove");
317 return;
318 }
319
320 // Update the board and set nextMove to null
321 else {
322 game.makeMove(nextMove);
323 nextMove = null;
324 checkForGameOver();
325 }
326 }
327
328 private void makeMove(List<String> args) {
329 // Verify that there are two arguments
330 if (args.size() != 2) {
331 output.println("Input error");
332 return;
333 }
334
335 // If it's the computer's turn, do nothing
336 if (! (game.getCurrentPlayer() instanceof HumanPlayer)) {
337 return;
338 }
339
340 // Check for well-formed move String
341 String moveString = args.get(0);
342 if (! Move.parseableMove(moveString)) {
343 output.println("Input error");
344 return;
345 }
346
347 // Check for well-formed time String
348 String timeString = args.get(1);
349 int time = 0;
350 try {
351 time = Integer.parseInt(timeString);
352 }
353 catch (NumberFormatException ex) {
354 output.println("Input error");
355 return;
356 }
357
358 // Check that the move is legal
359 Move move = Move.fromString(moveString);
360 if (! game.getGameState().isLegal(move)) {
361 output.println("Illegal move");
362 return;
363 }
364
365 // Everything in the command is legal. Make the move.
366 //Get the current player before we move
367 Player curplayer = game.getCurrentPlayer();
368
369 // Must do this before we subtract time
370 // because subtracting time might end the game.
371 game.makeMove(move);
372 game.subtractTimeLeftForPlayer(curplayer, time);
373 output.println(moveString);
374 checkForGameOver();
375 }
376
377 private void printBoard() {
378 output.println(game.getXML());
379 }
380
381 private void isLegalMove(List<String> args) {
382 // Verify that there is one argument
383 if (args.size() != 1) {
384 output.println("Input error");
385 return;
386 }
387
388 // Verify that the argument is a well-formed move string
389 String moveString = args.get(0);
390 if (! Move.parseableMove(moveString)) {
391 output.println("Input error");
392 return;
393 }
394
395 // Determine legality and output accordingly
396 Move move = Move.fromString(moveString);
397 if (game.getGameState().isLegal(move)) {
398 output.println("legal");
399 }
400 else {
401 output.println("illegal");
402 }
403 }
404
405 private void printAllMoves() {
406 List<String> moves = new ArrayList<String>();
407
408 for (Move m : game.getGameState().legalMoves()) {
409 moves.add(m.toString());
410 }
411 Collections.sort(moves);
412
413 for (String m : moves) {
414 output.println(m);
415 }
416 }
417
418 private void getTime(List<String> args) {
419 // Verify that there is one argument
420 if (args.size() != 1) {
421 output.println("Input error");
422 return;
423 }
424
425 if (args.get(0).equals("white")) {
426 int timeLeft =
427 game.getTimeLeftForPlayer(game.getPlayerByColor(Color.WHITE));
428 if (timeLeft == -1) {
429 output.println("unlimited");
430 }
431 else {
432 output.println(timeLeft);
433 }
434 }
435 else if (args.get(0).equals("black")) {
436 int timeLeft =
437 game.getTimeLeftForPlayer(game.getPlayerByColor(Color.BLACK));
438 if (timeLeft == -1) {
439 output.println("unlimited");
440 }
441 else {
442 output.println(timeLeft);
443 }
444 }
445 else {
446 output.println("Input error");
447 }
448 }
449
450 /**
451 * @param str is a String consisting of "words" separated by spaces
452 * @return a List of Strings whose elements are the "words" of str, in order
453 */
454 private List<String> parse(String str) {
455 List<String> lst = new ArrayList<String>();
456
457 while (str.indexOf(" ") != -1) { // While there's still a space
458 lst.add(str.substring(0, str.indexOf(" "))); // Add the next word
459 str = str.substring(str.indexOf(" ") + 1); // Remove it from str
460 }
461
462 lst.add(str); // Add the final word to the list
463
464 return lst;
465 }
466
467 // If the game is over, print a message and reset the TUI
468 private void checkForGameOver() {
469 if (game.getWinner() == Color.NONE) {
470 return;
471 }
472 else if (game.getWinner() == Color.WHITE) {
473 output.println("White Player has won");
474 }
475 else {
476 output.println("Black Player has won");
477 }
478
479 // Reset state variables
480 //game = null; //Don't reset this just yet
481 gameRunning = false;
482 nextMove = null;
483 }
484
485 }