2098 Commits

Author SHA1 Message Date
68a1e8413c spectre.c: expose a couple more internal functions.
spectre-test will want to use these for an additional generation mode.
2023-06-18 09:18:58 +01:00
86466959e8 Spectre tiling: add a comment with some reference URLs.
I meant to fold this into yesterday's main commit, but there's always
something that gets forgotten.
2023-06-17 11:11:26 +01:00
a33d9fad02 Loopy / grid.c: support the new Spectre monotiling.
This uses a tile shape very similar to the hat, but the tiling
_structure_ is totally changed so that there aren't any reflected
copies of the tile.

I'm not sure how much difference this makes to gameplay: the two
tilings are very similar for Loopy purposes. But the code was fun to
write, and I think the Spectre shape is noticeably prettier, so I'm
adding this to the collection anyway.

The test programs also generate a pile of SVG images used in the
companion article on my website.
2023-06-16 19:15:47 +01:00
c82537b457 Fix some unused-variable warnings.
A test-build with a modern clang points out a number of 'set but not
used' variables, which clang seems to have got better at recently.

In cases where there's conditioned-out or commented-out code using the
variable, I've left it in and added a warning-suppressing cast to
void. Otherwise I've just deleted the variables.
2023-06-16 19:04:50 +01:00
de13ca2874 Add a 'core' library alongside 'common'.
The 'core' library contains almost all the same objects as 'common',
but leaves out hat.c. And the auxiliary program 'hatgen' now links
against that slightly reduced core library instead of 'common'.

This avoids a dependency loop: one of hatgen's jobs is to generate
hat-tables.h, but hat-tables.h is a dependency of it.

