001    package antichess;
002    
003    
004    
005    /**
006     * TextPlayer implements the GamePlayer interface.  TextPlayer
007     * is for use with the TextUI.  The TextPlayer implements thet TextGamePlayer
008     * interface which extends the GamePlayer interface, so it properly implements the methods
009     * required of a GamePlayer.  The TextGamePlayer interface supports methods that the TextUI
010     * will call in order to fit the specifications for the TextUI.
011     * 
012     * 
013     * @author nlharr
014     *
015     * 
016     * @specfield clock                     //GameClock that store the timer for this TextPlayer
017     * @specfield playerType        //Player of this TextPlayer
018     * @specfield currentMove       //ChessMove that the TextPlayer is caching that the controller can access
019     * @specfield controller        //GameController        
020     */
021    
022    
023    public class TextPlayer implements TextGamePlayer{
024            private AntichessBoard board;
025            private GameClock clock;
026            private Player playerType;
027            private ChessMove moveForController;
028            private GameController controller;
029            
030    //      Abstraction Function
031            //clock = clock
032            //playerType = playerType
033            //controller = controller
034            //currentMove = moveForController
035            
036            //Representation Invariants
037            //clock != null
038            //playerType != Player.NONE
039            
040            
041            private void checkRep(){
042                    if (clock == null) throw new RuntimeException("Clock is null");
043                    if (playerType.equals(Player.NONE)) throw new RuntimeException("PlayerType is none");
044            }
045            
046            
047            /**
048             * Creates a new TextAIPlayer
049             *  currentMove = null
050             *  clock = clock
051             *  playerType = playerType
052             *  board = board
053             */
054            public TextPlayer(AntichessBoard board, GameClock clock, 
055                                                    Player playerType){
056                    this.board = board;
057                    this.clock = clock;
058                    this.playerType = playerType;
059                    this.moveForController = null;
060                    checkRep();
061            }
062            
063            
064            /**
065             * @see GamePlayer
066             * @return false;
067             */
068            public boolean automaticallyTimed() {
069                    return false;
070            }
071            
072            /**
073             * @see GamePlayer
074             * @modifies controller, currentMove
075             * @effects controller = controller, currentMove = null;
076             * @return currentMove (the value before it is made null)
077             */
078            public synchronized ChessMove getMove(GameController controller) {
079                    
080                    this.controller = controller;
081                    ChessMove tempMove = moveForController;
082                    moveForController = null;
083                    return tempMove;
084            }
085    
086            public void gameEnded() {
087                    
088            }
089    
090            /**
091             * @see GamePlayer
092             * @return playerType
093             */
094            public Player getPlayerType() {
095                    return playerType;
096            }
097            
098            
099            //converts a letter to a column number
100            private static int letterToInt(String letter){
101                    if (letter.equals("a")){
102                            return 0;
103                    } else if (letter.equals("b")){
104                            return 1;
105                    }  else if (letter.equals("c")){
106                            return 2;
107                    }  else if (letter.equals("d")){
108                            return 3;
109                    }  else if (letter.equals("e")){
110                            return 4;
111                    }  else if (letter.equals("f")){
112                            return 5;
113                    }  else if (letter.equals("g")){
114                            return 6;
115                    }  else if (letter.equals("h")){
116                            return 7;
117                    }  else {
118                            throw new NumberFormatException("invalid letter");
119                    } 
120            }
121            
122            /**
123             * Converts a string 'move' to the move specified by the 
124             * @return a ChessMove that the string 'move' represents
125             * @throws NumberFormatException, IllegalArgumentException if the format of the move does not match
126             * the given specifications
127             */
128            public static ChessMove stringToMove(String move, AntichessBoard board) throws NumberFormatException, IllegalArgumentException{
129                    if (move.length() != 5){
130                            throw new IllegalArgumentException("String is improper length"); 
131                    }
132                    int startRow = Integer.valueOf(move.substring(1, 2))-1;
133                    int endRow   = Integer.valueOf(move.substring(4, 5))-1;
134                    int startColumn = letterToInt(move.substring(0,1));
135                    int endColumn = letterToInt(move.substring(3,4));
136                    if (board.getPieceAt(startRow, startColumn)==null){
137                            throw new IllegalArgumentException("No piece at that row");
138                    }
139                    return board.createMove(board.getPieceAt(startRow, startColumn), endRow, endColumn);
140                    
141            }
142            
143            
144            /**
145             * 
146             * @modifies currentMove, clock
147             * @effects if moveString represents a valid move currentMove = move represented by moveString
148             *                      if moveString represents a valid move the timer in clock for this TextPlayer is decremented
149             *                      by time
150             * Once this happens it notifies the controller that is has a move
151             * and prints moveString to stdout.
152             * 
153             * Prints "Illegal move" to stdout if moveString does not represent a valid
154             * move.
155             * @return false if the move represented by moveString is illegal
156             *                              or the format of moveString is invalid
157             *         false if the board says it is a different players move
158             *         true else
159             *  
160             * @throws RuntimeException if the TextPlayer has not had a move requested
161             */
162            
163            public synchronized boolean doMove(String moveString, long time){
164                    //checks if it is the proper players turn
165                    if (!board.getPlayer().equals(this.getPlayerType())){
166                            return false;
167                    }
168                    
169                    //converts the string to a move and checks if it's legal
170                    ChessMove move;
171                    try{
172                            move = stringToMove(moveString, board);
173                    }
174                    catch (Exception ex){
175                            System.out.println("Illegal move");
176                            return false;
177                    }
178                    if (!board.isMoveLegal(move)){
179                            System.out.println("Illegal move");
180                            return false;
181                    }
182                    
183                    
184                    //notifies the controller that we have a move
185                    if (this.controller == null){
186                            throw new RuntimeException("Controller is null");
187                    }
188                    if (clock.hasPlayer(playerType)){
189                            clock.setTime(clock.getTime(playerType)-time, playerType);
190                    }
191                    
192                    System.out.println(moveString);
193                    moveForController = move;
194                    controller.notifyControllerHasMove();
195                    return true;
196            }
197            
198            
199            /**
200             * Prints 'Human turn' to stdout
201             */
202            public void getPossibleMove(){
203                    System.out.println("Human turn");
204            }
205            
206            
207            /**
208             * Prints 'Please specify human move' to stdout
209             * @return false
210             */
211            public boolean doPossibleMove(){
212                    System.out.println("Please specify human move");
213                    return false;
214            }
215            
216            
217            
218    }