Rename Position::turn_color() to Position::side()
[purplehaze.git] / src / output.cpp
blob45f6d09592e9d3a26d7b238f252473b5f7ce62ff
1 /* Copyright (C) 2007-2011 Vincent Ollivier
3 * Purple Haze is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * Purple Haze is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include <assert.h>
18 #include <iostream>
19 #include <iomanip>
20 #include <string>
21 #include <sstream>
22 #include <bitset>
24 #include "game.h"
26 static const int WIDE = 10;
28 void Game::print_thinking_header()
30 if (!output_thinking) return;
31 std::cout << std::setw(4) << "ply"
32 << std::setw(WIDE - 1) << "score"
33 << std::setw(WIDE) << "time"
34 << std::setw(WIDE + 3) << "nodes"
35 << std::setw(WIDE) << "pv"
36 << std::endl;
39 void Game::print_thinking(int depth, int score, Move m)
41 if (!output_thinking) return;
42 std::cout << std::setw(4) << depth
43 << std::setw(WIDE - 1) << score
44 << std::setw(WIDE) << time.elapsed()
45 << std::setw(WIDE + 3) << nodes_count
46 << std::setw(WIDE - 3) << " ";
47 const int ply = tree.ply();
49 if (current_position().side() == BLACK) {
50 std::cout << " " << 1 + (ply / 2) << ". ...";
53 assert(is_legal(m) || assert_msg(debug_move(m)));
54 std::cout << output_pv(depth, score, m) << std::endl;
57 bool is_mate(int score)
59 return ((score < -INF + MAX_PLY) || (INF - MAX_PLY < score));
62 std::string Game::output_pv(int depth, int score, Move m)
64 std::ostringstream stream;
65 stream << " ";
66 const int ply = tree.ply();
67 if (current_position().side() == WHITE) {
68 stream << 1 + (ply / 2) << ". ";
70 stream << output_move(m);
72 make_move(m); // Update nodes_count
73 --nodes_count;
75 bool is_in_check = is_check(current_position().side());
77 // Find next move in TT
78 bool is_empty;
79 Transposition trans = tt.lookup(current_position().hash(), &is_empty);
80 Move move = trans.best_move();
81 if (depth > 0 && is_legal(move) && trans.bound() < 3) {
82 if (is_in_check) stream << "+"; // Check
83 stream << output_pv(depth - 1, trans.value(), move);
84 } else if (move.is_null() && is_mate(score)) {
85 if (is_in_check) stream << "#"; // Mate
86 } else if (is_in_check) {
87 stream << "+"; // Cut-off
90 undo_move(m);
91 return stream.str();
94 std::string Game::output_move(Move m)
96 std::ostringstream stream;
98 // Castling
99 if (m.is_castle()) {
100 if (m.castle_side() == QUEEN) stream << "O-";
101 return stream.str() + "O-O";
104 // Type of piece
105 Square from = m.orig();
106 Piece p = board[from];
107 PieceType t = p.type();
108 if (t > PAWN) stream << Piece(WHITE, t); // Upper case
110 // Disambiguation
111 if (t != PAWN) {
112 Color c = p.color();
113 Square to = m.dest();
114 for (int i = 0; i < pieces.count(c, t); ++i) {
115 Piece other(c, t, i);
116 if (other == p) continue;
117 Square s = pieces.position(other);
118 if (board.can_attack(t, s, to) && board.can_go(other, s, to)) {
119 // If another piece of the same type can theoretically
120 // attack the destination (fast answer by array lookup)
121 // and can really go to this destination (not so fast
122 // answer) then a disambiguation is needed
123 stream << static_cast<char>('a' + m.orig_file());
124 break;
129 // Capture
130 if (m.is_capture()) {
131 if (t == PAWN) stream << static_cast<char>('a' + m.orig_file());
132 stream << "x";
135 // Destination
136 stream << output_square(m.dest_file(), m.dest_rank());
138 // Promotion
139 if (m.is_promotion()) {
140 stream << "=" << Piece(WHITE, m.promotion_type());
143 return stream.str();
146 std::string Game::output_square(File f, Rank r)
148 std::ostringstream stream;
149 stream << static_cast<char>('a' + f) << static_cast<char>('1' + r);
150 return stream.str();
153 std::string get_stat(std::string title, double value, std::string unit = "")
155 std::ostringstream stream;
156 stream << std::left << " " << std::setw(20) << title;
157 int precision = (unit == "%" ? 2 : 0);
158 stream << std::fixed << std::setprecision(precision) << value << unit;
159 return stream.str();
162 std::string get_meta(double value, std::string unit)
164 std::ostringstream stream;
165 stream << " ("
166 << std::fixed << std::setprecision(2) << value << unit
167 << ")";
168 return stream.str();
171 double get_percent(double a, double b)
173 return 100 * a / b;
176 template <class T>
177 std::string print_table_stats(const HashTable<T>& table, int table_size)
179 long zeros = 0;
180 long ones = 0;
181 for (int i = 0; i < table.size(); ++i) {
182 Hash h = table.hash_at(i);
183 if (!h) continue;
184 std::bitset<64> b = h;
185 int z = b.count();
186 zeros += 64 - z;
187 ones += z;
190 std::ostringstream stream;
191 stream << get_stat("Table Size", table_size / 1024 / 1024, "Mb");
192 stream << std::endl;
194 stream << get_stat("Entries", table.size());
195 stream << std::endl;
197 stream << get_stat("Usage", table.usage());
198 stream << get_meta(get_percent(table.usage(), table.size()), "%");
199 stream << std::endl;
201 stream << get_stat("0's", get_percent(zeros, 64 * table.usage()), "%");
202 stream << std::endl;
204 stream << get_stat("1's", get_percent(ones, 64 * table.usage()), "%");
205 stream << std::endl;
207 stream << get_stat("Lookups", table.nb_lookups());
208 stream << std::endl;
210 stream << get_stat("Hits", table.nb_hits());
211 stream << get_meta(get_percent(table.nb_hits(),
212 table.nb_lookups()), "%");
213 stream << std::endl;
215 stream << get_stat("Collisions", table.nb_collisions());
216 stream << get_meta(get_percent(table.nb_collisions(),
217 table.nb_lookups()), "%");
218 stream << std::endl;
220 stream << get_stat("Misses", table.nb_misses());
221 stream << get_meta(get_percent(table.nb_misses(),
222 table.nb_lookups()), "%");
223 stream << std::endl;
225 return stream.str();
228 void Game::print_tt_stats()
230 std::cout << "Transposition Table usage:" << std::endl;
231 std::cout << print_table_stats(tt, TT_SIZE) << std::endl;
233 std::cout << "Material Table usage:" << std::endl;
234 std::cout << print_table_stats(material_table, MT_SIZE) << std::endl;
237 std::string Game::debug_move(Move m)
239 std::ostringstream stream;
240 Color c = current_position().side();
241 stream << std::endl << board << std::endl
242 << (c == WHITE ? "White" : "Black") << " to move" << std::endl
243 << "m = " << output_move(m) << " (" << m << ")" << std::endl
244 << "m is en passant: " << m.is_en_passant() << std::endl
245 << "m is promotion: " << m.is_promotion() << std::endl
246 << "m is legal: " << is_legal(m) << std::endl
247 << std::hex << current_position().hash();
248 return stream.str();