Of course, the generated hat-tables.h is already committed, so this
doesn't present a bootstrapping problem in a normal build. But if
someone modifies hatgen.c in order to regenerate hat-tables.h, and
does so in a way that makes it uncompilable, they can't rebuild hatgen
and try again! Of course you can always revert changes with git, but
it's annoying to have to. Better to keep the dependencies non-cyclic
in the first place.
2023-06-16 18:32:55 +01:00
43f4fde2f2 hat-test: support SVG output.
I want to generate an SVG diagram for an upcoming article.
2023-06-16 18:32:55 +01:00
2be0e4a242 Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Pearl 2023-06-16 10:17:37 +01:00
5fb94c9f47 Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Cube 2023-06-15 23:11:57 +01:00
73e7bf73bb Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Fifteen 2023-06-15 21:53:56 +01:00
1a316f47ad Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Bridges 2023-06-11 20:13:10 +01:00
19b3bfc0d3 Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Slant 2023-06-11 00:33:28 +01:00
1547154efb Expose the NO_EFFECT/UNUSED distinction through midend_process_key()
This removed the "handled" pointer and instead extends the existing
boolean return value (quit or don't quit) into an enumeration.  One of
the values still quits the program, but now there are different values
for keys that had an effect, had no effect, and are not used by the
puzzle at all.  The mapping from interpret_move results to process_key
results is roughly:

move string    -> PKR_SOME_EFFECT
MOVE_UI_UPDATE -> PKR_SOME_EFFECT
MOVE_NO_EFFECT -> PKR_NO_EFFECT
MOVE_UNUSED    -> PKR_UNUSED

The mid-end can also generate results internally, and is the only place
that PKR_QUIT can arise.

For compatibility, PKR_QUIT is zero, so anything expecting a false
return value to mean quit will be unsurprised.  The other values are
ordered so that lower values indicate a greater amount of handling of
the key.
2023-06-11 00:33:28 +01:00
87e98e6715 Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in Mines 2023-06-11 00:33:27 +01:00
a943f3177f Add MOVE_NO_EFFECT and MOVE_UNUSED return values from interpret_move()
These allow for distinguishing the case where a puzzle doesn't have a
use for a key from the case where it just happens to have no effect in
the current state of the puzzle.  These were both represented by NULL,
but that now represents the case where a puzzle hasn't been updated to
make the distinction yet.
2023-06-11 00:33:27 +01:00
a9af3fda1d Rename UI_UPDATE as MOVE_UI_UPDATE
All the other constants named UI_* are special key names that can be
passed to midend_process_key(), but UI_UPDATE is a special return value
from the back-end interpret_move() function instead.  This renaming
makes the distinction clear and provides a naming convention for future
special return values from interpret_move().
2023-06-11 00:33:27 +01:00
b08c13f5f4 Update a comment in Mines to reflect that we have user prefs now 2023-06-11 00:32:40 +01:00
7333d27b0c Fix a few minor memory leaks.
Thanks to Jeremy Stephens for reporting them.
2023-06-06 19:54:40 +01:00
4227ac1fd5 Add preferences for existing UI style controls
Some puzzles have keys that make changes to the display style in ways
that would probably have been user preferences if they had existed.
I've added a user preference for each of these.  The keys still work,
and unlike the preferences can be changed without saving any state.

The affected settings are:
 * Labelling colours with numbers in Guess ("L" key)
 * Labelling regions with numbers in Map ("L" key)
 * Whether monsters are shown as letters or pictures in Undead ("A" key)
2023-05-30 19:57:15 +02:00
5acce15ed9 js: pass preferences file from JS to C on the heap, not the stack
The C stack used by Emscripten is quite small, so passing more than a
few klilobytes of data on it tends to cause an overflow.  Current
versions of puzzles will only generate tiny preferences files, but this
might change in future and in any case Puzzles shouldn't crash just
because the preferences in local storage have got corrupted.

To fix this, we now have JavaScript allocate a suitable amount of C heap
memory using malloc() and stick the preferences file in there.

This could plausibly fail if the preferences file were really big, but
that's unlikely since browsers generally limit an origin to about 5 MB
of local storage.  In any case, if malloc() does fail, we'll just ignore
the preferences file, which is probably the right thing to do.
2023-05-30 17:00:31 +02:00
6a41c0b7a0 js: handle exceptions when accessing localStorage
Trying to access window.localStorage will generate an exception if the
local storage is for some reason inaccessible.  This can be
demonstrated in Firefox by configuring it to block a site from using
site data.  Writing to local storage might also cause an exception if,
for instance, the quota of data for a site is exceeded.

If an exception is raised while loading preferences we log the fact
but don't make any report to the user, behaving as if no preferences
were found.  This avoids annoying the user on every visit to a puzzle
page if they're not trying to use preferences.

If something goes wrong when saving, we do currently report that to
the user in an alert box.  This seems reasonable since it's in
response to an explicit user action.
2023-05-30 15:07:41 +02:00
b6c842a28c Emscripten: fix edge case of js_canvas_find_font_midpoint.
If the puzzle canvas is at a ludicrously small size, so that you
attempt to use a zero-height font, then obviously nothing sensible
will appear in the way of text, but you'd at least like to avoid a
crash. But currently, js_canvas_find_font_midpoint will make a canvas,
print some height-0 text into it, and try to retrieve the image pixels
to see what the actual font height was - and this will involve asking
getImageData for a zero-sized rectangle of pixels, which is an error.

Of course, there's only one possible return value from this function
if the font height is 0, so we can just return it without going via
getImageData at all.

(This crash can be provoked by trying to resize the puzzle canvas to
Far Too Small, or by interleaving canvas resizes with browser-tab
zooming. I've had one report that it also occurs in less silly
situations, which I haven't been able to reproduce. However, this
seems like a general improvement anyway.)
2023-05-26 21:29:29 +01:00
8237b02be4 Loopy: fix redraw issue due to enlarged dots.
dot_bbox() wasn't taking into account the new size of the dots, so
sometimes a rectangle of the grid would be redrawn without a partial
dot it should have contained, because nothing had noticed that that
dot overlapped that rectangle.

Actually I'm not sure why this bug wasn't happening _before_ I
enlarged the dots, because the previous code seemed to think dots had
a fixed size in pixels regardless of tile size, which wasn't even
true _before_ my recent commit 4de9836bc8c36cd. Perhaps it did occur,
just never while I was watching.
2023-05-07 21:41:50 +01:00
d0f97926e8 Isolate icons build from the running user's preferences.
Preferences that adjust the display, such as Pearl graphics style or
Light Up lit-blobs toggling, shouldn't affect the official icons, even
if a ~/.config/sgt-puzzles exists in the account that builds the
puzzles.
2023-05-05 12:51:25 +01:00
63346a8cea Windows: reorganise menu ids.
A user pointed out today that IDM_PREFS overlaps the second preset,
because I forgot that IDM_PRESETS was not a single id but the base for
an open-ended series.

Looking more closely, there are several other problems with the IDM_*
constants. IDM_GAMES (used in COMBINED mode) shouldn't be at an
arbitrary distance _above_ IDM_PRESETS, because that risks a
collision; instead, the games' and presets' ids should interleave.
Also, the ids above IDM_GAMES were going up in steps of 1, which
should have been 0x10, for the usual reason that the bottom four bits
of the id aren't guaranteed. And IDM_KEYEMUL was completely unused (I
suspect it was part of the discarded WinCE support).

