Add Game::is_dangerous() for Futility Pruning
[purplehaze.git] / src / main.cpp
blobe796a1ae848ac999b9029f882274d5290a119d60
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 <fstream>
19 #include <getopt.h>
20 #include <iomanip>
21 #include <iostream>
22 #include <sstream>
23 #include <stdio.h>
24 #include <string>
25 #include <time.h>
27 #include "common.h"
28 #include "game.h"
29 #include "xboard.h"
31 static const std::string OPTIONS[][2] = {
33 "help",
34 "display this screen"
37 "version",
38 "display the version"
41 "setboard",
42 "set the board according to <fen> position"
45 "perft",
46 "count all nodes at each depth from the starting position"
49 "divide",
50 "count all nodes from each moves at the starting position"
53 "testsuite <epd> [<time>]",
54 "search each position of <epd> for <time> seconds"
57 "perftsuite <epd>",
58 "compare perft results to each position of <epd>"
61 "xboard",
62 "start XBoard protocole mode"
65 "quit",
66 "exit Purple Haze"
70 std::string prompt()
72 std::cout << "> ";
73 std::string cmd;
74 std::cin >> cmd;
75 return cmd;
78 int main(int argc, char *argv[])
80 bool option_color = false;
81 char opt;
82 while ((opt = getopt(argc, argv, "c")) != -1) {
83 switch (opt) {
84 case 'c':
85 option_color = true;
86 break;
89 std::cout << "Purple Haze " << VERSION << std::endl;
90 std::cout << std::endl;
92 // Parse commands from CLI
93 std::string init_fen(DEFAULT_FEN);
94 for (std::string cmd = prompt(); cmd != "quit"; cmd = prompt()) {
95 if (cmd == "xboard") { // Xboard protocol mode
96 Xboard xboard;
97 xboard.loop();
98 return 0;
99 } else if (cmd == "help") {
100 std::cout << std::left;
101 int comm_width = 30;
102 int desc_width = 50;
103 for (const std::string (&option)[2] : OPTIONS) {
104 std::string name = option[0];
105 std::string usage = option[1];
106 std::cout << std::setw(comm_width) << name
107 << std::setw(desc_width) << usage
108 << std::endl;
110 } else if (cmd == "version") {
111 std::cout << VERSION << std::endl;
112 } else if (cmd == "setboard") { // Get FEN
113 getline(std::cin, init_fen);
114 init_fen.erase(0, 1); // Remove the first whitespace
115 } else if (cmd == "perft") {
116 Game game;
117 game.init(init_fen);
118 for (unsigned int i = 1; ; ++i) {
119 clock_t starting_time = clock();
120 unsigned long long int perft_result = game.perft(i);
121 long double elapsed = clock() - starting_time;
122 long double perft_time = elapsed / CLOCKS_PER_SEC;
123 std::cout << "Perft(" << i << ") = " << perft_result;
124 std::cout << " (" << perft_time << " secs, ";
125 std::cout << perft_result / perft_time << " nps)" << std::endl;
127 } else if (cmd == "divide") {
128 int depth = 0;
129 std::cin >> depth;
130 std::cout << std::endl;
131 Game game;
132 game.init(init_fen);
133 Color c = game.current_position().get_turn_color();
134 unsigned int nodes_count = 0;
135 unsigned int moves_count = 0;
136 Moves moves(game.board, game.pieces, game.current_position(),
137 game.search_moves);
138 Move move;
139 while (!(move = moves.next()).is_null()) {
140 game.make_move(move);
141 if (!game.is_check(c)) {
142 unsigned int cnt = game.perft(depth - 1);
143 nodes_count += cnt;
144 ++moves_count;
145 std::cout << move << " " << cnt << std::endl;
147 game.undo_move(move);
149 std::cout << std::endl;
150 std::cout << "Moves: " << moves_count << std::endl;
151 std::cout << "Positions: " << nodes_count << std::endl;
152 } else if (cmd == "testsuite") { // Load EPD test suite
153 std::string filename;
154 std::cin >> filename;
156 // Check if filename exists
157 std::ifstream epdfile;
158 epdfile.open(filename.c_str());
159 if (!epdfile.is_open()) {
160 std::cerr << "Cannot open '" << filename;
161 std::cerr << "': No such file or directory" << std::endl;
164 // Get time per move (optional)
165 std::string seconds;
166 getline(std::cin, seconds);
167 int time = 10;
168 if (seconds != "") {
169 std::istringstream iss(seconds);
170 iss >> time;
173 std::cout << "Loading '" << filename << "', ";
174 std::cout << time << "s per move" << std::endl; // In seconds
176 // Load game protocol
177 Protocol proto;
178 proto.set_output_thinking(false);
179 proto.set_time(1, time);
181 // Read positions in file
182 unsigned int res = 0;
183 unsigned int i = 0;
184 std::string line;
185 while (getline(epdfile, line)) {
186 proto.new_game();
187 // TODO: add am (avoid move)
188 size_t fensep = line.find(" bm ");
189 size_t bmsep = line.find(";");
190 size_t not_found = std::string::npos;
191 if (fensep == not_found || bmsep == not_found) continue;
193 // Load position in game
194 init_fen = line.substr(0, fensep);
195 std::cout << "Loading position #" << i + 1
196 << " '" << init_fen << "' ";
197 proto.set_board(init_fen);
199 // Search best move and test it
200 std::string best_moves = line.substr(fensep + 4,
201 bmsep - fensep - 4);
202 std::cout << "bm " << best_moves;
203 std::string move = proto.search_move(true);
204 std::cout << " => " << move;
205 std::istringstream iss(best_moves);
206 bool is_found = false;
207 do {
208 std::string best_move;
209 iss >> best_move;
210 if (best_move == move) {
211 is_found = true;
212 break;
214 } while (iss);
216 if (is_found) {
217 if (option_color) std::cout << color_green;
218 std::cout << " OK";
219 ++res;
220 } else {
221 if (option_color) std::cout << color_red;
222 std::cout << " KO";
224 if (option_color) std::cout << color_reset;
225 std::cout << std::endl;
226 ++i;
228 std::cout << "Result: " << res << "/" << i << std::endl;
229 epdfile.close();
230 } else if (cmd == "perftsuite") { // Load perft test suite
231 std::string filename;
232 std::cin >> filename;
233 std::ifstream epdfile;
234 epdfile.open(filename.c_str());
235 if (!epdfile.is_open()) {
236 std::cerr << "Cannot open '" << filename << "': "
237 << "No such file or directory" << std::endl;
239 const size_t npos = std::string::npos;
240 std::string line;
241 while (getline(epdfile, line)) {
242 std::string fen;
243 Game game;
244 for (int i = 0; line.length() > 0; ++i) {
246 const size_t pos = line.find(" ;");
247 std::string sub = line.substr(0, pos);
248 line = (pos != npos ? line.substr(pos + 2, npos) : "");
250 if (i == 0) {
251 fen = sub;
252 game.init(fen);
253 std::cout << "FEN '" << fen << "': " << std::flush;
254 } else {
255 sub = sub.substr(3, npos); // Skip /^D\d /
256 unsigned int moves = game.perft(i);
257 unsigned int expected;
258 std::istringstream(sub) >> expected;
259 if (option_color) {
260 std::cout << (moves == expected ? color_green
261 : color_red);
263 std::cout << (moves == expected ? "." : "x")
264 << (option_color ? color_reset : "")
265 << std::flush;
268 std::cout << std::endl;
270 epdfile.close();
273 return 0;