chiark / gitweb /
Tents: mark squares as non-tents with {Shift,Control}-cursor keys.
[sgt-puzzles.git] / grid.c
diff --git a/grid.c b/grid.c
index aba11570742e45e123631dd4f80311cc18ae8fbc..6a90c9942e3a44edff78696e9c6ffd00e554cdce 100644 (file)
--- a/grid.c
+++ b/grid.c
@@ -1069,7 +1069,7 @@ void grid_find_incentre(grid_face *f)
                     eq[2] = eqs[0][3]*eqs[1][2] - eqs[1][3]*eqs[0][2];
 
                     /* Parametrise x and y in terms of some t. */
-                    if (abs(eq[0]) < abs(eq[1])) {
+                    if (fabs(eq[0]) < fabs(eq[1])) {
                         /* Parameter is x. */
                         xt[0] = 1; xt[1] = 0;
                         yt[0] = -eq[0]/eq[1]; yt[1] = eq[2]/eq[1];
@@ -2631,6 +2631,8 @@ static void grid_size_penrose(int width, int height,
     *yextent = l * height;
 }
 
+static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */
+
 static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
 {
     int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
@@ -2638,32 +2640,48 @@ static char *grid_new_desc_penrose(grid_type type, int width, int height, random
     int inner_radius;
     char gd[255];
     int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
+    grid *g;
 
-    /* We want to produce a random bit of penrose tiling, so we calculate
-     * a random offset (within the patch that penrose.c calculates for us)
-     * and an angle (multiple of 36) to rotate the patch. */
+    while (1) {
+        /* We want to produce a random bit of penrose tiling, so we
+         * calculate a random offset (within the patch that penrose.c
+         * calculates for us) and an angle (multiple of 36) to rotate
+         * the patch. */
 
-    penrose_calculate_size(which, tilesize, width, height,
-                           &outer_radius, &startsz, &depth);
+        penrose_calculate_size(which, tilesize, width, height,
+                               &outer_radius, &startsz, &depth);
 
-    /* Calculate radius of (circumcircle of) patch, subtract from
-     * radius calculated. */
-    inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
+        /* Calculate radius of (circumcircle of) patch, subtract from
+         * radius calculated. */
+        inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
 
-    /* Pick a random offset (the easy way: choose within outer square,
-     * discarding while it's outside the circle) */
-    do {
-        xoff = random_upto(rs, 2*inner_radius) - inner_radius;
-        yoff = random_upto(rs, 2*inner_radius) - inner_radius;
-    } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
+        /* Pick a random offset (the easy way: choose within outer
+         * square, discarding while it's outside the circle) */
+        do {
+            xoff = random_upto(rs, 2*inner_radius) - inner_radius;
+            yoff = random_upto(rs, 2*inner_radius) - inner_radius;
+        } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
 
-    aoff = random_upto(rs, 360/36) * 36;
+        aoff = random_upto(rs, 360/36) * 36;
 
-    debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
-           tilesize, width, height, outer_radius, inner_radius));
-    debug(("    -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
+        debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
+               tilesize, width, height, outer_radius, inner_radius));
+        debug(("    -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
 
-    sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
+        sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
+
+        /*
+         * Now test-generate our grid, to make sure it actually
+         * produces something.
+         */
+        g = grid_new_penrose(width, height, which, gd);
+        if (g) {
+            grid_free(g);
+            break;
+        }
+        /* If not, go back to the top of this while loop and try again
+         * with a different random offset. */
+    }
 
     return dupstr(gd);
 }
@@ -2674,6 +2692,7 @@ static char *grid_validate_desc_penrose(grid_type type, int width, int height,
     int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
     double outer_radius;
     int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
+    grid *g;
 
     if (!desc)
         return "Missing grid description string.";
@@ -2690,6 +2709,15 @@ static char *grid_validate_desc_penrose(grid_type type, int width, int height,
     if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360)
         return "Angle offset out of bounds.";
 
+    /*
+     * Test-generate to ensure these parameters don't end us up with
+     * no grid at all.
+     */
+    g = grid_new_penrose(width, height, which, desc);
+    if (!g)
+        return "Patch coordinates do not identify a usable grid fragment";
+    grid_free(g);
+
     return NULL;
 }
 
@@ -2763,7 +2791,22 @@ static grid *grid_new_penrose(int width, int height, int which, const char *desc
     debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
            g->num_faces, g->num_faces/height, g->num_faces/width));
 
+    /*
+     * Return NULL if we ended up with an empty grid, either because
+     * the initial generation was over too small a rectangle to
+     * encompass any face or because grid_trim_vigorously ended up
+     * removing absolutely everything.
+     */
+    if (g->num_faces == 0 || g->num_dots == 0) {
+        grid_free(g);
+        return NULL;
+    }
     grid_trim_vigorously(g);
+    if (g->num_faces == 0 || g->num_dots == 0) {
+        grid_free(g);
+        return NULL;
+    }
+
     grid_make_consistent(g);
 
     /*