]> git.cworth.org Git - dvonn/blobdiff - dvonn.c
Add visual indication of selected stack
[dvonn] / dvonn.c
diff --git a/dvonn.c b/dvonn.c
index 43de664e9e362d9aa56d57ef9847d470a7f9e963..ebea5e6cab115c4f2333b49391ed6ad5437d90f6 100644 (file)
--- a/dvonn.c
+++ b/dvonn.c
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -34,15 +34,15 @@ typedef struct {
     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;
 
@@ -72,7 +72,7 @@ on_delete_event_quit (GtkWidget  *widget,
 
 /* 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;
 
@@ -80,6 +80,63 @@ loa_game_update_windows (loa_game_t *game)
        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,
@@ -87,35 +144,51 @@ on_button_press_event (GtkWidget   *widget,
 {
     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;
     }
        
@@ -124,14 +197,14 @@ on_button_press_event (GtkWidget  *widget,
                          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);
     }
 
@@ -153,7 +226,7 @@ on_expose_event_draw (GtkWidget             *widget,
 {
     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;
 
@@ -199,7 +272,28 @@ on_expose_event_draw (GtkWidget            *widget,
                            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);
@@ -212,7 +306,7 @@ on_expose_event_draw (GtkWidget             *widget,
 }
 
 static void
-loa_game_init (loa_game_t *game)
+dvonn_game_init (dvonn_game_t *game)
 {
     game->views = NULL;
     game->num_views = 0;
@@ -223,7 +317,7 @@ loa_game_init (loa_game_t *game)
 }
 
 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;
@@ -233,7 +327,7 @@ view_init (view_t *view, loa_game_t *game, GtkWidget *window)
 }
 
 static void
-loa_game_create_view (loa_game_t *game)
+dvonn_game_create_view (dvonn_game_t *game)
 {
     view_t *view;
     GtkWidget *window;
@@ -280,15 +374,15 @@ loa_game_create_view (loa_game_t *game)
 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 ();