Now the #defines that are the bases of series are labelled as
IDM_FOO_BASE; they interleave as they should; and there's a clear
comment.
2023-05-03 13:01:43 +01:00
89e9026ee4 midend_apply_prefs: apply prefs to the right ui.
The function takes a game_ui pointer as an argument, and then ignores
it and unconditionally applies the midend's saved preferences to me->ui.
2023-05-02 19:51:29 +01:00
e0bb6d3b85 Untangle: add a 'snap to grid' user preference.
Requested by a user who otherwise found themself spending too much
time struggling to get lines nicely horizontal or vertical.

The implementation is easy, but the question is what size of grid is
appropriate. Untangle's own generated games are constructed by making
a planar graph drawn on an extremely coarse grid - but snapping to
_that_ grid would give away information about the puzzle solution, and
also, Untangle wouldn't know any similar information about graphs
generated by any other method.

So a better approach is to choose a size of grid that guarantees that
_any_ graph with n vertices can be drawn on it with nonintersecting
straight edges. That sounds like a tricky maths problem - but happily,
the solution is given in a book I already had a copy of. References in
a comment (plus a proof of a pedantic followup detail about multiple
planar embeddings).
2023-05-01 14:30:54 +01:00
628cc6785b Untangle: replace manual int64 bodging with int64_t.
Where possible, that is. If our compilation environment has provided
int64_t, we can just make our int64 type be that, and not have to mess
around with multi-word arithmetic at all.
2023-05-01 14:30:54 +01:00
c48a9f44ff Replace check of __STDC_VERSION__ with HAVE_STDINT_H.
Just spotted this in puzzles.h. We don't need to guess any more from
the C standards version whether stdint.h is available: we've actually
checked _precisely that_ in cmake, so it's better to use the answer.
2023-05-01 13:42:43 +01:00
4de9836bc8 Loopy: slightly increase the size of dots.
In the Hats tiling, each tile has two consecutive edges collinear.
When both edges are turned on, i.e. drawn in black just like the dot,
it becomes _just slightly_ tricky to spot the dot in the middle of
that straight line, which is important if you're counting edges around
the face to check a clue.

Increasing the radius from 2/32 to 3/32 of tile size is far too big.
2.5/32 seems reasonable, though.
2023-04-29 14:10:40 +01:00
b293605ce3 hat-test: fix memory leaks.
I ran this again today with Leak Sanitiser on, which complained.
2023-04-29 13:40:51 +01:00
bbbbc8b22b Pattern: Reduce row clue spacing if there are lots of them
Normally, we put two spaces between row clues, but if there are a lot
then even with a smaller font size they might overflow the left edge
of the window.  To compensate, go down to one space between clues
instead of two if there are a lot of them.  On my laptop the GTK build
demonstrates overflow with "13x1:1//1//1//1//1//1//1/1.1.1.1.1.1.1".
2023-04-24 22:25:12 +01:00
fcf1d274c3 Pattern: switch to small font when there are many row clues
If you have a particularly large number of clues in a row, they can
end up falling off the left edge of the window.  This used not to be a
problem because the rendering code would squash them closer together
if necessary.  But then I switched to drawing them all as a single
string (so that two-digit row clues would get enough space with a
large font) and that broke the old mechanism.

Now we detect if there are enough clues that our conservative guess at
the string length looks like overflowing in the big font, and switch
to the small one if necessary.  If we had a drawing call to measure a
string then we could be cleverer about this, but we don't.

This problem can be demonstrated with "7x1:1//1//1//1/1.1.1.1" in the
GTK port with the fonts my laptop has.

