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