Commit | Line | Data |
---|---|---|
7eeb782e AT |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ |
2 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * | |
3 | * http://www.gnu.org/software/gnugo/ for more information. * | |
4 | * * | |
5 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * | |
6 | * 2008 and 2009 by the Free Software Foundation. * | |
7 | * * | |
8 | * This program is free software; you can redistribute it and/or * | |
9 | * modify it under the terms of the GNU General Public License as * | |
10 | * published by the Free Software Foundation - version 3 or * | |
11 | * (at your option) any later version. * | |
12 | * * | |
13 | * This program is distributed in the hope that it will be useful, * | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
16 | * GNU General Public License in file COPYING for more details. * | |
17 | * * | |
18 | * You should have received a copy of the GNU General Public * | |
19 | * License along with this program; if not, write to the Free * | |
20 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * | |
21 | * Boston, MA 02111, USA. * | |
22 | \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
23 | ||
24 | #include "gnugo.h" | |
25 | ||
7eeb782e AT |
26 | #include <assert.h> |
27 | #include <ctype.h> | |
7eeb782e | 28 | #include <math.h> |
c150f57c AT |
29 | #include <stdio.h> |
30 | #include <string.h> | |
7eeb782e | 31 | |
c150f57c AT |
32 | #include "gg_utils.h" |
33 | #include "gtp.h" | |
7eeb782e AT |
34 | #include "interface.h" |
35 | #include "liberty.h" | |
7eeb782e AT |
36 | |
37 | /* Internal state that's not part of the engine. */ | |
38 | static int report_uncertainty = 0; | |
39 | static int gtp_orientation = 0; | |
40 | ||
41 | static void gtp_print_code(int c); | |
c150f57c AT |
42 | static void gtp_print_vertices2(int n, int* moves); |
43 | static void rotate_on_input(int ai, int aj, int* bi, int* bj); | |
44 | static void rotate_on_output(int ai, int aj, int* bi, int* bj); | |
7eeb782e | 45 | |
c150f57c | 46 | #define DECLARE(func) static int func(char* s) |
7eeb782e AT |
47 | |
48 | DECLARE(gtp_aa_confirm_safety); | |
49 | DECLARE(gtp_accurate_approxlib); | |
50 | DECLARE(gtp_accuratelib); | |
51 | DECLARE(gtp_advance_random_seed); | |
52 | DECLARE(gtp_all_legal); | |
53 | DECLARE(gtp_all_move_values); | |
54 | DECLARE(gtp_analyze_eyegraph); | |
55 | DECLARE(gtp_analyze_semeai); | |
56 | DECLARE(gtp_analyze_semeai_after_move); | |
57 | DECLARE(gtp_attack); | |
58 | DECLARE(gtp_attack_either); | |
59 | DECLARE(gtp_block_off); | |
60 | DECLARE(gtp_break_in); | |
61 | DECLARE(gtp_captures); | |
62 | DECLARE(gtp_clear_board); | |
63 | DECLARE(gtp_clear_cache); | |
64 | DECLARE(gtp_combination_attack); | |
65 | DECLARE(gtp_combination_defend); | |
66 | DECLARE(gtp_connect); | |
67 | DECLARE(gtp_countlib); | |
68 | DECLARE(gtp_cputime); | |
69 | DECLARE(gtp_decrease_depths); | |
70 | DECLARE(gtp_defend); | |
71 | DECLARE(gtp_defend_both); | |
72 | DECLARE(gtp_disconnect); | |
73 | DECLARE(gtp_does_attack); | |
74 | DECLARE(gtp_does_defend); | |
75 | DECLARE(gtp_does_surround); | |
76 | DECLARE(gtp_dragon_data); | |
77 | DECLARE(gtp_dragon_status); | |
78 | DECLARE(gtp_dragon_stones); | |
79 | DECLARE(gtp_draw_search_area); | |
80 | DECLARE(gtp_dump_stack); | |
81 | DECLARE(gtp_echo); | |
82 | DECLARE(gtp_echo_err); | |
83 | DECLARE(gtp_estimate_score); | |
84 | DECLARE(gtp_eval_eye); | |
85 | DECLARE(gtp_experimental_score); | |
86 | DECLARE(gtp_eye_data); | |
87 | DECLARE(gtp_final_score); | |
88 | DECLARE(gtp_final_status); | |
89 | DECLARE(gtp_final_status_list); | |
90 | DECLARE(gtp_findlib); | |
91 | DECLARE(gtp_finish_sgftrace); | |
92 | DECLARE(gtp_fixed_handicap); | |
93 | DECLARE(gtp_followup_influence); | |
94 | DECLARE(gtp_genmove); | |
95 | DECLARE(gtp_genmove_black); | |
96 | DECLARE(gtp_genmove_white); | |
97 | DECLARE(gtp_get_connection_node_counter); | |
98 | DECLARE(gtp_get_handicap); | |
99 | DECLARE(gtp_get_komi); | |
100 | DECLARE(gtp_get_life_node_counter); | |
101 | DECLARE(gtp_get_owl_node_counter); | |
102 | DECLARE(gtp_get_random_seed); | |
103 | DECLARE(gtp_get_reading_node_counter); | |
104 | DECLARE(gtp_get_trymove_counter); | |
105 | DECLARE(gtp_gg_genmove); | |
106 | DECLARE(gtp_gg_undo); | |
107 | DECLARE(gtp_half_eye_data); | |
108 | DECLARE(gtp_increase_depths); | |
109 | DECLARE(gtp_initial_influence); | |
110 | DECLARE(gtp_invariant_hash); | |
111 | DECLARE(gtp_invariant_hash_for_moves); | |
112 | DECLARE(gtp_is_legal); | |
113 | DECLARE(gtp_is_surrounded); | |
114 | DECLARE(gtp_kgs_genmove_cleanup); | |
115 | DECLARE(gtp_known_command); | |
116 | DECLARE(gtp_ladder_attack); | |
117 | DECLARE(gtp_last_move); | |
118 | DECLARE(gtp_limit_search); | |
119 | DECLARE(gtp_list_commands); | |
120 | DECLARE(gtp_list_stones); | |
121 | DECLARE(gtp_loadsgf); | |
122 | DECLARE(gtp_move_influence); | |
123 | DECLARE(gtp_move_probabilities); | |
124 | DECLARE(gtp_move_reasons); | |
125 | DECLARE(gtp_move_uncertainty); | |
126 | DECLARE(gtp_move_history); | |
127 | DECLARE(gtp_name); | |
128 | DECLARE(gtp_owl_attack); | |
129 | DECLARE(gtp_owl_connection_defends); | |
130 | DECLARE(gtp_owl_defend); | |
131 | DECLARE(gtp_owl_does_attack); | |
132 | DECLARE(gtp_owl_does_defend); | |
133 | DECLARE(gtp_owl_substantial); | |
134 | DECLARE(gtp_owl_threaten_attack); | |
135 | DECLARE(gtp_owl_threaten_defense); | |
136 | DECLARE(gtp_place_free_handicap); | |
137 | DECLARE(gtp_play); | |
138 | DECLARE(gtp_playblack); | |
139 | DECLARE(gtp_playwhite); | |
140 | DECLARE(gtp_popgo); | |
141 | DECLARE(gtp_printsgf); | |
142 | DECLARE(gtp_program_version); | |
143 | DECLARE(gtp_protocol_version); | |
144 | DECLARE(gtp_query_boardsize); | |
145 | DECLARE(gtp_query_orientation); | |
146 | DECLARE(gtp_quit); | |
147 | DECLARE(gtp_reg_genmove); | |
148 | DECLARE(gtp_report_uncertainty); | |
149 | DECLARE(gtp_reset_connection_node_counter); | |
150 | DECLARE(gtp_reset_life_node_counter); | |
151 | DECLARE(gtp_reset_owl_node_counter); | |
152 | DECLARE(gtp_reset_reading_node_counter); | |
153 | DECLARE(gtp_reset_search_mask); | |
154 | DECLARE(gtp_reset_trymove_counter); | |
155 | DECLARE(gtp_restricted_genmove); | |
156 | DECLARE(gtp_same_dragon); | |
157 | DECLARE(gtp_set_boardsize); | |
158 | DECLARE(gtp_set_free_handicap); | |
159 | DECLARE(gtp_set_komi); | |
160 | DECLARE(gtp_set_level); | |
161 | DECLARE(gtp_set_orientation); | |
162 | DECLARE(gtp_set_random_seed); | |
163 | DECLARE(gtp_set_search_diamond); | |
164 | DECLARE(gtp_set_search_limit); | |
165 | DECLARE(gtp_showboard); | |
166 | DECLARE(gtp_start_sgftrace); | |
167 | DECLARE(gtp_surround_map); | |
168 | DECLARE(gtp_tactical_analyze_semeai); | |
169 | DECLARE(gtp_test_eyeshape); | |
170 | DECLARE(gtp_time_left); | |
171 | DECLARE(gtp_time_settings); | |
172 | DECLARE(gtp_top_moves); | |
173 | DECLARE(gtp_top_moves_black); | |
174 | DECLARE(gtp_top_moves_white); | |
175 | DECLARE(gtp_tryko); | |
176 | DECLARE(gtp_trymove); | |
177 | DECLARE(gtp_tune_move_ordering); | |
178 | DECLARE(gtp_unconditional_status); | |
179 | DECLARE(gtp_undo); | |
180 | DECLARE(gtp_what_color); | |
181 | DECLARE(gtp_worm_cutstone); | |
182 | DECLARE(gtp_worm_data); | |
183 | DECLARE(gtp_worm_stones); | |
184 | ||
185 | /* List of known commands. */ | |
186 | static struct gtp_command commands[] = { | |
c150f57c AT |
187 | { "aa_confirm_safety", gtp_aa_confirm_safety }, |
188 | { "accurate_approxlib", gtp_accurate_approxlib }, | |
189 | { "accuratelib", gtp_accuratelib }, | |
190 | { "advance_random_seed", gtp_advance_random_seed }, | |
191 | { "all_legal", gtp_all_legal }, | |
192 | { "all_move_values", gtp_all_move_values }, | |
193 | { "analyze_eyegraph", gtp_analyze_eyegraph }, | |
194 | { "analyze_semeai", gtp_analyze_semeai }, | |
195 | { "analyze_semeai_after_move", gtp_analyze_semeai_after_move }, | |
196 | { "attack", gtp_attack }, | |
197 | { "attack_either", gtp_attack_either }, | |
198 | { "black", gtp_playblack }, | |
199 | { "block_off", gtp_block_off }, | |
200 | { "boardsize", gtp_set_boardsize }, | |
201 | { "break_in", gtp_break_in }, | |
202 | { "captures", gtp_captures }, | |
203 | { "clear_board", gtp_clear_board }, | |
204 | { "clear_cache", gtp_clear_cache }, | |
205 | { "color", gtp_what_color }, | |
206 | { "combination_attack", gtp_combination_attack }, | |
207 | { "combination_defend", gtp_combination_defend }, | |
208 | { "connect", gtp_connect }, | |
209 | { "countlib", gtp_countlib }, | |
210 | { "cputime", gtp_cputime }, | |
211 | { "decrease_depths", gtp_decrease_depths }, | |
212 | { "defend", gtp_defend }, | |
213 | { "defend_both", gtp_defend_both }, | |
214 | { "disconnect", gtp_disconnect }, | |
215 | { "does_attack", gtp_does_attack }, | |
216 | { "does_defend", gtp_does_defend }, | |
217 | { "does_surround", gtp_does_surround }, | |
218 | { "dragon_data", gtp_dragon_data }, | |
219 | { "dragon_status", gtp_dragon_status }, | |
220 | { "dragon_stones", gtp_dragon_stones }, | |
221 | { "draw_search_area", gtp_draw_search_area }, | |
222 | { "dump_stack", gtp_dump_stack }, | |
223 | { "echo", gtp_echo }, | |
224 | { "echo_err", gtp_echo_err }, | |
225 | { "estimate_score", gtp_estimate_score }, | |
226 | { "eval_eye", gtp_eval_eye }, | |
227 | { "experimental_score", gtp_experimental_score }, | |
228 | { "eye_data", gtp_eye_data }, | |
229 | { "final_score", gtp_final_score }, | |
230 | { "final_status", gtp_final_status }, | |
231 | { "final_status_list", gtp_final_status_list }, | |
232 | { "findlib", gtp_findlib }, | |
233 | { "finish_sgftrace", gtp_finish_sgftrace }, | |
234 | { "fixed_handicap", gtp_fixed_handicap }, | |
235 | { "followup_influence", gtp_followup_influence }, | |
236 | { "genmove", gtp_genmove }, | |
237 | { "genmove_black", gtp_genmove_black }, | |
238 | { "genmove_white", gtp_genmove_white }, | |
239 | { "get_connection_node_counter", gtp_get_connection_node_counter }, | |
240 | { "get_handicap", gtp_get_handicap }, | |
241 | { "get_komi", gtp_get_komi }, | |
242 | { "get_life_node_counter", gtp_get_life_node_counter }, | |
243 | { "get_owl_node_counter", gtp_get_owl_node_counter }, | |
244 | { "get_random_seed", gtp_get_random_seed }, | |
245 | { "get_reading_node_counter", gtp_get_reading_node_counter }, | |
246 | { "get_trymove_counter", gtp_get_trymove_counter }, | |
247 | { "gg-undo", gtp_gg_undo }, | |
248 | { "gg_genmove", gtp_gg_genmove }, | |
249 | { "half_eye_data", gtp_half_eye_data }, | |
250 | { "help", gtp_list_commands }, | |
251 | { "increase_depths", gtp_increase_depths }, | |
252 | { "initial_influence", gtp_initial_influence }, | |
253 | { "invariant_hash_for_moves", gtp_invariant_hash_for_moves }, | |
254 | { "invariant_hash", gtp_invariant_hash }, | |
255 | { "is_legal", gtp_is_legal }, | |
256 | { "is_surrounded", gtp_is_surrounded }, | |
257 | { "kgs-genmove_cleanup", gtp_kgs_genmove_cleanup }, | |
258 | { "known_command", gtp_known_command }, | |
259 | { "komi", gtp_set_komi }, | |
260 | { "ladder_attack", gtp_ladder_attack }, | |
261 | { "last_move", gtp_last_move }, | |
262 | { "level", gtp_set_level }, | |
263 | { "limit_search", gtp_limit_search }, | |
264 | { "list_commands", gtp_list_commands }, | |
265 | { "list_stones", gtp_list_stones }, | |
266 | { "loadsgf", gtp_loadsgf }, | |
267 | { "move_influence", gtp_move_influence }, | |
268 | { "move_probabilities", gtp_move_probabilities }, | |
269 | { "move_reasons", gtp_move_reasons }, | |
270 | { "move_uncertainty", gtp_move_uncertainty }, | |
271 | { "move_history", gtp_move_history }, | |
272 | { "name", gtp_name }, | |
273 | { "new_score", gtp_estimate_score }, | |
274 | { "orientation", gtp_set_orientation }, | |
275 | { "owl_attack", gtp_owl_attack }, | |
276 | { "owl_connection_defends", gtp_owl_connection_defends }, | |
277 | { "owl_defend", gtp_owl_defend }, | |
278 | { "owl_does_attack", gtp_owl_does_attack }, | |
279 | { "owl_does_defend", gtp_owl_does_defend }, | |
280 | { "owl_substantial", gtp_owl_substantial }, | |
281 | { "owl_threaten_attack", gtp_owl_threaten_attack }, | |
282 | { "owl_threaten_defense", gtp_owl_threaten_defense }, | |
283 | { "place_free_handicap", gtp_place_free_handicap }, | |
284 | { "play", gtp_play }, | |
285 | { "popgo", gtp_popgo }, | |
286 | { "printsgf", gtp_printsgf }, | |
287 | { "protocol_version", gtp_protocol_version }, | |
288 | { "query_boardsize", gtp_query_boardsize }, | |
289 | { "query_orientation", gtp_query_orientation }, | |
290 | { "quit", gtp_quit }, | |
291 | { "reg_genmove", gtp_reg_genmove }, | |
292 | { "report_uncertainty", gtp_report_uncertainty }, | |
293 | { "reset_connection_node_counter", gtp_reset_connection_node_counter }, | |
294 | { "reset_life_node_counter", gtp_reset_life_node_counter }, | |
295 | { "reset_owl_node_counter", gtp_reset_owl_node_counter }, | |
296 | { "reset_reading_node_counter", gtp_reset_reading_node_counter }, | |
297 | { "reset_search_mask", gtp_reset_search_mask }, | |
298 | { "reset_trymove_counter", gtp_reset_trymove_counter }, | |
299 | { "restricted_genmove", gtp_restricted_genmove }, | |
300 | { "same_dragon", gtp_same_dragon }, | |
301 | { "set_free_handicap", gtp_set_free_handicap }, | |
302 | { "set_random_seed", gtp_set_random_seed }, | |
303 | { "set_search_diamond", gtp_set_search_diamond }, | |
304 | { "set_search_limit", gtp_set_search_limit }, | |
305 | { "showboard", gtp_showboard }, | |
306 | { "start_sgftrace", gtp_start_sgftrace }, | |
307 | { "surround_map", gtp_surround_map }, | |
308 | { "tactical_analyze_semeai", gtp_tactical_analyze_semeai }, | |
309 | { "test_eyeshape", gtp_test_eyeshape }, | |
310 | { "time_left", gtp_time_left }, | |
311 | { "time_settings", gtp_time_settings }, | |
312 | { "top_moves", gtp_top_moves }, | |
313 | { "top_moves_black", gtp_top_moves_black }, | |
314 | { "top_moves_white", gtp_top_moves_white }, | |
315 | { "tryko", gtp_tryko }, | |
316 | { "trymove", gtp_trymove }, | |
317 | { "tune_move_ordering", gtp_tune_move_ordering }, | |
318 | { "unconditional_status", gtp_unconditional_status }, | |
319 | { "undo", gtp_undo }, | |
320 | { "version", gtp_program_version }, | |
321 | { "white", gtp_playwhite }, | |
322 | { "worm_cutstone", gtp_worm_cutstone }, | |
323 | { "worm_data", gtp_worm_data }, | |
324 | { "worm_stones", gtp_worm_stones }, | |
325 | { NULL, NULL } | |
7eeb782e AT |
326 | }; |
327 | ||
7eeb782e | 328 | /* Start playing using the Go Text Protocol. */ |
c150f57c AT |
329 | void play_gtp(FILE* gtp_input, FILE* gtp_output, FILE* gtp_dump_commands, |
330 | int gtp_initial_orientation) | |
7eeb782e | 331 | { |
c150f57c | 332 | /* Make sure `gtp_output' is unbuffered. (Line buffering is also |
7eeb782e AT |
333 | * okay but not necessary. Block buffering breaks GTP mode.) |
334 | * | |
335 | * FIXME: Maybe should go to `gtp.c'? | |
336 | */ | |
c150f57c AT |
337 | setbuf(gtp_output, NULL); |
338 | ||
339 | /* Inform the GTP utility functions about the board size. */ | |
340 | gtp_internal_set_boardsize(board_size); | |
341 | gtp_orientation = gtp_initial_orientation; | |
342 | gtp_set_vertex_transform_hooks(rotate_on_input, rotate_on_output); | |
343 | ||
344 | /* Initialize time handling. */ | |
345 | init_timers(); | |
346 | ||
347 | /* Prepare pattern matcher and reading code. */ | |
348 | reset_engine(); | |
349 | clearstats(); | |
350 | gtp_main_loop(commands, gtp_input, gtp_output, gtp_dump_commands); | |
351 | if (showstatistics) | |
352 | showstats(); | |
7eeb782e AT |
353 | } |
354 | ||
7eeb782e AT |
355 | /**************************** |
356 | * Administrative commands. * | |
357 | ****************************/ | |
358 | ||
359 | /* Function: Quit | |
360 | * Arguments: none | |
361 | * Fails: never | |
362 | * Returns: nothing | |
363 | * | |
364 | * Status: GTP version 2 standard command. | |
365 | */ | |
366 | static int | |
c150f57c | 367 | gtp_quit(char* s) |
7eeb782e | 368 | { |
c150f57c AT |
369 | UNUSED(s); |
370 | gtp_success(""); | |
371 | return GTP_QUIT; | |
7eeb782e AT |
372 | } |
373 | ||
7eeb782e AT |
374 | /* Function: Report protocol version. |
375 | * Arguments: none | |
376 | * Fails: never | |
377 | * Returns: protocol version number | |
378 | * | |
379 | * Status: GTP version 2 standard command. | |
380 | */ | |
381 | static int | |
c150f57c | 382 | gtp_protocol_version(char* s) |
7eeb782e | 383 | { |
c150f57c AT |
384 | UNUSED(s); |
385 | return gtp_success("%d", gtp_version); | |
7eeb782e AT |
386 | } |
387 | ||
7eeb782e AT |
388 | /**************************** |
389 | * Program identity. * | |
390 | ****************************/ | |
391 | ||
392 | /* Function: Report the name of the program. | |
393 | * Arguments: none | |
394 | * Fails: never | |
395 | * Returns: program name | |
396 | * | |
397 | * Status: GTP version 2 standard command. | |
398 | */ | |
399 | static int | |
c150f57c | 400 | gtp_name(char* s) |
7eeb782e | 401 | { |
c150f57c AT |
402 | UNUSED(s); |
403 | return gtp_success("GNU Go"); | |
7eeb782e AT |
404 | } |
405 | ||
7eeb782e AT |
406 | /* Function: Report the version number of the program. |
407 | * Arguments: none | |
408 | * Fails: never | |
409 | * Returns: version number | |
410 | * | |
411 | * Status: GTP version 2 standard command. | |
412 | */ | |
413 | static int | |
c150f57c | 414 | gtp_program_version(char* s) |
7eeb782e | 415 | { |
c150f57c AT |
416 | UNUSED(s); |
417 | return gtp_success(VERSION); | |
7eeb782e AT |
418 | } |
419 | ||
7eeb782e AT |
420 | /*************************** |
421 | * Setting the board size. * | |
422 | ***************************/ | |
423 | ||
424 | /* Function: Set the board size to NxN and clear the board. | |
425 | * Arguments: integer | |
426 | * Fails: board size outside engine's limits | |
427 | * Returns: nothing | |
428 | * | |
429 | * Status: GTP version 2 standard command. | |
430 | */ | |
431 | static int | |
c150f57c | 432 | gtp_set_boardsize(char* s) |
7eeb782e | 433 | { |
c150f57c | 434 | int boardsize; |
7eeb782e | 435 | |
c150f57c AT |
436 | if (sscanf(s, "%d", &boardsize) < 1) |
437 | return gtp_failure("boardsize not an integer"); | |
438 | ||
439 | if (!check_boardsize(boardsize, NULL)) { | |
440 | if (gtp_version == 1) | |
441 | return gtp_failure("unacceptable boardsize"); | |
442 | else | |
443 | return gtp_failure("unacceptable size"); | |
444 | } | |
7eeb782e | 445 | |
c150f57c | 446 | /* If this is called with a non-empty board, we assume that a new |
7eeb782e AT |
447 | * game will be started, for which we want a new random seed. |
448 | */ | |
c150f57c AT |
449 | if (stones_on_board(BLACK | WHITE) > 0) |
450 | update_random_seed(); | |
7eeb782e | 451 | |
c150f57c AT |
452 | board_size = boardsize; |
453 | clear_board(); | |
454 | gtp_internal_set_boardsize(boardsize); | |
455 | reset_engine(); | |
456 | return gtp_success(""); | |
7eeb782e AT |
457 | } |
458 | ||
459 | /* Function: Find the current boardsize | |
460 | * Arguments: none | |
461 | * Fails: never | |
462 | * Returns: board_size | |
463 | */ | |
464 | static int | |
c150f57c | 465 | gtp_query_boardsize(char* s) |
7eeb782e | 466 | { |
c150f57c | 467 | UNUSED(s); |
7eeb782e | 468 | |
c150f57c | 469 | return gtp_success("%d", board_size); |
7eeb782e AT |
470 | } |
471 | ||
472 | /*********************** | |
473 | * Clearing the board. * | |
474 | ***********************/ | |
475 | ||
476 | /* Function: Clear the board. | |
477 | * Arguments: none | |
478 | * Fails: never | |
479 | * Returns: nothing | |
480 | * | |
481 | * Status: GTP version 2 standard command. | |
482 | */ | |
483 | static int | |
c150f57c | 484 | gtp_clear_board(char* s) |
7eeb782e | 485 | { |
c150f57c | 486 | UNUSED(s); |
7eeb782e | 487 | |
c150f57c | 488 | /* If this is called with a non-empty board, we assume that a new |
7eeb782e AT |
489 | * game will be started, for which we want a new random seed. |
490 | */ | |
c150f57c AT |
491 | if (stones_on_board(BLACK | WHITE) > 0) |
492 | update_random_seed(); | |
493 | ||
494 | clear_board(); | |
495 | init_timers(); | |
7eeb782e | 496 | |
c150f57c | 497 | return gtp_success(""); |
7eeb782e AT |
498 | } |
499 | ||
500 | /**************************** | |
501 | * Setting the orientation. * | |
502 | ****************************/ | |
503 | ||
504 | /* Function: Set the orienation to N and clear the board | |
505 | * Arguments: integer | |
506 | * Fails: illegal orientation | |
507 | * Returns: nothing | |
508 | */ | |
509 | static int | |
c150f57c | 510 | gtp_set_orientation(char* s) |
7eeb782e | 511 | { |
c150f57c AT |
512 | int orientation; |
513 | if (sscanf(s, "%d", &orientation) < 1) | |
514 | return gtp_failure("orientation not an integer"); | |
515 | ||
516 | if (orientation < 0 || orientation > 7) | |
517 | return gtp_failure("unacceptable orientation"); | |
7eeb782e | 518 | |
c150f57c AT |
519 | clear_board(); |
520 | gtp_orientation = orientation; | |
521 | gtp_set_vertex_transform_hooks(rotate_on_input, rotate_on_output); | |
522 | return gtp_success(""); | |
7eeb782e AT |
523 | } |
524 | ||
525 | /* Function: Find the current orientation | |
526 | * Arguments: none | |
527 | * Fails: never | |
528 | * Returns: orientation | |
529 | */ | |
530 | static int | |
c150f57c | 531 | gtp_query_orientation(char* s) |
7eeb782e | 532 | { |
c150f57c | 533 | UNUSED(s); |
7eeb782e | 534 | |
c150f57c | 535 | return gtp_success("%d", gtp_orientation); |
7eeb782e AT |
536 | } |
537 | ||
538 | /*************************** | |
539 | * Setting komi. * | |
540 | ***************************/ | |
541 | ||
542 | /* Function: Set the komi. | |
543 | * Arguments: float | |
544 | * Fails: incorrect argument | |
545 | * Returns: nothing | |
546 | * | |
547 | * Status: GTP version 2 standard command. | |
548 | */ | |
549 | static int | |
c150f57c | 550 | gtp_set_komi(char* s) |
7eeb782e | 551 | { |
c150f57c AT |
552 | if (sscanf(s, "%f", &komi) < 1) |
553 | return gtp_failure("komi not a float"); | |
7eeb782e | 554 | |
c150f57c AT |
555 | return gtp_success(""); |
556 | } | |
7eeb782e AT |
557 | |
558 | /*************************** | |
559 | * Getting komi * | |
560 | ***************************/ | |
561 | ||
562 | /* Function: Get the komi | |
563 | * Arguments: none | |
564 | * Fails: never | |
565 | * Returns: Komi | |
566 | */ | |
567 | static int | |
c150f57c | 568 | gtp_get_komi(char* s) |
7eeb782e | 569 | { |
c150f57c AT |
570 | UNUSED(s); |
571 | return gtp_success("%4.1f", komi); | |
7eeb782e AT |
572 | } |
573 | ||
7eeb782e AT |
574 | /****************** |
575 | * Playing moves. * | |
576 | ******************/ | |
577 | ||
578 | /* Function: Play a black stone at the given vertex. | |
579 | * Arguments: vertex | |
580 | * Fails: invalid vertex, illegal move | |
581 | * Returns: nothing | |
582 | * | |
583 | * Status: Obsolete GTP version 1 command. | |
584 | */ | |
585 | static int | |
c150f57c | 586 | gtp_playblack(char* s) |
7eeb782e | 587 | { |
c150f57c AT |
588 | int i, j; |
589 | char* c; | |
7eeb782e | 590 | |
c150f57c AT |
591 | for (c = s; *c; c++) |
592 | *c = tolower((int)*c); | |
7eeb782e | 593 | |
c150f57c AT |
594 | if (strncmp(s, "pass", 4) == 0) { |
595 | i = -1; | |
596 | j = -1; | |
597 | } else if (!gtp_decode_coord(s, &i, &j)) | |
598 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 599 | |
c150f57c AT |
600 | if (!is_allowed_move(POS(i, j), BLACK)) |
601 | return gtp_failure("illegal move"); | |
7eeb782e | 602 | |
c150f57c AT |
603 | gnugo_play_move(POS(i, j), BLACK); |
604 | return gtp_success(""); | |
7eeb782e AT |
605 | } |
606 | ||
7eeb782e AT |
607 | /* Function: Play a white stone at the given vertex. |
608 | * Arguments: vertex | |
609 | * Fails: invalid vertex, illegal move | |
610 | * Returns: nothing | |
611 | * | |
612 | * Status: Obsolete GTP version 1 command. | |
613 | */ | |
614 | static int | |
c150f57c | 615 | gtp_playwhite(char* s) |
7eeb782e | 616 | { |
c150f57c AT |
617 | int i, j; |
618 | char* c; | |
7eeb782e | 619 | |
c150f57c AT |
620 | for (c = s; *c; c++) |
621 | *c = tolower((int)*c); | |
7eeb782e | 622 | |
c150f57c AT |
623 | if (strncmp(s, "pass", 4) == 0) { |
624 | i = -1; | |
625 | j = -1; | |
626 | } else if (!gtp_decode_coord(s, &i, &j)) | |
627 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 628 | |
c150f57c AT |
629 | if (!is_allowed_move(POS(i, j), WHITE)) |
630 | return gtp_failure("illegal move"); | |
7eeb782e | 631 | |
c150f57c AT |
632 | gnugo_play_move(POS(i, j), WHITE); |
633 | return gtp_success(""); | |
634 | } | |
7eeb782e AT |
635 | |
636 | /* Function: Play a stone of the given color at the given vertex. | |
637 | * Arguments: color, vertex | |
638 | * Fails: invalid vertex, illegal move | |
639 | * Returns: nothing | |
640 | * | |
641 | * Status: GTP version 2 standard command. | |
642 | */ | |
643 | static int | |
c150f57c | 644 | gtp_play(char* s) |
7eeb782e | 645 | { |
c150f57c AT |
646 | int i, j; |
647 | int color; | |
7eeb782e | 648 | |
c150f57c AT |
649 | if (!gtp_decode_move(s, &color, &i, &j)) |
650 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 651 | |
c150f57c AT |
652 | if (!is_allowed_move(POS(i, j), color)) |
653 | return gtp_failure("illegal move"); | |
7eeb782e | 654 | |
c150f57c AT |
655 | gnugo_play_move(POS(i, j), color); |
656 | return gtp_success(""); | |
7eeb782e AT |
657 | } |
658 | ||
7eeb782e AT |
659 | /* Function: Set up fixed placement handicap stones. |
660 | * Arguments: number of handicap stones | |
661 | * Fails: invalid number of stones for the current boardsize | |
662 | * Returns: list of vertices with handicap stones | |
663 | * | |
664 | * Status: GTP version 2 standard command. | |
665 | */ | |
666 | static int | |
c150f57c | 667 | gtp_fixed_handicap(char* s) |
7eeb782e | 668 | { |
c150f57c AT |
669 | int m, n; |
670 | int first = 1; | |
671 | int this_handicap; | |
7eeb782e | 672 | |
c150f57c AT |
673 | if (gtp_version == 1) |
674 | clear_board(); | |
675 | else if (stones_on_board(BLACK | WHITE) > 0) | |
676 | return gtp_failure("board not empty"); | |
7eeb782e | 677 | |
c150f57c AT |
678 | if (sscanf(s, "%d", &this_handicap) < 1) |
679 | return gtp_failure("handicap not an integer"); | |
7eeb782e | 680 | |
c150f57c AT |
681 | if (this_handicap < 2 && (gtp_version > 1 || this_handicap != 0)) |
682 | return gtp_failure("invalid handicap"); | |
7eeb782e | 683 | |
c150f57c AT |
684 | if (place_fixed_handicap(this_handicap) != this_handicap) { |
685 | clear_board(); | |
686 | return gtp_failure("invalid handicap"); | |
687 | } | |
7eeb782e | 688 | |
c150f57c | 689 | handicap = this_handicap; |
7eeb782e | 690 | |
c150f57c | 691 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 692 | |
c150f57c AT |
693 | for (m = 0; m < board_size; m++) |
694 | for (n = 0; n < board_size; n++) | |
695 | if (BOARD(m, n) != EMPTY) { | |
696 | if (!first) | |
697 | gtp_printf(" "); | |
698 | else | |
699 | first = 0; | |
700 | gtp_mprintf("%m", m, n); | |
701 | } | |
702 | ||
703 | return gtp_finish_response(); | |
704 | } | |
7eeb782e AT |
705 | |
706 | /* Function: Choose free placement handicap stones and put them on the board. | |
707 | * Arguments: number of handicap stones | |
708 | * Fails: invalid number of stones | |
709 | * Returns: list of vertices with handicap stones | |
710 | * | |
711 | * Status: GTP version 2 standard command. | |
712 | */ | |
713 | static int | |
c150f57c | 714 | gtp_place_free_handicap(char* s) |
7eeb782e | 715 | { |
c150f57c AT |
716 | int m, n; |
717 | int first = 1; | |
718 | int this_handicap; | |
719 | if (sscanf(s, "%d", &this_handicap) < 1) | |
720 | return gtp_failure("handicap not an integer"); | |
7eeb782e | 721 | |
c150f57c AT |
722 | if (stones_on_board(BLACK | WHITE) > 0) |
723 | return gtp_failure("board not empty"); | |
7eeb782e | 724 | |
c150f57c AT |
725 | if (this_handicap < 2) |
726 | return gtp_failure("invalid handicap"); | |
7eeb782e | 727 | |
c150f57c | 728 | handicap = place_free_handicap(this_handicap); |
7eeb782e | 729 | |
c150f57c | 730 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 731 | |
c150f57c AT |
732 | for (m = 0; m < board_size; m++) |
733 | for (n = 0; n < board_size; n++) | |
734 | if (BOARD(m, n) != EMPTY) { | |
735 | if (!first) | |
736 | gtp_printf(" "); | |
737 | else | |
738 | first = 0; | |
739 | gtp_mprintf("%m", m, n); | |
740 | } | |
741 | ||
742 | return gtp_finish_response(); | |
743 | } | |
7eeb782e AT |
744 | |
745 | /* Function: Put free placement handicap stones on the board. | |
746 | * Arguments: list of vertices with handicap stones | |
747 | * Fails: board not empty, bad list of vertices | |
748 | * Returns: nothing | |
749 | * | |
750 | * Status: GTP version 2 standard command. | |
751 | */ | |
752 | static int | |
c150f57c AT |
753 | gtp_set_free_handicap(char* s) |
754 | { | |
755 | int n; | |
756 | int i, j; | |
757 | int k; | |
758 | ||
759 | if (stones_on_board(BLACK | WHITE) > 0) | |
760 | return gtp_failure("board not empty"); | |
761 | ||
762 | for (k = 0; k < MAX_BOARD * MAX_BOARD; k++) { | |
763 | n = gtp_decode_coord(s, &i, &j); | |
764 | if (n > 0) { | |
765 | if (board[POS(i, j)] != EMPTY) { | |
766 | clear_board(); | |
767 | return gtp_failure("repeated vertex"); | |
768 | } | |
769 | add_stone(POS(i, j), BLACK); | |
770 | s += n; | |
771 | } else if (sscanf(s, "%*s") != EOF) | |
772 | return gtp_failure("invalid coordinate"); | |
773 | else | |
774 | break; | |
775 | } | |
7eeb782e | 776 | |
c150f57c AT |
777 | if (k < 2) { |
778 | clear_board(); | |
779 | return gtp_failure("invalid handicap"); | |
7eeb782e | 780 | } |
7eeb782e | 781 | |
c150f57c | 782 | handicap = k; |
7eeb782e | 783 | |
c150f57c | 784 | return gtp_success(""); |
7eeb782e AT |
785 | } |
786 | ||
7eeb782e AT |
787 | /* Function: Get the handicap |
788 | * Arguments: none | |
789 | * Fails: never | |
790 | * Returns: handicap | |
791 | */ | |
792 | static int | |
c150f57c | 793 | gtp_get_handicap(char* s) |
7eeb782e | 794 | { |
c150f57c AT |
795 | UNUSED(s); |
796 | return gtp_success("%d", handicap); | |
7eeb782e AT |
797 | } |
798 | ||
7eeb782e AT |
799 | /* Function: Load an sgf file, possibly up to a move number or the first |
800 | * occurence of a move. | |
801 | * Arguments: filename + move number, vertex, or nothing | |
802 | * Fails: missing filename or failure to open or parse file | |
803 | * Returns: color to play | |
804 | * | |
805 | * Status: GTP version 2 standard command. | |
806 | */ | |
807 | static int | |
c150f57c | 808 | gtp_loadsgf(char* s) |
7eeb782e | 809 | { |
c150f57c AT |
810 | char filename[GTP_BUFSIZE]; |
811 | char untilstring[GTP_BUFSIZE]; | |
812 | SGFTree sgftree; | |
813 | Gameinfo gameinfo; | |
814 | int nread; | |
815 | int color_to_move; | |
7eeb782e | 816 | |
c150f57c AT |
817 | nread = sscanf(s, "%s %s", filename, untilstring); |
818 | if (nread < 1) | |
819 | return gtp_failure("missing filename"); | |
7eeb782e | 820 | |
c150f57c AT |
821 | sgftree_clear(&sgftree); |
822 | if (!sgftree_readfile(&sgftree, filename)) | |
823 | return gtp_failure("cannot open or parse '%s'", filename); | |
7eeb782e | 824 | |
c150f57c AT |
825 | if (nread == 1) |
826 | color_to_move = gameinfo_play_sgftree_rot(&gameinfo, &sgftree, NULL, | |
827 | gtp_orientation); | |
828 | else | |
829 | color_to_move = gameinfo_play_sgftree_rot(&gameinfo, &sgftree, untilstring, | |
830 | gtp_orientation); | |
7eeb782e | 831 | |
c150f57c AT |
832 | if (color_to_move == EMPTY) |
833 | return gtp_failure("cannot load '%s'", filename); | |
7eeb782e | 834 | |
c150f57c AT |
835 | gtp_internal_set_boardsize(board_size); |
836 | reset_engine(); | |
837 | init_timers(); | |
7eeb782e | 838 | |
c150f57c AT |
839 | sgfFreeNode(sgftree.root); |
840 | ||
841 | gtp_start_response(GTP_SUCCESS); | |
842 | gtp_mprintf("%C", color_to_move); | |
843 | return gtp_finish_response(); | |
844 | } | |
7eeb782e AT |
845 | |
846 | /***************** | |
847 | * Board status. * | |
848 | *****************/ | |
849 | ||
850 | /* Function: Return the color at a vertex. | |
851 | * Arguments: vertex | |
852 | * Fails: invalid vertex | |
853 | * Returns: "black", "white", or "empty" | |
854 | */ | |
855 | static int | |
c150f57c | 856 | gtp_what_color(char* s) |
7eeb782e | 857 | { |
c150f57c AT |
858 | int i, j; |
859 | if (!gtp_decode_coord(s, &i, &j)) | |
860 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 861 | |
c150f57c AT |
862 | return gtp_success(color_to_string(BOARD(i, j))); |
863 | } | |
7eeb782e AT |
864 | |
865 | /* Function: List vertices with either black or white stones. | |
866 | * Arguments: color | |
867 | * Fails: invalid color | |
868 | * Returns: list of vertices | |
869 | */ | |
870 | static int | |
c150f57c | 871 | gtp_list_stones(char* s) |
7eeb782e | 872 | { |
c150f57c AT |
873 | int i, j; |
874 | int color = EMPTY; | |
875 | int vertexi[MAX_BOARD * MAX_BOARD]; | |
876 | int vertexj[MAX_BOARD * MAX_BOARD]; | |
877 | int vertices = 0; | |
7eeb782e | 878 | |
c150f57c AT |
879 | if (!gtp_decode_color(s, &color)) |
880 | return gtp_failure("invalid color"); | |
7eeb782e | 881 | |
c150f57c AT |
882 | for (i = 0; i < board_size; i++) |
883 | for (j = 0; j < board_size; j++) | |
884 | if (BOARD(i, j) == color) { | |
885 | vertexi[vertices] = i; | |
886 | vertexj[vertices++] = j; | |
887 | } | |
7eeb782e | 888 | |
c150f57c AT |
889 | gtp_start_response(GTP_SUCCESS); |
890 | gtp_print_vertices(vertices, vertexi, vertexj); | |
891 | return gtp_finish_response(); | |
892 | } | |
7eeb782e AT |
893 | |
894 | /* Function: Count number of liberties for the string at a vertex. | |
895 | * Arguments: vertex | |
896 | * Fails: invalid vertex, empty vertex | |
897 | * Returns: Number of liberties. | |
898 | */ | |
899 | static int | |
c150f57c | 900 | gtp_countlib(char* s) |
7eeb782e | 901 | { |
c150f57c AT |
902 | int i, j; |
903 | if (!gtp_decode_coord(s, &i, &j)) | |
904 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 905 | |
c150f57c AT |
906 | if (BOARD(i, j) == EMPTY) |
907 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 908 | |
c150f57c | 909 | return gtp_success("%d", countlib(POS(i, j))); |
7eeb782e AT |
910 | } |
911 | ||
7eeb782e AT |
912 | /* Function: Return the positions of the liberties for the string at a vertex. |
913 | * Arguments: vertex | |
914 | * Fails: invalid vertex, empty vertex | |
915 | * Returns: Sorted space separated list of vertices. | |
916 | */ | |
917 | static int | |
c150f57c | 918 | gtp_findlib(char* s) |
7eeb782e | 919 | { |
c150f57c AT |
920 | int i, j; |
921 | int libs[MAXLIBS]; | |
922 | int liberties; | |
7eeb782e | 923 | |
c150f57c AT |
924 | if (!gtp_decode_coord(s, &i, &j)) |
925 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 926 | |
c150f57c AT |
927 | if (BOARD(i, j) == EMPTY) |
928 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 929 | |
c150f57c AT |
930 | liberties = findlib(POS(i, j), MAXLIBS, libs); |
931 | gtp_start_response(GTP_SUCCESS); | |
932 | gtp_print_vertices2(liberties, libs); | |
933 | return gtp_finish_response(); | |
934 | } | |
7eeb782e AT |
935 | |
936 | /* Function: Determine which liberties a stone of given color | |
937 | * will get if played at given vertex. | |
938 | * Arguments: move (color + vertex) | |
939 | * Fails: invalid color, invalid vertex, occupied vertex | |
940 | * Returns: Sorted space separated list of liberties | |
941 | */ | |
942 | static int | |
c150f57c | 943 | gtp_accuratelib(char* s) |
7eeb782e | 944 | { |
c150f57c AT |
945 | int i, j; |
946 | int color; | |
947 | int libs[MAXLIBS]; | |
948 | int liberties; | |
7eeb782e | 949 | |
c150f57c AT |
950 | if (!gtp_decode_move(s, &color, &i, &j)) |
951 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 952 | |
c150f57c AT |
953 | if (BOARD(i, j) != EMPTY) |
954 | return gtp_failure("vertex must be empty"); | |
7eeb782e | 955 | |
c150f57c | 956 | liberties = accuratelib(POS(i, j), color, MAXLIBS, libs); |
7eeb782e | 957 | |
c150f57c AT |
958 | gtp_start_response(GTP_SUCCESS); |
959 | gtp_print_vertices2(liberties, libs); | |
960 | return gtp_finish_response(); | |
7eeb782e AT |
961 | } |
962 | ||
7eeb782e AT |
963 | /* Function: Determine which liberties a stone of given color |
964 | * will get if played at given vertex. | |
965 | * Arguments: move (color + vertex) | |
966 | * Fails: invalid color, invalid vertex, occupied vertex | |
967 | * Returns: Sorted space separated list of liberties | |
968 | * | |
969 | * Supposedly identical in behavior to the above function and | |
970 | * can be retired when this is confirmed. | |
971 | */ | |
972 | static int | |
c150f57c | 973 | gtp_accurate_approxlib(char* s) |
7eeb782e | 974 | { |
c150f57c AT |
975 | int i, j; |
976 | int color; | |
977 | int libs[MAXLIBS]; | |
978 | int liberties; | |
7eeb782e | 979 | |
c150f57c AT |
980 | if (!gtp_decode_move(s, &color, &i, &j)) |
981 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 982 | |
c150f57c AT |
983 | if (BOARD(i, j) != EMPTY) |
984 | return gtp_failure("vertex must be empty"); | |
7eeb782e | 985 | |
c150f57c | 986 | liberties = accuratelib(POS(i, j), color, MAXLIBS, libs); |
7eeb782e | 987 | |
c150f57c AT |
988 | gtp_start_response(GTP_SUCCESS); |
989 | gtp_print_vertices2(liberties, libs); | |
990 | return gtp_finish_response(); | |
7eeb782e AT |
991 | } |
992 | ||
7eeb782e AT |
993 | /* Function: Tell whether a move is legal. |
994 | * Arguments: move | |
995 | * Fails: invalid move | |
996 | * Returns: 1 if the move is legal, 0 if it is not. | |
997 | */ | |
998 | static int | |
c150f57c | 999 | gtp_is_legal(char* s) |
7eeb782e | 1000 | { |
c150f57c AT |
1001 | int i, j; |
1002 | int color; | |
7eeb782e | 1003 | |
c150f57c AT |
1004 | if (!gtp_decode_move(s, &color, &i, &j)) |
1005 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 1006 | |
c150f57c AT |
1007 | return gtp_success("%d", is_allowed_move(POS(i, j), color)); |
1008 | } | |
7eeb782e AT |
1009 | |
1010 | /* Function: List all legal moves for either color. | |
1011 | * Arguments: color | |
1012 | * Fails: invalid color | |
1013 | * Returns: Sorted space separated list of vertices. | |
1014 | */ | |
1015 | static int | |
c150f57c | 1016 | gtp_all_legal(char* s) |
7eeb782e | 1017 | { |
c150f57c AT |
1018 | int i, j; |
1019 | int color; | |
1020 | int movei[MAX_BOARD * MAX_BOARD]; | |
1021 | int movej[MAX_BOARD * MAX_BOARD]; | |
1022 | int moves = 0; | |
7eeb782e | 1023 | |
c150f57c AT |
1024 | if (!gtp_decode_color(s, &color)) |
1025 | return gtp_failure("invalid color"); | |
7eeb782e | 1026 | |
c150f57c AT |
1027 | for (i = 0; i < board_size; i++) |
1028 | for (j = 0; j < board_size; j++) | |
1029 | if (BOARD(i, j) == EMPTY && is_allowed_move(POS(i, j), color)) { | |
1030 | movei[moves] = i; | |
1031 | movej[moves++] = j; | |
1032 | } | |
7eeb782e | 1033 | |
c150f57c AT |
1034 | gtp_start_response(GTP_SUCCESS); |
1035 | gtp_print_vertices(moves, movei, movej); | |
1036 | return gtp_finish_response(); | |
1037 | } | |
7eeb782e AT |
1038 | |
1039 | /* Function: List the number of captures taken by either color. | |
1040 | * Arguments: color | |
1041 | * Fails: invalid color | |
1042 | * Returns: Number of captures. | |
1043 | */ | |
1044 | static int | |
c150f57c | 1045 | gtp_captures(char* s) |
7eeb782e | 1046 | { |
c150f57c | 1047 | int color; |
7eeb782e | 1048 | |
c150f57c AT |
1049 | if (!gtp_decode_color(s, &color)) |
1050 | return gtp_failure("invalid color"); | |
7eeb782e | 1051 | |
c150f57c AT |
1052 | if (color == BLACK) |
1053 | return gtp_success("%d", white_captured); | |
1054 | else | |
1055 | return gtp_success("%d", black_captured); | |
1056 | } | |
7eeb782e AT |
1057 | |
1058 | /* Function: Return the last move. | |
1059 | * Arguments: none | |
1060 | * Fails: no previous move known | |
1061 | * Returns: Color and vertex of last move. | |
1062 | */ | |
1063 | static int | |
c150f57c | 1064 | gtp_last_move(char* s) |
7eeb782e | 1065 | { |
c150f57c AT |
1066 | int pos; |
1067 | int color; | |
1068 | UNUSED(s); | |
1069 | ||
1070 | if (move_history_pointer <= 0) | |
1071 | return gtp_failure("no previous move known"); | |
1072 | ||
1073 | pos = move_history_pos[move_history_pointer - 1]; | |
1074 | color = move_history_color[move_history_pointer - 1]; | |
1075 | ||
1076 | gtp_start_response(GTP_SUCCESS); | |
1077 | gtp_mprintf("%C %m", color, I(pos), J(pos)); | |
1078 | return gtp_finish_response(); | |
7eeb782e AT |
1079 | } |
1080 | ||
1081 | /* Function: Print the move history in reverse order | |
1082 | * Arguments: none | |
1083 | * Fails: never | |
1084 | * Returns: List of moves played in reverse order in format: | |
1085 | * color move (one move per line) | |
1086 | */ | |
1087 | static int | |
c150f57c | 1088 | gtp_move_history(char* s) |
7eeb782e | 1089 | { |
c150f57c AT |
1090 | int k, pos, color; |
1091 | UNUSED(s); | |
1092 | ||
1093 | gtp_start_response(GTP_SUCCESS); | |
1094 | if (move_history_pointer > 0) | |
1095 | for (k = move_history_pointer - 1; k >= 0; k--) { | |
1096 | color = move_history_color[k]; | |
1097 | pos = move_history_pos[k]; | |
1098 | gtp_mprintf("%C %m\n", color, I(pos), J(pos)); | |
1099 | } | |
1100 | else | |
1101 | gtp_printf("\n"); | |
7eeb782e | 1102 | gtp_printf("\n"); |
c150f57c | 1103 | return GTP_OK; |
7eeb782e AT |
1104 | } |
1105 | ||
7eeb782e AT |
1106 | /* Function: Return the rotation/reflection invariant board hash. |
1107 | * Arguments: none | |
1108 | * Fails: never | |
1109 | * Returns: Invariant hash for the board as a hexadecimal number. | |
1110 | */ | |
1111 | static int | |
c150f57c | 1112 | gtp_invariant_hash(char* s) |
7eeb782e | 1113 | { |
c150f57c AT |
1114 | Hash_data hash; |
1115 | UNUSED(s); | |
1116 | hashdata_calc_orientation_invariant(&hash, board, board_ko_pos); | |
1117 | return gtp_success("%s", hashdata_to_string(&hash)); | |
7eeb782e AT |
1118 | } |
1119 | ||
7eeb782e AT |
1120 | /* Function: Return the rotation/reflection invariant board hash |
1121 | * obtained by playing all the possible moves for the | |
1122 | * given color. | |
1123 | * Arguments: color | |
1124 | * Fails: invalid color | |
1125 | * Returns: List of moves + invariant hash as a hexadecimal number, | |
1126 | * one pair of move + hash per line. | |
1127 | */ | |
1128 | static int | |
c150f57c | 1129 | gtp_invariant_hash_for_moves(char* s) |
7eeb782e | 1130 | { |
c150f57c AT |
1131 | Hash_data hash; |
1132 | int color; | |
1133 | int pos; | |
1134 | int move_found = 0; | |
1135 | ||
1136 | if (!gtp_decode_color(s, &color)) | |
1137 | return gtp_failure("invalid color"); | |
7eeb782e | 1138 | |
c150f57c | 1139 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 1140 | |
c150f57c AT |
1141 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
1142 | if (board[pos] == EMPTY | |
1143 | && trymove(pos, color, "gtp_invariant_hash_for_moves", NO_MOVE)) { | |
1144 | hashdata_calc_orientation_invariant(&hash, board, board_ko_pos); | |
1145 | gtp_mprintf("%m %s\n", I(pos), J(pos), hashdata_to_string(&hash)); | |
1146 | popgo(); | |
1147 | move_found = 1; | |
1148 | } | |
7eeb782e | 1149 | } |
7eeb782e | 1150 | |
c150f57c AT |
1151 | if (!move_found) |
1152 | gtp_printf("\n"); | |
1153 | ||
7eeb782e | 1154 | gtp_printf("\n"); |
c150f57c | 1155 | return GTP_OK; |
7eeb782e AT |
1156 | } |
1157 | ||
7eeb782e AT |
1158 | /********************** |
1159 | * Retractable moves. * | |
1160 | **********************/ | |
1161 | ||
1162 | /* Function: Play a stone of the given color at the given vertex. | |
1163 | * Arguments: move (color + vertex) | |
1164 | * Fails: invalid color, invalid vertex, illegal move | |
1165 | * Returns: nothing | |
1166 | */ | |
1167 | static int | |
c150f57c | 1168 | gtp_trymove(char* s) |
7eeb782e | 1169 | { |
c150f57c AT |
1170 | int i, j; |
1171 | int color; | |
1172 | if (!gtp_decode_move(s, &color, &i, &j)) | |
1173 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 1174 | |
c150f57c AT |
1175 | if (!trymove(POS(i, j), color, "gtp_trymove", NO_MOVE)) |
1176 | return gtp_failure("illegal move"); | |
7eeb782e | 1177 | |
c150f57c | 1178 | return gtp_success(""); |
7eeb782e AT |
1179 | } |
1180 | ||
1181 | /* Function: Play a stone of the given color at the given vertex, | |
1182 | * allowing illegal ko capture. | |
1183 | * Arguments: move (color + vertex) | |
1184 | * Fails: invalid color, invalid vertex, illegal move | |
1185 | * Returns: nothing | |
1186 | */ | |
1187 | static int | |
c150f57c | 1188 | gtp_tryko(char* s) |
7eeb782e | 1189 | { |
c150f57c AT |
1190 | int i, j; |
1191 | int color; | |
1192 | if (!gtp_decode_move(s, &color, &i, &j) || POS(i, j) == PASS_MOVE) | |
1193 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 1194 | |
c150f57c AT |
1195 | if (!tryko(POS(i, j), color, "gtp_tryko")) |
1196 | return gtp_failure("illegal move"); | |
7eeb782e | 1197 | |
c150f57c | 1198 | return gtp_success(""); |
7eeb782e AT |
1199 | } |
1200 | ||
7eeb782e AT |
1201 | /* Function: Undo a trymove or tryko. |
1202 | * Arguments: none | |
1203 | * Fails: stack empty | |
1204 | * Returns: nothing | |
1205 | */ | |
1206 | static int | |
c150f57c | 1207 | gtp_popgo(char* s) |
7eeb782e | 1208 | { |
c150f57c | 1209 | UNUSED(s); |
7eeb782e | 1210 | |
c150f57c AT |
1211 | if (stackp == 0) |
1212 | return gtp_failure("Stack empty."); | |
7eeb782e | 1213 | |
c150f57c AT |
1214 | popgo(); |
1215 | return gtp_success(""); | |
7eeb782e AT |
1216 | } |
1217 | ||
1218 | /********************* | |
1219 | * Caching * | |
1220 | *********************/ | |
1221 | ||
1222 | /* Function: clear the caches. | |
1223 | * Arguments: none. | |
1224 | * Fails: never. | |
1225 | * Returns: nothing. | |
1226 | */ | |
1227 | ||
1228 | static int | |
c150f57c | 1229 | gtp_clear_cache(char* s) |
7eeb782e | 1230 | { |
c150f57c AT |
1231 | UNUSED(s); |
1232 | clear_persistent_caches(); | |
1233 | reading_cache_clear(); | |
1234 | return gtp_success(""); | |
7eeb782e AT |
1235 | } |
1236 | ||
1237 | /********************* | |
1238 | * Tactical reading. * | |
1239 | *********************/ | |
1240 | ||
1241 | /* Function: Try to attack a string. | |
1242 | * Arguments: vertex | |
1243 | * Fails: invalid vertex, empty vertex | |
1244 | * Returns: attack code followed by attack point if attack code nonzero. | |
1245 | */ | |
1246 | static int | |
c150f57c | 1247 | gtp_attack(char* s) |
7eeb782e | 1248 | { |
c150f57c AT |
1249 | int i, j; |
1250 | int apos; | |
1251 | int attack_code; | |
7eeb782e | 1252 | |
c150f57c AT |
1253 | if (!gtp_decode_coord(s, &i, &j)) |
1254 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1255 | |
c150f57c AT |
1256 | if (BOARD(i, j) == EMPTY) |
1257 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1258 | |
c150f57c AT |
1259 | attack_code = attack(POS(i, j), &apos); |
1260 | gtp_start_response(GTP_SUCCESS); | |
1261 | gtp_print_code(attack_code); | |
1262 | if (attack_code > 0) { | |
1263 | gtp_printf(" "); | |
1264 | gtp_print_vertex(I(apos), J(apos)); | |
1265 | } | |
1266 | return gtp_finish_response(); | |
1267 | } | |
7eeb782e AT |
1268 | |
1269 | /* Function: Try to attack either of two strings | |
1270 | * Arguments: two vertices | |
1271 | * Fails: invalid vertex, empty vertex | |
1272 | * Returns: attack code against the strings. Guarantees there | |
1273 | * exists a move which will attack one of the two | |
1274 | * with attack_code, but does not return the move. | |
1275 | */ | |
1276 | static int | |
c150f57c | 1277 | gtp_attack_either(char* s) |
7eeb782e | 1278 | { |
c150f57c AT |
1279 | int ai, aj; |
1280 | int bi, bj; | |
1281 | int n; | |
1282 | int acode; | |
7eeb782e | 1283 | |
c150f57c AT |
1284 | n = gtp_decode_coord(s, &ai, &aj); |
1285 | if (n == 0) | |
1286 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1287 | |
c150f57c AT |
1288 | if (BOARD(ai, aj) == EMPTY) |
1289 | return gtp_failure("string vertex must be empty"); | |
7eeb782e | 1290 | |
c150f57c AT |
1291 | n = gtp_decode_coord(s + n, &bi, &bj); |
1292 | if (n == 0) | |
1293 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1294 | |
c150f57c AT |
1295 | if (BOARD(bi, bj) == EMPTY) |
1296 | return gtp_failure("string vertex must not be empty"); | |
7eeb782e | 1297 | |
c150f57c | 1298 | acode = attack_either(POS(ai, aj), POS(bi, bj)); |
7eeb782e | 1299 | |
c150f57c AT |
1300 | gtp_start_response(GTP_SUCCESS); |
1301 | gtp_print_code(acode); | |
1302 | return gtp_finish_response(); | |
7eeb782e AT |
1303 | } |
1304 | ||
7eeb782e AT |
1305 | /* Function: Try to defend a string. |
1306 | * Arguments: vertex | |
1307 | * Fails: invalid vertex, empty vertex | |
1308 | * Returns: defense code followed by defense point if defense code nonzero. | |
1309 | */ | |
1310 | static int | |
c150f57c | 1311 | gtp_defend(char* s) |
7eeb782e | 1312 | { |
c150f57c AT |
1313 | int i, j; |
1314 | int dpos; | |
1315 | int defend_code; | |
7eeb782e | 1316 | |
c150f57c AT |
1317 | if (!gtp_decode_coord(s, &i, &j)) |
1318 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1319 | |
c150f57c AT |
1320 | if (BOARD(i, j) == EMPTY) |
1321 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1322 | |
c150f57c AT |
1323 | defend_code = find_defense(POS(i, j), &dpos); |
1324 | gtp_start_response(GTP_SUCCESS); | |
1325 | gtp_print_code(defend_code); | |
1326 | if (defend_code > 0) { | |
1327 | gtp_printf(" "); | |
1328 | gtp_print_vertex(I(dpos), J(dpos)); | |
1329 | } | |
1330 | return gtp_finish_response(); | |
1331 | } | |
7eeb782e AT |
1332 | |
1333 | /* Function: Examine whether a specific move attacks a string tactically. | |
1334 | * Arguments: vertex (move), vertex (dragon) | |
1335 | * Fails: invalid vertex, empty vertex | |
1336 | * Returns: attack code | |
1337 | */ | |
1338 | static int | |
c150f57c | 1339 | gtp_does_attack(char* s) |
7eeb782e | 1340 | { |
c150f57c AT |
1341 | int i, j; |
1342 | int ti, tj; | |
1343 | int attack_code; | |
1344 | int n; | |
7eeb782e | 1345 | |
c150f57c AT |
1346 | n = gtp_decode_coord(s, &ti, &tj); |
1347 | if (n == 0) | |
1348 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1349 | |
c150f57c AT |
1350 | if (BOARD(ti, tj) != EMPTY) |
1351 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 1352 | |
c150f57c AT |
1353 | n = gtp_decode_coord(s + n, &i, &j); |
1354 | if (n == 0) | |
1355 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1356 | |
c150f57c AT |
1357 | if (BOARD(i, j) == EMPTY) |
1358 | return gtp_failure("string vertex must not be empty"); | |
7eeb782e | 1359 | |
c150f57c AT |
1360 | /* to get the variations into the sgf file, clear the reading cache */ |
1361 | if (sgf_dumptree) | |
1362 | reading_cache_clear(); | |
7eeb782e | 1363 | |
c150f57c AT |
1364 | attack_code = does_attack(POS(ti, tj), POS(i, j)); |
1365 | gtp_start_response(GTP_SUCCESS); | |
1366 | gtp_print_code(attack_code); | |
1367 | return gtp_finish_response(); | |
1368 | } | |
7eeb782e AT |
1369 | |
1370 | /* Function: Examine whether a specific move defends a string tactically. | |
1371 | * Arguments: vertex (move), vertex (dragon) | |
1372 | * Fails: invalid vertex, empty vertex | |
1373 | * Returns: attack code | |
1374 | */ | |
1375 | static int | |
c150f57c | 1376 | gtp_does_defend(char* s) |
7eeb782e | 1377 | { |
c150f57c AT |
1378 | int i, j; |
1379 | int ti, tj; | |
1380 | int defense_code; | |
1381 | int n; | |
7eeb782e | 1382 | |
c150f57c AT |
1383 | n = gtp_decode_coord(s, &ti, &tj); |
1384 | if (n == 0) | |
1385 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1386 | |
c150f57c AT |
1387 | if (BOARD(ti, tj) != EMPTY) |
1388 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 1389 | |
c150f57c AT |
1390 | n = gtp_decode_coord(s + n, &i, &j); |
1391 | if (n == 0) | |
1392 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1393 | |
c150f57c AT |
1394 | if (BOARD(i, j) == EMPTY) |
1395 | return gtp_failure("string vertex must not be empty"); | |
7eeb782e | 1396 | |
c150f57c AT |
1397 | /* to get the variations into the sgf file, clear the reading cache */ |
1398 | if (sgf_dumptree) | |
1399 | reading_cache_clear(); | |
7eeb782e | 1400 | |
c150f57c AT |
1401 | defense_code = does_defend(POS(ti, tj), POS(i, j)); |
1402 | gtp_start_response(GTP_SUCCESS); | |
1403 | gtp_print_code(defense_code); | |
1404 | return gtp_finish_response(); | |
1405 | } | |
7eeb782e AT |
1406 | |
1407 | /* Function: Try to attack a string strictly in a ladder. | |
1408 | * Arguments: vertex | |
1409 | * Fails: invalid vertex, empty vertex | |
1410 | * Returns: attack code followed by attack point if attack code nonzero. | |
1411 | */ | |
1412 | static int | |
c150f57c | 1413 | gtp_ladder_attack(char* s) |
7eeb782e | 1414 | { |
c150f57c AT |
1415 | int i, j; |
1416 | int apos; | |
1417 | int attack_code; | |
7eeb782e | 1418 | |
c150f57c AT |
1419 | if (!gtp_decode_coord(s, &i, &j)) |
1420 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1421 | |
c150f57c AT |
1422 | if (BOARD(i, j) == EMPTY) |
1423 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1424 | |
c150f57c AT |
1425 | if (countlib(POS(i, j)) != 2) |
1426 | return gtp_failure("string must have exactly 2 liberties"); | |
7eeb782e | 1427 | |
c150f57c AT |
1428 | attack_code = simple_ladder(POS(i, j), &apos); |
1429 | gtp_start_response(GTP_SUCCESS); | |
1430 | gtp_print_code(attack_code); | |
1431 | if (attack_code > 0) { | |
1432 | gtp_printf(" "); | |
1433 | gtp_print_vertex(I(apos), J(apos)); | |
1434 | } | |
1435 | return gtp_finish_response(); | |
1436 | } | |
7eeb782e AT |
1437 | |
1438 | /* Function: Increase depth values by one. | |
1439 | * Arguments: none | |
1440 | * Fails: never | |
1441 | * Returns: nothing | |
1442 | */ | |
1443 | static int | |
c150f57c | 1444 | gtp_increase_depths(char* s) |
7eeb782e | 1445 | { |
c150f57c AT |
1446 | UNUSED(s); |
1447 | increase_depth_values(); | |
1448 | return gtp_success(""); | |
1449 | } | |
7eeb782e AT |
1450 | |
1451 | /* Function: Decrease depth values by one. | |
1452 | * Arguments: none | |
1453 | * Fails: never | |
1454 | * Returns: nothing | |
1455 | */ | |
1456 | static int | |
c150f57c | 1457 | gtp_decrease_depths(char* s) |
7eeb782e | 1458 | { |
c150f57c AT |
1459 | UNUSED(s); |
1460 | decrease_depth_values(); | |
1461 | return gtp_success(""); | |
1462 | } | |
7eeb782e AT |
1463 | |
1464 | /****************** | |
1465 | * owl reading. * | |
1466 | ******************/ | |
1467 | ||
1468 | /* Function: Try to attack a dragon. | |
1469 | * Arguments: vertex | |
1470 | * Fails: invalid vertex, empty vertex | |
1471 | * Returns: attack code followed by attack point if attack code nonzero. | |
1472 | */ | |
1473 | static int | |
c150f57c | 1474 | gtp_owl_attack(char* s) |
7eeb782e | 1475 | { |
c150f57c AT |
1476 | int i, j; |
1477 | int attack_point; | |
1478 | int attack_code; | |
1479 | int result_certain; | |
1480 | int kworm; | |
7eeb782e | 1481 | |
c150f57c AT |
1482 | if (!gtp_decode_coord(s, &i, &j)) |
1483 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1484 | |
c150f57c AT |
1485 | if (BOARD(i, j) == EMPTY) |
1486 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1487 | |
c150f57c | 1488 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1489 | |
c150f57c AT |
1490 | /* to get the variations into the sgf file, clear the reading cache */ |
1491 | if (sgf_dumptree) | |
1492 | reading_cache_clear(); | |
1493 | ||
1494 | attack_code = owl_attack(POS(i, j), &attack_point, &result_certain, &kworm); | |
1495 | gtp_start_response(GTP_SUCCESS); | |
1496 | gtp_print_code(attack_code); | |
1497 | if (attack_code > 0) { | |
1498 | gtp_printf(" "); | |
1499 | gtp_print_vertex(I(attack_point), J(attack_point)); | |
1500 | } | |
1501 | if (!result_certain && report_uncertainty) | |
1502 | gtp_printf(" uncertain"); | |
1503 | return gtp_finish_response(); | |
1504 | } | |
7eeb782e AT |
1505 | |
1506 | /* Function: Try to defend a dragon. | |
1507 | * Arguments: vertex | |
1508 | * Fails: invalid vertex, empty vertex | |
1509 | * Returns: defense code followed by defense point if defense code nonzero. | |
1510 | */ | |
1511 | static int | |
c150f57c | 1512 | gtp_owl_defend(char* s) |
7eeb782e | 1513 | { |
c150f57c AT |
1514 | int i, j; |
1515 | int defense_point; | |
1516 | int defend_code; | |
1517 | int result_certain; | |
1518 | int kworm; | |
7eeb782e | 1519 | |
c150f57c AT |
1520 | if (!gtp_decode_coord(s, &i, &j)) |
1521 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1522 | |
c150f57c AT |
1523 | if (BOARD(i, j) == EMPTY) |
1524 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1525 | |
c150f57c AT |
1526 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
1527 | ||
1528 | /* to get the variations into the sgf file, clear the reading cache */ | |
1529 | if (sgf_dumptree) | |
1530 | reading_cache_clear(); | |
1531 | ||
1532 | defend_code = owl_defend(POS(i, j), &defense_point, &result_certain, &kworm); | |
1533 | gtp_start_response(GTP_SUCCESS); | |
1534 | gtp_print_code(defend_code); | |
1535 | if (defend_code > 0) { | |
1536 | gtp_printf(" "); | |
1537 | gtp_print_vertex(I(defense_point), J(defense_point)); | |
1538 | } | |
1539 | if (!result_certain && report_uncertainty) | |
1540 | gtp_printf(" uncertain"); | |
1541 | return gtp_finish_response(); | |
1542 | } | |
7eeb782e AT |
1543 | |
1544 | /* Function: Try to attack a dragon in 2 moves. | |
1545 | * Arguments: vertex | |
1546 | * Fails: invalid vertex, empty vertex | |
1547 | * Returns: attack code followed by the two attack points if | |
1548 | * attack code nonzero. | |
1549 | */ | |
1550 | static int | |
c150f57c | 1551 | gtp_owl_threaten_attack(char* s) |
7eeb782e | 1552 | { |
c150f57c AT |
1553 | int i, j; |
1554 | int attack_point1; | |
1555 | int attack_point2; | |
1556 | int attack_code; | |
7eeb782e | 1557 | |
c150f57c AT |
1558 | if (!gtp_decode_coord(s, &i, &j)) |
1559 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1560 | |
c150f57c AT |
1561 | if (BOARD(i, j) == EMPTY) |
1562 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1563 | |
c150f57c | 1564 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1565 | |
c150f57c AT |
1566 | /* to get the variations into the sgf file, clear the reading cache */ |
1567 | if (sgf_dumptree) | |
1568 | reading_cache_clear(); | |
1569 | ||
1570 | attack_code = owl_threaten_attack(POS(i, j), &attack_point1, &attack_point2); | |
1571 | gtp_start_response(GTP_SUCCESS); | |
1572 | gtp_print_code(attack_code); | |
1573 | if (attack_code > 0) { | |
1574 | gtp_printf(" "); | |
1575 | gtp_print_vertex(I(attack_point1), J(attack_point1)); | |
1576 | gtp_printf(" "); | |
1577 | gtp_print_vertex(I(attack_point2), J(attack_point2)); | |
1578 | } | |
1579 | return gtp_finish_response(); | |
1580 | } | |
7eeb782e AT |
1581 | |
1582 | /* Function: Try to defend a dragon with 2 moves. | |
1583 | * Arguments: vertex | |
1584 | * Fails: invalid vertex, empty vertex | |
1585 | * Returns: defense code followed by the 2 defense points if | |
1586 | * defense code nonzero. | |
1587 | */ | |
1588 | static int | |
c150f57c | 1589 | gtp_owl_threaten_defense(char* s) |
7eeb782e | 1590 | { |
c150f57c AT |
1591 | int i, j; |
1592 | int defense_point1; | |
1593 | int defense_point2; | |
1594 | int defend_code; | |
7eeb782e | 1595 | |
c150f57c AT |
1596 | if (!gtp_decode_coord(s, &i, &j)) |
1597 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1598 | |
c150f57c AT |
1599 | if (BOARD(i, j) == EMPTY) |
1600 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1601 | |
c150f57c | 1602 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1603 | |
c150f57c AT |
1604 | /* to get the variations into the sgf file, clear the reading cache */ |
1605 | if (sgf_dumptree) | |
1606 | reading_cache_clear(); | |
1607 | ||
1608 | defend_code = owl_threaten_defense(POS(i, j), &defense_point1, | |
1609 | &defense_point2); | |
1610 | gtp_start_response(GTP_SUCCESS); | |
1611 | gtp_print_code(defend_code); | |
1612 | if (defend_code > 0) { | |
1613 | gtp_printf(" "); | |
1614 | gtp_print_vertex(I(defense_point1), J(defense_point1)); | |
1615 | gtp_printf(" "); | |
1616 | gtp_print_vertex(I(defense_point2), J(defense_point2)); | |
1617 | } | |
1618 | return gtp_finish_response(); | |
1619 | } | |
7eeb782e AT |
1620 | |
1621 | /* Function: Examine whether a specific move attacks a dragon. | |
1622 | * Arguments: vertex (move), vertex (dragon) | |
1623 | * Fails: invalid vertex, empty vertex | |
1624 | * Returns: attack code | |
1625 | */ | |
1626 | static int | |
c150f57c | 1627 | gtp_owl_does_attack(char* s) |
7eeb782e | 1628 | { |
c150f57c AT |
1629 | int i, j; |
1630 | int ti, tj; | |
1631 | int attack_code; | |
1632 | int kworm; | |
1633 | int n; | |
7eeb782e | 1634 | |
c150f57c AT |
1635 | n = gtp_decode_coord(s, &ti, &tj); |
1636 | if (n == 0) | |
1637 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1638 | |
c150f57c AT |
1639 | if (BOARD(ti, tj) != EMPTY) |
1640 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 1641 | |
c150f57c AT |
1642 | n = gtp_decode_coord(s + n, &i, &j); |
1643 | if (n == 0) | |
1644 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1645 | |
c150f57c AT |
1646 | if (BOARD(i, j) == EMPTY) |
1647 | return gtp_failure("dragon vertex must not be empty"); | |
7eeb782e | 1648 | |
c150f57c | 1649 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1650 | |
c150f57c AT |
1651 | /* to get the variations into the sgf file, clear the reading cache */ |
1652 | if (sgf_dumptree) | |
1653 | reading_cache_clear(); | |
1654 | ||
1655 | attack_code = owl_does_attack(POS(ti, tj), POS(i, j), &kworm); | |
1656 | gtp_start_response(GTP_SUCCESS); | |
1657 | gtp_print_code(attack_code); | |
1658 | return gtp_finish_response(); | |
1659 | } | |
7eeb782e AT |
1660 | |
1661 | /* Function: Examine whether a specific move defends a dragon. | |
1662 | * Arguments: vertex (move), vertex (dragon) | |
1663 | * Fails: invalid vertex, empty vertex | |
1664 | * Returns: defense code | |
1665 | */ | |
1666 | static int | |
c150f57c | 1667 | gtp_owl_does_defend(char* s) |
7eeb782e | 1668 | { |
c150f57c AT |
1669 | int i, j; |
1670 | int ti, tj; | |
1671 | int defense_code; | |
1672 | int kworm; | |
1673 | int n; | |
7eeb782e | 1674 | |
c150f57c AT |
1675 | n = gtp_decode_coord(s, &ti, &tj); |
1676 | if (n == 0) | |
1677 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1678 | |
c150f57c AT |
1679 | if (BOARD(ti, tj) != EMPTY) |
1680 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 1681 | |
c150f57c AT |
1682 | n = gtp_decode_coord(s + n, &i, &j); |
1683 | if (n == 0) | |
1684 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1685 | |
c150f57c AT |
1686 | if (BOARD(i, j) == EMPTY) |
1687 | return gtp_failure("dragon vertex must not be empty"); | |
7eeb782e | 1688 | |
c150f57c | 1689 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1690 | |
c150f57c AT |
1691 | /* to get the variations into the sgf file, clear the reading cache */ |
1692 | if (sgf_dumptree) | |
1693 | reading_cache_clear(); | |
1694 | ||
1695 | defense_code = owl_does_defend(POS(ti, tj), POS(i, j), &kworm); | |
1696 | gtp_start_response(GTP_SUCCESS); | |
1697 | gtp_print_code(defense_code); | |
1698 | return gtp_finish_response(); | |
1699 | } | |
7eeb782e AT |
1700 | |
1701 | /* Function: Examine whether a connection defends involved dragons. | |
1702 | * Arguments: vertex (move), vertex (dragon1), vertex (dragon2) | |
1703 | * Fails: invalid vertex, empty vertex | |
1704 | * Returns: defense code | |
1705 | */ | |
1706 | static int | |
c150f57c | 1707 | gtp_owl_connection_defends(char* s) |
7eeb782e | 1708 | { |
c150f57c AT |
1709 | int ai, aj; |
1710 | int bi, bj; | |
1711 | int ti, tj; | |
1712 | int defense_code; | |
1713 | int n; | |
7eeb782e | 1714 | |
c150f57c AT |
1715 | n = gtp_decode_coord(s, &ti, &tj); |
1716 | if (n == 0) | |
1717 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1718 | |
c150f57c AT |
1719 | if (BOARD(ti, tj) != EMPTY) |
1720 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 1721 | |
c150f57c AT |
1722 | s += n; |
1723 | n = gtp_decode_coord(s, &ai, &aj); | |
1724 | if (n == 0) | |
1725 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1726 | |
c150f57c AT |
1727 | s += n; |
1728 | n = gtp_decode_coord(s, &bi, &bj); | |
1729 | if (n == 0) | |
1730 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1731 | |
c150f57c AT |
1732 | if (BOARD(ai, aj) == EMPTY || BOARD(bi, bj) == EMPTY) |
1733 | return gtp_failure("dragon vertex must not be empty"); | |
7eeb782e | 1734 | |
c150f57c AT |
1735 | if (BOARD(ai, aj) != BOARD(bi, bj)) |
1736 | return gtp_failure("dragon vertices must have the same color"); | |
7eeb782e | 1737 | |
c150f57c | 1738 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 1739 | |
c150f57c AT |
1740 | /* to get the variations into the sgf file, clear the reading cache */ |
1741 | if (sgf_dumptree) | |
1742 | reading_cache_clear(); | |
1743 | ||
1744 | defense_code = owl_connection_defends(POS(ti, tj), POS(ai, aj), POS(bi, bj)); | |
1745 | gtp_start_response(GTP_SUCCESS); | |
1746 | gtp_print_code(defense_code); | |
1747 | return gtp_finish_response(); | |
1748 | } | |
7eeb782e AT |
1749 | |
1750 | /* Function: Try to defend both of two strings | |
1751 | * Arguments: two vertices | |
1752 | * Fails: invalid vertex, empty vertex | |
1753 | * Returns: defend code for the strings. Guarantees there | |
1754 | * exists a move which will defend both of the two | |
1755 | * with defend_code, but does not return the move. | |
1756 | */ | |
1757 | static int | |
c150f57c | 1758 | gtp_defend_both(char* s) |
7eeb782e | 1759 | { |
c150f57c AT |
1760 | int ai, aj; |
1761 | int bi, bj; | |
1762 | int n; | |
1763 | int dcode; | |
7eeb782e | 1764 | |
c150f57c AT |
1765 | n = gtp_decode_coord(s, &ai, &aj); |
1766 | if (n == 0) | |
1767 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1768 | |
c150f57c AT |
1769 | if (BOARD(ai, aj) == EMPTY) |
1770 | return gtp_failure("string vertex must be empty"); | |
7eeb782e | 1771 | |
c150f57c AT |
1772 | n = gtp_decode_coord(s + n, &bi, &bj); |
1773 | if (n == 0) | |
1774 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1775 | |
c150f57c AT |
1776 | if (BOARD(bi, bj) == EMPTY) |
1777 | return gtp_failure("string vertex must not be empty"); | |
7eeb782e | 1778 | |
c150f57c | 1779 | dcode = defend_both(POS(ai, aj), POS(bi, bj)); |
7eeb782e | 1780 | |
c150f57c AT |
1781 | gtp_start_response(GTP_SUCCESS); |
1782 | gtp_print_code(dcode); | |
1783 | return gtp_finish_response(); | |
7eeb782e AT |
1784 | } |
1785 | ||
7eeb782e AT |
1786 | /* Function: Determine whether capturing a string gives a living dragon |
1787 | * Arguments: vertex | |
1788 | * Fails: invalid vertex, empty vertex | |
1789 | * Returns: 1 if dragon can live, 0 otherwise | |
1790 | */ | |
1791 | static int | |
c150f57c | 1792 | gtp_owl_substantial(char* s) |
7eeb782e | 1793 | { |
c150f57c AT |
1794 | int i, j; |
1795 | int result; | |
7eeb782e | 1796 | |
c150f57c AT |
1797 | if (!gtp_decode_coord(s, &i, &j)) |
1798 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1799 | |
c150f57c AT |
1800 | if (BOARD(i, j) == EMPTY) |
1801 | return gtp_failure("vertex must not be empty"); | |
1802 | ||
1803 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); | |
7eeb782e | 1804 | |
c150f57c AT |
1805 | /* to get the variations into the sgf file, clear the reading cache */ |
1806 | if (sgf_dumptree) | |
1807 | reading_cache_clear(); | |
7eeb782e | 1808 | |
c150f57c AT |
1809 | result = owl_substantial(POS(i, j)); |
1810 | return gtp_success("%d", result); | |
1811 | } | |
7eeb782e AT |
1812 | |
1813 | /* Function: Analyze a semeai | |
1814 | * Arguments: dragona, dragonb | |
1815 | * Fails: invalid vertices, empty vertices | |
1816 | * Returns: semeai defense result, semeai attack result, semeai move | |
1817 | */ | |
1818 | static int | |
c150f57c | 1819 | gtp_analyze_semeai(char* s) |
7eeb782e | 1820 | { |
c150f57c AT |
1821 | int i, j; |
1822 | int k; | |
1823 | int dragona, dragonb; | |
1824 | int resulta, resultb, move, result_certain; | |
7eeb782e | 1825 | |
c150f57c | 1826 | k = gtp_decode_coord(s, &i, &j); |
7eeb782e | 1827 | |
c150f57c AT |
1828 | if (k == 0) |
1829 | return gtp_failure("invalid coordinate"); | |
1830 | dragona = POS(i, j); | |
1831 | if (BOARD(i, j) == EMPTY) | |
1832 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1833 | |
c150f57c AT |
1834 | if (!gtp_decode_coord(s + k, &i, &j)) |
1835 | return gtp_failure("invalid coordinate"); | |
1836 | dragonb = POS(i, j); | |
1837 | if (BOARD(i, j) == EMPTY) | |
1838 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1839 | |
c150f57c AT |
1840 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
1841 | /* to get the variations into the sgf file, clear the reading cache */ | |
1842 | if (sgf_dumptree) | |
1843 | reading_cache_clear(); | |
7eeb782e | 1844 | |
c150f57c AT |
1845 | owl_analyze_semeai(dragona, dragonb, &resulta, &resultb, &move, 1, |
1846 | &result_certain); | |
1847 | gtp_start_response(GTP_SUCCESS); | |
1848 | gtp_print_code(resulta); | |
1849 | gtp_printf(" "); | |
1850 | gtp_print_code(resultb); | |
1851 | gtp_mprintf(" %m", I(move), J(move)); | |
1852 | if (!result_certain && report_uncertainty) | |
1853 | gtp_printf(" uncertain"); | |
7eeb782e | 1854 | |
c150f57c AT |
1855 | return gtp_finish_response(); |
1856 | } | |
7eeb782e AT |
1857 | |
1858 | /* Function: Analyze a semeai after a move have been made. | |
1859 | * Arguments: color, vertex, dragona, dragonb | |
1860 | * Fails: invalid vertices | |
1861 | * Returns: semeai defense result, semeai attack result, semeai move | |
1862 | */ | |
1863 | static int | |
c150f57c AT |
1864 | gtp_analyze_semeai_after_move(char* s) |
1865 | { | |
1866 | int i, j; | |
1867 | int color; | |
1868 | int move; | |
1869 | int k; | |
1870 | int dragona, dragonb; | |
1871 | int resulta, resultb, semeai_move, result_certain; | |
1872 | ||
1873 | k = gtp_decode_move(s, &color, &i, &j); | |
1874 | move = POS(i, j); | |
1875 | if (k == 0 || move == NO_MOVE) | |
1876 | return gtp_failure("invalid color or coordinate"); | |
1877 | if (board[move] != EMPTY) | |
1878 | return gtp_failure("move vertex is not empty"); | |
1879 | s += k; | |
1880 | ||
1881 | k = gtp_decode_coord(s, &i, &j); | |
1882 | if (k == 0) | |
1883 | return gtp_failure("invalid coordinate"); | |
1884 | dragona = POS(i, j); | |
1885 | if (board[dragona] == EMPTY) | |
1886 | return gtp_failure("dragon vertex must not be empty"); | |
1887 | s += k; | |
1888 | ||
1889 | if (!gtp_decode_coord(s, &i, &j)) | |
1890 | return gtp_failure("invalid coordinate"); | |
1891 | dragonb = POS(i, j); | |
1892 | if (board[dragonb] == EMPTY) | |
1893 | return gtp_failure("dragon vertex must not be empty"); | |
1894 | ||
1895 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); | |
1896 | /* to get the variations into the sgf file, clear the reading cache */ | |
1897 | if (sgf_dumptree) | |
1898 | reading_cache_clear(); | |
1899 | ||
1900 | owl_analyze_semeai_after_move(move, color, dragona, dragonb, | |
1901 | &resulta, &resultb, &semeai_move, 1, | |
1902 | &result_certain, 0); | |
1903 | gtp_start_response(GTP_SUCCESS); | |
1904 | gtp_print_code(resulta); | |
1905 | gtp_printf(" "); | |
1906 | gtp_print_code(resultb); | |
1907 | gtp_mprintf(" %m", I(semeai_move), J(semeai_move)); | |
1908 | if (!result_certain && report_uncertainty) | |
1909 | gtp_printf(" uncertain"); | |
7eeb782e | 1910 | |
c150f57c AT |
1911 | return gtp_finish_response(); |
1912 | } | |
7eeb782e AT |
1913 | |
1914 | /* Function: Analyze a semeai, not using owl | |
1915 | * Arguments: dragona, dragonb | |
1916 | * Fails: invalid vertices, empty vertices | |
1917 | * Returns: status of dragona, dragonb assuming dragona moves first | |
1918 | */ | |
1919 | static int | |
c150f57c | 1920 | gtp_tactical_analyze_semeai(char* s) |
7eeb782e | 1921 | { |
c150f57c AT |
1922 | int i, j; |
1923 | int k; | |
1924 | int dragona, dragonb; | |
1925 | int resulta, resultb, move, result_certain; | |
7eeb782e | 1926 | |
c150f57c | 1927 | k = gtp_decode_coord(s, &i, &j); |
7eeb782e | 1928 | |
c150f57c AT |
1929 | if (k == 0) |
1930 | return gtp_failure("invalid coordinate"); | |
1931 | dragona = POS(i, j); | |
1932 | if (BOARD(i, j) == EMPTY) | |
1933 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1934 | |
c150f57c AT |
1935 | if (!gtp_decode_coord(s + k, &i, &j)) |
1936 | return gtp_failure("invalid coordinate"); | |
1937 | dragonb = POS(i, j); | |
1938 | if (BOARD(i, j) == EMPTY) | |
1939 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1940 | |
c150f57c AT |
1941 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
1942 | /* to get the variations into the sgf file, clear the reading cache */ | |
1943 | if (sgf_dumptree) | |
1944 | reading_cache_clear(); | |
7eeb782e | 1945 | |
c150f57c AT |
1946 | owl_analyze_semeai(dragona, dragonb, &resulta, &resultb, &move, 0, |
1947 | &result_certain); | |
1948 | gtp_start_response(GTP_SUCCESS); | |
1949 | gtp_print_code(resulta); | |
1950 | gtp_printf(" "); | |
1951 | gtp_print_code(resultb); | |
1952 | gtp_mprintf(" %m", I(move), J(move)); | |
1953 | if (!result_certain && report_uncertainty) | |
1954 | gtp_printf(" uncertain"); | |
7eeb782e | 1955 | |
c150f57c AT |
1956 | return gtp_finish_response(); |
1957 | } | |
7eeb782e AT |
1958 | |
1959 | /*********************** | |
1960 | * Connection reading. * | |
1961 | ***********************/ | |
1962 | ||
1963 | /* Function: Try to connect two strings. | |
1964 | * Arguments: vertex, vertex | |
1965 | * Fails: invalid vertex, empty vertex, vertices of different colors | |
1966 | * Returns: connect result followed by connect point if successful. | |
1967 | */ | |
1968 | static int | |
c150f57c | 1969 | gtp_connect(char* s) |
7eeb782e | 1970 | { |
c150f57c AT |
1971 | int ai, aj; |
1972 | int bi, bj; | |
1973 | int connect_move = PASS_MOVE; | |
1974 | int result; | |
1975 | int n; | |
7eeb782e | 1976 | |
c150f57c AT |
1977 | n = gtp_decode_coord(s, &ai, &aj); |
1978 | if (n == 0) | |
1979 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1980 | |
c150f57c AT |
1981 | if (!gtp_decode_coord(s + n, &bi, &bj)) |
1982 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 1983 | |
c150f57c AT |
1984 | if (BOARD(ai, aj) == EMPTY || BOARD(bi, bj) == EMPTY) |
1985 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 1986 | |
c150f57c AT |
1987 | if (BOARD(ai, aj) != BOARD(bi, bj)) |
1988 | return gtp_failure("vertices must have same color"); | |
7eeb782e | 1989 | |
c150f57c AT |
1990 | result = string_connect(POS(ai, aj), POS(bi, bj), &connect_move); |
1991 | gtp_start_response(GTP_SUCCESS); | |
1992 | gtp_print_code(result); | |
1993 | if (result != 0) | |
1994 | gtp_mprintf(" %m", I(connect_move), J(connect_move)); | |
7eeb782e | 1995 | |
c150f57c AT |
1996 | return gtp_finish_response(); |
1997 | } | |
7eeb782e AT |
1998 | |
1999 | /* Function: Try to disconnect two strings. | |
2000 | * Arguments: vertex, vertex | |
2001 | * Fails: invalid vertex, empty vertex, vertices of different colors | |
2002 | * Returns: disconnect result followed by disconnect point if successful. | |
2003 | */ | |
2004 | static int | |
c150f57c | 2005 | gtp_disconnect(char* s) |
7eeb782e | 2006 | { |
c150f57c AT |
2007 | int ai, aj; |
2008 | int bi, bj; | |
2009 | int disconnect_move = PASS_MOVE; | |
2010 | int result; | |
2011 | int n; | |
7eeb782e | 2012 | |
c150f57c AT |
2013 | n = gtp_decode_coord(s, &ai, &aj); |
2014 | if (n == 0) | |
2015 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2016 | |
c150f57c AT |
2017 | if (!gtp_decode_coord(s + n, &bi, &bj)) |
2018 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2019 | |
c150f57c AT |
2020 | if (BOARD(ai, aj) == EMPTY || BOARD(bi, bj) == EMPTY) |
2021 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 2022 | |
c150f57c AT |
2023 | if (BOARD(ai, aj) != BOARD(bi, bj)) |
2024 | return gtp_failure("vertices must have same color"); | |
7eeb782e | 2025 | |
c150f57c AT |
2026 | result = disconnect(POS(ai, aj), POS(bi, bj), &disconnect_move); |
2027 | gtp_start_response(GTP_SUCCESS); | |
2028 | gtp_print_code(result); | |
2029 | if (result != 0) | |
2030 | gtp_mprintf(" %m", I(disconnect_move), J(disconnect_move)); | |
7eeb782e | 2031 | |
c150f57c AT |
2032 | return gtp_finish_response(); |
2033 | } | |
7eeb782e AT |
2034 | |
2035 | /* Function: Try to break from string into area. | |
2036 | * Arguments: vertex, vertices | |
2037 | * Fails: invalid vertex, empty vertex. | |
2038 | * Returns: result followed by break in point if successful. | |
2039 | */ | |
2040 | static int | |
c150f57c | 2041 | gtp_break_in(char* s) |
7eeb782e | 2042 | { |
c150f57c AT |
2043 | int ai, aj; |
2044 | int i, j; | |
2045 | signed char goal[BOARDMAX]; | |
2046 | int break_move = PASS_MOVE; | |
2047 | int result; | |
2048 | int n; | |
2049 | int k; | |
7eeb782e | 2050 | |
c150f57c AT |
2051 | n = gtp_decode_coord(s, &ai, &aj); |
2052 | if (n == 0) | |
2053 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2054 | |
c150f57c AT |
2055 | memset(goal, 0, BOARDMAX); |
2056 | s += n; | |
2057 | ||
2058 | for (k = 0; k < MAX_BOARD * MAX_BOARD; k++) { | |
2059 | n = gtp_decode_coord(s, &i, &j); | |
2060 | if (n > 0) { | |
2061 | goal[POS(i, j)] = 1; | |
2062 | s += n; | |
2063 | } else if (sscanf(s, "%*s") != EOF) | |
2064 | return gtp_failure("invalid coordinate"); | |
2065 | else | |
2066 | break; | |
7eeb782e | 2067 | } |
7eeb782e | 2068 | |
c150f57c AT |
2069 | if (BOARD(ai, aj) == EMPTY) |
2070 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 2071 | |
c150f57c AT |
2072 | result = break_in(POS(ai, aj), goal, &break_move); |
2073 | gtp_start_response(GTP_SUCCESS); | |
2074 | gtp_print_code(result); | |
2075 | if (result != 0) | |
2076 | gtp_mprintf(" %m", I(break_move), J(break_move)); | |
7eeb782e | 2077 | |
c150f57c | 2078 | return gtp_finish_response(); |
7eeb782e AT |
2079 | } |
2080 | ||
2081 | /* Function: Try to block string from area. | |
2082 | * Arguments: vertex, vertices | |
2083 | * Fails: invalid vertex, empty vertex. | |
2084 | * Returns: result followed by block point if successful. | |
2085 | */ | |
2086 | static int | |
c150f57c | 2087 | gtp_block_off(char* s) |
7eeb782e | 2088 | { |
c150f57c AT |
2089 | int ai, aj; |
2090 | int i, j; | |
2091 | signed char goal[BOARDMAX]; | |
2092 | int block_move = PASS_MOVE; | |
2093 | int result; | |
2094 | int n; | |
2095 | int k; | |
7eeb782e | 2096 | |
c150f57c AT |
2097 | n = gtp_decode_coord(s, &ai, &aj); |
2098 | if (n == 0) | |
2099 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2100 | |
c150f57c AT |
2101 | memset(goal, 0, BOARDMAX); |
2102 | s += n; | |
2103 | ||
2104 | for (k = 0; k < MAX_BOARD * MAX_BOARD; k++) { | |
2105 | n = gtp_decode_coord(s, &i, &j); | |
2106 | if (n > 0) { | |
2107 | goal[POS(i, j)] = 1; | |
2108 | s += n; | |
2109 | } else if (sscanf(s, "%*s") != EOF) | |
2110 | return gtp_failure("invalid coordinate"); | |
2111 | else | |
2112 | break; | |
7eeb782e | 2113 | } |
7eeb782e | 2114 | |
c150f57c AT |
2115 | if (BOARD(ai, aj) == EMPTY) |
2116 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 2117 | |
c150f57c AT |
2118 | result = block_off(POS(ai, aj), goal, &block_move); |
2119 | gtp_start_response(GTP_SUCCESS); | |
2120 | gtp_print_code(result); | |
2121 | if (result != 0) | |
2122 | gtp_mprintf(" %m", I(block_move), J(block_move)); | |
7eeb782e | 2123 | |
c150f57c | 2124 | return gtp_finish_response(); |
7eeb782e AT |
2125 | } |
2126 | ||
7eeb782e AT |
2127 | /******** |
2128 | * eyes * | |
2129 | ********/ | |
2130 | ||
2131 | /* Function: Evaluate an eye space | |
2132 | * Arguments: vertex | |
2133 | * Fails: invalid vertex | |
2134 | * Returns: Minimum and maximum number of eyes. If these differ an | |
2135 | * attack and a defense point are additionally returned. | |
2136 | * If the vertex is not an eye space or not of unique color, | |
2137 | * a single -1 is returned. | |
2138 | */ | |
2139 | ||
2140 | static int | |
c150f57c AT |
2141 | gtp_eval_eye(char* s) |
2142 | { | |
2143 | int m, n; | |
2144 | struct eyevalue value; | |
2145 | int attack_point; | |
2146 | int defense_point; | |
2147 | int pos; | |
7eeb782e | 2148 | |
c150f57c AT |
2149 | if (!gtp_decode_coord(s, &m, &n)) |
2150 | return gtp_failure("invalid coordinate"); | |
2151 | ||
2152 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); | |
2153 | ||
2154 | if (black_eye[POS(m, n)].color == BLACK) { | |
2155 | pos = black_eye[POS(m, n)].origin; | |
2156 | compute_eyes(pos, &value, &attack_point, &defense_point, | |
2157 | black_eye, half_eye, 0); | |
2158 | } else if (white_eye[POS(m, n)].color == WHITE) { | |
2159 | pos = white_eye[POS(m, n)].origin; | |
2160 | compute_eyes(pos, &value, &attack_point, &defense_point, | |
2161 | white_eye, half_eye, 0); | |
2162 | } else | |
2163 | /* Not an eye or not of unique color. */ | |
2164 | return gtp_success("-1"); | |
2165 | ||
2166 | gtp_start_response(GTP_SUCCESS); | |
2167 | gtp_printf("%d %d", min_eyes(&value), max_eyes(&value)); | |
2168 | if (eye_move_urgency(&value) > 0) { | |
2169 | gtp_printf(" "); | |
2170 | gtp_print_vertex(I(attack_point), J(attack_point)); | |
2171 | gtp_printf(" "); | |
2172 | gtp_print_vertex(I(defense_point), J(defense_point)); | |
2173 | } | |
2174 | return gtp_finish_response(); | |
2175 | } | |
7eeb782e AT |
2176 | |
2177 | /***************** | |
2178 | * dragon status * | |
2179 | *****************/ | |
2180 | ||
2181 | /* Function: Determine status of a dragon. | |
2182 | * Arguments: optional vertex | |
2183 | * Fails: invalid vertex, empty vertex | |
2184 | * Returns: status ("alive", "critical", "dead", or "unknown"), | |
2185 | * attack point, defense point. Points of attack and | |
2186 | * defense are only given if the status is critical. | |
2187 | * If no vertex is given, the status is listed for all | |
2188 | * dragons, one per row in the format "A4: alive". | |
2189 | * | |
2190 | * FIXME: Should be able to distinguish between life in seki | |
2191 | * and independent life. Should also be able to identify ko. | |
2192 | */ | |
2193 | ||
2194 | static int | |
c150f57c AT |
2195 | gtp_dragon_status(char* s) |
2196 | { | |
2197 | int i, j; | |
2198 | int str = NO_MOVE; | |
2199 | int pos; | |
2200 | int empty_response = 1; | |
2201 | ||
2202 | if (gtp_decode_coord(s, &i, &j)) { | |
2203 | str = POS(i, j); | |
2204 | if (board[str] == EMPTY) | |
2205 | return gtp_failure("vertex must not be empty"); | |
2206 | } else if (sscanf(s, "%*s") != EOF) | |
2207 | return gtp_failure("invalid coordinate"); | |
2208 | ||
2209 | silent_examine_position(EXAMINE_DRAGONS); | |
2210 | ||
2211 | gtp_start_response(GTP_SUCCESS); | |
2212 | ||
2213 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
2214 | if (ON_BOARD(pos) | |
2215 | && (pos == str | |
2216 | || (str == NO_MOVE | |
2217 | && board[pos] != EMPTY | |
2218 | && dragon[pos].origin == pos))) { | |
2219 | if (str == NO_MOVE) | |
2220 | gtp_mprintf("%m: ", I(pos), J(pos)); | |
2221 | ||
2222 | if (dragon[pos].status == ALIVE) | |
2223 | gtp_printf("alive\n"); | |
2224 | else if (dragon[pos].status == DEAD) | |
2225 | gtp_printf("dead\n"); | |
2226 | else if (dragon[pos].status == UNKNOWN) | |
2227 | gtp_printf("unknown\n"); | |
2228 | else { | |
2229 | /* Only remaining possibility. */ | |
2230 | assert(dragon[pos].status == CRITICAL); | |
2231 | /* Status critical, need to return attack and defense point as well. */ | |
2232 | gtp_mprintf("critical %m %m\n", | |
2233 | I(DRAGON2(pos).owl_attack_point), | |
2234 | J(DRAGON2(pos).owl_attack_point), | |
2235 | I(DRAGON2(pos).owl_defense_point), | |
2236 | J(DRAGON2(pos).owl_defense_point)); | |
2237 | } | |
2238 | empty_response = 0; | |
2239 | } | |
7eeb782e | 2240 | } |
7eeb782e | 2241 | |
c150f57c AT |
2242 | if (empty_response) |
2243 | gtp_printf("\n"); | |
7eeb782e | 2244 | |
c150f57c AT |
2245 | gtp_printf("\n"); |
2246 | return GTP_OK; | |
7eeb782e AT |
2247 | } |
2248 | ||
7eeb782e AT |
2249 | /* Function: Determine whether two stones belong to the same dragon. |
2250 | * Arguments: vertex, vertex | |
2251 | * Fails: invalid vertex, empty vertex | |
2252 | * Returns: 1 if the vertices belong to the same dragon, 0 otherwise | |
2253 | */ | |
2254 | ||
2255 | static int | |
c150f57c | 2256 | gtp_same_dragon(char* s) |
7eeb782e | 2257 | { |
c150f57c AT |
2258 | int ai, aj; |
2259 | int bi, bj; | |
2260 | int n; | |
7eeb782e | 2261 | |
c150f57c AT |
2262 | n = gtp_decode_coord(s, &ai, &aj); |
2263 | if (n == 0) | |
2264 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2265 | |
c150f57c AT |
2266 | if (!gtp_decode_coord(s + n, &bi, &bj)) |
2267 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2268 | |
c150f57c AT |
2269 | if (BOARD(ai, aj) == EMPTY || BOARD(bi, bj) == EMPTY) |
2270 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 2271 | |
c150f57c | 2272 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 2273 | |
c150f57c AT |
2274 | return gtp_success("%d", dragon[POS(ai, aj)].id == dragon[POS(bi, bj)].id); |
2275 | } | |
7eeb782e AT |
2276 | |
2277 | /************************ | |
2278 | * Unconditional status * | |
2279 | ************************/ | |
2280 | ||
2281 | /* Function: Determine the unconditional status of a vertex. | |
2282 | * Arguments: vertex | |
2283 | * Fails: invalid vertex | |
2284 | * Returns: unconditional status ("undecided", "alive", "dead", | |
2285 | * "white_territory", "black_territory"). Occupied vertices can | |
2286 | * be undecided, alive, or dead. Empty vertices can be | |
2287 | * undecided, white territory, or black territory. | |
2288 | */ | |
2289 | ||
2290 | static int | |
c150f57c | 2291 | gtp_unconditional_status(char* s) |
7eeb782e | 2292 | { |
c150f57c AT |
2293 | int i, j; |
2294 | enum dragon_status status; | |
7eeb782e | 2295 | |
c150f57c AT |
2296 | if (!gtp_decode_coord(s, &i, &j)) |
2297 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2298 | |
c150f57c | 2299 | silent_examine_position(EXAMINE_WORMS); |
7eeb782e | 2300 | |
c150f57c AT |
2301 | status = worm[POS(i, j)].unconditional_status; |
2302 | if (status == UNKNOWN) | |
2303 | return gtp_success("undecided"); | |
2304 | return gtp_success("%s", status_to_string(status)); | |
2305 | } | |
7eeb782e AT |
2306 | |
2307 | /*********************** | |
2308 | * combination attacks * | |
2309 | ***********************/ | |
2310 | ||
2311 | /* Function: Find a move by color capturing something through a | |
2312 | * combination attack. | |
2313 | * Arguments: color | |
2314 | * Fails: invalid color | |
2315 | * Returns: Recommended move, PASS if no move found | |
2316 | */ | |
2317 | ||
2318 | static int | |
c150f57c | 2319 | gtp_combination_attack(char* s) |
7eeb782e | 2320 | { |
c150f57c AT |
2321 | int color; |
2322 | int attack_point; | |
2323 | int n; | |
2324 | ||
2325 | n = gtp_decode_color(s, &color); | |
2326 | if (!n) | |
2327 | return gtp_failure("invalid color"); | |
7eeb782e | 2328 | |
c150f57c | 2329 | silent_examine_position(EXAMINE_ALL); |
7eeb782e | 2330 | |
c150f57c AT |
2331 | if (!atari_atari(color, &attack_point, NULL, verbose)) |
2332 | attack_point = NO_MOVE; | |
7eeb782e | 2333 | |
c150f57c AT |
2334 | gtp_start_response(GTP_SUCCESS); |
2335 | gtp_print_vertex(I(attack_point), J(attack_point)); | |
2336 | return gtp_finish_response(); | |
7eeb782e AT |
2337 | } |
2338 | ||
2339 | /* Function: If color can capture something through a | |
2340 | * combination attack, list moves by the opponent of color | |
2341 | * to defend against this attack. | |
2342 | * Arguments: color | |
2343 | * Fails: invalid color | |
2344 | * Returns: Recommended moves, PASS if no combination attack found. | |
2345 | */ | |
2346 | ||
2347 | static int | |
c150f57c | 2348 | gtp_combination_defend(char* s) |
7eeb782e | 2349 | { |
c150f57c AT |
2350 | int color; |
2351 | signed char defense_points[BOARDMAX]; | |
2352 | int pos; | |
2353 | int first = 1; | |
2354 | int n; | |
7eeb782e | 2355 | |
c150f57c AT |
2356 | n = gtp_decode_color(s, &color); |
2357 | if (!n) | |
2358 | return gtp_failure("invalid color"); | |
7eeb782e | 2359 | |
c150f57c | 2360 | silent_examine_position(EXAMINE_ALL); |
7eeb782e | 2361 | |
c150f57c AT |
2362 | memset(defense_points, 0, sizeof(defense_points)); |
2363 | if (!atari_atari(color, NULL, defense_points, verbose)) | |
2364 | return gtp_success("PASS"); | |
2365 | ||
2366 | gtp_start_response(GTP_SUCCESS); | |
2367 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
2368 | if (ON_BOARD(pos) && defense_points[pos]) { | |
2369 | if (!first) | |
2370 | gtp_printf(" "); | |
2371 | else | |
2372 | first = 0; | |
2373 | gtp_print_vertex(I(pos), J(pos)); | |
2374 | } | |
2375 | ||
2376 | return gtp_finish_response(); | |
7eeb782e AT |
2377 | } |
2378 | ||
2379 | /* Function: Run atari_atari_confirm_safety(). | |
2380 | * Arguments: move, optional int | |
2381 | * Fails: invalid move | |
2382 | * Returns: success code, if failure also defending move | |
2383 | */ | |
2384 | ||
2385 | static int | |
c150f57c | 2386 | gtp_aa_confirm_safety(char* s) |
7eeb782e | 2387 | { |
c150f57c AT |
2388 | int color; |
2389 | int i, j; | |
2390 | int n; | |
2391 | int minsize = 0; | |
2392 | int result; | |
2393 | int defense_point = NO_MOVE; | |
2394 | signed char saved_dragons[BOARDMAX]; | |
2395 | signed char saved_worms[BOARDMAX]; | |
7eeb782e | 2396 | |
c150f57c AT |
2397 | n = gtp_decode_move(s, &color, &i, &j); |
2398 | if (n == 0 || POS(i, j) == NO_MOVE) | |
2399 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 2400 | |
c150f57c | 2401 | sscanf(s + n, "%d", &minsize); |
7eeb782e | 2402 | |
c150f57c AT |
2403 | genmove(color, NULL, NULL); |
2404 | get_saved_dragons(POS(i, j), saved_dragons); | |
2405 | get_saved_worms(POS(i, j), saved_worms); | |
7eeb782e | 2406 | |
c150f57c AT |
2407 | result = atari_atari_confirm_safety(color, POS(i, j), |
2408 | &defense_point, minsize, | |
2409 | saved_dragons, saved_worms); | |
2410 | ||
2411 | gtp_start_response(GTP_SUCCESS); | |
2412 | gtp_mprintf("%d", result); | |
2413 | if (result == 0) | |
2414 | gtp_mprintf(" %m", I(defense_point), J(defense_point)); | |
2415 | ||
2416 | return gtp_finish_response(); | |
2417 | } | |
7eeb782e AT |
2418 | |
2419 | /******************** | |
2420 | * generating moves * | |
2421 | ********************/ | |
2422 | ||
2423 | /* Function: Generate and play the supposedly best black move. | |
2424 | * Arguments: none | |
2425 | * Fails: never | |
2426 | * Returns: a move coordinate or "PASS" | |
2427 | * | |
2428 | * Status: Obsolete GTP version 1 command. | |
2429 | */ | |
2430 | static int | |
c150f57c | 2431 | gtp_genmove_black(char* s) |
7eeb782e | 2432 | { |
c150f57c AT |
2433 | int move; |
2434 | UNUSED(s); | |
7eeb782e | 2435 | |
c150f57c AT |
2436 | if (stackp > 0) |
2437 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2438 | |
c150f57c | 2439 | move = genmove(BLACK, NULL, NULL); |
7eeb782e | 2440 | |
c150f57c | 2441 | gnugo_play_move(move, BLACK); |
7eeb782e | 2442 | |
c150f57c AT |
2443 | gtp_start_response(GTP_SUCCESS); |
2444 | gtp_print_vertex(I(move), J(move)); | |
2445 | return gtp_finish_response(); | |
7eeb782e AT |
2446 | } |
2447 | ||
2448 | /* Function: Generate and play the supposedly best white move. | |
2449 | * Arguments: none | |
2450 | * Fails: never | |
2451 | * Returns: a move coordinate or "PASS" | |
2452 | * | |
2453 | * Status: Obsolete GTP version 1 command. | |
2454 | */ | |
2455 | static int | |
c150f57c | 2456 | gtp_genmove_white(char* s) |
7eeb782e | 2457 | { |
c150f57c AT |
2458 | int move; |
2459 | UNUSED(s); | |
7eeb782e | 2460 | |
c150f57c AT |
2461 | if (stackp > 0) |
2462 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2463 | |
c150f57c | 2464 | move = genmove(WHITE, NULL, NULL); |
7eeb782e | 2465 | |
c150f57c | 2466 | gnugo_play_move(move, WHITE); |
7eeb782e | 2467 | |
c150f57c AT |
2468 | gtp_start_response(GTP_SUCCESS); |
2469 | gtp_print_vertex(I(move), J(move)); | |
2470 | return gtp_finish_response(); | |
7eeb782e AT |
2471 | } |
2472 | ||
2473 | /* Function: Generate and play the supposedly best move for either color. | |
2474 | * Arguments: color to move | |
2475 | * Fails: invalid color | |
2476 | * Returns: a move coordinate or "PASS" (or "resign" if resignation_allowed) | |
2477 | * | |
2478 | * Status: GTP version 2 standard command. | |
2479 | */ | |
2480 | static int | |
c150f57c | 2481 | gtp_genmove(char* s) |
7eeb782e | 2482 | { |
c150f57c AT |
2483 | int move; |
2484 | int resign; | |
2485 | int color; | |
2486 | int n; | |
7eeb782e | 2487 | |
c150f57c AT |
2488 | n = gtp_decode_color(s, &color); |
2489 | if (!n) | |
2490 | return gtp_failure("invalid color"); | |
7eeb782e | 2491 | |
c150f57c AT |
2492 | if (stackp > 0) |
2493 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2494 | |
c150f57c AT |
2495 | adjust_level_offset(color); |
2496 | move = genmove(color, NULL, &resign); | |
7eeb782e | 2497 | |
c150f57c AT |
2498 | if (resign) |
2499 | return gtp_success("resign"); | |
7eeb782e | 2500 | |
c150f57c | 2501 | gnugo_play_move(move, color); |
7eeb782e | 2502 | |
c150f57c AT |
2503 | gtp_start_response(GTP_SUCCESS); |
2504 | gtp_print_vertex(I(move), J(move)); | |
2505 | return gtp_finish_response(); | |
7eeb782e AT |
2506 | } |
2507 | ||
7eeb782e AT |
2508 | /* Function: Generate the supposedly best move for either color. |
2509 | * Arguments: color to move | |
2510 | * Fails: invalid color | |
2511 | * Returns: a move coordinate (or "PASS") | |
2512 | * | |
2513 | * Status: GTP version 2 standard command. | |
2514 | */ | |
2515 | static int | |
c150f57c | 2516 | gtp_reg_genmove(char* s) |
7eeb782e | 2517 | { |
c150f57c AT |
2518 | int move; |
2519 | int color; | |
2520 | int n; | |
2521 | unsigned int saved_random_seed = get_random_seed(); | |
7eeb782e | 2522 | |
c150f57c AT |
2523 | n = gtp_decode_color(s, &color); |
2524 | if (!n) | |
2525 | return gtp_failure("invalid color"); | |
7eeb782e | 2526 | |
c150f57c AT |
2527 | if (stackp > 0) |
2528 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2529 | |
c150f57c | 2530 | /* This is intended for regression purposes and should therefore be |
7eeb782e AT |
2531 | * deterministic. The best way to ensure this is to reset the random |
2532 | * number generator before calling genmove(). It is always seeded by | |
2533 | * 0. | |
2534 | */ | |
c150f57c AT |
2535 | set_random_seed(0); |
2536 | ||
2537 | move = genmove_conservative(color, NULL); | |
7eeb782e | 2538 | |
c150f57c AT |
2539 | set_random_seed(saved_random_seed); |
2540 | gtp_start_response(GTP_SUCCESS); | |
2541 | gtp_print_vertex(I(move), J(move)); | |
2542 | return gtp_finish_response(); | |
7eeb782e AT |
2543 | } |
2544 | ||
2545 | /* Function: Generate the supposedly best move for either color. | |
2546 | * Arguments: color to move, optionally a random seed | |
2547 | * Fails: invalid color | |
2548 | * Returns: a move coordinate (or "PASS") | |
2549 | * | |
2550 | * This differs from reg_genmove in the optional random seed. | |
2551 | */ | |
2552 | static int | |
c150f57c | 2553 | gtp_gg_genmove(char* s) |
7eeb782e | 2554 | { |
c150f57c AT |
2555 | int move; |
2556 | int color; | |
2557 | int n; | |
2558 | unsigned int saved_random_seed = get_random_seed(); | |
2559 | unsigned int seed; | |
7eeb782e | 2560 | |
c150f57c AT |
2561 | n = gtp_decode_color(s, &color); |
2562 | if (!n) | |
2563 | return gtp_failure("invalid color"); | |
7eeb782e | 2564 | |
c150f57c AT |
2565 | if (stackp > 0) |
2566 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2567 | |
c150f57c | 2568 | /* This is intended for regression purposes and should therefore be |
7eeb782e AT |
2569 | * deterministic. The best way to ensure this is to reset the random |
2570 | * number generator before calling genmove(). By default it is | |
2571 | * seeded with 0, but if an optional unsigned integer is given in | |
2572 | * the command after the color, this is used as seed instead. | |
2573 | */ | |
c150f57c AT |
2574 | seed = 0; |
2575 | sscanf(s + n, "%u", &seed); | |
2576 | set_random_seed(seed); | |
7eeb782e | 2577 | |
c150f57c AT |
2578 | move = genmove_conservative(color, NULL); |
2579 | set_random_seed(saved_random_seed); | |
2580 | gtp_start_response(GTP_SUCCESS); | |
2581 | gtp_print_vertex(I(move), J(move)); | |
2582 | return gtp_finish_response(); | |
2583 | } | |
7eeb782e AT |
2584 | |
2585 | /* Function: Generate the supposedly best move for either color from a | |
2586 | * choice of allowed vertices. | |
2587 | * Arguments: color to move, allowed vertices | |
2588 | * Fails: invalid color, invalid vertex, no vertex listed | |
2589 | * Returns: a move coordinate (or "PASS") | |
2590 | */ | |
2591 | static int | |
c150f57c | 2592 | gtp_restricted_genmove(char* s) |
7eeb782e | 2593 | { |
c150f57c AT |
2594 | int move; |
2595 | int i, j; | |
2596 | int color; | |
2597 | int n; | |
2598 | unsigned int saved_random_seed = get_random_seed(); | |
2599 | int allowed_moves[BOARDMAX]; | |
2600 | int number_allowed_moves = 0; | |
2601 | memset(allowed_moves, 0, sizeof(allowed_moves)); | |
7eeb782e | 2602 | |
c150f57c AT |
2603 | n = gtp_decode_color(s, &color); |
2604 | if (!n) | |
2605 | return gtp_failure("invalid color"); | |
7eeb782e | 2606 | |
c150f57c AT |
2607 | s += n; |
2608 | while (1) { | |
2609 | n = gtp_decode_coord(s, &i, &j); | |
2610 | if (n > 0) { | |
2611 | allowed_moves[POS(i, j)] = 1; | |
2612 | number_allowed_moves++; | |
2613 | s += n; | |
2614 | } else if (sscanf(s, "%*s") != EOF) | |
2615 | return gtp_failure("invalid coordinate"); | |
2616 | else | |
2617 | break; | |
7eeb782e | 2618 | } |
7eeb782e | 2619 | |
c150f57c AT |
2620 | if (number_allowed_moves == 0) |
2621 | return gtp_failure("no allowed vertex"); | |
7eeb782e | 2622 | |
c150f57c AT |
2623 | if (stackp > 0) |
2624 | return gtp_failure("genmove cannot be called when stackp > 0"); | |
7eeb782e | 2625 | |
c150f57c | 2626 | /* This is intended for regression purposes and should therefore be |
7eeb782e AT |
2627 | * deterministic. The best way to ensure this is to reset the random |
2628 | * number generator before calling genmove(). It is always seeded by | |
2629 | * 0. | |
2630 | */ | |
c150f57c | 2631 | set_random_seed(0); |
7eeb782e | 2632 | |
c150f57c AT |
2633 | move = genmove_restricted(color, allowed_moves); |
2634 | set_random_seed(saved_random_seed); | |
2635 | gtp_start_response(GTP_SUCCESS); | |
2636 | gtp_print_vertex(I(move), J(move)); | |
2637 | return gtp_finish_response(); | |
2638 | } | |
7eeb782e AT |
2639 | |
2640 | /* Function: Generate and play the supposedly best move for either color, | |
2641 | * not passing until all dead opponent stones have been removed. | |
2642 | * Arguments: color to move | |
2643 | * Fails: invalid color | |
2644 | * Returns: a move coordinate (or "PASS") | |
2645 | * | |
2646 | * Status: KGS specific command. | |
2647 | * | |
2648 | * A similar command, but possibly somewhat different, will likely be added | |
2649 | * to GTP version 3 at a later time. | |
2650 | */ | |
2651 | static int | |
c150f57c | 2652 | gtp_kgs_genmove_cleanup(char* s) |
7eeb782e | 2653 | { |
c150f57c AT |
2654 | int move; |
2655 | int color; | |
2656 | int n; | |
2657 | int save_capture_all_dead = capture_all_dead; | |
7eeb782e | 2658 | |
c150f57c AT |
2659 | n = gtp_decode_color(s, &color); |
2660 | if (!n) | |
2661 | return gtp_failure("invalid color"); | |
7eeb782e | 2662 | |
c150f57c AT |
2663 | if (stackp > 0) |
2664 | return gtp_failure("kgs-genmove_cleanup cannot be called when stackp > 0"); | |
7eeb782e | 2665 | |
c150f57c | 2666 | /* Turn on the capture_all_dead option to force removal of dead |
7eeb782e AT |
2667 | * opponent stones. |
2668 | */ | |
c150f57c | 2669 | capture_all_dead = 1; |
7eeb782e | 2670 | |
c150f57c AT |
2671 | adjust_level_offset(color); |
2672 | move = genmove(color, NULL, NULL); | |
7eeb782e | 2673 | |
c150f57c | 2674 | capture_all_dead = save_capture_all_dead; |
7eeb782e | 2675 | |
c150f57c AT |
2676 | gnugo_play_move(move, color); |
2677 | ||
2678 | gtp_start_response(GTP_SUCCESS); | |
2679 | gtp_print_vertex(I(move), J(move)); | |
2680 | return gtp_finish_response(); | |
2681 | } | |
7eeb782e AT |
2682 | |
2683 | /* Function : List the move reasons for a move. | |
2684 | * Arguments: vertex | |
2685 | * Fails: : invalid vertex, occupied vertex | |
2686 | * Returns : list of move reasons (may be empty) | |
2687 | */ | |
2688 | ||
2689 | static int | |
c150f57c | 2690 | gtp_move_reasons(char* s) |
7eeb782e | 2691 | { |
c150f57c AT |
2692 | int i, j; |
2693 | if (!gtp_decode_coord(s, &i, &j)) | |
2694 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 2695 | |
c150f57c AT |
2696 | if (BOARD(i, j) != EMPTY) |
2697 | return gtp_failure("vertex must not be occupied"); | |
7eeb782e | 2698 | |
c150f57c AT |
2699 | gtp_start_response(GTP_SUCCESS); |
2700 | if (list_move_reasons(gtp_output_file, POS(i, j)) == 0) | |
2701 | gtp_printf("\n"); | |
7eeb782e | 2702 | gtp_printf("\n"); |
c150f57c | 2703 | return GTP_OK; |
7eeb782e AT |
2704 | } |
2705 | ||
2706 | /* Function : Generate a list of all moves with values larger than zero in | |
2707 | * the previous genmove command. | |
2708 | * If no previous genmove command has been issued, the result | |
2709 | * of this command will be meaningless. | |
2710 | * Arguments: none | |
2711 | * Fails: : never | |
2712 | * Returns : list of moves with values | |
2713 | */ | |
2714 | ||
2715 | static int | |
c150f57c | 2716 | gtp_all_move_values(char* s) |
7eeb782e | 2717 | { |
c150f57c AT |
2718 | UNUSED(s); |
2719 | gtp_start_response(GTP_SUCCESS); | |
2720 | print_all_move_values(gtp_output_file); | |
2721 | gtp_printf("\n"); | |
2722 | return GTP_OK; | |
7eeb782e AT |
2723 | } |
2724 | ||
2725 | /* Function : Generate a sorted list of the best moves in the previous genmove | |
2726 | * command. | |
2727 | * If no previous genmove command has been issued, the result | |
2728 | * of this command will be meaningless. | |
2729 | * Arguments: none | |
2730 | * Fails: : never | |
2731 | * Returns : list of moves with weights | |
2732 | */ | |
2733 | ||
2734 | /* FIXME: Don't we want the moves one per row? */ | |
2735 | static int | |
c150f57c | 2736 | gtp_top_moves(char* s) |
7eeb782e | 2737 | { |
c150f57c AT |
2738 | int k; |
2739 | UNUSED(s); | |
2740 | gtp_start_response(GTP_SUCCESS); | |
2741 | for (k = 0; k < 10; k++) | |
2742 | if (best_move_values[k] > 0.0) { | |
2743 | gtp_print_vertex(I(best_moves[k]), J(best_moves[k])); | |
2744 | gtp_printf(" %.2f ", best_move_values[k]); | |
2745 | } | |
2746 | gtp_printf("\n\n"); | |
2747 | return GTP_OK; | |
7eeb782e AT |
2748 | } |
2749 | ||
2750 | /* Function : Generate a list of the best moves for white with weights | |
2751 | * Arguments: none | |
2752 | * Fails: : never | |
2753 | * Returns : list of moves with weights | |
2754 | */ | |
2755 | ||
2756 | static int | |
c150f57c | 2757 | gtp_top_moves_white(char* s) |
7eeb782e | 2758 | { |
c150f57c AT |
2759 | int k; |
2760 | UNUSED(s); | |
2761 | genmove(WHITE, NULL, NULL); | |
2762 | gtp_start_response(GTP_SUCCESS); | |
2763 | for (k = 0; k < 10; k++) | |
2764 | if (best_move_values[k] > 0.0) { | |
2765 | gtp_print_vertex(I(best_moves[k]), J(best_moves[k])); | |
2766 | gtp_printf(" %.2f ", best_move_values[k]); | |
2767 | } | |
2768 | return gtp_finish_response(); | |
7eeb782e AT |
2769 | } |
2770 | ||
2771 | /* Function : Generate a list of the best moves for black with weights | |
2772 | * Arguments: none | |
2773 | * Fails: : never | |
2774 | * Returns : list of moves with weights | |
2775 | */ | |
2776 | ||
2777 | static int | |
c150f57c | 2778 | gtp_top_moves_black(char* s) |
7eeb782e | 2779 | { |
c150f57c AT |
2780 | int k; |
2781 | UNUSED(s); | |
2782 | genmove(BLACK, NULL, NULL); | |
2783 | gtp_start_response(GTP_SUCCESS); | |
2784 | for (k = 0; k < 10; k++) | |
2785 | if (best_move_values[k] > 0.0) { | |
2786 | gtp_print_vertex(I(best_moves[k]), J(best_moves[k])); | |
2787 | gtp_printf(" %.2f ", best_move_values[k]); | |
2788 | } | |
2789 | return gtp_finish_response(); | |
7eeb782e AT |
2790 | } |
2791 | ||
7eeb782e AT |
2792 | /* Function: Set the playing level. |
2793 | * Arguments: int | |
2794 | * Fails: incorrect argument | |
2795 | * Returns: nothing | |
2796 | */ | |
2797 | static int | |
c150f57c | 2798 | gtp_set_level(char* s) |
7eeb782e | 2799 | { |
c150f57c AT |
2800 | int new_level; |
2801 | if (sscanf(s, "%d", &new_level) < 1) | |
2802 | return gtp_failure("level not an integer"); | |
2803 | ||
2804 | set_level(new_level); | |
2805 | return gtp_success(""); | |
7eeb782e AT |
2806 | } |
2807 | ||
2808 | /* Function: Undo one move | |
2809 | * Arguments: none | |
2810 | * Fails: If move history is too short. | |
2811 | * Returns: nothing | |
2812 | * | |
2813 | * Status: GTP version 2 standard command. | |
2814 | */ | |
2815 | ||
2816 | static int | |
c150f57c | 2817 | gtp_undo(char* s) |
7eeb782e | 2818 | { |
c150f57c | 2819 | UNUSED(s); |
7eeb782e | 2820 | |
c150f57c AT |
2821 | if (stackp > 0 || !undo_move(1)) |
2822 | return gtp_failure("cannot undo"); | |
7eeb782e | 2823 | |
c150f57c | 2824 | reset_engine(); |
7eeb782e | 2825 | |
c150f57c AT |
2826 | return gtp_success(""); |
2827 | } | |
7eeb782e AT |
2828 | |
2829 | /* Function: Undo a number of moves | |
2830 | * Arguments: optional int | |
2831 | * Fails: If move history is too short. | |
2832 | * Returns: nothing | |
2833 | */ | |
2834 | ||
2835 | static int | |
c150f57c | 2836 | gtp_gg_undo(char* s) |
7eeb782e | 2837 | { |
c150f57c | 2838 | int number_moves = 1; |
7eeb782e | 2839 | |
c150f57c | 2840 | sscanf(s, "%d", &number_moves); |
7eeb782e | 2841 | |
c150f57c AT |
2842 | if (number_moves < 0) |
2843 | return gtp_failure("can't undo a negative number of moves"); | |
7eeb782e | 2844 | |
c150f57c AT |
2845 | if (stackp > 0 || !undo_move(number_moves)) |
2846 | return gtp_failure("cannot undo"); | |
7eeb782e | 2847 | |
c150f57c | 2848 | reset_engine(); |
7eeb782e | 2849 | |
c150f57c AT |
2850 | return gtp_success(""); |
2851 | } | |
7eeb782e AT |
2852 | |
2853 | /***************** | |
2854 | * time handling * | |
2855 | *****************/ | |
2856 | ||
2857 | /* Function: Set time allowance | |
2858 | * Arguments: int main_time, int byo_yomi_time, int byo_yomi_stones | |
2859 | * Fails: syntax error | |
2860 | * Returns: nothing | |
2861 | * | |
2862 | * Status: GTP version 2 standard command. | |
2863 | */ | |
2864 | ||
2865 | static int | |
c150f57c | 2866 | gtp_time_settings(char* s) |
7eeb782e | 2867 | { |
c150f57c | 2868 | int main_time, byoyomi_time, byoyomi_stones; |
7eeb782e | 2869 | |
c150f57c AT |
2870 | if (sscanf(s, "%d %d %d", &main_time, &byoyomi_time, &byoyomi_stones) < 3) |
2871 | return gtp_failure("not three integers"); | |
7eeb782e | 2872 | |
c150f57c AT |
2873 | clock_settings(main_time, byoyomi_time, byoyomi_stones); |
2874 | return gtp_success(""); | |
2875 | } | |
7eeb782e AT |
2876 | |
2877 | /* Function: Report remaining time | |
2878 | * Arguments: color color, int time, int stones | |
2879 | * Fails: syntax error | |
2880 | * Returns: nothing | |
2881 | * | |
2882 | * Status: GTP version 2 standard command. | |
2883 | */ | |
2884 | ||
2885 | static int | |
c150f57c | 2886 | gtp_time_left(char* s) |
7eeb782e | 2887 | { |
c150f57c AT |
2888 | int color; |
2889 | int time; | |
2890 | int stones; | |
2891 | int n; | |
7eeb782e | 2892 | |
c150f57c AT |
2893 | n = gtp_decode_color(s, &color); |
2894 | if (!n) | |
2895 | return gtp_failure("invalid color"); | |
7eeb782e | 2896 | |
c150f57c AT |
2897 | if (sscanf(s + n, "%d %d", &time, &stones) < 2) |
2898 | return gtp_failure("time and stones not two integers"); | |
7eeb782e | 2899 | |
c150f57c AT |
2900 | update_time_left(color, time, stones); |
2901 | ||
2902 | return gtp_success(""); | |
2903 | } | |
7eeb782e AT |
2904 | |
2905 | /*********** | |
2906 | * scoring * | |
2907 | ***********/ | |
2908 | ||
2909 | static float final_score; | |
2910 | static enum dragon_status final_status[MAX_BOARD][MAX_BOARD]; | |
c150f57c AT |
2911 | static enum dragon_status status_numbers[6] = { ALIVE, DEAD, ALIVE_IN_SEKI, |
2912 | WHITE_TERRITORY, | |
2913 | BLACK_TERRITORY, DAME }; | |
2914 | static const char* status_names[6] = { "alive", "dead", "seki", | |
2915 | "white_territory", "black_territory", | |
2916 | "dame" }; | |
7eeb782e AT |
2917 | |
2918 | /* Helper function. */ | |
2919 | static void | |
2920 | finish_and_score_game(int seed) | |
2921 | { | |
c150f57c AT |
2922 | int move; |
2923 | int i, j; | |
2924 | int next; | |
2925 | int pass = 0; | |
2926 | int moves = 0; | |
2927 | int saved_board[MAX_BOARD][MAX_BOARD]; | |
2928 | struct board_state saved_pos; | |
2929 | static int current_board[MAX_BOARD][MAX_BOARD]; | |
2930 | static int current_seed = -1; | |
2931 | int cached_board = 1; | |
2932 | ||
2933 | if (current_seed != seed) { | |
2934 | current_seed = seed; | |
2935 | cached_board = 0; | |
2936 | } | |
2937 | ||
2938 | for (i = 0; i < board_size; i++) | |
2939 | for (j = 0; j < board_size; j++) | |
2940 | if (BOARD(i, j) != current_board[i][j]) { | |
2941 | current_board[i][j] = BOARD(i, j); | |
2942 | cached_board = 0; | |
2943 | } | |
2944 | ||
2945 | /* If this is exactly the same position as the one we analyzed the | |
7eeb782e AT |
2946 | * last time, the contents of final_score and final_status are up to date. |
2947 | */ | |
c150f57c AT |
2948 | if (cached_board) |
2949 | return; | |
7eeb782e | 2950 | |
c150f57c AT |
2951 | doing_scoring = 1; |
2952 | store_board(&saved_pos); | |
7eeb782e | 2953 | |
c150f57c | 2954 | /* Let black start if we have no move history. Otherwise continue |
7eeb782e AT |
2955 | * alternation. |
2956 | */ | |
c150f57c AT |
2957 | if (get_last_player() == EMPTY) |
2958 | next = BLACK; | |
7eeb782e | 2959 | else |
c150f57c AT |
2960 | next = OTHER_COLOR(get_last_player()); |
2961 | ||
2962 | do { | |
2963 | move = genmove_conservative(next, NULL); | |
2964 | gnugo_play_move(move, next); | |
2965 | if (move != PASS_MOVE) { | |
2966 | pass = 0; | |
2967 | moves++; | |
2968 | } else | |
2969 | pass++; | |
2970 | ||
2971 | next = OTHER_COLOR(next); | |
2972 | } while (pass < 2 && moves < board_size * board_size); | |
2973 | ||
2974 | final_score = aftermath_compute_score(next, NULL); | |
2975 | for (i = 0; i < board_size; i++) | |
2976 | for (j = 0; j < board_size; j++) { | |
2977 | final_status[i][j] = aftermath_final_status(next, POS(i, j)); | |
2978 | saved_board[i][j] = BOARD(i, j); | |
2979 | } | |
2980 | ||
2981 | restore_board(&saved_pos); | |
2982 | doing_scoring = 0; | |
2983 | ||
2984 | /* Update the status for vertices which were changed while finishing | |
7eeb782e AT |
2985 | * the game, up to filling dame. |
2986 | */ | |
c150f57c AT |
2987 | for (i = 0; i < board_size; i++) |
2988 | for (j = 0; j < board_size; j++) { | |
2989 | if (BOARD(i, j) == saved_board[i][j]) | |
2990 | continue; | |
2991 | ||
2992 | if (BOARD(i, j) == EMPTY) { | |
2993 | if (final_status[i][j] == ALIVE | |
2994 | || final_status[i][j] == ALIVE_IN_SEKI) | |
2995 | final_status[i][j] = DAME; | |
2996 | else if (final_status[i][j] == DEAD) { | |
2997 | if (saved_board[i][j] == BLACK) | |
2998 | final_status[i][j] = WHITE_TERRITORY; | |
2999 | else | |
3000 | final_status[i][j] = BLACK_TERRITORY; | |
3001 | } | |
3002 | } else if (BOARD(i, j) == BLACK) { | |
3003 | if (final_status[i][j] == WHITE_TERRITORY) | |
3004 | final_status[i][j] = DEAD; | |
3005 | else if (final_status[i][j] == DAME) | |
3006 | final_status[i][j] = ALIVE_IN_SEKI; | |
3007 | else if (final_status[i][j] == BLACK_TERRITORY) | |
3008 | final_status[i][j] = ALIVE; | |
3009 | else | |
3010 | final_status[i][j] = DEAD; | |
3011 | } else if (BOARD(i, j) == WHITE) { | |
3012 | if (final_status[i][j] == BLACK_TERRITORY) | |
3013 | final_status[i][j] = DEAD; | |
3014 | else if (final_status[i][j] == DAME) | |
3015 | final_status[i][j] = ALIVE_IN_SEKI; | |
3016 | else if (final_status[i][j] == WHITE_TERRITORY) | |
3017 | final_status[i][j] = ALIVE; | |
3018 | else | |
3019 | final_status[i][j] = DEAD; | |
3020 | } | |
3021 | } | |
7eeb782e AT |
3022 | } |
3023 | ||
7eeb782e AT |
3024 | /* Function: Compute the score of a finished game. |
3025 | * Arguments: Optional random seed | |
3026 | * Fails: never | |
3027 | * Returns: Score in SGF format (RE property). | |
3028 | * | |
3029 | * Status: GTP version 2 standard command. | |
3030 | */ | |
3031 | static int | |
c150f57c | 3032 | gtp_final_score(char* s) |
7eeb782e | 3033 | { |
c150f57c AT |
3034 | unsigned int saved_random_seed = get_random_seed(); |
3035 | int seed; | |
3036 | /* This is intended for regression purposes and should therefore be | |
7eeb782e AT |
3037 | * deterministic. The best way to ensure this is to reset the random |
3038 | * number generator before calling genmove(). By default it is | |
3039 | * seeded with 0, but if an optional unsigned integer is given in | |
3040 | * the command after the color, this is used as seed instead. | |
3041 | */ | |
c150f57c AT |
3042 | seed = 0; |
3043 | sscanf(s, "%d", &seed); | |
3044 | set_random_seed(seed); | |
7eeb782e | 3045 | |
c150f57c | 3046 | finish_and_score_game(seed); |
7eeb782e | 3047 | |
c150f57c | 3048 | set_random_seed(saved_random_seed); |
7eeb782e | 3049 | |
c150f57c AT |
3050 | gtp_start_response(GTP_SUCCESS); |
3051 | if (final_score > 0.0) | |
3052 | gtp_printf("W+%3.1f", final_score); | |
3053 | else if (final_score < 0.0) | |
3054 | gtp_printf("B+%3.1f", -final_score); | |
3055 | else | |
3056 | gtp_printf("0"); | |
3057 | return gtp_finish_response(); | |
7eeb782e AT |
3058 | } |
3059 | ||
7eeb782e AT |
3060 | /* Function: Report the final status of a vertex in a finished game. |
3061 | * Arguments: Vertex, optional random seed | |
3062 | * Fails: invalid vertex | |
3063 | * Returns: Status in the form of one of the strings "alive", "dead", | |
3064 | * "seki", "white_territory", "black_territory", or "dame". | |
3065 | */ | |
3066 | static int | |
c150f57c | 3067 | gtp_final_status(char* s) |
7eeb782e | 3068 | { |
c150f57c AT |
3069 | int seed; |
3070 | int n; | |
3071 | int ai, aj; | |
3072 | int k; | |
3073 | unsigned int saved_random_seed = get_random_seed(); | |
3074 | const char* result = NULL; | |
7eeb782e | 3075 | |
c150f57c AT |
3076 | n = gtp_decode_coord(s, &ai, &aj); |
3077 | if (n == 0) | |
3078 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3079 | |
c150f57c | 3080 | /* This is intended for regression purposes and should therefore be |
7eeb782e AT |
3081 | * deterministic. The best way to ensure this is to reset the random |
3082 | * number generator before calling genmove(). By default it is | |
3083 | * seeded with 0, but if an optional unsigned integer is given in | |
3084 | * the command after the color, this is used as seed instead. | |
3085 | */ | |
c150f57c AT |
3086 | seed = 0; |
3087 | sscanf(s + n, "%d", &seed); | |
3088 | set_random_seed(seed); | |
7eeb782e | 3089 | |
c150f57c | 3090 | finish_and_score_game(seed); |
7eeb782e | 3091 | |
c150f57c AT |
3092 | set_random_seed(saved_random_seed); |
3093 | for (k = 0; k < 6; k++) | |
3094 | if (final_status[ai][aj] == status_numbers[k]) { | |
3095 | result = status_names[k]; | |
3096 | break; | |
3097 | } | |
3098 | assert(result != NULL); | |
7eeb782e | 3099 | |
c150f57c | 3100 | return gtp_success(result); |
7eeb782e AT |
3101 | } |
3102 | ||
7eeb782e AT |
3103 | /* Function: Report vertices with a specific final status in a finished game. |
3104 | * Arguments: Status in the form of one of the strings "alive", "dead", | |
3105 | * "seki", "white_territory", "black_territory", or "dame". | |
3106 | * An optional random seed can be added. | |
3107 | * Fails: missing or invalid status string | |
3108 | * Returns: Vertices having the specified status. These are split with | |
3109 | * one string on each line if the vertices are nonempty (i.e. | |
3110 | * for "alive", "dead", and "seki"). | |
3111 | * | |
3112 | * Status: GTP version 2 standard command. | |
3113 | * However, "dame", "white_territory", and "black_territory" | |
3114 | * are private extensions. | |
3115 | */ | |
3116 | static int | |
c150f57c | 3117 | gtp_final_status_list(char* s) |
7eeb782e | 3118 | { |
c150f57c AT |
3119 | int seed; |
3120 | int n; | |
3121 | int i, j; | |
3122 | enum dragon_status status = UNKNOWN; | |
3123 | int k; | |
3124 | char status_string[GTP_BUFSIZE]; | |
3125 | int first; | |
3126 | unsigned int saved_random_seed = get_random_seed(); | |
3127 | ||
3128 | if (sscanf(s, "%s %n", status_string, &n) != 1) | |
3129 | return gtp_failure("missing status"); | |
7eeb782e | 3130 | |
c150f57c AT |
3131 | for (k = 0; k < 6; k++) { |
3132 | if (strcmp(status_string, status_names[k]) == 0) | |
3133 | status = status_numbers[k]; | |
3134 | } | |
7eeb782e | 3135 | |
c150f57c AT |
3136 | if (status == UNKNOWN) |
3137 | return gtp_failure("invalid status"); | |
7eeb782e | 3138 | |
c150f57c | 3139 | /* This is intended for regression purposes and should therefore be |
7eeb782e AT |
3140 | * deterministic. The best way to ensure this is to reset the random |
3141 | * number generator before calling genmove(). By default it is | |
3142 | * seeded with 0, but if an optional unsigned integer is given in | |
3143 | * the command after the color, this is used as seed instead. | |
3144 | */ | |
c150f57c AT |
3145 | seed = 0; |
3146 | sscanf(s + n, "%d", &seed); | |
3147 | set_random_seed(seed); | |
3148 | ||
3149 | finish_and_score_game(seed); | |
3150 | ||
3151 | set_random_seed(saved_random_seed); | |
7eeb782e | 3152 | |
c150f57c AT |
3153 | gtp_start_response(GTP_SUCCESS); |
3154 | ||
3155 | first = 1; | |
3156 | for (i = 0; i < board_size; i++) | |
3157 | for (j = 0; j < board_size; j++) { | |
3158 | if (final_status[i][j] != status) | |
3159 | continue; | |
3160 | if (BOARD(i, j) == EMPTY) { | |
3161 | if (!first) | |
3162 | gtp_printf(" "); | |
3163 | else | |
3164 | first = 0; | |
3165 | gtp_print_vertex(i, j); | |
3166 | } else { | |
3167 | int num_stones; | |
3168 | int stones[MAX_BOARD * MAX_BOARD]; | |
3169 | if (find_origin(POS(i, j)) != POS(i, j)) | |
3170 | continue; | |
3171 | if (!first) | |
3172 | gtp_printf("\n"); | |
3173 | else | |
3174 | first = 0; | |
3175 | num_stones = findstones(POS(i, j), board_size * board_size, stones); | |
3176 | gtp_print_vertices2(num_stones, stones); | |
3177 | } | |
3178 | } | |
3179 | ||
3180 | return gtp_finish_response(); | |
7eeb782e AT |
3181 | } |
3182 | ||
3183 | /* Function: Estimate the score | |
3184 | * Arguments: None | |
3185 | * Fails: never | |
3186 | * Returns: upper and lower bounds for the score | |
3187 | */ | |
3188 | ||
3189 | static int | |
c150f57c | 3190 | gtp_estimate_score(char* s) |
7eeb782e | 3191 | { |
c150f57c AT |
3192 | float score; |
3193 | float upper_bound, lower_bound; | |
3194 | UNUSED(s); | |
7eeb782e | 3195 | |
c150f57c AT |
3196 | score = gnugo_estimate_score(&upper_bound, &lower_bound); |
3197 | gtp_start_response(GTP_SUCCESS); | |
3198 | /* Traditionally W wins jigo */ | |
3199 | if (score >= 0.0) | |
3200 | gtp_printf("W+%3.1f (upper bound: %3.1f, lower: %3.1f)", | |
3201 | score, upper_bound, lower_bound); | |
3202 | else if (score < 0.0) | |
3203 | gtp_printf("B+%3.1f (upper bound: %3.1f, lower: %3.1f)", | |
3204 | -score, upper_bound, lower_bound); | |
3205 | return gtp_finish_response(); | |
3206 | } | |
7eeb782e AT |
3207 | |
3208 | /* Function: Estimate the score, taking into account which player moves next | |
3209 | * Arguments: Color to play | |
3210 | * Fails: Invalid color | |
3211 | * Returns: Score. | |
3212 | * | |
3213 | * This function generates a move for color, then adds the | |
3214 | * value of the move generated to the value of the position. | |
3215 | * Critical dragons are awarded to the opponent since the | |
3216 | * value of rescuing a critical dragon is taken into account | |
3217 | * in the value of the move generated. | |
3218 | */ | |
3219 | ||
3220 | static int | |
c150f57c | 3221 | gtp_experimental_score(char* s) |
7eeb782e | 3222 | { |
c150f57c AT |
3223 | float upper_bound, lower_bound, score; |
3224 | int color; | |
7eeb782e | 3225 | |
c150f57c AT |
3226 | if (!gtp_decode_color(s, &color) |
3227 | || (color != BLACK && color != WHITE)) | |
3228 | return gtp_failure("invalid color"); | |
7eeb782e | 3229 | |
c150f57c AT |
3230 | genmove_conservative(color, NULL); |
3231 | gnugo_estimate_score(&upper_bound, &lower_bound); | |
7eeb782e | 3232 | |
c150f57c AT |
3233 | if (debug & DEBUG_SCORING) |
3234 | fprintf(stderr, "upper = %3.1f, lower = %3.1f, best = %3.1f\n", | |
3235 | upper_bound, lower_bound, best_move_values[0]); | |
3236 | if (color == WHITE) | |
3237 | score = lower_bound + best_move_values[0]; | |
3238 | else | |
3239 | score = upper_bound - best_move_values[0]; | |
7eeb782e | 3240 | |
c150f57c AT |
3241 | return gtp_success("%3.1f", score); |
3242 | } | |
7eeb782e AT |
3243 | |
3244 | /************** | |
3245 | * statistics * | |
3246 | **************/ | |
3247 | ||
3248 | /* Function: Reset the count of life nodes. | |
3249 | * Arguments: none | |
3250 | * Fails: never | |
3251 | * Returns: nothing | |
3252 | * | |
3253 | * Note: This function is obsolete and only remains for backwards | |
3254 | * compatibility. | |
3255 | */ | |
3256 | static int | |
c150f57c | 3257 | gtp_reset_life_node_counter(char* s) |
7eeb782e | 3258 | { |
c150f57c AT |
3259 | UNUSED(s); |
3260 | return gtp_success(""); | |
7eeb782e AT |
3261 | } |
3262 | ||
7eeb782e AT |
3263 | /* Function: Retrieve the count of life nodes. |
3264 | * Arguments: none | |
3265 | * Fails: never | |
3266 | * Returns: number of life nodes | |
3267 | * | |
3268 | * Note: This function is obsolete and only remains for backwards | |
3269 | * compatibility. | |
3270 | */ | |
3271 | static int | |
c150f57c | 3272 | gtp_get_life_node_counter(char* s) |
7eeb782e | 3273 | { |
c150f57c AT |
3274 | UNUSED(s); |
3275 | return gtp_success("0"); | |
7eeb782e AT |
3276 | } |
3277 | ||
7eeb782e AT |
3278 | /* Function: Reset the count of owl nodes. |
3279 | * Arguments: none | |
3280 | * Fails: never | |
3281 | * Returns: nothing | |
3282 | */ | |
3283 | static int | |
c150f57c | 3284 | gtp_reset_owl_node_counter(char* s) |
7eeb782e | 3285 | { |
c150f57c AT |
3286 | UNUSED(s); |
3287 | reset_owl_node_counter(); | |
3288 | return gtp_success(""); | |
7eeb782e AT |
3289 | } |
3290 | ||
7eeb782e AT |
3291 | /* Function: Retrieve the count of owl nodes. |
3292 | * Arguments: none | |
3293 | * Fails: never | |
3294 | * Returns: number of owl nodes | |
3295 | */ | |
3296 | static int | |
c150f57c | 3297 | gtp_get_owl_node_counter(char* s) |
7eeb782e | 3298 | { |
c150f57c AT |
3299 | int nodes = get_owl_node_counter(); |
3300 | UNUSED(s); | |
3301 | return gtp_success("%d", nodes); | |
7eeb782e AT |
3302 | } |
3303 | ||
7eeb782e AT |
3304 | /* Function: Reset the count of reading nodes. |
3305 | * Arguments: none | |
3306 | * Fails: never | |
3307 | * Returns: nothing | |
3308 | */ | |
3309 | static int | |
c150f57c | 3310 | gtp_reset_reading_node_counter(char* s) |
7eeb782e | 3311 | { |
c150f57c AT |
3312 | UNUSED(s); |
3313 | reset_reading_node_counter(); | |
3314 | return gtp_success(""); | |
7eeb782e AT |
3315 | } |
3316 | ||
7eeb782e AT |
3317 | /* Function: Retrieve the count of reading nodes. |
3318 | * Arguments: none | |
3319 | * Fails: never | |
3320 | * Returns: number of reading nodes | |
3321 | */ | |
3322 | static int | |
c150f57c | 3323 | gtp_get_reading_node_counter(char* s) |
7eeb782e | 3324 | { |
c150f57c AT |
3325 | int nodes = get_reading_node_counter(); |
3326 | UNUSED(s); | |
3327 | return gtp_success("%d", nodes); | |
7eeb782e AT |
3328 | } |
3329 | ||
7eeb782e AT |
3330 | /* Function: Reset the count of trymoves/trykos. |
3331 | * Arguments: none | |
3332 | * Fails: never | |
3333 | * Returns: nothing | |
3334 | */ | |
3335 | static int | |
c150f57c | 3336 | gtp_reset_trymove_counter(char* s) |
7eeb782e | 3337 | { |
c150f57c AT |
3338 | UNUSED(s); |
3339 | reset_trymove_counter(); | |
3340 | return gtp_success(""); | |
7eeb782e AT |
3341 | } |
3342 | ||
7eeb782e AT |
3343 | /* Function: Retrieve the count of trymoves/trykos. |
3344 | * Arguments: none | |
3345 | * Fails: never | |
3346 | * Returns: number of trymoves/trykos | |
3347 | */ | |
3348 | static int | |
c150f57c | 3349 | gtp_get_trymove_counter(char* s) |
7eeb782e | 3350 | { |
c150f57c AT |
3351 | int nodes = get_trymove_counter(); |
3352 | UNUSED(s); | |
3353 | return gtp_success("%d", nodes); | |
7eeb782e AT |
3354 | } |
3355 | ||
7eeb782e AT |
3356 | /* Function: Reset the count of connection nodes. |
3357 | * Arguments: none | |
3358 | * Fails: never | |
3359 | * Returns: nothing | |
3360 | */ | |
3361 | static int | |
c150f57c | 3362 | gtp_reset_connection_node_counter(char* s) |
7eeb782e | 3363 | { |
c150f57c AT |
3364 | UNUSED(s); |
3365 | reset_connection_node_counter(); | |
3366 | return gtp_success(""); | |
7eeb782e AT |
3367 | } |
3368 | ||
7eeb782e AT |
3369 | /* Function: Retrieve the count of connection nodes. |
3370 | * Arguments: none | |
3371 | * Fails: never | |
3372 | * Returns: number of connection nodes | |
3373 | */ | |
3374 | static int | |
c150f57c | 3375 | gtp_get_connection_node_counter(char* s) |
7eeb782e | 3376 | { |
c150f57c AT |
3377 | int nodes = get_connection_node_counter(); |
3378 | UNUSED(s); | |
3379 | return gtp_success("%d", nodes); | |
7eeb782e AT |
3380 | } |
3381 | ||
7eeb782e AT |
3382 | /********* |
3383 | * debug * | |
3384 | *********/ | |
3385 | ||
7eeb782e AT |
3386 | /* Function: Test an eyeshape for inconsistent evaluations |
3387 | * Arguments: Eyeshape vertices | |
3388 | * Fails: Bad vertices | |
3389 | * Returns: Failure reports on stderr. | |
3390 | */ | |
3391 | static int | |
c150f57c | 3392 | gtp_test_eyeshape(char* s) |
7eeb782e | 3393 | { |
c150f57c AT |
3394 | int n; |
3395 | int i, j; | |
3396 | int eye_vertices[MAX_BOARD * MAX_BOARD]; | |
3397 | int eyesize = 0; | |
7eeb782e | 3398 | |
7eeb782e | 3399 | n = gtp_decode_coord(s, &i, &j); |
c150f57c AT |
3400 | while (n > 0) { |
3401 | eye_vertices[eyesize] = POS(i, j); | |
3402 | eyesize++; | |
3403 | s += n; | |
3404 | n = gtp_decode_coord(s, &i, &j); | |
3405 | } | |
7eeb782e | 3406 | |
c150f57c AT |
3407 | if (eyesize == 0) |
3408 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3409 | |
c150f57c | 3410 | test_eyeshape(eyesize, eye_vertices); |
7eeb782e | 3411 | |
c150f57c AT |
3412 | return gtp_success(""); |
3413 | } | |
7eeb782e AT |
3414 | |
3415 | /* Function: Compute an eyevalue and vital points for an eye graph | |
3416 | * Arguments: Eyeshape encoded in string | |
3417 | * Fails: Bad eyeshape, analysis failed | |
3418 | * Returns: Eyevalue, vital points | |
3419 | */ | |
3420 | static int | |
c150f57c | 3421 | gtp_analyze_eyegraph(char* s) |
7eeb782e | 3422 | { |
c150f57c AT |
3423 | struct eyevalue value; |
3424 | char analyzed_eyegraph[1024]; | |
3425 | int result = analyze_eyegraph(s, &value, analyzed_eyegraph); | |
7eeb782e | 3426 | |
c150f57c AT |
3427 | if (result == 0) |
3428 | return gtp_failure("failed to analyze"); | |
7eeb782e | 3429 | |
c150f57c | 3430 | return gtp_success("%s\n%s", eyevalue_to_string(&value), analyzed_eyegraph); |
7eeb782e AT |
3431 | } |
3432 | ||
7eeb782e AT |
3433 | /* Function: Returns elapsed CPU time in seconds. |
3434 | * Arguments: none | |
3435 | * Fails: never | |
3436 | * Returns: Total elapsed (user + system) CPU time in seconds. | |
3437 | */ | |
3438 | static int | |
c150f57c | 3439 | gtp_cputime(char* s) |
7eeb782e | 3440 | { |
c150f57c AT |
3441 | UNUSED(s); |
3442 | return gtp_success("%.3f", gg_cputime()); | |
7eeb782e AT |
3443 | } |
3444 | ||
7eeb782e AT |
3445 | /* Function: Write the position to stdout. |
3446 | * Arguments: none | |
3447 | * Fails: never | |
3448 | * Returns: nothing | |
3449 | * | |
3450 | * Status: GTP version 2 standard command. | |
3451 | */ | |
3452 | static int | |
c150f57c | 3453 | gtp_showboard(char* s) |
7eeb782e | 3454 | { |
c150f57c | 3455 | UNUSED(s); |
7eeb782e | 3456 | |
c150f57c AT |
3457 | gtp_start_response(GTP_SUCCESS); |
3458 | gtp_printf("\n"); | |
3459 | simple_showboard(gtp_output_file); | |
3460 | return gtp_finish_response(); | |
3461 | } | |
7eeb782e AT |
3462 | |
3463 | /* Function: Dump stack to stderr. | |
3464 | * Arguments: none | |
3465 | * Fails: never | |
3466 | * Returns: nothing | |
3467 | */ | |
3468 | static int | |
c150f57c | 3469 | gtp_dump_stack(char* s) |
7eeb782e | 3470 | { |
c150f57c AT |
3471 | UNUSED(s); |
3472 | dump_stack(); | |
3473 | return gtp_success(""); | |
7eeb782e AT |
3474 | } |
3475 | ||
3476 | /* Determine whether a string starts with a specific substring. */ | |
3477 | static int | |
c150f57c AT |
3478 | has_prefix(const char* s, const char* prefix) |
3479 | { | |
3480 | return strncmp(s, prefix, strlen(prefix)) == 0; | |
3481 | } | |
3482 | ||
3483 | static int | |
3484 | print_influence_data(struct influence_data* q, char* what_data) | |
3485 | { | |
3486 | float white_influence[BOARDMAX]; | |
3487 | float black_influence[BOARDMAX]; | |
3488 | float white_strength[BOARDMAX]; | |
3489 | float black_strength[BOARDMAX]; | |
3490 | float white_attenuation[BOARDMAX]; | |
3491 | float black_attenuation[BOARDMAX]; | |
3492 | float white_permeability[BOARDMAX]; | |
3493 | float black_permeability[BOARDMAX]; | |
3494 | float territory_value[BOARDMAX]; | |
3495 | int influence_regions[BOARDMAX]; | |
3496 | int non_territory[BOARDMAX]; | |
3497 | int m, n; | |
3498 | ||
3499 | float* float_pointer = NULL; | |
3500 | int* int_pointer = NULL; | |
3501 | ||
3502 | while (*what_data == ' ') | |
3503 | what_data++; | |
3504 | ||
3505 | get_influence(q, white_influence, black_influence, | |
3506 | white_strength, black_strength, | |
3507 | white_attenuation, black_attenuation, | |
3508 | white_permeability, black_permeability, | |
3509 | territory_value, influence_regions, non_territory); | |
3510 | ||
3511 | if (has_prefix(what_data, "white_influence")) | |
3512 | float_pointer = white_influence; | |
3513 | else if (has_prefix(what_data, "black_influence")) | |
3514 | float_pointer = black_influence; | |
3515 | else if (has_prefix(what_data, "white_strength")) | |
3516 | float_pointer = white_strength; | |
3517 | else if (has_prefix(what_data, "black_strength")) | |
3518 | float_pointer = black_strength; | |
3519 | else if (has_prefix(what_data, "white_attenuation")) | |
3520 | float_pointer = white_attenuation; | |
3521 | else if (has_prefix(what_data, "black_attenuation")) | |
3522 | float_pointer = black_attenuation; | |
3523 | else if (has_prefix(what_data, "white_permeability")) | |
3524 | float_pointer = white_permeability; | |
3525 | else if (has_prefix(what_data, "black_permeability")) | |
3526 | float_pointer = black_permeability; | |
3527 | else if (has_prefix(what_data, "territory_value")) | |
3528 | float_pointer = territory_value; | |
3529 | else if (has_prefix(what_data, "influence_regions")) | |
3530 | int_pointer = influence_regions; | |
3531 | else if (has_prefix(what_data, "non_territory")) | |
3532 | int_pointer = non_territory; | |
3533 | else | |
3534 | return gtp_failure("unknown influence data"); | |
3535 | ||
3536 | gtp_start_response(GTP_SUCCESS); | |
3537 | for (m = 0; m < board_size; m++) { | |
3538 | for (n = 0; n < board_size; n++) { | |
3539 | if (float_pointer) | |
3540 | gtp_printf("%6.2f ", float_pointer[POS(m, n)]); | |
3541 | else | |
3542 | gtp_printf("%2d ", int_pointer[POS(m, n)]); | |
3543 | } | |
3544 | gtp_printf("\n"); | |
7eeb782e | 3545 | } |
c150f57c AT |
3546 | |
3547 | /* We already have one newline and thus can't use gtp_finish_response(). */ | |
7eeb782e | 3548 | gtp_printf("\n"); |
c150f57c | 3549 | return GTP_OK; |
7eeb782e AT |
3550 | } |
3551 | ||
3552 | /* Function: Return information about the initial influence function. | |
3553 | * Arguments: color to move, what information | |
3554 | * Fails: never | |
3555 | * Returns: Influence data formatted like: | |
3556 | * | |
3557 | * 0.51 1.34 3.20 6.60 9.09 8.06 1.96 0.00 0.00 | |
3558 | * 0.45 1.65 4.92 12.19 17.47 15.92 4.03 0.00 0.00 | |
3559 | * . | |
3560 | * . | |
3561 | * . | |
3562 | * 0.00 0.00 0.00 0.00 0.00 100.00 75.53 41.47 23.41 | |
3563 | * | |
3564 | * The available choices of information are: | |
3565 | * | |
3566 | * white_influence (float) | |
3567 | * black_influence (float) | |
3568 | * white_strength (float) | |
3569 | * black_strength (float) | |
3570 | * white_attenuation (float) | |
3571 | * black_attenuation (float) | |
3572 | * white_permeability (float) | |
3573 | * black_permeability (float) | |
3574 | * territory_value (float) | |
3575 | * influence_regions (int) | |
3576 | * non_territory (int) | |
3577 | * | |
3578 | * The encoding of influence_regions is as follows: | |
3579 | * 4 white stone | |
3580 | * 3 white territory | |
3581 | * 2 white moyo | |
3582 | * 1 white area | |
3583 | * 0 neutral | |
3584 | * -1 black area | |
3585 | * -2 black moyo | |
3586 | * -3 black territory | |
3587 | * -4 black stone | |
3588 | */ | |
3589 | static int | |
c150f57c | 3590 | gtp_initial_influence(char* s) |
7eeb782e | 3591 | { |
c150f57c AT |
3592 | int color; |
3593 | struct influence_data* q; | |
3594 | int n; | |
7eeb782e | 3595 | |
c150f57c AT |
3596 | n = gtp_decode_color(s, &color); |
3597 | if (n == 0) | |
3598 | return gtp_failure("invalid color"); | |
7eeb782e | 3599 | |
c150f57c | 3600 | q = INITIAL_INFLUENCE(color); |
7eeb782e | 3601 | |
c150f57c | 3602 | silent_examine_position(EXAMINE_ALL); |
7eeb782e | 3603 | |
c150f57c AT |
3604 | return print_influence_data(q, s + n); |
3605 | } | |
7eeb782e AT |
3606 | |
3607 | /* Function: Return information about the influence function after a move. | |
3608 | * Arguments: move, what information | |
3609 | * Fails: never | |
3610 | * Returns: Influence data formatted like for initial_influence. | |
3611 | */ | |
3612 | static int | |
c150f57c | 3613 | gtp_move_influence(char* s) |
7eeb782e | 3614 | { |
c150f57c AT |
3615 | int color; |
3616 | int i, j; | |
3617 | int n; | |
7eeb782e | 3618 | |
c150f57c AT |
3619 | n = gtp_decode_move(s, &color, &i, &j); |
3620 | if (n == 0) | |
3621 | return gtp_failure("invalid move"); | |
7eeb782e | 3622 | |
c150f57c | 3623 | prepare_move_influence_debugging(POS(i, j), color); |
7eeb782e | 3624 | |
c150f57c AT |
3625 | return print_influence_data(&move_influence, s + n); |
3626 | } | |
7eeb782e AT |
3627 | |
3628 | /* Function: List probabilities of each move being played (when non-zero). | |
3629 | * If no previous genmove command has been issued, the result | |
3630 | * of this command will be meaningless. | |
3631 | * Arguments: none | |
3632 | * Fails: never | |
3633 | * Returns: Move, probabilty pairs, one per row. | |
3634 | */ | |
3635 | static int | |
c150f57c | 3636 | gtp_move_probabilities(char* s) |
7eeb782e | 3637 | { |
c150f57c AT |
3638 | float probabilities[BOARDMAX]; |
3639 | int pos; | |
3640 | int any_moves_printed = 0; | |
7eeb782e | 3641 | |
c150f57c | 3642 | UNUSED(s); |
7eeb782e | 3643 | |
c150f57c | 3644 | compute_move_probabilities(probabilities); |
7eeb782e | 3645 | |
c150f57c AT |
3646 | gtp_start_response(GTP_SUCCESS); |
3647 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
3648 | if (ON_BOARD(pos) && probabilities[pos] != 0.0) { | |
3649 | gtp_mprintf("%m ", I(pos), J(pos)); | |
3650 | gtp_printf("%.4f\n", probabilities[pos]); | |
3651 | any_moves_printed = 1; | |
3652 | } | |
7eeb782e | 3653 | } |
7eeb782e | 3654 | |
c150f57c AT |
3655 | if (!any_moves_printed) |
3656 | gtp_printf("\n"); | |
7eeb782e | 3657 | gtp_printf("\n"); |
7eeb782e | 3658 | |
c150f57c | 3659 | return GTP_OK; |
7eeb782e AT |
3660 | } |
3661 | ||
7eeb782e AT |
3662 | /* Function: Return the number of bits of uncertainty in the move. |
3663 | * If no previous genmove command has been issued, the result | |
3664 | * of this command will be meaningless. | |
3665 | * Arguments: none | |
3666 | * Fails: never | |
3667 | * Returns: bits of uncertainty | |
3668 | */ | |
3669 | static int | |
c150f57c | 3670 | gtp_move_uncertainty(char* s) |
7eeb782e | 3671 | { |
c150f57c AT |
3672 | float probabilities[BOARDMAX]; |
3673 | int pos; | |
3674 | double uncertainty = 0.0; | |
7eeb782e | 3675 | |
c150f57c | 3676 | UNUSED(s); |
7eeb782e | 3677 | |
c150f57c | 3678 | compute_move_probabilities(probabilities); |
7eeb782e | 3679 | |
c150f57c AT |
3680 | gtp_start_response(GTP_SUCCESS); |
3681 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
3682 | if (ON_BOARD(pos) && probabilities[pos] > 0.0) { | |
3683 | /* Shannon's formula */ | |
3684 | uncertainty += -1 * ((double)probabilities[pos]) * log((double)probabilities[pos]) / log(2.0); | |
3685 | } | |
7eeb782e | 3686 | } |
7eeb782e | 3687 | |
c150f57c | 3688 | gtp_printf("%.4f\n\n", uncertainty); |
7eeb782e | 3689 | |
c150f57c | 3690 | return GTP_OK; |
7eeb782e AT |
3691 | } |
3692 | ||
7eeb782e AT |
3693 | /* Function: Return information about the followup influence after a move. |
3694 | * Arguments: move, what information | |
3695 | * Fails: never | |
3696 | * Returns: Influence data formatted like for initial_influence. | |
3697 | */ | |
3698 | static int | |
c150f57c | 3699 | gtp_followup_influence(char* s) |
7eeb782e | 3700 | { |
c150f57c AT |
3701 | int color; |
3702 | int i, j; | |
3703 | int n; | |
7eeb782e | 3704 | |
c150f57c AT |
3705 | n = gtp_decode_move(s, &color, &i, &j); |
3706 | if (n == 0) | |
3707 | return gtp_failure("invalid move"); | |
7eeb782e | 3708 | |
c150f57c | 3709 | prepare_move_influence_debugging(POS(i, j), color); |
7eeb782e | 3710 | |
c150f57c AT |
3711 | return print_influence_data(&followup_influence, s + n); |
3712 | } | |
7eeb782e AT |
3713 | |
3714 | /* Function: Return the information in the worm data structure. | |
3715 | * Arguments: optional vertex | |
3716 | * Fails: never | |
3717 | * Returns: Worm data formatted like: | |
3718 | * | |
3719 | * A19: | |
3720 | * color black | |
3721 | * size 10 | |
3722 | * effective_size 17.83 | |
3723 | * origin A19 | |
3724 | * liberties 8 | |
3725 | * liberties2 15 | |
3726 | * liberties3 10 | |
3727 | * liberties4 8 | |
3728 | * attack PASS | |
3729 | * attack_code 0 | |
3730 | * lunch B19 | |
3731 | * defend PASS | |
3732 | * defend_code 0 | |
3733 | * cutstone 2 | |
3734 | * cutstone2 0 | |
3735 | * genus 0 | |
3736 | * inessential 0 | |
3737 | * B19: | |
3738 | * color white | |
3739 | * . | |
3740 | * . | |
3741 | * . | |
3742 | * inessential 0 | |
3743 | * C19: | |
3744 | * ... | |
3745 | * | |
3746 | * If an intersection is specified, only data for this one will be returned. | |
3747 | */ | |
3748 | static int | |
c150f57c AT |
3749 | gtp_worm_data(char* s) |
3750 | { | |
3751 | int i = -1; | |
3752 | int j = -1; | |
3753 | int m, n; | |
3754 | ||
3755 | if (sscanf(s, "%*c") >= 0 && !gtp_decode_coord(s, &i, &j)) | |
3756 | return gtp_failure("invalid color or coordinate"); | |
3757 | ||
3758 | silent_examine_position(EXAMINE_WORMS); | |
3759 | ||
3760 | gtp_start_response(GTP_SUCCESS); | |
3761 | ||
3762 | for (m = 0; m < board_size; m++) | |
3763 | for (n = 0; n < board_size; n++) | |
3764 | if (i == -1 || (m == i && n == j)) { | |
3765 | struct worm_data* w = &worm[POS(m, n)]; | |
3766 | gtp_print_vertex(m, n); | |
3767 | gtp_printf(":\n"); | |
3768 | gtp_mprintf("origin %m\n", I(w->origin), J(w->origin)); | |
3769 | gtp_mprintf("color %C\n", w->color); | |
3770 | gtp_printf("size %d\n", w->size); | |
3771 | gtp_printf("effective_size %.2f\n", w->effective_size); | |
3772 | gtp_printf("liberties %d\n", w->liberties); | |
3773 | gtp_printf("liberties2 %d\n", w->liberties2); | |
3774 | gtp_printf("liberties3 %d\n", w->liberties3); | |
3775 | gtp_printf("liberties4 %d\n", w->liberties4); | |
3776 | gtp_printf("attack_code %d\n", w->attack_codes[0]); | |
3777 | gtp_mprintf("attack_point %m\n", | |
3778 | I(w->attack_points[0]), J(w->attack_points[0])); | |
3779 | gtp_printf("defense_code %d\n", w->defense_codes[0]); | |
3780 | gtp_mprintf("defense_point %m\n", | |
3781 | I(w->defense_points[0]), J(w->defense_points[0])); | |
3782 | gtp_mprintf("lunch %m\n", | |
3783 | I(w->lunch), J(w->lunch)); | |
3784 | gtp_printf("cutstone %d\n", w->cutstone); | |
3785 | gtp_printf("cutstone2 %d\n", w->cutstone2); | |
3786 | gtp_printf("genus %d\n", w->genus); | |
3787 | gtp_printf("inessential %d\n", w->inessential); | |
3788 | gtp_printf("invincible %d\n", w->invincible); | |
3789 | gtp_printf("unconditional_status %s\n", | |
3790 | status_to_string(w->unconditional_status)); | |
3791 | } | |
3792 | ||
3793 | gtp_printf("\n"); | |
3794 | return GTP_OK; | |
7eeb782e AT |
3795 | } |
3796 | ||
3797 | /* Function: List the stones of a worm | |
3798 | * Arguments: the location, "BLACK" or "WHITE" | |
3799 | * Fails: if called on an empty or off-board location | |
3800 | * Returns: list of stones | |
3801 | */ | |
3802 | static int | |
c150f57c AT |
3803 | gtp_worm_stones(char* s) |
3804 | { | |
3805 | int i = -1; | |
3806 | int j = -1; | |
3807 | int color = EMPTY; | |
3808 | int m, n; | |
3809 | int u, v; | |
3810 | int board_empty = 1; | |
3811 | ||
3812 | if (sscanf(s, "%*c") >= 0) { | |
3813 | if (!gtp_decode_coord(s, &i, &j) | |
3814 | && !gtp_decode_color(s, &color)) | |
3815 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3816 | } |
7eeb782e | 3817 | |
c150f57c AT |
3818 | if (BOARD(i, j) == EMPTY) |
3819 | return gtp_failure("worm_stones called on an empty vertex"); | |
7eeb782e | 3820 | |
c150f57c AT |
3821 | gtp_start_response(GTP_SUCCESS); |
3822 | ||
3823 | for (u = 0; u < board_size; u++) | |
3824 | for (v = 0; v < board_size; v++) { | |
3825 | if (BOARD(u, v) == EMPTY | |
3826 | || (color != EMPTY && BOARD(u, v) != color)) | |
3827 | continue; | |
3828 | board_empty = 0; | |
3829 | if (find_origin(POS(u, v)) != POS(u, v)) | |
3830 | continue; | |
3831 | if (ON_BOARD2(i, j) | |
3832 | && !same_string(POS(u, v), POS(i, j))) | |
3833 | continue; | |
3834 | for (m = 0; m < board_size; m++) | |
3835 | for (n = 0; n < board_size; n++) | |
3836 | if (BOARD(m, n) != EMPTY | |
3837 | && same_string(POS(m, n), POS(u, v))) | |
3838 | gtp_mprintf("%m ", m, n); | |
3839 | gtp_printf("\n"); | |
3840 | } | |
3841 | ||
3842 | if (board_empty) | |
3843 | gtp_printf("\n"); /* in case no stones have been printed */ | |
3844 | gtp_printf("\n"); | |
3845 | return GTP_OK; | |
3846 | } | |
7eeb782e AT |
3847 | |
3848 | /* Function: Return the cutstone field in the worm data structure. | |
3849 | * Arguments: non-empty vertex | |
3850 | * Fails: never | |
3851 | * Returns: cutstone | |
3852 | */ | |
3853 | static int | |
c150f57c | 3854 | gtp_worm_cutstone(char* s) |
7eeb782e AT |
3855 | { |
3856 | ||
c150f57c AT |
3857 | int i, j; |
3858 | if (!gtp_decode_coord(s, &i, &j)) | |
3859 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3860 | |
c150f57c AT |
3861 | if (BOARD(i, j) == EMPTY) |
3862 | return gtp_failure("vertex must not be empty"); | |
7eeb782e | 3863 | |
c150f57c | 3864 | silent_examine_position(EXAMINE_WORMS); |
7eeb782e | 3865 | |
c150f57c | 3866 | return gtp_success(" %d", worm[POS(i, j)].cutstone); |
7eeb782e AT |
3867 | } |
3868 | ||
3869 | /* Function: Return the information in the dragon data structure. | |
3870 | * Arguments: optional intersection | |
3871 | * Fails: never | |
3872 | * Returns: Dragon data formatted in the corresponding way to gtp_worm_data. | |
3873 | */ | |
3874 | static int | |
c150f57c | 3875 | gtp_dragon_data(char* s) |
7eeb782e | 3876 | { |
c150f57c AT |
3877 | int i = -1; |
3878 | int j = -1; | |
3879 | int m, n; | |
3880 | int newline_needed = 0; | |
7eeb782e | 3881 | |
c150f57c AT |
3882 | if (sscanf(s, "%*c") >= 0 && !gtp_decode_coord(s, &i, &j)) |
3883 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3884 | |
c150f57c AT |
3885 | if (stackp > 0) |
3886 | return gtp_failure("dragon data unavailable when stackp > 0"); | |
7eeb782e | 3887 | |
c150f57c | 3888 | silent_examine_position(FULL_EXAMINE_DRAGONS); |
7eeb782e | 3889 | |
c150f57c | 3890 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 3891 | |
c150f57c AT |
3892 | if (ON_BOARD2(i, j) && BOARD(i, j) == EMPTY) |
3893 | gtp_mprintf("%m empty\n", i, j); | |
3894 | else { | |
3895 | newline_needed = 1; | |
3896 | for (m = 0; m < board_size; m++) | |
3897 | for (n = 0; n < board_size; n++) | |
3898 | if ((m == i && n == j) | |
3899 | || (i == -1 | |
3900 | && BOARD(m, n) != EMPTY | |
3901 | && dragon[POS(m, n)].origin == POS(m, n))) { | |
3902 | gtp_print_vertex(m, n); | |
3903 | gtp_printf(":\n"); | |
3904 | report_dragon(gtp_output_file, POS(m, n)); | |
3905 | newline_needed = 0; | |
3906 | } | |
3907 | } | |
3908 | if (newline_needed) | |
3909 | gtp_printf("\n"); | |
7eeb782e | 3910 | gtp_printf("\n"); |
c150f57c | 3911 | return GTP_OK; |
7eeb782e AT |
3912 | } |
3913 | ||
3914 | /* Function: List the stones of a dragon | |
3915 | * Arguments: the location | |
3916 | * Fails: if called on an empty or off-board location | |
3917 | * Returns: list of stones | |
3918 | */ | |
3919 | static int | |
c150f57c AT |
3920 | gtp_dragon_stones(char* s) |
3921 | { | |
3922 | int i = -1; | |
3923 | int j = -1; | |
3924 | int color = EMPTY; | |
3925 | int m, n; | |
3926 | int u, v; | |
3927 | ||
3928 | if (sscanf(s, "%*c") >= 0) { | |
3929 | if (!gtp_decode_coord(s, &i, &j) | |
3930 | && !gtp_decode_color(s, &color)) | |
3931 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 3932 | } |
c150f57c AT |
3933 | |
3934 | if (BOARD(i, j) == EMPTY) | |
3935 | return gtp_failure("dragon_stones called on an empty vertex"); | |
3936 | ||
3937 | silent_examine_position(EXAMINE_DRAGONS); | |
3938 | ||
3939 | gtp_start_response(GTP_SUCCESS); | |
3940 | ||
3941 | for (u = 0; u < board_size; u++) | |
3942 | for (v = 0; v < board_size; v++) { | |
3943 | if (BOARD(u, v) == EMPTY | |
3944 | || (color != EMPTY && BOARD(u, v) != color)) | |
3945 | continue; | |
3946 | if (dragon[POS(u, v)].origin != POS(u, v)) | |
3947 | continue; | |
3948 | if (ON_BOARD2(i, j) && dragon[POS(i, j)].origin != POS(u, v)) | |
3949 | continue; | |
3950 | for (m = 0; m < board_size; m++) | |
3951 | for (n = 0; n < board_size; n++) | |
3952 | if (dragon[POS(m, n)].origin == POS(u, v)) | |
3953 | gtp_mprintf("%m ", m, n); | |
3954 | gtp_printf("\n"); | |
3955 | } | |
3956 | ||
3957 | gtp_printf("\n"); | |
3958 | return GTP_OK; | |
7eeb782e AT |
3959 | } |
3960 | ||
3961 | /* Function: Return the information in the eye data structure. | |
3962 | * Arguments: color, vertex | |
3963 | * Fails: never | |
3964 | * Returns: eye data fields and values, one pair per row | |
3965 | */ | |
3966 | static int | |
c150f57c | 3967 | gtp_eye_data(char* s) |
7eeb782e | 3968 | { |
c150f57c AT |
3969 | int color = EMPTY; |
3970 | int i = -1; | |
3971 | int j = -1; | |
3972 | struct eye_data* e; | |
7eeb782e | 3973 | |
c150f57c AT |
3974 | if (!gtp_decode_move(s, &color, &i, &j)) |
3975 | return gtp_failure("invalid color or coordinate"); | |
7eeb782e | 3976 | |
c150f57c AT |
3977 | if (stackp > 0) |
3978 | return gtp_failure("eye data unavailable when stackp > 0"); | |
7eeb782e | 3979 | |
c150f57c | 3980 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 3981 | |
c150f57c | 3982 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 3983 | |
c150f57c AT |
3984 | if (color == BLACK) |
3985 | e = &black_eye[POS(i, j)]; | |
3986 | else | |
3987 | e = &white_eye[POS(i, j)]; | |
7eeb782e | 3988 | |
c150f57c AT |
3989 | gtp_mprintf("origin %m\n", I(e->origin), J(e->origin)); |
3990 | gtp_mprintf("color %C\n", e->color); | |
3991 | gtp_printf("esize %d\n", e->esize); | |
3992 | gtp_printf("msize %d\n", e->msize); | |
3993 | gtp_printf("value %s\n", eyevalue_to_string(&e->value)); | |
3994 | gtp_printf("marginal %d\n", e->marginal); | |
3995 | gtp_printf("neighbors %d\n", e->neighbors); | |
3996 | gtp_printf("marginal_neighbors %d\n", e->marginal_neighbors); | |
3997 | ||
3998 | gtp_printf("\n"); | |
3999 | return GTP_OK; | |
4000 | } | |
7eeb782e AT |
4001 | |
4002 | /* Function: Return the information in the half eye data structure. | |
4003 | * Arguments: vertex | |
4004 | * Fails: never | |
4005 | * Returns: half eye data fields and values, one pair per row | |
4006 | */ | |
4007 | static int | |
c150f57c | 4008 | gtp_half_eye_data(char* s) |
7eeb782e | 4009 | { |
c150f57c AT |
4010 | int i = -1; |
4011 | int j = -1; | |
4012 | struct half_eye_data* h; | |
4013 | int k; | |
7eeb782e | 4014 | |
c150f57c AT |
4015 | if (!gtp_decode_coord(s, &i, &j)) |
4016 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4017 | |
c150f57c AT |
4018 | if (stackp > 0) |
4019 | return gtp_failure("half eye data unavailable when stackp > 0"); | |
7eeb782e | 4020 | |
c150f57c | 4021 | silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL); |
7eeb782e | 4022 | |
c150f57c | 4023 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 4024 | |
c150f57c | 4025 | h = &half_eye[POS(i, j)]; |
7eeb782e | 4026 | |
c150f57c AT |
4027 | gtp_printf("value %.2f\n", h->value); |
4028 | if (h->type == HALF_EYE) | |
4029 | gtp_printf("type HALF_EYE\n"); | |
4030 | else if (h->type == FALSE_EYE) | |
4031 | gtp_printf("type FALSE_EYE\n"); | |
4032 | else | |
4033 | gtp_printf("type %d\n", h->type); | |
4034 | gtp_printf("num_attacks %d\n", h->num_attacks); | |
4035 | for (k = 0; k < h->num_attacks; k++) | |
4036 | gtp_mprintf("attack_point[%d] %m\n", k, I(h->attack_point[k]), | |
4037 | J(h->attack_point[k])); | |
4038 | gtp_printf("num_defenses %d\n", h->num_defenses); | |
4039 | for (k = 0; k < h->num_defenses; k++) | |
4040 | gtp_mprintf("defense_point[%d] %m\n", k, I(h->defense_point[k]), | |
4041 | J(h->defense_point[k])); | |
4042 | ||
4043 | gtp_printf("\n"); | |
4044 | return GTP_OK; | |
4045 | } | |
7eeb782e AT |
4046 | |
4047 | static SGFTree gtp_sgftree; | |
4048 | ||
4049 | /* Function: Start storing moves executed during reading in an sgf | |
4050 | * tree in memory. | |
4051 | * Arguments: none | |
4052 | * Fails: never | |
4053 | * Returns: nothing | |
4054 | * | |
4055 | * Warning: You had better know what you're doing if you try to use this | |
4056 | * command. | |
4057 | */ | |
4058 | static int | |
c150f57c | 4059 | gtp_start_sgftrace(char* s) |
7eeb782e | 4060 | { |
c150f57c AT |
4061 | UNUSED(s); |
4062 | sgffile_begindump(>p_sgftree); | |
4063 | count_variations = 1; | |
4064 | return gtp_success(""); | |
7eeb782e AT |
4065 | } |
4066 | ||
7eeb782e AT |
4067 | /* Function: Finish storing moves in an sgf tree and write it to file. |
4068 | * Arguments: filename | |
4069 | * Fails: never | |
4070 | * Returns: nothing | |
4071 | * | |
4072 | * Warning: You had better know what you're doing if you try to use this | |
4073 | * command. | |
4074 | */ | |
4075 | static int | |
c150f57c | 4076 | gtp_finish_sgftrace(char* s) |
7eeb782e | 4077 | { |
c150f57c AT |
4078 | char filename[GTP_BUFSIZE]; |
4079 | int nread; | |
7eeb782e | 4080 | |
c150f57c AT |
4081 | nread = sscanf(s, "%s", filename); |
4082 | if (nread < 1) | |
4083 | return gtp_failure("missing filename"); | |
7eeb782e | 4084 | |
c150f57c AT |
4085 | sgffile_enddump(filename); |
4086 | count_variations = 0; | |
4087 | return gtp_success(""); | |
4088 | } | |
7eeb782e AT |
4089 | |
4090 | /* Function: Dump the current position as a static sgf file to filename, | |
4091 | * or as output if filename is missing or "-" | |
4092 | * Arguments: optional filename | |
4093 | * Fails: never | |
4094 | * Returns: nothing if filename, otherwise the sgf | |
4095 | */ | |
4096 | static int | |
c150f57c | 4097 | gtp_printsgf(char* s) |
7eeb782e | 4098 | { |
c150f57c AT |
4099 | char filename[GTP_BUFSIZE]; |
4100 | int nread; | |
4101 | int next; | |
7eeb782e | 4102 | |
c150f57c AT |
4103 | if (get_last_player() == EMPTY) |
4104 | next = BLACK; | |
4105 | else | |
4106 | next = OTHER_COLOR(get_last_player()); | |
7eeb782e | 4107 | |
c150f57c | 4108 | nread = sscanf(s, "%s", filename); |
7eeb782e | 4109 | |
c150f57c AT |
4110 | if (nread < 1) |
4111 | gg_snprintf(filename, GTP_BUFSIZE, "%s", "-"); | |
7eeb782e | 4112 | |
c150f57c AT |
4113 | if (strcmp(filename, "-") == 0) { |
4114 | gtp_start_response(GTP_SUCCESS); | |
4115 | sgffile_printsgf(next, filename); | |
4116 | gtp_printf("\n"); | |
4117 | return GTP_OK; | |
4118 | } else { | |
4119 | sgffile_printsgf(next, filename); | |
4120 | return gtp_success(""); | |
4121 | } | |
4122 | } | |
7eeb782e AT |
4123 | |
4124 | /* Function: Tune the parameters for the move ordering in the tactical | |
4125 | * reading. | |
4126 | * Arguments: MOVE_ORDERING_PARAMETERS integers | |
4127 | * Fails: incorrect arguments | |
4128 | * Returns: nothing | |
4129 | */ | |
4130 | static int | |
c150f57c | 4131 | gtp_tune_move_ordering(char* s) |
7eeb782e | 4132 | { |
c150f57c AT |
4133 | int params[MOVE_ORDERING_PARAMETERS]; |
4134 | int k; | |
4135 | int p; | |
4136 | int n; | |
7eeb782e | 4137 | |
c150f57c AT |
4138 | for (k = 0; k < MOVE_ORDERING_PARAMETERS; k++) { |
4139 | if (sscanf(s, "%d%n", &p, &n) == 0) | |
4140 | return gtp_failure("incorrect arguments, expected %d integers", | |
4141 | MOVE_ORDERING_PARAMETERS); | |
4142 | params[k] = p; | |
4143 | s += n; | |
4144 | } | |
7eeb782e | 4145 | |
c150f57c AT |
4146 | tune_move_ordering(params); |
4147 | return gtp_success(""); | |
7eeb782e AT |
4148 | } |
4149 | ||
4150 | /* Function: Echo the parameter | |
4151 | * Arguments: string | |
4152 | * Fails: never | |
4153 | * Returns: nothing | |
4154 | */ | |
4155 | static int | |
c150f57c | 4156 | gtp_echo(char* s) |
7eeb782e | 4157 | { |
c150f57c | 4158 | return gtp_success("%s", s); |
7eeb782e AT |
4159 | } |
4160 | ||
7eeb782e AT |
4161 | /* Function: Echo the parameter to stdout AND stderr |
4162 | * Arguments: string | |
4163 | * Fails: never | |
4164 | * Returns: nothing | |
4165 | */ | |
4166 | static int | |
c150f57c | 4167 | gtp_echo_err(char* s) |
7eeb782e | 4168 | { |
c150f57c AT |
4169 | fprintf(stderr, "%s", s); |
4170 | fflush(gtp_output_file); | |
4171 | fflush(stderr); | |
4172 | return gtp_success("%s", s); | |
7eeb782e AT |
4173 | } |
4174 | ||
4175 | /* Function: List all known commands | |
4176 | * Arguments: none | |
4177 | * Fails: never | |
4178 | * Returns: list of known commands, one per line | |
4179 | * | |
4180 | * Status: GTP version 2 standard command. | |
4181 | */ | |
4182 | static int | |
c150f57c | 4183 | gtp_list_commands(char* s) |
7eeb782e | 4184 | { |
c150f57c AT |
4185 | int k; |
4186 | UNUSED(s); | |
7eeb782e | 4187 | |
c150f57c | 4188 | gtp_start_response(GTP_SUCCESS); |
7eeb782e | 4189 | |
c150f57c AT |
4190 | for (k = 0; commands[k].name != NULL; k++) |
4191 | gtp_printf("%s\n", commands[k].name); | |
7eeb782e | 4192 | |
c150f57c AT |
4193 | gtp_printf("\n"); |
4194 | return GTP_OK; | |
7eeb782e AT |
4195 | } |
4196 | ||
7eeb782e AT |
4197 | /* Function: Tell whether a command is known. |
4198 | * Arguments: command name | |
4199 | * Fails: never | |
4200 | * Returns: "true" if command exists, "false" if not | |
4201 | * | |
4202 | * Status: GTP version 2 standard command. | |
4203 | */ | |
4204 | static int | |
c150f57c | 4205 | gtp_known_command(char* s) |
7eeb782e | 4206 | { |
c150f57c AT |
4207 | int k; |
4208 | char command[GTP_BUFSIZE]; | |
7eeb782e | 4209 | |
c150f57c AT |
4210 | if (sscanf(s, "%s", command) == 1) { |
4211 | for (k = 0; commands[k].name != NULL; k++) | |
4212 | if (strcmp(command, commands[k].name) == 0) | |
4213 | return gtp_success("true"); | |
4214 | } | |
7eeb782e | 4215 | |
c150f57c | 4216 | return gtp_success("false"); |
7eeb782e AT |
4217 | } |
4218 | ||
7eeb782e AT |
4219 | /* Function: Turn uncertainty reports from owl_attack |
4220 | * and owl_defend on or off. | |
4221 | * Arguments: "on" or "off" | |
4222 | * Fails: invalid argument | |
4223 | * Returns: nothing | |
4224 | */ | |
4225 | static int | |
c150f57c | 4226 | gtp_report_uncertainty(char* s) |
7eeb782e | 4227 | { |
c150f57c AT |
4228 | if (!strncmp(s, "on", 2)) { |
4229 | report_uncertainty = 1; | |
4230 | return gtp_success(""); | |
4231 | } | |
4232 | if (!strncmp(s, "off", 3)) { | |
4233 | report_uncertainty = 0; | |
4234 | return gtp_success(""); | |
4235 | } | |
4236 | return gtp_failure("invalid argument"); | |
7eeb782e | 4237 | } |
7eeb782e AT |
4238 | |
4239 | static void | |
4240 | gtp_print_code(int c) | |
4241 | { | |
c150f57c AT |
4242 | static int conversion[6] = { |
4243 | 0, /* LOSE */ | |
4244 | 3, /* KO_B */ | |
4245 | 5, /* LOSS */ | |
4246 | 4, /* GAIN */ | |
4247 | 2, /* KO_A */ | |
4248 | 1, /* WIN */ | |
4249 | }; | |
4250 | gtp_printf("%d", conversion[c]); | |
7eeb782e AT |
4251 | } |
4252 | ||
4253 | static void | |
c150f57c | 4254 | gtp_print_vertices2(int n, int* moves) |
7eeb782e | 4255 | { |
c150f57c AT |
4256 | int movei[MAX_BOARD * MAX_BOARD]; |
4257 | int movej[MAX_BOARD * MAX_BOARD]; | |
4258 | int k; | |
4259 | ||
4260 | for (k = 0; k < n; k++) { | |
4261 | movei[k] = I(moves[k]); | |
4262 | movej[k] = J(moves[k]); | |
4263 | } | |
7eeb782e | 4264 | |
c150f57c | 4265 | gtp_print_vertices(n, movei, movej); |
7eeb782e AT |
4266 | } |
4267 | ||
4268 | /************* | |
4269 | * transform * | |
4270 | *************/ | |
4271 | ||
4272 | static void | |
c150f57c | 4273 | rotate_on_input(int ai, int aj, int* bi, int* bj) |
7eeb782e | 4274 | { |
c150f57c | 4275 | rotate(ai, aj, bi, bj, board_size, gtp_orientation); |
7eeb782e AT |
4276 | } |
4277 | ||
4278 | static void | |
c150f57c | 4279 | rotate_on_output(int ai, int aj, int* bi, int* bj) |
7eeb782e | 4280 | { |
c150f57c | 4281 | inv_rotate(ai, aj, bi, bj, board_size, gtp_orientation); |
7eeb782e AT |
4282 | } |
4283 | ||
7eeb782e AT |
4284 | /*************** |
4285 | * random seed * | |
4286 | ***************/ | |
4287 | ||
4288 | /* Function: Get the random seed | |
4289 | * Arguments: none | |
4290 | * Fails: never | |
4291 | * Returns: random seed | |
4292 | */ | |
4293 | static int | |
c150f57c | 4294 | gtp_get_random_seed(char* s) |
7eeb782e | 4295 | { |
c150f57c AT |
4296 | UNUSED(s); |
4297 | return gtp_success("%d", get_random_seed()); | |
7eeb782e AT |
4298 | } |
4299 | ||
4300 | /* Function: Set the random seed | |
4301 | * Arguments: integer | |
4302 | * Fails: invalid data | |
4303 | * Returns: nothing | |
4304 | */ | |
4305 | static int | |
c150f57c | 4306 | gtp_set_random_seed(char* s) |
7eeb782e | 4307 | { |
c150f57c AT |
4308 | int seed; |
4309 | if (sscanf(s, "%d", &seed) < 1) | |
4310 | return gtp_failure("invalid seed"); | |
7eeb782e | 4311 | |
c150f57c AT |
4312 | set_random_seed(seed); |
4313 | return gtp_success(""); | |
4314 | } | |
7eeb782e AT |
4315 | |
4316 | /* Function: Advance the random seed by a number of games. | |
4317 | * Arguments: integer | |
4318 | * Fails: invalid data | |
4319 | * Returns: New random seed. | |
4320 | */ | |
4321 | static int | |
c150f57c | 4322 | gtp_advance_random_seed(char* s) |
7eeb782e | 4323 | { |
c150f57c AT |
4324 | int i; |
4325 | int games; | |
4326 | if (sscanf(s, "%d", &games) < 1 | |
4327 | || games < 0) | |
4328 | return gtp_failure("invalid number of games"); | |
4329 | ||
4330 | for (i = 0; i < games; i++) | |
4331 | update_random_seed(); | |
7eeb782e | 4332 | |
c150f57c | 4333 | return gtp_success("%d", get_random_seed()); |
7eeb782e AT |
4334 | } |
4335 | ||
4336 | /*************** | |
4337 | * surrounding * | |
4338 | ***************/ | |
4339 | ||
4340 | /* Function: Determine if a dragon is surrounded | |
4341 | * Arguments: vertex (dragon) | |
4342 | * Fails: invalid vertex, empty vertex | |
4343 | * Returns: 1 if surrounded, 2 if weakly surrounded, 0 if not | |
4344 | */ | |
4345 | static int | |
c150f57c | 4346 | gtp_is_surrounded(char* s) |
7eeb782e | 4347 | { |
c150f57c AT |
4348 | int i, j; |
4349 | int n; | |
7eeb782e | 4350 | |
c150f57c AT |
4351 | n = gtp_decode_coord(s, &i, &j); |
4352 | if (n == 0) | |
4353 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4354 | |
c150f57c AT |
4355 | if (BOARD(i, j) == EMPTY) |
4356 | return gtp_failure("dragon vertex must be nonempty"); | |
7eeb782e | 4357 | |
c150f57c AT |
4358 | silent_examine_position(EXAMINE_DRAGONS); |
4359 | return gtp_success("%d", DRAGON2(POS(i, j)).surround_status); | |
7eeb782e AT |
4360 | } |
4361 | ||
4362 | /* Function: Determine if a move surrounds a dragon | |
4363 | * Arguments: vertex (move), vertex (dragon) | |
4364 | * Fails: invalid vertex, empty (dragon, nonempty (move) | |
4365 | * Returns: 1 if (move) surrounds (dragon) | |
4366 | */ | |
4367 | static int | |
c150f57c | 4368 | gtp_does_surround(char* s) |
7eeb782e | 4369 | { |
c150f57c AT |
4370 | int si, sj, di, dj; |
4371 | int n; | |
7eeb782e | 4372 | |
c150f57c AT |
4373 | n = gtp_decode_coord(s, &si, &sj); |
4374 | if (n == 0) | |
4375 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4376 | |
c150f57c AT |
4377 | if (BOARD(si, sj) != EMPTY) |
4378 | return gtp_failure("move vertex must be empty"); | |
7eeb782e | 4379 | |
c150f57c AT |
4380 | n = gtp_decode_coord(s + n, &di, &dj); |
4381 | if (n == 0) | |
4382 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4383 | |
c150f57c AT |
4384 | if (BOARD(di, dj) == EMPTY) |
4385 | return gtp_failure("dragon vertex must be nonempty"); | |
7eeb782e | 4386 | |
c150f57c AT |
4387 | silent_examine_position(EXAMINE_DRAGONS); |
4388 | return gtp_success("%d", does_surround(POS(si, sj), POS(di, dj))); | |
7eeb782e AT |
4389 | } |
4390 | ||
4391 | /* Function: Report the surround map for dragon at a vertex | |
4392 | * Arguments: vertex (dragon), vertex (mapped location) | |
4393 | * Fails: invalid vertex, empty dragon | |
4394 | * Returns: value of surround map at (mapped location), or -1 if | |
4395 | * dragon not surrounded. | |
4396 | */ | |
4397 | ||
4398 | static int | |
c150f57c | 4399 | gtp_surround_map(char* s) |
7eeb782e | 4400 | { |
c150f57c AT |
4401 | int di, dj, mi, mj; |
4402 | int n; | |
7eeb782e | 4403 | |
c150f57c AT |
4404 | n = gtp_decode_coord(s, &di, &dj); |
4405 | if (n == 0) | |
4406 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4407 | |
c150f57c AT |
4408 | if (BOARD(di, dj) == EMPTY) |
4409 | return gtp_failure("dragon vertex must not be empty"); | |
7eeb782e | 4410 | |
c150f57c AT |
4411 | n = gtp_decode_coord(s + n, &mi, &mj); |
4412 | if (n == 0) | |
4413 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4414 | |
c150f57c AT |
4415 | silent_examine_position(EXAMINE_DRAGONS); |
4416 | return gtp_success("%d", surround_map(POS(di, dj), POS(mi, mj))); | |
7eeb782e AT |
4417 | } |
4418 | ||
4419 | /*************** | |
4420 | * search area * | |
4421 | ***************/ | |
4422 | ||
4423 | /* Function: limit search, and establish a search diamond | |
4424 | * Arguments: pos | |
4425 | * Fails: invalid value | |
4426 | * Returns: nothing | |
4427 | */ | |
4428 | static int | |
c150f57c | 4429 | gtp_set_search_diamond(char* s) |
7eeb782e | 4430 | { |
c150f57c AT |
4431 | int i, j; |
4432 | ||
4433 | if (!gtp_decode_coord(s, &i, &j)) | |
4434 | return gtp_failure("invalid coordinate"); | |
7eeb782e | 4435 | |
c150f57c AT |
4436 | set_limit_search(1); |
4437 | set_search_diamond(POS(i, j)); | |
4438 | return gtp_success(""); | |
7eeb782e AT |
4439 | } |
4440 | ||
4441 | /* Function: unmark the entire board for limited search | |
4442 | * Arguments: none | |
4443 | * Fails: never | |
4444 | * Returns: nothing | |
4445 | */ | |
4446 | static int | |
c150f57c | 4447 | gtp_reset_search_mask(char* s) |
7eeb782e | 4448 | { |
c150f57c | 4449 | UNUSED(s); |
7eeb782e | 4450 | |
c150f57c AT |
4451 | reset_search_mask(); |
4452 | return gtp_success(""); | |
7eeb782e | 4453 | } |
c150f57c | 4454 | |
7eeb782e AT |
4455 | /* Function: sets the global variable limit_search |
4456 | * Arguments: value | |
4457 | * Fails: invalid arguments | |
4458 | * Returns: nothing | |
4459 | */ | |
4460 | static int | |
c150f57c | 4461 | gtp_limit_search(char* s) |
7eeb782e | 4462 | { |
c150f57c | 4463 | int value; |
7eeb782e | 4464 | |
c150f57c AT |
4465 | if (sscanf(s, "%d", &value) < 1) |
4466 | return gtp_failure("invalid value for search limit"); | |
4467 | set_limit_search(value); | |
4468 | return gtp_success(""); | |
7eeb782e AT |
4469 | } |
4470 | ||
4471 | /* Function: mark a vertex for limited search | |
4472 | * Arguments: position | |
4473 | * Fails: invalid arguments | |
4474 | * Returns: nothing | |
4475 | */ | |
4476 | static int | |
c150f57c | 4477 | gtp_set_search_limit(char* s) |
7eeb782e | 4478 | { |
c150f57c | 4479 | int i, j; |
7eeb782e | 4480 | |
c150f57c AT |
4481 | gtp_decode_coord(s, &i, &j); |
4482 | set_search_mask(POS(i, j), 1); | |
4483 | return gtp_success(""); | |
7eeb782e | 4484 | } |
c150f57c | 4485 | |
7eeb782e AT |
4486 | /* Function: Draw search area. Writes to stderr. |
4487 | * Arguments: none | |
4488 | * Fails: never | |
4489 | * Returns: nothing | |
4490 | */ | |
4491 | static int | |
c150f57c | 4492 | gtp_draw_search_area(char* s) |
7eeb782e | 4493 | { |
c150f57c | 4494 | UNUSED(s); |
7eeb782e | 4495 | |
c150f57c AT |
4496 | gtp_start_response(GTP_SUCCESS); |
4497 | gtp_printf("\n"); | |
4498 | draw_search_area(); | |
4499 | return gtp_finish_response(); | |
7eeb782e AT |
4500 | } |
4501 | ||
7eeb782e AT |
4502 | /* |
4503 | * Local Variables: | |
c150f57c AT |
4504 | * tab-width: 4 |
4505 | * c-basic-offset: 4 | |
7eeb782e AT |
4506 | * End: |
4507 | */ |