Stop the analysis pass in Loopy's redraw routine from being
[sgt/puzzles] / net.c
diff --git a/net.c b/net.c
index 7124636..2e19f1f 100644 (file)
--- a/net.c
+++ b/net.c
@@ -232,7 +232,7 @@ static void decode_params(game_params *ret, char const *string)
            ret->wrapping = TRUE;
        } else if (*p == 'b') {
            p++;
-            ret->barrier_probability = atof(p);
+            ret->barrier_probability = (float)atof(p);
            while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
        } else if (*p == 'a') {
             p++;
@@ -950,8 +950,10 @@ static void perturb(int w, int h, unsigned char *tiles, int wrapping,
     }
     sfree(perim2);
 
-    if (i == nperim)
+    if (i == nperim) {
+        sfree(perimeter);
        return;                        /* nothing we can do! */
+    }
 
     /*
      * Now we've constructed a new link, we need to find the entire
@@ -1403,13 +1405,55 @@ static char *new_game_desc(game_params *params, random_state *rs,
 
     /*
      * Now shuffle the grid.
+     * 
+     * In order to avoid accidentally generating an already-solved
+     * grid, we will reshuffle as necessary to ensure that at least
+     * one edge has a mismatched connection.
+     *
+     * This can always be done, since validate_params() enforces a
+     * grid area of at least 2 and our generator never creates
+     * either type of rotationally invariant tile (cross and
+     * blank). Hence there must be at least one edge separating
+     * distinct tiles, and it must be possible to find orientations
+     * of those tiles such that one tile is trying to connect
+     * through that edge and the other is not.
+     * 
+     * (We could be more subtle, and allow the shuffle to generate
+     * a grid in which all tiles match up locally and the only
+     * criterion preventing the grid from being already solved is
+     * connectedness. However, that would take more effort, and
+     * it's easier to simply make sure every grid is _obviously_
+     * not solved.)
      */
-    for (y = 0; y < h; y++) {
-       for (x = 0; x < w; x++) {
-           int orig = index(params, tiles, x, y);
-           int rot = random_upto(rs, 4);
-           index(params, tiles, x, y) = ROT(orig, rot);
-       }
+    while (1) {
+        int mismatches;
+
+        for (y = 0; y < h; y++) {
+            for (x = 0; x < w; x++) {
+                int orig = index(params, tiles, x, y);
+                int rot = random_upto(rs, 4);
+                index(params, tiles, x, y) = ROT(orig, rot);
+            }
+        }
+
+        mismatches = 0;
+        /*
+         * I can't even be bothered to check for mismatches across
+         * a wrapping edge, so I'm just going to enforce that there
+         * must be a mismatch across a non-wrapping edge, which is
+         * still always possible.
+         */
+        for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
+            if (x+1 < w && ((ROT(index(params, tiles, x, y), 2) ^ 
+                             index(params, tiles, x+1, y)) & L))
+                mismatches++;
+            if (y+1 < h && ((ROT(index(params, tiles, x, y), 2) ^ 
+                             index(params, tiles, x, y+1)) & U))
+                mismatches++;
+        }
+
+        if (mismatches > 0)
+            break;
     }
 
     /*
@@ -1608,7 +1652,7 @@ static game_state *new_game(midend *me, game_params *params, char *desc)
             if (!(barrier(state, x, 0) & U) ||
                 !(barrier(state, x, state->height-1) & D))
                 state->wrapping = TRUE;
-        for (y = 0; y < state->width; y++)
+        for (y = 0; y < state->height; y++)
             if (!(barrier(state, 0, y) & L) ||
                 !(barrier(state, state->width-1, y) & R))
                 state->wrapping = TRUE;
@@ -1740,6 +1784,11 @@ static char *solve_game(game_state *state, game_state *currstate,
     return ret;
 }
 
+static int game_can_format_as_text_now(game_params *params)
+{
+    return TRUE;
+}
+
 static char *game_text_format(game_state *state)
 {
     return NULL;
@@ -1873,7 +1922,7 @@ struct game_drawstate {
  * Process a move.
  */
 static char *interpret_move(game_state *state, game_ui *ui,
-                           game_drawstate *ds, int x, int y, int button)
+                           const game_drawstate *ds, int x, int y, int button)
 {
     char *nullret;
     int tx = -1, ty = -1, dir = 0;
@@ -1931,7 +1980,11 @@ static char *interpret_move(game_state *state, game_ui *ui,
              * Middle button never drags: it only toggles the lock.
              */
             action = TOGGLE_LOCK;
-        } else if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
+        } else if (button == LEFT_BUTTON
+#ifndef STYLUS_BASED
+                   || button == RIGHT_BUTTON /* (see above) */
+#endif
+                  ) {
             /*
              * Otherwise, we note down the start point for a drag.
              */
@@ -1941,7 +1994,11 @@ static char *interpret_move(game_state *state, game_ui *ui,
             ui->dragstarty = y % TILE_SIZE;
             ui->dragged = FALSE;
             return nullret;            /* no actual action */
-        } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
+        } else if (button == LEFT_DRAG
+#ifndef STYLUS_BASED
+                   || button == RIGHT_DRAG
+#endif
+                  ) {
             /*
              * Find the new drag point and see if it necessitates a
              * rotation.
@@ -1990,7 +2047,11 @@ static char *interpret_move(game_state *state, game_ui *ui,
                 ui->dragstarty = yC;
                 ui->dragged = TRUE;
             }
-        } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
+        } else if (button == LEFT_RELEASE
+#ifndef STYLUS_BASED
+                   || button == RIGHT_RELEASE
+#endif
+                  ) {
             if (!ui->dragged) {
                 /*
                  * There was a click but no perceptible drag:
@@ -2014,8 +2075,7 @@ static char *interpret_move(game_state *state, game_ui *ui,
 
 #endif /* USE_DRAGGING */
 
-    } else if (button == CURSOR_UP || button == CURSOR_DOWN ||
-              button == CURSOR_RIGHT || button == CURSOR_LEFT) {
+    } else if (IS_CURSOR_MOVE(button)) {
         switch (button) {
           case CURSOR_UP:       dir = U; break;
           case CURSOR_DOWN:     dir = D; break;
@@ -2030,12 +2090,12 @@ static char *interpret_move(game_state *state, game_ui *ui,
     } else if (button == 'a' || button == 's' || button == 'd' ||
               button == 'A' || button == 'S' || button == 'D' ||
                button == 'f' || button == 'F' ||
-              button == CURSOR_SELECT) {
+               IS_CURSOR_SELECT(button)) {
        tx = ui->cur_x;
        ty = ui->cur_y;
        if (button == 'a' || button == 'A' || button == CURSOR_SELECT)
            action = ROTATE_LEFT;
-       else if (button == 's' || button == 'S')
+       else if (button == 's' || button == 'S' || button == CURSOR_SELECT2)
            action = TOGGLE_LOCK;
        else if (button == 'd' || button == 'D')
            action = ROTATE_RIGHT;
@@ -2137,7 +2197,7 @@ static char *interpret_move(game_state *state, game_ui *ui,
 static game_state *execute_move(game_state *from, char *move)
 {
     game_state *ret;
-    int tx, ty, n, noanim, orig;
+    int tx = -1, ty = -1, n, noanim, orig;
 
     ret = dup_game(from);
 
@@ -2186,6 +2246,7 @@ static game_state *execute_move(game_state *from, char *move)
        }
     }
     if (!noanim) {
+        if (tx == -1 || ty == -1) { free_game(ret); return NULL; }
        ret->last_rotate_x = tx;
        ret->last_rotate_y = ty;
     }
@@ -2316,8 +2377,8 @@ static float *game_colours(frontend *fe, int *ncolours)
     return ret;
 }
 
-static void draw_thick_line(drawing *dr, int x1, int y1, int x2, int y2,
-                            int colour)
+static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2,
+                            int colour)
 {
     draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE);
     draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE);
