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/>.
30 static const std::string OPTIONS
[][2] = {
41 "set the board according to <fen> position"
45 "count all nodes at each depth from the starting position"
49 "count all nodes from each moves at the starting position"
52 "testsuite <epd> [<time>]",
53 "search each position of <epd> for <time> seconds"
57 "compare perft results to each position of <epd>"
61 "start XBoard protocole mode"
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" }
77 std::cout
<< "Usage: purplehaze [options]" << 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];
86 << std::setw(option_width
) << name
87 << std::setw(80 - 2 - option_width
) << usage
90 std::cout
<< std::endl
91 << "Report bugs to <bug-purplehaze@vinc.cc>" << std::endl
;
94 static std::string
prompt()
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
;
112 while ((opt
= getopt(argc
, argv
, "chl:m:t:")) != EOF
) {
124 mt_size
= std::stoi(optarg
) << 20;
127 tt_size
= std::stoi(optarg
) << 20;
131 std::cerr
<< "Try 'purplehaze -h' for more information."
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
);
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
);
151 } else if (cmd
== "help") {
152 std::cout
<< std::left
;
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
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
);
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") {
182 std::cout
<< std::endl
;
183 Game
game(tt_size
, mt_size
);
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(),
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);
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
;
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
;
219 // Get time per move (optional)
222 std::istringstream
iss(args
);
223 if (!(iss
>> time
)) {
224 args
.erase(0, 1); // Remove whitespace
225 std::cerr
<< "Invalid argument '" << args
<< "'"
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;
243 while (getline(epdfile
, line
)) {
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
) {
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,
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;
267 std::string best_move
;
269 if (best_move
== move
) {
277 std::cout
<< color_green
;
283 std::cout
<< color_red
;
288 std::cout
<< color_reset
;
290 std::cout
<< std::endl
;
293 std::cout
<< "Result: " << res
<< "/" << i
<< std::endl
;
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
;
305 const size_t npos
= std::string::npos
;
307 while (getline(epdfile
, line
)) {
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
) : "");
319 std::cout
<< "FEN '" << fen
<< "': " << std::flush
;
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
;
326 std::cout
<< (moves
== expected
? color_green
329 std::cout
<< (moves
== expected
? "." : "x")
330 << (option_color
? color_reset
: "")
334 std::cout
<< std::endl
;
338 std::cerr
<< "Invalid command '" << cmd
<< "'" << std::endl
;