1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
26 void metadata_summary (void)
28 char buffer
[256] = "";
30 if (rb
->strlen (header
.black
) ||
31 rb
->strlen (header
.white
) ||
32 rb
->strlen (header
.black_rank
) ||
33 rb
->strlen (header
.white_rank
))
34 rb
->snprintf (buffer
, sizeof(buffer
),
35 "%s [%s] v. %s [%s] ",
36 header
.black
, header
.black_rank
,
37 header
.white
, header
.white_rank
);
39 if (header
.handicap
> 1)
41 rb
->snprintf (buffer
+ rb
->strlen(buffer
),
42 sizeof (buffer
) - rb
->strlen (buffer
),
43 "%d stones ", header
.handicap
);
46 if (header
.komi
!= 0 && !(header
.komi
== 1 && header
.handicap
> 1))
48 snprint_fixed (buffer
+ rb
->strlen(buffer
),
49 sizeof (buffer
) - rb
->strlen (buffer
),
51 rb
->snprintf (buffer
+ rb
->strlen(buffer
),
52 sizeof (buffer
) - rb
->strlen (buffer
),
56 if (rb
->strlen(header
.result
))
58 rb
->snprintf (buffer
+ rb
->strlen(buffer
),
59 sizeof (buffer
) - rb
->strlen (buffer
),
60 "(%s)", header
.result
);
63 /* waiting for user input messes up the testing code, so ifdef it*/
64 #if !defined(GBN_TEST)
65 if (rb
->strlen(buffer
))
67 rb
->splash(0, buffer
);
68 rb
->action_userabort(TIMEOUT_BLOCK
);
74 align_buffer (void *buffer
, size_t * buffer_size
)
76 unsigned int wasted
= (-(long) buffer
) & 3;
78 if (!buffer
|| !buffer_size
)
83 if (*buffer_size
<= wasted
)
89 *buffer_size
-= wasted
;
91 return (void *) (((char *) buffer
) + wasted
);
97 setup_stack (struct stack_t
*stack
, void *buffer
, size_t buffer_size
)
99 if (!stack
|| !buffer
|| !buffer_size
)
101 DEBUGF ("INVALID STACK SETUP!!\n");
105 buffer
= align_buffer (buffer
, &buffer_size
);
107 if (!buffer
|| !buffer_size
)
109 DEBUGF ("Buffer disappeared after alignment!\n");
113 stack
->buffer
= buffer
;
114 stack
->size
= buffer_size
;
121 push_stack (struct stack_t
* stack
, void *buffer
, size_t buffer_size
)
123 if (stack
->sp
+ buffer_size
> stack
->size
)
125 DEBUGF ("stack full!!\n");
129 rb
->memcpy (&stack
->buffer
[stack
->sp
], buffer
, buffer_size
);
131 stack
->sp
+= buffer_size
;
137 pop_stack (struct stack_t
* stack
, void *buffer
, size_t buffer_size
)
139 if (!peek_stack (stack
, buffer
, buffer_size
))
144 stack
->sp
-= buffer_size
;
150 peek_stack (struct stack_t
* stack
, void *buffer
, size_t buffer_size
)
152 if (stack
->sp
< buffer_size
)
157 rb
->memcpy (buffer
, &stack
->buffer
[stack
->sp
- buffer_size
], buffer_size
);
163 empty_stack (struct stack_t
*stack
)
169 push_pos_stack (struct stack_t
*stack
, unsigned short pos
)
171 return push_stack (stack
, &pos
, sizeof (pos
));
175 push_int_stack (struct stack_t
*stack
, int num
)
177 return push_stack (stack
, &num
, sizeof (num
));
181 push_char_stack (struct stack_t
*stack
, char num
)
183 return push_stack (stack
, &num
, sizeof (num
));
188 /* IMPORTANT: keep in sync with the enum prop_type_t enum in types.h */
189 char *prop_names
[] = {
190 /* look up the SGF specification for the meaning of these */
196 "DM", "GB", "GW", "HO", "UC", "V",
198 "BM", "DO", "IT", "TE",
200 "CR", "SQ", "TR", "DD", "MA", "SL", "LB", "N",
202 "AP", "CA", "FF", "GM", "ST", "SZ",
204 "AN", "PB", "PW", "HA", "KM", "TB", "TW", "BR", "WR",
205 "BT", "WT", "CP", "DT", "EV", "RO", "GN", "GC", "ON",
206 "OT", "PC", "RE", "RU", "SO", "TM", "US",
208 "BL", "WL", "OB", "OW", "FG", "PM", "VW"
211 /* These seems to be specified by the SGF specification. You can do free
212 form ones as well, but I haven't implemented that (and don't plan to) */
213 const char *ruleset_names
[] = { "AGA", "Japanese", "Chinese", "NZ", "GOE" };
218 create_or_open_file (const char *filename
)
222 if (!rb
->file_exists (filename
))
224 fd
= rb
->creat(filename
, 0666);
228 fd
= rb
->open (filename
, O_RDWR
);
236 snprint_fixed (char *buffer
, int buffer_size
, int fixed
)
238 return rb
->snprintf (buffer
, buffer_size
, "%s%d.%d",
239 fixed
< 0 ? "-" : "",
240 abs (fixed
) >> 1, 5 * (fixed
& 1));
249 int result
= rb
->read (fd
, &peeked_char
, 1);
256 result
= rb
->lseek (fd
, -1, SEEK_CUR
);
272 int result
= rb
->read (fd
, &read_char
, 1);
284 write_char (int fd
, char to_write
)
286 int result
= write_file (fd
, &to_write
, 1);
297 write_file (int fd
, const void *buf
, size_t count
)
299 const char *buffer
= buf
;
305 result
= rb
->write (fd
, buffer
, count
);
320 read_file (int fd
, void *buf
, size_t count
)
328 result
= rb
->read (fd
, buffer
, count
);
343 read_char_no_whitespace (int fd
)
345 int result
= peek_char_no_whitespace (fd
);
353 peek_char_no_whitespace (int fd
)
357 while (is_whitespace (result
= peek_char (fd
)))
378 is_whitespace (int value
)
382 value
== '\n' || value
== '\r' || value
== '\f' || value
== '\v')
393 sanitize_string (char *string
)
395 bool escaped
= false;
427 get_header_string_and_size (struct header_t
*header
,
428 enum prop_type_t type
, char **buffer
, int *size
)
430 if (buffer
== 0 || header
== 0)
435 if (type
== PROP_BLACK_NAME
)
437 *buffer
= header
->black
;
440 else if (type
== PROP_WHITE_NAME
)
442 *buffer
= header
->white
;
445 else if (type
== PROP_BLACK_RANK
)
447 *buffer
= header
->black_rank
;
450 else if (type
== PROP_WHITE_RANK
)
452 *buffer
= header
->white_rank
;
455 else if (type
== PROP_BLACK_TEAM
)
457 *buffer
= header
->black_team
;
460 else if (type
== PROP_WHITE_TEAM
)
462 *buffer
= header
->white_team
;
465 else if (type
== PROP_DATE
)
467 *buffer
= header
->date
;
470 else if (type
== PROP_ROUND
)
472 *buffer
= header
->round
;
475 else if (type
== PROP_EVENT
)
477 *buffer
= header
->event
;
480 else if (type
== PROP_PLACE
)
482 *buffer
= header
->place
;
485 else if (type
== PROP_OVERTIME
)
487 *buffer
= header
->overtime
;
488 *size
= MAX_OVERTIME
;
490 else if (type
== PROP_RESULT
)
492 *buffer
= header
->result
;
495 else if (type
== PROP_RULESET
)
497 *buffer
= header
->ruleset
;
509 /* TEST CODE BEGINS HERE define GBN_TEST to run this, either in goban.h or
510 in the CFLAGS. The tests will be run when the plugin starts, after
511 which the plugin will exit. Any error stops testing since many tests
512 depend on previous setup. Note: The testing can take a while as there
513 are some big loops. Be patient. */
522 #include "sgf_storage.h"
524 /* If this isn't on a single line, the line numbers it reports will be wrong.
526 * I'm sure there's a way to make it better, but it's not really worth it.
528 #define gbn_assert(test) if (test) {DEBUGF("%d passed\n", __LINE__);} else {DEBUGF("%d FAILED!\n", __LINE__); rb->splashf(10 * HZ, "Test on line %d of util.c failed!", __LINE__); return;}
533 rb
->splash (3 * HZ
, "Running tests. Failures will stop testing.");
537 /* allocating and freeing storage units */
539 gbn_assert (alloc_storage_sgf ());
541 int prevent_infinite
= 100000000;
544 while (alloc_storage_sgf () >= 0 && --prevent_infinite
)
549 gbn_assert (prevent_infinite
);
550 gbn_assert (count
> 100);
552 /* make sure it fails a few times */
553 gbn_assert (alloc_storage_sgf () < 0);
554 gbn_assert (alloc_storage_sgf () < 0);
555 gbn_assert (alloc_storage_sgf () < 0);
557 free_storage_sgf (0);
559 gbn_assert (alloc_storage_sgf () == 0);
561 gbn_assert (alloc_storage_sgf () < 0);
564 for (i
= 0; i
<= count
; ++i
)
566 free_storage_sgf (i
);
569 gbn_assert (alloc_storage_sgf () >= 0);
572 for (i
= 0; i
< count
; ++i
)
574 gbn_assert (alloc_storage_sgf () >= 0);
581 /* setting up, saving and loading */
582 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 0, 15));
583 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 0, -30));
584 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 4, 1));
585 gbn_assert (setup_game (MIN_BOARD_SIZE
, MIN_BOARD_SIZE
, 1, 1));
587 gbn_assert (setup_game (MIN_BOARD_SIZE
, MAX_BOARD_SIZE
, 1, 1));
588 gbn_assert (setup_game (MAX_BOARD_SIZE
, MIN_BOARD_SIZE
, 1, 1));
590 gbn_assert (!setup_game (MAX_BOARD_SIZE
+ 1, MAX_BOARD_SIZE
+ 1, 0, 15));
591 gbn_assert (!setup_game (MIN_BOARD_SIZE
- 1, MIN_BOARD_SIZE
- 1, 0, 15));
592 gbn_assert (!setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, -1, 15));
594 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 1, 1));
595 gbn_assert (save_game (DEFAULT_SAVE_DIR
"/t1.sgf"));
596 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/t1.sgf"));
597 gbn_assert (save_game (DEFAULT_SAVE_DIR
"/t2.sgf"));
598 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/t2.sgf"));
600 gbn_assert (!save_game ("/DIR_DOESNT_EXIST/blah.sgf"));
601 gbn_assert (!load_game ("/DIR_DOESNT_EXIST/blah.sgf"));
602 gbn_assert (!load_game (DEFAULT_SAVE_DIR
"/DOESNT_EXIST.sgf"));
606 /* test of a long game, captures, illegal moves */
607 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/long.sgf"));
608 while (move_num
< 520)
610 gbn_assert (num_variations_sgf () == 1);
611 gbn_assert (redo_node_sgf ());
614 gbn_assert (play_move_sgf (POS (2, 0), BLACK
));
615 gbn_assert (play_move_sgf (POS (2, 1), WHITE
));
617 gbn_assert (move_num
== 522);
619 gbn_assert (white_captures
== 261 && black_captures
== 0);
621 gbn_assert (play_move_sgf (PASS_POS
, BLACK
));
622 gbn_assert (play_move_sgf (PASS_POS
, WHITE
));
624 gbn_assert (move_num
== 524);
627 int b_count
, w_count
, e_count
;
628 b_count
= w_count
= e_count
= 0;
629 for (x
= 0; x
< 19; ++x
)
631 for (y
= 0; y
< 19; ++y
)
633 gbn_assert (!legal_move_board (POS (x
, y
), BLACK
, false));
634 gbn_assert (!play_move_sgf (POS (x
, y
), BLACK
));
635 switch (get_point_board (POS (x
, y
)))
652 gbn_assert (b_count
== 0 && w_count
== 261 && e_count
== 19 * 19 - 261);
654 gbn_assert (undo_node_sgf ());
655 gbn_assert (move_num
== 523);
657 int infinite_prevention
= 0;
660 gbn_assert (undo_node_sgf ());
662 ++infinite_prevention
;
663 gbn_assert (infinite_prevention
< 100000);
666 gbn_assert (save_game (DEFAULT_SAVE_DIR
"/long_out.sgf"));
669 /* test of basic moves, legal moves, adding and removing stones */
670 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 0, 0));
671 gbn_assert (play_move_sgf
672 (POS (MAX_BOARD_SIZE
/ 2, MAX_BOARD_SIZE
/ 2), BLACK
));
673 gbn_assert (move_num
== 1 && current_player
== WHITE
);
674 gbn_assert (!legal_move_board
675 (POS (MAX_BOARD_SIZE
/ 2, MAX_BOARD_SIZE
/ 2), WHITE
, true));
677 int saved_node
= current_node
;
678 gbn_assert (add_stone_sgf (POS (0, 0), BLACK
));
679 gbn_assert (current_node
!= saved_node
);
680 gbn_assert (get_point_board (POS (0, 0)) == BLACK
);
681 gbn_assert (move_num
== 1 && current_player
== WHITE
);
683 saved_node
= current_node
;
684 gbn_assert (add_stone_sgf (POS (0, 1), WHITE
));
685 gbn_assert (current_node
== saved_node
);
686 gbn_assert (get_point_board (POS (0, 1)) == WHITE
);
688 gbn_assert (add_stone_sgf (POS (0, 0), EMPTY
));
689 gbn_assert (add_stone_sgf (POS (0, 1), EMPTY
));
690 gbn_assert (get_point_board (POS (0, 0)) == EMPTY
);
691 gbn_assert (get_point_board (POS (0, 1)) == EMPTY
);
695 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/cap.sgf"));
696 gbn_assert (play_move_sgf (POS (0, 0), BLACK
));
697 gbn_assert (black_captures
== 8);
698 gbn_assert (undo_node_sgf ());
699 gbn_assert (black_captures
== 0);
701 gbn_assert (!play_move_sgf (POS (0, 0), WHITE
));
702 play_mode
= MODE_FORCE_PLAY
;
703 gbn_assert (play_move_sgf (POS (0, 0), WHITE
));
704 play_mode
= MODE_PLAY
;
706 gbn_assert (black_captures
== 9);
707 gbn_assert (get_point_board (POS (0, 0)) == EMPTY
);
708 gbn_assert (undo_node_sgf ());
709 gbn_assert (black_captures
== 0);
711 gbn_assert (play_move_sgf (POS (9, 9), BLACK
));
712 gbn_assert (black_captures
== 44);
714 for (x
= 0; x
< 19; ++x
)
716 for (y
= 0; y
< 19; ++y
)
718 gbn_assert (get_point_board (POS (x
, y
)) == BLACK
||
719 add_stone_sgf (POS (x
, y
), BLACK
));
723 gbn_assert (get_point_board (POS (0, 0)) == BLACK
);
724 gbn_assert (add_stone_sgf (POS (9, 9), EMPTY
));
725 gbn_assert (play_move_sgf (POS (9, 9), WHITE
));
726 gbn_assert (white_captures
== 360);
728 gbn_assert (undo_node_sgf ());
729 gbn_assert (white_captures
== 0);
731 play_mode
= MODE_FORCE_PLAY
;
732 gbn_assert (play_move_sgf (POS (9, 9), BLACK
));
733 play_mode
= MODE_PLAY
;
734 gbn_assert (white_captures
== 361);
736 for (x
= 0; x
< 19; ++x
)
738 for (y
= 0; y
< 19; ++y
)
740 gbn_assert (get_point_board (POS (x
, y
)) == EMPTY
);
746 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 0, 15));
749 * Set up the board to look like this:
755 gbn_assert (add_stone_sgf (POS (0, 1), BLACK
));
756 gbn_assert (add_stone_sgf (POS (1, 0), BLACK
));
757 gbn_assert (add_stone_sgf (POS (1, 1), WHITE
));
758 gbn_assert (add_stone_sgf (POS (0, 2), WHITE
));
760 /* take the ko and make sure black can't take back */
761 gbn_assert (play_move_sgf (POS (0, 0), WHITE
));
762 gbn_assert (!play_move_sgf (POS (0, 1), BLACK
));
764 /* make sure white can fill, even with the ko_pos set */
765 gbn_assert (play_move_sgf (POS (0, 1), WHITE
));
766 /* and make sure undo sets the ko again */
767 gbn_assert (undo_node_sgf ());
768 gbn_assert (!play_move_sgf (POS (0, 1), BLACK
));
770 /* make sure ko threats clear the ko */
771 gbn_assert (play_move_sgf (POS (2, 2), BLACK
)); /* ko threat */
772 gbn_assert (play_move_sgf (POS (2, 3), WHITE
)); /* response */
773 gbn_assert (play_move_sgf (POS (0, 1), BLACK
)); /* take ko */
775 gbn_assert (undo_node_sgf ());
776 gbn_assert (undo_node_sgf ());
777 gbn_assert (undo_node_sgf ());
779 /* make sure a pass is counted as a ko threat */
780 gbn_assert (!play_move_sgf (POS (0, 1), BLACK
));
781 gbn_assert (play_move_sgf (PASS_POS
, BLACK
));
782 gbn_assert (play_move_sgf (PASS_POS
, WHITE
));
783 gbn_assert (play_move_sgf (POS (0, 1), BLACK
));
785 /* and finally let's make sure that white can't directly retake */
786 gbn_assert (!play_move_sgf (POS (0, 0), WHITE
));
790 /* test some header information saving/loading as well as comment
792 char some_comment
[] =
793 "blah blah blah i am a stupid comment. here's some annoying characters: 01234567890!@#$%^&*()[[[[\\\\\\]ABCDEFGHIJKLMNOPQRSTUVWXYZ";
794 /* that bit near the end is literally this: \\\] which tests escaping
796 char read_buffer
[256];
798 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 5, -20));
800 /* this also tests that ko_pos is reset by setuping up a new game */
801 gbn_assert (play_move_sgf (POS (0, 0), WHITE
));
802 gbn_assert (write_comment_sgf (some_comment
) > 0);
803 gbn_assert (play_move_sgf (POS (0, 1), BLACK
));
804 rb
->strcpy (header
.black
, "Jack Black");
805 rb
->strcpy (header
.white
, "Jill White");
807 gbn_assert (save_game (DEFAULT_SAVE_DIR
"/head.sgf"));
809 gbn_assert (setup_game (MIN_BOARD_SIZE
, MIN_BOARD_SIZE
, 1, 1));
810 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/head.sgf"));
812 gbn_assert (header
.komi
== -20 && header
.handicap
== 5);
813 gbn_assert (board_width
== MAX_BOARD_SIZE
814 && board_height
== MAX_BOARD_SIZE
);
815 gbn_assert (rb
->strcmp (header
.black
, "Jack Black") == 0);
816 gbn_assert (rb
->strcmp (header
.white
, "Jill White") == 0);
817 gbn_assert (redo_node_sgf ());
818 gbn_assert (read_comment_sgf (read_buffer
, sizeof (read_buffer
)));
819 gbn_assert (rb
->strcmp (read_buffer
, some_comment
) == 0);
820 gbn_assert (redo_node_sgf ());
821 gbn_assert (get_point_board (POS (0, 0)) == WHITE
);
822 gbn_assert (get_point_board (POS (0, 1)) == BLACK
);
826 /* test saving and loading a file with unhandled SGF properties. this
827 test requires that the user diff unhnd.sgf with unhnd_out.sgf (any
828 substantial difference is a bug and should be reported) the
829 following are NOT substantial differences: - reordering of
830 properties in a node - whitespace changes outside of a comment
831 value or other property value - reordering of property values */
832 gbn_assert (load_game (DEFAULT_SAVE_DIR
"/unhnd.sgf"));
833 gbn_assert (save_game (DEFAULT_SAVE_DIR
"/unhnd_out.sgf"));
837 /* Test variations a bit */
838 gbn_assert (setup_game (MAX_BOARD_SIZE
, MAX_BOARD_SIZE
, 0, 13));
839 /* start at a move, otherwise add_stone won't create a variation */
840 gbn_assert (play_move_sgf (POS (5, 5), BLACK
));
841 /* make sure it doesn't */
842 gbn_assert (undo_node_sgf ());
843 gbn_assert (add_stone_sgf (POS (4, 5), WHITE
));
844 gbn_assert (!undo_node_sgf ());
845 gbn_assert (num_variations_sgf () == 1);
846 gbn_assert (play_move_sgf (POS (5, 5), BLACK
));
848 gbn_assert (play_move_sgf (POS (0, 0), BLACK
));
849 gbn_assert (num_variations_sgf () == 1);
850 gbn_assert (undo_node_sgf ());
851 gbn_assert (play_move_sgf (POS (0, 1), BLACK
));
852 gbn_assert (num_variations_sgf () == 2);
853 gbn_assert (undo_node_sgf ());
854 gbn_assert (play_move_sgf (POS (0, 1), BLACK
));
855 gbn_assert (num_variations_sgf () == 2);
856 gbn_assert (undo_node_sgf ());
857 gbn_assert (play_move_sgf (POS (0, 2), BLACK
));
858 gbn_assert (num_variations_sgf () == 3);
859 gbn_assert (undo_node_sgf ());
860 gbn_assert (play_move_sgf (POS (0, 3), WHITE
));
861 gbn_assert (num_variations_sgf () == 4);
862 gbn_assert (undo_node_sgf ());
863 gbn_assert (play_move_sgf (PASS_POS
, BLACK
));
864 gbn_assert (num_variations_sgf () == 5);
865 gbn_assert (undo_node_sgf ());
866 gbn_assert (add_stone_sgf (POS (1, 1), BLACK
));
867 gbn_assert (add_stone_sgf (POS (1, 2), BLACK
));
868 gbn_assert (add_stone_sgf (POS (1, 3), WHITE
));
869 gbn_assert (num_variations_sgf () == 6);
870 gbn_assert (undo_node_sgf ());
871 gbn_assert (add_stone_sgf (POS (1, 1), BLACK
));
872 gbn_assert (add_stone_sgf (POS (1, 2), BLACK
));
873 gbn_assert (add_stone_sgf (POS (1, 3), WHITE
));
874 gbn_assert (num_variations_sgf () == 7);
875 gbn_assert (next_variation_sgf ());
876 gbn_assert (get_point_board (POS (0, 0)) == BLACK
);
877 gbn_assert (get_point_board (POS (0, 1)) == EMPTY
);
878 gbn_assert (get_point_board (POS (0, 2)) == EMPTY
);
879 gbn_assert (get_point_board (POS (1, 1)) == EMPTY
);
880 gbn_assert (get_point_board (POS (1, 2)) == EMPTY
);
881 gbn_assert (get_point_board (POS (1, 3)) == EMPTY
);
883 rb
->splash (10 * HZ
, "All tests passed. Exiting");
885 #endif /* GBN_TEST */