Fix from James H: the shared code between drawing and printing
[sgt/puzzles] / loopy.c
diff --git a/loopy.c b/loopy.c
index a52065e..488b507 100644 (file)
--- a/loopy.c
+++ b/loopy.c
@@ -102,6 +102,7 @@ enum {
     COL_HIGHLIGHT,
     COL_MISTAKE,
     COL_SATISFIED,
+    COL_FAINT,
     NCOLOURS
 };
 
@@ -844,6 +845,14 @@ static float *game_colours(frontend *fe, int *ncolours)
     ret[COL_SATISFIED * 3 + 1] = 0.0F;
     ret[COL_SATISFIED * 3 + 2] = 0.0F;
 
+    /* We want the faint lines to be a bit darker than the background.
+     * Except if the background is pretty dark already; then it ought to be a
+     * bit lighter.  Oy vey.
+     */
+    ret[COL_FAINT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
+    ret[COL_FAINT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
+    ret[COL_FAINT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.9F;
+
     *ncolours = NCOLOURS;
     return ret;
 }
@@ -1300,6 +1309,7 @@ static int can_colour_face(grid *g, char* board, int face_index,
     int i, j;
     grid_face *test_face = g->faces + face_index;
     grid_face *starting_face, *current_face;
+    grid_dot *starting_dot;
     int transitions;
     int current_state, s; /* booleans: equal or not-equal to 'colour' */
     int found_same_coloured_neighbour = FALSE;
@@ -1344,17 +1354,39 @@ static int can_colour_face(grid *g, char* board, int face_index,
      *     test_face->dots[i]->faces[j]
      * We assume dots go clockwise around the test face,
      * and faces go clockwise around dots. */
+
+    /*
+     * The end condition is slightly fiddly. In sufficiently strange
+     * degenerate grids, our test face may be adjacent to the same
+     * other face multiple times (typically if it's the exterior
+     * face). Consider this, in particular:
+     * 
+     *   +--+
+     *   |  |
+     *   +--+--+
+     *   |  |  |
+     *   +--+--+
+     * 
+     * The bottom left face there is adjacent to the exterior face
+     * twice, so we can't just terminate our iteration when we reach
+     * the same _face_ we started at. Furthermore, we can't
+     * condition on having the same (i,j) pair either, because
+     * several (i,j) pairs identify the bottom left contiguity with
+     * the exterior face! We canonicalise the (i,j) pair by taking
+     * one step around before we set the termination tracking.
+     */
+
     i = j = 0;
-    starting_face = test_face->dots[0]->faces[0];
-    if (starting_face == test_face) {
+    current_face = test_face->dots[0]->faces[0];
+    if (current_face == test_face) {
         j = 1;
-        starting_face = test_face->dots[0]->faces[1];
+        current_face = test_face->dots[0]->faces[1];
     }
-    current_face = starting_face;
     transitions = 0;
     current_state = (FACE_COLOUR(current_face) == colour);
-
-    do {
+    starting_dot = NULL;
+    starting_face = NULL;
+    while (TRUE) {
         /* Advance to next face.
          * Need to loop here because it might take several goes to
          * find it. */
@@ -1385,13 +1417,22 @@ static int can_colour_face(grid *g, char* board, int face_index,
         /* (i,j) are now advanced to next face */
         current_face = test_face->dots[i]->faces[j];
         s = (FACE_COLOUR(current_face) == colour);
-        if (s != current_state) {
-            ++transitions;
-            current_state = s;
-            if (transitions > 2)
-                return FALSE; /* no point in continuing */
+       if (!starting_dot) {
+           starting_dot = test_face->dots[i];
+           starting_face = current_face;
+           current_state = s;
+       } else {
+           if (s != current_state) {
+               ++transitions;
+               current_state = s;
+               if (transitions > 2)
+                   break;
+           }
+           if (test_face->dots[i] == starting_dot &&
+               current_face == starting_face)
+               break;
         }
-    } while (current_face != starting_face);
+    }
 
     return (transitions == 2) ? TRUE : FALSE;
 }
@@ -1504,6 +1545,7 @@ static void add_full_clues(game_state *state, random_state *rs)
     face_scores = snewn(num_faces, struct face_score);
     for (i = 0; i < num_faces; i++) {
         face_scores[i].random = random_bits(rs, 31);
+        face_scores[i].black_score = face_scores[i].white_score = 0;
     }
     
     /* Colour a random, finite face white.  The infinite face is implicitly
@@ -1555,12 +1597,11 @@ static void add_full_clues(game_state *state, random_state *rs)
         struct face_score *fs_white, *fs_black;
         int c_lightable = count234(lightable_faces_sorted);
         int c_darkable = count234(darkable_faces_sorted);
-        if (c_lightable == 0) {
-            /* No more lightable faces.  Because of how the algorithm
-             * works, there should be no more darkable faces either. */
-            assert(c_darkable == 0);
+        if (c_lightable == 0 && c_darkable == 0) {
+            /* No more faces we can use at all. */
             break;
         }
+       assert(c_lightable != 0 && c_darkable != 0);
 
         fs_white = (struct face_score *)index234(lightable_faces_sorted, 0);
         fs_black = (struct face_score *)index234(darkable_faces_sorted, 0);
@@ -3181,6 +3222,10 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
            button_char = 'y';
            break;
          case LINE_YES:
+#ifdef STYLUS_BASED
+           button_char = 'n';
+           break;
+#endif
          case LINE_NO:
            button_char = 'u';
            break;
@@ -3195,6 +3240,10 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
            button_char = 'n';
            break;
          case LINE_NO:
+#ifdef STYLUS_BASED
+           button_char = 'y';
+           break;
+#endif
          case LINE_YES:
            button_char = 'u';
            break;
@@ -3223,6 +3272,8 @@ static game_state *execute_move(game_state *state, char *move)
 
     while (*move) {
         i = atoi(move);
+        if (i < 0 || i >= newstate->game_grid->num_edges)
+            goto fail;
         move += strspn(move, "1234567890");
         switch (*(move++)) {
          case 'y':
@@ -3467,7 +3518,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         else if (state->lines[i] == LINE_UNKNOWN)
             line_colour = COL_LINEUNKNOWN;
         else if (state->lines[i] == LINE_NO)
-            line_colour = COL_BACKGROUND;
+            line_colour = COL_FAINT;
         else if (ds->flashing)
             line_colour = COL_HIGHLIGHT;
         else
@@ -3482,7 +3533,16 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         ymin = min(y1, y2);
         ymax = max(y1, y2);
 
-        if (line_colour != COL_BACKGROUND) {
+       if (line_colour == COL_FAINT) {
+            static int draw_faint_lines = -1;
+            if (draw_faint_lines < 0) {
+               char *env = getenv("LOOPY_FAINT_LINES");
+               draw_faint_lines = (!env || (env[0] == 'y' ||
+                                            env[0] == 'Y'));
+            }
+           if (draw_faint_lines)
+               draw_line(dr, x1, y1, x2, y2, line_colour);
+       } else {
             /* (dx, dy) points roughly from (x1, y1) to (x2, y2).
              * The line is then "fattened" in a (roughly) perpendicular
              * direction to create a thin rectangle. */
@@ -3549,7 +3609,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize)
     game_drawstate ads, *ds = &ads;
     grid *g = state->game_grid;
 
-    game_set_size(dr, ds, NULL, tilesize);
+    ds->tilesize = tilesize;
 
     for (i = 0; i < g->num_dots; i++) {
         int x, y;