Add Game::is_dangerous() for Futility Pruning
[purplehaze.git] / src / output.cpp
blob2f0973c6af4f834283ea2da685ed6f69e951c38a
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.get_elapsed_time()
45 << std::setw(WIDE + 3) << nodes_count
46 << std::setw(WIDE - 3) << " ";
47 int ply = current_position().get_ply();
49 if (current_position().get_turn_color() == 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 int ply = current_position().get_ply();
67 if (current_position().get_turn_color() == WHITE) {
68 stream << 1 + (ply / 2) << ". ";
70 stream << output_move(m);
72 make_move(m);
74 bool is_in_check = is_check(current_position().get_turn_color());
76 // Find next move in TT
77 bool is_empty;
78 Transposition trans = tt.lookup(current_position().hash(), &is_empty);
79 Move move = trans.get_best_move();
80 if (depth > 0 && is_legal(move) && trans.get_bound() < 3) {
81 if (is_in_check) stream << "+"; // Check
82 stream << output_pv(depth - 1, trans.get_value(), move);
83 } else if (move.is_null() && is_mate(score)) {
84 if (is_in_check) stream << "#"; // Mate
85 } else if (is_in_check) {
86 stream << "+"; // Cut-off
89 undo_move(m);
90 return stream.str();
93 std::string Game::output_move(Move m)
95 std::ostringstream stream;
97 // Castling
98 if (m.is_castle()) {
99 if (m.get_castle_side() == QUEEN) stream << "O-";
100 return stream.str() + "O-O";
103 // Type of piece
104 Square from = m.get_orig();
105 Piece p = board.get_piece(from);
106 PieceType t = p.get_type();
107 if (t > PAWN) stream << Piece(WHITE, t); // Upper case
109 // Disambiguation
110 if (t != PAWN) {
111 Color c = p.get_color();
112 Square to = m.get_dest();
113 for (int i = 0; i < pieces.count(c, t); ++i) {
114 Piece other(c, t, i);
115 if (other == p) continue;
116 Square s = pieces.get_position(other);
117 if (board.can_attack(t, s, to) && board.can_go(other, s, to)) {
118 // If another piece of the same type can theoretically
119 // attack the destination (fast answer by array lookup)
120 // and can really go to this destination (not so fast
121 // answer) then a disambiguation is needed
122 stream << static_cast<char>('a' + m.get_orig_file());
123 break;
128 // Capture
129 if (m.is_capture()) {
130 if (t == PAWN) stream << static_cast<char>('a' + m.get_orig_file());
131 stream << "x";
134 // Destination
135 stream << output_square(m.get_dest_file(), m.get_dest_rank());
137 // Promotion
138 if (m.is_promotion()) {
139 stream << "=" << Piece(WHITE, m.get_promotion_type());
142 return stream.str();
145 std::string Game::output_square(File f, Rank r)
147 std::ostringstream stream;
148 stream << static_cast<char>('a' + f) << static_cast<char>('1' + r);
149 return stream.str();
152 std::string get_stat(std::string title, double value, std::string unit = "")
154 std::ostringstream stream;
155 stream << std::left << " " << std::setw(20) << title;
156 int precision = (unit == "%" ? 2 : 0);
157 stream << std::fixed << std::setprecision(precision) << value << unit;
158 return stream.str();
161 std::string get_meta(double value, std::string unit)
163 std::ostringstream stream;
164 stream << " ("
165 << std::fixed << std::setprecision(2) << value << unit
166 << ")";
167 return stream.str();
170 double get_percent(double a, double b)
172 return 100 * a / b;
175 template <class T>
176 std::string print_table_stats(HashTable<T>& table, int table_size)
178 long zeros = 0;
179 long ones = 0;
180 for (int i = 0; i < table.size(); ++i) {
181 Hash h = table.get_hash_at(i);
182 if (!h) continue;
183 std::bitset<64> b = h;
184 int z = b.count();
185 zeros += 64 - z;
186 ones += z;
189 std::ostringstream stream;
190 stream << get_stat("Table Size", table_size / 1024 / 1024, "Mb");
191 stream << std::endl;
193 stream << get_stat("Entries", table.size());
194 stream << std::endl;
196 stream << get_stat("Usage", table.get_usage());
197 stream << get_meta(get_percent(table.get_usage(), table.size()), "%");
198 stream << std::endl;
200 stream << get_stat("0's", get_percent(zeros, 64 * table.get_usage()), "%");
201 stream << std::endl;
203 stream << get_stat("1's", get_percent(ones, 64 * table.get_usage()), "%");
204 stream << std::endl;
206 stream << get_stat("Lookups", table.get_nb_lookups());
207 stream << std::endl;
209 stream << get_stat("Hits", table.get_nb_hits());
210 stream << get_meta(get_percent(table.get_nb_hits(),
211 table.get_nb_lookups()), "%");
212 stream << std::endl;
214 stream << get_stat("Collisions", table.get_nb_collisions());
215 stream << get_meta(get_percent(table.get_nb_collisions(),
216 table.get_nb_lookups()), "%");
217 stream << std::endl;
219 stream << get_stat("Misses", table.get_nb_misses());
220 stream << get_meta(get_percent(table.get_nb_misses(),
221 table.get_nb_lookups()), "%");
222 stream << std::endl;
224 return stream.str();
227 void Game::print_tt_stats()
229 std::cout << "Transposition Table usage:" << std::endl;
230 std::cout << print_table_stats(tt, TT_SIZE) << std::endl;
232 std::cout << "Material Table usage:" << std::endl;
233 std::cout << print_table_stats(material_table, MT_SIZE) << std::endl;
236 std::string Game::debug_move(Move m)
238 std::ostringstream stream;
239 Color c = current_position().get_turn_color();
240 stream << std::endl << board << std::endl
241 << (c == WHITE ? "White" : "Black") << " to move" << std::endl
242 << "m = " << output_move(m) << " (" << m << ")" << std::endl
243 << "m is en passant: " << m.is_en_passant() << std::endl
244 << "m is promotion: " << m.is_promotion() << std::endl
245 << "m is legal: " << is_legal(m) << std::endl
246 << std::hex << current_position().hash();
247 return stream.str();