]> git.sesse.net Git - stockfish/blob - src/san.cpp
Use extendable instead of depth extension
[stockfish] / src / san.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
4   Copyright (C) 2008 Marco Costalba
5
6   Stockfish is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   Stockfish is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21 ////
22 //// Includes
23 ////
24
25 #include <cassert>
26 #include <cstring>
27 #include <iomanip>
28 #include <string>
29 #include <sstream>
30
31 #include "movepick.h"
32 #include "san.h"
33
34 extern SearchStack EmptySearchStack;
35
36 ////
37 //// Local definitions
38 ////
39
40 namespace {
41
42   /// Types
43
44   enum Ambiguity {
45     AMBIGUITY_NONE,
46     AMBIGUITY_FILE,
47     AMBIGUITY_RANK,
48     AMBIGUITY_BOTH
49   };
50
51
52   /// Functions
53
54   Ambiguity move_ambiguity(Position &pos, Move m);
55   const std::string time_string(int milliseconds);
56   const std::string score_string(Value v);
57 }
58
59
60 ////
61 //// Functions
62 ////
63
64 /// move_to_san() takes a position and a move as input, where it is assumed
65 /// that the move is a legal move from the position.  The return value is
66 /// a string containing the move in short algebraic notation.
67
68 const std::string move_to_san(Position &pos, Move m) {
69   std::string str;
70
71   assert(pos.is_ok());
72   assert(move_is_ok(m));
73
74   if(m == MOVE_NONE) {
75     str = "(none)";
76     return str;
77   }
78   else if(m == MOVE_NULL) {
79     str = "(null)";
80     return str;
81   }
82   else if(move_is_long_castle(m))
83     str = "O-O-O";
84   else if(move_is_short_castle(m))
85     str = "O-O";
86   else {
87     Square from, to;
88     Piece pc;
89
90     from = move_from(m);
91     to = move_to(m);
92     pc = pos.piece_on(move_from(m));
93
94     str = "";
95
96     if(type_of_piece(pc) == PAWN) {
97       if(pos.move_is_capture(m))
98         str += file_to_char(square_file(move_from(m)));
99     }
100     else {
101       str += piece_type_to_char(type_of_piece(pc), true);
102
103       Ambiguity amb = move_ambiguity(pos, m);
104       switch(amb) {
105
106       case AMBIGUITY_NONE:
107         break;
108
109       case AMBIGUITY_FILE:
110         str += file_to_char(square_file(from));
111         break;
112
113       case AMBIGUITY_RANK:
114         str += rank_to_char(square_rank(from));
115         break;
116
117       case AMBIGUITY_BOTH:
118         str += square_to_string(from);
119         break;
120
121       default:
122         assert(false);
123       }
124     }
125
126     if(pos.move_is_capture(m))
127       str += "x";
128
129     str += square_to_string(move_to(m));
130
131     if(move_promotion(m)) {
132       str += "=";
133       str += piece_type_to_char(move_promotion(m), true);
134     }
135   }
136
137   // Is the move check?  We don't use pos.move_is_check(m) here, because
138   // Position::move_is_check doesn't detect all checks (not castling moves,
139   // promotions and en passant captures).
140   UndoInfo u;
141   pos.do_move(m, u);
142   if(pos.is_check())
143     str += pos.is_mate()? "#" : "+";
144   pos.undo_move(m, u);
145
146   return str;
147 }
148
149
150 /// move_from_san() takes a position and a string as input, and tries to
151 /// interpret the string as a move in short algebraic notation.  On success,
152 /// the move is returned.  On failure (i.e. if the string is unparsable, or
153 /// if the move is illegal or ambiguous), MOVE_NONE is returned.
154
155 Move move_from_san(Position &pos, const std::string &movestr) {
156   assert(pos.is_ok());
157
158   MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
159
160   // Castling moves
161   if(movestr == "O-O-O") {
162     Move m;
163     while((m = mp.get_next_move()) != MOVE_NONE)
164       if(move_is_long_castle(m) && pos.pl_move_is_legal(m))
165         return m;
166     return MOVE_NONE;
167   }
168   else if(movestr == "O-O") {
169     Move m;
170     while((m = mp.get_next_move()) != MOVE_NONE)
171       if(move_is_short_castle(m) && pos.pl_move_is_legal(m))
172         return m;
173     return MOVE_NONE;
174   }
175
176   // Normal moves
177   const char *cstr = movestr.c_str();
178   const char *c;
179   char *cc;
180   char str[10];
181   int i;
182
183   // Initialize str[] by making a copy of movestr with the characters
184   // 'x', '=', '+' and '#' removed.
185   cc = str;
186   for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
187     if(!strchr("x=+#", *c)) {
188       *cc = strchr("nrq", *c)? toupper(*c) : *c;
189       cc++;
190     }
191   *cc = '\0';
192
193   size_t left = 0, right = strlen(str) - 1;
194   PieceType pt = NO_PIECE_TYPE, promotion;
195   Square to;
196   File fromFile = FILE_NONE;
197   Rank fromRank = RANK_NONE;
198
199   // Promotion?
200   if(strchr("BNRQ", str[right])) {
201     promotion = piece_type_from_char(str[right]);
202     right--;
203   }
204   else
205     promotion = NO_PIECE_TYPE;
206
207   // Find the moving piece:
208   if(left < right) {
209     if(strchr("BNRQK", str[left])) {
210       pt = piece_type_from_char(str[left]);
211       left++;
212     }
213     else
214       pt = PAWN;
215   }
216
217   // Find the to square:
218   if(left < right) {
219     if(str[right] < '1' || str[right] > '8' ||
220        str[right-1] < 'a' || str[right-1] > 'h')
221       return MOVE_NONE;
222     to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
223     right -= 2;
224   }
225   else
226     return MOVE_NONE;
227
228   // Find the file and/or rank of the from square:
229   if(left <= right) {
230     if(strchr("abcdefgh", str[left])) {
231       fromFile = file_from_char(str[left]);
232       left++;
233     }
234     if(strchr("12345678", str[left]))
235       fromRank = rank_from_char(str[left]);
236   }
237
238   // Look for a matching move:
239   Move m, move = MOVE_NONE;
240   int matches = 0;
241
242   while((m = mp.get_next_move()) != MOVE_NONE) {
243     bool match = true;
244     if(pos.type_of_piece_on(move_from(m)) != pt)
245       match = false;
246     else if(move_to(m) != to)
247       match = false;
248     else if(move_promotion(m) != promotion)
249       match = false;
250     else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
251       match = false;
252     else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
253       match = false;
254     if(match) {
255       move = m;
256       matches++;
257     }
258   }
259
260   if(matches == 1)
261     return move;
262   else
263     return MOVE_NONE;
264 }
265
266
267 /// line_to_san() takes a position and a line (an array of moves representing
268 /// a sequence of legal moves from the position) as input, and returns a
269 /// string containing the line in short algebraic notation.  If the boolean
270 /// parameter 'breakLines' is true, line breaks are inserted, with a line
271 /// length of 80 characters.  After a line break, 'startColumn' spaces are
272 /// inserted at the beginning of the new line.
273
274 const std::string line_to_san(const Position &pos, Move line[], int startColumn,
275                               bool breakLines) {
276   Position p = Position(pos);
277   UndoInfo u;
278   std::stringstream s;
279   std::string moveStr;
280   size_t length, maxLength;
281
282   length = 0;
283   maxLength = 80 - startColumn;
284
285   for(int i = 0; line[i] != MOVE_NONE; i++) {
286     moveStr = move_to_san(p, line[i]);
287     length += moveStr.length() + 1;
288     if(breakLines && length > maxLength) {
289       s << "\n";
290       for(int j = 0; j < startColumn; j++)
291         s << " ";
292       length = moveStr.length() + 1;
293     }
294     s << moveStr << " ";
295
296     if(line[i] == MOVE_NULL)
297       p.do_null_move(u);
298     else
299       p.do_move(line[i], u);
300   }
301
302   return s.str();
303 }
304
305
306 /// pretty_pv() creates a human-readable string from a position and a PV.
307 /// It is used to write search information to the log file (which is created
308 /// when the UCI parameter "Use Search Log" is "true").
309
310 const std::string pretty_pv(const Position &pos, int time, int depth,
311                             uint64_t nodes, Value score, Move pv[]) {
312   std::stringstream s;
313
314   // Depth
315   s << std::setw(2) << std::setfill(' ') << depth << "  ";
316
317   // Score
318   s << std::setw(8) << score_string(score);
319
320   // Time
321   s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
322
323   // Nodes
324   if(nodes < 1000000ULL)
325     s << std::setw(8) << std::setfill(' ') << nodes << " ";
326   else if(nodes < 1000000000ULL)
327     s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
328   else
329     s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
330
331   // PV
332   s << line_to_san(pos, pv, 30, true);
333
334   return s.str();
335 }
336
337
338 namespace {
339
340   Ambiguity move_ambiguity(Position &pos, Move m) {
341     Square from, to;
342     Piece pc;
343
344     from = move_from(m);
345     to = move_to(m);
346     pc = pos.piece_on(from);
347
348     // King moves are never ambiguous, because there is never two kings of
349     // the same color.
350     if(type_of_piece(pc) == KING)
351       return AMBIGUITY_NONE;
352
353     MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
354     Move mv, moveList[8];
355     int i, j, n;
356
357     n = 0;
358     while((mv = mp.get_next_move()) != MOVE_NONE)
359       if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
360          && pos.pl_move_is_legal(mv))
361         moveList[n++] = mv;
362     if(n == 1)
363       return AMBIGUITY_NONE;
364
365     j = 0;
366     for(i = 0; i < n; i++)
367       if(square_file(move_from(moveList[i])) == square_file(from))
368         j++;
369     if(j == 1)
370       return AMBIGUITY_FILE;
371
372     j = 0;
373     for(i = 0; i < n; i++)
374       if(square_rank(move_from(moveList[i])) == square_rank(from))
375         j++;
376     if(j == 1)
377       return AMBIGUITY_RANK;
378
379     return AMBIGUITY_BOTH;
380   }
381
382
383   const std::string time_string(int milliseconds) {
384     std::stringstream s;
385
386     int hours = milliseconds / (1000 * 60 * 60);
387     int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
388     int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
389
390     if(hours)
391       s << hours << ':';
392     s << std::setw(2) << std::setfill('0') << minutes << ':';
393     s << std::setw(2) << std::setfill('0') << seconds;
394
395     return s.str();
396   }
397
398
399   const std::string score_string(Value v) {
400     std::stringstream s;
401
402     if(abs(v) >= VALUE_MATE - 200) {
403       if(v < 0)
404         s << "-#" << (VALUE_MATE + v) / 2;
405       else
406         s << "#" << (VALUE_MATE - v + 1) / 2;
407     }
408     else {
409       float floatScore = float(v) / float(PawnValueMidgame);
410       if(v >= 0)
411         s << '+';
412       s << std::setprecision(2) << std::fixed << floatScore;
413     }
414     return s.str();
415   }
416
417 }