1 /* san.cpp
2
3 GNU Chess protocol adapter
4
5 Copyright (C) 2001-2011 Free Software Foundation, Inc.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 // san.cpp
23
24 // includes
25
26 #include <cctype>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30
31 #include "attack.h"
32 #include "board.h"
33 #include "list.h"
34 #include "move.h"
35 #include "move_gen.h"
36 #include "move_legal.h"
37 #include "piece.h"
38 #include "san.h"
39 #include "square.h"
40 #include "util.h"
41
42 namespace adapter {
43
44 // constants
45
46 static const bool UseSlowDebug = false;
47
48 enum ambiguity_t {
49 AMBIGUITY_NONE,
50 AMBIGUITY_FILE,
51 AMBIGUITY_RANK,
52 AMBIGUITY_SQUARE
53 };
54
55 // functions
56
57 static bool san_to_lan (const char san[], const board_t * board, char string[], int size);
58 static int move_from_lan (const char string[], const board_t * board);
59
60 static int ambiguity (int move, const board_t * board);
61
62 // move_to_san()
63
move_to_san(int move,const board_t * board,char string[],int size)64 bool move_to_san(int move, const board_t * board, char string[], int size) {
65
66 int from, to, piece;
67 char tmp_string[256];
68
69 ASSERT(move_is_ok(move));
70 ASSERT(board_is_ok(board));
71 ASSERT(string!=NULL);
72 ASSERT(size>=8);
73
74 ASSERT(move_is_legal(move,board));
75
76 if (size < 8) return false;
77
78 // init
79
80 from = move_from(move);
81 to = move_to(move);
82
83 string[0] = '\0';
84
85 // castle
86
87 if (move_is_castle(move,board)) {
88
89 if (to > from) {
90 strcat(string,"O-O");
91 } else {
92 strcat(string,"O-O-O");
93 }
94
95 goto check;
96 }
97
98 // from
99
100 piece = board->square[from];
101
102 if (piece_is_pawn(piece)) {
103
104 // pawn
105
106 if (move_is_capture(move,board)) {
107 sprintf(tmp_string,"%c",file_to_char(square_file(from)));
108 strcat(string,tmp_string);
109 }
110
111 } else {
112
113 // piece
114
115 sprintf(tmp_string,"%c",toupper(piece_to_char(piece)));
116 strcat(string,tmp_string);
117
118 // ambiguity
119
120 switch (ambiguity(move,board)) {
121 case AMBIGUITY_NONE:
122 break;
123 case AMBIGUITY_FILE:
124 sprintf(tmp_string,"%c",file_to_char(square_file(from)));
125 strcat(string,tmp_string);
126 break;
127 case AMBIGUITY_RANK:
128 sprintf(tmp_string,"%c",rank_to_char(square_rank(from)));
129 strcat(string,tmp_string);
130 break;
131 case AMBIGUITY_SQUARE:
132 if (!square_to_string(from,tmp_string,256)) return false;
133 strcat(string,tmp_string);
134 break;
135 default:
136 ASSERT(false);
137 break;
138 }
139 }
140
141 // capture
142
143 if (move_is_capture(move,board)) strcat(string,"x");
144
145 // to
146
147 if (!square_to_string(to,tmp_string,256)) return false;
148 strcat(string,tmp_string);
149
150 // promote
151
152 if (move_is_promote(move)) {
153 sprintf(tmp_string,"=%c",toupper(piece_to_char(move_promote(move,board))));
154 strcat(string,tmp_string);
155 }
156
157 // check
158
159 check:
160
161 if (move_is_mate(move,board)) {
162 strcat(string,"#");
163 } else if (move_is_check(move,board)) {
164 strcat(string,"+");
165 }
166
167 return true;
168 }
169
170 // move_from_san()
171
move_from_san(const char string[],const board_t * board)172 int move_from_san(const char string[], const board_t * board) {
173
174 char s[256];
175 int move;
176
177 ASSERT(string!=NULL);
178 ASSERT(board_is_ok(board));
179
180 san_to_lan(string,board,s,256);
181 move = move_from_lan(s,board);
182
183 ASSERT(!UseSlowDebug||move==move_from_san_debug(string,board));
184
185 return move;
186 }
187
188 // move_from_san_debug()
189
move_from_san_debug(const char string[],const board_t * board)190 int move_from_san_debug(const char string[], const board_t * board) {
191
192 list_t list[1];
193 int i, move;
194 char move_string[256];
195
196 ASSERT(string!=NULL);
197 ASSERT(board_is_ok(board));
198
199 gen_legal_moves(list,board);
200
201 for (i = 0; i < list_size(list); i++) {
202 move = list_move(list,i);
203 if (!move_to_san(move,board,move_string,256)) ASSERT(false);
204 if (my_string_equal(move_string,string)) return move;
205 }
206
207 return MoveNone;
208 }
209
210 // san_to_lan()
211
san_to_lan(const char san[],const board_t * board,char string[],int size)212 static bool san_to_lan(const char san[], const board_t * board, char string[], int size) {
213
214 int len;
215 int left, right;
216 int c;
217 int king, rook;
218 char king_string[3], rook_string[3];
219
220 ASSERT(san!=NULL);
221 ASSERT(board_is_ok(board));
222 ASSERT(string!=NULL);
223 ASSERT(size>=8);
224
225 // init
226
227 if (size < 8) return false;
228 strcpy(string,"???????");
229
230 len = strlen(san);
231
232 left = 0;
233 right = len;
234
235 // skip trailing '+' or '#'
236
237 if (left < right) {
238 c = san[right-1];
239 if (c == '+' || c == '#') right--;
240 }
241
242 // castling
243
244 ASSERT(left==0);
245
246 if (false) {
247
248 } else if (right == 3 && strncmp(san,"O-O",3) == 0) {
249
250 if (board->castle[board->turn][SideH] == SquareNone) return false;
251
252 king = king_pos(board,board->turn);
253 rook = board->castle[board->turn][SideH];
254
255 square_to_string(king,king_string,3);
256 square_to_string(rook,rook_string,3);
257
258 sprintf(string,"K%s?%s?",king_string,rook_string);
259
260 } else if (right == 5 && strncmp(san,"O-O-O",5) == 0) {
261
262 if (board->castle[board->turn][SideA] == SquareNone) return false;
263
264 king = king_pos(board,board->turn);
265 rook = board->castle[board->turn][SideA];
266
267 square_to_string(king,king_string,3);
268 square_to_string(rook,rook_string,3);
269
270 sprintf(string,"K%s?%s?",king_string,rook_string);
271
272 } else {
273
274 // moved piece
275
276 if (left < right) {
277
278 c = san[left];
279
280 if (char_is_piece(c)) {
281 string[0] = c;
282 left++;
283 }
284 }
285
286 // promotion
287
288 if (left < right) {
289
290 c = toupper(san[right-1]);
291
292 if (char_is_piece(c)) {
293
294 string[6] = c;
295 right--;
296
297 // skip '='
298
299 if (left < right && san[right-1] == '=') right--;
300 }
301 }
302
303 // to-square rank
304
305 if (left < right) {
306
307 c = san[right-1];
308
309 if (char_is_rank(c)) {
310 string[5] = c;
311 right--;
312 }
313 }
314
315 // to-square file
316
317 if (left < right) {
318
319 c = san[right-1];
320
321 if (char_is_file(c)) {
322 string[4] = c;
323 right--;
324 }
325 }
326
327 // captured piece
328
329 if (left < right) {
330
331 c = san[right-1];
332
333 if (char_is_piece(c)) {
334 string[3] = c;
335 right--;
336 }
337 }
338
339 // skip middle '-' or 'x'
340
341 if (left < right) {
342 c = san[right-1];
343 if (c == '-' || c == 'x') right--;
344 }
345
346 // from-square file
347
348 if (left < right) {
349
350 c = san[left];
351
352 if (char_is_file(c)) {
353 string[1] = c;
354 left++;
355 }
356 }
357
358 // from-square rank
359
360 if (left < right) {
361
362 c = san[left];
363
364 if (char_is_rank(c)) {
365 string[2] = c;
366 left++;
367 }
368 }
369
370 if (left != right) return false;
371 }
372
373 // end
374
375 return true;
376 }
377
378 // move_from_lan()
379
move_from_lan(const char string[],const board_t * board)380 static int move_from_lan(const char string[], const board_t * board) {
381
382 int len;
383 int move;
384 int promote;
385 char s[256];
386 int from, to;
387 int colour;
388 int inc;
389 int piece_char;
390 int n;
391 const uint8 * ptr;
392 int piece;
393 int side;
394
395 ASSERT(string!=NULL);
396 ASSERT(board_is_ok(board));
397
398 // init
399
400 len = strlen(string);
401 if (len != 7) return MoveNone;
402
403 move = MoveNone;
404 colour = board->turn;
405
406 // promote
407
408 promote = 0;
409
410 switch (string[6]) {
411 case '?': // not a promotion
412 break;
413 case 'N':
414 promote = MovePromoteKnight;
415 break;
416 case 'B':
417 promote = MovePromoteBishop;
418 break;
419 case 'R':
420 promote = MovePromoteRook;
421 break;
422 case 'Q':
423 promote = MovePromoteQueen;
424 break;
425 default:
426 return MoveNone;
427 break;
428 }
429
430 // to square
431
432 s[0] = string[4];
433 s[1] = string[5];
434 s[2] = '\0';
435
436 to = square_from_string(s);
437 if (to == SquareNone) return MoveNone;
438
439 // known from square?
440
441 if (string[1] != '?' && string[2] != '?') {
442
443 // from square
444
445 s[0] = string[1];
446 s[1] = string[2];
447 s[2] = '\0';
448
449 from = square_from_string(s);
450 if (from == SquareNone) return MoveNone;
451
452 // convert "king slide" castling to KxR
453
454 if (piece_is_king(board->square[from])
455 && square_rank(to) == square_rank(from)
456 && abs(to-from) > 1) {
457 side = (to > from) ? SideH : SideA;
458 to = board->castle[colour][side];
459 if (to == SquareNone) return MoveNone;
460 }
461
462 // move
463
464 move = move_make(from,to) | promote;
465
466 return move;
467 }
468
469 // pawn non-capture?
470
471 if (string[0] == '?' && string[1] == '?') {
472
473 if (board->square[to] != Empty) return MoveNone; // useful?
474
475 inc = (colour_is_white(colour)) ? +16 : -16;
476
477 from = to - inc;
478 if (board->square[from] == Empty && square_side_rank(to,colour) == Rank4) {
479 from -= inc;
480 }
481
482 if (board->square[from] != piece_make_pawn(colour)) { // useful?
483 return MoveNone;
484 }
485
486 // move
487
488 move = move_make(from,to) | promote;
489
490 return move;
491 }
492
493 // pawn capture?
494
495 piece_char = string[0];
496
497 if (piece_char == '?' && string[1] != '?') {
498 piece_char = 'P';
499 }
500
501 // attack loop
502
503 n = 0;
504
505 for (ptr = board->list[colour]; (from=*ptr) != SquareNone; ptr++) {
506
507 piece = board->square[from];
508
509 if (toupper(piece_to_char(piece)) == piece_char) {
510 if (piece_attack(board,piece,from,to)) {
511 if (true
512 && (string[1] == '?' || file_to_char(square_file(from)) == string[1])
513 && (string[2] == '?' || rank_to_char(square_rank(from)) == string[2])) {
514 if (!is_pinned(board,from,to,colour)) {
515 move = move_make(from,to) | promote;
516 n++;
517 }
518 }
519 }
520 }
521 }
522
523 if (n != 1) move = MoveNone;
524
525 return move;
526 }
527
528 // ambiguity()
529
ambiguity(int move,const board_t * board)530 static int ambiguity(int move, const board_t * board) {
531
532 int from, to, piece;
533 list_t list[1];
534 int i, n, m;
535
536 // init
537
538 from = move_from(move);
539 to = move_to(move);
540 piece = move_piece(move,board);
541
542 gen_legal_moves(list,board);
543
544 // no ambiguity?
545
546 n = 0;
547
548 for (i = 0; i < list_size(list); i++) {
549 m = list_move(list,i);
550 if (move_piece(m,board) == piece && move_to(m) == to) {
551 n++;
552 }
553 }
554
555 if (n == 1) return AMBIGUITY_NONE;
556
557 // file ambiguity?
558
559 n = 0;
560
561 for (i = 0; i < list_size(list); i++) {
562 m = list_move(list,i);
563 if (move_piece(m,board) == piece && move_to(m) == to) {
564 if (square_file(move_from(m)) == square_file(from)) n++;
565 }
566 }
567
568 if (n == 1) return AMBIGUITY_FILE;
569
570 // rank ambiguity?
571
572 n = 0;
573
574 for (i = 0; i < list_size(list); i++) {
575 m = list_move(list,i);
576 if (move_piece(m,board) == piece && move_to(m) == to) {
577 if (square_rank(move_from(m)) == square_rank(from)) n++;
578 }
579 }
580
581 if (n == 1) return AMBIGUITY_RANK;
582
583 // square ambiguity
584
585 return AMBIGUITY_SQUARE;
586 }
587
588 } // namespace adapter
589
590 // end of san.cpp
591
592