Missed a vital semicolon off the Cygwin version.c makefile fragment.
[sgt/puzzles] / solo.c
diff --git a/solo.c b/solo.c
index dc3bbc4..c9c6412 100644 (file)
--- a/solo.c
+++ b/solo.c
@@ -116,7 +116,7 @@ static game_params *default_params(void)
 
     ret->c = ret->r = 3;
     ret->symm = SYMM_ROT2;            /* a plausible default */
-    ret->diff = DIFF_SIMPLE;           /* so is this */
+    ret->diff = DIFF_BLOCK;           /* so is this */
 
     return ret;
 }
@@ -141,9 +141,11 @@ static int game_fetch_preset(int i, char **name, game_params **params)
     } presets[] = {
         { "2x2 Trivial", { 2, 2, SYMM_ROT2, DIFF_BLOCK } },
         { "2x3 Basic", { 2, 3, SYMM_ROT2, DIFF_SIMPLE } },
+        { "3x3 Trivial", { 3, 3, SYMM_ROT2, DIFF_BLOCK } },
         { "3x3 Basic", { 3, 3, SYMM_ROT2, DIFF_SIMPLE } },
         { "3x3 Intermediate", { 3, 3, SYMM_ROT2, DIFF_INTERSECT } },
         { "3x3 Advanced", { 3, 3, SYMM_ROT2, DIFF_SET } },
+        { "3x3 Unreasonable", { 3, 3, SYMM_ROT2, DIFF_RECURSIVE } },
         { "3x4 Basic", { 3, 4, SYMM_ROT2, DIFF_SIMPLE } },
         { "4x4 Basic", { 4, 4, SYMM_ROT2, DIFF_SIMPLE } },
     };
@@ -163,6 +165,7 @@ static game_params *decode_params(char const *string)
 
     ret->c = ret->r = atoi(string);
     ret->symm = SYMM_ROT2;
+    ret->diff = DIFF_BLOCK;
     while (*string && isdigit((unsigned char)*string)) string++;
     if (*string == 'x') {
         string++;
@@ -193,6 +196,8 @@ static game_params *decode_params(char const *string)
                 string++, ret->diff = DIFF_INTERSECT;
             else if (*string == 'a')   /* advanced */
                 string++, ret->diff = DIFF_SET;
+            else if (*string == 'u')   /* unreasonable */
+                string++, ret->diff = DIFF_RECURSIVE;
         } else
             string++;                  /* eat unknown character */
     }
@@ -239,7 +244,7 @@ static config_item *game_configure(game_params *params)
 
     ret[3].name = "Difficulty";
     ret[3].type = C_CHOICES;
-    ret[3].sval = ":Trivial:Basic:Intermediate:Advanced";
+    ret[3].sval = ":Trivial:Basic:Intermediate:Advanced:Unreasonable";
     ret[3].ival = params->diff;
 
     ret[4].name = NULL;
@@ -1351,6 +1356,11 @@ static int symmetries(game_params *params, int x, int y, int *output, int s)
     return i;
 }
 
