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