1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.svg.chess;
19
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.vectomatic.dom.svg.OMSVGAnimatedString;
27 import org.vectomatic.dom.svg.OMSVGDocument;
28 import org.vectomatic.dom.svg.OMSVGGElement;
29 import org.vectomatic.dom.svg.OMSVGMatrix;
30 import org.vectomatic.dom.svg.OMSVGPoint;
31 import org.vectomatic.dom.svg.OMSVGRectElement;
32 import org.vectomatic.dom.svg.OMSVGSVGElement;
33 import org.vectomatic.dom.svg.OMSVGUseElement;
34
35 import com.alonsoruibal.chess.Board;
36 import com.alonsoruibal.chess.Move;
37 import com.alonsoruibal.chess.bitboard.BitboardUtils;
38 import com.alonsoruibal.chess.movegen.LegalMoveGenerator;
39 import com.alonsoruibal.chess.movegen.MoveGenerator;
40 import com.google.gwt.core.client.GWT;
41 import com.google.gwt.core.client.Scheduler;
42 import com.google.gwt.dom.client.Style.Cursor;
43 import com.google.gwt.event.dom.client.MouseDownEvent;
44 import com.google.gwt.event.dom.client.MouseDownHandler;
45 import com.google.gwt.event.dom.client.MouseEvent;
46 import com.google.gwt.event.dom.client.MouseMoveEvent;
47 import com.google.gwt.event.dom.client.MouseMoveHandler;
48 import com.google.gwt.event.dom.client.MouseUpEvent;
49 import com.google.gwt.event.dom.client.MouseUpHandler;
50 import com.google.gwt.user.client.Command;
51
52
53
54
55
56
57
58
59
60
61
62
63 public class ChessBoard implements MouseDownHandler, MouseUpHandler, MouseMoveHandler {
64
65 private ChessCss css;
66 private OMSVGSVGElement svgElt;
67 private OMSVGGElement boardElt;
68 private OMSVGDocument boardDoc;
69 private OMSVGUseElement targetPiece;
70 private int sqWidth;
71 private int sqHeight;
72
73 private Board board;
74 private MoveGenerator legalMoveGenerator;
75 private enum BoardMode {
76 SRC_MODE,
77 DEST_MODE
78 };
79
80
81
82
83 private int moveNumber;
84
85
86
87
88 private Map<Integer, Set<Integer>> srcToDestIndex;
89
90
91
92 private int srcIndex;
93
94
95
96 private int destIndex;
97
98
99
100 private OMSVGPoint mouseDownCoords;
101
102
103
104 private BoardMode mode;
105
106
107
108 private Map<String, OMSVGRectElement> algebraicToRects;
109
110
111
112 private Map<String, OMSVGUseElement> algebraicToPieces;
113
114 private Main main;
115
116 public ChessBoard(Board board, OMSVGSVGElement svgElt, Main main) {
117 this.board = board;
118 this.svgElt = svgElt;
119 this.main = main;
120 this.boardDoc = (OMSVGDocument) svgElt.getOwnerDocument();
121 this.boardElt = (OMSVGGElement) boardDoc.getElementById("board");
122 this.css = Resources.INSTANCE.getCss();
123 this.srcToDestIndex = new HashMap<Integer, Set<Integer>>();
124 this.mode = BoardMode.SRC_MODE;
125 this.srcIndex = -1;
126 this.destIndex = -1;
127 this.algebraicToRects = new HashMap<String, OMSVGRectElement>();
128 for (int i = 0; i < 8; i++) {
129 for (int j = 0; j < 8; j++) {
130 int index = j + 8 * i;
131
132 String squareId = BitboardUtils.index2Algebraic(index);
133 OMSVGRectElement squareElt = (OMSVGRectElement) boardDoc.getElementById(squareId);
134 algebraicToRects.put(squareId, squareElt);
135 }
136 }
137 OMSVGRectElement sqElement = algebraicToRects.get("a1");
138 this.sqWidth = (int)sqElement.getWidth().getBaseVal().getValue();
139 this.sqHeight = (int)sqElement.getHeight().getBaseVal().getValue();
140 this.algebraicToPieces = new HashMap<String, OMSVGUseElement>();
141
142
143 legalMoveGenerator = new LegalMoveGenerator();
144 moveNumber = -1;
145 update(false);
146
147
148 boardElt.addMouseMoveHandler(this);
149 boardElt.addMouseUpHandler(this);
150 }
151
152
153
154
155
156
157
158
159
160 public void addPiece(char piece, String algebraic) {
161 if (piece != '.') {
162 OMSVGRectElement squareElt = (OMSVGRectElement) boardDoc.getElementById(algebraic);
163 OMSVGUseElement useElt = boardDoc.createSVGUseElement();
164 useElt.getX().getBaseVal().setValue(squareElt.getX().getBaseVal().getValue());
165 useElt.getY().getBaseVal().setValue(squareElt.getY().getBaseVal().getValue());
166 useElt.getWidth().getBaseVal().setValue(sqWidth);
167 useElt.getHeight().getBaseVal().setValue(sqHeight);
168 useElt.getHref().setBaseVal("#" + Character.toString(piece));
169 useElt.getStyle().setCursor(Cursor.MOVE);
170 useElt.addMouseDownHandler(this);
171 useElt.addMouseUpHandler(this);
172 boardElt.appendChild(useElt);
173 algebraicToPieces.put(algebraic, useElt);
174 }
175 }
176
177
178
179
180
181
182 public void removePiece(String algebraic) {
183 OMSVGUseElement useElt = algebraicToPieces.remove(algebraic);
184 if (useElt != null) {
185 boardElt.removeChild(useElt);
186 }
187 }
188
189
190
191
192
193
194
195 public char getPiece(String algebraic) {
196 OMSVGUseElement useElt = algebraicToPieces.get(algebraic);
197 if (useElt != null) {
198 OMSVGAnimatedString href = useElt.getHref();
199 if (href != null) {
200 String baseVal = href.getBaseVal();
201 if (baseVal != null) {
202 return baseVal.charAt(1);
203 }
204 }
205 }
206 return '.';
207 }
208
209
210
211
212
213
214 public void update(boolean force) {
215
216
217 if (force || board.getMoveNumber() != moveNumber) {
218 srcToDestIndex.clear();
219 moveNumber = board.getMoveNumber();
220 int[] moves = new int[64];
221 int moveCount = legalMoveGenerator.generateMoves(board, moves, 0);
222 for (int i = 0; i < moveCount; i++) {
223 int srcIndex = Move.getFromIndex(moves[i]);
224 Set<Integer> destIndices = srcToDestIndex.get(srcIndex);
225 if (destIndices == null) {
226 destIndices = new HashSet<Integer>();
227 srcToDestIndex.put(srcIndex, destIndices);
228 }
229 destIndices.add(Move.getToIndex(moves[i]));
230 }
231 }
232
233 Set<Integer> destIndices = srcToDestIndex.containsKey(srcIndex) ? srcToDestIndex.get(srcIndex) : Collections.<Integer>emptySet();
234 for (int i = 0; i < 8; i++) {
235 for (int j = 0; j < 8; j++) {
236 int index = j + 8 * i;
237
238 String squareId = BitboardUtils.index2Algebraic(index);
239 OMSVGRectElement squareElt = algebraicToRects.get(squareId);
240
241
242 String className = ((j + i ) % 2) == 0 ? css.whiteSquare() : css.blackSquare();
243 if (destIndices.contains(index)) {
244 className = css.blueSquare();
245 }
246 if (mode == BoardMode.DEST_MODE && index == destIndex) {
247 className = destIndices.contains(index) ? css.greenSquare() : css.redSquare();
248 }
249 if (index == srcIndex) {
250 className = css.yellowSquare();
251 }
252 if (!className.equals(squareElt.getClassName().getBaseVal())) {
253 squareElt.setClassNameBaseVal(className);
254
255 }
256
257
258 char piece = board.getPieceAt(BitboardUtils.index2Square((byte)index));
259 if (getPiece(squareId) != piece) {
260 removePiece(squareId);
261 addPiece(piece, squareId);
262 }
263 }
264 }
265 }
266
267 @Override
268 public void onMouseDown(MouseDownEvent event) {
269
270 onMouseDown_(event);
271 event.stopPropagation();
272 event.preventDefault();
273 }
274
275 private void onMouseDown_(MouseEvent<?> event) {
276 String algebraic = getAlgebraic(event);
277 if (targetPiece == null) {
278 targetPiece = algebraicToPieces.get(algebraic);
279 if (targetPiece != null) {
280 this.destIndex = BitboardUtils.algebraic2Index(algebraic);
281 mode = BoardMode.DEST_MODE;
282 mouseDownCoords = getLocalCoordinates(event);
283 update(false);
284 }
285 } else {
286 int index = algebraic != null ? BitboardUtils.algebraic2Index(algebraic) : -1;
287 if (index == srcIndex) {
288 targetPiece = null;
289 }
290 }
291 event.stopPropagation();
292 event.preventDefault();
293 }
294
295 @Override
296 public void onMouseUp(MouseUpEvent event) {
297
298
299 if (targetPiece != null) {
300 mode = BoardMode.SRC_MODE;
301 Set<Integer> destIndices = srcToDestIndex.containsKey(srcIndex) ? srcToDestIndex.get(srcIndex) : Collections.<Integer>emptySet();
302 if (destIndices.contains(destIndex)) {
303 final int move = Move.getFromString(board, BitboardUtils.index2Algebraic(srcIndex) + BitboardUtils.index2Algebraic(destIndex));
304 board.doMove(move);
305 GWT.log("newItem(" + board.getMoveNumber() + ")", null);
306 main.addMove();
307 Scheduler.get().scheduleDeferred(new Command() {
308 @Override
309 public void execute() {
310 main.nextMove();
311 }
312
313 });
314 } else {
315 targetPiece.getX().getBaseVal().setValue(getX(srcIndex));
316 targetPiece.getY().getBaseVal().setValue(getY(srcIndex));
317 }
318 targetPiece = null;
319 update(false);
320 } else {
321 onMouseDown_(event);
322 }
323 event.stopPropagation();
324 event.preventDefault();
325 }
326
327 @Override
328 public void onMouseMove(MouseMoveEvent event) {
329 String algebraic = getAlgebraic(event);
330
331 int index = algebraic != null ? BitboardUtils.algebraic2Index(algebraic) : -1;
332 if (mode == BoardMode.SRC_MODE) {
333 if (srcIndex != index) {
334 srcIndex = index;
335 update(false);
336 }
337 } else {
338
339 OMSVGPoint p = getLocalCoordinates(event);
340 targetPiece.getX().getBaseVal().setValue(getX(srcIndex) + p.getX() - mouseDownCoords.getX());
341 targetPiece.getY().getBaseVal().setValue(getY(srcIndex) + p.getY() - mouseDownCoords.getY());
342 if (destIndex != index) {
343 destIndex = index;
344 update(false);
345 }
346 }
347 event.stopPropagation();
348 event.preventDefault();
349 }
350
351 public OMSVGPoint getLocalCoordinates(MouseEvent<?> e) {
352 OMSVGPoint p = svgElt.createSVGPoint(e.getClientX(), e.getClientY());
353 OMSVGMatrix m = boardElt.getScreenCTM().inverse();
354 return p.matrixTransform(m);
355 }
356
357 public int getX(int index) {
358 return sqWidth * (7 - (index % 8));
359 }
360 public int getY(int index) {
361 return sqHeight * (7 - (index / 8));
362 }
363
364
365
366
367
368
369
370
371
372 public String getAlgebraic(MouseEvent<?> event) {
373
374 OMSVGPoint p = getLocalCoordinates(event);
375 int x = (int)(p.getX() / sqWidth);
376 int y = (int)(p.getY() / sqHeight);
377 if (x >= 0 && x <= 7 && y >= 0 && y <= 7) {
378 char algebraic[] = new char[2];
379 algebraic[0] = (char)('a' + x);
380 algebraic[1] = (char)('8' - y);
381 return new String(algebraic);
382 }
383 return null;
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422 }