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/>.
29 typedef std::vector
<std::string
> Tokens
;
31 static Tokens
tokenize(const std::string line
)
33 std::istringstream
iss(line
);
35 std::copy(std::istream_iterator
<std::string
>(iss
),
36 std::istream_iterator
<std::string
>(),
37 std::back_inserter
<Tokens
>(tokens
));
38 assert(tokens
.size() > 0);
42 static std::string
read_line(Log
&log
)
45 getline(std::cin
, line
);
46 if (line
.length() == 0) {
48 } else if (line
.substr(0, 4) != "ping") {
49 log
.to(Log::FILE) << Log::IN
<< line
<< std::endl
;
54 static bool is_move(const std::string move
)
56 return 4 <= move
.size() && move
.size() <= 5 &&
57 'a' <= move
[0] && move
[0] <= 'h' &&
58 '1' <= move
[1] && move
[1] <= '8' &&
59 'a' <= move
[2] && move
[2] <= 'h' &&
60 '1' <= move
[3] && move
[3] <= '8';
65 if (get_verbosity() > 1) {
66 log
.to(Log::FILE) << game
.board
<< std::endl
;
69 const std::string move
= search_move();
70 std::string output
= "";
73 if (game
.current_position().side() == WHITE
) {
74 output
+= "0-1 {Black mates}";
76 output
+= "1-0 {White mates}";
78 } else if (move
== "DRAW") {
79 output
= "result 1/2-1/2";
80 } else if (!force_mode
) {
83 const float elapsed
= game
.time
.elapsed() / 100.0f
;
84 const float allocated
= game
.time
.allocated() / 100.0f
;
85 log
.to(Log::FILE) << Log::DEBUG
86 << elapsed
<< " of " << allocated
87 << " second" << (allocated
!= 1.0f
? "s" : "")
88 << " used to play" << std::endl
;
90 if (get_verbosity() > 1) {
92 game
.print_tt_stats();
94 output
= "move " + move
;
96 log
.to(Log::BOTH
) << Log::OUT
<< output
<< std::endl
;
101 signal(SIGINT
, SIG_IGN
);
102 log
<< std::endl
; // Acknowledge Xboard mode
104 const std::time_t t
= std::time(NULL
);
105 std::string datetime
= std::ctime(&t
);
106 datetime
.erase(datetime
.end() - 1); // Remove trailing new line character
107 log
.to(Log::FILE) << Log::DEBUG
<< "[" << datetime
<< "] "
108 << "Purple Haze " << VERSION
<< std::endl
109 << Log::IN
<< "xboard" << std::endl
;
112 while ((cmd
= read_line(log
)) != "quit") {
113 const Tokens args
= tokenize(cmd
);
114 if (args
[0] == "protover") {
115 assert(args
.size() == 2);
116 const int version
= std::stoi(args
[1]);
118 for (const std::string (&feature
)[2] : XBOARD_FEATURES
) {
119 std::string title
= feature
[0];
120 std::string value
= feature
[1];
121 if (value
!= "0" && value
!= "1") {
122 // Non boolean values must be quoted
123 value
= '"' + value
+ '"';
125 std::string out
= "feature " + title
+ "=" + value
;
126 log
.to(Log::BOTH
) << Log::OUT
<< out
<< std::endl
;
129 } else if (args
[0] == "accepted" || args
[0] == "rejected") {
130 assert(args
.size() == 2);
131 const std::string feature
= args
[1];
133 // Given the current feature list, there is nothing to be done
134 // when a feature gets accepted or rejected.
135 } else if (args
[0] == "new") {
136 assert(args
.size() == 1);
138 set_board(DEFAULT_FEN
);
140 } else if (args
[0] == "setboard") {
142 set_board(cmd
.erase(0, std::string("setboard ").length()));
144 } else if (args
[0] == "go") {
145 assert(args
.size() == 1);
147 if (thinker
.joinable()) {
150 thinker
= std::thread(&Xboard::think
, this);
151 } else if (args
[0] == "?") {
152 assert(args
.size() == 1);
154 } else if (args
[0] == "force") {
155 assert(args
.size() == 1);
157 } else if (args
[0] == "ping") {
158 if (thinker
.joinable()) {
159 thinker
.join(); // Wait before replying to 'ping'
161 log
.to(Log::FILE) << Log::IN
<< cmd
<< std::endl
;
162 assert(args
.size() == 2);
163 const int n
= std::stoi(args
[1]);
164 log
.to(Log::BOTH
) << Log::OUT
<< "pong " << n
<< std::endl
;
165 } else if (args
[0] == "level") {
166 assert(args
.size() == 4);
167 int moves
= std::stoi(args
[1]); // Number of moves
169 // TODO "level 0 m 0" means play the entire game in 'm' minutes,
170 // but the current time management don't support it.
175 // Time interval in minutes or minutes:seconds
177 size_t sep
= args
[2].find(":");
178 if (sep
== std::string::npos
) {
179 const int minutes
= std::stoi(args
[2]);
182 const int minutes
= std::stoi(args
[2].substr(0, sep
));
183 const int seconds
= std::stoi(args
[2].substr(sep
+ 1));
184 time
= minutes
* 60 + seconds
;
188 const int control
= std::stoi(args
[3]);
191 set_time(moves
, time
);
192 } else { // Not in Xboard protocol
193 // TODO If not zero, control is a time increment,
194 // but currently this time is not directly used
195 set_time(moves
, time
);
197 } else if (args
[0] == "time") {
198 assert(args
.size() == 2);
199 const int time
= std::stoi(args
[1]);
201 } else if (args
[0] == "otim") {
202 assert(args
.size() == 2);
203 } else if (args
[0] == "sd") {
204 assert(args
.size() == 2);
205 const int d
= std::stoi(args
[1]);
207 } else if (args
[0] == "undo") {
208 assert(args
.size() == 1);
210 } else if (args
[0] == "remove") {
211 assert(args
.size() == 1);
214 } else if (args
[0] == "post") {
215 assert(args
.size() == 1);
216 set_output_thinking(true);
217 } else if (args
[0] == "nopost") {
218 assert(args
.size() == 1);
219 set_output_thinking(false);
220 } else if (args
[0] == "hard") {
221 assert(args
.size() == 1);
222 } else if (is_move(args
[0]) && !parse_move(args
[0]).is_null()) {
223 assert(args
.size() == 1);
224 std::string move
= args
[0];
225 log
.to(Log::FILE) << Log::DEBUG
226 << "move '" << move
<< "' successfully parsed"
229 if (!play_move(move
)) {
230 log
.to(Log::BOTH
) << Log::OUT
231 << "Illegal move: " << move
<< std::endl
;
235 if (thinker
.joinable()) {
238 thinker
= std::thread(&Xboard::think
, this);
240 } else if (args
[0] == "verbose") { // Debug mode
241 assert(args
.size() == 1);
244 log
.to(Log::FILE) << Log::DEBUG
245 << "unrecognized command '" << cmd
<< "'"
249 if (thinker
.joinable()) {