1996 lines
59 KiB
Plaintext
1996 lines
59 KiB
Plaintext
#!/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:
|
|
*/
|