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);