Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / regression / view.pike
CommitLineData
7eeb782e
AT
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 */