summaryrefslogtreecommitdiff
path: root/apps/plugins/goban/util.c
diff options
context:
space:
mode:
authorMustapha Senhaji <moos@rockbox.org>2009-02-11 16:03:17 +0000
committerMustapha Senhaji <moos@rockbox.org>2009-02-11 16:03:17 +0000
commitf2d5c3532fd21c04e77aa86732742b578283b697 (patch)
tree1c55cb7e69ed11391f77187c86ff8c435ed85877 /apps/plugins/goban/util.c
parent21f0c9a2829415f52b64cbdf965b01525e78f17a (diff)
New game plugin by Joshua Simmons FS#7369: Goban plugin, Go/Igo/Weiqi/Baduk game recorder and viewer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19972 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/goban/util.c')
-rw-r--r--apps/plugins/goban/util.c885
1 files changed, 885 insertions, 0 deletions
diff --git a/apps/plugins/goban/util.c b/apps/plugins/goban/util.c
new file mode 100644
index 0000000000..e9966311ef
--- /dev/null
+++ b/apps/plugins/goban/util.c
@@ -0,0 +1,885 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "util.h"
+#include "game.h"
+
+
+void metadata_summary (void)
+{
+ char buffer[256] = "";
+
+ if (rb->strlen (header.black) ||
+ rb->strlen (header.white) ||
+ rb->strlen (header.black_rank) ||
+ rb->strlen (header.white_rank))
+ rb->snprintf (buffer, sizeof(buffer),
+ "%s [%s] v. %s [%s] ",
+ header.black, header.black_rank,
+ header.white, header.white_rank);
+
+ if (header.handicap > 1)
+ {
+ rb->snprintf (buffer + rb->strlen(buffer),
+ sizeof (buffer) - rb->strlen (buffer),
+ "%d stones ", header.handicap);
+ }
+
+ if (header.komi != 0 && !(header.komi == 1 && header.handicap > 1))
+ {
+ snprint_fixed (buffer + rb->strlen(buffer),
+ sizeof (buffer) - rb->strlen (buffer),
+ header.komi);
+ rb->snprintf (buffer + rb->strlen(buffer),
+ sizeof (buffer) - rb->strlen (buffer),
+ " komi ");
+ }
+
+ if (rb->strlen(header.result))
+ {
+ rb->snprintf (buffer + rb->strlen(buffer),
+ sizeof (buffer) - rb->strlen (buffer),
+ "(%s)", header.result);
+ }
+
+ /* waiting for user input messes up the testing code, so ifdef it*/
+#if !defined(GBN_TEST)
+ if (rb->strlen(buffer))
+ {
+ rb->splash(0, buffer);
+ rb->action_userabort(TIMEOUT_BLOCK);
+ }
+#endif
+}
+
+void *
+align_buffer (void *buffer, size_t * buffer_size)
+{
+ unsigned int wasted = (-(long) buffer) & 3;
+
+ if (!buffer || !buffer_size)
+ {
+ return NULL;
+ }
+
+ if (*buffer_size <= wasted)
+ {
+ *buffer_size = 0;
+ return NULL;
+ }
+
+ *buffer_size -= wasted;
+
+ return (void *) (((char *) buffer) + wasted);
+}
+
+
+
+bool
+setup_stack (struct stack_t *stack, void *buffer, size_t buffer_size)
+{
+ if (!stack || !buffer || !buffer_size)
+ {
+ DEBUGF ("INVALID STACK SETUP!!\n");
+ return false;
+ }
+
+ buffer = align_buffer (buffer, &buffer_size);
+
+ if (!buffer || !buffer_size)
+ {
+ DEBUGF ("Buffer disappeared after alignment!\n");
+ return false;
+ }
+
+ stack->buffer = buffer;
+ stack->size = buffer_size;
+ stack->sp = 0;
+
+ return true;
+}
+
+bool
+push_stack (struct stack_t * stack, void *buffer, size_t buffer_size)
+{
+ if (stack->sp + buffer_size > stack->size)
+ {
+ DEBUGF ("stack full!!\n");
+ return false;
+ }
+
+ rb->memcpy (&stack->buffer[stack->sp], buffer, buffer_size);
+
+ stack->sp += buffer_size;
+
+ return true;
+}
+
+bool
+pop_stack (struct stack_t * stack, void *buffer, size_t buffer_size)
+{
+ if (!peek_stack (stack, buffer, buffer_size))
+ {
+ return false;
+ }
+
+ stack->sp -= buffer_size;
+
+ return true;
+}
+
+bool
+peek_stack (struct stack_t * stack, void *buffer, size_t buffer_size)
+{
+ if (stack->sp < buffer_size)
+ {
+ return false;
+ }
+
+ rb->memcpy (buffer, &stack->buffer[stack->sp - buffer_size], buffer_size);
+
+ return true;
+}
+
+void
+empty_stack (struct stack_t *stack)
+{
+ stack->sp = 0;
+}
+
+bool
+push_pos_stack (struct stack_t *stack, unsigned short pos)
+{
+ return push_stack (stack, &pos, sizeof (pos));
+}
+
+bool
+push_int_stack (struct stack_t *stack, int num)
+{
+ return push_stack (stack, &num, sizeof (num));
+}
+
+bool
+push_char_stack (struct stack_t *stack, char num)
+{
+ return push_stack (stack, &num, sizeof (num));
+}
+
+
+
+/* IMPORTANT: keep in sync with the enum prop_type_t enum in types.h */
+char *prop_names[] = {
+ /* look up the SGF specification for the meaning of these */
+ "B", "W",
+ "AB", "AW", "AE",
+
+ "PL", "C",
+
+ "DM", "GB", "GW", "HO", "UC", "V",
+
+ "BM", "DO", "IT", "TE",
+
+ "CR", "SQ", "TR", "DD", "MA", "SL", "LB", "N",
+
+ "AP", "CA", "FF", "GM", "ST", "SZ",
+
+ "AN", "PB", "PW", "HA", "KM", "TB", "TW", "BR", "WR",
+ "BT", "WT", "CP", "DT", "EV", "RO", "GN", "GC", "ON",
+ "OT", "PC", "RE", "RU", "SO", "TM", "US",
+
+ "BL", "WL", "OB", "OW", "FG", "PM", "VW"
+};
+
+/* These seems to be specified by the SGF specification. You can do free
+ form ones as well, but I haven't implemented that (and don't plan to) */
+char *ruleset_names[] = { "AGA", "Japanese", "Chinese", "NZ", "GOE" };
+
+
+
+int
+create_or_open_file (const char *filename)
+{
+ int fd;
+
+ if (!rb->file_exists (filename))
+ {
+ fd = rb->creat (filename);
+ }
+ else
+ {
+ fd = rb->open (filename, O_RDWR);
+ }
+
+ return fd;
+}
+
+
+int
+snprint_fixed (char *buffer, int buffer_size, int fixed)
+{
+ return rb->snprintf (buffer, buffer_size, "%s%d.%d",
+ fixed < 0 ? "-" : "",
+ abs (fixed) >> 1, 5 * (fixed & 1));
+}
+
+
+int
+peek_char (int fd)
+{
+ char peeked_char;
+
+ int result = rb->read (fd, &peeked_char, 1);
+
+ if (result != 1)
+ {
+ return -1;
+ }
+
+ result = rb->lseek (fd, -1, SEEK_CUR);
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ return peeked_char;
+}
+
+
+int
+read_char (int fd)
+{
+ char read_char;
+
+ int result = rb->read (fd, &read_char, 1);
+
+ if (result != 1)
+ {
+ return -1;
+ }
+
+ return read_char;
+}
+
+
+bool
+write_char (int fd, char to_write)
+{
+ int result = write_file (fd, &to_write, 1);
+
+ if (result != 1)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+ssize_t
+write_file (int fd, const void *buf, size_t count)
+{
+ const char *buffer = buf;
+ int result;
+ int ret_val = count;
+
+ while (count)
+ {
+ result = rb->write (fd, buffer, count);
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ count -= result;
+ buffer += result;
+ }
+
+ return ret_val;
+}
+
+ssize_t
+read_file (int fd, void *buf, size_t count)
+{
+ char *buffer = buf;
+ int result;
+ int ret_val = count;
+
+ while (count)
+ {
+ result = rb->read (fd, buffer, count);
+
+ if (result <= 0)
+ {
+ return -1;
+ }
+
+ count -= result;
+ buffer += result;
+ }
+
+ return ret_val;
+}
+
+int
+read_char_no_whitespace (int fd)
+{
+ int result = peek_char_no_whitespace (fd);
+
+ read_char (fd);
+
+ return result;
+}
+
+int
+peek_char_no_whitespace (int fd)
+{
+ int result;
+
+ while (is_whitespace (result = peek_char (fd)))
+ {
+ read_char (fd);
+ }
+
+ return result;
+}
+
+
+void
+close_file (int *fd)
+{
+ if (*fd >= 0)
+ {
+ rb->close (*fd);
+ }
+
+ *fd = -1;
+}
+
+bool
+is_whitespace (int value)
+{
+ if (value == ' ' ||
+ value == '\t' ||
+ value == '\n' || value == '\r' || value == '\f' || value == '\v')
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void
+sanitize_string (char *string)
+{
+ bool escaped = false;
+
+ if (!string)
+ {
+ return;
+ }
+
+ while (1)
+ {
+ switch (*string)
+ {
+ case '\0':
+ return;
+ case '\\':
+ escaped = !escaped;
+ break;
+ case ']':
+ if (!escaped)
+ {
+ *string = ']';
+ }
+ escaped = false;
+ break;
+ default:
+ break;
+ };
+ ++string;
+ }
+}
+
+
+bool
+get_header_string_and_size (struct header_t *header,
+ enum prop_type_t type, char **buffer, int *size)
+{
+ if (buffer == 0 || header == 0)
+ {
+ return false;
+ }
+
+ if (type == PROP_BLACK_NAME)
+ {
+ *buffer = header->black;
+ *size = MAX_NAME;
+ }
+ else if (type == PROP_WHITE_NAME)
+ {
+ *buffer = header->white;
+ *size = MAX_NAME;
+ }
+ else if (type == PROP_BLACK_RANK)
+ {
+ *buffer = header->black_rank;
+ *size = MAX_RANK;
+ }
+ else if (type == PROP_WHITE_RANK)
+ {
+ *buffer = header->white_rank;
+ *size = MAX_RANK;
+ }
+ else if (type == PROP_BLACK_TEAM)
+ {
+ *buffer = header->black_team;
+ *size = MAX_TEAM;
+ }
+ else if (type == PROP_WHITE_TEAM)
+ {
+ *buffer = header->white_team;
+ *size = MAX_TEAM;
+ }
+ else if (type == PROP_DATE)
+ {
+ *buffer = header->date;
+ *size = MAX_DATE;
+ }
+ else if (type == PROP_ROUND)
+ {
+ *buffer = header->round;
+ *size = MAX_ROUND;
+ }
+ else if (type == PROP_EVENT)
+ {
+ *buffer = header->event;
+ *size = MAX_EVENT;
+ }
+ else if (type == PROP_PLACE)
+ {
+ *buffer = header->place;
+ *size = MAX_PLACE;
+ }
+ else if (type == PROP_OVERTIME)
+ {
+ *buffer = header->overtime;
+ *size = MAX_OVERTIME;
+ }
+ else if (type == PROP_RESULT)
+ {
+ *buffer = header->result;
+ *size = MAX_RESULT;
+ }
+ else if (type == PROP_RULESET)
+ {
+ *buffer = header->ruleset;
+ *size = MAX_RULESET;
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* TEST CODE BEGINS HERE define GBN_TEST to run this, either in goban.h or
+ in the CFLAGS. The tests will be run when the plugin starts, after
+ which the plugin will exit. Any error stops testing since many tests
+ depend on previous setup. Note: The testing can take a while as there
+ are some big loops. Be patient. */
+
+#ifdef GBN_TEST
+
+#include "goban.h"
+#include "types.h"
+#include "board.h"
+#include "game.h"
+#include "sgf.h"
+#include "sgf_storage.h"
+
+/* If this isn't on a single line, the line numbers it reports will be wrong.
+ *
+ * I'm sure there's a way to make it better, but it's not really worth it.
+ */
+#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;}
+
+void
+run_tests (void)
+{
+ rb->splash (3 * HZ, "Running tests. Failures will stop testing.");
+
+
+
+ /* allocating and freeing storage units */
+
+ gbn_assert (alloc_storage_sgf ());
+
+ int prevent_infinite = 100000000;
+
+ int count = 1;
+ while (alloc_storage_sgf () >= 0 && --prevent_infinite)
+ {
+ ++count;
+ }
+
+ gbn_assert (prevent_infinite);
+ gbn_assert (count > 100);
+
+ /* make sure it fails a few times */
+ gbn_assert (alloc_storage_sgf () < 0);
+ gbn_assert (alloc_storage_sgf () < 0);
+ gbn_assert (alloc_storage_sgf () < 0);
+
+ free_storage_sgf (0);
+
+ gbn_assert (alloc_storage_sgf () == 0);
+
+ gbn_assert (alloc_storage_sgf () < 0);
+
+ int i;
+ for (i = 0; i <= count; ++i)
+ {
+ free_storage_sgf (i);
+ }
+
+ gbn_assert (alloc_storage_sgf () >= 0);
+ --count;
+
+ for (i = 0; i < count; ++i)
+ {
+ gbn_assert (alloc_storage_sgf () >= 0);
+ }
+
+ free_tree_sgf ();
+
+
+
+ /* setting up, saving and loading */
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 15));
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, -30));
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 4, 1));
+ gbn_assert (setup_game (MIN_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1));
+
+ gbn_assert (setup_game (MIN_BOARD_SIZE, MAX_BOARD_SIZE, 1, 1));
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1));
+
+ gbn_assert (!setup_game (MAX_BOARD_SIZE + 1, MAX_BOARD_SIZE + 1, 0, 15));
+ gbn_assert (!setup_game (MIN_BOARD_SIZE - 1, MIN_BOARD_SIZE - 1, 0, 15));
+ gbn_assert (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, -1, 15));
+
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 1, 1));
+ gbn_assert (save_game (DEFAULT_SAVE_DIR "/t1.sgf"));
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/t1.sgf"));
+ gbn_assert (save_game (DEFAULT_SAVE_DIR "/t2.sgf"));
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/t2.sgf"));
+
+ gbn_assert (!save_game ("/DIR_DOESNT_EXIST/blah.sgf"));
+ gbn_assert (!load_game ("/DIR_DOESNT_EXIST/blah.sgf"));
+ gbn_assert (!load_game (DEFAULT_SAVE_DIR "/DOESNT_EXIST.sgf"));
+
+
+
+ /* test of a long game, captures, illegal moves */
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/long.sgf"));
+ while (move_num < 520)
+ {
+ gbn_assert (num_variations_sgf () == 1);
+ gbn_assert (redo_node_sgf ());
+ }
+
+ gbn_assert (play_move_sgf (POS (2, 0), BLACK));
+ gbn_assert (play_move_sgf (POS (2, 1), WHITE));
+
+ gbn_assert (move_num == 522);
+
+ gbn_assert (white_captures == 261 && black_captures == 0);
+
+ gbn_assert (play_move_sgf (PASS_POS, BLACK));
+ gbn_assert (play_move_sgf (PASS_POS, WHITE));
+
+ gbn_assert (move_num == 524);
+
+ int x, y;
+ int b_count, w_count, e_count;
+ b_count = w_count = e_count = 0;
+ for (x = 0; x < 19; ++x)
+ {
+ for (y = 0; y < 19; ++y)
+ {
+ gbn_assert (!legal_move_board (POS (x, y), BLACK, false));
+ gbn_assert (!play_move_sgf (POS (x, y), BLACK));
+ switch (get_point_board (POS (x, y)))
+ {
+ case BLACK:
+ ++b_count;
+ break;
+ case WHITE:
+ ++w_count;
+ break;
+ case EMPTY:
+ ++e_count;
+ break;
+ default:
+ gbn_assert (false);
+ }
+ }
+ }
+
+ gbn_assert (b_count == 0 && w_count == 261 && e_count == 19 * 19 - 261);
+
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (move_num == 523);
+
+ int infinite_prevention = 0;
+ while (move_num > 0)
+ {
+ gbn_assert (undo_node_sgf ());
+
+ ++infinite_prevention;
+ gbn_assert (infinite_prevention < 100000);
+ }
+
+ gbn_assert (save_game (DEFAULT_SAVE_DIR "/long_out.sgf"));
+
+
+ /* test of basic moves, legal moves, adding and removing stones */
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0));
+ gbn_assert (play_move_sgf
+ (POS (MAX_BOARD_SIZE / 2, MAX_BOARD_SIZE / 2), BLACK));
+ gbn_assert (move_num == 1 && current_player == WHITE);
+ gbn_assert (!legal_move_board
+ (POS (MAX_BOARD_SIZE / 2, MAX_BOARD_SIZE / 2), WHITE, true));
+
+ int saved_node = current_node;
+ gbn_assert (add_stone_sgf (POS (0, 0), BLACK));
+ gbn_assert (current_node != saved_node);
+ gbn_assert (get_point_board (POS (0, 0)) == BLACK);
+ gbn_assert (move_num == 1 && current_player == WHITE);
+
+ saved_node = current_node;
+ gbn_assert (add_stone_sgf (POS (0, 1), WHITE));
+ gbn_assert (current_node == saved_node);
+ gbn_assert (get_point_board (POS (0, 1)) == WHITE);
+
+ gbn_assert (add_stone_sgf (POS (0, 0), EMPTY));
+ gbn_assert (add_stone_sgf (POS (0, 1), EMPTY));
+ gbn_assert (get_point_board (POS (0, 0)) == EMPTY);
+ gbn_assert (get_point_board (POS (0, 1)) == EMPTY);
+
+
+ /* test captures */
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/cap.sgf"));
+ gbn_assert (play_move_sgf (POS (0, 0), BLACK));
+ gbn_assert (black_captures == 8);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (black_captures == 0);
+
+ gbn_assert (!play_move_sgf (POS (0, 0), WHITE));
+ play_mode = MODE_FORCE_PLAY;
+ gbn_assert (play_move_sgf (POS (0, 0), WHITE));
+ play_mode = MODE_PLAY;
+
+ gbn_assert (black_captures == 9);
+ gbn_assert (get_point_board (POS (0, 0)) == EMPTY);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (black_captures == 0);
+
+ gbn_assert (play_move_sgf (POS (9, 9), BLACK));
+ gbn_assert (black_captures == 44);
+
+ for (x = 0; x < 19; ++x)
+ {
+ for (y = 0; y < 19; ++y)
+ {
+ gbn_assert (get_point_board (POS (x, y)) == BLACK ||
+ add_stone_sgf (POS (x, y), BLACK));
+ }
+ }
+
+ gbn_assert (get_point_board (POS (0, 0)) == BLACK);
+ gbn_assert (add_stone_sgf (POS (9, 9), EMPTY));
+ gbn_assert (play_move_sgf (POS (9, 9), WHITE));
+ gbn_assert (white_captures == 360);
+
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (white_captures == 0);
+
+ play_mode = MODE_FORCE_PLAY;
+ gbn_assert (play_move_sgf (POS (9, 9), BLACK));
+ play_mode = MODE_PLAY;
+ gbn_assert (white_captures == 361);
+
+ for (x = 0; x < 19; ++x)
+ {
+ for (y = 0; y < 19; ++y)
+ {
+ gbn_assert (get_point_board (POS (x, y)) == EMPTY);
+ }
+ }
+
+
+ /* test ko */
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 15));
+
+ /*
+ * Set up the board to look like this:
+ * -X------
+ * XO------
+ * O-------
+ * --------
+ */
+ gbn_assert (add_stone_sgf (POS (0, 1), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 0), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 1), WHITE));
+ gbn_assert (add_stone_sgf (POS (0, 2), WHITE));
+
+ /* take the ko and make sure black can't take back */
+ gbn_assert (play_move_sgf (POS (0, 0), WHITE));
+ gbn_assert (!play_move_sgf (POS (0, 1), BLACK));
+
+ /* make sure white can fill, even with the ko_pos set */
+ gbn_assert (play_move_sgf (POS (0, 1), WHITE));
+ /* and make sure undo sets the ko again */
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (!play_move_sgf (POS (0, 1), BLACK));
+
+ /* make sure ko threats clear the ko */
+ gbn_assert (play_move_sgf (POS (2, 2), BLACK)); /* ko threat */
+ gbn_assert (play_move_sgf (POS (2, 3), WHITE)); /* response */
+ gbn_assert (play_move_sgf (POS (0, 1), BLACK)); /* take ko */
+
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (undo_node_sgf ());
+
+ /* make sure a pass is counted as a ko threat */
+ gbn_assert (!play_move_sgf (POS (0, 1), BLACK));
+ gbn_assert (play_move_sgf (PASS_POS, BLACK));
+ gbn_assert (play_move_sgf (PASS_POS, WHITE));
+ gbn_assert (play_move_sgf (POS (0, 1), BLACK));
+
+ /* and finally let's make sure that white can't directly retake */
+ gbn_assert (!play_move_sgf (POS (0, 0), WHITE));
+
+
+
+ /* test some header information saving/loading as well as comment
+ saving loading */
+ char some_comment[] =
+ "blah blah blah i am a stupid comment. here's some annoying characters: 01234567890!@#$%^&*()[[[[\\\\\\]ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ /* that bit near the end is literally this: \\\] which tests escaping
+ of ]s */
+ char read_buffer[256];
+
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 5, -20));
+
+ /* this also tests that ko_pos is reset by setuping up a new game */
+ gbn_assert (play_move_sgf (POS (0, 0), WHITE));
+ gbn_assert (write_comment_sgf (some_comment) > 0);
+ gbn_assert (play_move_sgf (POS (0, 1), BLACK));
+ rb->strcpy (header.black, "Jack Black");
+ rb->strcpy (header.white, "Jill White");
+
+ gbn_assert (save_game (DEFAULT_SAVE_DIR "/head.sgf"));
+
+ gbn_assert (setup_game (MIN_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1));
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/head.sgf"));
+
+ gbn_assert (header.komi == -20 && header.handicap == 5);
+ gbn_assert (board_width == MAX_BOARD_SIZE
+ && board_height == MAX_BOARD_SIZE);
+ gbn_assert (rb->strcmp (header.black, "Jack Black") == 0);
+ gbn_assert (rb->strcmp (header.white, "Jill White") == 0);
+ gbn_assert (redo_node_sgf ());
+ gbn_assert (read_comment_sgf (read_buffer, sizeof (read_buffer)));
+ gbn_assert (rb->strcmp (read_buffer, some_comment) == 0);
+ gbn_assert (redo_node_sgf ());
+ gbn_assert (get_point_board (POS (0, 0)) == WHITE);
+ gbn_assert (get_point_board (POS (0, 1)) == BLACK);
+
+
+
+ /* test saving and loading a file with unhandled SGF properties. this
+ test requires that the user diff unhnd.sgf with unhnd_out.sgf (any
+ substantial difference is a bug and should be reported) the
+ following are NOT substantial differences: - reordering of
+ properties in a node - whitespace changes outside of a comment
+ value or other property value - reordering of property values */
+ gbn_assert (load_game (DEFAULT_SAVE_DIR "/unhnd.sgf"));
+ gbn_assert (save_game (DEFAULT_SAVE_DIR "/unhnd_out.sgf"));
+
+
+
+ /* Test variations a bit */
+ gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 13));
+ /* start at a move, otherwise add_stone won't create a variation */
+ gbn_assert (play_move_sgf (POS (5, 5), BLACK));
+ /* make sure it doesn't */
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (add_stone_sgf (POS (4, 5), WHITE));
+ gbn_assert (!undo_node_sgf ());
+ gbn_assert (num_variations_sgf () == 1);
+ gbn_assert (play_move_sgf (POS (5, 5), BLACK));
+
+ gbn_assert (play_move_sgf (POS (0, 0), BLACK));
+ gbn_assert (num_variations_sgf () == 1);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (play_move_sgf (POS (0, 1), BLACK));
+ gbn_assert (num_variations_sgf () == 2);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (play_move_sgf (POS (0, 1), BLACK));
+ gbn_assert (num_variations_sgf () == 2);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (play_move_sgf (POS (0, 2), BLACK));
+ gbn_assert (num_variations_sgf () == 3);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (play_move_sgf (POS (0, 3), WHITE));
+ gbn_assert (num_variations_sgf () == 4);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (play_move_sgf (PASS_POS, BLACK));
+ gbn_assert (num_variations_sgf () == 5);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (add_stone_sgf (POS (1, 1), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 2), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 3), WHITE));
+ gbn_assert (num_variations_sgf () == 6);
+ gbn_assert (undo_node_sgf ());
+ gbn_assert (add_stone_sgf (POS (1, 1), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 2), BLACK));
+ gbn_assert (add_stone_sgf (POS (1, 3), WHITE));
+ gbn_assert (num_variations_sgf () == 7);
+ gbn_assert (next_variation_sgf ());
+ gbn_assert (get_point_board (POS (0, 0)) == BLACK);
+ gbn_assert (get_point_board (POS (0, 1)) == EMPTY);
+ gbn_assert (get_point_board (POS (0, 2)) == EMPTY);
+ gbn_assert (get_point_board (POS (1, 1)) == EMPTY);
+ gbn_assert (get_point_board (POS (1, 2)) == EMPTY);
+ gbn_assert (get_point_board (POS (1, 3)) == EMPTY);
+
+ rb->splash (10 * HZ, "All tests passed. Exiting");
+}
+#endif /* GBN_TEST */