Reorder README
[purplehaze.git] / src / main.cpp
blobec13cc1c9a281b8399924d2a24b134d89d6b7d4e
1 /* Copyright (C) 2007-2012 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 <cassert>
18 #include <ctime>
19 #include <fstream>
20 #include <getopt.h>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
24 #include <string>
26 #include "common.h"
27 #include "game.h"
28 #include "xboard.h"
30 static const std::string OPTIONS[][2] = {
32 "help",
33 "display this screen"
36 "version",
37 "display the version"
40 "setboard",
41 "set the board according to <fen> position"
44 "perft",
45 "count all nodes at each depth from the starting position"
48 "divide",
49 "count all nodes from each moves at the starting position"
52 "testsuite <epd> [<time>]",
53 "search each position of <epd> for <time> seconds"
56 "perftsuite <epd>",
57 "compare perft results to each position of <epd>"
60 "xboard",
61 "start XBoard protocole mode"
64 "quit",
65 "exit Purple Haze"
69 static const std::string FLAGS[][2] = {
70 { "-c", "use colored output" },
71 { "-l <file>", "write log to <file>" },
72 { "-m <size>", "set material table size to <size> megabytes" },
73 { "-t <size>", "set transposition table size to <size> megabytes" }
76 void print_usage() {
77 std::cout << "Usage: purplehaze [options]" << std::endl
78 << std::endl
79 << "Options:" << std::endl;
80 std::cout << std::left;
81 int option_width = 15;
82 for (const std::string (&option)[2] : FLAGS) {
83 std::string name = option[0];
84 std::string usage = option[1];
85 std::cout << " "
86 << std::setw(option_width) << name
87 << std::setw(80 - 2 - option_width) << usage
88 << std::endl;
90 std::cout << std::endl
91 << "Report bugs to <bug-purplehaze@vinc.cc>" << std::endl;
94 static std::string prompt()
96 std::cout << "> ";
97 std::string cmd;
98 std::cin >> cmd;
99 if (cmd == "") {
100 cmd = "quit";
102 return cmd;
105 int main(int argc, char *argv[])
107 std::string logfile = "";
108 bool option_color = false;
109 int tt_size = TT_SIZE;
110 int mt_size = MT_SIZE;
111 int opt;
112 while ((opt = getopt(argc, argv, "chl:m:t:")) != EOF) {
113 switch (opt) {
114 case 'c':
115 option_color = true;
116 break;
117 case 'h':
118 print_usage();
119 return 0;
120 case 'l':
121 logfile = optarg;
122 break;
123 case 'm':
124 mt_size = std::stoi(optarg) << 20;
125 break;
126 case 't':
127 tt_size = std::stoi(optarg) << 20;
128 break;
129 case '?':
130 default:
131 std::cerr << "Try 'purplehaze -h' for more information."
132 << std::endl;
133 return 1;
136 std::cout << "Purple Haze " << VERSION << std::endl;
137 std::cout << std::endl;
139 // Parse commands from CLI
140 std::string init_fen(DEFAULT_FEN);
141 std::string cmd;
142 while ((cmd = prompt()) != "quit") {
143 if (cmd == "xboard") { // Xboard protocol mode
144 std::cin.ignore(); // Discards end of line
145 Xboard xboard(tt_size, mt_size);
146 if (!logfile.empty()) {
147 xboard.debug(logfile);
149 xboard.loop();
150 return 0;
151 } else if (cmd == "help") {
152 std::cout << std::left;
153 int comm_width = 30;
154 int desc_width = 50;
155 for (const std::string (&option)[2] : OPTIONS) {
156 std::string name = option[0];
157 std::string usage = option[1];
158 std::cout << std::setw(comm_width) << name
159 << std::setw(desc_width) << usage
160 << std::endl;
162 } else if (cmd == "version") {
163 std::cout << VERSION << std::endl;
164 } else if (cmd == "setboard") { // Get FEN
165 getline(std::cin, init_fen);
166 init_fen.erase(0, 1); // Remove the first whitespace
167 } else if (cmd == "perft") {
168 Game game(tt_size, mt_size);
169 game.init(init_fen);
170 for (unsigned int i = 1; ; ++i) {
171 clock_t start = clock();
172 unsigned long long int perft_result = game.perft(i);
173 long double clocks = clock() - start;
174 long double perft_time = clocks / CLOCKS_PER_SEC;
175 std::cout << "Perft(" << i << ") = " << perft_result;
176 std::cout << " (" << perft_time << " secs, ";
177 std::cout << perft_result / perft_time << " nps)" << std::endl;
179 } else if (cmd == "divide") {
180 int depth = 0;
181 std::cin >> depth;
182 std::cout << std::endl;
183 Game game(tt_size, mt_size);
184 game.init(init_fen);
185 Color c = game.current_position().side();
186 unsigned int nodes_count = 0;
187 unsigned int moves_count = 0;
188 Moves moves(game.board, game.pieces, game.current_position(),
189 game.search_moves);
190 Move move;
191 while (!(move = moves.next()).is_null()) {
192 game.make_move(move);
193 if (!game.is_check(c)) {
194 const unsigned int n = game.perft(depth - 1);
195 nodes_count += n;
196 ++moves_count;
197 std::cout << move << " " << n << std::endl;
199 game.undo_move(move);
201 std::cout << std::endl;
202 std::cout << "Moves: " << moves_count << std::endl;
203 std::cout << "Positions: " << nodes_count << std::endl;
204 } else if (cmd == "testsuite") { // Load EPD test suite
205 std::string filename;
206 std::cin >> filename;
207 std::string args;
208 getline(std::cin, args);
210 // Check if filename exists
211 std::ifstream epdfile;
212 epdfile.open(filename.c_str());
213 if (!epdfile.is_open()) {
214 std::cerr << "Cannot open '" << filename << "': "
215 << "No such file or directory" << std::endl;
216 continue;
219 // Get time per move (optional)
220 int time = 10;
221 if (args != "") {
222 std::istringstream iss(args);
223 if (!(iss >> time)) {
224 args.erase(0, 1); // Remove whitespace
225 std::cerr << "Invalid argument '" << args << "'"
226 << std::endl;
227 continue;
231 std::cout << "Loading '" << filename << "', ";
232 std::cout << time << "s per move" << std::endl; // In seconds
234 // Load game protocol
235 Protocol proto(tt_size, mt_size);
236 proto.set_output_thinking(false);
237 proto.set_time(1, time);
239 // Read positions in file
240 unsigned int res = 0;
241 unsigned int i = 0;
242 std::string line;
243 while (getline(epdfile, line)) {
244 proto.new_game();
245 // TODO: add am (avoid move)
246 size_t fensep = line.find(" bm ");
247 size_t bmsep = line.find(";");
248 size_t not_found = std::string::npos;
249 if (fensep == not_found || bmsep == not_found) {
250 continue;
253 // Load position in game
254 init_fen = line.substr(0, fensep);
255 std::cout << "FEN #" << i + 1 << " '" << init_fen << "' ";
256 proto.set_board(init_fen);
258 // Search best move and test it
259 std::string best_moves = line.substr(fensep + 4,
260 bmsep - fensep - 4);
261 std::cout << "bm " << best_moves;
262 std::string move = proto.search_move(true);
263 std::cout << " => " << move;
264 std::istringstream iss(best_moves);
265 bool is_found = false;
266 do {
267 std::string best_move;
268 iss >> best_move;
269 if (best_move == move) {
270 is_found = true;
271 break;
273 } while (iss);
275 if (is_found) {
276 if (option_color) {
277 std::cout << color_green;
279 std::cout << " OK";
280 ++res;
281 } else {
282 if (option_color) {
283 std::cout << color_red;
285 std::cout << " KO";
287 if (option_color) {
288 std::cout << color_reset;
290 std::cout << std::endl;
291 ++i;
293 std::cout << "Result: " << res << "/" << i << std::endl;
294 epdfile.close();
295 } else if (cmd == "perftsuite") { // Load perft test suite
296 std::string filename;
297 std::cin >> filename;
298 std::ifstream epdfile;
299 epdfile.open(filename.c_str());
300 if (!epdfile.is_open()) {
301 std::cerr << "Cannot open '" << filename << "': "
302 << "No such file or directory" << std::endl;
303 continue;
305 const size_t npos = std::string::npos;
306 std::string line;
307 while (getline(epdfile, line)) {
308 std::string fen;
309 Game game(tt_size, mt_size);
310 for (int i = 0; line.length() > 0; ++i) {
312 const size_t pos = line.find(" ;");
313 std::string sub = line.substr(0, pos);
314 line = (pos != npos ? line.substr(pos + 2, npos) : "");
316 if (i == 0) {
317 fen = sub;
318 game.init(fen);
319 std::cout << "FEN '" << fen << "': " << std::flush;
320 } else {
321 sub = sub.substr(3, npos); // Skip /^D\d /
322 unsigned int moves = game.perft(i);
323 unsigned int expected;
324 std::istringstream(sub) >> expected;
325 if (option_color) {
326 std::cout << (moves == expected ? color_green
327 : color_red);
329 std::cout << (moves == expected ? "." : "x")
330 << (option_color ? color_reset : "")
331 << std::flush;
334 std::cout << std::endl;
336 epdfile.close();
337 } else {
338 std::cerr << "Invalid command '" << cmd << "'" << std::endl;
341 return 0;