I think overflow can still occur even with a small font, so once I've
demonstrated that I'll try to fix it.
2023-04-24 22:13:15 +01:00
42cc7f6cb0 Correct a comment in Pattern's clue-drawing code 2023-04-24 21:45:02 +01:00
20d424bf1b Emscripten: change the localStorage key used for preferences.
I picked pathname + "#preferences" because Ben suggested either that
or pathname + "/preferences", and I thought the former sounded neater.
But Ben now suggests that it might be better to avoid using # in the
key, just in case anyone should later want to use the whole URL
_including_ the fragment ID as the key for some other localStorage
usage - for example, autosaved progress per puzzle _instance_, keying
off the puzzle URL with a fragment ID in the game description.

So, per Ben's replacement suggestion, change the "#" to a space.
2023-04-24 14:24:19 +01:00
43db4aa38e Support user preferences in the Emscripten frontend.
Here, user preferences are stored in localStorage, so that they can
persist when you come back to the same puzzle page later.

localStorage is global across a whole web server, which means we need
to take care to put our uses of it in a namespace reasonably unlikely
to collide with unrelated web pages on the same server. Ben suggested
that a good way to do this would be to store things in localStorage
under keys derived from location.pathname. In this case I've appended
a fragment id "#preferences" to that, so that space alongside it
remains for storing other things we might want in future (such as
serialised saved-game files used as quick-save slots).

When loading preferences, I've chosen to pass the whole serialised
preferences buffer from Javascript to C as a single C string argument
to a callback, rather than reusing the existing system for C to read
the save file a chunk at a time. Partly that's because preferences
data is bounded in size whereas saved games can keep growing; also
it's because the way I'm storing preferences data means it will be a
UTF-8 string, and I didn't fancy trying to figure out byte offsets in
the data on the JS side.

I think at this point I should stop keeping a list in the docs of
which frontends support preferences. Most of the in-tree ones do now,
and that means the remaining interesting frontends are ones I don't
have a full list of. At this moment I guess no out-of-tree frontends
support preferences (unless someone is _very_ quick off the mark), but
as and when that changes, I won't necessarily know, and don't want to
have to keep updating the docs when I find out.
2023-04-24 10:17:33 +01:00
2b6d34adbd emcc.c: remove savefile_read_ctx.
It wasn't ever used! Looks as if I pasted it in here from one of the
other implementations, before realising that wasn't how I was going to
read save files at all.
2023-04-24 10:04:20 +01:00
8c968483f8 emcc.c: missing (void) in a function definition.
This isn't C++, ahem.
2023-04-24 10:04:20 +01:00
12b2608b24 Fix bounds check in buffer_append.
We're about to append one character to the buffer _and_ put a \0 after
it, so we need the buffer to be at least _two_ characters longer than
where the current position is.

I think this bug would have had a hard time showing up in normal use,
but I managed to trigger it by completely messing up a prototype
Emscripten preferences implementation, and a good thing too.
2023-04-24 10:04:20 +01:00
bf453043db Support user preferences in the Mac frontend.
The low-level load and save routines are basically copy-pasted from
gtk.c, with only minor changes to deal with the different locally
appropriate config file location and the lack of savefile_write_ctx.
2023-04-24 08:49:43 +01:00
81680583fd GTK save_prefs: fix a wrongly sourced error report.
After a failed rename(), we should find out what went wrong by looking
in errno itself, not in wctx->error which reported a problem in the
previous step.
2023-04-24 08:35:42 +01:00
e080e0e325 Slant: actually check ui->swap_buttons.
Apparently I wrote the new-look code that sets up the prefs field in
the UI, but didn't remember to rewrite the code in interpret_move that
ought to read it.
2023-04-23 22:34:40 +01:00
f01b1674bd GTK: stop referring to &thegame in prefs I/O functions.
I had to do this in the Windows front end to cope with compiling in
both COMBINED and one-puzzle mode: you can't refer to &thegame because
it might not exist in this build, so instead, if you want to load/save
preferences for a given midend, you ask that midend for _its_ game
structure, and use that to make the necessary file names.

On Unix, we don't have COMBINED mode. But now I've thought of it, this
seems like a good idiom anyway, for the sake of futureproofing against
the day someone decides to implement combined mode on Unix.

