shithub: puzzles

Download patch

ref: c44e91567cfdb92ba9fcc6272cf1997134c58239
parent: 07029044b7f525f2aa930b28ee809d1834e44920
author: Simon Tatham <anakin@pobox.com>
date: Thu Jan 27 13:32:33 EST 2022

Pearl: permit drawing a whole loop in one drag.

A user reported recently that they were trying this as an extra
challenge (solve the whole puzzle mentally and then draw it in
finished form in one UI action). But the backtracking behaviour of
Pearl's dragging mode meant that the loop erased itself as soon as the
drag came back to a revisited position.

In this commit I fix that by making the exception that you can
unconditionally return to the start point of the drag, _provided_ that
in doing so you don't create a grid cell of degree > 2.

--- a/pearl.c
+++ b/pearl.c
@@ -1891,6 +1891,11 @@
     char *draglines;            /* size w*h; lines flipped by current drag */
 };
 
+static void interpret_ui_drag(const game_state *state, const game_ui *ui,
+                              bool *clearing, int i, int *sx, int *sy,
+                              int *dx, int *dy, int *dir,
+                              int *oldstate, int *newstate);
+
 static void update_ui_drag(const game_state *state, game_ui *ui,
                            int gx, int gy)
 {
@@ -1919,12 +1924,41 @@
      * the drag path so far has the effect of truncating the path back
      * to that square, so a player can back out part of an uncommitted
      * drag without having to let go of the mouse.
+     *
+     * An exception is that you're allowed to drag round in a loop
+     * back to the very start of the drag, provided that doesn't
+     * create a vertex of the wrong degree. This allows a player who's
+     * after an extra challenge to draw the entire loop in a single
+     * drag, without it cancelling itself just before release.
      */
-    for (i = 0; i < ui->ndragcoords; i++)
+    for (i = 1; i < ui->ndragcoords; i++)
         if (pos == ui->dragcoords[i]) {
             ui->ndragcoords = i+1;
             return;
         }
+
+    if (pos == ui->dragcoords[0]) {
+        /* More complex check for a loop-shaped drag, which has to go
+         * through interpret_ui_drag to decide on the final degree of
+         * the start/end vertex. */
+        ui->dragcoords[ui->ndragcoords] = pos;
+        bool clearing = true;
+        int lines = state->lines[pos] & (L|R|U|D);
+        for (i = 0; i < ui->ndragcoords; i++) {
+            int sx, sy, dx, dy, dir, oldstate, newstate;
+            interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
+                              &dir, &oldstate, &newstate);
+            if (sx == gx && sy == gy)
+                lines ^= (oldstate ^ newstate);
+            if (dx == gx && dy == gy)
+                lines ^= (F(oldstate) ^ F(newstate));
+        }
+        if (NBITS(lines) > 2) {
+            /* Bad vertex degree: fall back to the backtracking behaviour. */
+            ui->ndragcoords = 1;
+            return;
+        }
+    }
 
     /*
      * Otherwise, dragging the mouse into a square that's a rook-move