Commit 27960a8f authored by Jonathan Michalon's avatar Jonathan Michalon

Pre-valaification of board module. It compils but needs more work.

parent 9f54577b
......@@ -4,7 +4,7 @@ noinst_PROGRAMS = test
libovcc_la_CPPFLAGS = -DG_LOG_DOMAIN=\"libovcc\"
libovcc_la_SOURCES = enumtypes.c \
ovcc-marshal.c \
board.c \
board.vala \
game.c \
pawn.c \
player.c \
......@@ -16,7 +16,6 @@ libovcc_la_SOURCES = enumtypes.c \
utils.vala \
xmlutils.c
ovccinclude_HEADERS = ovcc.h \
board.h \
game.h \
player.h \
tileset.h
......
/*
*
* Copyright (C) 2009 Colomban Wendling <ban@herbesfolles.org>
* Jonathan Michalon <studios.chalmion@no-log.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "board.h"
#include <glib.h>
#include <glib-object.h>
#include "ovcc-marshal.h"
#include "typeutils.h"
#include "tile.h"
#include "tilesdef.h"
/**
* SECTION: board
* @short_description: Implementation of the board of OVCC
* @include: libovcc/board.h
*
* This is the API of the board (where tiles are placed) for OVCC.
*/
DEFINE_BOXED_TYPE (OVCCPosition, ovcc_pos, ovcc_pos_copy, ovcc_pos_free)
static guint
position_hash (gconstpointer key)
{
#if 0
const OVCCPosition *pos = key;
/* FIXME is this a good way to determine a unique key from two values ??? */
return pos->x * 1000 + pos->y;
#else
/* from g_str_hash, do a direct in-memory hash */
const signed char *p = key;
guint32 h = *p;
gsize i = 0;
for (i = 1; i < sizeof (OVCCPosition); i++)
h = (h << 5) - h + p[i];
return h;
#endif
}
/**
* ovcc_pos_alloc:
*
* Allocates an #OVCCPosition on the heap.
* Note that this is function in unlikely to be useful in your code, since
* stacked positions are possible and generally more convenient.
*
* Returns: A newly allocated #OVCCPosition, set to 0. Free it with
* ovcc_pos_free().
*/
OVCCPosition *
ovcc_pos_alloc (void)
{
return g_slice_alloc0 (sizeof (OVCCPosition));
}
/**
* ovcc_pos_free:
* @pos: An #OVCCPosition
*
* Frees the memory allocated by ovcc_pos_alloc() or ovcc_pos_copy().
*/
void
ovcc_pos_free (OVCCPosition *pos)
{
g_slice_free1 (sizeof *pos, pos);
}
/**
* ovcc_pos_copy:
* @pos: A pointer to an #OVCCPosition
*
* Copies an #OVCCPosition.
*
* Returns: A newly allocated copy of @pos. Free with ovcc_pos_free().
*/
OVCCPosition *
ovcc_pos_copy (const OVCCPosition *pos)
{
return g_slice_copy (sizeof *pos, pos);
}
/**
* ovcc_pos_equal:
* @a: A pointer to an #OVCCPosition
* @b: A pointer to an #OVCCPosition
*
* Checks whether two #OVCCPosition refers to the same position.
*
* Returns: %TRUE if the position are the same, %FALSE otherwise.
*/
gboolean
ovcc_pos_equal (const OVCCPosition *a,
const OVCCPosition *b)
{
return (a->x == b->x && a->y == b->y);
}
struct _OVCCBoardPrivate
{
GHashTable *board;
GHashTable *pawns;
gint leftedge;
gint topedge;
gint rightedge;
gint bottomedge;
};
enum
{
SIGNAL_TILE_ADDED,
N_SIGNALS
};
static void ovcc_board_do_add_tile (OVCCBoard *board,
OVCCTile *tile,
const OVCCPosition *pos);
G_DEFINE_TYPE (OVCCBoard, ovcc_board, G_TYPE_OBJECT)
static guint ovcc_board_signals[N_SIGNALS] = { 0 };
static void
ovcc_board_finalize (GObject *object)
{
OVCCBoard *board = OVCC_BOARD (object);
g_hash_table_destroy (board->priv->board);
/* chain parent destructor */
G_OBJECT_CLASS (ovcc_board_parent_class)->finalize (object);
}
static void
ovcc_board_init (OVCCBoard *board)
{
board->priv = G_TYPE_INSTANCE_GET_PRIVATE (board,
OVCC_TYPE_BOARD,
OVCCBoardPrivate);
board->priv->leftedge = 0;
board->priv->topedge = 0;
board->priv->rightedge = 0;
board->priv->bottomedge = 0;
board->priv->board = g_hash_table_new_full (position_hash,
(GEqualFunc)ovcc_pos_equal,
(GDestroyNotify)ovcc_pos_free,
g_object_unref);
}
static void
ovcc_board_class_init (OVCCBoardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ovcc_board_finalize;
/**
* OVCCBoard::tile-added:
* @board: The object that received the signal
* @tile: The tile that was just added
* @pos: The position at which @tile is on the board
*
* This signal is emitted when a tile is added to the board.
*/
ovcc_board_signals[SIGNAL_TILE_ADDED] =
g_signal_new ("tile-added",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (OVCCBoardClass, tile_added),
NULL, NULL,
_ovcc_marshal_VOID__OBJECT_BOXED,
G_TYPE_NONE,
2,
OVCC_TYPE_TILE,
OVCC_TYPE_POS);
g_type_class_add_private (klass, sizeof (OVCCBoardPrivate));
}
/**
* ovcc_board_new:
* @stack: The #OVCCStack with witch reset the new board for the first time.
* See the documentation of ovcc_board_reset() for details.
*
* Creates a new #OVCCBoard.
*
* Returns: The newly created #OVCCBoard.
*/
OVCCBoard *
ovcc_board_new (OVCCStack *stack)
{
OVCCBoard *board;
board = g_object_new (OVCC_TYPE_BOARD, NULL);
ovcc_board_reset (board, stack);
return board;
}
/**
* ovcc_board_reset:
* @board: The #OVCCBoard to reset.
* @stack: The #OVCCStack from which pop the tile to use as first one.
*
* Removes all tiles from an #OVCCBoard and puts the first tile in the middle.
*/
void
ovcc_board_reset (OVCCBoard *board,
OVCCStack *stack)
{
OVCCPosition pos = {0, 0};
OVCCTile *first;
g_hash_table_remove_all (board->priv->board);
board->priv->leftedge = 0;
board->priv->topedge = 0;
board->priv->rightedge = 0;
board->priv->bottomedge = 0;
first = ovcc_stack_pop (stack);
if (first) {
ovcc_board_do_add_tile (board, first, &pos);
g_object_unref (first);
}
}
/**
* ovcc_board_is_tile_there:
* @board: An #OVCCBoard.
* @pos: The #OVCCPosition to look at.
*
* Says whether there is a tile on a given position of the board.
*
* Returns: %TRUE if a tile is found, %FALSE otherwise.
*/
gboolean
ovcc_board_is_tile_there (OVCCBoard *board,
const OVCCPosition *pos)
{
return ovcc_board_get_tile (board, pos) != NULL;
}
/**
* ovcc_board_get_tile:
* @board: An #OVCCBoard.
* @pos: The #OVCCPosition of the tile on the board.
*
* Gives a pointer to the tile at the given position on the given board.
*
* Returns: The tile, or %NULL if none.
*/
OVCCTile *
ovcc_board_get_tile (OVCCBoard *board,
const OVCCPosition *pos)
{
return g_hash_table_lookup (board->priv->board, pos);
}
/**
* ovcc_board_add_tile_check:
* @board: An #OVCCBoard.
* @tile: An #OVCCTile.
* @pos: The position where check for placement.
*
* Checks if a given tile could be placed at a given position.
*
* For a tile to be acceptable for placement, it needs to have at least
* one neighbour and all its sides must be of the same type as their neighbours.
*
* Returns: %TRUE if placement is possible, %FALSE otherwise.
*/
/* TODO: try & check this! */
gboolean
ovcc_board_add_tile_check (OVCCBoard *board,
const OVCCTile *tile,
const OVCCPosition *pos)
{
gboolean valid = TRUE; /* whether the relations are consistent */
gboolean have_neighbour = FALSE; /* whether the tile have a neighbour */
/* cannot place a tile if there is already one */
if (ovcc_board_is_tile_there (board, pos)) {
valid = FALSE;
} else {
gsize i;
/* check for each possible tile around */
for (i = 0; valid && i < OVCC_TILE_SIDE_NUM; i++) {
OVCCTileSidePosition front; /* the neighbour's side that collapse this one */
OVCCPosition tmppos = *pos;
OVCCTile *tmptile;
switch (i) {
case OVCC_TILE_SIDE_POS_TOP:
tmppos.y --; front = OVCC_TILE_SIDE_POS_BOTTOM; break;
case OVCC_TILE_SIDE_POS_BOTTOM:
tmppos.y ++; front = OVCC_TILE_SIDE_POS_TOP; break;
case OVCC_TILE_SIDE_POS_LEFT:
tmppos.x --; front = OVCC_TILE_SIDE_POS_RIGHT; break;
case OVCC_TILE_SIDE_POS_RIGHT:
tmppos.x ++; front = OVCC_TILE_SIDE_POS_LEFT; break;
default:
g_return_val_if_reached (FALSE);
}
tmptile = ovcc_board_get_tile (board, &tmppos);
valid = (! tmptile) ||
(ovcc_tile_get_side_content_type (tile, i) ==
ovcc_tile_get_side_content_type (tmptile, front));
if (tmptile) {
have_neighbour = TRUE;
}
}
}
return valid && have_neighbour;
}
static void
ovcc_board_do_add_tile (OVCCBoard *board,
OVCCTile *tile,
const OVCCPosition *pos)
{
g_hash_table_insert (board->priv->board,
ovcc_pos_copy (pos), g_object_ref (tile));
board->priv->leftedge = MIN (pos->x, board->priv->leftedge);
board->priv->topedge = MIN (pos->y, board->priv->topedge);
board->priv->rightedge = MAX (pos->x, board->priv->rightedge);
board->priv->bottomedge = MAX (pos->y, board->priv->bottomedge);
g_signal_emit (board, ovcc_board_signals[SIGNAL_TILE_ADDED], 0,
tile, pos);
}
/**
* ovcc_board_add_tile:
* @board: An #OVCCBoard.
* @tile: An #OVCCTile to add on the board.
* @pos: The #OVCCPosition where to place the tile.
*
* Tries to add a tile at the given position on the board.
* It adds the tile only if it can actually be placed at the requested position.
* If you only want to check whether the tile can be placed at a given position
* without actually placing it, use ovcc_board_add_tile_check().
*/
gboolean
ovcc_board_add_tile (OVCCBoard *board,
OVCCTile *tile,
const OVCCPosition *pos)
{
gboolean placed = FALSE;
if (ovcc_board_add_tile_check (board, tile, pos)) {
ovcc_board_do_add_tile (board, tile, pos);
placed = TRUE;
}
return placed;
}
static gboolean
do_try_place_tile (OVCCBoard *board,
OVCCPosition *pos,
OVCCTile *tile,
gpointer user_data)
{
gsize i;
gboolean keep_doing = TRUE;
OVCCTile *new_tile = user_data;
(void)tile;
for (i = 0; keep_doing && i < OVCC_TILE_SIDE_NUM; i++) {
OVCCPosition npos = *pos;
gint r;
switch (i) {
case OVCC_TILE_SIDE_POS_TOP: npos.y--; break;
case OVCC_TILE_SIDE_POS_BOTTOM: npos.y++; break;
case OVCC_TILE_SIDE_POS_LEFT: npos.x--; break;
case OVCC_TILE_SIDE_POS_RIGHT: npos.x++; break;
}
for (r = 0; keep_doing && r < OVCC_TILE_SIDE_NUM; r++) {
if (ovcc_board_add_tile_check (board, new_tile, &npos)) {
keep_doing = FALSE;
} else {
ovcc_tile_rotate (new_tile, 1);
}
}
}
return keep_doing;
}
/**
* ovcc_board_is_tile_placable:
* @board: An #OVCCBoard
* @tile: An #OVCCTile
*
* Checks whether a given tile can be placed on the board.
*
* Returns: %TRUE is the tile can be placed, %FALSE otherwise.
*/
gboolean
ovcc_board_is_tile_placeable (OVCCBoard *board,
OVCCTile *tile)
{
g_return_val_if_fail (OVCC_IS_BOARD (board), FALSE);
g_return_val_if_fail (OVCC_IS_TILE (tile), FALSE);
return ovcc_board_foreach (board, do_try_place_tile, tile) == FALSE;
}
/**
* ovcc_board_get_bounds:
* @board: An #OVCCBoard
* @left: Return location for the farest left coordinate of the board, or NULL
* @top: Return location for the farest top coordinate, or NULL
* @right: Return location for the farest right coordinate, or NULL
* @bottom: Return location for the farest bottom coordinate, or NULL
* @width: Return location for the width of the board, or NULL
* @height: Return location for the height of the board, or NULL
*
* Gets the bounding "box" of filled positions on the board.
*/
void
ovcc_board_get_bounds (const OVCCBoard *board,
gint *left,
gint *top,
gint *right,
gint *bottom,
gint *width,
gint *height)
{
#define RET_PARAM(var, val) \
if ((var) != NULL) { *(var) = (val); }
RET_PARAM (left, board->priv->leftedge)
RET_PARAM (top, board->priv->topedge)
RET_PARAM (right, board->priv->rightedge + 1)
RET_PARAM (bottom, board->priv->bottomedge + 1)
RET_PARAM (width, ABS (board->priv->leftedge - board->priv->rightedge) + 1)
RET_PARAM (height, ABS (board->priv->topedge - board->priv->bottomedge) + 1)
#undef RET_PARAM
}
struct CBForeachData {
OVCCBoard *board;
OVCCBoardForeachFunc func;
gpointer data;
gboolean keep_doing;
};
static void
hash_table_foreach_cb (gpointer key,
gpointer value,
gpointer data)
{
struct CBForeachData *fdata = data;
if (fdata->keep_doing) {
OVCCPosition *pos = key;
OVCCTile *tile = value;
fdata->keep_doing = fdata->func (fdata->board, pos, tile, fdata->data);
}
}
/**
* ovcc_board_foreach:
* @board: An #OVCCBoard.
* @func: An #OVCCBoardForeachFunc function to be applied on each tile
* of the board.
* @data: Arbitrary data to pass to @func.
*
* This function calls @func for each tile in the board, giving the tile, its
* position and @data as user-data.
*
* Returns: the return value of the last iteration of @func. In other words,
* %TRUE if the callback never stopped iterating, %FALSE otherwise.
*/
gboolean
ovcc_board_foreach (OVCCBoard *board,
OVCCBoardForeachFunc func,
gpointer data)
{
struct CBForeachData fdata;
fdata.board = board;
fdata.func = func;
fdata.data = data;
fdata.keep_doing = TRUE;
g_hash_table_foreach (board->priv->board, hash_table_foreach_cb, &fdata);
return fdata.keep_doing;
}
/**
* ovcc_board_dump:
* @board: The #OVCCBoard to dump.
*
* Outputs a representation of the given board through the GLib's print
* handler. See manual of g_print() for more details.
*/
void
ovcc_board_dump (OVCCBoard *board)
{
gint i, j;
gint line;
guint mask;
OVCCPosition pos;
/* Output the position numbers */
g_print (" ");
for (j = board->priv->leftedge; j <= board->priv->rightedge; j++) {
g_print ("|%3i |", j);
}
g_print ("\n");
for (i = board->priv->topedge; i <= board->priv->bottomedge; i++) {
pos.y = i;
for (line = -1; line < 6; line++) {
switch (line) {
case 0:
mask = OVCC_TILE_DUMP_TOP;
break;
case 1:
mask = OVCC_TILE_DUMP_MIDDLE_TOP;
break;
case 2:
mask = OVCC_TILE_DUMP_MIDDLE;
break;
case 3:
mask = OVCC_TILE_DUMP_MIDDLE_BOTTOM;
break;
case 4:
mask = OVCC_TILE_DUMP_BOTTOM;
break;
}
if (line == 2) {
g_print ("%3i ", i);
} else if (line == -1 || line == 5) {
g_print ("----");
} else {
g_print (" ");
}
for (j = board->priv->leftedge; j <= board->priv->rightedge; j++) {
pos.x = j;
if (ovcc_board_is_tile_there (board, &pos)) {
if (line == -1 || line == 5) {
g_print ("-------");
} else {
g_print ("|");
ovcc_tile_dump (ovcc_board_get_tile (board, &pos), mask);
g_print ("|");
}
} else {
g_print (" ");
}
}
g_print ("\n");
}
}
/* Output the position numbers */
g_print (" ");
for (j = board->priv->leftedge; j <= board->priv->rightedge; j++) {
g_print ("|%3i |", j);
}
g_print ("\n");
}
/*
*
* Copyright (C) 2009 Colomban Wendling <ban@herbesfolles.org>
* Jonathan Michalon <studios.chalmion@no-log.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*