forked from len0rd/rockbox
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
This commit is contained in:
parent
21f0c9a282
commit
f2d5c3532f
25 changed files with 9332 additions and 0 deletions
9
apps/plugins/goban/SOURCES
Normal file
9
apps/plugins/goban/SOURCES
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
goban.c
|
||||||
|
board.c
|
||||||
|
display.c
|
||||||
|
game.c
|
||||||
|
sgf.c
|
||||||
|
sgf_output.c
|
||||||
|
sgf_parse.c
|
||||||
|
sgf_storage.c
|
||||||
|
util.c
|
354
apps/plugins/goban/board.c
Normal file
354
apps/plugins/goban/board.c
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 "goban.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
unsigned int board_width = MAX_BOARD_SIZE;
|
||||||
|
unsigned int board_height = MAX_BOARD_SIZE;
|
||||||
|
|
||||||
|
/* Board has 'invalid' markers around each border */
|
||||||
|
unsigned char board_data[(MAX_BOARD_SIZE + 2) * (MAX_BOARD_SIZE + 2)];
|
||||||
|
int white_captures = 0;
|
||||||
|
int black_captures = 0;
|
||||||
|
|
||||||
|
uint8_t board_marks[(MAX_BOARD_SIZE + 2) * (MAX_BOARD_SIZE + 2)];
|
||||||
|
uint8_t current_mark = 255;
|
||||||
|
|
||||||
|
/* there can't be any changes off of the board, so no need to add the
|
||||||
|
borders */
|
||||||
|
uint8_t board_changes[MAX_BOARD_SIZE * MAX_BOARD_SIZE];
|
||||||
|
|
||||||
|
unsigned short ko_pos = INVALID_POS;
|
||||||
|
|
||||||
|
/* forward declarations */
|
||||||
|
static void setup_marks (void);
|
||||||
|
static void make_mark (unsigned short pos);
|
||||||
|
static int get_liberties_helper (unsigned short pos, unsigned char orig_color);
|
||||||
|
static int flood_fill_helper (unsigned short pos, unsigned char orig_color,
|
||||||
|
unsigned char color);
|
||||||
|
|
||||||
|
|
||||||
|
/* these aren't "board marks" in the marks on the SGF sense, they are used
|
||||||
|
internally to mark already visited points and the like (such as when
|
||||||
|
doing liberty counting for groups) */
|
||||||
|
static void
|
||||||
|
setup_marks (void)
|
||||||
|
{
|
||||||
|
unsigned int x, y;
|
||||||
|
|
||||||
|
current_mark++;
|
||||||
|
|
||||||
|
if (current_mark == 0)
|
||||||
|
{
|
||||||
|
current_mark++;
|
||||||
|
|
||||||
|
for (y = 0; y < board_height; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < board_width; ++x)
|
||||||
|
{
|
||||||
|
board_marks[POS (x, y)] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
make_mark (unsigned short pos)
|
||||||
|
{
|
||||||
|
board_marks[pos] = current_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_marked (unsigned short pos)
|
||||||
|
{
|
||||||
|
return board_marks[pos] == current_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clear_board (void)
|
||||||
|
{
|
||||||
|
unsigned int i, x, y;
|
||||||
|
|
||||||
|
/* for the borders */
|
||||||
|
for (i = 0; i < (2 + MAX_BOARD_SIZE) * (2 + MAX_BOARD_SIZE); ++i)
|
||||||
|
{
|
||||||
|
board_data[i] = INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now make the actual board part */
|
||||||
|
for (y = 0; y < board_height; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < board_width; ++x)
|
||||||
|
{
|
||||||
|
board_data[POS (x, y)] = EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
white_captures = 0;
|
||||||
|
black_captures = 0;
|
||||||
|
|
||||||
|
ko_pos = INVALID_POS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
set_size_board (int width, int height)
|
||||||
|
{
|
||||||
|
if (width < MIN_BOARD_SIZE || width > MAX_BOARD_SIZE ||
|
||||||
|
height < MIN_BOARD_SIZE || height > MAX_BOARD_SIZE)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
board_width = width;
|
||||||
|
board_height = height;
|
||||||
|
setup_display ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char
|
||||||
|
get_point_board (unsigned short pos)
|
||||||
|
{
|
||||||
|
return board_data[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_point_board (unsigned short pos, unsigned char color)
|
||||||
|
{
|
||||||
|
board_data[pos] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_board (unsigned short pos)
|
||||||
|
{
|
||||||
|
if (pos < POS (0, 0) ||
|
||||||
|
pos > POS (board_width - 1, board_height - 1) ||
|
||||||
|
get_point_board (pos) == INVALID)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
get_liberties_board (unsigned short pos)
|
||||||
|
{
|
||||||
|
if (!on_board (pos) || get_point_board (pos) == EMPTY)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_marks ();
|
||||||
|
|
||||||
|
int ret_val = 0;
|
||||||
|
unsigned char orig_color = get_point_board (pos);
|
||||||
|
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
push_pos_stack (&parse_stack, pos);
|
||||||
|
|
||||||
|
/* Since we only ever test for liberties in order to determine
|
||||||
|
captures and the like, there's no reason to count any liberties
|
||||||
|
higher than 2 (we sometimes need to know if something has 1 liberty
|
||||||
|
for dealing with ko) */
|
||||||
|
while (pop_pos_stack (&parse_stack, &pos) && ret_val < 2)
|
||||||
|
{
|
||||||
|
ret_val += get_liberties_helper (NORTH (pos), orig_color);
|
||||||
|
ret_val += get_liberties_helper (SOUTH (pos), orig_color);
|
||||||
|
ret_val += get_liberties_helper (EAST (pos), orig_color);
|
||||||
|
ret_val += get_liberties_helper (WEST (pos), orig_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there's more than two liberties, the stack isn't empty, so empty
|
||||||
|
it */
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_liberties_helper (unsigned short pos, unsigned char orig_color)
|
||||||
|
{
|
||||||
|
if (on_board (pos) &&
|
||||||
|
get_point_board (pos) != OTHER (orig_color) && !is_marked (pos))
|
||||||
|
{
|
||||||
|
make_mark (pos);
|
||||||
|
|
||||||
|
if (get_point_board (pos) == EMPTY)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
push_pos_stack (&parse_stack, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
flood_fill_board (unsigned short pos, unsigned char color)
|
||||||
|
{
|
||||||
|
if (!on_board (pos) || get_point_board (pos) == color)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
int ret_val = 0;
|
||||||
|
|
||||||
|
unsigned char orig_color = get_point_board (pos);
|
||||||
|
|
||||||
|
set_point_board (pos, color);
|
||||||
|
++ret_val;
|
||||||
|
push_pos_stack (&parse_stack, pos);
|
||||||
|
|
||||||
|
while (pop_pos_stack (&parse_stack, &pos))
|
||||||
|
{
|
||||||
|
ret_val += flood_fill_helper (NORTH (pos), orig_color, color);
|
||||||
|
ret_val += flood_fill_helper (SOUTH (pos), orig_color, color);
|
||||||
|
ret_val += flood_fill_helper (EAST (pos), orig_color, color);
|
||||||
|
ret_val += flood_fill_helper (WEST (pos), orig_color, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
flood_fill_helper (unsigned short pos, unsigned char orig_color,
|
||||||
|
unsigned char color)
|
||||||
|
{
|
||||||
|
if (on_board (pos) && get_point_board (pos) == orig_color)
|
||||||
|
{
|
||||||
|
set_point_board (pos, color);
|
||||||
|
push_pos_stack (&parse_stack, pos);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
legal_move_board (unsigned short pos, unsigned char color, bool allow_suicide)
|
||||||
|
{
|
||||||
|
/* you can always pass */
|
||||||
|
if (pos == PASS_POS)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!on_board (pos) || (color != BLACK && color != WHITE))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == ko_pos && color == current_player)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_point_board (pos) != EMPTY)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't need to save the current state, because it's always empty
|
||||||
|
since we tested for that above */
|
||||||
|
set_point_board (pos, color);
|
||||||
|
|
||||||
|
/* if we have liberties, it can't be illegal */
|
||||||
|
if (get_liberties_board (pos) > 0 ||
|
||||||
|
/* if we can capture something, it can't be illegal */
|
||||||
|
(get_point_board (NORTH (pos)) == OTHER (color) &&
|
||||||
|
!get_liberties_board (NORTH (pos))) ||
|
||||||
|
(get_point_board (SOUTH (pos)) == OTHER (color) &&
|
||||||
|
!get_liberties_board (SOUTH (pos))) ||
|
||||||
|
(get_point_board (EAST (pos)) == OTHER (color) &&
|
||||||
|
!get_liberties_board (EAST (pos))) ||
|
||||||
|
(get_point_board (WEST (pos)) == OTHER (color) &&
|
||||||
|
!get_liberties_board (WEST (pos))) ||
|
||||||
|
/* if we're allowed to suicide, only multi-stone suicide is legal
|
||||||
|
(no ruleset allows single-stone suicide that I know of) */
|
||||||
|
(allow_suicide && (get_point_board (NORTH (pos)) == color ||
|
||||||
|
get_point_board (SOUTH (pos)) == color ||
|
||||||
|
get_point_board (EAST (pos)) == color ||
|
||||||
|
get_point_board (WEST (pos)) == color)))
|
||||||
|
{
|
||||||
|
/* undo our previous set */
|
||||||
|
set_point_board (pos, EMPTY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* undo our previous set */
|
||||||
|
set_point_board (pos, EMPTY);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned short
|
||||||
|
WRAP (unsigned short pos)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
if (on_board (pos))
|
||||||
|
{
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = I (pos);
|
||||||
|
y = J (pos);
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
{
|
||||||
|
x = board_width - 1;
|
||||||
|
}
|
||||||
|
else if ((unsigned int) x >= board_width)
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < 0)
|
||||||
|
{
|
||||||
|
y = board_height - 1;
|
||||||
|
}
|
||||||
|
else if ((unsigned int) y >= board_height)
|
||||||
|
{
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
return POS (x, y);
|
||||||
|
}
|
||||||
|
}
|
100
apps/plugins/goban/board.h
Normal file
100
apps/plugins/goban/board.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_BOARD_H
|
||||||
|
#define GOBAN_BOARD_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "goban.h" /* for LCD_BOARD_SIZE */
|
||||||
|
|
||||||
|
#define WHITE 1
|
||||||
|
#define BLACK 2
|
||||||
|
#define EMPTY 0
|
||||||
|
#define NONE 0
|
||||||
|
#define INVALID 4
|
||||||
|
|
||||||
|
#define OTHER(color) (color == BLACK ? WHITE : BLACK)
|
||||||
|
|
||||||
|
/* MAX_BOARD_SIZE no longer dependent on screen
|
||||||
|
size since zooming was implemented */
|
||||||
|
#define MAX_BOARD_SIZE 19
|
||||||
|
#define MIN_BOARD_SIZE 1
|
||||||
|
|
||||||
|
#define INVALID_POS ((unsigned short) -5003)
|
||||||
|
#define PASS_POS ((unsigned short) -5002)
|
||||||
|
|
||||||
|
#define POS(i, j) ((i + 1) + (j + 1) * (MAX_BOARD_SIZE + 2))
|
||||||
|
#define WEST(i) (i - 1)
|
||||||
|
#define EAST(i) (i + 1)
|
||||||
|
#define NORTH(i) (i - (MAX_BOARD_SIZE + 2))
|
||||||
|
#define SOUTH(i) (i + (MAX_BOARD_SIZE + 2))
|
||||||
|
|
||||||
|
unsigned short WRAP (unsigned short pos);
|
||||||
|
|
||||||
|
#define I(pos) (pos % (MAX_BOARD_SIZE + 2) - 1)
|
||||||
|
#define J(pos) (pos / (MAX_BOARD_SIZE + 2) - 1)
|
||||||
|
|
||||||
|
/* Clear the data from the board, including marks and such. Should be
|
||||||
|
called after setting the board size */
|
||||||
|
void clear_board (void);
|
||||||
|
|
||||||
|
/* Set the size of the board. Follow with a call to clear_board() and a
|
||||||
|
call to setup_display(). Returns false on failure (generally, invalid
|
||||||
|
width or height) */
|
||||||
|
bool set_size_board (int width, int height);
|
||||||
|
|
||||||
|
/* Returns true if the given move is legal. allow_suicide should be true
|
||||||
|
if suicide is a valid move with the current ruleset */
|
||||||
|
bool legal_move_board (unsigned short pos, unsigned char color,
|
||||||
|
bool allow_suicide);
|
||||||
|
|
||||||
|
/* Returns true if the pos is on the board */
|
||||||
|
bool on_board (unsigned short pos);
|
||||||
|
|
||||||
|
/* Get the color on the board at the given pos. Should be
|
||||||
|
BLACK/WHITE/EMPTY, and sometimes INVALID. */
|
||||||
|
unsigned char get_point_board (unsigned short pos);
|
||||||
|
|
||||||
|
/* Set the color of point at pos, which must be on the board */
|
||||||
|
void set_point_board (unsigned short pos, unsigned char color);
|
||||||
|
|
||||||
|
/* Get the number of liberties of the group of which pos is a stone.
|
||||||
|
Returns less than zero if pos is empty. If the number of liberties of
|
||||||
|
the group is greater than 2, 2 is returned. */
|
||||||
|
int get_liberties_board (unsigned short pos);
|
||||||
|
|
||||||
|
/* A simple flood fill algorithm for capturing or uncapturing stones.
|
||||||
|
Returns the number of locations changed. */
|
||||||
|
int flood_fill_board (unsigned short pos, unsigned char color);
|
||||||
|
|
||||||
|
/* The size of the board */
|
||||||
|
extern unsigned int board_width;
|
||||||
|
extern unsigned int board_height;
|
||||||
|
|
||||||
|
/* The number of captures for each player */
|
||||||
|
extern int black_captures;
|
||||||
|
extern int white_captures;
|
||||||
|
|
||||||
|
/* If there is a ko which cannot be retaken, this is set to the point
|
||||||
|
which may not be played at. Otherwise this is INVALID_POS. */
|
||||||
|
extern unsigned short ko_pos;
|
||||||
|
|
||||||
|
#endif
|
1091
apps/plugins/goban/display.c
Normal file
1091
apps/plugins/goban/display.c
Normal file
File diff suppressed because it is too large
Load diff
111
apps/plugins/goban/display.h
Normal file
111
apps/plugins/goban/display.h
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_DISPLAY_H
|
||||||
|
#define GOBAN_DISPLAY_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "goban.h"
|
||||||
|
|
||||||
|
/* Call before using the display */
|
||||||
|
void setup_display (void);
|
||||||
|
|
||||||
|
/* Draw the board and the "footer" */
|
||||||
|
void draw_screen_display (void);
|
||||||
|
|
||||||
|
/* The location of the cursor */
|
||||||
|
extern unsigned short cursor_pos;
|
||||||
|
|
||||||
|
/* True if we should draw variations */
|
||||||
|
extern bool draw_variations;
|
||||||
|
|
||||||
|
/* Used to set the zoom level, loaded in from the config file */
|
||||||
|
unsigned int saved_circle_size;
|
||||||
|
|
||||||
|
/* the size of one intersection on the board, in pixels */
|
||||||
|
unsigned int intersection_size;
|
||||||
|
|
||||||
|
/* Clear the marks from the board */
|
||||||
|
void clear_marks_display (void);
|
||||||
|
|
||||||
|
/* Add a mark to the display */
|
||||||
|
void set_mark_display (unsigned short pos, unsigned char mark_char);
|
||||||
|
|
||||||
|
/* Set to indicate if we should display the 'C' in the footer or not */
|
||||||
|
void set_comment_display (bool new_val);
|
||||||
|
|
||||||
|
/* Move the display so that the position pos is in view, very useful if
|
||||||
|
we're zoomed in (otherwise does nothing) */
|
||||||
|
void move_display (unsigned short pos);
|
||||||
|
|
||||||
|
/* These should all be obvious. Zoom levels start at 1. set_zoom_display
|
||||||
|
will set the default zoom level if called with zoom_level == 0 */
|
||||||
|
void set_zoom_display (unsigned int zoom_level);
|
||||||
|
unsigned int current_zoom_display (void);
|
||||||
|
unsigned int min_zoom_display (void);
|
||||||
|
unsigned int max_zoom_display (void);
|
||||||
|
|
||||||
|
/* MIN and MAX intersection sizes */
|
||||||
|
#define MIN_DEFAULT_INT_SIZE (11)
|
||||||
|
|
||||||
|
/* The absolute minimum that is allowed */
|
||||||
|
#define MIN_INT_SIZE (3)
|
||||||
|
|
||||||
|
/* Don't allow one bigger than the size of the screen */
|
||||||
|
#define MAX_INT_SIZE (min((LCD_BOARD_WIDTH & 1) ? LCD_BOARD_WIDTH : \
|
||||||
|
LCD_BOARD_WIDTH - 1, \
|
||||||
|
(LCD_BOARD_HEIGHT & 1) ? LCD_BOARD_HEIGHT : \
|
||||||
|
LCD_BOARD_HEIGHT - 1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* NOTE: we do one "extra" intersection in each direction which goes off
|
||||||
|
the screen, because it makes drawing a little bit prettier (the board
|
||||||
|
doesn't end before the edge of the screen this way) */
|
||||||
|
|
||||||
|
|
||||||
|
/* right is a screen boundary if we're on a tall screen, bottom is a
|
||||||
|
screen boundary if we're on a wide screen */
|
||||||
|
#if defined(GBN_TALL_SCREEN)
|
||||||
|
#define MIN_X ((min_x_int == 0) ? 0 : min_x_int - 1)
|
||||||
|
#define MIN_Y (min_y_int)
|
||||||
|
/* always flush with top, no need for extra */
|
||||||
|
|
||||||
|
#define MAX_X (min(min_x_int + num_x_ints + 1, board_width))
|
||||||
|
#define MAX_Y (min(min_y_int + num_y_ints, board_height))
|
||||||
|
#else
|
||||||
|
#define MIN_X (min_x_int)
|
||||||
|
/* always flush with left, no need for extra */
|
||||||
|
|
||||||
|
#define MIN_Y ((min_y_int == 0) ? 0 : min_y_int - 1)
|
||||||
|
#define MAX_X (min(min_x_int + num_x_ints, board_width))
|
||||||
|
#define MAX_Y (min(min_y_int + num_y_ints + 1, board_height))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* These are the same as above, except without the extra intersection is
|
||||||
|
board-boundary directions */
|
||||||
|
#define REAL_MIN_X (min_x_int)
|
||||||
|
#define REAL_MIN_Y (min_y_int)
|
||||||
|
#define REAL_MAX_X (min(min_x_int + num_x_ints, board_width))
|
||||||
|
#define REAL_MAX_Y (min(min_y_int + num_y_ints, board_height))
|
||||||
|
|
||||||
|
#endif
|
236
apps/plugins/goban/game.c
Normal file
236
apps/plugins/goban/game.c
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 "game.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "goban.h"
|
||||||
|
#include "sgf.h"
|
||||||
|
#include "sgf_output.h"
|
||||||
|
#include "sgf_parse.h"
|
||||||
|
#include "sgf_storage.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
static void pre_game_setup (void);
|
||||||
|
|
||||||
|
char save_file[SAVE_FILE_LENGTH];
|
||||||
|
bool game_dirty = false;
|
||||||
|
bool autosave_dirty = false;
|
||||||
|
|
||||||
|
int move_num = 0;
|
||||||
|
|
||||||
|
unsigned char current_player = BLACK;
|
||||||
|
|
||||||
|
struct header_t header;
|
||||||
|
|
||||||
|
void
|
||||||
|
set_game_modified (void)
|
||||||
|
{
|
||||||
|
game_dirty = true;
|
||||||
|
autosave_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
load_game (const char *filename)
|
||||||
|
{
|
||||||
|
rb->memset (&header, 0, sizeof (header));
|
||||||
|
|
||||||
|
if (rb->strlen (filename) + 1 > SAVE_FILE_LENGTH)
|
||||||
|
{
|
||||||
|
DEBUGF ("file name too long\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rb->file_exists (filename))
|
||||||
|
{
|
||||||
|
DEBUGF ("file doesn't exist!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre_game_setup ();
|
||||||
|
|
||||||
|
rb->strcpy (save_file, filename);
|
||||||
|
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cpu_boost (true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rb->splash (0, "Loading...");
|
||||||
|
|
||||||
|
bool parse_failed = false;
|
||||||
|
if (!parse_sgf (save_file))
|
||||||
|
{
|
||||||
|
rb->splash (3 * HZ, "Unable to parse SGF file. Will overwrite.");
|
||||||
|
parse_failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_dirty = false;
|
||||||
|
autosave_dirty = false;
|
||||||
|
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cpu_boost (false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (header.handicap >= 2)
|
||||||
|
{
|
||||||
|
current_player = WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
post_game_setup_sgf ();
|
||||||
|
|
||||||
|
if (!parse_failed)
|
||||||
|
{
|
||||||
|
draw_screen_display();
|
||||||
|
if (rb->strcmp(filename, DEFAULT_SAVE))
|
||||||
|
{
|
||||||
|
metadata_summary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
save_game (const char *filename)
|
||||||
|
{
|
||||||
|
if (rb->strlen (filename) + 1 > SAVE_FILE_LENGTH)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We only have to do something if the game is dirty, or we're being
|
||||||
|
asked to save to a different location than we loaded from.
|
||||||
|
|
||||||
|
If the game isn't dirty and we're being asked to save to default,
|
||||||
|
we also don't have to do anything.*/
|
||||||
|
if (!game_dirty &&
|
||||||
|
(rb->strcmp (filename, save_file) == 0 ||
|
||||||
|
rb->strcmp (filename, DEFAULT_SAVE) == 0))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cpu_boost (true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rb->splash (0, "Saving...");
|
||||||
|
|
||||||
|
if (output_sgf (filename))
|
||||||
|
{
|
||||||
|
/* saving only "cleans" the game if it's not a save to default,
|
||||||
|
* or if our save_file is actually default
|
||||||
|
*
|
||||||
|
* (so autosaves won't prevent legitimate saves to a Save As or
|
||||||
|
* loaded file)
|
||||||
|
*/
|
||||||
|
if (rb->strcmp (filename, DEFAULT_SAVE) ||
|
||||||
|
rb->strcmp (save_file, DEFAULT_SAVE) == 0)
|
||||||
|
{
|
||||||
|
game_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* but saving anywhere means that autosave isn't dirty */
|
||||||
|
autosave_dirty = false;
|
||||||
|
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cpu_boost (false);
|
||||||
|
#endif
|
||||||
|
/* The save succeeded. Now, if we saved to an actual file (not to the
|
||||||
|
* DEFAULT_SAVE), then we should delete the DEFAULT_SAVE file because
|
||||||
|
* the changes stored in it are no longer unsaved.
|
||||||
|
*/
|
||||||
|
if (rb->strcmp (filename, DEFAULT_SAVE))
|
||||||
|
{
|
||||||
|
rb->remove(DEFAULT_SAVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cpu_boost (false);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
pre_game_setup (void)
|
||||||
|
{
|
||||||
|
rb->memset (&header, 0, sizeof (header));
|
||||||
|
|
||||||
|
clear_caches_sgf ();
|
||||||
|
free_tree_sgf ();
|
||||||
|
|
||||||
|
set_size_board (MAX_BOARD_SIZE, MAX_BOARD_SIZE);
|
||||||
|
|
||||||
|
clear_board ();
|
||||||
|
|
||||||
|
game_dirty = true;
|
||||||
|
move_num = 0;
|
||||||
|
|
||||||
|
play_mode = MODE_PLAY;
|
||||||
|
|
||||||
|
rb->strcpy (save_file, DEFAULT_SAVE);
|
||||||
|
|
||||||
|
header_marked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
setup_game (int width, int height, int handicap, int komi)
|
||||||
|
{
|
||||||
|
pre_game_setup ();
|
||||||
|
|
||||||
|
if (!set_size_board (width, height))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_board ();
|
||||||
|
|
||||||
|
/* place handicap */
|
||||||
|
if (handicap >= 2)
|
||||||
|
{
|
||||||
|
current_player = WHITE;
|
||||||
|
}
|
||||||
|
else if (handicap < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_player = BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.handicap = handicap;
|
||||||
|
setup_handicap_sgf ();
|
||||||
|
|
||||||
|
header.komi = komi;
|
||||||
|
|
||||||
|
post_game_setup_sgf ();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
59
apps/plugins/goban/game.h
Normal file
59
apps/plugins/goban/game.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GAME_GOBAN_H
|
||||||
|
#define GAME_GOBAN_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Call whenever anything saveable in the SGF file might have changed */
|
||||||
|
void set_game_modified (void);
|
||||||
|
|
||||||
|
/* Setup a new game, clearing anything currently in the SGF tree Returns
|
||||||
|
false on failure, in which case the old data is NOT guaranteed to be
|
||||||
|
available anymore */
|
||||||
|
bool setup_game (int width, int height, int handicap, int komi);
|
||||||
|
|
||||||
|
/* Load a game from a file, clearing anything currently in the SGF tree
|
||||||
|
Returns false on failure, in which case the old data is NOT guaranteed
|
||||||
|
to be available anymore */
|
||||||
|
bool load_game (const char *filename);
|
||||||
|
|
||||||
|
/* Save the data in the SGF tree to a file. Returns false on failure */
|
||||||
|
bool save_game (const char *filename);
|
||||||
|
|
||||||
|
/* The number of the current move (starts at 0, first move is 1) */
|
||||||
|
extern int move_num;
|
||||||
|
|
||||||
|
/* The color of the current_player, either BLACK or WHITE */
|
||||||
|
extern unsigned char current_player;
|
||||||
|
/* Where should we save to if explicitly saved? */
|
||||||
|
extern char save_file[];
|
||||||
|
/* True if there are unsaved changes in the file */
|
||||||
|
extern bool game_dirty;
|
||||||
|
|
||||||
|
/* True if there are changes that have been neither autosaved nor saved */
|
||||||
|
extern bool autosave_dirty;
|
||||||
|
|
||||||
|
/* The game metadata */
|
||||||
|
extern struct header_t header;
|
||||||
|
|
||||||
|
#endif
|
1232
apps/plugins/goban/goban.c
Normal file
1232
apps/plugins/goban/goban.c
Normal file
File diff suppressed because it is too large
Load diff
277
apps/plugins/goban/goban.h
Normal file
277
apps/plugins/goban/goban.h
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_MAIN_H
|
||||||
|
#define GOBAN_MAIN_H
|
||||||
|
|
||||||
|
/* Enable this to run test mode. (see the end of util.c) */
|
||||||
|
#if 0
|
||||||
|
#define GBN_TEST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Colors of various things. The colors on mono bitmap targets is fixed
|
||||||
|
based on the background/foreground color. */
|
||||||
|
#ifdef HAVE_LCD_COLOR
|
||||||
|
#define BOARD_COLOR LCD_RGBPACK(184,136,72)
|
||||||
|
#define WHITE_COLOR LCD_RGBPACK(255,255,255)
|
||||||
|
#define BLACK_COLOR LCD_RGBPACK(0,0,0)
|
||||||
|
#define LINE_COLOR LCD_RGBPACK(0,0,0)
|
||||||
|
#define BACKGROUND_COLOR LCD_RGBPACK(41,104,74)
|
||||||
|
#define CURSOR_COLOR LCD_RGBPACK(222,0,0)
|
||||||
|
#define MARK_COLOR LCD_RGBPACK(0,0,255)
|
||||||
|
#elif LCD_DEPTH > 1 /* grayscale */
|
||||||
|
#define BOARD_COLOR LCD_LIGHTGRAY
|
||||||
|
#define WHITE_COLOR LCD_WHITE
|
||||||
|
#define BLACK_COLOR LCD_BLACK
|
||||||
|
#define LINE_COLOR LCD_BLACK
|
||||||
|
#define BACKGROUND_COLOR LCD_DARKGRAY
|
||||||
|
#define CURSOR_COLOR LCD_DARKGRAY
|
||||||
|
#define MARK_COLOR LCD_DARKGRAY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Key setups */
|
||||||
|
#ifdef HAVE_TOUCHSCREEN
|
||||||
|
#define GBN_BUTTON_UP BUTTON_TOPMIDDLE
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_BOTTOMMIDDLE
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_MIDLEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_MIDRIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_BOTTOMLEFT
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_BOTTOMRIGHT
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_TOPLEFT
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_CENTER | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_CENTER | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_TOPRIGHT
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|
||||||
|
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
|
||||||
|
|| (CONFIG_KEYPAD == IPOD_4G_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_MENU
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_PLAY
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_SCROLL_BACK
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_SCROLL_FWD
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
/* no context */
|
||||||
|
/* no next var */
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) \
|
||||||
|
|| (CONFIG_KEYPAD == SANSA_FUZE_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_SCROLL_BACK
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_SCROLL_FWD
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_POWER
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_REC
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_POWER
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_REC
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == GIGABEAT_PAD) \
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_MENU
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_A
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_MENU
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_PLAY
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_SCROLL_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_SCROLL_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_FF
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_REW
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_POWER
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_PLAY | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_PLAY | BUTTON_REPEAT
|
||||||
|
/* No next var */
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
|
||||||
|
(CONFIG_KEYPAD == IRIVER_H300_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_OFF
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_ON
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_MODE
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_REC
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == MROBE100_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_MENU
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_PLAY
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_DISPLAY
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_POWER
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_PLAY
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_REC
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_POWER
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_SELECT | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_SELECT | BUTTON_REPEAT
|
||||||
|
/* no next var */
|
||||||
|
|
||||||
|
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
|
||||||
|
/* TODO: these are basically complete guesses, I have no manual to go by */
|
||||||
|
#define GBN_BUTTON_UP BUTTON_RC_VOL_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_RC_VOL_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_RC_REW
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RC_FF
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_MODE
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_PLAY | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_PLAY | BUTTON_REPEAT
|
||||||
|
/* no next var */
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == RECORDER_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_RETREAT BUTTON_F1
|
||||||
|
#define GBN_BUTTON_ADVANCE BUTTON_F3
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_F2
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_PLAY | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_CONTEXT BUTTON_PLAY | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_NEXT_VAR BUTTON_ON
|
||||||
|
|
||||||
|
#elif (CONFIG_KEYPAD == ONDIO_PAD)
|
||||||
|
#define GBN_BUTTON_UP BUTTON_UP
|
||||||
|
#define GBN_BUTTON_DOWN BUTTON_DOWN
|
||||||
|
#define GBN_BUTTON_LEFT BUTTON_LEFT
|
||||||
|
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
|
||||||
|
#define GBN_BUTTON_MENU BUTTON_MENU | BUTTON_REPEAT
|
||||||
|
#define GBN_BUTTON_PLAY BUTTON_MENU | BUTTON_REL
|
||||||
|
#define GBN_BUTTON_NAV_MODE BUTTON_OFF
|
||||||
|
/* No context */
|
||||||
|
/* No advance/retreat */
|
||||||
|
/* no next var */
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unsupported keypad
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* The smallest dimension of the LCD */
|
||||||
|
#define LCD_MIN_DIMENSION (LCD_HEIGHT > LCD_WIDTH ? LCD_WIDTH : LCD_HEIGHT)
|
||||||
|
|
||||||
|
|
||||||
|
/* Determine if we have a wide screen or a tall screen. This is used to
|
||||||
|
place the board and footer in acceptable locations also, set the
|
||||||
|
LCD_BOARD_SIZE, making sure that we have at least 16 pixels for the
|
||||||
|
"footer" on either the bottom or the right. */
|
||||||
|
|
||||||
|
#define FOOTER_RESERVE (16)
|
||||||
|
|
||||||
|
#if (LCD_WIDTH > LCD_HEIGHT)
|
||||||
|
|
||||||
|
#define GBN_WIDE_SCREEN
|
||||||
|
|
||||||
|
#define LCD_BOARD_WIDTH (LCD_WIDTH - FOOTER_RESERVE)
|
||||||
|
#define LCD_BOARD_HEIGHT LCD_HEIGHT
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define GBN_TALL_SCREEN
|
||||||
|
|
||||||
|
#define LCD_BOARD_WIDTH LCD_WIDTH
|
||||||
|
#define LCD_BOARD_HEIGHT (LCD_HEIGHT - FOOTER_RESERVE)
|
||||||
|
|
||||||
|
#endif // LCD_WIDTH > LCD_HEIGHT
|
||||||
|
|
||||||
|
|
||||||
|
/* The directory we default to for saving crap */
|
||||||
|
#define DEFAULT_SAVE_DIR "/sgf"
|
||||||
|
|
||||||
|
/* The default file we save to */
|
||||||
|
#define DEFAULT_SAVE (DEFAULT_SAVE_DIR "/gbn_def.sgf")
|
||||||
|
|
||||||
|
/* The size of the buffer we store filenames in (1 reserved for '\0') */
|
||||||
|
#define SAVE_FILE_LENGTH 256
|
||||||
|
|
||||||
|
/* The maximum setting for idle autosave time, in minutes */
|
||||||
|
#define MAX_AUTOSAVE (30)
|
||||||
|
|
||||||
|
/* On mono targets, draw while stones with a black outline so they are
|
||||||
|
actually visibile instead of being white on white */
|
||||||
|
#if (LCD_DEPTH == 1)
|
||||||
|
#define OUTLINE_STONES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The current play mode */
|
||||||
|
extern enum play_mode_t play_mode;
|
||||||
|
|
||||||
|
/* Show comments when redoing onto a move? */
|
||||||
|
extern bool auto_show_comments;
|
||||||
|
|
||||||
|
/* A stack used for parsing/outputting as well as some board functions
|
||||||
|
such as counting liberties and filling in/ removing stones */
|
||||||
|
extern struct stack_t parse_stack;
|
||||||
|
|
||||||
|
#endif
|
22
apps/plugins/goban/goban.make
Normal file
22
apps/plugins/goban/goban.make
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# __________ __ ___.
|
||||||
|
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
# \/ \/ \/ \/ \/
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
GOBANSRCDIR := $(APPSDIR)/plugins/goban
|
||||||
|
GOBANBUILDDIR := $(BUILDDIR)/apps/plugins/goban
|
||||||
|
|
||||||
|
ROCKS += $(GOBANBUILDDIR)/goban.rock
|
||||||
|
|
||||||
|
|
||||||
|
GOBAN_SRC := $(call preprocess, $(GOBANSRCDIR)/SOURCES)
|
||||||
|
GOBAN_OBJ := $(call c2obj, $(GOBAN_SRC))
|
||||||
|
|
||||||
|
OTHER_SRC += $(GOBAN_SRC)
|
||||||
|
|
||||||
|
$(GOBANBUILDDIR)/goban.rock: $(GOBAN_OBJ)
|
2237
apps/plugins/goban/sgf.c
Normal file
2237
apps/plugins/goban/sgf.c
Normal file
File diff suppressed because it is too large
Load diff
170
apps/plugins/goban/sgf.h
Normal file
170
apps/plugins/goban/sgf.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_SGF_H
|
||||||
|
#define GOBAN_SGF_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Play one move. If play_mode is not PLAY_MODE_FORCE, this will fail if
|
||||||
|
an illegal move is played (plays by the "wrong" color are not counted
|
||||||
|
as illegal. pos may be a POS() on the board, or PASS_POS. Returns true
|
||||||
|
if the move was successfully played. */
|
||||||
|
bool play_move_sgf (unsigned short pos, unsigned char color);
|
||||||
|
|
||||||
|
/* Add a stone to the board, or remove one if color == EMPTY. returns true
|
||||||
|
if the stone is successfully added/removed (will return false if there
|
||||||
|
is nothing to add/remove there, eg. when adding a black stone when a
|
||||||
|
black stone is already there) */
|
||||||
|
bool add_stone_sgf (unsigned short pos, unsigned char color);
|
||||||
|
|
||||||
|
/* Add a mark to the board at the current node pos must be on the board
|
||||||
|
Returns false on failure. */
|
||||||
|
bool add_mark_sgf (unsigned short pos, enum prop_type_t type);
|
||||||
|
|
||||||
|
/* Returns true if there are more nodes before or after (respectively) the
|
||||||
|
current node */
|
||||||
|
bool has_prev_nodes_sgf (void);
|
||||||
|
bool has_more_nodes_sgf (void);
|
||||||
|
|
||||||
|
/* When at the start of a branch, this will follow one of the branches.
|
||||||
|
Variations are numbered from 0. Returns false on failure. */
|
||||||
|
bool go_to_variation_sgf (unsigned int num);
|
||||||
|
|
||||||
|
/* Get the number of variations at the current move (will be 1 unless the
|
||||||
|
current node is a branch node */
|
||||||
|
int num_variations_sgf (void);
|
||||||
|
|
||||||
|
/* Calls into the display subsystem to mark any child variations of the
|
||||||
|
current node. Returns the number of variations marked */
|
||||||
|
int mark_child_variations_sgf (void);
|
||||||
|
|
||||||
|
/* Calls into the display subsystem to pass marks to be drawn on the board
|
||||||
|
for the current node. Does not do child variation marks. */
|
||||||
|
void set_all_marks_sgf (void);
|
||||||
|
|
||||||
|
/* Add a child regardless of if there is a child node already or not.
|
||||||
|
*variation_number will be set to the variation number of the added
|
||||||
|
variation Returns the handle of the new node. */
|
||||||
|
int add_child_sgf (int *variation_number);
|
||||||
|
|
||||||
|
/* Goes to the next variation after the current one if the current node is
|
||||||
|
a branch node. Returns the number of the new variation */
|
||||||
|
int next_variation_sgf (void);
|
||||||
|
|
||||||
|
/* ints in these are handles to storage locations, bools mean failure if
|
||||||
|
false */
|
||||||
|
int add_prop_sgf (int node, enum prop_type_t type, union prop_data_t data);
|
||||||
|
bool delete_prop_sgf (int node, enum prop_type_t type);
|
||||||
|
bool delete_prop_handle_sgf (int node, int prop);
|
||||||
|
int get_prop_sgf (int node, enum prop_type_t type, int *previous_prop);
|
||||||
|
|
||||||
|
/* If there is already a property of the same type, it will be
|
||||||
|
overwritten, otherwise a new one is added Returns the handle of the
|
||||||
|
added property. */
|
||||||
|
int add_or_set_prop_sgf (int node, enum prop_type_t type,
|
||||||
|
union prop_data_t data);
|
||||||
|
|
||||||
|
/* Find a property of similar type with the same position in the current
|
||||||
|
node. (for example, if type == PROP_ADD_BLACK and pos == POS(0, 0), it
|
||||||
|
will find a prop with type == PROP_ADD_WHITE and pos == POS(0, 0), but
|
||||||
|
not any of the mark types with pos == POS(0, 0). returns the handle of
|
||||||
|
the found property */
|
||||||
|
int get_prop_pos_sgf (enum prop_type_t type, union prop_data_t data);
|
||||||
|
|
||||||
|
/* If there is a move in the current node, return its handle. */
|
||||||
|
int get_move_sgf (void);
|
||||||
|
|
||||||
|
/* If there is a comment in the current node, this will read it out into
|
||||||
|
the buffer. Returns the size of the comment read (including the '\0').
|
||||||
|
The buffer can be treated as a string. */
|
||||||
|
int read_comment_sgf (char *buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
/* Write a comment property to the current node. This will overwrite any
|
||||||
|
comment that currently exists. Returns the number of characters
|
||||||
|
written. */
|
||||||
|
int write_comment_sgf (char *string);
|
||||||
|
|
||||||
|
/* Move forward or back in the SGF tree, following any chosen variations
|
||||||
|
(variations are "chosen" every time you go through one) These will
|
||||||
|
update the board showing any moves/added or removed stones/ marks/etc.
|
||||||
|
. */
|
||||||
|
bool undo_node_sgf (void);
|
||||||
|
bool redo_node_sgf (void);
|
||||||
|
|
||||||
|
/* Returns true if the SGF property type is handled in some way. For
|
||||||
|
* real SGF properties (in other words, ones that can actually be read from
|
||||||
|
* a file, not psuedo-properties), if they are unhandled that just means that
|
||||||
|
* we copy them verbatim from the old file to the new, keeping them with the
|
||||||
|
* correct node
|
||||||
|
*/
|
||||||
|
bool is_handled_sgf (enum prop_type_t type);
|
||||||
|
|
||||||
|
/* Sets up the handicap on the board (based on header.handicap) */
|
||||||
|
void setup_handicap_sgf (void);
|
||||||
|
|
||||||
|
/* Goes to the start of a handicap game. */
|
||||||
|
void goto_handicap_start_sgf (void);
|
||||||
|
|
||||||
|
/* Must be called after setting up a new game, either blank or loaded from
|
||||||
|
a file. (Picks a place to put the header properties if none was found,
|
||||||
|
and may do other stuff) */
|
||||||
|
bool post_game_setup_sgf (void);
|
||||||
|
|
||||||
|
/* Get the child that matches the given move.
|
||||||
|
*
|
||||||
|
* Returns the variation number of the matching move, or negative if
|
||||||
|
* none is found.
|
||||||
|
*/
|
||||||
|
int get_matching_child_sgf (unsigned short pos, unsigned char color);
|
||||||
|
|
||||||
|
#define NO_NODE (-1)
|
||||||
|
#define NO_PROP (-1)
|
||||||
|
|
||||||
|
/* These flags are used in undo handling for moves and added/removed
|
||||||
|
stones */
|
||||||
|
#define FLAG_ORIG_EMPTY ((uint8_t) (1 << 7))
|
||||||
|
#define FLAG_ORIG_BLACK ((uint8_t) (1 << 6))
|
||||||
|
#define FLAG_KO_THREAT ((uint8_t) (1 << 5))
|
||||||
|
#define FLAG_SELF_CAP ((uint8_t) (1 << 4))
|
||||||
|
#define FLAG_W_CAP ((uint8_t) (1 << 3))
|
||||||
|
#define FLAG_E_CAP ((uint8_t) (1 << 2))
|
||||||
|
#define FLAG_S_CAP ((uint8_t) (1 << 1))
|
||||||
|
#define FLAG_N_CAP ((uint8_t) (1))
|
||||||
|
|
||||||
|
#define MIN_STORAGE_BUFFER_SIZE 200
|
||||||
|
#define UNHANDLED_PROP_LIST_FILE (PLUGIN_GAMES_DIR "/gbn_misc.bin")
|
||||||
|
|
||||||
|
/* Handle of the current node, the start of the game, and the root
|
||||||
|
* of the tree
|
||||||
|
*/
|
||||||
|
extern int current_node;
|
||||||
|
extern int tree_head;
|
||||||
|
extern int start_node;
|
||||||
|
|
||||||
|
extern int sgf_fd;
|
||||||
|
extern int unhandled_fd;
|
||||||
|
|
||||||
|
/* true if the header location has already been marked in the current
|
||||||
|
game, false otherwise */
|
||||||
|
extern bool header_marked;
|
||||||
|
|
||||||
|
#endif
|
433
apps/plugins/goban/sgf_output.c
Normal file
433
apps/plugins/goban/sgf_output.c
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 "goban.h"
|
||||||
|
#include "sgf_output.h"
|
||||||
|
#include "sgf.h"
|
||||||
|
#include "sgf_storage.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "game.h"
|
||||||
|
|
||||||
|
static void pos_to_sgf (unsigned short pos, char *buffer);
|
||||||
|
|
||||||
|
static void output_prop (int prop_handle);
|
||||||
|
static void output_all_props (void);
|
||||||
|
static bool output_current_node (void);
|
||||||
|
static void output_gametree (void);
|
||||||
|
static void output_header_props (void);
|
||||||
|
static bool output_header_helper (enum prop_type_t type);
|
||||||
|
static int stupid_num_variations (void);
|
||||||
|
|
||||||
|
bool
|
||||||
|
output_sgf (const char *filename)
|
||||||
|
{
|
||||||
|
int current = -1;
|
||||||
|
union prop_data_t temp_data;
|
||||||
|
int saved = current_node;
|
||||||
|
|
||||||
|
sgf_fd = create_or_open_file (filename);
|
||||||
|
|
||||||
|
if (sgf_fd < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF ("outputting to: %s (%d)\n", filename, sgf_fd);
|
||||||
|
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
rb->lseek (sgf_fd, 0, SEEK_SET);
|
||||||
|
rb->ftruncate (sgf_fd, 0);
|
||||||
|
|
||||||
|
if (sgf_fd < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree_head < 0)
|
||||||
|
{
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
push_int_stack (&parse_stack, tree_head);
|
||||||
|
|
||||||
|
while (pop_int_stack (&parse_stack, ¤t))
|
||||||
|
{
|
||||||
|
int var_to_process = 0;
|
||||||
|
int temp_prop =
|
||||||
|
get_prop_sgf (current, PROP_VARIATION_TO_PROCESS, NULL);
|
||||||
|
|
||||||
|
if (temp_prop >= 0)
|
||||||
|
{
|
||||||
|
var_to_process = get_prop (temp_prop)->data.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node = current;
|
||||||
|
|
||||||
|
if (var_to_process > 0)
|
||||||
|
{
|
||||||
|
write_char (sgf_fd, ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_to_process == stupid_num_variations ())
|
||||||
|
{
|
||||||
|
delete_prop_sgf (current, PROP_VARIATION_TO_PROCESS);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_char (sgf_fd, '\n');
|
||||||
|
write_char (sgf_fd, '(');
|
||||||
|
|
||||||
|
/* we need to do more processing on this branchpoint, either
|
||||||
|
to do more variations or to output the ')' */
|
||||||
|
push_int_stack (&parse_stack, current);
|
||||||
|
|
||||||
|
/* increment the stored variation to process */
|
||||||
|
temp_data.number = var_to_process + 1;
|
||||||
|
add_or_set_prop_sgf (current,
|
||||||
|
PROP_VARIATION_TO_PROCESS, temp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->yield ();
|
||||||
|
|
||||||
|
/* now we did the setup for sibling varaitions to be processed so
|
||||||
|
do the actual outputting of a game tree branch */
|
||||||
|
|
||||||
|
go_to_variation_sgf (var_to_process);
|
||||||
|
output_gametree ();
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node = saved;
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
DEBUGF ("done outputting, file closed\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
output_header_props (void)
|
||||||
|
{
|
||||||
|
char buffer[128];
|
||||||
|
|
||||||
|
rb->strncpy (buffer, "GM[1]FF[4]CA[UTF-8]AP[Rockbox Goban:1.0]ST[2]\n\n",
|
||||||
|
sizeof (buffer));
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
|
||||||
|
/* board size */
|
||||||
|
if (board_width != board_height)
|
||||||
|
{
|
||||||
|
rb->snprintf (buffer, sizeof (buffer), "%s[%d:%d]",
|
||||||
|
prop_names[PROP_SIZE], board_width, board_height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rb->snprintf (buffer, sizeof (buffer), "%s[%d]",
|
||||||
|
prop_names[PROP_SIZE], board_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
|
||||||
|
rb->snprintf (buffer, sizeof (buffer), "%s[", prop_names[PROP_KOMI]);
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
|
||||||
|
snprint_fixed (buffer, sizeof (buffer), header.komi);
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
|
||||||
|
write_char (sgf_fd, ']');
|
||||||
|
|
||||||
|
output_header_helper (PROP_RULESET);
|
||||||
|
output_header_helper (PROP_RESULT);
|
||||||
|
|
||||||
|
output_header_helper (PROP_BLACK_NAME);
|
||||||
|
output_header_helper (PROP_WHITE_NAME);
|
||||||
|
output_header_helper (PROP_BLACK_RANK);
|
||||||
|
output_header_helper (PROP_WHITE_RANK);
|
||||||
|
output_header_helper (PROP_BLACK_TEAM);
|
||||||
|
output_header_helper (PROP_WHITE_TEAM);
|
||||||
|
|
||||||
|
output_header_helper (PROP_EVENT);
|
||||||
|
output_header_helper (PROP_PLACE);
|
||||||
|
output_header_helper (PROP_DATE);
|
||||||
|
|
||||||
|
if (output_header_helper (PROP_OVERTIME) || header.time_limit != 0)
|
||||||
|
{
|
||||||
|
rb->snprintf (buffer, sizeof (buffer), "%s[%d]",
|
||||||
|
prop_names[PROP_TIME_LIMIT], header.time_limit);
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
write_char (sgf_fd, '\n');
|
||||||
|
write_char (sgf_fd, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
output_header_helper (enum prop_type_t type)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
int size;
|
||||||
|
char temp_buffer[16];
|
||||||
|
|
||||||
|
if (!get_header_string_and_size (&header, type, &buffer, &size))
|
||||||
|
{
|
||||||
|
DEBUGF ("output_header_helper called with invalid prop type!!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rb->strlen (buffer))
|
||||||
|
{
|
||||||
|
rb->snprintf (temp_buffer, sizeof (temp_buffer), "%s[",
|
||||||
|
prop_names[type]);
|
||||||
|
|
||||||
|
write_file (sgf_fd, temp_buffer, rb->strlen (temp_buffer));
|
||||||
|
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
|
||||||
|
rb->strcpy (temp_buffer, "]");
|
||||||
|
|
||||||
|
write_file (sgf_fd, temp_buffer, rb->strlen (temp_buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool first_node_in_tree = true;
|
||||||
|
static void
|
||||||
|
output_gametree (void)
|
||||||
|
{
|
||||||
|
first_node_in_tree = true;
|
||||||
|
|
||||||
|
while (output_current_node ())
|
||||||
|
{
|
||||||
|
current_node = get_node (current_node)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
output_current_node (void)
|
||||||
|
{
|
||||||
|
if (current_node < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stupid_num_variations () > 1 &&
|
||||||
|
get_prop_sgf (current_node, PROP_VARIATION_TO_PROCESS, NULL) < 0)
|
||||||
|
{
|
||||||
|
/* push it up for the gametree stuff to take care of it and fail
|
||||||
|
out, stopping the node printing */
|
||||||
|
push_int_stack (&parse_stack, current_node);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_node_in_tree)
|
||||||
|
{
|
||||||
|
first_node_in_tree = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_char (sgf_fd, '\n');
|
||||||
|
}
|
||||||
|
write_char (sgf_fd, ';');
|
||||||
|
|
||||||
|
output_all_props ();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum prop_type_t last_output_type = PROP_INVALID;
|
||||||
|
static void
|
||||||
|
output_all_props (void)
|
||||||
|
{
|
||||||
|
int temp_handle = get_node (current_node)->props;
|
||||||
|
|
||||||
|
last_output_type = PROP_INVALID;
|
||||||
|
|
||||||
|
while (temp_handle >= 0)
|
||||||
|
{
|
||||||
|
output_prop (temp_handle);
|
||||||
|
temp_handle = get_prop (temp_handle)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
output_prop (int prop_handle)
|
||||||
|
{
|
||||||
|
char buffer[16];
|
||||||
|
enum prop_type_t temp_type = get_prop (prop_handle)->type;
|
||||||
|
|
||||||
|
buffer[0] = 't';
|
||||||
|
buffer[1] = 't';
|
||||||
|
|
||||||
|
if (is_handled_sgf (temp_type) && temp_type != PROP_COMMENT)
|
||||||
|
{
|
||||||
|
if (temp_type != last_output_type)
|
||||||
|
{
|
||||||
|
write_file (sgf_fd, prop_names[temp_type],
|
||||||
|
PROP_NAME_LEN (temp_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
write_char (sgf_fd, '[');
|
||||||
|
|
||||||
|
if (temp_type == PROP_HANDICAP)
|
||||||
|
{
|
||||||
|
rb->snprintf (buffer, sizeof (buffer), "%d",
|
||||||
|
get_prop (prop_handle)->data.number);
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
}
|
||||||
|
else if (temp_type == PROP_LABEL)
|
||||||
|
{
|
||||||
|
pos_to_sgf (get_prop (prop_handle)->data.position, buffer);
|
||||||
|
buffer[2] = '\0';
|
||||||
|
|
||||||
|
rb->snprintf (&buffer[2], sizeof (buffer) - 2, ":%c",
|
||||||
|
get_prop (prop_handle)->data.label_extra);
|
||||||
|
|
||||||
|
write_file (sgf_fd, buffer, rb->strlen (buffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos_to_sgf (get_prop (prop_handle)->data.position, buffer);
|
||||||
|
|
||||||
|
write_file (sgf_fd, buffer, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_char (sgf_fd, ']');
|
||||||
|
}
|
||||||
|
else if (temp_type == PROP_ROOT_PROPS)
|
||||||
|
{
|
||||||
|
output_header_props ();
|
||||||
|
}
|
||||||
|
else if (temp_type == PROP_GENERIC_UNHANDLED || temp_type == PROP_COMMENT)
|
||||||
|
{
|
||||||
|
bool escaped = false;
|
||||||
|
bool in_prop_value = false;
|
||||||
|
int temp;
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
rb->lseek (unhandled_fd, get_prop (prop_handle)->data.number,
|
||||||
|
SEEK_SET);
|
||||||
|
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
temp = peek_char (unhandled_fd);
|
||||||
|
|
||||||
|
switch (temp)
|
||||||
|
{
|
||||||
|
case ';':
|
||||||
|
escaped = false;
|
||||||
|
if (in_prop_value)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* otherwise, fall through */
|
||||||
|
case -1:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
escaped = !escaped;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
escaped = false;
|
||||||
|
in_prop_value = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ']':
|
||||||
|
if (!escaped)
|
||||||
|
{
|
||||||
|
in_prop_value = false;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
escaped = false;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!done)
|
||||||
|
{
|
||||||
|
write_char (sgf_fd, temp);
|
||||||
|
read_char (unhandled_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_output_type = temp_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pos_to_sgf (unsigned short pos, char *buffer)
|
||||||
|
{
|
||||||
|
if (pos == PASS_POS)
|
||||||
|
{
|
||||||
|
/* "tt" is a pass per SGF specification */
|
||||||
|
buffer[0] = buffer[1] = 't';
|
||||||
|
}
|
||||||
|
else if (pos != INVALID_POS)
|
||||||
|
{
|
||||||
|
buffer[0] = 'a' + I (pos);
|
||||||
|
buffer[1] = 'a' + J (pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid pos converted to SGF\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stupid_num_variations (void)
|
||||||
|
{
|
||||||
|
int result = 1;
|
||||||
|
struct prop_t *temp_prop;
|
||||||
|
struct node_t *temp_node = get_node (current_node);
|
||||||
|
|
||||||
|
if (temp_node == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_prop = get_prop (temp_node->props);
|
||||||
|
|
||||||
|
while (temp_prop)
|
||||||
|
{
|
||||||
|
if (temp_prop->type == PROP_VARIATION)
|
||||||
|
{
|
||||||
|
++result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// variations are at the beginning of the prop list
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_prop = get_prop (temp_prop->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
30
apps/plugins/goban/sgf_output.h
Normal file
30
apps/plugins/goban/sgf_output.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_SGF_OUTPUT_H
|
||||||
|
#define GOBAN_SGF_OUTPUT_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Do the actual outputting of an SGF file. Return false on failure */
|
||||||
|
bool output_sgf (const char *filename);
|
||||||
|
|
||||||
|
#endif
|
857
apps/plugins/goban/sgf_parse.c
Normal file
857
apps/plugins/goban/sgf_parse.c
Normal file
|
@ -0,0 +1,857 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 "goban.h"
|
||||||
|
#include "sgf_parse.h"
|
||||||
|
#include "sgf.h"
|
||||||
|
#include "sgf_storage.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "game.h"
|
||||||
|
|
||||||
|
static void handle_prop_value (enum prop_type_t type);
|
||||||
|
static int read_prop_value (char *buffer, size_t buffer_size);
|
||||||
|
static void do_range (enum prop_type_t type, unsigned short ul,
|
||||||
|
unsigned short br);
|
||||||
|
static void parse_prop (void);
|
||||||
|
static void parse_node (void);
|
||||||
|
static enum prop_type_t parse_prop_type (void);
|
||||||
|
|
||||||
|
static unsigned short sgf_to_pos (char *buffer);
|
||||||
|
|
||||||
|
bool
|
||||||
|
parse_sgf (const char *filename)
|
||||||
|
{
|
||||||
|
int saved = current_node;
|
||||||
|
|
||||||
|
/* for parsing */
|
||||||
|
int first_handle = 0; /* first node in the branch */
|
||||||
|
int file_position = 0;
|
||||||
|
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
|
||||||
|
sgf_fd = rb->open (filename, O_RDONLY);
|
||||||
|
|
||||||
|
if (sgf_fd < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node = start_node;
|
||||||
|
|
||||||
|
if (current_node < 0)
|
||||||
|
{
|
||||||
|
current_node = saved;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
/* seek to the first '(' */
|
||||||
|
while (peek_char_no_whitespace (sgf_fd) != '(')
|
||||||
|
{
|
||||||
|
if (read_char_no_whitespace (sgf_fd) == -1)
|
||||||
|
{
|
||||||
|
DEBUGF ("end of file or error before we found a '('\n");
|
||||||
|
current_node = saved;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR));
|
||||||
|
push_int_stack (&parse_stack, current_node);
|
||||||
|
|
||||||
|
while (pop_int_stack (&parse_stack, &first_handle) &&
|
||||||
|
pop_int_stack (&parse_stack, &file_position))
|
||||||
|
{
|
||||||
|
/* DEBUGF("poped off %d\n", file_position); */
|
||||||
|
|
||||||
|
rb->yield ();
|
||||||
|
|
||||||
|
current_node = first_handle;
|
||||||
|
|
||||||
|
if (file_position == -1)
|
||||||
|
{
|
||||||
|
temp = read_char_no_whitespace (sgf_fd);
|
||||||
|
if (temp != '(')
|
||||||
|
{
|
||||||
|
/* we're here because there may have been a sibling after
|
||||||
|
another gametree that was handled, but there's no '(',
|
||||||
|
so there wasnt' a sibling, so just go on to any more
|
||||||
|
gametrees in the stack */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* there may be more siblings after we process this one */
|
||||||
|
push_int_stack (&parse_stack, -1);
|
||||||
|
push_int_stack (&parse_stack, first_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* check for a sibling after we finish with this node */
|
||||||
|
push_int_stack (&parse_stack, -1);
|
||||||
|
push_int_stack (&parse_stack, first_handle);
|
||||||
|
|
||||||
|
rb->lseek (sgf_fd, file_position, SEEK_SET);
|
||||||
|
|
||||||
|
|
||||||
|
/* we're at the start of a gametree here, right at the '(' */
|
||||||
|
temp = read_char_no_whitespace (sgf_fd);
|
||||||
|
|
||||||
|
if (temp != '(')
|
||||||
|
{
|
||||||
|
DEBUGF ("start of gametree doesn't have a '('!\n");
|
||||||
|
current_node = saved;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
temp = peek_char_no_whitespace (sgf_fd);
|
||||||
|
/* DEBUGF("||| %d, %c\n", absolute_position(), (char) temp); */
|
||||||
|
|
||||||
|
if (temp == ';')
|
||||||
|
{
|
||||||
|
/* fill the tree_head node before moving on */
|
||||||
|
if (current_node != tree_head ||
|
||||||
|
get_node (current_node)->props >= 0)
|
||||||
|
{
|
||||||
|
int temp = add_child_sgf (NULL);
|
||||||
|
|
||||||
|
if (temp >= 0)
|
||||||
|
{
|
||||||
|
current_node = temp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rb->splash (2 * HZ, "Out of memory while parsing!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
read_char_no_whitespace (sgf_fd);
|
||||||
|
parse_node ();
|
||||||
|
}
|
||||||
|
else if (temp == ')')
|
||||||
|
{
|
||||||
|
/* finished this gametree */
|
||||||
|
|
||||||
|
/* we want to end one past the ')', so eat it up: */
|
||||||
|
read_char_no_whitespace (sgf_fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (temp == '(')
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
DEBUGF ("adding %d\n", (int) rb->lseek (sgf_fd, 0,
|
||||||
|
SEEK_CUR)); */
|
||||||
|
push_int_stack (&parse_stack, rb->lseek (sgf_fd, 0, SEEK_CUR));
|
||||||
|
push_int_stack (&parse_stack, current_node);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (temp == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("extra characters found while parsing: %c\n", temp);
|
||||||
|
/* skip the extras i guess */
|
||||||
|
read_char_no_whitespace (sgf_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node = get_node (tree_head)->next;
|
||||||
|
while (current_node >= 0 && get_node (current_node)->props < 0)
|
||||||
|
{
|
||||||
|
temp = current_node; /* to be freed later */
|
||||||
|
|
||||||
|
/* update the ->prev pointed on all branches of the next node */
|
||||||
|
current_node = get_node (current_node)->next;
|
||||||
|
/* DEBUGF("trying to set prev for branch %d\n", current_node); */
|
||||||
|
if (current_node >= 0)
|
||||||
|
{
|
||||||
|
get_node (current_node)->prev = tree_head;
|
||||||
|
|
||||||
|
struct prop_t *loop_prop =
|
||||||
|
get_prop (get_node (current_node)->props);
|
||||||
|
|
||||||
|
while (loop_prop != 0)
|
||||||
|
{
|
||||||
|
if (loop_prop->type == PROP_VARIATION)
|
||||||
|
{
|
||||||
|
get_node (loop_prop->data.number)->prev = tree_head;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* all of the variations have to be up front, so we
|
||||||
|
can quit here */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loop_prop = get_prop (loop_prop->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the tree head */
|
||||||
|
get_node (tree_head)->next = get_node (temp)->next;
|
||||||
|
/* DEBUGF("freeing %d %d %d\n", temp, start_node, saved); */
|
||||||
|
if (start_node == temp || saved == temp)
|
||||||
|
{
|
||||||
|
start_node = saved = tree_head;
|
||||||
|
}
|
||||||
|
free_storage_sgf (temp);
|
||||||
|
|
||||||
|
current_node = get_node (tree_head)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_node = saved;
|
||||||
|
|
||||||
|
|
||||||
|
/* DEBUGF("got past!\n"); */
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_node (void)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
temp = peek_char_no_whitespace (sgf_fd);
|
||||||
|
|
||||||
|
if (temp == -1 || temp == ')' || temp == '(' || temp == ';')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parse_prop ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int start_of_prop = 0;
|
||||||
|
static void
|
||||||
|
parse_prop (void)
|
||||||
|
{
|
||||||
|
enum prop_type_t temp_type = PROP_INVALID;
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
temp = peek_char_no_whitespace (sgf_fd);
|
||||||
|
|
||||||
|
if (temp == -1 || temp == ')' || temp == '(' || temp == ';')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (temp == '[')
|
||||||
|
{
|
||||||
|
handle_prop_value (temp_type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start_of_prop = rb->lseek (sgf_fd, 0, SEEK_CUR);
|
||||||
|
temp_type = parse_prop_type ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static enum prop_type_t
|
||||||
|
parse_prop_type (void)
|
||||||
|
{
|
||||||
|
char buffer[3];
|
||||||
|
int pos = 0;
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
rb->memset (buffer, 0, sizeof (buffer));
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
temp = peek_char_no_whitespace (sgf_fd);
|
||||||
|
|
||||||
|
if (temp == ';' || temp == '[' || temp == '(' ||
|
||||||
|
temp == -1 || temp == ')')
|
||||||
|
{
|
||||||
|
if (pos == 1 || pos == 2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PROP_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (temp >= 'A' && temp <= 'Z')
|
||||||
|
{
|
||||||
|
buffer[pos++] = temp;
|
||||||
|
|
||||||
|
if (pos == 2)
|
||||||
|
{
|
||||||
|
read_char_no_whitespace (sgf_fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = read_char_no_whitespace (sgf_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if we're still reading a prop name, in which case we fail
|
||||||
|
(but first we want to eat up the rest of the prop name) */
|
||||||
|
bool failed = false;
|
||||||
|
while (peek_char_no_whitespace (sgf_fd) != ';' &&
|
||||||
|
peek_char_no_whitespace (sgf_fd) != '[' &&
|
||||||
|
peek_char_no_whitespace (sgf_fd) != '(' &&
|
||||||
|
peek_char_no_whitespace (sgf_fd) != '}' &&
|
||||||
|
peek_char_no_whitespace (sgf_fd) != -1)
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
read_char_no_whitespace (sgf_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed)
|
||||||
|
{
|
||||||
|
return PROP_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < PROP_NAMES_SIZE; ++i)
|
||||||
|
{
|
||||||
|
if (rb->strcmp (buffer, prop_names[i]) == 0)
|
||||||
|
{
|
||||||
|
return (enum prop_type_t) i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PROP_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
read_prop_value (char *buffer, size_t buffer_size)
|
||||||
|
{
|
||||||
|
bool escaped = false;
|
||||||
|
int temp;
|
||||||
|
int bytes_read = 0;
|
||||||
|
|
||||||
|
/* make it a string, the lazy way */
|
||||||
|
rb->memset (buffer, 0, buffer_size);
|
||||||
|
--buffer_size;
|
||||||
|
|
||||||
|
if (peek_char (sgf_fd) == '[')
|
||||||
|
{
|
||||||
|
read_char (sgf_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
temp = read_char (sgf_fd);
|
||||||
|
if (temp == ']' && !escaped)
|
||||||
|
{
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
else if (temp == '\\')
|
||||||
|
{
|
||||||
|
if (escaped)
|
||||||
|
{
|
||||||
|
if (buffer && buffer_size)
|
||||||
|
{
|
||||||
|
*(buffer++) = temp;
|
||||||
|
++bytes_read;
|
||||||
|
--buffer_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
escaped = !escaped;
|
||||||
|
}
|
||||||
|
else if (temp == -1)
|
||||||
|
{
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
escaped = false;
|
||||||
|
if (buffer && buffer_size)
|
||||||
|
{
|
||||||
|
*(buffer++) = temp;
|
||||||
|
++bytes_read;
|
||||||
|
--buffer_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_prop_value (enum prop_type_t type)
|
||||||
|
{
|
||||||
|
/* max size of generically supported prop values is 6, which is 5 for
|
||||||
|
a point range ab:cd and one for the \0
|
||||||
|
|
||||||
|
(this buffer is only used for them, things such as white and black
|
||||||
|
player names are stored in different buffers) */
|
||||||
|
|
||||||
|
/* make it a little bigger for other random crap, like reading in time
|
||||||
|
*/
|
||||||
|
#define PROP_HANDLER_BUFFER_SIZE 16
|
||||||
|
|
||||||
|
char real_buffer[PROP_HANDLER_BUFFER_SIZE];
|
||||||
|
char *buffer = real_buffer;
|
||||||
|
|
||||||
|
int temp;
|
||||||
|
union prop_data_t temp_data;
|
||||||
|
bool in_prop_value = false;
|
||||||
|
bool escaped = false;
|
||||||
|
bool done = false;
|
||||||
|
int temp_width, temp_height;
|
||||||
|
unsigned short temp_pos_ul, temp_pos_br;
|
||||||
|
int temp_size;
|
||||||
|
char *temp_buffer;
|
||||||
|
bool got_value;
|
||||||
|
|
||||||
|
/* special extra handling for root properties, set a marker telling us
|
||||||
|
the right place to spit the values out in output_sgf */
|
||||||
|
if (type == PROP_GAME ||
|
||||||
|
type == PROP_APPLICATION ||
|
||||||
|
type == PROP_CHARSET ||
|
||||||
|
type == PROP_SIZE ||
|
||||||
|
type == PROP_FILE_FORMAT || type == PROP_VARIATION_TYPE)
|
||||||
|
{
|
||||||
|
header_marked = true;
|
||||||
|
|
||||||
|
temp_data.number = 0; /* meaningless */
|
||||||
|
|
||||||
|
/* don't add more than one, so just set it if we found one already
|
||||||
|
*/
|
||||||
|
add_or_set_prop_sgf (current_node, PROP_ROOT_PROPS, temp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!is_handled_sgf (type) || type == PROP_COMMENT)
|
||||||
|
{
|
||||||
|
/* DEBUGF("unhandled prop %d\n", (int) type); */
|
||||||
|
rb->lseek (sgf_fd, start_of_prop, SEEK_SET);
|
||||||
|
|
||||||
|
temp_data.number = rb->lseek (unhandled_fd, 0, SEEK_CUR);
|
||||||
|
/* absolute_position(&unhandled_prop_list); */
|
||||||
|
|
||||||
|
add_prop_sgf (current_node,
|
||||||
|
type == PROP_COMMENT ? PROP_COMMENT :
|
||||||
|
PROP_GENERIC_UNHANDLED, temp_data);
|
||||||
|
|
||||||
|
got_value = false;
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
temp = peek_char (sgf_fd);
|
||||||
|
|
||||||
|
switch (temp)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
if (got_value && !in_prop_value)
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
escaped = !escaped;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
escaped = false;
|
||||||
|
in_prop_value = true;
|
||||||
|
got_value = true;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
if (!escaped)
|
||||||
|
{
|
||||||
|
in_prop_value = false;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
case '(':
|
||||||
|
case ';':
|
||||||
|
if (!in_prop_value)
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (got_value && !in_prop_value)
|
||||||
|
{
|
||||||
|
if (!is_whitespace (temp))
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (done)
|
||||||
|
{
|
||||||
|
write_char (unhandled_fd, ';');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* don't write out-of-prop whitespace */
|
||||||
|
if (in_prop_value || !is_whitespace (temp))
|
||||||
|
{
|
||||||
|
write_char (unhandled_fd, (char) temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_char (sgf_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == PROP_BLACK_MOVE || type == PROP_WHITE_MOVE)
|
||||||
|
{
|
||||||
|
/* DEBUGF("move prop %d\n", (int) type); */
|
||||||
|
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
|
||||||
|
temp_data.position = INVALID_POS;
|
||||||
|
|
||||||
|
/* empty is apparently acceptable as a pass */
|
||||||
|
if (temp == 0)
|
||||||
|
{
|
||||||
|
temp_data.position = PASS_POS;
|
||||||
|
}
|
||||||
|
else if (temp == 2)
|
||||||
|
{
|
||||||
|
temp_data.position = sgf_to_pos (buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid move position read in, of wrong size!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (temp_data.position != INVALID_POS)
|
||||||
|
{
|
||||||
|
add_prop_sgf (current_node, type, temp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == PROP_ADD_BLACK ||
|
||||||
|
type == PROP_ADD_WHITE ||
|
||||||
|
type == PROP_ADD_EMPTY ||
|
||||||
|
type == PROP_CIRCLE ||
|
||||||
|
type == PROP_SQUARE ||
|
||||||
|
type == PROP_TRIANGLE ||
|
||||||
|
type == PROP_DIM || type == PROP_MARK || type == PROP_SELECTED)
|
||||||
|
{
|
||||||
|
/* DEBUGF("add prop %d\n", (int) type); */
|
||||||
|
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
if (temp == 2)
|
||||||
|
{
|
||||||
|
temp_data.position = sgf_to_pos (buffer);
|
||||||
|
|
||||||
|
if (temp_data.position != INVALID_POS &&
|
||||||
|
temp_data.position != PASS_POS)
|
||||||
|
{
|
||||||
|
add_prop_sgf (current_node, type, temp_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (temp == 5)
|
||||||
|
{
|
||||||
|
/* example: "ab:cd", two positions separated by a colon */
|
||||||
|
temp_pos_ul = sgf_to_pos (buffer);
|
||||||
|
temp_pos_br = sgf_to_pos (&(buffer[3]));
|
||||||
|
|
||||||
|
if (!on_board (temp_pos_ul) || !on_board (temp_pos_br) ||
|
||||||
|
buffer[2] != ':')
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid range value!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
do_range (type, temp_pos_ul, temp_pos_br);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid position or range read in. wrong size!\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == PROP_LABEL)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (temp < 4 || buffer[2] != ':')
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid LaBel property '%s'", buffer);
|
||||||
|
}
|
||||||
|
temp_data.position = sgf_to_pos (buffer);
|
||||||
|
|
||||||
|
if (!on_board (temp_data.position))
|
||||||
|
{
|
||||||
|
DEBUGF ("LaBel set on invalid position!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_data.label_extra = buffer[3];
|
||||||
|
|
||||||
|
add_prop_sgf (current_node, type, temp_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (type == PROP_GAME)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
if (temp != 1 || buffer[0] != '1')
|
||||||
|
{
|
||||||
|
rb->splash (2 * HZ, "This isn't a Go SGF!! Parsing stopped.");
|
||||||
|
DEBUGF ("incorrect game type loaded!\n");
|
||||||
|
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PROP_FILE_FORMAT)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
if (temp != 1 || (buffer[0] != '3' && buffer[0] != '4'))
|
||||||
|
{
|
||||||
|
rb->splash (2 * HZ, "Wrong SGF file version! Parsing stopped.");
|
||||||
|
DEBUGF ("can't handle file format %c\n", buffer[0]);
|
||||||
|
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PROP_APPLICATION ||
|
||||||
|
type == PROP_CHARSET || type == PROP_VARIATION_TYPE)
|
||||||
|
{
|
||||||
|
/* we don't care. on output we'll write our own values for these */
|
||||||
|
read_prop_value (NULL, 0);
|
||||||
|
}
|
||||||
|
else if (type == PROP_SIZE)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
if (temp == 0)
|
||||||
|
{
|
||||||
|
rb->splash (HZ, "Invalid board size specified in file.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_width = rb->atoi (buffer);
|
||||||
|
while (*buffer != ':' && *buffer != '\0')
|
||||||
|
{
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*buffer != '\0')
|
||||||
|
{
|
||||||
|
++buffer;
|
||||||
|
temp_height = rb->atoi (buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_height = temp_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!set_size_board (temp_width, temp_height))
|
||||||
|
{
|
||||||
|
rb->splashf (HZ,
|
||||||
|
"Board too big/small! (%dx%d) Stopping parse.",
|
||||||
|
temp_width, temp_height);
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear_board ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PROP_KOMI)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (temp == 0)
|
||||||
|
{
|
||||||
|
header.komi = 0;
|
||||||
|
DEBUGF ("invalid komi specification. setting to zero\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
header.komi = rb->atoi (buffer) << 1;
|
||||||
|
while (*buffer != '.' && *buffer != ',' && *buffer != '\0')
|
||||||
|
{
|
||||||
|
++buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer != '\0')
|
||||||
|
{
|
||||||
|
++buffer;
|
||||||
|
|
||||||
|
if (*buffer == 0)
|
||||||
|
{
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
else if (*buffer >= '1' && *buffer <= '9')
|
||||||
|
{
|
||||||
|
header.komi += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*buffer != '0')
|
||||||
|
{
|
||||||
|
DEBUGF ("extra characters after komi value!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PROP_BLACK_NAME ||
|
||||||
|
type == PROP_WHITE_NAME ||
|
||||||
|
type == PROP_BLACK_RANK ||
|
||||||
|
type == PROP_WHITE_RANK ||
|
||||||
|
type == PROP_BLACK_TEAM ||
|
||||||
|
type == PROP_WHITE_TEAM ||
|
||||||
|
type == PROP_DATE ||
|
||||||
|
type == PROP_ROUND ||
|
||||||
|
type == PROP_EVENT ||
|
||||||
|
type == PROP_PLACE ||
|
||||||
|
type == PROP_OVERTIME ||
|
||||||
|
type == PROP_RESULT || type == PROP_RULESET)
|
||||||
|
{
|
||||||
|
if (!get_header_string_and_size
|
||||||
|
(&header, type, &temp_buffer, &temp_size))
|
||||||
|
{
|
||||||
|
rb->splash (5 * HZ,
|
||||||
|
"Error getting header string. Report this.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp = read_prop_value (temp_buffer, temp_size - 1);
|
||||||
|
#if 0
|
||||||
|
DEBUGF ("read %d bytes into header for type: %d\n", temp, type);
|
||||||
|
DEBUGF ("data: %s\n", temp_buffer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PROP_TIME_LIMIT)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
header.time_limit = rb->atoi (buffer);
|
||||||
|
DEBUGF ("setting time: %d (%s)\n", header.time_limit, buffer);
|
||||||
|
}
|
||||||
|
else if (type == PROP_HANDICAP)
|
||||||
|
{
|
||||||
|
temp = read_prop_value (buffer, PROP_HANDLER_BUFFER_SIZE);
|
||||||
|
if (start_node == tree_head)
|
||||||
|
{
|
||||||
|
if (rb->atoi (buffer) >= 2)
|
||||||
|
{
|
||||||
|
start_node = current_node;
|
||||||
|
temp_data.number = header.handicap = rb->atoi (buffer);
|
||||||
|
add_prop_sgf (current_node, type, temp_data);
|
||||||
|
DEBUGF ("setting handicap: %d\n", header.handicap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("invalid HAndicap prop. ignoring\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rb->splash (HZ, "extraneous HAndicap prop present in file!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF ("UNHANDLED PROP TYPE!!!\n");
|
||||||
|
rb->splash (3 * HZ,
|
||||||
|
"A SGF prop was not dealt with. Please report this");
|
||||||
|
read_prop_value (NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* upper-left and bottom right */
|
||||||
|
static void
|
||||||
|
do_range (enum prop_type_t type, unsigned short ul, unsigned short br)
|
||||||
|
{
|
||||||
|
/* this code is overly general and accepts ranges even if ul and br
|
||||||
|
aren't the required corners it's easier doing that that failing if
|
||||||
|
the input is bad */
|
||||||
|
|
||||||
|
bool x_reverse = false;
|
||||||
|
bool y_reverse = false;
|
||||||
|
union prop_data_t temp_data;
|
||||||
|
|
||||||
|
if (I (br) < I (ul))
|
||||||
|
{
|
||||||
|
x_reverse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (J (br) < J (ul))
|
||||||
|
{
|
||||||
|
y_reverse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
for (x = I (ul);
|
||||||
|
x_reverse ? (x >= I (br)) : (x <= I (br)); x_reverse ? --x : ++x)
|
||||||
|
{
|
||||||
|
for (y = J (ul);
|
||||||
|
y_reverse ? (y >= J (br)) : (y <= J (br)); y_reverse ? --y : ++y)
|
||||||
|
{
|
||||||
|
temp_data.position = POS (x, y);
|
||||||
|
|
||||||
|
DEBUGF ("adding %d %d for range (type %d)\n",
|
||||||
|
I (temp_data.position), J (temp_data.position), type);
|
||||||
|
add_prop_sgf (current_node, type, temp_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned short
|
||||||
|
sgf_to_pos (char *buffer)
|
||||||
|
{
|
||||||
|
if (buffer[0] == 't' && buffer[1] == 't')
|
||||||
|
{
|
||||||
|
return PASS_POS;
|
||||||
|
}
|
||||||
|
else if (buffer[0] < 'a' || buffer[0] > 'z' ||
|
||||||
|
buffer[1] < 'a' || buffer[1] > 'z')
|
||||||
|
{
|
||||||
|
return INVALID_POS;
|
||||||
|
}
|
||||||
|
return POS (buffer[0] - 'a', buffer[1] - 'a');
|
||||||
|
}
|
||||||
|
|
30
apps/plugins/goban/sgf_parse.h
Normal file
30
apps/plugins/goban/sgf_parse.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_SGF_PARSE_H
|
||||||
|
#define GOBAN_SGF_PARSE_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Do the actual parsing of an SGF file. Return false on failure */
|
||||||
|
bool parse_sgf (const char *filename);
|
||||||
|
|
||||||
|
#endif
|
493
apps/plugins/goban/sgf_storage.c
Normal file
493
apps/plugins/goban/sgf_storage.c
Normal file
|
@ -0,0 +1,493 @@
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 "goban.h"
|
||||||
|
#include "sgf_storage.h"
|
||||||
|
#include "sgf.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define ALIGNMENT_VAL (sizeof (union storage_t))
|
||||||
|
|
||||||
|
union storage_t *storage_buffer[] = { NULL, NULL };
|
||||||
|
size_t storage_buffer_size[] = { 0, 0 };
|
||||||
|
|
||||||
|
uint8_t *storage_free_list[] = { NULL, NULL };
|
||||||
|
size_t storage_free_list_size[] = { 0, 0 };
|
||||||
|
|
||||||
|
bool storage_initialized[] = { false, false };
|
||||||
|
|
||||||
|
size_t total_storage_size = 0;
|
||||||
|
|
||||||
|
/* the next handle to check */
|
||||||
|
int next_free_handle_buffer;
|
||||||
|
int next_free_handle;
|
||||||
|
|
||||||
|
static bool setup_storage_buffer (char *temp_buffer, size_t size);
|
||||||
|
static void clear_storage_buffer (int index);
|
||||||
|
static bool find_free (int *ret_buffer, int *ret_handle);
|
||||||
|
static bool is_free (int buffer_num, int handle);
|
||||||
|
static void set_free (int buffer_num, int handle, bool free);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static void debugf_current_node (void);
|
||||||
|
|
||||||
|
static void
|
||||||
|
debugf_current_node (void)
|
||||||
|
{
|
||||||
|
int temp_prop = NO_PROP;
|
||||||
|
if (current_node < 0)
|
||||||
|
{
|
||||||
|
DEBUGF ("CURRENT_NODE < 0 ON DEBUGF_CURRENT_NODE!!!!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DEBUGF ("-----------------------------------------\n");
|
||||||
|
DEBUGF ("current_node: %d\n", current_node);
|
||||||
|
DEBUGF ("start_node %d %d\n", start_node, tree_head);
|
||||||
|
DEBUGF ("prev/next: %d/%d\n", get_node (current_node)->prev,
|
||||||
|
get_node (current_node)->next);
|
||||||
|
DEBUGF ("num variations: %d\n", num_variations_sgf ());
|
||||||
|
DEBUGF ("props:\n");
|
||||||
|
if (!get_node (current_node) ||
|
||||||
|
(temp_prop = get_node (current_node)->props) < 0)
|
||||||
|
{
|
||||||
|
DEBUGF ("none\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (temp_prop < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUGF (" handle: %d\n", temp_prop);
|
||||||
|
DEBUGF (" type: %d ", get_prop (temp_prop)->type);
|
||||||
|
if (get_prop (temp_prop)->type < PROP_NAMES_SIZE)
|
||||||
|
{
|
||||||
|
DEBUGF ("(%s)", prop_names[get_prop (temp_prop)->type]);
|
||||||
|
}
|
||||||
|
DEBUGF ("\n");
|
||||||
|
if (get_prop (temp_prop)->type == PROP_BLACK_MOVE ||
|
||||||
|
get_prop (temp_prop)->type == PROP_WHITE_MOVE ||
|
||||||
|
get_prop (temp_prop)->type == PROP_ADD_BLACK ||
|
||||||
|
get_prop (temp_prop)->type == PROP_ADD_WHITE ||
|
||||||
|
get_prop (temp_prop)->type == PROP_ADD_EMPTY)
|
||||||
|
{
|
||||||
|
DEBUGF (" i: %d j: %d\n",
|
||||||
|
I (get_prop (temp_prop)->data.position),
|
||||||
|
J (get_prop (temp_prop)->data.position));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUGF (" data: %d\n", get_prop (temp_prop)->data.number);
|
||||||
|
}
|
||||||
|
DEBUGF (" next: %d\n", get_prop (temp_prop)->next);
|
||||||
|
|
||||||
|
temp_prop = get_prop (temp_prop)->next;
|
||||||
|
if (temp_prop >= 0)
|
||||||
|
{
|
||||||
|
DEBUGF ("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF ("-----------------------------------------\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_storage_buffer (int index)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
|
||||||
|
/* everything starts free */
|
||||||
|
rb->memset (storage_free_list[index],
|
||||||
|
(unsigned char) 0xFF,
|
||||||
|
storage_free_list_size[index]);
|
||||||
|
|
||||||
|
/* if there are extra bits at the end of the free list (because
|
||||||
|
storage_buffer_size is not divisible by 8) then we set them not
|
||||||
|
free, so we won't end up using those ever by accident (shouldn't be
|
||||||
|
possible anyways, but makes calculation easier later) */
|
||||||
|
temp = storage_free_list_size[index] * 8 - storage_buffer_size[index];
|
||||||
|
storage_free_list[index][storage_free_list_size[index] - 1] ^=
|
||||||
|
(1 << temp) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free_tree_sgf (void)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0;
|
||||||
|
i < sizeof (storage_initialized) /
|
||||||
|
sizeof (storage_initialized[0]); ++i)
|
||||||
|
{
|
||||||
|
if (storage_initialized[i])
|
||||||
|
{
|
||||||
|
clear_storage_buffer (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_head = start_node = current_node = alloc_storage_sgf ();
|
||||||
|
|
||||||
|
if (tree_head < 0)
|
||||||
|
{
|
||||||
|
rb->splash (5 * HZ,
|
||||||
|
"Error allocating first node! Please exit immediately.");
|
||||||
|
}
|
||||||
|
|
||||||
|
get_node (tree_head)->props = NO_PROP;
|
||||||
|
get_node (tree_head)->next = NO_NODE;
|
||||||
|
get_node (tree_head)->prev = NO_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
audio_stolen_sgf (void)
|
||||||
|
{
|
||||||
|
return storage_initialized[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
alloc_storage_sgf (void)
|
||||||
|
{
|
||||||
|
int buffer_num;
|
||||||
|
int handle;
|
||||||
|
int temp_buffer;
|
||||||
|
int ret_val;
|
||||||
|
|
||||||
|
char *new_storage_buffer;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (!find_free (&buffer_num, &handle))
|
||||||
|
{
|
||||||
|
if (!storage_initialized[1])
|
||||||
|
{
|
||||||
|
rb->splash (2 * HZ, "Stopping music playback to get more space");
|
||||||
|
DEBUGF ("stealing audio buffer: %d\n", (int) total_storage_size);
|
||||||
|
|
||||||
|
new_storage_buffer = rb->plugin_get_audio_buffer (&size);
|
||||||
|
setup_storage_buffer (new_storage_buffer, size);
|
||||||
|
|
||||||
|
DEBUGF ("after stealing: %d\n", (int) total_storage_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try again */
|
||||||
|
if (!find_free (&buffer_num, &handle))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_free (buffer_num, handle, false);
|
||||||
|
|
||||||
|
temp_buffer = 0;
|
||||||
|
ret_val = handle;
|
||||||
|
|
||||||
|
while (temp_buffer != buffer_num)
|
||||||
|
{
|
||||||
|
if (storage_initialized[temp_buffer])
|
||||||
|
{
|
||||||
|
ret_val += storage_buffer_size[temp_buffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
++temp_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free_storage_sgf (int handle)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (handle < 0 || (unsigned int) handle >= total_storage_size)
|
||||||
|
{
|
||||||
|
DEBUGF ("tried to free an out of bounds handle!!\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
while ((unsigned int) handle >= storage_buffer_size[index])
|
||||||
|
{
|
||||||
|
handle -= storage_buffer_size[index++];
|
||||||
|
}
|
||||||
|
rb->memset (&storage_buffer[index][handle], 0xFF,
|
||||||
|
sizeof (union storage_t));
|
||||||
|
set_free (index, handle, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
find_free (int *ret_buffer, int *ret_handle)
|
||||||
|
{
|
||||||
|
unsigned int handle = next_free_handle;
|
||||||
|
unsigned int buffer_index = next_free_handle_buffer;
|
||||||
|
|
||||||
|
/* so we know where we started, to prevent infinite loop */
|
||||||
|
unsigned int start_handle = handle;
|
||||||
|
unsigned int start_buffer = buffer_index;
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++handle;
|
||||||
|
|
||||||
|
if (handle >= storage_buffer_size[buffer_index])
|
||||||
|
{
|
||||||
|
handle = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++buffer_index;
|
||||||
|
|
||||||
|
if (buffer_index >= sizeof (storage_initialized) /
|
||||||
|
sizeof (storage_initialized[0]))
|
||||||
|
{
|
||||||
|
buffer_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!storage_initialized[buffer_index]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_free (buffer_index, handle))
|
||||||
|
{
|
||||||
|
next_free_handle_buffer = buffer_index;
|
||||||
|
next_free_handle = handle;
|
||||||
|
|
||||||
|
*ret_buffer = buffer_index;
|
||||||
|
*ret_handle = handle;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (handle != start_handle || buffer_index != start_buffer);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_free (int buffer_num, int handle)
|
||||||
|
{
|
||||||
|
return storage_free_list[buffer_num][handle / 8] &
|
||||||
|
(1 << (7 - (handle % 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_free (int buffer_num, int handle, bool free)
|
||||||
|
{
|
||||||
|
if (free)
|
||||||
|
{
|
||||||
|
/* simple, just 'or' the byte with the specific bit switched on */
|
||||||
|
storage_free_list[buffer_num][handle / 8] |= 1 << (7 - (handle % 8));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* start with a byte with all bits turned on and turn off the one
|
||||||
|
we're trying to set to zero. then take that result and 'and'
|
||||||
|
it with the current value */
|
||||||
|
storage_free_list[buffer_num][handle / 8] &=
|
||||||
|
0xFF ^ (1 << (7 - (handle % 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
setup_sgf (void)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
char *temp_buffer;
|
||||||
|
|
||||||
|
temp_buffer = rb->plugin_get_buffer (&size);
|
||||||
|
setup_storage_buffer (temp_buffer, size);
|
||||||
|
|
||||||
|
if (total_storage_size < MIN_STORAGE_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
rb->splash (2 * HZ, "Stopping music playback to get more space");
|
||||||
|
DEBUGF ("storage_buffer_size < MIN!!: %d\n", (int) total_storage_size);
|
||||||
|
|
||||||
|
temp_buffer = rb->plugin_get_audio_buffer (&size);
|
||||||
|
setup_storage_buffer (temp_buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_storage_size < MIN_STORAGE_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
rb->splash (5 * HZ, "Low memory. Large files may not load.");
|
||||||
|
|
||||||
|
DEBUGF ("storage_buffer_size < MIN!!!!: %d\n",
|
||||||
|
(int) total_storage_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGF ("storage_buffer_size: %d\n", (int) total_storage_size);
|
||||||
|
|
||||||
|
|
||||||
|
unhandled_fd = create_or_open_file (UNHANDLED_PROP_LIST_FILE);
|
||||||
|
|
||||||
|
if (unhandled_fd < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->lseek (unhandled_fd, 0, SEEK_SET);
|
||||||
|
rb->ftruncate (unhandled_fd, 0);
|
||||||
|
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
clear_caches_sgf (void)
|
||||||
|
{
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
rb->lseek (unhandled_fd, 0, SEEK_SET);
|
||||||
|
rb->ftruncate (unhandled_fd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cleanup_sgf (void)
|
||||||
|
{
|
||||||
|
empty_stack (&parse_stack);
|
||||||
|
|
||||||
|
rb->lseek (unhandled_fd, 0, SEEK_SET);
|
||||||
|
rb->ftruncate (unhandled_fd, 0);
|
||||||
|
close_file (&unhandled_fd);
|
||||||
|
|
||||||
|
close_file (&sgf_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
setup_storage_buffer (char *temp_buffer, size_t size)
|
||||||
|
{
|
||||||
|
unsigned int index = 0;
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (index >= sizeof (storage_initialized) /
|
||||||
|
sizeof (storage_initialized[0]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!storage_initialized[index])
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_buffer = align_buffer (temp_buffer, &size);
|
||||||
|
if (!temp_buffer || !size)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same as temp = size / (sizeof(union storage_t) + 1/8)
|
||||||
|
|
||||||
|
(we need 1 bit extra for each union storage_t, for the free list) */
|
||||||
|
temp =
|
||||||
|
(8 * (size - ALIGNMENT_VAL - 1)) / (8 * sizeof (union storage_t) + 1);
|
||||||
|
/* the - ALIGNMENT_VAL - 1 is for possible wasted space in alignment
|
||||||
|
and possible extra byte needed in the free list */
|
||||||
|
|
||||||
|
storage_buffer[index] = (void *) temp_buffer;
|
||||||
|
storage_buffer_size[index] = temp;
|
||||||
|
|
||||||
|
storage_free_list_size[index] = storage_buffer_size[index] / 8;
|
||||||
|
if (storage_free_list_size[index] * 8 < storage_buffer_size[index])
|
||||||
|
{
|
||||||
|
++(storage_free_list_size[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_buffer += sizeof (union storage_t) * temp;
|
||||||
|
size -= sizeof (union storage_t) * temp;
|
||||||
|
|
||||||
|
temp_buffer = align_buffer (temp_buffer, &size);
|
||||||
|
if (!temp_buffer || !size)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < storage_free_list_size[index])
|
||||||
|
{
|
||||||
|
DEBUGF ("Big problem on line %d in sgf.c\n", __LINE__);
|
||||||
|
rb->splashf (5 * HZ,
|
||||||
|
"Error in allocating storage buffer! Exit and report this!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_free_list[index] = temp_buffer;
|
||||||
|
total_storage_size += storage_buffer_size[index];
|
||||||
|
storage_initialized[index] = true;
|
||||||
|
|
||||||
|
clear_storage_buffer (index);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_t *
|
||||||
|
get_node (int handle)
|
||||||
|
{
|
||||||
|
if (handle < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if ((unsigned int) handle >= total_storage_size)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
while ((unsigned int) handle >= storage_buffer_size[index])
|
||||||
|
{
|
||||||
|
handle -= storage_buffer_size[index++];
|
||||||
|
}
|
||||||
|
return &(storage_buffer[index][handle].node);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct prop_t *
|
||||||
|
get_prop (int handle)
|
||||||
|
{
|
||||||
|
if (handle < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if ((unsigned int) handle >= total_storage_size)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
while ((unsigned int) handle >= storage_buffer_size[index])
|
||||||
|
{
|
||||||
|
handle -= storage_buffer_size[index++];
|
||||||
|
}
|
||||||
|
return &(storage_buffer[index][handle].prop);
|
||||||
|
}
|
||||||
|
|
57
apps/plugins/goban/sgf_storage.h
Normal file
57
apps/plugins/goban/sgf_storage.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_SGF_STORAGE_H
|
||||||
|
#define GOBAN_SGF_STORAGE_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Must be called (and return true) before using anything in the SGF
|
||||||
|
subsystem returns false on failure */
|
||||||
|
bool setup_sgf (void);
|
||||||
|
|
||||||
|
/* Do cleanup, call before exiting the plugin. You must not use any SGF
|
||||||
|
subsystem functions after calling this */
|
||||||
|
void cleanup_sgf (void);
|
||||||
|
|
||||||
|
/* Get ready for a new game (either loaded or blank) */
|
||||||
|
void clear_caches_sgf (void);
|
||||||
|
|
||||||
|
/* Clear the SGF tree and get it ready for a new game (loaded or blank) */
|
||||||
|
void free_tree_sgf (void);
|
||||||
|
|
||||||
|
/* Returns true if the Rockbox audio buffer has been stolen */
|
||||||
|
bool audio_stolen_sgf (void);
|
||||||
|
|
||||||
|
/* Returns a handle to a struct storage_t (NOT a pointer) < 0 handles are
|
||||||
|
invalid */
|
||||||
|
int alloc_storage_sgf (void);
|
||||||
|
|
||||||
|
/* Free one storage location */
|
||||||
|
void free_storage_sgf (int handle);
|
||||||
|
|
||||||
|
/* Get a pointer to a node or property which corresponds to the given
|
||||||
|
* storage handle
|
||||||
|
*/
|
||||||
|
struct node_t *get_node (int handle);
|
||||||
|
struct prop_t *get_prop (int handle);
|
||||||
|
|
||||||
|
#endif
|
289
apps/plugins/goban/types.h
Normal file
289
apps/plugins/goban/types.h
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_TYPES_H
|
||||||
|
#define GOBAN_TYPES_H
|
||||||
|
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
/* A generic stack sp is the stack pointer (0 for an empty stack) */
|
||||||
|
struct stack_t
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
size_t sp;
|
||||||
|
char *buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* All of the types of SGF properties that we understand. Not all of these
|
||||||
|
are handled, even if we understand them, unhandled properties are
|
||||||
|
handled differently (basically they are copied to a secondary location
|
||||||
|
and copied back if we output the tree) The ones at the end aren't real
|
||||||
|
SGF properties, they are only to help us in parsing/outputting/keeping
|
||||||
|
track of variations/etc. IMPORTANT: if you edit this, you must be
|
||||||
|
careful to keep it in line with prop_names, PROP_NAMES_SIZE, and
|
||||||
|
PROP_NAME_LEN otherwise really bad things will happen. There is too much
|
||||||
|
information on each of these to list here, please see
|
||||||
|
http://www.red-bean.com/sgf/ */
|
||||||
|
enum prop_type_t
|
||||||
|
{
|
||||||
|
PROP_BLACK_MOVE,
|
||||||
|
PROP_WHITE_MOVE,
|
||||||
|
|
||||||
|
PROP_ADD_BLACK,
|
||||||
|
PROP_ADD_WHITE,
|
||||||
|
PROP_ADD_EMPTY,
|
||||||
|
|
||||||
|
PROP_PLAYER_TO_PLAY,
|
||||||
|
PROP_COMMENT,
|
||||||
|
|
||||||
|
/* information about the position reached by the current node */
|
||||||
|
PROP_EVEN,
|
||||||
|
PROP_BLACK_GOOD,
|
||||||
|
PROP_WHITE_GOOD,
|
||||||
|
PROP_HOTSPOT,
|
||||||
|
PROP_UNCLEAR,
|
||||||
|
PROP_VALUE,
|
||||||
|
|
||||||
|
/* information about the current move */
|
||||||
|
PROP_BAD,
|
||||||
|
PROP_DOUBTFUL,
|
||||||
|
PROP_INTERESTING,
|
||||||
|
PROP_TESUJI,
|
||||||
|
|
||||||
|
/* marks on the board */
|
||||||
|
PROP_CIRCLE,
|
||||||
|
PROP_SQUARE,
|
||||||
|
PROP_TRIANGLE,
|
||||||
|
PROP_DIM,
|
||||||
|
PROP_MARK,
|
||||||
|
PROP_SELECTED,
|
||||||
|
|
||||||
|
/* labels go on points, names name the node */
|
||||||
|
PROP_LABEL,
|
||||||
|
PROP_NODE_NAME,
|
||||||
|
|
||||||
|
/* root props */
|
||||||
|
PROP_APPLICATION,
|
||||||
|
PROP_CHARSET,
|
||||||
|
PROP_FILE_FORMAT,
|
||||||
|
PROP_GAME,
|
||||||
|
PROP_VARIATION_TYPE,
|
||||||
|
PROP_SIZE,
|
||||||
|
|
||||||
|
/* game info props */
|
||||||
|
PROP_ANNOTATOR,
|
||||||
|
PROP_BLACK_NAME,
|
||||||
|
PROP_WHITE_NAME,
|
||||||
|
PROP_HANDICAP,
|
||||||
|
PROP_KOMI,
|
||||||
|
PROP_BLACK_TERRITORY,
|
||||||
|
PROP_WHITE_TERRITORY,
|
||||||
|
PROP_BLACK_RANK,
|
||||||
|
PROP_WHITE_RANK,
|
||||||
|
PROP_BLACK_TEAM,
|
||||||
|
PROP_WHITE_TEAM,
|
||||||
|
PROP_COPYRIGHT,
|
||||||
|
PROP_DATE,
|
||||||
|
PROP_EVENT,
|
||||||
|
PROP_ROUND,
|
||||||
|
PROP_GAME_NAME,
|
||||||
|
PROP_GAME_COMMENT,
|
||||||
|
PROP_OPENING_NAME,
|
||||||
|
PROP_OVERTIME,
|
||||||
|
PROP_PLACE,
|
||||||
|
PROP_RESULT,
|
||||||
|
PROP_RULESET,
|
||||||
|
PROP_SOURCE,
|
||||||
|
PROP_TIME_LIMIT,
|
||||||
|
PROP_USER,
|
||||||
|
|
||||||
|
/* these are all the <whatever> left /after/ the current move */
|
||||||
|
PROP_BLACK_TIME_LEFT,
|
||||||
|
PROP_WHITE_TIME_LEFT,
|
||||||
|
PROP_BLACK_STONES_LEFT, /* the number of stones left in a canadian
|
||||||
|
style overtime period */
|
||||||
|
PROP_WHITE_STONES_LEFT, /* same for white */
|
||||||
|
|
||||||
|
|
||||||
|
/* these are mostly used for printing, we don't handle these */
|
||||||
|
PROP_FIGURE,
|
||||||
|
PROP_PRINT_MOVE_MODE,
|
||||||
|
|
||||||
|
/* view only part of the board. probably don't handle this */
|
||||||
|
PROP_VIEW,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* psuedo PROP types, used for variations and parsing and such */
|
||||||
|
|
||||||
|
PROP_VARIATION, /* used for branches */
|
||||||
|
PROP_INVALID,
|
||||||
|
PROP_GENERIC_UNHANDLED, /* used to mark the place where an
|
||||||
|
unhandled property was copied to
|
||||||
|
secondary storage (so we can output it
|
||||||
|
again when we output the game tree) */
|
||||||
|
PROP_ANY, /* Used as a parameter when any property
|
||||||
|
type is supposed to match */
|
||||||
|
PROP_VARIATION_TO_PROCESS, /* Used in parsing/outputting */
|
||||||
|
PROP_VARIATION_CHOICE, /* Used to store which variation we should
|
||||||
|
follow when we get to a branch */
|
||||||
|
PROP_ROOT_PROPS /* Marks the place where we should output
|
||||||
|
the information from struct header_t
|
||||||
|
header */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char *prop_names[];
|
||||||
|
/* IMPORTANT: keep this array full of all properties that we want to be
|
||||||
|
able to parse out of an SGF file. This next part assumes that they go
|
||||||
|
before unparseable (psuedo) props in the above enum, or else we need to
|
||||||
|
insert placeholders in the array for unparseables */
|
||||||
|
|
||||||
|
#define PROP_NAMES_SIZE (63)
|
||||||
|
|
||||||
|
/* The only one character property names are the moves, everything else is
|
||||||
|
two characters */
|
||||||
|
#define PROP_NAME_LEN(type) ((type == PROP_BLACK_MOVE || \
|
||||||
|
type == PROP_WHITE_MOVE) ? 1 : 2)
|
||||||
|
|
||||||
|
/* Data for a property. Can be a number, a node handle (for variations),
|
||||||
|
or a position and either a short or a character (the extras are for
|
||||||
|
label characters, and stone undo data (moves and added/removed)) */
|
||||||
|
union prop_data_t
|
||||||
|
{
|
||||||
|
unsigned int number;
|
||||||
|
int node;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned short position;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
unsigned short stone_extra;
|
||||||
|
unsigned char label_extra;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* What should happen when the user "plays" a move */
|
||||||
|
enum play_mode_t
|
||||||
|
{
|
||||||
|
MODE_PLAY,
|
||||||
|
MODE_FORCE_PLAY,
|
||||||
|
MODE_ADD_BLACK,
|
||||||
|
MODE_ADD_WHITE,
|
||||||
|
MODE_REMOVE,
|
||||||
|
MODE_MARK,
|
||||||
|
MODE_CIRCLE,
|
||||||
|
MODE_SQUARE,
|
||||||
|
MODE_TRIANGLE,
|
||||||
|
MODE_LABEL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Different types of board marks */
|
||||||
|
enum mark_t
|
||||||
|
{
|
||||||
|
MARK_VARIATION,
|
||||||
|
MARK_SQUARE,
|
||||||
|
MARK_CIRCLE,
|
||||||
|
MARK_TRIANGLE,
|
||||||
|
MARK_LAST_MOVE,
|
||||||
|
MARK_LABEL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* An SGF property next is the handle to the next property in the node, or
|
||||||
|
less than zero if there isn't another */
|
||||||
|
struct prop_t
|
||||||
|
{
|
||||||
|
union prop_data_t data;
|
||||||
|
enum prop_type_t type;
|
||||||
|
int next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The names of the rulesets, ex. "AGA", "Japanese", etc. */
|
||||||
|
extern char *ruleset_names[];
|
||||||
|
|
||||||
|
/* IMPORTANT! keep in sync with ruleset_names!!! */
|
||||||
|
enum ruleset_t
|
||||||
|
{
|
||||||
|
RULESET_AGA = 0,
|
||||||
|
RULESET_JAPANESE,
|
||||||
|
RULESET_CHINESE,
|
||||||
|
RULESET_NEW_ZEALAND,
|
||||||
|
RULESET_ING,
|
||||||
|
__RULESETS_SIZE /* make sure i am last! */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_RULESETS ((int) __RULESETS_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
/* One SGF node which can contain an indefinite number of SGF properties
|
||||||
|
Less than zero for any of these means that there is nothing in that
|
||||||
|
direction. */
|
||||||
|
struct node_t
|
||||||
|
{
|
||||||
|
int props;
|
||||||
|
int next;
|
||||||
|
int prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* convenience union for keeping a mixed array of props and nodes */
|
||||||
|
union storage_t
|
||||||
|
{
|
||||||
|
struct prop_t prop;
|
||||||
|
struct node_t node;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The game metadata which can be stored in the SGF file */
|
||||||
|
|
||||||
|
#define MAX_NAME 59
|
||||||
|
#define MAX_EVENT 100
|
||||||
|
#define MAX_RESULT 16
|
||||||
|
#define MAX_RANK 10
|
||||||
|
#define MAX_TEAM 32
|
||||||
|
#define MAX_DATE 32
|
||||||
|
#define MAX_ROUND 8
|
||||||
|
#define MAX_PLACE 100
|
||||||
|
#define MAX_OVERTIME 32
|
||||||
|
#define MAX_RULESET 32
|
||||||
|
|
||||||
|
struct header_t
|
||||||
|
{
|
||||||
|
char white[MAX_NAME];
|
||||||
|
char black[MAX_NAME];
|
||||||
|
char white_rank[MAX_RANK];
|
||||||
|
char black_rank[MAX_RANK];
|
||||||
|
char white_team[MAX_TEAM];
|
||||||
|
char black_team[MAX_TEAM];
|
||||||
|
char date[MAX_DATE];
|
||||||
|
char round[MAX_ROUND];
|
||||||
|
char event[MAX_EVENT];
|
||||||
|
char place[MAX_PLACE];
|
||||||
|
char result[MAX_RESULT];
|
||||||
|
char overtime[MAX_OVERTIME];
|
||||||
|
char ruleset[MAX_RULESET];
|
||||||
|
int time_limit;
|
||||||
|
int komi;
|
||||||
|
uint8_t handicap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
885
apps/plugins/goban/util.c
Normal file
885
apps/plugins/goban/util.c
Normal file
|
@ -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 */
|
113
apps/plugins/goban/util.h
Normal file
113
apps/plugins/goban/util.h
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GOBAN_UTIL_H
|
||||||
|
#define GOBAN_UTIL_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "goban.h"
|
||||||
|
|
||||||
|
/* Call before using a stack, returns false on setup failure */
|
||||||
|
bool setup_stack (struct stack_t *stack, void *buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
/* Push, pop or peek from the stack. Returns false on failure (usually
|
||||||
|
stack full or empty, depending on the function) */
|
||||||
|
bool push_stack (struct stack_t *stack, void *buffer, size_t buffer_size);
|
||||||
|
bool pop_stack (struct stack_t *stack, void *buffer, size_t buffer_size);
|
||||||
|
bool peek_stack (struct stack_t *stack, void *buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
/* Clear all of the data from the stack and move the stack pointer to the
|
||||||
|
beginning */
|
||||||
|
void empty_stack (struct stack_t *stack);
|
||||||
|
|
||||||
|
/* Convenience functions for pushing/poping/peeking standard value types
|
||||||
|
to a stack */
|
||||||
|
#define pop_pos_stack(stack, pos) pop_stack(stack, pos, sizeof (unsigned short))
|
||||||
|
#define peek_pos_stack(stack, pos) peek_stack(stack, pos, sizeof (unsigned short))
|
||||||
|
|
||||||
|
#define pop_int_stack(stack, num) pop_stack(stack, num, sizeof (int))
|
||||||
|
#define peek_int_stack(stack, num) peek_stack(stack, num, sizeof (int))
|
||||||
|
|
||||||
|
#define pop_char_stack(stack, num) pop_stack(stack, num, sizeof (char))
|
||||||
|
#define peek_char_stack(stack, num) peek_stack(stack, num, sizeof (char))
|
||||||
|
|
||||||
|
bool push_pos_stack (struct stack_t *stack, unsigned short pos);
|
||||||
|
bool push_int_stack (struct stack_t *stack, int num);
|
||||||
|
bool push_char_stack (struct stack_t *stack, char num);
|
||||||
|
|
||||||
|
|
||||||
|
#define min(x, y) (x < y ? x : y)
|
||||||
|
#define max(x, y) (x > y ? x : y)
|
||||||
|
|
||||||
|
/* Returns the fd of the file */
|
||||||
|
int create_or_open_file (const char *filename);
|
||||||
|
|
||||||
|
/* Returns the number of characters printed */
|
||||||
|
int snprint_fixed (char *buffer, int buffer_size, int fixed);
|
||||||
|
|
||||||
|
/* These should all be obvious, they are simply wrappers on the normal
|
||||||
|
rockbox file functions which will loop several times if the rockbox
|
||||||
|
file functions don't deal with all of the data at once */
|
||||||
|
ssize_t read_file (int fd, void *buf, size_t count);
|
||||||
|
ssize_t write_file (int fd, const void *buf, size_t count);
|
||||||
|
void close_file (int *fd);
|
||||||
|
|
||||||
|
int peek_char (int fd);
|
||||||
|
int read_char (int fd);
|
||||||
|
bool write_char (int fd, char to_write);
|
||||||
|
|
||||||
|
/* Seek to the next non-whitespace character (doesn't go anywhere if the
|
||||||
|
current character is already non-whitespace), and then peek it -1 on
|
||||||
|
EOF or error */
|
||||||
|
int peek_char_no_whitespace (int fd);
|
||||||
|
/* Same deal, with reading -1 on EOF or error */
|
||||||
|
int read_char_no_whitespace (int fd);
|
||||||
|
|
||||||
|
/* Returns true if a character is whitespace. Should /NOT/ be called with
|
||||||
|
anything but the return value of one of the peeking/reading functions or
|
||||||
|
a standard character. */
|
||||||
|
bool is_whitespace (int value);
|
||||||
|
|
||||||
|
/* Gets rid of ']' characdters from the string by overwritting them. This
|
||||||
|
is needed in header strings because they would otherwise corrupt the
|
||||||
|
SGF file when outputted */
|
||||||
|
void sanitize_string (char *string);
|
||||||
|
|
||||||
|
/* Return an aligned version of the bufer, with the size updated Returns
|
||||||
|
NULL if the buffer is too small to align. */
|
||||||
|
void *align_buffer (void *buffer, size_t * buffer_size);
|
||||||
|
|
||||||
|
/* Get the string and buffer size for a SGF property which is stored in
|
||||||
|
the header. Returns false on failure, in which case any information set
|
||||||
|
in **buffer and *size are not to be trusted or used. */
|
||||||
|
bool get_header_string_and_size (struct header_t *header,
|
||||||
|
enum prop_type_t type,
|
||||||
|
char **buffer, int *size);
|
||||||
|
|
||||||
|
/* Output a summary of the game metadata (Game Info)
|
||||||
|
*/
|
||||||
|
void metadata_summary (void);
|
||||||
|
|
||||||
|
#ifdef GBN_TEST
|
||||||
|
void run_tests (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -19,6 +19,7 @@ rmi,viewers/midi,7
|
||||||
rsp,viewers/searchengine,8
|
rsp,viewers/searchengine,8
|
||||||
sok,games/sokoban,1
|
sok,games/sokoban,1
|
||||||
pgn,games/chessbox,1
|
pgn,games/chessbox,1
|
||||||
|
sgf,games/goban,1
|
||||||
ss,games/sudoku,1
|
ss,games/sudoku,1
|
||||||
wav,viewers/wav2wv,-
|
wav,viewers/wav2wv,-
|
||||||
wav,viewers/mp3_encoder,-
|
wav,viewers/mp3_encoder,-
|
||||||
|
|
|
@ -448,6 +448,7 @@ Ryan Press
|
||||||
Craig Elliott
|
Craig Elliott
|
||||||
Kenderes Tamas
|
Kenderes Tamas
|
||||||
Eric Shattow
|
Eric Shattow
|
||||||
|
Joshua Simmons
|
||||||
|
|
||||||
|
|
||||||
The libmad team
|
The libmad team
|
||||||
|
|
243
manual/plugins/goban.tex
Normal file
243
manual/plugins/goban.tex
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
\subsection{Goban}
|
||||||
|
\screenshot{plugins/images/ss-goban}{Goban}{The Rockbox Goban plugin}
|
||||||
|
Goban is a a plugin for playing, viewing and recording games of Go (also known
|
||||||
|
as Weiqi, Baduk, Igo and Goe). It uses standard Smart Game Format (SGF) files
|
||||||
|
for saving and loading games.
|
||||||
|
|
||||||
|
You can find a short introduction to Go at
|
||||||
|
\url{http://senseis.xmp.net/?WhatIsGo} and more information about SGF files
|
||||||
|
can be read at \url{http://senseis.xmp.net/?SmartGameFormat} or the SGF
|
||||||
|
specification at
|
||||||
|
\url{http://www.red-bean.com/sgf/}.
|
||||||
|
|
||||||
|
This plugin can load all modern SGF files (file format 3 or 4) with few problems.
|
||||||
|
It attempts to preserve SGF properties which it doesn't understand, and most common
|
||||||
|
SGF properties are handled fully. It is possible to view (and edit if you like)
|
||||||
|
Kogo's Joseki Dictionary (\url{http://waterfire.us/joseki.htm}) with this plugin,
|
||||||
|
although the load and save times can be on the order of a minute or two on
|
||||||
|
particularly slow devices. Large SGF files may stop audio playback for the duration
|
||||||
|
of the plugin's run in order to free up more memory and some very large SGF files will
|
||||||
|
not even load on devices with little available memory.
|
||||||
|
|
||||||
|
\emph{Note: } The plugin does \emph{NOT} support SGF files with multiple games in
|
||||||
|
one file. These are rare, but if you have one don't even try it (the file will most
|
||||||
|
likely be corrupted if you save over it). You have been warned.
|
||||||
|
|
||||||
|
The file \fname {"/sgf/gbn\_def.sgf"} is used by the plugin to store any unsaved
|
||||||
|
changes in the most recently loaded game. This means that if you forget to save your
|
||||||
|
changes, you should load \fname {"/sgf/gbn\_def.sgf"} immediately to offload the changes
|
||||||
|
to another file. If you load another file first then your changes will be lost
|
||||||
|
permanently. The \fname {"/sgf/gbn\_def.sgf"} file is also the file loaded if another
|
||||||
|
is not selected.
|
||||||
|
|
||||||
|
The information panel which displays the current move number may also contain
|
||||||
|
these markers: \\
|
||||||
|
\begin{tabularx}{\textwidth}{lX}\toprule
|
||||||
|
\textbf{Mark} & \textbf{Meaning} \\ \midrule
|
||||||
|
\emph{+ } & There are nodes after the current node in the SGF tree. \\
|
||||||
|
\emph{* } & There are sibling variations which can be navigated to using the %
|
||||||
|
\emph{Next Variation} menu option of the \emph{Context Menu}%
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_C200_PAD,SANSA_FUZE_PAD,RECORDER_PAD,%
|
||||||
|
MROBE100_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,IRIVER_H100_PAD,%
|
||||||
|
IRIVER_H300_PAD}{ or the %
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD}{\ButtonRec}%
|
||||||
|
\opt{RECORDER_PAD}{\ButtonOn}%
|
||||||
|
\opt{MROBE100_PAD}{\ButtonPower}%
|
||||||
|
\opt{GIGABEAT_PAD}{\ButtonA}%
|
||||||
|
\opt{GIGABEAT_S_PAD}{\ButtonPlay}%
|
||||||
|
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonRec} button}. \\
|
||||||
|
\emph{C } & There is a comment at the current node. It can be viewed/edited using
|
||||||
|
the \emph{Add/Edit Comment} menu option of the \emph{Context Menu}. \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
|
||||||
|
\subsection{Controls}
|
||||||
|
\begin{table}
|
||||||
|
\begin{btnmap}{}{}
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,GIGABEAT_PAD,%
|
||||||
|
GIGABEAT_S_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,MROBE100_PAD,%
|
||||||
|
IAUDIO_X5_PAD,RECORDER_PAD,ONDIO_PAD}{\ButtonUp}%
|
||||||
|
\opt{IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD}{\ButtonMenu}%
|
||||||
|
\opt{IRIVER_H10_PAD}{\ButtonScrollUp} & Move cursor up \\
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,
|
||||||
|
IRIVER_H100_PAD,IRIVER_H300_PAD,MROBE100_PAD,IAUDIO_X5_PAD,RECORDER_PAD,
|
||||||
|
ONDIO_PAD}{\ButtonDown}%
|
||||||
|
\opt{IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD}{\ButtonPlay}%
|
||||||
|
\opt{IRIVER_H10_PAD}{\ButtonScrollDown} & Move cursor down \\
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD,%
|
||||||
|
SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,IRIVER_H10_PAD,IRIVER_H100_PAD,%
|
||||||
|
IRIVER_H300_PAD,MROBE100_PAD,IAUDIO_X5_PAD,RECORDER_PAD,%
|
||||||
|
ONDIO_PAD}{\ButtonLeft} & Move cursor left %
|
||||||
|
\opt{ONDIO_PAD}{if in \emph{board} navigation mode, or %
|
||||||
|
retreat one node in the game tree if in %
|
||||||
|
\emph{tree} navigation mode} \\
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD,%
|
||||||
|
SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,IRIVER_H10_PAD,IRIVER_H100_PAD,%
|
||||||
|
IRIVER_H300_PAD,MROBE100_PAD,IAUDIO_X5_PAD,RECORDER_PAD,%
|
||||||
|
ONDIO_PAD}{\ButtonRight} & Move cursor right
|
||||||
|
\opt{ONDIO_PAD}{if in \emph{board} navigation mode, or advance one node in
|
||||||
|
the game tree if in \emph{tree} navigation mode} \\
|
||||||
|
\opt{ONDIO_PAD}{{\ButtonOff} & Toggle between \emph{board} and \emph{tree}
|
||||||
|
navigation modes \\}
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD,%
|
||||||
|
SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,%
|
||||||
|
MROBE100_PAD,IAUDIO_X5_PAD}{\ButtonSelect}%
|
||||||
|
\opt{IRIVER_H10_PAD,RECORDER_PAD}{\ButtonPlay}%
|
||||||
|
\opt{ONDIO_PAD}{\ButtonMenu} & Play a move (or use a tool if play-mode has
|
||||||
|
been changed). \\
|
||||||
|
\nopt{ONDIO_PAD}{
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_1G2G_PAD,IPOD_3G_PAD,%
|
||||||
|
IPOD_4G_PAD}{\ButtonScrollBack}%
|
||||||
|
\opt{SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD}{\ButtonVolDown}%
|
||||||
|
\opt{IRIVER_H10_PAD}{\ButtonFF}%
|
||||||
|
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOff}%
|
||||||
|
\opt{MROBE100_PAD}{\ButtonMenu}%
|
||||||
|
\opt{IAUDIO_X5_PAD}{\ButtonPlay}%
|
||||||
|
\opt{RECORDER_PAD}{\ButtonFOne} & Retreat one node in the game tree \\
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD,%
|
||||||
|
IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD}{\ButtonScrollFwd}%
|
||||||
|
\opt{SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD}{\ButtonVolUp}%
|
||||||
|
\opt{IRIVER_H10_PAD}{\ButtonRew}%
|
||||||
|
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonOn}%
|
||||||
|
\opt{MROBE100_PAD}{\ButtonPlay}%
|
||||||
|
\opt{IAUDIO_X5_PAD}{\ButtonRec}%
|
||||||
|
\opt{RECORDER_PAD}{\ButtonFThree} & Advance one node in the game tree \\ }
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,IRIVER_H10_PAD,%
|
||||||
|
IAUDIO_X5_PAD}{\ButtonPower}%
|
||||||
|
\opt{MROBE100_PAD}{\ButtonDisplay}%
|
||||||
|
\opt{IPOD_1G2G_PAD,IPOD_3G_PAD,IPOD_4G_PAD}{Long \ButtonSelect}%
|
||||||
|
\opt{GIGABEAT_PAD,GIGABEAT_S_PAD}{\ButtonMenu}%
|
||||||
|
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonMode}%
|
||||||
|
\opt{RECORDER_PAD}{\ButtonFTwo}%
|
||||||
|
\opt{ONDIO_PAD}{Long \ButtonMenu} & Main Menu \\
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,%
|
||||||
|
IRIVER_H100_PAD,IRIVER_H300_PAD,MROBE100_PAD,IAUDIO_X5_PAD,%
|
||||||
|
IRIVER_H10_PAD}{%
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD,SANSA_C200_PAD,GIGABEAT_PAD,%
|
||||||
|
GIGABEAT_S_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,MROBE100_PAD,%
|
||||||
|
IAUDIO_X5_PAD}{Long \ButtonSelect}%
|
||||||
|
\opt{IRIVER_H10_PAD}{Long \ButtonPlay} & Context Menu \\ }
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_C200_PAD,SANSA_FUZE_PAD,RECORDER_PAD,MROBE100_PAD,%
|
||||||
|
GIGABEAT_PAD,GIGABEAT_S_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}{%
|
||||||
|
\opt{SANSA_E200_PAD,SANSA_FUZE_PAD}{\ButtonRec}%
|
||||||
|
\opt{SANSA_C200_PAD}{\ButtonRec}%
|
||||||
|
\opt{RECORDER_PAD}{\ButtonOn}%
|
||||||
|
\opt{MROBE100_PAD}{\ButtonPower}%
|
||||||
|
\opt{GIGABEAT_PAD}{\ButtonA}%
|
||||||
|
\opt{GIGABEAT_S_PAD}{\ButtonPlay}%
|
||||||
|
\opt{IRIVER_H100_PAD,IRIVER_H300_PAD}{\ButtonRec} & Go to the next variation %
|
||||||
|
when at the first node in %
|
||||||
|
a branch \\ }
|
||||||
|
\end{btnmap}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{Menus}
|
||||||
|
\begin {description}
|
||||||
|
\item [Main Menu. ]
|
||||||
|
The main menu for game setup and access to other menus.
|
||||||
|
|
||||||
|
\emph {New. } Create a new game with your choice of board size and handicaps. \\
|
||||||
|
\emph {Save. } Save the current state of the game. It will be saved to
|
||||||
|
\fname {"/sgf/gbn\_def.sgf"} unless otherwise set. \\
|
||||||
|
\emph {Save As. } Save to a specified file. \\
|
||||||
|
\emph {Game Info. } View and modify the metadata of the current game. \\
|
||||||
|
\emph {Playback Control. } Control the playback of the current playlist and
|
||||||
|
modify the volume of your player. \\
|
||||||
|
\emph {Zoom Level. } Zoom in or out on the board. If you set the zoom level, it
|
||||||
|
will be saved and used again the next time you open this plugin. \\
|
||||||
|
\emph {Options. } Open the Options Menu. \\
|
||||||
|
\emph {Context Menu. } Open the Context Menu which allows you to set play modes
|
||||||
|
and other tools. \\
|
||||||
|
\emph {Quit. } Leave the plugin. Any unsaved changes are saved to
|
||||||
|
\fname {"/sgf/gbn\_def.sgf"}. \\
|
||||||
|
|
||||||
|
\item [Game Info. ]
|
||||||
|
The menu for modifying game info (metadata) of the current game. This
|
||||||
|
information will be saved to the SGF file and can be viewed in almost all
|
||||||
|
SGF readers.
|
||||||
|
|
||||||
|
\emph {Basic Info. } Shows a quick view of the basic game metadata, if any
|
||||||
|
has been set (otherwise does nothing). This option does not allow editing. \\
|
||||||
|
\emph {Time Limit. } The time limit of the current game. \\
|
||||||
|
\emph {Overtime. } The overtime settings of the current game. \\
|
||||||
|
\emph {Result. } The result of the current game. This text must follow the
|
||||||
|
format specified at \url{http://www.red-bean.com/sgf/properties.html#RE} to
|
||||||
|
be read by other SGF readers. Some examples are \emph {B+R} (Black wins by
|
||||||
|
resignation), \emph {B+5.5} (Black wins by 5.5 points), \emph {W+T} (White wins
|
||||||
|
on Time). \\
|
||||||
|
\emph {Handicap. } The handicap of the current game. \\
|
||||||
|
\emph {Komi. } The komi of the current game (compensation to the white
|
||||||
|
player for black having the first move). \\
|
||||||
|
\emph {Ruleset. } The name of the ruleset in use for this game. The \emph{NZ}
|
||||||
|
and \emph{GOE} rulesets include suicide as a legal move (for multi-stone
|
||||||
|
suicide only); the rest do not. \\
|
||||||
|
\emph {Black Player. } The name of the black player. \\
|
||||||
|
\emph {Black Rank. } Black's rank, in dan or kyu. \\
|
||||||
|
\emph {Black Team. } The name of black's team, if any. \\
|
||||||
|
\emph {White Player. } The name of the white player. \\
|
||||||
|
\emph {White Rank. } White's rank, in dan or kyu. \\
|
||||||
|
\emph {White Team. } The name of white's team, if any. \\
|
||||||
|
\emph {Date. } The date that this game took place. This text must follow the
|
||||||
|
format specified at \url{http://www.red-bean.com/sgf/properties.html#DT} to
|
||||||
|
be read by other SGF readers. \\
|
||||||
|
\emph {Event. } The name of the event which this game was a part of, if any.
|
||||||
|
\\
|
||||||
|
\emph {Place. } The place that this game took place. \\
|
||||||
|
\emph {Round. } If part of a tournament, the round number for this game. \\
|
||||||
|
\emph {Done. } Return to the previous menu. \\
|
||||||
|
|
||||||
|
\item [Options. ]
|
||||||
|
Customize the behavior of the plugin in certain ways.
|
||||||
|
|
||||||
|
\emph {Show Child Variations? } Enable this to mark child variations on the board
|
||||||
|
if there are more than one. Note: variations which don't start with a move are
|
||||||
|
not visible in this way. \\
|
||||||
|
\emph {Disable Idle Poweroff? } Enable this if you do not want the \dap{} to turn
|
||||||
|
off after a certain period of inactivity (depends on your global Rockbox
|
||||||
|
settings). \\
|
||||||
|
\emph {Idle Autosave Time. } Set the amount of idle time to wait before
|
||||||
|
automatically saving any unsaved changes. These autosaves go to the file
|
||||||
|
\fname {"/sgf/gbn\_def.sgf"} regardless of if you have loaded a game or used
|
||||||
|
\emph {Save As} to save the game before or not. Set to \emph {Off} to disable
|
||||||
|
this functionality completely. \\
|
||||||
|
\emph {Automatically Show Comments? } If this is enabled and you navigate to a
|
||||||
|
node containing game comments, they will automatically be displayed. \\
|
||||||
|
|
||||||
|
\item [Context Menu. ]
|
||||||
|
The menu for choosing different play modes and tools, adding or editing
|
||||||
|
comments, adding pass moves, or switching between sibling variations.
|
||||||
|
|
||||||
|
\emph {Play Mode. } Play moves normally on the board. If there are
|
||||||
|
child moves from the current node, this mode will let you follow variations
|
||||||
|
by simply playing the first move in the sequence. Unless it is following a
|
||||||
|
variation, this mode will not allow you to play illegal moves. This is the
|
||||||
|
default mode before another is set after loading a game or creating a new
|
||||||
|
one. \\
|
||||||
|
\emph {Add Black Mode. } Add black stones to the board as desired. These
|
||||||
|
stones are not moves and do not perform captures or count as ko threats. \\
|
||||||
|
\emph {Add White Mode. } Add white stones to the board as desired. These
|
||||||
|
stones are not moves and do not perform captures or count as ko threats. \\
|
||||||
|
\emph {Erase Stone Mode. } Remove stones from the board as desired. These
|
||||||
|
removed stones are not counted as captured, they are simply removed. \\
|
||||||
|
\emph {Pass. } Play a single pass move. This does not change the mode of
|
||||||
|
play. \\
|
||||||
|
\emph {Next Variation. } If the game is at the first move in a variation,
|
||||||
|
this will navigate to the next variation after the current one. This is
|
||||||
|
the only way to reach variations which start with adding or removing
|
||||||
|
stones, as you cannot follow them by "playing" the same move. \\
|
||||||
|
\emph {Force Play Mode. } The same as Play Mode except that this mode will
|
||||||
|
allow you to play illegal moves such as retaking a ko immediately without a
|
||||||
|
ko threat, suicide on rulesets which don't allow it (including single stone
|
||||||
|
suicide), and playing a move where there is already a stone. \\
|
||||||
|
\emph {Mark Mode. } Add generic marks to the board, or remove them. \\
|
||||||
|
\emph {Circle Mode. } Add circle marks to the board, or remove them. \\
|
||||||
|
\emph {Square Mode. } Add square marks to the board, or remove them. \\
|
||||||
|
\emph {Triangle Mode. } Add triangle marks to the board, or remove them. \\
|
||||||
|
\emph {Label Mode. } Add one character labels to the board. Each label
|
||||||
|
starts at the letter 'a' and each subsequent application of a label will
|
||||||
|
increment the letter. To remove a label, click on it until it cycles
|
||||||
|
through the allowed letters and disappears. \\
|
||||||
|
\emph {Add/Edit Comment. } Add or edit a comment at the current node. \\
|
||||||
|
\emph {Done. } Go back to the previous screen. \\
|
||||||
|
\end{description}
|
||||||
|
|
|
@ -35,6 +35,8 @@ text files%
|
||||||
|
|
||||||
{\input{plugins/flipit.tex}}
|
{\input{plugins/flipit.tex}}
|
||||||
|
|
||||||
|
\opt{lcd_bitmap}{\input{plugins/goban.tex}}
|
||||||
|
|
||||||
\opt{player}{\input{plugins/jackpot.tex}}
|
\opt{player}{\input{plugins/jackpot.tex}}
|
||||||
|
|
||||||
\opt{lcd_bitmap}{\input{plugins/jewels.tex}}
|
\opt{lcd_bitmap}{\input{plugins/jewels.tex}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue