shithub: puzzles

Download patch

ref: c108271b44c9607fce88499d794cbf1648213f1d
parent: 55813ea2fc7ef456d57cb9ddbfed4a802f1bb442
author: Simon Tatham <anakin@pobox.com>
date: Mon Sep 6 06:51:01 EDT 2021

Unruly: new 'Trivial' difficulty level.

A user recently mentioned having found even 'Easy' to be harder than
they'd like. That difficulty level generates puzzles that can be
solved by filling in any square for which the other colour would
immediately generate a run of 3, and spotting rows that have the
correct total number of one colour and filling in all remaining
squares in the other colour.

Initially I thought there wasn't much I could do to make the solution
techniques easier than that. But after a bit of thought, I decided the
second criterion can be weakened a bit. The new 'Trivial' level
replaces it with a special case: when a row or column only has _one_
remaining unfilled square, the remaining square is filled in by
counting.

That version of the rule doesn't require the player to do any counting
in order to _spot_ possible applications of it: you can see at a
glance that a row or column has only one remaining grey square, even
if having seen it you then have to count to work out which colour to
fill it in. So it makes a gentler introduction to the game.

--- a/unruly.c
+++ b/unruly.c
@@ -81,6 +81,7 @@
     int diff;
 };
 #define DIFFLIST(A)                             \
+    A(TRIVIAL,Trivial, t)                       \
     A(EASY,Easy, e)                             \
     A(NORMAL,Normal, n)                         \
 
@@ -95,6 +96,7 @@
 #define DIFFCONFIG DIFFLIST(CONFIG)
 
 static const struct game_params unruly_presets[] = {
+    { 8,  8, false, DIFF_TRIVIAL},
     { 8,  8, false, DIFF_EASY},
     { 8,  8, false, DIFF_NORMAL},
     {10, 10, false, DIFF_EASY},
@@ -743,6 +745,61 @@
     return ret;
 }
 
+static int unruly_solver_check_single_gap(game_state *state,
+                                          int *complete, bool horizontal,
+                                          int *rowcount, int *colcount,
+                                          char fill)
+{
+    int w2 = state->w2, h2 = state->h2;
+    int count = (horizontal ? h2 : w2); /* number of rows to check */
+    int target = (horizontal ? w2 : h2) / 2; /* target number of 0s/1s */
+    int *other = (horizontal ? rowcount : colcount);
+
+    int ret = 0;
+
+    int i;
+    /* Check for completed rows/cols for one number, then fill in the rest */
+    for (i = 0; i < count; i++) {
+        if (complete[i] == target && other[i] == target - 1) {
+#ifdef STANDALONE_SOLVER
+            if (solver_verbose) {
+                printf("Solver: Row %i has only one square left which must be "
+                       "%c\n", i, (fill == N_ZERO ? '0' : '1'));
+            }
+#endif
+            ret += unruly_solver_fill_row(state, i, horizontal, rowcount,
+                                          colcount, fill);
+        }
+    }
+
+    return ret;
+}
+
+static int unruly_solver_check_all_single_gap(game_state *state,
+                                              struct unruly_scratch *scratch)
+{
+    int ret = 0;
+
+    ret +=
+        unruly_solver_check_single_gap(state, scratch->ones_rows, true,
+                                       scratch->zeros_rows,
+                                       scratch->zeros_cols, N_ZERO);
+    ret +=
+        unruly_solver_check_single_gap(state, scratch->ones_cols, false,
+                                       scratch->zeros_rows,
+                                       scratch->zeros_cols, N_ZERO);
+    ret +=
+        unruly_solver_check_single_gap(state, scratch->zeros_rows, true,
+                                       scratch->ones_rows,
+                                       scratch->ones_cols, N_ONE);
+    ret +=
+        unruly_solver_check_single_gap(state, scratch->zeros_cols, false,
+                                       scratch->ones_rows,
+                                       scratch->ones_cols, N_ONE);
+
+    return ret;
+}
+
 static int unruly_solver_check_complete_nums(game_state *state,
                                              int *complete, bool horizontal,
                                              int *rowcount, int *colcount,
@@ -1140,10 +1197,23 @@
 
         /* Keep using the simpler techniques while they produce results */
         if (done) {
-            if (maxdiff < DIFF_EASY)
-                maxdiff = DIFF_EASY;
+            if (maxdiff < DIFF_TRIVIAL)
+                maxdiff = DIFF_TRIVIAL;
             continue;
         }
+
+        /* Check for rows with only one unfilled square */
+        done += unruly_solver_check_all_single_gap(state, scratch);
+
+        if (done) {
+            if (maxdiff < DIFF_TRIVIAL)
+                maxdiff = DIFF_TRIVIAL;
+            continue;
+        }
+
+        /* Easy techniques */
+        if (diff < DIFF_EASY)
+            break;
 
         /* Check for completed rows */
         done += unruly_solver_check_all_complete_nums(state, scratch);