+struct game_aux_info {
+    int c, r;
+    digit *grid;
+};
+
 static char *new_game_seed(game_params *params, random_state *rs,
                           game_aux_info **aux)
 {
@@ -1363,7 +1373,7 @@ static char *new_game_seed(game_params *params, random_state *rs,
     char *seed;
     int coords[16], ncoords;
     int xlim, ylim;
-    int maxdiff;
+    int maxdiff, recursing;
 
     /*
      * Adjust the maximum difficulty level to be consistent with
@@ -1394,11 +1404,24 @@ static char *new_game_seed(game_params *params, random_state *rs,
         assert(ret == 1);
         assert(check_valid(c, r, grid));
 
+       /*
+        * Save the solved grid in the aux_info.
+        */
+       {
+           game_aux_info *ai = snew(game_aux_info);
+           ai->c = c;
+           ai->r = r;
+           ai->grid = snewn(cr * cr, digit);
+           memcpy(ai->grid, grid, cr * cr * sizeof(digit));
+           *aux = ai;
+       }
+
         /*
          * Now we have a solved grid, start removing things from it
          * while preserving solubility.
          */
         symmetry_limit(params, &xlim, &ylim, params->symm);
+       recursing = FALSE;
         while (1) {
             int x, y, i, j;
 
@@ -1435,6 +1458,8 @@ static char *new_game_seed(game_params *params, random_state *rs,
              * nsolve.
              */
             for (i = 0; i < nlocs; i++) {
+               int ret;
+
                 x = locs[i].x;
                 y = locs[i].y;
 
@@ -1443,7 +1468,12 @@ static char *new_game_seed(game_params *params, random_state *rs,
                 for (j = 0; j < ncoords; j++)
                     grid2[coords[2*j+1]*cr+coords[2*j]] = 0;
 
-                if (nsolve(c, r, grid2) <= maxdiff) {
+               if (recursing)
+                   ret = (rsolve(c, r, grid2, NULL, 2) == 1);
+               else
+                   ret = (nsolve(c, r, grid2) <= maxdiff);
+
+                if (ret) {
                     for (j = 0; j < ncoords; j++)
                         grid[coords[2*j+1]*cr+coords[2*j]] = 0;
                     break;
@@ -1452,15 +1482,22 @@ static char *new_game_seed(game_params *params, random_state *rs,
 
             if (i == nlocs) {
                 /*
-                 * There was nothing we could remove without destroying
-                 * solvability.
+                 * There was nothing we could remove without
+                 * destroying solvability. If we're trying to
+                 * generate a recursion-only grid and haven't
+                 * switched over to rsolve yet, we now do;
+                 * otherwise we give up.
                  */
-                break;
+               if (maxdiff == DIFF_RECURSIVE && !recursing) {
+                   recursing = TRUE;
+               } else {
+                   break;
+               }
             }
         }
 
         memcpy(grid2, grid, area);
-    } while (nsolve(c, r, grid2) != maxdiff);
+    } while (nsolve(c, r, grid2) < maxdiff);
 
     sfree(grid2);
     sfree(locs);
@@ -1516,7 +1553,8 @@ static char *new_game_seed(game_params *params, random_state *rs,
 
 static void game_free_aux_info(game_aux_info *aux)
 {
-    assert(!"Shouldn't happen");
+    sfree(aux->grid);
+    sfree(aux);
 }
 
 static char *validate_seed(game_params *params, char *seed)
@@ -1614,31 +1652,37 @@ static void free_game(game_state *state)
     sfree(state);
 }
 
-static game_state *solve_game(game_state *state, game_aux_info *aux,
+static game_state *solve_game(game_state *state, game_aux_info *ai,
                              char **error)
 {
     game_state *ret;
-    int c = state->c, r = state->r;
+    int c = state->c, r = state->r, cr = c*r;
     int rsolve_ret;
 
-    /*
-     * I could have stored the grid I invented in the game_aux_info
-     * and extracted it here where available, but it seems easier
-     * just to run my internal solver in all cases.
-     */
-
     ret = dup_game(state);
     ret->completed = ret->cheated = TRUE;
 
-    rsolve_ret = rsolve(c, r, ret->grid, NULL, 2);
+    /*
+     * If we already have the solution in the aux_info, save
+     * ourselves some time.
+     */
+    if (ai) {
+
+       assert(c == ai->c);
+       assert(r == ai->r);
+       memcpy(ret->grid, ai->grid, cr * cr * sizeof(digit));
 
-    if (rsolve_ret != 1) {
-       free_game(ret);
-       if (rsolve_ret == 0)
-           *error = "No solution exists for this puzzle";
-       else
-           *error = "Multiple solutions exist for this puzzle";
-       return NULL;
+    } else {
+       rsolve_ret = rsolve(c, r, ret->grid, NULL, 2);
+
+       if (rsolve_ret != 1) {
+           free_game(ret);
+           if (rsolve_ret == 0)
+               *error = "No solution exists for this puzzle";
+           else
+               *error = "Multiple solutions exist for this puzzle";
+           return NULL;
+       }
     }
 
     return ret;
@@ -1737,6 +1781,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
     int tx, ty;
     game_state *ret;
 
+    button &= ~MOD_NUM_KEYPAD;        /* we treat this the same as normal */
+
     tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
     ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;