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