#!/usr/bin/env pike /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This distributed with GNU Go, a go program. * * * * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 and 2006 * * by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 3 * * or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Defaults: // linux string sgf_viewer_command = "quarry %s"; // windows (for example) // string sgf_viewer_command = "c:\\programs\\winmgt\\winmgt %s"; class GtpResponse { string status; string text; void create(string|void _status, string|void _text) { status = _status; text = _text; } int success() { return status == "="; } int failure() { return status == "?"; } } class SimpleGtp { static object engine; static function crash_callback = 0; function trace_callback = 0; Stdio.File engine_in; Stdio.FILE engine_out; Stdio.FILE engine_err; string command_line; int id_number = 1; // Send a command to the go engine. GtpResponse send_command(string s) { s = id_number + " " + s + "\n"; id_number++; engine_in->write(s); int first_line = 1; GtpResponse response = GtpResponse(); while (1) { string s = engine_out->gets(); if (!s) { // FIXME: This is probably not adequate. if (crash_callback) crash_callback(); engine_in->close(); engine_out->close(); break; } s -= "\r"; if (first_line) { if (s == "") continue; response->status = s[0..0]; first_line = 0; sscanf(s[1..], "%*d %s", response->text); } else { if (s == "") break; response->text += "\n" + s; } } return response; } // Tell the program to stop playing. void quit() { crash_callback = 0; send_command("quit"); } static void program_trace_reader() { while (1) { string s = engine_err->gets(); if (!s) break; s -= "\r"; if (trace_callback) trace_callback(s); } engine_err->close(); } void create(array(string) program_start_array, function|void crash_callback_) { command_line = program_start_array * " "; crash_callback = crash_callback_; engine_in = Stdio.File(); engine_out = Stdio.FILE(); engine_err = Stdio.FILE(); engine = Process.create_process(program_start_array, (["stdin":engine_in->pipe(), "stdout":engine_out->pipe(), "stderr":engine_err->pipe()])); thread_create(program_trace_reader); } } class Goban { constant letters = "ABCDEFGHJKLMNOPQRSTUVWXYZ" / ""; int boardsize; int gobansize; int spacing; int offset; Image.Fonts.Font font; Image.Fonts.Font small_font; array(string) white_stones; array(string) black_stones; class Markup(string vertex, string symbol, string color) { void draw(Image.Image image) { vertex = upper_case(vertex); if (vertex == "PASS") return; [int x, int y] = vertex_to_pixel_coord(vertex); image->setcolor(@Image.Color(color)->rgb()); if (sscanf(symbol, "text:%s", string text) == 1) { Image.Image text_image = font->write(text); if (text_image->xsize() >= 0.9*spacing) text_image = small_font->write(text); int width = text_image->xsize(); int height = text_image->ysize(); image->paste(text_image * 0 + ({220, 150, 50}), x - width / 2, y - height / 2); image->paste_alpha_color(text_image, x - width / 2, y - height / 2); } else { switch (symbol) { case "circle": image->circle(x, y, spacing / 3, spacing / 3); break; case "square": int delta = spacing / 4; image->line(x - delta, y - delta, x - delta, y + delta); image->line(x - delta, y + delta, x + delta, y + delta); image->line(x + delta, y + delta, x + delta, y - delta); image->line(x + delta, y - delta, x - delta, y - delta); break; case "big_square": delta = spacing / 2 - 1; image->line(x - delta, y - delta, x - delta, y + delta); image->line(x - delta, y + delta, x + delta, y + delta); image->line(x + delta, y + delta, x + delta, y - delta); image->line(x + delta, y - delta, x - delta, y - delta); break; case "triangle": delta = spacing / 2 - 1; image->line(x - delta, y + delta, x + delta, y + delta); image->line(x + delta, y + delta, x, y - delta); image->line(x, y - delta, x - delta, y + delta); break; case "dot": draw_disc(image, x, y, spacing / 6); break; case "small_dot": draw_disc(image, x, y, spacing / 9); break; case "big_dot": draw_disc(image, x, y, spacing / 4); break; case "stone": draw_disc(image, x, y, spacing / 2, 0.5); break; default: werror("Unknown symbol: " + symbol + "\n"); break; } } } } array(Markup) markups; static void create(int boardsize_, int gobansize_) { boardsize = boardsize_; gobansize = gobansize_; spacing = (int) (gobansize / (boardsize + 1.5)); offset = (gobansize - spacing * (boardsize - 1)) / 2; font = Image.Fonts.Font(font_filename, spacing / 2); small_font = Image.Fonts.Font(font_filename, spacing / 3); white_stones = ({}); black_stones = ({}); markups = ({}); } void add_stones(string color, string|array(string) stones) { if (color == "WHITE") white_stones |= Array.arrayify(stones); else if (color == "BLACK") black_stones |= Array.arrayify(stones); } int occupied(string vertex) { return (has_value(white_stones, vertex) || has_value(black_stones, vertex)); } void add_symbol(string vertex, string name, string color) { markups += ({Markup(upper_case(vertex), name, color)}); } void add_text(string vertex, string text, string color) { markups += ({Markup(upper_case(vertex), "text:" + text, color)}); } void clear_markup() { markups = ({}); } Image.Image draw_board() { Image.Image board = Image.Image(gobansize, gobansize); board = board->clear(220, 150, 50); draw_grid(board); draw_hoshi_marks(board); draw_letters_and_numbers(board); foreach (black_stones, string stone) draw_stone(board, "BLACK", stone); foreach (white_stones, string stone) draw_stone(board, "WHITE", stone); markups->draw(board); return board; } static void draw_grid(Image.Image board) { int start = offset; int end = start + (boardsize - 1) * spacing; for (int k = 0; k < boardsize; k++) { int kth = start + k * spacing; board->setcolor(0, 0, 0); board->line(start, kth, end, kth); board->line(kth, start, kth, end); } } static void draw_hoshi_marks(Image.Image board) { int a = 2 + (boardsize >= 12); int b = boardsize - a - 1; int c = boardsize / 2; if ((boardsize % 2 == 0 && boardsize >= 8) || (boardsize % 2 == 1 && boardsize >= 9)) { draw_disc(board, offset + a * spacing, offset + a * spacing, 0.1 * spacing); draw_disc(board, offset + a * spacing, offset + b * spacing, 0.1 * spacing); draw_disc(board, offset + b * spacing, offset + a * spacing, 0.1 * spacing); draw_disc(board, offset + b * spacing, offset + b * spacing, 0.1 * spacing); } if (boardsize % 2 == 1 && boardsize >= 5) draw_disc(board, offset + c * spacing, offset + c * spacing, 0.1 * spacing); if (boardsize % 2 == 1 && boardsize >= 13) { draw_disc(board, offset + a * spacing, offset + c * spacing, 0.1 * spacing); draw_disc(board, offset + b * spacing, offset + c * spacing, 0.1 * spacing); draw_disc(board, offset + c * spacing, offset + a * spacing, 0.1 * spacing); draw_disc(board, offset + c * spacing, offset + b * spacing, 0.1 * spacing); } } static void draw_letters_and_numbers(Image.Image board) { int start = offset; int end = start + (boardsize - 1) * spacing; for (int k = 0; k < boardsize; k++) { int kth = start + k * spacing; Image.Image number = font->write((string) (boardsize - k)); int width = number->xsize(); int height = number->ysize(); board->paste_alpha_color(number, (start - spacing / 2 - width) / 2 - 1, kth - height / 2 + 1); board->paste_alpha_color(number, end + (start + spacing / 2 - width) / 2 + 1, kth - height / 2 + 1); Image.Image letter = font->write(letters[k]); width = letter->xsize(); height = letter->ysize(); board->paste_alpha_color(letter, kth - width / 2, (start - spacing / 2 - height) / 2 - 1); board->paste_alpha_color(letter, kth - width / 2, end + (start + spacing / 2 - height) / 2 + 1); } } static void draw_stone(Image.Image board, string color, string vertex) { int start = offset; int x, y; [x, y] = vertex_to_xy(upper_case(vertex)); if (color == "BLACK") board->setcolor(0, 0, 0); else if (color == "WHITE") board->setcolor(255, 255, 255); else board->setcolor(128, 128, 128); float radius = (spacing + 1) / 2.0; draw_disc(board, start + x * spacing, start + y * spacing, radius); } array(int) vertex_to_xy(string vertex) { int x = search(letters, vertex[0..0]); int y = boardsize - (int) vertex[1..]; return ({x, y}); } string xy_to_vertex(int x, int y) { return letters[x] + (boardsize - y); } static array(int) vertex_to_pixel_coord(string vertex) { [int x, int y] = vertex_to_xy(vertex); return ({offset + x * spacing, offset + y * spacing}); } string pixel_coord_to_vertex(int|float pixel_x, int|float pixel_y) { int x = (int) floor((pixel_x - offset + spacing / 2.0) / spacing); int y = (int) floor((pixel_y - offset + spacing / 2.0) / spacing); if (x < 0 || x >= boardsize || y < 0 || y >= boardsize) return ""; return sprintf("%s%d", letters[x], boardsize - y); } static void draw_disc(Image.Image image, int|float x, int|float y, int|float r, float|void alpha) { int N = 20; x = (float) x; y = (float) y; r = (float) r; if (!alpha) alpha = 1.0; array(float) coords = ({}); for (int k = 0; k < N; k++) coords += ({r + 0.5 + r * cos(2 * 3.14159265 * k / N), r + 0.5 + r * sin(2 * 3.14159265 * k / N)}); Image.Image disc = Image.Image((int) ceil(2*r), (int) ceil(2*r)); disc->setcolor(255, 255, 255); disc->polyfill(coords); image->paste_alpha_color(disc*alpha, (int) (x - 0.5 * disc->xsize()), (int) (y - 0.5 * disc->ysize())); } } string font_filename = ""; int main(int argc, array(string) argv) { if (argc < 2) { werror("Usage: %s TEST-FILE:TEST-NUMBER\n", basename(argv[0])); return 1; } if (!find_font()) return 1; SimpleGtp engine = SimpleGtp("../interface/gnugo --quiet --mode gtp -w -t -d0x101840" / " "); if (!engine) { werror("Failed to start engine."); return 1; } GTK.setup_gtk(argv); Controller controller = Controller(engine, argv[1..]); GTK.main(); return 0; } array(string) recursive_find_files(string dir, string suffix) { array(string) found_files = ({}); if (!get_dir(dir)) return ({}); foreach (get_dir(dir), string filename) { string full_name = dir + "/" + filename; if (Stdio.is_dir(full_name)) found_files += recursive_find_files(full_name, suffix); else if (has_suffix(filename, suffix)) found_files += ({full_name}); } return found_files; } int find_font() { if (getenv("GNUGO_FONT")) { font_filename = getenv("GNUGO_FONT"); return 1; } // Search for fonts below /usr/share/fonts. array(string) font_files = recursive_find_files("/usr/share/fonts", ".ttf"); if (sizeof(font_files) == 0) font_files = recursive_find_files("/usr/share/fonts", ".pfb"); if (sizeof(font_files) == 0) { werror("No font found while searching below /usr/share/fonts.\n"); werror("Locate a font file with suffix .ttf (truetype) or .pfb (type1)\n"); werror("and point to it from the environment variable GNUGO_FONT.\n"); return 0; } // Compute the length of the filename proper, i.e. without the // path to the file. int fontlength(string s) { return sizeof((s / "/")[-1]); }; // Choose the one with shortest name (arbitrary but may avoid e.g. // italic fonts). font_filename = font_files[0]; foreach (font_files[1..], string font_file) { if (fontlength(font_filename) > fontlength(font_file)) font_filename = font_file; else if (fontlength(font_filename) == fontlength(font_file) && has_value(lower_case(font_filename), "mono")) font_filename = font_file; } return 1; } class RegressionViewer { Goban goban; SimpleGtp engine; array(string) traces; GTK.Widget goban_widget; GTK.Widget data_widget; GTK.ScrolledWindow scrolled_data_window; GTK.Image gtk_image; GDK.Image gdk_image; GTK.Clist clist; Controller parent; //Evil. Used for callbacks. mapping(string:array(string)) worms = ([]); mapping(string:array(string)) dragons = ([]); int worms_initialized = 0; int dragons_initialized = 0; string name; string result; string testcase_command; array(string) complete_test; function on_board_click_callback; static void create(SimpleGtp engine_, array(string) complete_test_, string testcase_command_, function callback, string name_, Controller parent_) { engine = engine_; parent = parent_; complete_test = complete_test_; testcase_command = testcase_command_; name = name_; load_testcase(); werror("%s\n", send_command("showboard")); int boardsize = (int) send_command("query_boardsize"); on_board_click_callback = callback; setup_board(boardsize); scrolled_data_window = GTK.ScrolledWindow(); scrolled_data_window->set_policy(GTK.POLICY_AUTOMATIC, GTK.POLICY_AUTOMATIC); clist = GTK.Clist(3); scrolled_data_window->add(clist); handle_testcase(); } static void setup_board(int boardsize) { goban = Goban(boardsize, 600); goban->add_stones("WHITE", send_command("list_stones white") / " "); goban->add_stones("BLACK", send_command("list_stones black") / " "); Image.Image im = goban->draw_board(); gdk_image = GDK.Image(0)->set(im); gtk_image = GTK.Image(gdk_image); goban_widget = GTK.EventBox()->add(gtk_image); goban_widget->add_events(GDK.ButtonPressMask); goban_widget->add_events(GDK.KeyPressMask); goban_widget->signal_connect_new("button_press_event", button_pressed_on_board); goban_widget->signal_connect_new("key_press_event", key_pressed_on_board); } void new_testcase(array(string) complete_test_, string testcase_command_) { werror("Loading new testcase.\n"); worms_initialized = 0; dragons_initialized = 0; result = ""; worms = ([]); dragons = ([]); complete_test = complete_test_; testcase_command = testcase_command_; load_testcase(); werror("%s\n", send_command("showboard")); int boardsize = (int) send_command("query_boardsize"); werror("Loaded new testcase.\n"); goban = Goban(boardsize, 600); goban->add_stones("WHITE", send_command("list_stones white") / " "); goban->add_stones("BLACK", send_command("list_stones black") / " "); redraw_board(); } static void load_testcase() { foreach(complete_test, string testline) { werror(testline + "\n"); if (!has_value("0123456789 #", testline[0..0])) send_command(testline); } } void handle_testcase() { traces = ({}); engine->trace_callback = collect_traces; result = send_command(testcase_command); redraw_board(); engine->trace_callback = 0; } static void collect_traces(string s) { traces += ({s}); } void get_dragons() { foreach (send_command("dragon_stones") / "\n", string dragon) dragons[(dragon / " ")[0]] = dragon / " " - ({""}); dragons_initialized = 1; } static void get_worms() { foreach (send_command("worm_stones") / "\n", string worm) worms[(worm / " ")[0]] = worm / " " - ({""}); worms_initialized = 1; } void add_markup(int mode) { goban->clear_markup(); if (mode <= 4) { function add_suitable_markup = ({add_worms_and_dragons_markup, add_move_generation_markup, add_eyes_markup, add_influence_markup, add_reading_markup})[mode]; add_suitable_markup(); redraw_board(); } } static void add_worms_and_dragons_markup() { mapping status_colors = (["alive":"green", "critical":"yellow", "dead":"red", "unknown":"blue"]); mapping safety_colors = (["alive":"green", "critical":"yellow", "dead":"red", "tactically dead":"brown", "alive in seki":"cyan", "strongly alive":"blue", "invincible":"purple", "inessential":"orange"]); if (parent->dragon_status_button->get_active()) { if (!dragons_initialized) get_dragons(); foreach (dragons; string dragon; array(string) stones) { string status = get_worm_or_dragon_data("dragon", "status", dragon); foreach (stones, string stone) goban->add_symbol(stone, "dot", status_colors[status]); } } else if (parent->dragon_safety_button->get_active()) { if (!dragons_initialized) get_dragons(); foreach (dragons; string dragon; array(string) stones) { string safety = get_worm_or_dragon_data("dragon", "safety", dragon); foreach (stones, string stone) goban->add_symbol(stone, "dot", safety_colors[safety]); } } else if (parent->worm_status_button->get_active()) { if (!worms_initialized) get_worms(); foreach (worms; string worm; array(string) stones) { string attack = get_worm_or_dragon_data("worm", "attack_code", worm); string defense = get_worm_or_dragon_data("worm", "defense_code", worm); string status = "alive"; if (attack != "0") { if (defense == "0") status = "dead"; else status = "critical"; } foreach (stones, string stone) goban->add_symbol(stone, "dot", status_colors[status]); } } } static void add_move_generation_markup() { if ((parent->top_moves_button->get_active() || parent->all_moves_button->get_active()) && (has_prefix(testcase_command, "reg_genmove") || has_prefix(testcase_command, "restricted_genmove"))) { string color = "green"; string answers = parent->expected_result; if (answers[0..0] == "!") { answers = answers[1..]; color = "red"; } if (has_prefix(testcase_command, "restricted_genmove")) { foreach ((testcase_command / " ")[2..], string allowed_move) { if (color == "green" ^ has_value(answers, allowed_move)) goban->add_symbol(allowed_move, "triangle", "red"); else goban->add_symbol(allowed_move, "triangle", "green"); } } else foreach (answers / "|" - ({""}), string answer) goban->add_symbol(answer, "big_square", color); color = (testcase_command / " ")[1]; goban->add_symbol(result, "stone", color); } if (parent->top_moves_button->get_active()) { array(string) top_moves = send_command("top_moves") / " "; for (int k = 0; k < sizeof(top_moves) / 2; k++) goban->add_text(top_moves[2 * k], top_moves[2 * k + 1], "blue"); } else if (parent->all_moves_button->get_active()) { array(string) all_moves = send_command("all_move_values") / "\n"; foreach (all_moves, string move_value) { sscanf(move_value, "%s%*[ ]%s", string vertex, string value); goban->add_text(vertex, value, "blue"); } } else if (parent->delta_territory_button->get_active() && parent->delta_territory_move != "PASS") { goban->add_symbol(parent->delta_territory_move, "stone", "gray"); parent->delta_territory_button_text ->set_text("delta territory for " + parent->delta_territory_move); clist->clear(); int k; for (k = sizeof(traces) - 1; k >= 0; k--) { if (sscanf(traces[k], " " + parent->delta_territory_move + ": %*f - change in territory%*s") == 2 && !has_value(traces[k], "cached")) break; } if (k >= 0) { clist->append(({traces[k], "", ""})); for (k--; k >= 0; k--) { if (sscanf(traces[k], " %*s: - %*s") < 2) break; clist->prepend(({traces[k], "", ""})); if (sscanf(traces[k], " %*s: - %s territory change %s ", string vertex, string value) == 3) { goban->add_text(vertex, value, (float) value < 0.0 ? "red" : "blue"); } } } clist->columns_autosize(); parent->set_title(this_object(), ("Delta territory for " + parent->delta_territory_move)); } } static mapping(string:mapping(string:string)) eye_data = 0; static mapping(string:string) half_eye_data = ([]); static mapping(string:mapping(string:string)) eye_types = 0; static void add_eyes_markup() { if (!eye_data) { eye_data = (["white":([]), "black":([])]); eye_types = (["white":([]), "black":([])]); for (int y = 0; y < goban->boardsize; y++) { for (int x = 0; x < goban->boardsize; x++) { string vertex = goban->xy_to_vertex(x, y); multiset(string) is_marginal = (<>); foreach (({"white", "black"}), string color) { string this_eye = send_command("eye_data " + color + " " + vertex); if (!Regexp("origin +PASS")->match(this_eye)) { eye_data[color][vertex] = this_eye; if (Regexp("marginal +1")->match(this_eye)) is_marginal[color] = 1; } } if (!eye_data["white"][vertex] && !eye_data["black"][vertex]) continue; string half_eye = send_command("half_eye_data " + vertex); if (!Regexp("type +0")->match(half_eye)) half_eye_data[vertex] = half_eye; foreach (({"white", "black"}), string color) { if (!eye_data[color][vertex]) continue; if (is_marginal[color]) eye_types[color][vertex] = "marginal"; else if (Regexp("type +HALF_EYE")->match(half_eye)) eye_types[color][vertex] = "half"; else eye_types[color][vertex] = "proper"; } } } } string color; if (parent->white_eyes_button->get_active()) color = "white"; else color = "black"; foreach (eye_types[color]; string vertex; string eye_type) { mapping (string:string) grayish = (["white" : "#c0c0c0", "black" : "#404040"]); if (eye_type == "proper") goban->add_symbol(vertex, "big_dot", color); else if (eye_type == "half") goban->add_symbol(vertex, "big_dot", grayish[color]); else goban->add_symbol(vertex, "dot", grayish[color]); } } static void add_influence_markup() { string command; string what_data; if (parent->initial_w_influence_dragons_known_button->get_active()) command = "initial_influence white"; else if (parent->initial_b_influence_dragons_known_button->get_active()) command = "initial_influence black"; else if (parent->after_move_influence_button->get_active()) { if (parent->move_influence_move == "PASS") return; command = sprintf("move_influence %s %s", parent->current_move_color, parent->move_influence_move); goban->add_symbol(parent->move_influence_move, "stone", "gray"); } else if (parent->followup_influence_button->get_active()) { if (parent->move_influence_move == "PASS") return; command = sprintf("followup_influence %s %s", parent->current_move_color, parent->move_influence_move); goban->add_symbol(parent->move_influence_move, "stone", "gray"); } if (parent->influence_regions_button->get_active()) what_data = "influence_regions"; if (parent->territory_value_button->get_active()) what_data = "territory_value"; else if (parent->white_influence_button->get_active()) what_data = "white_influence"; else if (parent->black_influence_button->get_active()) what_data = "black_influence"; else if (parent->white_influence_button->get_active()) what_data = "white_influence"; else if (parent->black_influence_button->get_active()) what_data = "black_influence"; else if (parent->white_strength_button->get_active()) what_data = "white_strength"; else if (parent->black_strength_button->get_active()) what_data = "black_strength"; else if (parent->white_permeability_button->get_active()) what_data = "white_permeability"; else if (parent->black_permeability_button->get_active()) what_data = "black_permeability"; else if (parent->white_attenuation_button->get_active()) what_data = "white_attenuation"; else if (parent->black_attenuation_button->get_active()) what_data = "black_attenuation"; else if (parent->non_territory_button->get_active()) what_data = "non_territory"; string result = send_command(command + " " + what_data); array(string) values = (replace(result, "\n", " ") / " ") - ({""}); int k = 0; for (int y = 0; y < goban->boardsize; y++) { for (int x = 0; x < goban->boardsize; x++) { string vertex = goban->xy_to_vertex(x, y); if (what_data == "influence_regions") { int value = (int) values[k]; mapping(int:string) sizes = ([3:"big_dot", 2:"big_dot", 1:"dot", -1:"dot", -2:"big_dot", -3:"big_dot"]); mapping(int:string) colors = ([3:"white", 2:"#c0c0c0", 1:"#c0c0c0", -1:"#404040", -2:"#404040", -3:"black"]); if (sizes[value]) goban->add_symbol(vertex, sizes[value], colors[value]); } else if (has_value(what_data, "permeability")) { if ((float) values[k] != 1.0) goban->add_text(vertex, values[k], "blue"); } else if (what_data == "non_territory") { if (!goban->occupied(vertex)) { int value = (int) values[k]; if (value == 1) goban->add_symbol(vertex, "dot", "black"); else if (value == 2) goban->add_symbol(vertex, "dot", "white"); else if (value == 0) goban->add_symbol(vertex, "dot", "gray"); } } else if ((float) values[k] > 0.0) goban->add_text(vertex, values[k], "blue"); else if ((float) values[k] < 0.0) goban->add_text(vertex, values[k], "red"); k++; } } } static void add_reading_markup() { if ((parent->connection_reading_button->get_active() || parent->semeai_reading_button->get_active()) && parent->first_vertex != "") goban->add_symbol(parent->first_vertex, "big_dot", "green"); } void redraw_board() { gdk_image->set(goban->draw_board()); gtk_image->queue_draw(); } void show_worm_data(string vertex) { string worm_data = send_command("worm_data " + vertex); clist->clear(); foreach ((worm_data / "\n")[1..], string data_item) { sscanf(data_item, "%s%*[ ]%s", string field, string value); clist->append(({field, value, ""})); } clist->columns_autosize(); parent->set_title(this_object(), "Worm data for " + vertex); } void show_dragon_data(string vertex, int part) { string dragon_data = send_command("dragon_data " + vertex); clist->clear(); array(string) selected_data; if (part == 1) selected_data = (dragon_data / "\n")[1..20]; else selected_data = (dragon_data / "\n")[21..]; foreach (selected_data, string data_item) { sscanf(data_item, "%s%*[ ]%s", string field, string value); clist->append(({field, value, ""})); } clist->columns_autosize(); parent->set_title(this_object(), "Dragon data for " + vertex); } void show_move_reasons(string vertex) { string move_reasons = send_command("move_reasons " + vertex); clist->clear(); foreach ((move_reasons / "\n"), string move_reason) clist->append(({move_reason, "", ""})); int k; for (k = sizeof(traces) - 1; k >= 0; k--) { if (sscanf(traces[k], "Move generation values " + vertex + " to %*s") == 1) break; } if (k >= 0) { array(string) interesting_lines = ({traces[k]}); for (k--; k >= 0; k--) { if (sscanf(traces[k], " " + vertex + ": %*s") == 1) interesting_lines += ({traces[k]}); else if (sscanf(traces[k], " " + vertex + ": %*s") != 1) break; } clist->append(({"", "", ""})); foreach (reverse(interesting_lines), string line) clist->append(({line, "", ""})); } int first_pattern = 1; int add_continuation_lines = 0; foreach (traces, string line) { if (sscanf(line, "pattern %*s matched at " + vertex + "%s", string s) == 2 && s == "") { if (first_pattern) { clist->append(({"", "", ""})); first_pattern = 0; } add_continuation_lines = 1; clist->append(({line, "", ""})); } else if (has_prefix(line, "...") && add_continuation_lines) clist->append(({line, "", ""})); else add_continuation_lines = 0; } /* Look for blunder devaluation */ foreach (traces, string line) if (has_prefix(line, "Move at " + vertex + " is")) clist->append(({line, "", ""})); clist->columns_autosize(); parent->set_title(this_object(), "Move reasons for " + vertex); } void show_eye_data(string vertex) { clist->clear(); string color; if (parent->white_eyes_button->get_active()) color = "white"; else color = "black"; if (eye_data[color][vertex]) { foreach (eye_data[color][vertex] / "\n", string data_item) { sscanf(data_item, "%s%*[ ]%s", string field, string value); clist->append(({field, value, ""})); } if (half_eye_data[vertex]) { clist->append(({"", "", ""})); foreach (half_eye_data[vertex] / "\n", string data_item) { sscanf(data_item, "%s%*[ ]%s", string field, string value); clist->append(({field, value, ""})); } } } clist->columns_autosize(); parent->set_title(this_object(), color + " eye data for " + vertex); } static void button_pressed_on_board(GDK.Event event) { // werror("Button: %O\n", (mapping) event); string vertex = goban->pixel_coord_to_vertex(event->x, event->y); on_board_click_callback(vertex); } static void key_pressed_on_board(GDK.Event event) { // werror("Key: %O\n", (mapping) event); // First thing to find out is what the event object contains and // how we can get the mouse position when the key was pressed. } string send_command(string command) { string result; result = engine->send_command(command)->text; return result; } mapping(string:string) worm_and_dragon_cache = ([]); static string get_worm_or_dragon_data(string worm_or_dragon, string field, string vertex) { string command = worm_or_dragon + "_data " + vertex; string data = worm_and_dragon_cache[command]; if (!data) { data = send_command(command); worm_and_dragon_cache[command] = data; } foreach (data / "\n", string row) if (has_prefix(row, field)) { sscanf(row, "%*s%*[ ]%s", string value); return value; } return ""; } void do_reading(string reset_counter, string get_counter, string sgffilename, string sgf_viewer_cmd, string first_command, string second_command) { string result; send_command("clear_cache"); if (sizeof(parent->viewers) > 1) sgffilename += "." + replace(name, " ", "_"); if (sgffilename != "") send_command("start_sgftrace"); send_command(reset_counter); result = send_command(first_command); clist->append(({first_command, result, "(" + send_command(get_counter) + " nodes)"})); if (result[0..0] != "0" && second_command != "") { send_command(reset_counter); string result = send_command(second_command); clist->append(({second_command, result, "(" + send_command(get_counter) + " nodes)"})); } if (sgffilename != "") send_command("finish_sgftrace " + sgffilename); if (sgf_viewer_cmd != "") Process.create_process(sprintf(sgf_viewer_cmd, sgffilename) / " "); clist->columns_autosize(); parent->set_title(this_object(), "Reading result"); } } class Controller { array(RegressionViewer) viewers = ({}); array(GTK.Widget) viewer_title_widgets = ({}); string current_move_color = ""; GTK.Window main_window; GTK.Notebook controller_notebook; GTK.Notebook gobans_notebook; GTK.Notebook data_notebook; GTK.Notebook selector_notebook; GTK.ScrolledWindow scrolled_testcase_text; GTK.Label testcase_text; GTK.RadioButton worm_data_button; GTK.RadioButton dragon_data1_button; GTK.RadioButton dragon_data2_button; GTK.RadioButton worm_status_button; GTK.RadioButton dragon_status_button; GTK.RadioButton dragon_safety_button; GTK.RadioButton top_moves_button; GTK.RadioButton all_moves_button; GTK.RadioButton delta_territory_button; GTK.Label delta_territory_button_text; // GTK.RadioButton initial_w_influence_dragons_unknown_button; // GTK.RadioButton initial_b_influence_dragons_unknown_button; GTK.RadioButton initial_w_influence_dragons_known_button; GTK.RadioButton initial_b_influence_dragons_known_button; GTK.RadioButton after_move_influence_button; GTK.Label after_move_influence_button_text; GTK.RadioButton followup_influence_button; GTK.Label followup_influence_button_text; GTK.RadioButton influence_regions_button; GTK.RadioButton territory_value_button; GTK.RadioButton white_influence_button; GTK.RadioButton black_influence_button; GTK.RadioButton white_strength_button; GTK.RadioButton black_strength_button; GTK.RadioButton white_permeability_button; GTK.RadioButton black_permeability_button; GTK.RadioButton white_attenuation_button; GTK.RadioButton black_attenuation_button; GTK.RadioButton non_territory_button; GTK.RadioButton white_eyes_button; GTK.RadioButton black_eyes_button; GTK.RadioButton tactical_reading_button, owl_reading_button, owl_does_attack_button, owl_does_defend_button, connection_reading_button, semeai_reading_button; GTK.CheckButton sgf_traces_button, sgf_viewer_button; GTK.Entry sgf_filename_entry, sgf_viewer_entry; GTK.Table sgf_stuff; GTK.Button new_testcase_button, new_engine_button; GTK.Button next_testcase_button, prev_testcase_button; GTK.Entry new_testcase_entry, engine_path_entry, engine_name_entry; string delta_territory_move = "PASS"; string move_influence_move = "PASS"; string first_vertex = ""; int testcase_index = 0; array(string) testcases; string testcase_name; string testcase_command; string result; string expected_result; // All lines from the test file shown at the top of the control window. array(string) full_testcase; // All lines from the test file which may be needed to load the // testcase correctly. array(string) complete_testcase; static mixed nasty_signal_id; static void create(SimpleGtp engine_, array(string) testcases_) { testcases = testcases_; if (!excerpt_testcase(testcases[0], engine_)) { werror("Failed to load testcase.\n"); exit(1); } testcase_name = testcases[0]; scrolled_testcase_text = GTK.ScrolledWindow(GTK.Adjustment(), GTK.Adjustment()) ->set_policy(GTK.POLICY_AUTOMATIC, GTK.POLICY_AUTOMATIC) ->set_usize(450, 100); testcase_text = GTK.Label(full_testcase * "\n") ->set_justify(GTK.JUSTIFY_LEFT) ->set_alignment(0.0, 0.0); scrolled_testcase_text->add(testcase_text); main_window = GTK.Window(GTK.WindowToplevel); controller_notebook = GTK.Notebook(); controller_notebook->set_tab_pos(GTK.POS_LEFT); gobans_notebook = (GTK.Notebook() ->set_show_tabs(0)); data_notebook = (GTK.Notebook() ->set_show_tabs(0) ->set_show_border(0)); selector_notebook = (GTK.Notebook() ->set_tab_pos(GTK.POS_TOP)); selector_notebook->signal_connect_new("switch_page", change_engine); GTK.Widget main_window_contents = (GTK.Vbox(0, 0) ->pack_start(GTK.Hbox(0, 24) ->pack_start(scrolled_testcase_text, 0, 0, 24) ->pack_start(GTK.Alignment(0.0, 0.5, 0.0, 0.0) ->add(selector_notebook), 0, 0, 0), 0, 0, 0) ->add(GTK.Hbox(0, 2) ->add(GTK.Vbox(0, 6) ->pack_start(controller_notebook, 0, 0, 0) ->add(data_notebook)) ->pack_start(GTK.Alignment(0.0, 0.0, 0.0, 0.0) ->add(gobans_notebook), 0, 0, 0))); main_window->add(main_window_contents); main_window->set_title(testcases[0]); main_window->signal_connect_new("destroy", quit); if (has_prefix(testcase_command, "reg_genmove") || has_prefix(testcase_command, "restricted_genmove")) { sscanf(testcase_command, "%*s %s", string color); if (lower_case(color[0..0]) == "w") current_move_color = "white"; else current_move_color = "black"; } worm_data_button = GTK.RadioButton("worm data"); dragon_data1_button = GTK.RadioButton("dragon data, part 1", worm_data_button); dragon_data2_button = GTK.RadioButton("dragon data, part 2", worm_data_button); worm_status_button = GTK.RadioButton("worm status"); dragon_status_button = GTK.RadioButton("dragon status", worm_status_button); dragon_safety_button = GTK.RadioButton("dragon safety", worm_status_button); worm_status_button->signal_connect_new("clicked", markup_button_pressed); dragon_status_button->signal_connect_new("clicked", markup_button_pressed); dragon_safety_button->signal_connect_new("clicked", markup_button_pressed); top_moves_button = GTK.RadioButton("top moves"); all_moves_button = GTK.RadioButton("all moves", top_moves_button); delta_territory_button_text = GTK.Label("delta territory for PASS"); delta_territory_button_text->set_alignment(0.0, 0.0); delta_territory_button = GTK.RadioButton(0, top_moves_button); delta_territory_button->add(delta_territory_button_text); top_moves_button->signal_connect_new("clicked", markup_button_pressed); all_moves_button->signal_connect_new("clicked", markup_button_pressed); delta_territory_button->signal_connect_new("clicked", markup_button_pressed); white_eyes_button = GTK.RadioButton("white eyes"); black_eyes_button = GTK.RadioButton("black eyes", white_eyes_button); white_eyes_button->signal_connect_new("clicked", markup_button_pressed); black_eyes_button->signal_connect_new("clicked", markup_button_pressed); // initial_w_influence_dragons_unknown_button = // GTK.RadioButton("white influence, dragons unknown"); // initial_b_influence_dragons_unknown_button = // GTK.RadioButton("black influence, dragons unknown", // initial_w_influence_dragons_unknown_button); initial_w_influence_dragons_known_button = GTK.RadioButton("white influence, dragons known"); initial_b_influence_dragons_known_button = GTK.RadioButton("black influence, dragons known", initial_w_influence_dragons_known_button); after_move_influence_button_text = GTK.Label("after move influence for PASS"); after_move_influence_button_text->set_alignment(0.0, 0.0); after_move_influence_button = GTK.RadioButton(0, initial_w_influence_dragons_known_button); after_move_influence_button->add(after_move_influence_button_text); followup_influence_button_text = GTK.Label("followup influence for PASS"); followup_influence_button_text->set_alignment(0.0, 0.0); followup_influence_button = GTK.RadioButton(0, initial_w_influence_dragons_known_button); followup_influence_button->add(followup_influence_button_text); influence_regions_button = GTK.RadioButton("influence regions"); territory_value_button = GTK.RadioButton("territory value", influence_regions_button); white_influence_button = GTK.RadioButton("white influence", influence_regions_button); black_influence_button = GTK.RadioButton("black influence", influence_regions_button); white_strength_button = GTK.RadioButton("white strength", influence_regions_button); black_strength_button = GTK.RadioButton("black strength", influence_regions_button); white_permeability_button = GTK.RadioButton("white permeability", influence_regions_button); black_permeability_button = GTK.RadioButton("black permeability", influence_regions_button); white_attenuation_button = GTK.RadioButton("white attenuation", influence_regions_button); black_attenuation_button = GTK.RadioButton("black attenuation", influence_regions_button); non_territory_button = GTK.RadioButton("non-territory", influence_regions_button); ({initial_w_influence_dragons_known_button, initial_b_influence_dragons_known_button, after_move_influence_button, followup_influence_button, influence_regions_button, territory_value_button, white_influence_button, black_influence_button, white_strength_button, black_strength_button, white_permeability_button, black_permeability_button, white_attenuation_button, black_attenuation_button, non_territory_button})->signal_connect_new("clicked", markup_button_pressed); tactical_reading_button = GTK.RadioButton("tactical reading"); owl_reading_button = GTK.RadioButton("owl reading", tactical_reading_button); owl_does_attack_button = GTK.RadioButton("owl_does_attack", tactical_reading_button); owl_does_defend_button = GTK.RadioButton("owl_does_defend", tactical_reading_button); connection_reading_button = GTK.RadioButton("connection reading", tactical_reading_button); semeai_reading_button = GTK.RadioButton("semeai reading", tactical_reading_button); sgf_traces_button = (GTK.CheckButton("save sgf traces to") ->set_active(1)); sgf_filename_entry = GTK.Entry(); sgf_filename_entry->set_text("vars.sgf"); sgf_filename_entry->set_editable(1); sgf_viewer_button = GTK.CheckButton("start sgf viewer as"); sgf_viewer_entry = GTK.Entry()->set_text(sgf_viewer_command) ->set_editable(1); sgf_viewer_button->signal_connect("toggled", sgf_viewer_button_toggled); sgf_traces_button->signal_connect("toggled", sgf_traces_button_toggled); sgf_stuff = GTK.Table(2, 2, 0) ->attach_defaults(sgf_traces_button, 0, 1, 0, 1) ->attach_defaults(sgf_filename_entry, 1, 2, 0, 1) ->attach_defaults(sgf_viewer_button, 0, 1, 1, 2) ->attach_defaults(sgf_viewer_entry, 1, 2, 1, 2); new_testcase_entry = GTK.Entry(); new_testcase_entry->set_text(testcases[0]); new_testcase_entry->set_editable(1); new_testcase_button = GTK.Button("Load new testcase"); new_testcase_button->signal_connect_new("clicked", new_testcase); new_testcase_entry->signal_connect_new("activate", new_testcase); if (sizeof(testcases)) { prev_testcase_button = GTK.Button("Previous testcase"); prev_testcase_button->signal_connect_new("clicked", prev_testcase); prev_testcase_button->set_sensitive(0); next_testcase_button = GTK.Button("Next testcase"); next_testcase_button->signal_connect_new("clicked", next_testcase); } engine_path_entry = GTK.Entry(); engine_path_entry->set_text("../interface/gnugo"); engine_path_entry->set_editable(1); engine_name_entry = GTK.Entry(); engine_name_entry->set_text("Engine 2"); engine_name_entry->set_editable(1); engine_path_entry->signal_connect_new("activate", select_new_engine); new_engine_button = GTK.Button("Start new engine"); new_engine_button->signal_connect_new("clicked", select_new_engine); GTK.Widget worms_and_dragons_page = (GTK.Vbox(0, 0) ->pack_start(worm_data_button, 0, 0, 0) ->pack_start(dragon_data1_button, 0, 0, 0) ->pack_start(dragon_data2_button, 0, 0, 0) ->pack_start(GTK.Label(""), 0, 0, 0) ->pack_start(worm_status_button, 0, 0, 0) ->pack_start(dragon_status_button, 0, 0, 0) ->pack_start(dragon_safety_button, 0, 0, 0)); controller_notebook->append_page(worms_and_dragons_page, GTK.Label("worms and dragons")); GTK.Widget move_generation_page = (GTK.Vbox(0, 0) ->pack_start(top_moves_button, 0, 0, 0) ->pack_start(all_moves_button, 0, 0, 0) ->pack_start(delta_territory_button, 0, 0, 0)); controller_notebook->append_page(move_generation_page, GTK.Label("move generation")); GTK.Widget eyes_page = (GTK.Vbox(0, 0) ->pack_start(white_eyes_button, 0, 0, 0) ->pack_start(black_eyes_button, 0, 0, 0)); controller_notebook->append_page(eyes_page, GTK.Label("eyes")); GTK.Widget influence_page = (GTK.Vbox(0, 0) ->pack_start(GTK.Vbox(0,0) // ->add(initial_w_influence_dragons_unknown_button) // ->add(initial_b_influence_dragons_unknown_button) ->add(initial_w_influence_dragons_known_button) ->add(initial_b_influence_dragons_known_button) ->add(after_move_influence_button) ->add(followup_influence_button), 0, 0, 0) ->pack_start(GTK.Label(""), 0, 0, 0) ->pack_start(GTK.Hbox(0,12) ->add(GTK.Vbox(0,0) ->pack_start(influence_regions_button, 0, 0, 0) ->pack_start(territory_value_button, 0, 0, 0) ->pack_start(non_territory_button, 0, 0, 0) ->pack_start(white_influence_button, 0, 0, 0) ->pack_start(black_influence_button, 0, 0, 0)) ->add(GTK.Vbox(0,0) ->pack_start(white_strength_button, 0, 0, 0) ->pack_start(black_strength_button, 0, 0, 0) ->pack_start(white_permeability_button, 0, 0, 0) ->pack_start(black_permeability_button, 0, 0, 0) ->pack_start(white_attenuation_button, 0, 0, 0) ->pack_start(black_attenuation_button, 0, 0, 0)), 0, 0, 0)); controller_notebook->append_page(influence_page, GTK.Label("influence")); GTK.Widget reading_page = (GTK.Vbox(0, 0) ->pack_start(tactical_reading_button, 0, 0, 0) ->pack_start(owl_reading_button, 0, 0, 0) ->pack_start(owl_does_attack_button, 0, 0, 0) ->pack_start(owl_does_defend_button, 0, 0, 0) ->pack_start(connection_reading_button, 0, 0, 0) ->pack_start(semeai_reading_button, 0, 0, 0) ->pack_start(GTK.Label(""), 0, 0, 0) ->pack_start(sgf_stuff, 0, 0, 0)); controller_notebook->append_page(reading_page, GTK.Label("reading")); GTK.Widget engines_page = (GTK.Vbox(0, 12) ->pack_start(engine_path_entry, 0, 0, 0)); engines_page->pack_start(engine_name_entry, 0, 0, 0); engines_page->pack_start(GTK.Alignment(1.0, 0.0, 0.0, 0.0) ->add(new_engine_button), 0, 0, 0) ->pack_start(new_testcase_entry, 0, 0, 0) ->pack_start(new_testcase_button, 0, 0, 0); if (sizeof(testcases) > 1) { GTK.Widget next_prev = (GTK.Hbox(0, 12)->pack_start(prev_testcase_button, 0, 0, 0) ->pack_start(next_testcase_button, 0, 0, 0)); engines_page->pack_start(next_prev, 0, 0, 0); } controller_notebook->append_page(engines_page->set_border_width(12), GTK.Label("engines")); nasty_signal_id = controller_notebook->signal_connect_new("switch_page", add_markup); if (has_prefix(testcase_command, "reg_genmove") || has_prefix(testcase_command, "restricted_genmove")) { controller_notebook->show_all(); controller_notebook->set_page(1); } main_window->show_all(); add_regression_viewer(RegressionViewer(engine_, complete_testcase, testcase_command, button_pressed_on_a_board, "Default engine", this_object())); add_markup(controller_notebook->get_current_page()); } static void select_new_engine() { string new_engine_path = engine_path_entry->get_text(); if (!Stdio.is_file(new_engine_path)) { viewers->clist->clear(); viewers->clist->append(({"The engine path does not point to a file.\n", "", ""})); return; } SimpleGtp new_engine = SimpleGtp((new_engine_path + " --quiet --mode gtp -w -t -d0x101840") / " "); if (!new_engine) werror("Failed to start new engine.\n"); else { add_regression_viewer( RegressionViewer(new_engine, complete_testcase, testcase_command, button_pressed_on_a_board, engine_name_entry->get_text(), this_object())); } engine_name_entry->set_text(sprintf("Engine %d", sizeof(viewers) + 1)); } static void add_regression_viewer(RegressionViewer viewer) { viewers += ({ viewer }); viewer->goban_widget->show_all(); gobans_notebook->append_page(viewer->goban_widget, 0); GTK.Widget title_label = GTK.Label(""); viewer_title_widgets += ({ title_label }); GTK.Widget data_page = (GTK.Vbox(0, 2) ->pack_start(title_label, 0, 0, 0) ->add(viewer->scrolled_data_window) ->show_all()); data_notebook->append_page(data_page, 0); selector_notebook ->append_page((GTK.Alignment(0.0, 0.5, 0.0, 0.0) ->set_border_width(4) ->add(GTK.Label(viewer->engine->command_line))), GTK.Label(viewer->name)); selector_notebook->show_all(); selector_notebook->set_page(sizeof(viewers) - 1); } void set_title(RegressionViewer viewer, string title) { for (int k = 0; k < sizeof(viewers); k++) { if (viewers[k] == viewer) viewer_title_widgets[k]->set_text(title); } } static void markup_button_pressed(mixed ... foo) { add_markup(controller_notebook->get_current_page()); } static void add_markup(int mode) { viewers->add_markup(mode); } static void change_engine(int engine_index) { gobans_notebook->set_page(engine_index); data_notebook->set_page(engine_index); } void button_pressed_on_a_board(string vertex) { if (vertex == "") return; switch (controller_notebook->get_current_page()) { case 0: // Worms and dragons. if (worm_data_button->get_active()) viewers->show_worm_data(vertex); else if (dragon_data1_button->get_active()) viewers->show_dragon_data(vertex, 1); else viewers->show_dragon_data(vertex, 2); break; case 1: // Move generation. if (!delta_territory_button->get_active()) viewers->show_move_reasons(vertex); else { delta_territory_move = vertex; markup_button_pressed(); } break; case 2: // Eyes. viewers->show_eye_data(vertex); break; case 3: // Influence. if (after_move_influence_button->get_active() || followup_influence_button->get_active()) { move_influence_move = vertex; after_move_influence_button_text ->set_text("after move influence for " + vertex); followup_influence_button_text ->set_text("followup influence for " + vertex); markup_button_pressed(); } break; case 4: // Reading. string sgffilename; string sgf_viewer_cmd; string reset_counter, get_counter; if (sgf_viewer_button->get_active()) { sgffilename = sgf_filename_entry->get_text(); sgf_viewer_cmd = sgf_viewer_entry->get_text(); } else if (sgf_traces_button->get_active()) { sgffilename = sgf_filename_entry->get_text(); sgf_viewer_cmd = ""; } else { sgffilename = ""; sgf_viewer_cmd = ""; } if (first_vertex == "" || tactical_reading_button->get_active() || owl_reading_button->get_active()) viewers->goban->clear_markup(); viewers->goban->add_symbol(vertex, "big_dot", "green"); viewers->redraw_board(); viewers->clist->clear(); if (tactical_reading_button->get_active() || owl_reading_button->get_active()) { string prefix = ""; reset_counter = "reset_reading_node_counter"; get_counter = "get_reading_node_counter"; if (owl_reading_button->get_active()) { prefix = "owl_"; reset_counter = "reset_owl_node_counter"; get_counter = "get_owl_node_counter"; } viewers->do_reading(reset_counter, get_counter, sgffilename, sgf_viewer_cmd, prefix + "attack " + vertex, prefix + "defend " + vertex); } else if (owl_does_attack_button->get_active() || owl_does_defend_button->get_active() || connection_reading_button->get_active() || semeai_reading_button->get_active()) { if (first_vertex == "") { first_vertex = vertex; } else if (first_vertex != vertex) { string c1, c2; if (owl_does_attack_button->get_active()) { c1 = sprintf("owl_does_attack %s %s\n", first_vertex, vertex); viewers->do_reading("reset_owl_node_counter", "get_owl_node_counter", sgffilename, sgf_viewer_cmd, c1, ""); } else if (owl_does_defend_button->get_active()) { c1 = sprintf("owl_does_defend %s %s\n", first_vertex, vertex); viewers->do_reading("reset_owl_node_counter", "get_owl_node_counter", sgffilename, sgf_viewer_cmd, c1, ""); } else if (connection_reading_button->get_active()) { c1 = sprintf("connect %s %s\n", first_vertex, vertex); c2 = "dis" + c1; viewers->do_reading("reset_connection_node_counter", "get_connection_node_counter", sgffilename, sgf_viewer_cmd, c1, c2); } else { c1 = sprintf("analyze_semeai %s %s", first_vertex, vertex); c2 = sprintf("analyze_semeai %s %s", vertex, first_vertex); // FIXME: We should use a semeai node counter rather // than the owl node counter, except that it doesn't // exist yet. viewers->do_reading("reset_owl_node_counter", "get_owl_node_counter", sgffilename, sgf_viewer_cmd, c1, c2); } first_vertex = ""; } } break; } } static void this_new_testcase(string new_testcase) { werror("Trying to load new testcase %s.", new_testcase); if (!excerpt_testcase(new_testcase, viewers[0]->engine)) { werror("Failed to load testcase.\n"); return; } testcase_name = new_testcase; main_window->set_title(testcase_name); testcase_text->set_text(full_testcase * "\n"); viewers->new_testcase(complete_testcase, testcase_command); viewers->handle_testcase(); if (has_prefix(testcase_command, "reg_genmove") || has_prefix(testcase_command, "restricted_genmove")) { controller_notebook->show_all(); controller_notebook->set_page(1); } } static void new_testcase() { this_new_testcase(new_testcase_entry->get_text()); } static void next_testcase() { if (testcase_index >= sizeof(testcases) - 1) return; testcase_index += 1; prev_testcase_button->set_sensitive(1); if (testcase_index == sizeof(testcases) - 1) next_testcase_button->set_sensitive(0); this_new_testcase(testcases[testcase_index]); } static void prev_testcase() { if (testcase_index == 0) return; testcase_index -= 1; if (!testcases) werror("Error handling list of test cases!\n"); next_testcase_button->set_sensitive(1); if (testcase_index == 0) prev_testcase_button->set_sensitive(0); this_new_testcase(testcases[testcase_index]); } // The engine parameter is only needed to find out the color to // move when an sgf file is given. Since there can be multiple // viewers we shouldn't use this engine object for anything else // though. Notice also that no viewer has been set up yet at the // time of this call. static int excerpt_testcase(string testcase, SimpleGtp engine) { string filename; int number; if (sscanf(testcase, "%s:%d", filename, number) < 2) return 0; if (!has_suffix(filename, ".tst") && !has_suffix(filename, ".sgf")) filename += ".tst"; string testfile = Stdio.read_file(filename); if (!testfile) return 0; if (has_suffix(filename, ".sgf")) { // Only sgf file provided. Fake a testcase. string s = "loadsgf " + filename + " " + number; string color = engine->send_command(s)->text; testcase_command = "reg_genmove " + color; full_testcase = ({s, testcase_command}); complete_testcase = ({s, testcase_command}); expected_result = ""; return 1; } full_testcase = ({}); complete_testcase = ({}); array(string) testlines = testfile / "\n"; for (int k = 0; k < sizeof(testlines); k++) { int this_number; string testline = testlines[k]; if (testline[0..0] >= "a" && testline[0..0] <= "z") { if (testline[0..6] != "loadsgf") complete_testcase += ({ testline }); else complete_testcase = ({ testline }); } else if (sscanf(testline, "%d %s", this_number, testline) == 2 && this_number == number) { testcase_command = testline; sscanf(testlines[k + 1], "#? [%s]", expected_result); full_testcase += ({testlines[k]}); full_testcase += ({testlines[k + 1]}); return 1; } if (has_value("0123456789 ", testline[0..0])) full_testcase = ({}); else full_testcase += ({testline}); } return 0; } void sgf_traces_button_toggled() { if (!sgf_traces_button->get_active()) sgf_viewer_button->set_active(0); } void sgf_viewer_button_toggled() { if (sgf_viewer_button->get_active()) sgf_traces_button->set_active(1); } void debug_callback(mixed ... args) { write("Debug callback:%O\n", args); } static void quit() { // Otherwise Pike errors occur. controller_notebook->signal_disconnect(nasty_signal_id); GTK.main_quit(); } } /* * Local Variables: * tab-width: 8 * c-basic-offset: 4 * End: */