/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007-2009 Joshua Simmons * * 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) We avoid having to clear the entire array every time by storing the "current_mark" number and defining marked as "== current_mark". We still need to clear the whole array once per "cycle" though, or we'd get false positives sometimes */ 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 x, y; /* for the borders */ rb->memset(board_data, INVALID, sizeof(board_data)); /* 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); } }