Chris Boyle reports that a few users of the Android port were confused
by this, e.g. https://github.com/chrisboyle/sgtpuzzles/issues/624 .
(That seems surprising to me, since I view Map as extremely closely
related to Solo - both are special cases of the general game class
'here is a partial k-colouring of a graph, find the unique total
k-colouring that extends it', just with different ranges of k and
different valid graphs. And surely nobody approaches a Sudoku puzzle
and expects to be able to rub out provided clues they don't like! But
I suppose if you're thinking of Map as a completely separate puzzle
then perhaps that analogy doesn't have the same force.)
In Jigsaw Solo, block boundaries aren't convex, so it's possible for
one of them to have an inward corner. If that corner is in the top
left of a selectable cell, and you right-click that cell to display
the pencil-mode 'cursor' in the form of a triangle in the top left,
then the cursor was accidentally drawn on top of the block boundary,
where it ought to be underneath it.
For example, in game id 5j:d1d4_4c3_1d2d,bb_baaa_dca_baaba, right-
clicking in the bottom right square of the grid demonstrates the
problem.
Jonas Kölker fixed this for Keen in 2015, in commit 6482ed0e3c886af.
This is the identical fix, in Solo's very similar-looking drawing
routine. I feel embarrassed to have taken eight years to get round to
it!
The loop that selects one of the disconnected regions (represented as
equivalence classes in a dsf) to _not_ highlight as an error was
failing to call dsf_canonify() to get the canonical element of the
class.
Instead, it was relying on having met the canonical element of the
class first, because it iterates up the array in order, and in the old
dsf implementation, canonical elements were always minimal ones. But
the DSF refactoring made that untrue, so now we have to explicitly
canonify each value we come to.
Thanks to Steffen Bauer for the diagnosis and suggested fix.
A user reports that trying to generate a 2x2 or 3x3 puzzle at Tricky
difficulty causes the generator to hang, for the usual reason that
there aren't any - puzzles of that size are either ambiguous or Easy.
The usual response in this code base is to quietly downgrade the
puzzle difficulty when absolutely necessary, so here's some code to do
that (and also for 2x3, which the user didn't test, but unsurprisingly
behaves the same way).
'height', not 'neight'. Apparently has been misspelled since the
puzzle was first committed. I suppose they look similar enough that it
never caused a problem.
Thanks to Steffen Bauer for spotting this. The call to
midend_get_params(me) was making a duplicate of me->params, and
nothing was freeing it.
Since the game_params is passed to request_keys as a const pointer, it
should be safe to pass me->params itself, so that instead of adding a
free, we can remove the unnecessary allocation.
On a device with a phone keypad, driving Guess by typing numbers rather
than using the arrow keys seems natural. But if you're going to do
that, showing the labels makes it a lot easier. KaiOS doesn't have any
way for the user to control this, so we should default to the friendlier
state.
Not that I actually need it, but it's just as easy to load multiple
environment <script>s from the DOM as it is to load one, so we may as
well do that. Since only one element can have id="environment", we do
this by matching class="environment" as well.
It will be useful on KaiOS to be able to specify default user
preferences that aren't the standard ones, in the same way that we
specify some environment variables. As with environment variables, we
can now do this be embedding a <script> element in the HTML like this:
<script class="preferences" type="text/plain">
show-labels=true
</script>
These are loaded before the preferences from localStorage, so they just
set defaults and can be overridden by the user (but not on KaiOS yet,
because we still don't have dialogue boxes there).
The js_canvas_get_preferred_size() function was declining to suggest a
size for the puzzle if document.readyState wasn't "complete". I think
my idea here was that if the document wasn't fully loaded then I
couldn't trust the size of the containing <div>. While this was true,
declining to provide a size didn't help much since the puzzle still
needed a size, and the size of the containing <div> was the best guess
we had.
Now that function always returns the size of the containing <div> if
it exists. This appears to mean that puzzles don't show a brief flash
of being the wrong size on KaiOS. That was particularly visible with
Flood, where the wrong-size version had borders around the tiles that
the right-size version lacked. The containing <div> isn't used on the
standard Web versions, so there's no change to behaviour there.
In grid-snapping mode, Untangle was still recording vertex positions
in increments of one pixel. This meant that if you positioned
vertices on a small window, then enlarged the window and positioned
more vertices, the two sets of vertices generally wouldn't line up
with one another. This was annoying, and obviously silly when
Untangle has a resolution-independent co-ordinate system. So now the
snapped positions are recorded in a form that doesn't depend on the
tilesize.
If you clicked somewhere that had no effect (outside the grid or on a
square matching the colour of the top-left corner), interpret_move()
would return MOVE_NO_EFFECT (previously NULL) even though it had unset
ui->cursor. So the keyboard cursor would remain visible until the
next window resize (or similar) when it would vanish. Now
interpret_move() correctly returns MOVE_UI_UPDATE in these cases, so
the cursor vanishes immediately.
If you clicked somewhere that had no effect (on an immutable square or
a middle click on an empty square), interpret_move() would return NULL
even though it had unset ui->cursor. So the keyboard cursor would
remain visible until the next window resize (or similar) when it would
vanish. Now interpret_move() correctly returns MOVE_UI_UPDATE in
these cases, so the cursor vanishes immediately.
I missed this in my main commit for UI preferences
(4227ac1fd5dc25c247e7526526079b85e1890766) because it wasn't documented.
Now it is documented and it has a preference.
As far as I could tell, cur_x, cur_y, prev_cur_x, and prev_cur_y in
game_drawstate were never used. prev_cur_x and prev_cur_y in game_ui
were assigned but never referenced. So they may as well all go.
It looks as if it's been broken for about nine years, ever since
commit 822243de1bc1fc6d introduced the system for drag-selecting a
diagonal of squares. The effect of moving the keyboard cursor and then
pressing a button was to cause crashes because the ui fields
introduced for that system to use (ohx, ohy, odx, ody, odn) were all
completely uninitialised.
If you're using the mouse to change pencil marks, you have to
right-click to pencil-highlight a square, then press a number or
letter key to add or remove a highlight. That causes the highlight to
vanish again. So adding or removing multiple pencil marks requires a
right-click + keypress per mark.
Chris's Android port reversed that decision, making the pencil
highlight persist so that you could 'click' just once and then press
multiple pencil keys. That makes it easier to add lots of highlights,
but harder to just remove a single one (click + press + click to
remove the highlight), unless you don't mind keeping the highlight
around afterwards cluttering up your view.
In other words, this is just the sort of thing users might reasonably
disagree on. So now we have an organised preferences system, we can
let them disagree, and each configure it whichever way they like!
This only affects mouse-based play. The keyboard cursor has _always_
worked this way, because it doesn't disappear at all; its behaviour is
unchanged, and independent of the new preference.
Light Up is unusual in that clicking outside the grid hides the
cursor, so the return value from clicks outside the grid is
MOVE_NO_EFFECT or MOVE_UI_UPDATE rather than the more usual
MOVE_UNUSED.
This adds an extra parameter to move_cursor() that's an optional pointer
to a bool indicating whether the cursor is visible. This allows for
centralising the common idiom of having the keyboard cursor become
visible when a cursor key is pressed. Consistently with the vast
majority of existing puzzles, the cursor moves even if it was invisible
before, and becomes visible even if it can't move.
The function now also returns one of the special constants that can be
returned by interpret_move(), so that the caller can correctly return
MOVE_UI_UPDATE or MOVE_NO_EFFECT without needing to carefully check for
changes itself.
Callers are updated only to the extent that they all pass NULL as the
new argument. Most of them could now be substantially simplified.