shithub: puzzles

Download patch

ref: 68363231f062192156799af7a153fc3ab3a0f5ed
parent: 1ba4e37228e6eae0d09c0cca7f4ab7fb489fdc9d
author: Simon Tatham <anakin@pobox.com>
date: Mon Feb 18 16:12:16 EST 2019

Galaxies: prevent creation of empty undo-chain items.

If you drag an arrow on to a square which is already filled in as part
of a completed region, or whose counterpart is filled in, or whose
counterpart is actually a dot, then the game can't actually place a
double arrow. Previously, it didn't find that out until execute_move
time, at which point it was too late to prevent a no-op action from
being placed on the undo chain.

Now we do those checks in interpret_move, before generating the move
string that tries to place the double arrow in the first place. So
execute_move can now enforce by assertion that arrow-placement moves
it gets are valid.

--- a/galaxies.c
+++ b/galaxies.c
@@ -346,29 +346,44 @@
            tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/
 }
 
-static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
+static bool ok_to_add_assoc_with_opposite_internal(
+    const game_state *state, space *tile, space *opposite)
+{
     int *colors;
-    space *opposite = space_opposite_dot(state, tile, dot);
+    bool toret;
 
-    if (opposite == NULL) {
-        return;
-    }
-    if (opposite->flags & F_DOT) {
-        return;
-    }
+    if (tile->flags & F_DOT)
+        return false;
+    if (opposite == NULL)
+        return false;
+    if (opposite->flags & F_DOT)
+        return false;
 
+    toret = true;
     colors = snewn(state->w * state->h, int);
     check_complete(state, NULL, colors);
-    if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2]) {
-        sfree(colors);
-        return;
-    }
-    if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2]) {
-        sfree(colors);
-        return;
-    }
 
+    if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2])
+        toret = false;
+    if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2])
+        toret = false;
+
     sfree(colors);
+    return toret;
+}
+
+static bool ok_to_add_assoc_with_opposite(
+    const game_state *state, space *tile, space *dot)
+{
+    space *opposite = space_opposite_dot(state, tile, dot);
+    return ok_to_add_assoc_with_opposite_internal(state, tile, opposite);
+}
+
+static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
+    space *opposite = space_opposite_dot(state, tile, dot);
+
+    assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
+
     remove_assoc_with_opposite(state, tile);
     add_assoc(state, tile, dot);
     remove_assoc_with_opposite(state, opposite);
@@ -2596,8 +2611,15 @@
 	 */
         if (INUI(state, px, py)) {
             sp = &SPACE(state, px, py);
+            dot = &SPACE(state, ui->dotx, ui->doty);
 
-            if (!(sp->flags & F_DOT))
+            /*
+             * Exception: if it's not actually legal to add an arrow
+             * and its opposite at this position, we don't try,
+             * because otherwise we'd append an empty entry to the
+             * undo chain.
+             */
+            if (ok_to_add_assoc_with_opposite(state, sp, dot))
 		sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
 			sep, px, py, ui->dotx, ui->doty);
 	}