/*
- * Copyright (C) 2008 Carl Worth
+ * Copyright (C) 2009 Carl Worth
*
* 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
int cell_size;
} layout_t;
-typedef struct _loa_game loa_game_t;
+typedef struct _dvonn_game dvonn_game_t;
typedef struct {
- loa_game_t *game;
+ dvonn_game_t *game;
GtkWidget *window;
layout_t layout;
} view_t;
-struct _loa_game {
+struct _dvonn_game {
view_t **views;
int num_views;
/* XXX: This really should have an interest rectangle. */
static void
-loa_game_update_windows (loa_game_t *game)
+dvonn_game_update_windows (dvonn_game_t *game)
{
int i;
gtk_widget_queue_draw (game->views[i]->window);
}
+/* Convert from a board index to a device-pixel coordinate pair, (at
+ * the center of the ring). */
+static void
+layout_board_to_device (layout_t *layout, double *x_ret, double *y_ret)
+{
+ double x = *x_ret, y = *y_ret;
+
+ *x_ret = layout->x_offset + layout->cell_size *
+ (x + (y - DVONN_BOARD_Y_SIZE/2)/2.0 + 0.5);
+ *y_ret = layout->y_offset + layout->cell_size * (M_SQRT1_2 * y + 0.5);
+}
+
+/* Convert from a device-pixel coordinate pair to a board index. */
+static void
+layout_device_to_board (layout_t *layout, int *x_ret, int *y_ret)
+{
+ int x = *x_ret, y = *y_ret;
+ int x1, y1, x2, y2;
+ double x1_dev, y1_dev, x2_dev, y2_dev;
+ double dx, dy, d1, d2;
+
+ /* Because the vertical spacing between adjacent rows is less than
+ * layout->cell_size, the simple calculations here give us two
+ * candidate (x,y) pairs. We then choose the correct one based on
+ * a distance calculation.
+ */
+ y1 = (y - layout->y_offset) / (layout->cell_size * M_SQRT1_2);
+ x1 = (double) (x - layout->x_offset) / layout->cell_size - (y1 - DVONN_BOARD_Y_SIZE/2)/2.0;
+
+ y2 = y1 - 1;
+ x2 = (double) (x - layout->x_offset) / layout->cell_size - (y2 - DVONN_BOARD_Y_SIZE/2)/2.0;
+
+ x1_dev = x1;
+ y1_dev = y1;
+ layout_board_to_device (layout, &x1_dev, &y1_dev);
+
+ x2_dev = x2;
+ y2_dev = y2;
+ layout_board_to_device (layout, &x2_dev, &y2_dev);
+
+ dx = x - x1_dev;
+ dy = y - y1_dev;
+ d1 = sqrt (dx*dx + dy*dy);
+
+ dx = x - x2_dev;
+ dy = y - y2_dev;
+ d2 = sqrt (dx*dx + dy*dy);
+
+ if (d1 < d2) {
+ *x_ret = x1;
+ *y_ret = y1;
+ } else {
+ *x_ret = x2;
+ *y_ret = y2;
+ }
+}
+
static gboolean
on_button_press_event (GtkWidget *widget,
GdkEventButton *event,
{
view_t *view = user_data;
layout_t *layout = &view->layout;
- loa_game_t *game = view->game;
+ dvonn_game_t *game = view->game;
int x, y;
char *error;
- x = (event->x - layout->x_offset) / layout->cell_size;
- y = (event->y - layout->y_offset) / layout->cell_size;
+ x = event->x;
+ y = event->y;
+ layout_device_to_board (layout, &x, &y);
+
+ /* Do nothing for out-of-bounds clicks */
+ if (x < 0 || x >= DVONN_BOARD_X_SIZE ||
+ y < 0 || y >= DVONN_BOARD_Y_SIZE)
+ {
+ return TRUE;
+ }
+
+ /* Nor for cells which have array entries that are invalid. */
+ if (game->board.cells[x][y].type == DVONN_CELL_INVALID)
+ return TRUE;
+
+ if (game->board.phase == DVONN_PHASE_PLACEMENT) {
+ if (dvonn_board_place (&game->board, x, y, &error))
+ dvonn_game_update_windows (game);
+ else
+ printf ("Illegal placement %c%d: %s\n",
+ 'a' + x,
+ y + 1,
+ error);
+
+ return TRUE;
+ }
if (! game->has_selected) {
if (game->board.cells[x][y].type == game->board.player) {
game->has_selected = TRUE;
game->selected_x = x;
game->selected_y = y;
- loa_game_update_windows (game);
+ dvonn_game_update_windows (game);
}
return TRUE;
}
- /* Do nothing for out-of-bounds clicks */
- if (x < 0 || x >= DVONN_BOARD_X_SIZE ||
- y < 0 || y >= DVONN_BOARD_Y_SIZE)
- {
- return TRUE;
- }
-
- if (x == game->selected_x &&
- y == game->selected_y)
+ if (x == game->selected_x && y == game->selected_y)
{
game->has_selected = FALSE;
- loa_game_update_windows (game);
+ dvonn_game_update_windows (game);
return TRUE;
}
x, y, &error))
{
game->has_selected = FALSE;
- loa_game_update_windows (game);
+ dvonn_game_update_windows (game);
return TRUE;
} else {
printf ("Illegal move %c%d%c%d: %s\n",
'a' + game->selected_x,
- DVONN_BOARD_Y_SIZE - game->selected_y,
+ game->selected_y + 1,
'a' + x,
- DVONN_BOARD_Y_SIZE - y,
+ y + 1,
error);
}
{
view_t *view = user_data;
layout_t *layout = &view->layout;
- loa_game_t *game = view->game;
+ dvonn_game_t *game = view->game;
cairo_t *cr;
int x, y;
x + (y - DVONN_BOARD_Y_SIZE/2) / 2.0,
M_SQRT1_2 * y);
ring_path (cr);
- cairo_set_source_rgba (cr, 0.0, 0.0, 0.2, 0.1);
+ switch (cell.type) {
+ case DVONN_CELL_WHITE:
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+ break;
+ case DVONN_CELL_BLACK:
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+ break;
+ case DVONN_CELL_RED:
+ cairo_set_source_rgb (cr, 0.8, 0.2, 0.2); /* red */
+ break;
+ case DVONN_CELL_EMPTY:
+ default:
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.2, 0.1);
+ break;
+ }
+ if (game->has_selected &&
+ x == game->selected_x &&
+ y == game->selected_y)
+ {
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgba (cr, 0.2, 0.2, 1.0, 0.4);
+ }
cairo_fill (cr);
cairo_restore (cr);
}
static void
-loa_game_init (loa_game_t *game)
+dvonn_game_init (dvonn_game_t *game)
{
game->views = NULL;
game->num_views = 0;
}
static void
-view_init (view_t *view, loa_game_t *game, GtkWidget *window)
+view_init (view_t *view, dvonn_game_t *game, GtkWidget *window)
{
view->game = game;
view->window = window;
}
static void
-loa_game_create_view (loa_game_t *game)
+dvonn_game_create_view (dvonn_game_t *game)
{
view_t *view;
GtkWidget *window;
int
main (int argc, char *argv[])
{
- loa_game_t game;
+ dvonn_game_t game;
- loa_game_init (&game);
+ dvonn_game_init (&game);
gtk_init (&argc, &argv);
/* Create two views of the game (one for each player) */
- loa_game_create_view (&game);
- loa_game_create_view (&game);
+ dvonn_game_create_view (&game);
+ dvonn_game_create_view (&game);
gtk_main ();