@@ -2453,9 +2514,9 @@ static void draw_tile(drawing *dr, game_state *state, game_drawstate *ds,
             ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
             ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
             MATMUL(tx, ty, matrix, ex, ey);
-            draw_thick_line(dr, bx+(int)cx, by+(int)cy,
-                           bx+(int)(cx+tx), by+(int)(cy+ty),
-                            COL_WIRE);
+            draw_filled_line(dr, bx+(int)cx, by+(int)cy,
+                            bx+(int)(cx+tx), by+(int)(cy+ty),
+                            COL_WIRE);
         }
     }
     for (dir = 1; dir < 0x10; dir <<= 1) {
@@ -2803,6 +2864,11 @@ static float game_flash_length(game_state *oldstate,
     return 0.0F;
 }
 
+static int game_status(game_state *state)
+{
+    return state->completed ? +1 : 0;
+}
+
 static int game_timing_state(game_state *state, game_ui *ui)
 {
     return TRUE;
@@ -2816,8 +2882,8 @@ static void game_print_size(game_params *params, float *x, float *y)
      * I'll use 8mm squares by default.
      */
     game_compute_size(params, 800, &pw, &ph);
-    *x = pw / 100.0;
-    *y = ph / 100.0;
+    *x = pw / 100.0F;
+    *y = ph / 100.0F;
 }
 
 static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
@@ -2963,7 +3029,7 @@ const struct game thegame = {
     dup_game,
     free_game,
     TRUE, solve_game,
-    FALSE, game_text_format,
+    FALSE, game_can_format_as_text_now, game_text_format,
     new_ui,
     free_ui,
     encode_ui,
@@ -2978,6 +3044,7 @@ const struct game thegame = {
     game_redraw,
     game_anim_length,
     game_flash_length,
+    game_status,
     TRUE, FALSE, game_print_size, game_print,
     TRUE,                             /* wants_statusbar */
     FALSE, game_timing_state,