45 Commits

Author SHA1 Message Date
15f4fa851a Forbid lines off the grid in Pearl
While they couldn't be generated in normal play, execute_move() would
permit lines and marks across the edge of the grid that would then
generate assertion failures ("dsf_update_completion: Assertion
`INGRID(state, bx, by)' failed.").

I've added a check to execute_move() that after updating a square, the
square doesn't have any lines or marks that leave the grid.

This save file demonstrated the problem:

SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
VERSZON :1:1
GAME    :5:Pearl
PARAMS  :5:5x6dt
CPARAMS :5:5x6dt
DESC    :6:eeeeee
NSTATES :1:2
STATEPOS:1:1
MOVE    :6:F1,4,2
2023-02-02 23:09:19 +00:00
789e11f8f8 Remove various unused game functions
If can_configure is false, then the game's configure() and
custom_params() functions will never be called.  If can_solve is false,
solve() will never be called.  If can_format_as_text_ever is false,
can_format_as_text_now() and text_format() will never be called.  If
can_print is false, print_size() and print() will never be called.  If
is_timed is false, timing_state() will never be called.

In each case, almost all puzzles provided a function nonetheless.  I
think this is because in Puzzles' early history there was no "game"
structure, so the functions had to be present for linking to work.  But
now that everything indirects through the "game" structure, unused
functions can be left unimplemented and the corresponding pointers set
to NULL.

So now where the flags mentioned above are false, the corresponding
functions are omitted and the function pointers in the "game" structures
are NULL.
2023-01-31 23:25:05 +00:00
8a3fb82e23 Last-ditch maximum size limit for Pearl
This makes sure that width * height <= INT_MAX, which it rather needs
to be.
2023-01-15 16:24:27 +00:00
676ec87b6d Pearl: make PEARL_GUI_LOOPY affect printed output.
This display style is perfectly playable on paper (proof: it works for
Loopy), so there's no reason not to support both modes in both output
routines.
2022-12-11 18:52:06 +00:00
a3310ab857 New backend function: current_key_label()
This provides a way for the front end to ask how a particular key should
be labelled right now (specifically, for a given game_state and
game_ui).  This is useful on feature phones where it's conventional to
put a small caption above each soft key indicating what it currently
does.

The function currently provides labels only for CURSOR_SELECT and
CURSOR_SELECT2.  This is because these are the only keys that need
labelling on KaiOS.

The concept of labelling keys also turns up in the request_keys() call,
but there are quite a few differences.  The labels returned by
current_key_label() are dynamic and likely to vary with each move, while
the labels provided by request_keys() are constant for a given
game_params.  Also, the keys returned by request_keys() don't generally
include CURSOR_SELECT and CURSOR_SELECT2, because those aren't necessary
on platforms with pointing devices.  It might be possible to provide a
unified API covering both of this, but I think it would be quite
difficult to work with.

Where a key is to be unlabelled, current_key_label() is expected to
return an empty string.  This leaves open the possibility of NULL
indicating a fallback to button2label or the label specified by
request_keys() in the future.

It's tempting to try to implement current_key_label() by calling
interpret_move() and parsing its output.  This doesn't work for two
reasons.  One is that interpret_move() is entitled to modify the
game_ui, and there isn't really a practical way to back those changes
out.  The other is that the information returned by interpret_move()
isn't sufficient to generate a label.  For instance, in many puzzles it
generates moves that toggle the state of a square, but we want the label
to reflect which state the square will be toggled to.  The result is
that I've generally ended up pulling bits of code from interpret_move()
and execute_move() together to implement current_key_label().

Alongside the back-end function, there's a midend_current_key_label()
that's a thin wrapper around the back-end function.  It just adds an
assertion about which key's being requested and a default null
implementation so that back-ends can avoid defining the function if it
will do nothing useful.
2022-12-09 20:48:30 +00:00
10bd3aeb2a pearl: Return NULL when Backspace or Escape does nothing
When there's no drag in progress, cancelling the drag has no effect.
Returning NULL lets the front-end know this, which in particular means
the Backspace key can leave the app in KaiOS.
2022-11-18 14:40:56 +00:00
49dbf1f60d Pearl: Require width or height to be at least 6 for Tricky
Josh Triplett reported:
> If I ask pearl to generate a 5x5 tricky puzzle, it runs forever.

I find that 5x6 or 6x5 works, so set the minimum accordingly.

References: https://bugs.debian.org/667963
2022-07-31 08:53:08 +01:00
c43a34fbfe Pearl: reorder helper functions.
interpret_ui_drag is now called from update_ui_drag, so it makes more
sense to have the former appear first in the file, together with its
comment explaining the expected usage.
2022-01-27 18:48:47 +00:00
c44e91567c 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.
2022-01-27 18:47:31 +00:00
12b64a1db1 Build a lot of conditioned-out test and helper programs.
Most of these aren't especially useful, but if we're going to have
them in the code base at all, we should at least ensure they compile:
bit-rotted conditioned-out code is of no value.

One of the new programs is 'galaxieseditor', which borrows most of the
Galaxies code but changes the UI so that you can create and remove
_dots_ instead of edges, and then run the solver to see whether it can
solve the puzzle you've designed. Unlike the rest, this is a GUI
helper tool, using the 'guiprogram' cmake function introduced in the
previous commit.

The programs are:
 - 'combi', a test program for the utility module that generates all
   combinations of n things
 - 'divvy', a test program for the module that divides a rectangle at
   random into equally-sized polyominoes
 - 'penrose-test', a test program for the Penrose-tiling generator
   used in Loopy, which outputs an SVG of a piece of tiling
 - 'penrose-vector', a much smaller test program for the vector
   arithmetic subroutines in that code
 - 'sort-test', a test of this code base's local array sorting routine
 - 'tree234-test', the exhaustive test code that's been in tree234.c
   all along.

Not all of them compiled first time. Most of the fixes were the usual
kind of thing: fixing compiler warnings by removing unused
variables/functions, bringing uses of internal APIs up to date. A
notable one was that galaxieseditor's interpret_move() modified the
input game state, which was an error all along and is now detected by
me having made it a const pointer; I had to replace that with an extra
wrinkle in the move-string format, so that now execute_move() makes
the modification.

The one I'm _least_ proud of is squelching a huge number of
format-string warnings in tree234-test by interposing a variadic
function without __attribute__((printf)).
2021-05-25 10:52:25 +01:00
c0da615a93 Centralise initial clearing of the puzzle window.
I don't know how I've never thought of this before! Pretty much every
game in this collection has to have a mechanism for noticing when
game_redraw is called for the first time on a new drawstate, and if
so, start by covering the whole window with a filled rectangle of the
background colour. This is a pain for implementers, and also awkward
because the drawstate often has to _work out_ its own pixel size (or
else remember it from when its size method was called).

The backends all do that so that the frontends don't have to guarantee
anything about the initial window contents. But that's a silly
tradeoff to begin with (there are way more backends than frontends, so
this _adds_ work rather than saving it), and also, in this code base
there's a standard way to handle things you don't want to have to do
in every backend _or_ every frontend: do them just once in the midend!

So now that rectangle-drawing operation happens in midend_redraw, and
I've been able to remove it from almost every puzzle. (A couple of
puzzles have other approaches: Slant didn't have a rectangle-draw
because it handles even the game borders using its per-tile redraw
function, and Untangle clears the whole window on every redraw
_anyway_ because it would just be too confusing not to.)

In some cases I've also been able to remove the 'started' flag from
the drawstate. But in many cases that has to stay because it also
triggers drawing of static display furniture other than the
background.
2021-04-25 13:07:59 +01:00
78bc9ea7f7 Add method for frontends to query the backend's cursor location.
The Rockbox frontend allows games to be displayed in a "zoomed-in"
state targets with small displays. Currently we use a modal interface
-- a "viewing" mode in which the cursor keys are used to pan around
the rendered bitmap; and an "interaction" mode that actually sends
keys to the game.

This commit adds a midend_get_cursor_location() function to allow the
frontend to retrieve the backend's cursor location or other "region of
interest" -- such as the player location in Cube or Inertia.

With this information, the Rockbox frontend can now intelligently
follow the cursor around in the zoomed-in state, eliminating the need
for a modal interface.
2020-12-07 19:40:06 +00:00
db3b531e2c Add missing 'static' to game-internal declarations.
Another thing I spotted while trawling the whole source base was that
a couple of games had omitted 'static' on a lot of their internal
functions. Checking with nm, there turned out to be quite a few more
than I'd spotted by eye, so this should fix them all.

Also added one missing 'const', on the lookup table nbits[] in Tracks.
2018-11-13 22:06:19 +00:00
5f5b284c0b Use C99 bool within source modules.
This is the main bulk of this boolification work, but although it's
making the largest actual change, it should also be the least
disruptive to anyone interacting with this code base downstream of me,
because it doesn't modify any interface between modules: all the
inter-module APIs were updated one by one in the previous commits.
This just cleans up the code within each individual source file to use
bool in place of int where I think that makes things clearer.
2018-11-13 21:48:24 +00:00
a550ea0a47 Replace TRUE/FALSE with C99 true/false throughout.
This commit removes the old #defines of TRUE and FALSE from puzzles.h,
and does a mechanical search-and-replace throughout the code to
replace them with the C99 standard lowercase spellings.
2018-11-13 21:48:24 +00:00
a76d269cf2 Adopt C99 bool in the game backend API.
encode_params, validate_params and new_desc now take a bool parameter;
fetch_preset, can_format_as_text_now and timing_state all return bool;
and the data fields is_timed, wants_statusbar and can_* are all bool.
All of those were previously typed as int, but semantically boolean.

This commit changes the API declarations in puzzles.h, updates all the
games to match (including the unfinisheds), and updates the developer
docs as well.
2018-11-13 21:34:42 +00:00
60a929a250 Add a request_keys() function with a midend wrapper.
This function gives the front end a way to find out what keys the back
end requires; and as such it is mostly useful for ports without a
keyboard. It is based on changes originally found in Chris Boyle's
Android port, though some modifications were needed to make it more
flexible.
2018-04-22 17:04:50 +01:00
b3243d7504 Return error messages as 'const char *', not 'char *'.
They're never dynamically allocated, and are almost always string
literals, so const is more appropriate.
2017-10-01 16:34:41 +01:00
de67801b0f Use a proper union in struct config_item.
This allows me to use different types for the mutable, dynamically
allocated string value in a C_STRING control and the fixed constant
list of option names in a C_CHOICES.
2017-10-01 16:34:41 +01:00
eeb2db283d New name UI_UPDATE for interpret_move's return "".
Now midend.c directly tests the returned pointer for equality to this
value, instead of checking whether it's the empty string.

A minor effect of this is that games may now return a dynamically
allocated empty string from interpret_move() and treat it as just
another legal move description. But I don't expect anyone to be
perverse enough to actually do that! The main purpose is that it
avoids returning a string literal from a function whose return type is
a pointer to _non-const_ char, i.e. we are now one step closer to
being able to make this code base clean under -Wwrite-strings.
2017-10-01 15:18:14 +01:00
a7dc17c425 Rework the preset menu system to permit submenus.
To do this, I've completely replaced the API between mid-end and front
end, so any downstream front end maintainers will have to do some
rewriting of their own (sorry). I've done the necessary work in all
five of the front ends I keep in-tree here - Windows, GTK, OS X,
Javascript/Emscripten, and Java/NestedVM - and I've done it in various
different styles (as each front end found most convenient), so that
should provide a variety of sample code to show downstreams how, if
they should need it.

I've left in the old puzzle back-end API function to return a flat
list of presets, so for the moment, all the puzzle backends are
unchanged apart from an extra null pointer appearing in their
top-level game structure. In a future commit I'll actually use the new
feature in a puzzle; perhaps in the further future it might make sense
to migrate all the puzzles to the new API and stop providing back ends
with two alternative ways of doing things, but this seemed like enough
upheaval for one day.
2017-04-26 21:51:23 +01:00
0b348877e2 Fix missing error highlights (+ array underrun!) in Pearl.
I was accidentally re-checking the value of component_state[comp]
after resetting comp to the special value -1, which caused a failure
to highlight stray path-shaped components if they existed in addition
to a large loop component. (Plus, of course, it's just illegal no
matter what visible behaviour it does or doesn't cause in practice.)

Fixed by adjusting the code to more closely match the version in Loopy
(I wonder how I managed to add two pieces of code in commit b31155b73
without noticing this difference between them).
2016-12-16 18:33:10 +00:00
b31155b732 Account for disconnected paths in Loopy and Pearl error highlights.
In commits 24848706e and adc54741f, I revamped the highlighting of
erroneous connected components of those two puzzles' solution graphs
in cases where a non-solution loop existed, so that the largest
component was considered correct and the smaller ones lit up in red.

I intended this to work in the cases where you have most of a correct
solution as one component and a small spurious loop as another (in
which case the latter lights up red), or conversely where your mostly
correct component was joined into a loop leaving a few edges out (in
which case the left-out edges again light up red). However, a user
points out that I overlooked the case where your mostly correct
solution is not all one component! If you've got lots of separate
pieces of path, and one tiny loop that's definitely wrong, it's silly
to light up all but the longest piece of path as if they're erroneous.

Fixed by treating all the non-loop components as one unit for these
purposes. So if there is at least one loop and it isn't the only thing
on the board, then we _either_ light up all loops (if they're all
smaller than the set of non-loop paths put together), _or_ light up
everything but the largest loop (if that loop is the biggest thing on
the board).
2016-04-28 20:42:23 +01:00
adc54741f0 Pearl: revise loop detection similarly to Loopy.
Pearl has more or less the same attitude to loops as Loopy does, in
that a loop is required in the solution but some loops during play
want to be highlighted as errors. So it makes sense to use the same
strategy for loop highlighting.

I've cloned-and-hacked the code from Loopy rather than abstracting it
out, because there were some fiddly differences in application (like
checking of untouched clues in Pearl). Perhaps some day I can come
back and make it all neater.

Also, while I'm here, I've cleaned up the loop_length field in
game_state, which was carefully set up by check_completion() but never
actually used for anything. (If I remember rightly, it was going to be
used for a fancy victory flash which never saw the light of day.)
2016-02-24 19:36:41 +00:00
c5500926bf Pearl: reinstate a conditioned-out assertion.
I think this assertion must have been put under '#if 0' during early
development, and accidentally never taken out once the game started
actually working. Putting it back in doesn't cause the self-tests to
fail, so I'm reinstating it - if it does fail, I'd like to know about
it!
2016-02-24 19:31:54 +00:00
e22a9c8396 build fix 2015-10-03 17:39:22 +01:00
ea8da331e3 Cancel dragging in Pearl by pressing Escape or Backspace. 2015-10-03 16:59:10 +01:00
bf8ff09a84 Change Pearl's cursor logic.
Keyboard dragging while holding Control now moves the cursor to the
target square. Shift-Control-arrowkey performs the previous behavior
of Control-arrowkey.
2015-10-03 16:59:04 +01:00
d64a79e2b8 Refactor Pearl's mark_in_direction, and invert a parameter. 2015-10-03 16:59:04 +01:00
8e35087e09 Refactor modifier handling in Pearl's cursor code. 2015-10-03 16:59:04 +01:00
7543082f26 Add game_text_format to Pearl. 2015-10-03 16:58:18 +01:00
4fb9776d83 Fix a typo in the comments of Pearl.
Each stone can have 2 lines radiating from its centre, not 3.
2015-10-03 16:57:44 +01:00
251b21c418 Giant const patch of doom: add a 'const' to every parameter in every
puzzle backend function which ought to have it, and propagate those
consts through to per-puzzle subroutines as needed.

I've recently had to do that to a few specific parameters which were
being misused by particular puzzles (r9657, r9830), which suggests
that it's probably a good idea to do the whole lot pre-emptively
before the next such problem shows up.

[originally from svn r9832]
[r9657 == 3b250baa02a7332510685948bf17576c397b8ceb]
[r9830 == 0b93de904a98f119b1a95d3a53029f1ed4bfb9b3]
2013-04-13 10:37:32 +00:00
0b93de904a Add 'const' to the game_params arguments in validate_desc and
new_desc. Oddities in the 'make test' output brought to my attention
that a few puzzles have been modifying their input game_params for
various reasons; they shouldn't do that, because that's the
game_params held permanently by the midend and it will affect
subsequent game generations if they modify it. So now those arguments
are const, and all the games which previously modified their
game_params now take a copy and modify that instead.

[originally from svn r9830]
2013-04-12 17:11:49 +00:00
8a7dad6d2d Bring Pearl's game-completion handling in line with my usual practice:
the 'completed' flag is not reset if you make a new move transforming
a solved game into an unsolved state, so the game won't flash again if
you manually erase and redraw a line segment (though it still will if
you undo and redo past the first solved state in the undo history).

[originally from svn r9750]
2013-01-19 18:56:06 +00:00
3b250baa02 New rule: interpret_move() is passed a pointer to the game_drawstate
basically just so that it can divide mouse coordinates by the tile
size, but is definitely not expected to _write_ to it, and it hadn't
previously occurred to me that anyone might try. Therefore,
interpret_move() now gets a pointer to a _const_ game_drawstate
instead of a writable one.

All existing puzzles cope fine with this API change (as long as the
new const qualifier is also added to a couple of subfunctions to which
interpret_move delegates work), except for the just-committed Undead,
which somehow had ds->ascii and ui->ascii the wrong way round but is
otherwise unproblematic.

[originally from svn r9657]
2012-09-09 18:40:12 +00:00
ec732a4f4e Add a difficulty exception to prevent Pearl spinning forever when
asked to generate a 5x5 Tricky puzzle. (Debian bug #667963)

[originally from svn r9454]
2012-04-08 13:45:10 +00:00
faabfe3b62 Patch from Jonas Koelker to add keyboard control support to Pearl.
[originally from svn r9411]
2012-02-19 10:15:59 +00:00
f38adf6394 Tweak a conditional expression in pearl.c to work around a display bug
in the Java build - which turns out to be a JVM bug in OpenJDK 6,
causing the NestedVM rendition of the expression (i==1?3:4) to be
mis-JITed. OpenJDK 7 appears not to do that any more, but this
equivalent (for these purposes) rephrasing should perturb the code
just enough to dodge the problem.

[originally from svn r9408]
2012-02-17 19:07:31 +00:00
21629d7e44 Fix one-character typo in r9405 which was breaking right-clicks.
[originally from svn r9406]
[r9405 == 0c13787c2a17adc891f8e47c06b259f80bc8251a]
2012-02-13 21:10:47 +00:00
0c13787c2a David Nickerson reports odd behaviour involving a drag start point
persisting between separate mouse actions. Revamp all uses of the
ndragcoords field in an attempt to stamp that out: we now distinguish
between active drags (>0), a valid click but no drag yet (0), and a
totally invalid situation in which all mouse activity will be ignored
until the next fresh attempt (-1).

[originally from svn r9405]
2012-02-08 23:31:58 +00:00
340c300c1d David Nickerson reports that it's possible to lay a line over a 'no
line here' cross mark by dragging, and furthermore, that doing so puts
that grid edge into a stuck state that no UI action short of undo can
get it back out of. Fix drags to stop at crosses, and fix execute_move
to fault any move string that nonetheless somehow managed to try to
set a line over a cross without explicitly tagging it 'R'.

[originally from svn r9400]
2012-02-02 07:18:14 +00:00
f3168895c8 Move a debug statement at the end of new_clues from the caller (just
after return) to the callee (just before). Might print something
useful in the soak-test context (where that debug statement will now
be printed and previously wasn't), but the main aim is to remove the
variable 'ngen' at the main call site, which triggered a set-but-not-
used warning if the debug statement that printed it was compiled out.

[originally from svn r9392]
2012-01-31 08:32:26 +00:00
e406092a0e Tweak right-click processing to be less finicky.
[originally from svn r9381]
2012-01-22 15:12:56 +00:00
b16eece9fc New puzzle! Or rather, new-ish, because this one has been lying around
in the 'unfinished' directory for a while, and has now been finished
up thanks to James Harvey putting in some effort and galvanising me to
put in the rest. This is 'Pearl', an implementation of Nikoli's 'Masyu'.

The code in Loopy that generates a random loop along grid edges to use
as the puzzle solution has been abstracted out into loopgen.[ch] so
that Pearl can use it for its puzzle solutions too. I've also
introduced a new utility module called 'tdq' (for 'to-do queue').

[originally from svn r9379]
2012-01-22 14:14:26 +00:00