Hi! Here's a first shot at letting a gtp client decide on which groups are dead before letting the user score.
Open issues: - No visual feed-back to the user that the engine is scoring. Opening a small dialog box would be nice, as it may take some seconds. - If Quarry is used to have two gtp clients play against each other, only black is asked to score. - The score the gtp client calculates should be queried and compared to what Quarry determines. Yell if they disagree. - Seki? The missing feed-back while scoring might be a show-stopper, but before starting on that, I wanted to ask if the approach in general is ok. * src/utils/string-list.c, src/utils/utils.h (string_list_count): New function. * src/gtp/gtp-client.c (gtp_client_final_status_list) (parse_final_status_list): New functions. * src/gtp/gtp-client.h (GtpStoneStatus) (GtpClientFinalStatusListCallback): New types. * src/gtp/gtp-client.h (parse_final_status_list): New prototype. * src/gui-gtk/gtk-goban-window.c (engine_has_scored) (enter_scoring_mode): New functions. * src/gui-gtk/gtk-goban-window.c (move_has_been_played): Invoke gtp_client_final_status_list of first gtp client player (if there is one) before user may score. Index: src/gtp/gtp-client.c =================================================================== RCS file: /cvs/quarry/quarry/src/gtp/gtp-client.c,v retrieving revision 1.5 diff -u -r1.5 gtp-client.c --- src/gtp/gtp-client.c 19 Jun 2004 20:38:23 -0000 1.5 +++ src/gtp/gtp-client.c 18 Jul 2004 15:38:57 -0000 @@ -82,6 +82,9 @@ GtpClientUserCallbackData *callback_data); static int parse_generated_move(GtpClient *client, int successful, GtpClientUserCallbackData *callback_data); +static int parse_final_status_list + (GtpClient *client, int successful, + GtpClientUserCallbackData *callback_data); /* Create a new client for an engine that can be sent commands to by @@ -621,6 +624,37 @@ COLOR_STRING(color)); } +void +gtp_client_final_status_list(GtpClient *client, + GtpClientFinalStatusListCallback response_callback, + void *user_data, GtpStoneStatus status) +{ + const char *status_string = NULL; + + assert(client); + + switch (status) { + case GTP_ALIVE: + status_string = "alive"; + break; + + case GTP_DEAD: + status_string = "dead"; + break; + + case GTP_SEKI: + status_string = "seki"; + break; + } + + assert(status_string); + + send_command(client, (GtpClientResponseCallback) parse_final_status_list, + store_user_callback_data(((GtpClientResponseCallback) + response_callback), + user_data, status, NULL), + "final_status_list %s", status_string); +} static void send_command(GtpClient *client, @@ -908,6 +942,37 @@ utils_free(callback_data); return !successful || move_parsed; +} + + +static int +parse_final_status_list(GtpClient *client, int successful, + GtpClientUserCallbackData *callback_data) +{ + StringListItem *this_item; + int idx = 0; + + GtpClientFinalStatusListCallback final_status_list_callback + = (GtpClientFinalStatusListCallback) callback_data->response_callback; + + BoardPositionList *stones + = board_position_list_new_empty(string_list_count(&client->response)); + + for (this_item = client->response.first; + this_item; + this_item = this_item->next) { + int x, y; + game_parse_point(GAME_GO, client->board_size, client->board_size, + this_item->text, &x, &y); + stones->positions[idx++] = POSITION(x, y); + } + + final_status_list_callback(client, stones != NULL, + callback_data->user_data, + callback_data->integer_data, stones); + utils_free(callback_data); + + return !successful || stones != NULL; } Index: src/gtp/gtp-client.h =================================================================== RCS file: /cvs/quarry/quarry/src/gtp/gtp-client.h,v retrieving revision 1.4 diff -u -r1.4 gtp-client.h --- src/gtp/gtp-client.h 11 Jun 2004 21:39:47 -0000 1.4 +++ src/gtp/gtp-client.h 18 Jul 2004 15:38:57 -0000 @@ -55,6 +55,11 @@ GTP_SUCCESS } GtpError; +typedef enum { + GTP_ALIVE, + GTP_DEAD, + GTP_SEKI +} GtpStoneStatus; typedef struct _GtpClient GtpClient; @@ -158,6 +163,10 @@ int color, int x, int y, BoardAbstractMoveData *move_data); +typedef void (* GtpClientFinalStatusListCallback) + (GtpClient *client, int successful, void *user_data, + GtpStoneStatus status, BoardPositionList *stones); + GtpClient * gtp_client_new (GtpClientSendToEngine send_to_engine, @@ -234,6 +243,11 @@ (GtpClient *client, GtpClientMoveCallback response_callback, void *user_data, int color); + +void gtp_client_final_status_list + (GtpClient *client, + GtpClientFinalStatusListCallback response_callback, + void *user_data, GtpStoneStatus status); #endif /* QUARRY_GTP_CLIENT_H */ Index: src/gui-gtk/gtk-goban-window.c =================================================================== RCS file: /cvs/quarry/quarry/src/gui-gtk/gtk-goban-window.c,v retrieving revision 1.16 diff -u -r1.16 gtk-goban-window.c --- src/gui-gtk/gtk-goban-window.c 16 Jul 2004 19:56:45 -0000 1.16 +++ src/gui-gtk/gtk-goban-window.c 18 Jul 2004 15:39:01 -0000 @@ -105,8 +105,14 @@ SpecialModeButtonClicked cancel_callback); static void leave_special_mode(GtkGobanWindow *goban_window); -static void free_handicap_mode_done(GtkGobanWindow *goban_window); +static void engine_has_scored(GtpClient *client, int successful, + GtkGobanWindow *goban_window, + GtpStoneStatus status, + BoardPositionList *dead_stones); +static void enter_scoring_mode(GtkGobanWindow *goban_window); static void go_scoring_mode_done(GtkGobanWindow *goban_window); +static void free_handicap_mode_done(GtkGobanWindow *goban_window); + static void play_pass_move(GtkGobanWindow *goban_window); @@ -1733,19 +1739,69 @@ } else { if (goban_window->board->game == GAME_GO) { + int player; + int engine_is_scoring = FALSE; + goban_window->dead_stones = g_malloc(BOARD_GRID_SIZE * sizeof(char)); board_fill_grid(goban_window->board, goban_window->dead_stones, 0); - enter_special_mode(goban_window, - "Please select dead stones\nto score the game", - go_scoring_mode_done, NULL); - set_goban_signal_handlers(goban_window, - G_CALLBACK(go_scoring_mode_pointer_moved), - G_CALLBACK(go_scoring_mode_goban_clicked)); + for (player = 0; player < NUM_COLORS; player++) + if (goban_window->players[player]) { + gtp_client_final_status_list(goban_window->players[player], + (GtpClientFinalStatusListCallback) + engine_has_scored, + goban_window, + GTP_DEAD); + engine_is_scoring = TRUE; + break; + } - update_territory_markup(goban_window); + if(!engine_is_scoring) { + enter_scoring_mode(goban_window); + } + } + } +} + + +static void +engine_has_scored(GtpClient *client, int successful, + GtkGobanWindow *goban_window, + GtpStoneStatus status, BoardPositionList *dead_strings) +{ + UNUSED(client); + + if (successful) { + BoardPositionList *dead_stones; + int idx; + + assert(status == GTP_DEAD); + + for(idx = 0; idx < dead_strings->num_positions; idx++) { + dead_stones + = go_get_string_stones(goban_window->board, + POSITION_X(dead_strings->positions[idx]), + POSITION_Y(dead_strings->positions[idx])); + board_position_list_mark_on_grid(dead_stones, + goban_window->dead_stones, 1); } } + + enter_scoring_mode(goban_window); +} + + +static void +enter_scoring_mode(GtkGobanWindow *goban_window) +{ + enter_special_mode(goban_window, + "Please select dead stones\nto score the game", + go_scoring_mode_done, NULL); + set_goban_signal_handlers(goban_window, + G_CALLBACK(go_scoring_mode_pointer_moved), + G_CALLBACK(go_scoring_mode_goban_clicked)); + + update_territory_markup(goban_window); } Index: src/utils/string-list.c =================================================================== RCS file: /cvs/quarry/quarry/src/utils/string-list.c,v retrieving revision 1.4 diff -u -r1.4 string-list.c --- src/utils/string-list.c 27 May 2004 20:40:24 -0000 1.4 +++ src/utils/string-list.c 18 Jul 2004 15:39:02 -0000 @@ -147,6 +147,23 @@ } +int +string_list_count(void *abstract_list) +{ + StringList *list = (StringList *) abstract_list; + StringListItem *this_item; + + int count = 0; + + assert(list); + + for (this_item = list->first; this_item; this_item = this_item->next) + count++; + + return count; +} + + void string_list_fill_from_string(void *abstract_list, const char *super_string) { Index: src/utils/utils.h =================================================================== RCS file: /cvs/quarry/quarry/src/utils/utils.h,v retrieving revision 1.7 diff -u -r1.7 utils.h --- src/utils/utils.h 16 Jul 2004 19:55:07 -0000 1.7 +++ src/utils/utils.h 18 Jul 2004 15:39:03 -0000 @@ -294,6 +294,7 @@ ((abstract_list)->first != NULL \ && (abstract_list)->first == (abstract_list)->last) +int string_list_count(void *abstract_list); void string_list_fill_from_string(void *abstract_list, const char *super_string);