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    }