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 }