001 package core; 002 003 import java.util.HashMap; 004 import java.util.Map; 005 006 import rules.Board; 007 import rules.Empty; 008 import rules.GameState; 009 import rules.King; 010 import rules.NoneSquare; 011 import rules.Pawn; 012 import rules.PieceFactory; 013 import rules.Queen; 014 015 /** 016 * A Move represents a single player's move on a board. It is immutable, and may 017 * mean different things on different boards. 018 * 019 * A can either be represented by an ordered pair of Positions or by a pass. 020 * 021 * @specfield ispass : boolean // If the move is a pass (OR) 022 * @specfield fromposition : Position // Where the move originates 023 * @specfield toposition : Position // Where the move ends 024 * @derivedfield boardupdates : Map<Position, Piece> // How the move changes a 025 * board 026 * 027 * Abstract invariant: either it is a pass or the positions are valid 028 */ 029 public class Move 030 { 031 private Position from; 032 private Position to; 033 034 // AF: 035 // ispass = (from == null || to == null) 036 // fromposition = from 037 // toposition = to 038 // RI: 039 // fromposition, toposition are both null or both valid positions. 040 041 /** 042 * @see Piece 043 */ 044 public Move(Position from, Position to) 045 { 046 this.from = from; 047 this.to = to; 048 checkRep(); 049 } 050 051 /** 052 * Construct the pass move. 053 */ 054 public Move() 055 { 056 this(null, null); 057 } 058 059 /** 060 * Checks that the RI is not violated 061 */ 062 private void checkRep() 063 { 064 if (from == null || to == null) 065 { 066 assert (from == null && to == null); 067 } else 068 { 069 assert (0 <= from.getX() && from.getX() < 8 && 0 <= from.getY() && from 070 .getY() < 8); 071 } 072 } 073 074 /** 075 * @returns the Position the move is from 076 */ 077 public Position getFrom() 078 { 079 return from; 080 } 081 082 /** 083 * @returns the Position the move is to 084 */ 085 public Position getTo() 086 { 087 return to; 088 } 089 090 /** 091 * @returns whether this is a pass 092 */ 093 public boolean isPass() 094 { 095 return from == null || to == null; 096 } 097 098 /** 099 * @param s 100 * is a String representing an Antichess move 101 * @requires s is of the form [position1]-[position2]; that is, two valid 102 * and distinct positions separated by a hyphen. For example, 103 * a3-a8 is a legal argument to this method. Or, s.equals("") for 104 * a pass. 105 * @return a Move representing the same set of changes to the GameState g as 106 * the String s. Its toString method will return a String equal to 107 * s. 108 */ 109 public static Move fromString(String s) 110 { 111 // Check that s is a well-formed move string 112 Move move; 113 if (s.equals("")) 114 { 115 move = new Move(); 116 } else if (!Move.parseableMove(s)) 117 { 118 throw new RuntimeException("'" + s + "' is not a valid move."); 119 } else 120 { 121 Position from = Position.fromString(s.substring(0, 2)); 122 Position end = Position.fromString(s.substring(3)); 123 move = new Move(from, end); 124 } 125 return move; 126 } 127 128 /** 129 * @param g 130 * is the gamestate to make this move on 131 * @requires g != null and this move is a valid move candidate; the pieces 132 * may move as desired, but for possibly not capturing or entering 133 * into check. 134 * @returns the Map<Position, Piece> of updates to g to perform this move. 135 */ 136 public Map<Position, Piece> toMap(GameState g) 137 { 138 Map<Position, Piece> move = new HashMap<Position, Piece>(); 139 if (isPass()) 140 { 141 return move; 142 } 143 move.put(from, new Empty()); 144 Board board = ((GameState) g).getBoard(); 145 146 Piece movingPiece = board.get(from); 147 Piece overriddenPiece = board.get(to); 148 if (movingPiece instanceof NoneSquare) 149 { 150 System.out.println(this.toString()); 151 throw new RuntimeException("Got move from NoneSquare"); 152 // return null; 153 } 154 155 // The "to" square has whatever was in the "from" square before 156 // but with lastmove incremented 157 if (movingPiece instanceof Pawn && 158 Math.abs(to.getY() - from.getY()) == 2) { //Pawn push 159 move.put(to, PieceFactory.newPiece(movingPiece, g.getMoveNum() + 1)); 160 } else { 161 move.put(to, PieceFactory.newPiece(movingPiece)); 162 } 163 164 // Must be a castle 165 if (movingPiece instanceof King && from.getX() == 4 166 && Math.abs(to.getX() - from.getX()) == 2) 167 { 168 // Don't need to check for Y or moves; legality checking covers 169 // those. 170 Position rookFrom, rookTo; 171 if (to.getX() == 2) 172 { 173 rookFrom = Position.get(0, from.getY()); 174 rookTo = Position.get(3, from.getY()); 175 } else 176 { 177 rookFrom = Position.get(7, from.getY()); 178 rookTo = Position.get(5, from.getY()); 179 } 180 move.put(rookFrom, new Empty()); 181 move.put(rookTo, PieceFactory.newPiece(board.get(rookFrom))); 182 } else if (movingPiece instanceof Pawn) 183 { 184 // Promotion 185 if (to.getY() == 0 || to.getY() == 7) 186 { 187 // Overrides pawn at the location 188 move.put(to, new Queen(movingPiece.getColor(), 1)); 189 } 190 // En passant 191 if (to.getX() != from.getX() && overriddenPiece instanceof NoneSquare) 192 { 193 move.put(Position.get(to.getX(), from.getY()), new Empty()); 194 } 195 } 196 if (!(movingPiece instanceof King)) 197 overriddenPiece.postMove(g, move, to); 198 // All the board updates have been added to our map; we can return it. 199 return move; 200 } 201 202 /** 203 * @return true if s is of the form [position1]-[position2]; that is, two 204 * valid and distinct positions separated by a hyphen. For example, 205 * a3-a8 is a valid move String. 206 */ 207 public static boolean parseableMove(String s) 208 { 209 if (s.equals("pass")) 210 { 211 return true; 212 } 213 return s.length() == 5 && Position.validPosition(s.substring(0, 2)) 214 && s.charAt(2) == '-' && Position.validPosition(s.substring(3)); 215 } 216 217 /** 218 * @return a String representation of this, in the form 219 * [position1]-[position2], where position1 and position2 are the 220 * old and new positions of the moving piece, respectively. In the 221 * case of castling, the King is the "moving" piece. In the case of 222 * en passant, the capturing pawn is the "moving" piece. 223 */ 224 public String toString() 225 { 226 if (isPass()) 227 { 228 return ""; 229 } 230 return from.toString() + "-" + to.toString(); 231 } 232 233 /** 234 * @see Object 235 */ 236 public boolean equals(Object o) 237 { 238 if (o != null && o instanceof Move) 239 { 240 Move m = (Move) o; 241 if (isPass()){ 242 return m.isPass(); 243 } 244 return from.equals(m.from) && to.equals(m.to); 245 } 246 return false; 247 } 248 249 /** 250 * @see Object 251 */ 252 public int hashCode() 253 { 254 if (from == null){ 255 return -1; 256 } else { 257 return from.hashCode() * 64 + to.hashCode(); 258 } 259 } 260 }