delete_prefs() doesn't get passed a frontend _or_ a midend, so that
just has to take a bare 'const game *' parameter, and in main() we
pass &thegame to it. So that will still need changing in a combined
mode, if one is ever added.
2023-04-23 14:58:31 +01:00
1fa28340e8 Support user preferences on Windows.
This is done using basically the same methods as on Unix, and just
translating the system calls in save_prefs to a different API.
2023-04-23 14:54:29 +01:00
35cd44c563 make_prefs_path(): tolerate NULL inputs.
Just noticed that if prefs_dir() returns NULL, we'll already have
passed it to this function before the calling functions get round to
checking. I think it's less wordy all round to make this helper
function propagate NULL than to mess about with lots of extra if
statements in between all the calls.
2023-04-23 14:53:11 +01:00
5c0def1850 Document the new Net preference.
Naturally, I only remembered to do this after 'git push'.
2023-04-23 14:16:17 +01:00
2d91261eb3 Net: preference for how loop highlighting interacts with locking.
Net's loop highlighting detects any loop in the current state of the
grid. I've occasionally found that to be a bit of a spoiler, since
sometimes it can point out a deduction I should make before I've
figured it out for myself - e.g. when I've just locked all but two of
the squares involved in the loop, and the last two _just happen_ to be
oriented so as to complete the loop. In that situation I'd prefer if
the loop _didn't_ immediately light up and point out to me that I need
to arrange that those squares aren't connected to each other.

The simple answer is to only count edges connecting two _locked_
squares, for the purposes of loop detection. But this is obviously
unacceptable to some players - in particular, those who play without
the locking feature at all. So it should be a user preference.
2023-04-23 14:02:39 +01:00
c0bd524848 Fix failure to update me->ui when changing preferences.
This must have been introduced during a last-minute rebase, or similar
- I'm sure it worked a couple of days ago! Because midend_set_config
passed a NULL game_ui to midend_set_prefs, the latter would make up a
temporary UI and apply the changes to that. As a result, the midend's
main UI would keep the original backend preferences, and those would
also be the ones saved to the config file.
2023-04-23 14:00:40 +01:00
e2add4185c GTK: add a command-line --delete-prefs option.
If you want to remove your saved preferences for a puzzle for any
reason (perhaps because of one of those unsympathetic managers, or
perhaps because it's become corrupted in some way), you can of course
manually go and find the config file and delete it. But if you're not
sure where it is, it's helpful to have a method of telling the puzzle
itself to delete the config file.

Perhaps it would be useful to expose this in the GUI as well, though
I'm not quite sure where is the best place to put it.

(_Perhaps_ it would also be useful to have a more thorough option that
deleted _all_ puzzles' configurations - although that would involve
telling every separate puzzle binary in this collection what all the
other ones' names are, which would be an entirely new build headache.)
2023-04-23 13:26:36 +01:00
6c66e2b2de Support preferences in the GTK frontend.
Finally, some user-visible behaviour changes as a payoff for all that
preparation work! In this commit, the GTK puzzles get a 'Preferences'
option in the menu, which presents a dialog box to configure the
preference settings.

On closing that dialog box, the puzzle preferences are enacted
immediately, and also saved to a configuration file where the next run
of the same puzzle will reload them.

The default file location is ~/.config/sgt-puzzles/<puzzlename>.conf,
although you can override the .config dir via $XDG_CONFIG_HOME or
override the Puzzles-specific subdir with $SGT_PUZZLES_DIR.

This is the first commit that actually exposes all the new preferences
work to the user, and therefore, I've also added documentation of all
the current preference options.
2023-04-23 13:26:36 +01:00
4752c7a2d9 Universal preference option for one-key shortcuts.
With this option turned off (it's on by default), the single-letter
keyboard shortcuts like 'q' for quit and 'n' for new game don't
function any more. You can still access the same functions via more
complicated shortcuts like Ctrl-Q or Ctrl-N, and front ends can
provide any other UI they like for the same operations, but this way,
people aren't at risk of blowing away half an hour of puzzling with
one misaimed key.

This is a thing people have occasionally asked for, and I've generally
resisted on the grounds that I have sympathy for people playing
puzzles at work who need to be able to close the game quickly when an
unsympathetic boss wanders by. But now we have a preferences system,
we can cater to those people _and_ the ones who don't mind.

More immediately useful: adding _at least one_ universal preference in
the initial version of this system means that there will be no games
with no preference options at all, and therefore, no need to put
conditionals all through the participating frontends to deal with
whether the Preferences menu option should even be provided. That
would have been a waste of time because all those conditionals would
just have to be removed again as soon as the first universal
preference came along - so adding an easy one _now_ means we can save
the effort of putting in the temporary conditionals in the first
place.
2023-04-23 13:26:33 +01:00