1#!/usr/bin/env pike 2 3/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ 4 * This distributed with GNU Go, a go program. * 5 * * 6 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 and 2006 * 7 * by the Free Software Foundation. * 8 * * 9 * This program is free software; you can redistribute it and/or * 10 * modify it under the terms of the GNU General Public License as * 11 * published by the Free Software Foundation - version 3 * 12 * or (at your option) any later version. * 13 * * 14 * This program is distributed in the hope that it will be useful, * 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 17 * GNU General Public License in file COPYING for more details. * 18 * * 19 * You should have received a copy of the GNU General Public * 20 * License along with this program; if not, write to the Free * 21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 22 * Boston, MA 02111, USA. * 23\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 24 25// Defaults: 26// linux 27 string sgf_viewer_command = "quarry %s"; 28// windows (for example) 29// string sgf_viewer_command = "c:\\programs\\winmgt\\winmgt %s"; 30 31class GtpResponse 32{ 33 string status; 34 string text; 35 36 void create(string|void _status, string|void _text) 37 { 38 status = _status; 39 text = _text; 40 } 41 42 int success() 43 { 44 return status == "="; 45 } 46 47 int failure() 48 { 49 return status == "?"; 50 } 51} 52 53class SimpleGtp 54{ 55 static object engine; 56 static function crash_callback = 0; 57 function trace_callback = 0; 58 59 Stdio.File engine_in; 60 Stdio.FILE engine_out; 61 Stdio.FILE engine_err; 62 63 string command_line; 64 65 int id_number = 1; 66 67 // Send a command to the go engine. 68 GtpResponse send_command(string s) 69 { 70 s = id_number + " " + s + "\n"; 71 id_number++; 72 engine_in->write(s); 73 int first_line = 1; 74 GtpResponse response = GtpResponse(); 75 while (1) 76 { 77 string s = engine_out->gets(); 78 if (!s) 79 { 80 // FIXME: This is probably not adequate. 81 if (crash_callback) 82 crash_callback(); 83 engine_in->close(); 84 engine_out->close(); 85 break; 86 } 87 88 s -= "\r"; 89 90 if (first_line) 91 { 92 if (s == "") 93 continue; 94 response->status = s[0..0]; 95 first_line = 0; 96 sscanf(s[1..], "%*d %s", response->text); 97 } 98 else 99 { 100 if (s == "") 101 break; 102 response->text += "\n" + s; 103 } 104 } 105 return response; 106 } 107 108 // Tell the program to stop playing. 109 void quit() 110 { 111 crash_callback = 0; 112 send_command("quit"); 113 } 114 115 static void program_trace_reader() 116 { 117 while (1) 118 { 119 string s = engine_err->gets(); 120 if (!s) 121 break; 122 s -= "\r"; 123 if (trace_callback) 124 trace_callback(s); 125 } 126 127 engine_err->close(); 128 } 129 130 void create(array(string) program_start_array, 131 function|void crash_callback_) 132 { 133 command_line = program_start_array * " "; 134 crash_callback = crash_callback_; 135 engine_in = Stdio.File(); 136 engine_out = Stdio.FILE(); 137 engine_err = Stdio.FILE(); 138 engine = Process.create_process(program_start_array, 139 (["stdin":engine_in->pipe(), 140 "stdout":engine_out->pipe(), 141 "stderr":engine_err->pipe()])); 142 thread_create(program_trace_reader); 143 } 144} 145 146class Goban 147{ 148 constant letters = "ABCDEFGHJKLMNOPQRSTUVWXYZ" / ""; 149 int boardsize; 150 int gobansize; 151 int spacing; 152 int offset; 153 Image.Fonts.Font font; 154 Image.Fonts.Font small_font; 155 array(string) white_stones; 156 array(string) black_stones; 157 158 class Markup(string vertex, string symbol, string color) 159 { 160 void draw(Image.Image image) 161 { 162 vertex = upper_case(vertex); 163 if (vertex == "PASS") 164 return; 165 [int x, int y] = vertex_to_pixel_coord(vertex); 166 image->setcolor(@Image.Color(color)->rgb()); 167 if (sscanf(symbol, "text:%s", string text) == 1) 168 { 169 Image.Image text_image = font->write(text); 170 if (text_image->xsize() >= 0.9*spacing) 171 text_image = small_font->write(text); 172 int width = text_image->xsize(); 173 int height = text_image->ysize(); 174 image->paste(text_image * 0 + ({220, 150, 50}), x - width / 2, 175 y - height / 2); 176 image->paste_alpha_color(text_image, x - width / 2, 177 y - height / 2); 178 } 179 else 180 { 181 switch (symbol) 182 { 183 case "circle": 184 image->circle(x, y, spacing / 3, spacing / 3); 185 break; 186 case "square": 187 int delta = spacing / 4; 188 image->line(x - delta, y - delta, x - delta, y + delta); 189 image->line(x - delta, y + delta, x + delta, y + delta); 190 image->line(x + delta, y + delta, x + delta, y - delta); 191 image->line(x + delta, y - delta, x - delta, y - delta); 192 break; 193 case "big_square": 194 delta = spacing / 2 - 1; 195 image->line(x - delta, y - delta, x - delta, y + delta); 196 image->line(x - delta, y + delta, x + delta, y + delta); 197 image->line(x + delta, y + delta, x + delta, y - delta); 198 image->line(x + delta, y - delta, x - delta, y - delta); 199 break; 200 case "triangle": 201 delta = spacing / 2 - 1; 202 image->line(x - delta, y + delta, x + delta, y + delta); 203 image->line(x + delta, y + delta, x, y - delta); 204 image->line(x, y - delta, x - delta, y + delta); 205 break; 206 case "dot": 207 draw_disc(image, x, y, spacing / 6); 208 break; 209 case "small_dot": 210 draw_disc(image, x, y, spacing / 9); 211 break; 212 case "big_dot": 213 draw_disc(image, x, y, spacing / 4); 214 break; 215 case "stone": 216 draw_disc(image, x, y, spacing / 2, 0.5); 217 break; 218 default: 219 werror("Unknown symbol: " + symbol + "\n"); 220 break; 221 } 222 } 223 } 224 } 225 226 array(Markup) markups; 227 228 static void create(int boardsize_, int gobansize_) 229 { 230 boardsize = boardsize_; 231 gobansize = gobansize_; 232 spacing = (int) (gobansize / (boardsize + 1.5)); 233 offset = (gobansize - spacing * (boardsize - 1)) / 2; 234 font = Image.Fonts.Font(font_filename, spacing / 2); 235 small_font = Image.Fonts.Font(font_filename, spacing / 3); 236 white_stones = ({}); 237 black_stones = ({}); 238 239 markups = ({}); 240 } 241 242 void add_stones(string color, string|array(string) stones) 243 { 244 if (color == "WHITE") 245 white_stones |= Array.arrayify(stones); 246 else if (color == "BLACK") 247 black_stones |= Array.arrayify(stones); 248 } 249 250 int occupied(string vertex) 251 { 252 return (has_value(white_stones, vertex) 253 || has_value(black_stones, vertex)); 254 } 255 256 void add_symbol(string vertex, string name, string color) 257 { 258 markups += ({Markup(upper_case(vertex), name, color)}); 259 } 260 261 void add_text(string vertex, string text, string color) 262 { 263 markups += ({Markup(upper_case(vertex), "text:" + text, color)}); 264 } 265 266 void clear_markup() 267 { 268 markups = ({}); 269 } 270 271 Image.Image draw_board() 272 { 273 Image.Image board = Image.Image(gobansize, gobansize); 274 board = board->clear(220, 150, 50); 275 276 draw_grid(board); 277 278 draw_hoshi_marks(board); 279 280 draw_letters_and_numbers(board); 281 282 foreach (black_stones, string stone) 283 draw_stone(board, "BLACK", stone); 284 foreach (white_stones, string stone) 285 draw_stone(board, "WHITE", stone); 286 287 markups->draw(board); 288 289 return board; 290 } 291 292 static void draw_grid(Image.Image board) 293 { 294 int start = offset; 295 int end = start + (boardsize - 1) * spacing; 296 for (int k = 0; k < boardsize; k++) 297 { 298 int kth = start + k * spacing; 299 board->setcolor(0, 0, 0); 300 board->line(start, kth, end, kth); 301 board->line(kth, start, kth, end); 302 } 303 } 304 305 static void draw_hoshi_marks(Image.Image board) 306 { 307 int a = 2 + (boardsize >= 12); 308 int b = boardsize - a - 1; 309 int c = boardsize / 2; 310 311 if ((boardsize % 2 == 0 && boardsize >= 8) 312 || (boardsize % 2 == 1 && boardsize >= 9)) 313 { 314 draw_disc(board, offset + a * spacing, 315 offset + a * spacing, 0.1 * spacing); 316 draw_disc(board, offset + a * spacing, 317 offset + b * spacing, 0.1 * spacing); 318 draw_disc(board, offset + b * spacing, 319 offset + a * spacing, 0.1 * spacing); 320 draw_disc(board, offset + b * spacing, 321 offset + b * spacing, 0.1 * spacing); 322 } 323 324 if (boardsize % 2 == 1 && boardsize >= 5) 325 draw_disc(board, offset + c * spacing, 326 offset + c * spacing, 0.1 * spacing); 327 328 if (boardsize % 2 == 1 && boardsize >= 13) 329 { 330 draw_disc(board, offset + a * spacing, 331 offset + c * spacing, 0.1 * spacing); 332 draw_disc(board, offset + b * spacing, 333 offset + c * spacing, 0.1 * spacing); 334 draw_disc(board, offset + c * spacing, 335 offset + a * spacing, 0.1 * spacing); 336 draw_disc(board, offset + c * spacing, 337 offset + b * spacing, 0.1 * spacing); 338 } 339 340 } 341 342 static void draw_letters_and_numbers(Image.Image board) 343 { 344 int start = offset; 345 int end = start + (boardsize - 1) * spacing; 346 for (int k = 0; k < boardsize; k++) 347 { 348 int kth = start + k * spacing; 349 Image.Image number = font->write((string) (boardsize - k)); 350 int width = number->xsize(); 351 int height = number->ysize(); 352 board->paste_alpha_color(number, 353 (start - spacing / 2 - width) / 2 - 1, 354 kth - height / 2 + 1); 355 board->paste_alpha_color(number, 356 end + (start + spacing / 2 - width) / 2 + 1, 357 kth - height / 2 + 1); 358 Image.Image letter = font->write(letters[k]); 359 width = letter->xsize(); 360 height = letter->ysize(); 361 board->paste_alpha_color(letter, 362 kth - width / 2, 363 (start - spacing / 2 - height) / 2 - 1); 364 board->paste_alpha_color(letter, 365 kth - width / 2, 366 end + (start + spacing / 2 - height) / 2 + 1); 367 } 368 } 369 370 static void draw_stone(Image.Image board, string color, string vertex) 371 { 372 int start = offset; 373 int x, y; 374 [x, y] = vertex_to_xy(upper_case(vertex)); 375 376 if (color == "BLACK") 377 board->setcolor(0, 0, 0); 378 else if (color == "WHITE") 379 board->setcolor(255, 255, 255); 380 else 381 board->setcolor(128, 128, 128); 382 383 float radius = (spacing + 1) / 2.0; 384 draw_disc(board, start + x * spacing, start + y * spacing, radius); 385 386 } 387 388 array(int) vertex_to_xy(string vertex) 389 { 390 int x = search(letters, vertex[0..0]); 391 int y = boardsize - (int) vertex[1..]; 392 return ({x, y}); 393 } 394 395 string xy_to_vertex(int x, int y) 396 { 397 return letters[x] + (boardsize - y); 398 } 399 400 static array(int) vertex_to_pixel_coord(string vertex) 401 { 402 [int x, int y] = vertex_to_xy(vertex); 403 return ({offset + x * spacing, offset + y * spacing}); 404 } 405 406 string pixel_coord_to_vertex(int|float pixel_x, int|float pixel_y) 407 { 408 int x = (int) floor((pixel_x - offset + spacing / 2.0) / spacing); 409 int y = (int) floor((pixel_y - offset + spacing / 2.0) / spacing); 410 if (x < 0 || x >= boardsize || y < 0 || y >= boardsize) 411 return ""; 412 return sprintf("%s%d", letters[x], boardsize - y); 413 } 414 415 static void draw_disc(Image.Image image, int|float x, int|float y, 416 int|float r, float|void alpha) 417 { 418 int N = 20; 419 x = (float) x; 420 y = (float) y; 421 r = (float) r; 422 if (!alpha) 423 alpha = 1.0; 424 array(float) coords = ({}); 425 for (int k = 0; k < N; k++) 426 coords += ({r + 0.5 + r * cos(2 * 3.14159265 * k / N), 427 r + 0.5 + r * sin(2 * 3.14159265 * k / N)}); 428 Image.Image disc = Image.Image((int) ceil(2*r), (int) ceil(2*r)); 429 disc->setcolor(255, 255, 255); 430 disc->polyfill(coords); 431 image->paste_alpha_color(disc*alpha, (int) (x - 0.5 * disc->xsize()), 432 (int) (y - 0.5 * disc->ysize())); 433 } 434} 435 436string font_filename = ""; 437 438int main(int argc, array(string) argv) 439{ 440 if (argc < 2) { 441 werror("Usage: %s TEST-FILE:TEST-NUMBER\n", basename(argv[0])); 442 return 1; 443 } 444 445 if (!find_font()) 446 return 1; 447 448 SimpleGtp engine = SimpleGtp("../interface/gnugo --quiet --mode gtp -w -t -d0x101840" / " "); 449 if (!engine) 450 { 451 werror("Failed to start engine."); 452 return 1; 453 } 454 455 GTK.setup_gtk(argv); 456 Controller controller = Controller(engine, argv[1..]); 457 458 GTK.main(); 459 460 return 0; 461} 462 463 464array(string) recursive_find_files(string dir, string suffix) 465{ 466 array(string) found_files = ({}); 467 if (!get_dir(dir)) 468 return ({}); 469 foreach (get_dir(dir), string filename) 470 { 471 string full_name = dir + "/" + filename; 472 if (Stdio.is_dir(full_name)) 473 found_files += recursive_find_files(full_name, suffix); 474 else if (has_suffix(filename, suffix)) 475 found_files += ({full_name}); 476 } 477 return found_files; 478} 479 480int find_font() 481{ 482 if (getenv("GNUGO_FONT")) 483 { 484 font_filename = getenv("GNUGO_FONT"); 485 return 1; 486 } 487 488 // Search for fonts below /usr/share/fonts. 489 array(string) font_files = recursive_find_files("/usr/share/fonts", 490 ".ttf"); 491 if (sizeof(font_files) == 0) 492 font_files = recursive_find_files("/usr/share/fonts", ".pfb"); 493 494 if (sizeof(font_files) == 0) 495 { 496 werror("No font found while searching below /usr/share/fonts.\n"); 497 werror("Locate a font file with suffix .ttf (truetype) or .pfb (type1)\n"); 498 werror("and point to it from the environment variable GNUGO_FONT.\n"); 499 return 0; 500 } 501 502 // Compute the length of the filename proper, i.e. without the 503 // path to the file. 504 int fontlength(string s) { 505 return sizeof((s / "/")[-1]); 506 }; 507 508 // Choose the one with shortest name (arbitrary but may avoid e.g. 509 // italic fonts). 510 font_filename = font_files[0]; 511 foreach (font_files[1..], string font_file) 512 { 513 if (fontlength(font_filename) > fontlength(font_file)) 514 font_filename = font_file; 515 else if (fontlength(font_filename) == fontlength(font_file) 516 && has_value(lower_case(font_filename), "mono")) 517 font_filename = font_file; 518 } 519 520 return 1; 521} 522 523class RegressionViewer 524{ 525 Goban goban; 526 SimpleGtp engine; 527 array(string) traces; 528 529 GTK.Widget goban_widget; 530 GTK.Widget data_widget; 531 532 GTK.ScrolledWindow scrolled_data_window; 533 GTK.Image gtk_image; 534 GDK.Image gdk_image; 535 GTK.Clist clist; 536 537 Controller parent; //Evil. Used for callbacks. 538 539 mapping(string:array(string)) worms = ([]); 540 mapping(string:array(string)) dragons = ([]); 541 int worms_initialized = 0; 542 int dragons_initialized = 0; 543 544 string name; 545 string result; 546 string testcase_command; 547 array(string) complete_test; 548 549 function on_board_click_callback; 550 551 static void create(SimpleGtp engine_, 552 array(string) complete_test_, string testcase_command_, 553 function callback, string name_, 554 Controller parent_) 555 { 556 engine = engine_; 557 parent = parent_; 558 complete_test = complete_test_; 559 testcase_command = testcase_command_; 560 name = name_; 561 562 load_testcase(); 563 werror("%s\n", send_command("showboard")); 564 int boardsize = (int) send_command("query_boardsize"); 565 on_board_click_callback = callback; 566 567 setup_board(boardsize); 568 569 scrolled_data_window = GTK.ScrolledWindow(); 570 scrolled_data_window->set_policy(GTK.POLICY_AUTOMATIC, 571 GTK.POLICY_AUTOMATIC); 572 573 clist = GTK.Clist(3); 574 scrolled_data_window->add(clist); 575 handle_testcase(); 576 } 577 578 static void setup_board(int boardsize) 579 { 580 goban = Goban(boardsize, 600); 581 goban->add_stones("WHITE", send_command("list_stones white") / " "); 582 goban->add_stones("BLACK", send_command("list_stones black") / " "); 583 Image.Image im = goban->draw_board(); 584 585 gdk_image = GDK.Image(0)->set(im); 586 gtk_image = GTK.Image(gdk_image); 587 goban_widget = GTK.EventBox()->add(gtk_image); 588 goban_widget->add_events(GDK.ButtonPressMask); 589 goban_widget->add_events(GDK.KeyPressMask); 590 goban_widget->signal_connect_new("button_press_event", 591 button_pressed_on_board); 592 goban_widget->signal_connect_new("key_press_event", 593 key_pressed_on_board); 594 } 595 596 597 void new_testcase(array(string) complete_test_, string testcase_command_) 598 { 599 werror("Loading new testcase.\n"); 600 worms_initialized = 0; 601 dragons_initialized = 0; 602 result = ""; 603 worms = ([]); 604 dragons = ([]); 605 606 complete_test = complete_test_; 607 testcase_command = testcase_command_; 608 609 load_testcase(); 610 werror("%s\n", send_command("showboard")); 611 int boardsize = (int) send_command("query_boardsize"); 612 613 werror("Loaded new testcase.\n"); 614 goban = Goban(boardsize, 600); 615 goban->add_stones("WHITE", send_command("list_stones white") / " "); 616 goban->add_stones("BLACK", send_command("list_stones black") / " "); 617 redraw_board(); 618 } 619 620 621 static void load_testcase() 622 { 623 foreach(complete_test, string testline) { 624 werror(testline + "\n"); 625 if (!has_value("0123456789 #", testline[0..0])) 626 send_command(testline); 627 } 628 } 629 630 void handle_testcase() 631 { 632 traces = ({}); 633 engine->trace_callback = collect_traces; 634 result = send_command(testcase_command); 635 redraw_board(); 636 engine->trace_callback = 0; 637 } 638 639 static void collect_traces(string s) 640 { 641 traces += ({s}); 642 } 643 644 void get_dragons() 645 { 646 foreach (send_command("dragon_stones") / "\n", string dragon) 647 dragons[(dragon / " ")[0]] = dragon / " " - ({""}); 648 dragons_initialized = 1; 649 } 650 651 static void get_worms() 652 { 653 foreach (send_command("worm_stones") / "\n", string worm) 654 worms[(worm / " ")[0]] = worm / " " - ({""}); 655 worms_initialized = 1; 656 } 657 658 659 void add_markup(int mode) 660 { 661 goban->clear_markup(); 662 if (mode <= 4) { 663 function add_suitable_markup = ({add_worms_and_dragons_markup, 664 add_move_generation_markup, 665 add_eyes_markup, 666 add_influence_markup, 667 add_reading_markup})[mode]; 668 add_suitable_markup(); 669 redraw_board(); 670 } 671 } 672 673 static void add_worms_and_dragons_markup() 674 { 675 mapping status_colors = (["alive":"green", 676 "critical":"yellow", 677 "dead":"red", 678 "unknown":"blue"]); 679 mapping safety_colors = (["alive":"green", 680 "critical":"yellow", 681 "dead":"red", 682 "tactically dead":"brown", 683 "alive in seki":"cyan", 684 "strongly alive":"blue", 685 "invincible":"purple", 686 "inessential":"orange"]); 687 688 if (parent->dragon_status_button->get_active()) 689 { 690 if (!dragons_initialized) 691 get_dragons(); 692 foreach (dragons; string dragon; array(string) stones) 693 { 694 string status = get_worm_or_dragon_data("dragon", "status", 695 dragon); 696 foreach (stones, string stone) 697 goban->add_symbol(stone, "dot", 698 status_colors[status]); 699 } 700 } 701 else if (parent->dragon_safety_button->get_active()) 702 { 703 if (!dragons_initialized) 704 get_dragons(); 705 foreach (dragons; string dragon; array(string) stones) 706 { 707 string safety = get_worm_or_dragon_data("dragon", "safety", 708 dragon); 709 foreach (stones, string stone) 710 goban->add_symbol(stone, "dot", 711 safety_colors[safety]); 712 } 713 } 714 else if (parent->worm_status_button->get_active()) 715 { 716 if (!worms_initialized) 717 get_worms(); 718 foreach (worms; string worm; array(string) stones) 719 { 720 string attack = get_worm_or_dragon_data("worm", "attack_code", 721 worm); 722 string defense = get_worm_or_dragon_data("worm", 723 "defense_code", worm); 724 string status = "alive"; 725 if (attack != "0") 726 { 727 if (defense == "0") 728 status = "dead"; 729 else 730 status = "critical"; 731 } 732 733 foreach (stones, string stone) 734 goban->add_symbol(stone, "dot", 735 status_colors[status]); 736 } 737 } 738 } 739 740 static void add_move_generation_markup() 741 { 742 if ((parent->top_moves_button->get_active() 743 || parent->all_moves_button->get_active()) 744 && (has_prefix(testcase_command, "reg_genmove") 745 || has_prefix(testcase_command, "restricted_genmove"))) 746 { 747 string color = "green"; 748 string answers = parent->expected_result; 749 if (answers[0..0] == "!") { 750 answers = answers[1..]; 751 color = "red"; 752 } 753 if (has_prefix(testcase_command, "restricted_genmove")) 754 { 755 foreach ((testcase_command / " ")[2..], string allowed_move) 756 { 757 if (color == "green" ^ has_value(answers, allowed_move)) 758 goban->add_symbol(allowed_move, "triangle", "red"); 759 else 760 goban->add_symbol(allowed_move, "triangle", "green"); 761 } 762 } 763 else 764 foreach (answers / "|" - ({""}), string answer) 765 goban->add_symbol(answer, "big_square", color); 766 767 768 color = (testcase_command / " ")[1]; 769 goban->add_symbol(result, "stone", color); 770 } 771 772 if (parent->top_moves_button->get_active()) 773 { 774 array(string) top_moves = send_command("top_moves") / " "; 775 for (int k = 0; k < sizeof(top_moves) / 2; k++) 776 goban->add_text(top_moves[2 * k], 777 top_moves[2 * k + 1], "blue"); 778 } 779 else if (parent->all_moves_button->get_active()) 780 { 781 array(string) all_moves = send_command("all_move_values") / "\n"; 782 foreach (all_moves, string move_value) 783 { 784 sscanf(move_value, "%s%*[ ]%s", string vertex, string value); 785 goban->add_text(vertex, value, "blue"); 786 } 787 } 788 else if (parent->delta_territory_button->get_active() 789 && parent->delta_territory_move != "PASS") 790 { 791 goban->add_symbol(parent->delta_territory_move, "stone", "gray"); 792 parent->delta_territory_button_text 793 ->set_text("delta territory for " 794 + parent->delta_territory_move); 795 clist->clear(); 796 797 int k; 798 for (k = sizeof(traces) - 1; k >= 0; k--) 799 { 800 if (sscanf(traces[k], " " + parent->delta_territory_move 801 + ": %*f - change in territory%*s") == 2 802 && !has_value(traces[k], "cached")) 803 break; 804 } 805 if (k >= 0) 806 { 807 clist->append(({traces[k], "", ""})); 808 for (k--; k >= 0; k--) 809 { 810 if (sscanf(traces[k], " %*s: - %*s") < 2) 811 break; 812 813 clist->prepend(({traces[k], "", ""})); 814 if (sscanf(traces[k], 815 " %*s: - %s territory change %s ", 816 string vertex, string value) == 3) 817 { 818 goban->add_text(vertex, value, 819 (float) value < 0.0 ? "red" : "blue"); 820 } 821 } 822 } 823 clist->columns_autosize(); 824 parent->set_title(this_object(), 825 ("Delta territory for " 826 + parent->delta_territory_move)); 827 } 828 } 829 830 static mapping(string:mapping(string:string)) eye_data = 0; 831 static mapping(string:string) half_eye_data = ([]); 832 static mapping(string:mapping(string:string)) eye_types = 0; 833 834 static void add_eyes_markup() 835 { 836 if (!eye_data) 837 { 838 eye_data = (["white":([]), "black":([])]); 839 eye_types = (["white":([]), "black":([])]); 840 for (int y = 0; y < goban->boardsize; y++) 841 { 842 for (int x = 0; x < goban->boardsize; x++) 843 { 844 string vertex = goban->xy_to_vertex(x, y); 845 multiset(string) is_marginal = (<>); 846 foreach (({"white", "black"}), string color) 847 { 848 string this_eye = send_command("eye_data " + color + 849 " " + vertex); 850 if (!Regexp("origin +PASS")->match(this_eye)) 851 { 852 eye_data[color][vertex] = this_eye; 853 if (Regexp("marginal +1")->match(this_eye)) 854 is_marginal[color] = 1; 855 } 856 } 857 858 if (!eye_data["white"][vertex] 859 && !eye_data["black"][vertex]) 860 continue; 861 862 string half_eye = send_command("half_eye_data " + vertex); 863 if (!Regexp("type +0")->match(half_eye)) 864 half_eye_data[vertex] = half_eye; 865 866 foreach (({"white", "black"}), string color) 867 { 868 if (!eye_data[color][vertex]) 869 continue; 870 if (is_marginal[color]) 871 eye_types[color][vertex] = "marginal"; 872 else if (Regexp("type +HALF_EYE")->match(half_eye)) 873 eye_types[color][vertex] = "half"; 874 else 875 eye_types[color][vertex] = "proper"; 876 } 877 } 878 } 879 } 880 881 string color; 882 if (parent->white_eyes_button->get_active()) 883 color = "white"; 884 else 885 color = "black"; 886 887 foreach (eye_types[color]; string vertex; string eye_type) 888 { 889 mapping (string:string) grayish = (["white" : "#c0c0c0", 890 "black" : "#404040"]); 891 if (eye_type == "proper") 892 goban->add_symbol(vertex, "big_dot", color); 893 else if (eye_type == "half") 894 goban->add_symbol(vertex, "big_dot", grayish[color]); 895 else 896 goban->add_symbol(vertex, "dot", grayish[color]); 897 } 898 } 899 900 static void add_influence_markup() 901 { 902 string command; 903 string what_data; 904 if (parent->initial_w_influence_dragons_known_button->get_active()) 905 command = "initial_influence white"; 906 else if (parent->initial_b_influence_dragons_known_button->get_active()) 907 command = "initial_influence black"; 908 else if (parent->after_move_influence_button->get_active()) 909 { 910 if (parent->move_influence_move == "PASS") 911 return; 912 command = sprintf("move_influence %s %s", 913 parent->current_move_color, 914 parent->move_influence_move); 915 goban->add_symbol(parent->move_influence_move, "stone", "gray"); 916 } 917 else if (parent->followup_influence_button->get_active()) 918 { 919 if (parent->move_influence_move == "PASS") 920 return; 921 command = sprintf("followup_influence %s %s", 922 parent->current_move_color, 923 parent->move_influence_move); 924 goban->add_symbol(parent->move_influence_move, "stone", "gray"); 925 } 926 927 if (parent->influence_regions_button->get_active()) 928 what_data = "influence_regions"; 929 if (parent->territory_value_button->get_active()) 930 what_data = "territory_value"; 931 else if (parent->white_influence_button->get_active()) 932 what_data = "white_influence"; 933 else if (parent->black_influence_button->get_active()) 934 what_data = "black_influence"; 935 else if (parent->white_influence_button->get_active()) 936 what_data = "white_influence"; 937 else if (parent->black_influence_button->get_active()) 938 what_data = "black_influence"; 939 else if (parent->white_strength_button->get_active()) 940 what_data = "white_strength"; 941 else if (parent->black_strength_button->get_active()) 942 what_data = "black_strength"; 943 else if (parent->white_permeability_button->get_active()) 944 what_data = "white_permeability"; 945 else if (parent->black_permeability_button->get_active()) 946 what_data = "black_permeability"; 947 else if (parent->white_attenuation_button->get_active()) 948 what_data = "white_attenuation"; 949 else if (parent->black_attenuation_button->get_active()) 950 what_data = "black_attenuation"; 951 else if (parent->non_territory_button->get_active()) 952 what_data = "non_territory"; 953 954 string result = send_command(command + " " + what_data); 955 array(string) values = (replace(result, "\n", " ") / " ") - ({""}); 956 int k = 0; 957 for (int y = 0; y < goban->boardsize; y++) 958 { 959 for (int x = 0; x < goban->boardsize; x++) 960 { 961 string vertex = goban->xy_to_vertex(x, y); 962 if (what_data == "influence_regions") 963 { 964 int value = (int) values[k]; 965 mapping(int:string) sizes = ([3:"big_dot", 966 2:"big_dot", 967 1:"dot", 968 -1:"dot", 969 -2:"big_dot", 970 -3:"big_dot"]); 971 mapping(int:string) colors = ([3:"white", 972 2:"#c0c0c0", 973 1:"#c0c0c0", 974 -1:"#404040", 975 -2:"#404040", 976 -3:"black"]); 977 if (sizes[value]) 978 goban->add_symbol(vertex, sizes[value], colors[value]); 979 } 980 else if (has_value(what_data, "permeability")) 981 { 982 if ((float) values[k] != 1.0) 983 goban->add_text(vertex, values[k], "blue"); 984 } 985 else if (what_data == "non_territory") 986 { 987 if (!goban->occupied(vertex)) 988 { 989 int value = (int) values[k]; 990 if (value == 1) 991 goban->add_symbol(vertex, "dot", "black"); 992 else if (value == 2) 993 goban->add_symbol(vertex, "dot", "white"); 994 else if (value == 0) 995 goban->add_symbol(vertex, "dot", "gray"); 996 } 997 } 998 else if ((float) values[k] > 0.0) 999 goban->add_text(vertex, values[k], "blue"); 1000 else if ((float) values[k] < 0.0) 1001 goban->add_text(vertex, values[k], "red"); 1002 1003 k++; 1004 } 1005 } 1006 } 1007 1008 static void add_reading_markup() 1009 { 1010 if ((parent->connection_reading_button->get_active() 1011 || parent->semeai_reading_button->get_active()) 1012 && parent->first_vertex != "") 1013 goban->add_symbol(parent->first_vertex, 1014 "big_dot", "green"); 1015 } 1016 1017 void redraw_board() 1018 { 1019 gdk_image->set(goban->draw_board()); 1020 gtk_image->queue_draw(); 1021 } 1022 1023 void show_worm_data(string vertex) 1024 { 1025 string worm_data = send_command("worm_data " + vertex); 1026 clist->clear(); 1027 foreach ((worm_data / "\n")[1..], string data_item) 1028 { 1029 sscanf(data_item, "%s%*[ ]%s", string field, string value); 1030 clist->append(({field, value, ""})); 1031 } 1032 clist->columns_autosize(); 1033 parent->set_title(this_object(), "Worm data for " + vertex); 1034 } 1035 1036 void show_dragon_data(string vertex, int part) 1037 { 1038 string dragon_data = send_command("dragon_data " + vertex); 1039 clist->clear(); 1040 1041 array(string) selected_data; 1042 if (part == 1) 1043 selected_data = (dragon_data / "\n")[1..20]; 1044 else 1045 selected_data = (dragon_data / "\n")[21..]; 1046 1047 foreach (selected_data, string data_item) 1048 { 1049 sscanf(data_item, "%s%*[ ]%s", string field, string value); 1050 clist->append(({field, value, ""})); 1051 } 1052 clist->columns_autosize(); 1053 parent->set_title(this_object(), "Dragon data for " + vertex); 1054 } 1055 1056 void show_move_reasons(string vertex) 1057 { 1058 string move_reasons = send_command("move_reasons " + vertex); 1059 clist->clear(); 1060 1061 foreach ((move_reasons / "\n"), string move_reason) 1062 clist->append(({move_reason, "", ""})); 1063 1064 int k; 1065 for (k = sizeof(traces) - 1; k >= 0; k--) 1066 { 1067 if (sscanf(traces[k], "Move generation values " 1068 + vertex + " to %*s") == 1) 1069 break; 1070 } 1071 if (k >= 0) 1072 { 1073 array(string) interesting_lines = ({traces[k]}); 1074 for (k--; k >= 0; k--) 1075 { 1076 if (sscanf(traces[k], " " + vertex + ": %*s") == 1) 1077 interesting_lines += ({traces[k]}); 1078 else if (sscanf(traces[k], " " + vertex + ": %*s") != 1) 1079 break; 1080 } 1081 clist->append(({"", "", ""})); 1082 foreach (reverse(interesting_lines), string line) 1083 clist->append(({line, "", ""})); 1084 } 1085 1086 int first_pattern = 1; 1087 int add_continuation_lines = 0; 1088 foreach (traces, string line) 1089 { 1090 if (sscanf(line, "pattern %*s matched at " + vertex + "%s", 1091 string s) == 2 1092 && s == "") 1093 { 1094 if (first_pattern) 1095 { 1096 clist->append(({"", "", ""})); 1097 first_pattern = 0; 1098 } 1099 add_continuation_lines = 1; 1100 clist->append(({line, "", ""})); 1101 } 1102 else if (has_prefix(line, "...") && add_continuation_lines) 1103 clist->append(({line, "", ""})); 1104 else 1105 add_continuation_lines = 0; 1106 } 1107 1108 /* Look for blunder devaluation */ 1109 foreach (traces, string line) 1110 if (has_prefix(line, "Move at " + vertex + " is")) 1111 clist->append(({line, "", ""})); 1112 1113 clist->columns_autosize(); 1114 parent->set_title(this_object(), "Move reasons for " + vertex); 1115 } 1116 1117 void show_eye_data(string vertex) 1118 { 1119 clist->clear(); 1120 1121 string color; 1122 if (parent->white_eyes_button->get_active()) 1123 color = "white"; 1124 else 1125 color = "black"; 1126 1127 if (eye_data[color][vertex]) 1128 { 1129 foreach (eye_data[color][vertex] / "\n", string data_item) 1130 { 1131 sscanf(data_item, "%s%*[ ]%s", string field, string value); 1132 clist->append(({field, value, ""})); 1133 } 1134 if (half_eye_data[vertex]) 1135 { 1136 clist->append(({"", "", ""})); 1137 foreach (half_eye_data[vertex] / "\n", string data_item) 1138 { 1139 sscanf(data_item, "%s%*[ ]%s", string field, string value); 1140 clist->append(({field, value, ""})); 1141 } 1142 } 1143 } 1144 1145 clist->columns_autosize(); 1146 parent->set_title(this_object(), color + " eye data for " + vertex); 1147 } 1148 1149 1150 static void button_pressed_on_board(GDK.Event event) 1151 { 1152// werror("Button: %O\n", (mapping) event); 1153 string vertex = goban->pixel_coord_to_vertex(event->x, event->y); 1154 on_board_click_callback(vertex); 1155 } 1156 1157 static void key_pressed_on_board(GDK.Event event) 1158 { 1159// werror("Key: %O\n", (mapping) event); 1160 // First thing to find out is what the event object contains and 1161 // how we can get the mouse position when the key was pressed. 1162 } 1163 1164 string send_command(string command) 1165 { 1166 string result; 1167 result = engine->send_command(command)->text; 1168 return result; 1169 } 1170 1171 mapping(string:string) worm_and_dragon_cache = ([]); 1172 1173 static string get_worm_or_dragon_data(string worm_or_dragon, string field, 1174 string vertex) 1175 { 1176 string command = worm_or_dragon + "_data " + vertex; 1177 string data = worm_and_dragon_cache[command]; 1178 if (!data) 1179 { 1180 data = send_command(command); 1181 worm_and_dragon_cache[command] = data; 1182 } 1183 1184 foreach (data / "\n", string row) 1185 if (has_prefix(row, field)) 1186 { 1187 sscanf(row, "%*s%*[ ]%s", string value); 1188 return value; 1189 } 1190 1191 return ""; 1192 } 1193 1194 void do_reading(string reset_counter, string get_counter, 1195 string sgffilename, string sgf_viewer_cmd, 1196 string first_command, string second_command) 1197 { 1198 string result; 1199 send_command("clear_cache"); 1200 if (sizeof(parent->viewers) > 1) 1201 sgffilename += "." + replace(name, " ", "_"); 1202 if (sgffilename != "") 1203 send_command("start_sgftrace"); 1204 1205 send_command(reset_counter); 1206 result = send_command(first_command); 1207 clist->append(({first_command, result, 1208 "(" + send_command(get_counter) + " nodes)"})); 1209 1210 if (result[0..0] != "0" && second_command != "") 1211 { 1212 send_command(reset_counter); 1213 string result = send_command(second_command); 1214 clist->append(({second_command, result, 1215 "(" + send_command(get_counter) + " nodes)"})); 1216 } 1217 if (sgffilename != "") 1218 send_command("finish_sgftrace " + sgffilename); 1219 if (sgf_viewer_cmd != "") 1220 Process.create_process(sprintf(sgf_viewer_cmd, sgffilename) 1221 / " "); 1222 clist->columns_autosize(); 1223 parent->set_title(this_object(), "Reading result"); 1224 } 1225} 1226 1227 1228class Controller 1229{ 1230 array(RegressionViewer) viewers = ({}); 1231 array(GTK.Widget) viewer_title_widgets = ({}); 1232 1233 string current_move_color = ""; 1234 1235 GTK.Window main_window; 1236 GTK.Notebook controller_notebook; 1237 GTK.Notebook gobans_notebook; 1238 GTK.Notebook data_notebook; 1239 GTK.Notebook selector_notebook; 1240 1241 GTK.ScrolledWindow scrolled_testcase_text; 1242 GTK.Label testcase_text; 1243 1244 GTK.RadioButton worm_data_button; 1245 GTK.RadioButton dragon_data1_button; 1246 GTK.RadioButton dragon_data2_button; 1247 GTK.RadioButton worm_status_button; 1248 GTK.RadioButton dragon_status_button; 1249 GTK.RadioButton dragon_safety_button; 1250 1251 GTK.RadioButton top_moves_button; 1252 GTK.RadioButton all_moves_button; 1253 GTK.RadioButton delta_territory_button; 1254 GTK.Label delta_territory_button_text; 1255 1256// GTK.RadioButton initial_w_influence_dragons_unknown_button; 1257// GTK.RadioButton initial_b_influence_dragons_unknown_button; 1258 GTK.RadioButton initial_w_influence_dragons_known_button; 1259 GTK.RadioButton initial_b_influence_dragons_known_button; 1260 GTK.RadioButton after_move_influence_button; 1261 GTK.Label after_move_influence_button_text; 1262 GTK.RadioButton followup_influence_button; 1263 GTK.Label followup_influence_button_text; 1264 GTK.RadioButton influence_regions_button; 1265 GTK.RadioButton territory_value_button; 1266 GTK.RadioButton white_influence_button; 1267 GTK.RadioButton black_influence_button; 1268 GTK.RadioButton white_strength_button; 1269 GTK.RadioButton black_strength_button; 1270 GTK.RadioButton white_permeability_button; 1271 GTK.RadioButton black_permeability_button; 1272 GTK.RadioButton white_attenuation_button; 1273 GTK.RadioButton black_attenuation_button; 1274 GTK.RadioButton non_territory_button; 1275 1276 GTK.RadioButton white_eyes_button; 1277 GTK.RadioButton black_eyes_button; 1278 1279 GTK.RadioButton tactical_reading_button, owl_reading_button, 1280 owl_does_attack_button, owl_does_defend_button, 1281 connection_reading_button, semeai_reading_button; 1282 GTK.CheckButton sgf_traces_button, sgf_viewer_button; 1283 GTK.Entry sgf_filename_entry, sgf_viewer_entry; 1284 GTK.Table sgf_stuff; 1285 GTK.Button new_testcase_button, new_engine_button; 1286 GTK.Button next_testcase_button, prev_testcase_button; 1287 GTK.Entry new_testcase_entry, engine_path_entry, engine_name_entry; 1288 1289 string delta_territory_move = "PASS"; 1290 string move_influence_move = "PASS"; 1291 string first_vertex = ""; 1292 1293 int testcase_index = 0; 1294 1295 array(string) testcases; 1296 string testcase_name; 1297 string testcase_command; 1298 string result; 1299 string expected_result; 1300 1301 // All lines from the test file shown at the top of the control window. 1302 array(string) full_testcase; 1303 1304 // All lines from the test file which may be needed to load the 1305 // testcase correctly. 1306 array(string) complete_testcase; 1307 1308 static mixed nasty_signal_id; 1309 1310 static void create(SimpleGtp engine_, array(string) testcases_) 1311 { 1312 testcases = testcases_; 1313 if (!excerpt_testcase(testcases[0], engine_)) 1314 { 1315 werror("Failed to load testcase.\n"); 1316 exit(1); 1317 } 1318 testcase_name = testcases[0]; 1319 1320 scrolled_testcase_text 1321 = GTK.ScrolledWindow(GTK.Adjustment(), GTK.Adjustment()) 1322 ->set_policy(GTK.POLICY_AUTOMATIC, GTK.POLICY_AUTOMATIC) 1323 ->set_usize(450, 100); 1324 testcase_text = GTK.Label(full_testcase * "\n") 1325 ->set_justify(GTK.JUSTIFY_LEFT) 1326 ->set_alignment(0.0, 0.0); 1327 scrolled_testcase_text->add(testcase_text); 1328 1329 main_window = GTK.Window(GTK.WindowToplevel); 1330 controller_notebook = GTK.Notebook(); 1331 controller_notebook->set_tab_pos(GTK.POS_LEFT); 1332 1333 gobans_notebook = (GTK.Notebook() 1334 ->set_show_tabs(0)); 1335 data_notebook = (GTK.Notebook() 1336 ->set_show_tabs(0) 1337 ->set_show_border(0)); 1338 selector_notebook = (GTK.Notebook() 1339 ->set_tab_pos(GTK.POS_TOP)); 1340 selector_notebook->signal_connect_new("switch_page", change_engine); 1341 1342 GTK.Widget main_window_contents 1343 = (GTK.Vbox(0, 0) 1344 ->pack_start(GTK.Hbox(0, 24) 1345 ->pack_start(scrolled_testcase_text, 0, 0, 24) 1346 ->pack_start(GTK.Alignment(0.0, 0.5, 0.0, 0.0) 1347 ->add(selector_notebook), 1348 0, 0, 0), 1349 0, 0, 0) 1350 ->add(GTK.Hbox(0, 2) 1351 ->add(GTK.Vbox(0, 6) 1352 ->pack_start(controller_notebook, 0, 0, 0) 1353 ->add(data_notebook)) 1354 ->pack_start(GTK.Alignment(0.0, 0.0, 0.0, 0.0) 1355 ->add(gobans_notebook), 1356 0, 0, 0))); 1357 main_window->add(main_window_contents); 1358 1359 main_window->set_title(testcases[0]); 1360 main_window->signal_connect_new("destroy", quit); 1361 1362 if (has_prefix(testcase_command, "reg_genmove") 1363 || has_prefix(testcase_command, "restricted_genmove")) 1364 { 1365 sscanf(testcase_command, "%*s %s", string color); 1366 if (lower_case(color[0..0]) == "w") 1367 current_move_color = "white"; 1368 else 1369 current_move_color = "black"; 1370 } 1371 1372 worm_data_button = GTK.RadioButton("worm data"); 1373 dragon_data1_button = GTK.RadioButton("dragon data, part 1", 1374 worm_data_button); 1375 dragon_data2_button = GTK.RadioButton("dragon data, part 2", 1376 worm_data_button); 1377 1378 worm_status_button = GTK.RadioButton("worm status"); 1379 dragon_status_button = GTK.RadioButton("dragon status", 1380 worm_status_button); 1381 dragon_safety_button = GTK.RadioButton("dragon safety", 1382 worm_status_button); 1383 worm_status_button->signal_connect_new("clicked", 1384 markup_button_pressed); 1385 dragon_status_button->signal_connect_new("clicked", 1386 markup_button_pressed); 1387 dragon_safety_button->signal_connect_new("clicked", 1388 markup_button_pressed); 1389 1390 top_moves_button = GTK.RadioButton("top moves"); 1391 all_moves_button = GTK.RadioButton("all moves", top_moves_button); 1392 delta_territory_button_text = GTK.Label("delta territory for PASS"); 1393 delta_territory_button_text->set_alignment(0.0, 0.0); 1394 delta_territory_button = GTK.RadioButton(0, top_moves_button); 1395 delta_territory_button->add(delta_territory_button_text); 1396 top_moves_button->signal_connect_new("clicked", markup_button_pressed); 1397 all_moves_button->signal_connect_new("clicked", markup_button_pressed); 1398 delta_territory_button->signal_connect_new("clicked", 1399 markup_button_pressed); 1400 1401 white_eyes_button = GTK.RadioButton("white eyes"); 1402 black_eyes_button = GTK.RadioButton("black eyes", white_eyes_button); 1403 white_eyes_button->signal_connect_new("clicked", 1404 markup_button_pressed); 1405 black_eyes_button->signal_connect_new("clicked", 1406 markup_button_pressed); 1407 1408// initial_w_influence_dragons_unknown_button = 1409// GTK.RadioButton("white influence, dragons unknown"); 1410// initial_b_influence_dragons_unknown_button = 1411// GTK.RadioButton("black influence, dragons unknown", 1412// initial_w_influence_dragons_unknown_button); 1413 initial_w_influence_dragons_known_button = 1414 GTK.RadioButton("white influence, dragons known"); 1415 initial_b_influence_dragons_known_button = 1416 GTK.RadioButton("black influence, dragons known", 1417 initial_w_influence_dragons_known_button); 1418 after_move_influence_button_text = 1419 GTK.Label("after move influence for PASS"); 1420 after_move_influence_button_text->set_alignment(0.0, 0.0); 1421 after_move_influence_button = 1422 GTK.RadioButton(0, initial_w_influence_dragons_known_button); 1423 after_move_influence_button->add(after_move_influence_button_text); 1424 followup_influence_button_text = 1425 GTK.Label("followup influence for PASS"); 1426 followup_influence_button_text->set_alignment(0.0, 0.0); 1427 followup_influence_button = 1428 GTK.RadioButton(0, initial_w_influence_dragons_known_button); 1429 followup_influence_button->add(followup_influence_button_text); 1430 influence_regions_button = GTK.RadioButton("influence regions"); 1431 territory_value_button = GTK.RadioButton("territory value", 1432 influence_regions_button); 1433 white_influence_button = GTK.RadioButton("white influence", 1434 influence_regions_button); 1435 black_influence_button = GTK.RadioButton("black influence", 1436 influence_regions_button); 1437 white_strength_button = GTK.RadioButton("white strength", 1438 influence_regions_button); 1439 black_strength_button = GTK.RadioButton("black strength", 1440 influence_regions_button); 1441 white_permeability_button = GTK.RadioButton("white permeability", 1442 influence_regions_button); 1443 black_permeability_button = GTK.RadioButton("black permeability", 1444 influence_regions_button); 1445 white_attenuation_button = GTK.RadioButton("white attenuation", 1446 influence_regions_button); 1447 black_attenuation_button = GTK.RadioButton("black attenuation", 1448 influence_regions_button); 1449 non_territory_button = GTK.RadioButton("non-territory", 1450 influence_regions_button); 1451 ({initial_w_influence_dragons_known_button, 1452 initial_b_influence_dragons_known_button, 1453 after_move_influence_button, 1454 followup_influence_button, 1455 influence_regions_button, 1456 territory_value_button, 1457 white_influence_button, 1458 black_influence_button, 1459 white_strength_button, 1460 black_strength_button, 1461 white_permeability_button, 1462 black_permeability_button, 1463 white_attenuation_button, 1464 black_attenuation_button, 1465 non_territory_button})->signal_connect_new("clicked", 1466 markup_button_pressed); 1467 1468 1469 tactical_reading_button = GTK.RadioButton("tactical reading"); 1470 owl_reading_button = GTK.RadioButton("owl reading", 1471 tactical_reading_button); 1472 owl_does_attack_button = GTK.RadioButton("owl_does_attack", 1473 tactical_reading_button); 1474 owl_does_defend_button = GTK.RadioButton("owl_does_defend", 1475 tactical_reading_button); 1476 connection_reading_button = GTK.RadioButton("connection reading", 1477 tactical_reading_button); 1478 semeai_reading_button = GTK.RadioButton("semeai reading", 1479 tactical_reading_button); 1480 sgf_traces_button = (GTK.CheckButton("save sgf traces to") 1481 ->set_active(1)); 1482 sgf_filename_entry = GTK.Entry(); 1483 sgf_filename_entry->set_text("vars.sgf"); 1484 sgf_filename_entry->set_editable(1); 1485 1486 sgf_viewer_button = GTK.CheckButton("start sgf viewer as"); 1487 sgf_viewer_entry = GTK.Entry()->set_text(sgf_viewer_command) 1488 ->set_editable(1); 1489 sgf_viewer_button->signal_connect("toggled", sgf_viewer_button_toggled); 1490 sgf_traces_button->signal_connect("toggled", sgf_traces_button_toggled); 1491 sgf_stuff = GTK.Table(2, 2, 0) 1492 ->attach_defaults(sgf_traces_button, 0, 1, 0, 1) 1493 ->attach_defaults(sgf_filename_entry, 1, 2, 0, 1) 1494 ->attach_defaults(sgf_viewer_button, 0, 1, 1, 2) 1495 ->attach_defaults(sgf_viewer_entry, 1, 2, 1, 2); 1496 1497 new_testcase_entry = GTK.Entry(); 1498 new_testcase_entry->set_text(testcases[0]); 1499 new_testcase_entry->set_editable(1); 1500 new_testcase_button = GTK.Button("Load new testcase"); 1501 new_testcase_button->signal_connect_new("clicked", new_testcase); 1502 new_testcase_entry->signal_connect_new("activate", new_testcase); 1503 if (sizeof(testcases)) { 1504 prev_testcase_button = GTK.Button("Previous testcase"); 1505 prev_testcase_button->signal_connect_new("clicked", prev_testcase); 1506 prev_testcase_button->set_sensitive(0); 1507 next_testcase_button = GTK.Button("Next testcase"); 1508 next_testcase_button->signal_connect_new("clicked", next_testcase); 1509 } 1510 engine_path_entry = GTK.Entry(); 1511 engine_path_entry->set_text("../interface/gnugo"); 1512 engine_path_entry->set_editable(1); 1513 1514 engine_name_entry = GTK.Entry(); 1515 engine_name_entry->set_text("Engine 2"); 1516 engine_name_entry->set_editable(1); 1517 1518 engine_path_entry->signal_connect_new("activate", select_new_engine); 1519 new_engine_button = GTK.Button("Start new engine"); 1520 new_engine_button->signal_connect_new("clicked", select_new_engine); 1521 1522 GTK.Widget worms_and_dragons_page 1523 = (GTK.Vbox(0, 0) 1524 ->pack_start(worm_data_button, 0, 0, 0) 1525 ->pack_start(dragon_data1_button, 0, 0, 0) 1526 ->pack_start(dragon_data2_button, 0, 0, 0) 1527 ->pack_start(GTK.Label(""), 0, 0, 0) 1528 ->pack_start(worm_status_button, 0, 0, 0) 1529 ->pack_start(dragon_status_button, 0, 0, 0) 1530 ->pack_start(dragon_safety_button, 0, 0, 0)); 1531 controller_notebook->append_page(worms_and_dragons_page, 1532 GTK.Label("worms and dragons")); 1533 1534 GTK.Widget move_generation_page 1535 = (GTK.Vbox(0, 0) 1536 ->pack_start(top_moves_button, 0, 0, 0) 1537 ->pack_start(all_moves_button, 0, 0, 0) 1538 ->pack_start(delta_territory_button, 0, 0, 0)); 1539 controller_notebook->append_page(move_generation_page, 1540 GTK.Label("move generation")); 1541 1542 GTK.Widget eyes_page = (GTK.Vbox(0, 0) 1543 ->pack_start(white_eyes_button, 0, 0, 0) 1544 ->pack_start(black_eyes_button, 0, 0, 0)); 1545 controller_notebook->append_page(eyes_page, GTK.Label("eyes")); 1546 1547 GTK.Widget influence_page 1548 = (GTK.Vbox(0, 0) 1549 ->pack_start(GTK.Vbox(0,0) 1550// ->add(initial_w_influence_dragons_unknown_button) 1551// ->add(initial_b_influence_dragons_unknown_button) 1552 ->add(initial_w_influence_dragons_known_button) 1553 ->add(initial_b_influence_dragons_known_button) 1554 ->add(after_move_influence_button) 1555 ->add(followup_influence_button), 0, 0, 0) 1556 ->pack_start(GTK.Label(""), 0, 0, 0) 1557 ->pack_start(GTK.Hbox(0,12) 1558 ->add(GTK.Vbox(0,0) 1559 ->pack_start(influence_regions_button, 0, 0, 0) 1560 ->pack_start(territory_value_button, 0, 0, 0) 1561 ->pack_start(non_territory_button, 0, 0, 0) 1562 ->pack_start(white_influence_button, 0, 0, 0) 1563 ->pack_start(black_influence_button, 0, 0, 0)) 1564 ->add(GTK.Vbox(0,0) 1565 ->pack_start(white_strength_button, 0, 0, 0) 1566 ->pack_start(black_strength_button, 0, 0, 0) 1567 ->pack_start(white_permeability_button, 0, 0, 0) 1568 ->pack_start(black_permeability_button, 0, 0, 0) 1569 ->pack_start(white_attenuation_button, 0, 0, 0) 1570 ->pack_start(black_attenuation_button, 0, 0, 0)), 1571 0, 0, 0)); 1572 controller_notebook->append_page(influence_page, 1573 GTK.Label("influence")); 1574 1575 GTK.Widget reading_page 1576 = (GTK.Vbox(0, 0) 1577 ->pack_start(tactical_reading_button, 0, 0, 0) 1578 ->pack_start(owl_reading_button, 0, 0, 0) 1579 ->pack_start(owl_does_attack_button, 0, 0, 0) 1580 ->pack_start(owl_does_defend_button, 0, 0, 0) 1581 ->pack_start(connection_reading_button, 0, 0, 0) 1582 ->pack_start(semeai_reading_button, 0, 0, 0) 1583 ->pack_start(GTK.Label(""), 0, 0, 0) 1584 ->pack_start(sgf_stuff, 0, 0, 0)); 1585 controller_notebook->append_page(reading_page, GTK.Label("reading")); 1586 1587 GTK.Widget engines_page 1588 = (GTK.Vbox(0, 12) 1589 ->pack_start(engine_path_entry, 0, 0, 0)); 1590 engines_page->pack_start(engine_name_entry, 0, 0, 0); 1591 engines_page->pack_start(GTK.Alignment(1.0, 0.0, 0.0, 0.0) 1592 ->add(new_engine_button), 0, 0, 0) 1593 ->pack_start(new_testcase_entry, 0, 0, 0) 1594 ->pack_start(new_testcase_button, 0, 0, 0); 1595 if (sizeof(testcases) > 1) { 1596 GTK.Widget next_prev 1597 = (GTK.Hbox(0, 12)->pack_start(prev_testcase_button, 0, 0, 0) 1598 ->pack_start(next_testcase_button, 0, 0, 0)); 1599 engines_page->pack_start(next_prev, 0, 0, 0); 1600 } 1601 controller_notebook->append_page(engines_page->set_border_width(12), 1602 GTK.Label("engines")); 1603 1604 nasty_signal_id = controller_notebook->signal_connect_new("switch_page", 1605 add_markup); 1606 1607 if (has_prefix(testcase_command, "reg_genmove") 1608 || has_prefix(testcase_command, "restricted_genmove")) { 1609 controller_notebook->show_all(); 1610 controller_notebook->set_page(1); 1611 } 1612 1613 main_window->show_all(); 1614 1615 add_regression_viewer(RegressionViewer(engine_, 1616 complete_testcase, 1617 testcase_command, 1618 button_pressed_on_a_board, 1619 "Default engine", 1620 this_object())); 1621 add_markup(controller_notebook->get_current_page()); 1622 } 1623 1624 static void select_new_engine() 1625 { 1626 string new_engine_path = engine_path_entry->get_text(); 1627 if (!Stdio.is_file(new_engine_path)) { 1628 viewers->clist->clear(); 1629 viewers->clist->append(({"The engine path does not point to a file.\n", "", ""})); 1630 return; 1631 } 1632 1633 SimpleGtp new_engine = SimpleGtp((new_engine_path + " --quiet --mode gtp -w -t -d0x101840") / " "); 1634 if (!new_engine) 1635 werror("Failed to start new engine.\n"); 1636 else { 1637 add_regression_viewer( 1638 RegressionViewer(new_engine, complete_testcase, 1639 testcase_command, button_pressed_on_a_board, 1640 engine_name_entry->get_text(), 1641 this_object())); 1642 } 1643 1644 engine_name_entry->set_text(sprintf("Engine %d", sizeof(viewers) + 1)); 1645 } 1646 1647 static void add_regression_viewer(RegressionViewer viewer) 1648 { 1649 viewers += ({ viewer }); 1650 viewer->goban_widget->show_all(); 1651 gobans_notebook->append_page(viewer->goban_widget, 0); 1652 1653 GTK.Widget title_label = GTK.Label(""); 1654 viewer_title_widgets += ({ title_label }); 1655 1656 GTK.Widget data_page = (GTK.Vbox(0, 2) 1657 ->pack_start(title_label, 0, 0, 0) 1658 ->add(viewer->scrolled_data_window) 1659 ->show_all()); 1660 data_notebook->append_page(data_page, 0); 1661 1662 selector_notebook 1663 ->append_page((GTK.Alignment(0.0, 0.5, 0.0, 0.0) 1664 ->set_border_width(4) 1665 ->add(GTK.Label(viewer->engine->command_line))), 1666 GTK.Label(viewer->name)); 1667 selector_notebook->show_all(); 1668 selector_notebook->set_page(sizeof(viewers) - 1); 1669 } 1670 1671 void set_title(RegressionViewer viewer, string title) 1672 { 1673 for (int k = 0; k < sizeof(viewers); k++) { 1674 if (viewers[k] == viewer) 1675 viewer_title_widgets[k]->set_text(title); 1676 } 1677 } 1678 1679 static void markup_button_pressed(mixed ... foo) 1680 { 1681 add_markup(controller_notebook->get_current_page()); 1682 } 1683 1684 static void add_markup(int mode) 1685 { 1686 viewers->add_markup(mode); 1687 } 1688 1689 static void change_engine(int engine_index) 1690 { 1691 gobans_notebook->set_page(engine_index); 1692 data_notebook->set_page(engine_index); 1693 } 1694 1695 void button_pressed_on_a_board(string vertex) 1696 { 1697 if (vertex == "") 1698 return; 1699 1700 switch (controller_notebook->get_current_page()) { 1701 case 0: 1702 // Worms and dragons. 1703 if (worm_data_button->get_active()) 1704 viewers->show_worm_data(vertex); 1705 else if (dragon_data1_button->get_active()) 1706 viewers->show_dragon_data(vertex, 1); 1707 else 1708 viewers->show_dragon_data(vertex, 2); 1709 break; 1710 1711 case 1: 1712 // Move generation. 1713 if (!delta_territory_button->get_active()) 1714 viewers->show_move_reasons(vertex); 1715 else 1716 { 1717 delta_territory_move = vertex; 1718 markup_button_pressed(); 1719 } 1720 break; 1721 1722 case 2: 1723 // Eyes. 1724 viewers->show_eye_data(vertex); 1725 break; 1726 1727 case 3: 1728 // Influence. 1729 if (after_move_influence_button->get_active() 1730 || followup_influence_button->get_active()) 1731 { 1732 move_influence_move = vertex; 1733 after_move_influence_button_text 1734 ->set_text("after move influence for " + vertex); 1735 followup_influence_button_text 1736 ->set_text("followup influence for " + vertex); 1737 markup_button_pressed(); 1738 } 1739 break; 1740 1741 case 4: 1742 // Reading. 1743 string sgffilename; 1744 string sgf_viewer_cmd; 1745 string reset_counter, get_counter; 1746 1747 if (sgf_viewer_button->get_active()) { 1748 sgffilename = sgf_filename_entry->get_text(); 1749 sgf_viewer_cmd = sgf_viewer_entry->get_text(); 1750 } 1751 else if (sgf_traces_button->get_active()) { 1752 sgffilename = sgf_filename_entry->get_text(); 1753 sgf_viewer_cmd = ""; 1754 } 1755 else { 1756 sgffilename = ""; 1757 sgf_viewer_cmd = ""; 1758 } 1759 1760 if (first_vertex == "" 1761 || tactical_reading_button->get_active() 1762 || owl_reading_button->get_active()) 1763 viewers->goban->clear_markup(); 1764 1765 viewers->goban->add_symbol(vertex, "big_dot", "green"); 1766 viewers->redraw_board(); 1767 1768 viewers->clist->clear(); 1769 1770 if (tactical_reading_button->get_active() 1771 || owl_reading_button->get_active()) 1772 { 1773 string prefix = ""; 1774 reset_counter = "reset_reading_node_counter"; 1775 get_counter = "get_reading_node_counter"; 1776 if (owl_reading_button->get_active()) 1777 { 1778 prefix = "owl_"; 1779 reset_counter = "reset_owl_node_counter"; 1780 get_counter = "get_owl_node_counter"; 1781 } 1782 1783 viewers->do_reading(reset_counter, get_counter, 1784 sgffilename, sgf_viewer_cmd, 1785 prefix + "attack " + vertex, 1786 prefix + "defend " + vertex); 1787 } 1788 else if (owl_does_attack_button->get_active() 1789 || owl_does_defend_button->get_active() 1790 || connection_reading_button->get_active() 1791 || semeai_reading_button->get_active()) 1792 { 1793 if (first_vertex == "") 1794 { 1795 first_vertex = vertex; 1796 } 1797 else if (first_vertex != vertex) 1798 { 1799 string c1, c2; 1800 1801 if (owl_does_attack_button->get_active()) { 1802 c1 = sprintf("owl_does_attack %s %s\n", 1803 first_vertex, vertex); 1804 viewers->do_reading("reset_owl_node_counter", 1805 "get_owl_node_counter", 1806 sgffilename, sgf_viewer_cmd, 1807 c1, ""); 1808 } 1809 else if (owl_does_defend_button->get_active()) { 1810 c1 = sprintf("owl_does_defend %s %s\n", 1811 first_vertex, vertex); 1812 viewers->do_reading("reset_owl_node_counter", 1813 "get_owl_node_counter", 1814 sgffilename, sgf_viewer_cmd, 1815 c1, ""); 1816 } 1817 else if (connection_reading_button->get_active()) 1818 { 1819 c1 = sprintf("connect %s %s\n", 1820 first_vertex, 1821 vertex); 1822 c2 = "dis" + c1; 1823 viewers->do_reading("reset_connection_node_counter", 1824 "get_connection_node_counter", 1825 sgffilename, sgf_viewer_cmd, 1826 c1, c2); 1827 } 1828 else 1829 { 1830 c1 = sprintf("analyze_semeai %s %s", 1831 first_vertex, vertex); 1832 c2 = sprintf("analyze_semeai %s %s", vertex, 1833 first_vertex); 1834 // FIXME: We should use a semeai node counter rather 1835 // than the owl node counter, except that it doesn't 1836 // exist yet. 1837 viewers->do_reading("reset_owl_node_counter", 1838 "get_owl_node_counter", 1839 sgffilename, sgf_viewer_cmd, 1840 c1, c2); 1841 } 1842 first_vertex = ""; 1843 } 1844 } 1845 break; 1846 } 1847 } 1848 1849 static void this_new_testcase(string new_testcase) 1850 { 1851 werror("Trying to load new testcase %s.", new_testcase); 1852 if (!excerpt_testcase(new_testcase, viewers[0]->engine)) 1853 { 1854 werror("Failed to load testcase.\n"); 1855 return; 1856 } 1857 testcase_name = new_testcase; 1858 main_window->set_title(testcase_name); 1859 testcase_text->set_text(full_testcase * "\n"); 1860 viewers->new_testcase(complete_testcase, testcase_command); 1861 viewers->handle_testcase(); 1862 1863 if (has_prefix(testcase_command, "reg_genmove") 1864 || has_prefix(testcase_command, "restricted_genmove")) { 1865 controller_notebook->show_all(); 1866 controller_notebook->set_page(1); 1867 } 1868 } 1869 1870 static void new_testcase() 1871 { 1872 this_new_testcase(new_testcase_entry->get_text()); 1873 } 1874 1875 static void next_testcase() 1876 { 1877 if (testcase_index >= sizeof(testcases) - 1) 1878 return; 1879 testcase_index += 1; 1880 prev_testcase_button->set_sensitive(1); 1881 if (testcase_index == sizeof(testcases) - 1) 1882 next_testcase_button->set_sensitive(0); 1883 this_new_testcase(testcases[testcase_index]); 1884 } 1885 1886 static void prev_testcase() 1887 { 1888 if (testcase_index == 0) 1889 return; 1890 testcase_index -= 1; 1891 if (!testcases) 1892 werror("Error handling list of test cases!\n"); 1893 next_testcase_button->set_sensitive(1); 1894 if (testcase_index == 0) 1895 prev_testcase_button->set_sensitive(0); 1896 this_new_testcase(testcases[testcase_index]); 1897 } 1898 1899 1900 // The engine parameter is only needed to find out the color to 1901 // move when an sgf file is given. Since there can be multiple 1902 // viewers we shouldn't use this engine object for anything else 1903 // though. Notice also that no viewer has been set up yet at the 1904 // time of this call. 1905 static int excerpt_testcase(string testcase, SimpleGtp engine) 1906 { 1907 string filename; 1908 int number; 1909 if (sscanf(testcase, "%s:%d", filename, number) < 2) 1910 return 0; 1911 1912 if (!has_suffix(filename, ".tst") 1913 && !has_suffix(filename, ".sgf")) 1914 filename += ".tst"; 1915 1916 string testfile = Stdio.read_file(filename); 1917 if (!testfile) 1918 return 0; 1919 1920 if (has_suffix(filename, ".sgf")) 1921 { 1922 // Only sgf file provided. Fake a testcase. 1923 string s = "loadsgf " + filename + " " + number; 1924 string color = engine->send_command(s)->text; 1925 testcase_command = "reg_genmove " + color; 1926 full_testcase = ({s, testcase_command}); 1927 complete_testcase = ({s, testcase_command}); 1928 expected_result = ""; 1929 return 1; 1930 } 1931 1932 full_testcase = ({}); 1933 complete_testcase = ({}); 1934 array(string) testlines = testfile / "\n"; 1935 for (int k = 0; k < sizeof(testlines); k++) 1936 { 1937 int this_number; 1938 string testline = testlines[k]; 1939 if (testline[0..0] >= "a" && testline[0..0] <= "z") { 1940 if (testline[0..6] != "loadsgf") 1941 complete_testcase += ({ testline }); 1942 else 1943 complete_testcase = ({ testline }); 1944 } 1945 else if (sscanf(testline, "%d %s", this_number, testline) == 2 1946 && this_number == number) 1947 { 1948 testcase_command = testline; 1949 sscanf(testlines[k + 1], "#? [%s]", expected_result); 1950 full_testcase += ({testlines[k]}); 1951 full_testcase += ({testlines[k + 1]}); 1952 return 1; 1953 } 1954 1955 if (has_value("0123456789 ", testline[0..0])) 1956 full_testcase = ({}); 1957 else 1958 full_testcase += ({testline}); 1959 } 1960 1961 return 0; 1962 } 1963 1964 void sgf_traces_button_toggled() 1965 { 1966 if (!sgf_traces_button->get_active()) 1967 sgf_viewer_button->set_active(0); 1968 } 1969 1970 void sgf_viewer_button_toggled() 1971 { 1972 if (sgf_viewer_button->get_active()) 1973 sgf_traces_button->set_active(1); 1974 } 1975 1976 void debug_callback(mixed ... args) 1977 { 1978 write("Debug callback:%O\n", args); 1979 } 1980 1981 static void quit() 1982 { 1983 // Otherwise Pike errors occur. 1984 controller_notebook->signal_disconnect(nasty_signal_id); 1985 GTK.main_quit(); 1986 } 1987} 1988 1989 1990/* 1991 * Local Variables: 1992 * tab-width: 8 1993 * c-basic-offset: 4 1994 * End: 1995 */ 1996