chiark / gitweb /
Simon Tatham [Tue, 2 Apr 2019 20:08:43 +0000 (21:08 +0100)]
Dominosa: rewrite the solver.
The new solver should be equivalent to the previous solver's
intelligence level, but it's more usefully split up into basic
data-structure maintenance and separate deduction routines that you
can omit some of. So it's a better basis to build on when adding
further deductions or dividing the existing ones into tiers.
The new solver also produces much more legible diagnostics, when the
command-line solver is run in -v mode.
Simon Tatham [Tue, 2 Apr 2019 17:42:01 +0000 (18:42 +0100)]
Dominosa: add a command-line solver.
I've made the existing optional solver diagnostics appear as the
verbose output of the solver program. They're not particularly legible
at the moment, but they're better than nothing.
Simon Tatham [Mon, 18 Feb 2019 21:12:16 +0000 (21:12 +0000)]
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.
Simon Tatham [Sun, 10 Feb 2019 14:05:30 +0000 (14:05 +0000)]
Pegs: clear ui->cur_jumping on undo or redo.
Fixes an assertion failure in which you move the keyboard cursor to a
peg, press Enter to indicate that you're about to jump it to
somewhere, and then instead execute an undo or redo action which
replaces the peg with a hole. Thanks to Vitaly Ostrosablin for the
report.
Simon Tatham [Fri, 25 Jan 2019 20:27:49 +0000 (20:27 +0000)]
benchmark.pl: replace use of Perl <> with <<>>.
I've only just found out that it has the effect of treating the argv
words not as plain filenames, but as arguments to Perl default 'open',
i.e. if they end in | then the text before that is treated as a
command. That's not what was intended!
Simon Tatham [Wed, 12 Dec 2018 22:18:00 +0000 (22:18 +0000)]
Replace fe->preset_menu when we change midend.
Thanks to Rocco Matano for reporting that in the -DCOMBINED version of
the Windows front end, switching games causes a crash because the
presets menu from the old midend is still left over in fe, and its
presence inhibits the setup code from making a new one. Now we throw
it out at the same time as we throw out the old midend itself.
Also, the condition 'if (!fe->preset_menu)' was misguided. I think the
point of that was to avoid pointlessly tearing down and rebuilding the
preset menu when we're _not_ changing game - but that's a cost too
small to worry about if it causes the slightest trouble. Now
fe->preset_menu should always be NULL at that point in the function,
so I've replaced the if with an assert.
Simon Tatham [Sun, 25 Nov 2018 00:46:48 +0000 (00:46 +0000)]
Fix GTK 2 crash introduced by previous commit.
Moving the snaffle_colours() call earlier is fine in GTK 3, where the
potential call to frontend_default_colour doesn't depend on the window
already having been created. But it falls over in GTK 2 where it does.
Moved the non-headless-mode version of that call back to where it was
before the --screenshot change.
Simon Tatham [Fri, 23 Nov 2018 23:44:17 +0000 (23:44 +0000)]
Don't initialise GTK in --screenshot mode.
I had this idea today and immediately wondered why I'd never had it
before!
To generate the puzzle screenshots used on the website and as program
icons, we run the GTK front end with the --screenshot option, which
sets up GTK, insists on connecting to an X server (or other display),
draws the state of a puzzle on a Cairo surface, writes that surface
out to a .png file, and exits.
But there's no reason we actually need the GTK setup during that
process, especially because the surface we do the drawing on is our
_own_ surface, not even one provided to us by GTK. We could just set
up a Cairo surface by itself, draw on it, and save it to a file.
Calling gtk_init is not only pointless, but actively inconvenient,
because it means the build script depends on having an X server
available for the sole purpose of making gtk_init not complain.
So now I've simplified things, by adding a 'headless' flag in
new_window and the frontend structure, which suppresses all uses of
actual GTK, leaving only the Cairo surface setup and enough supporting
stuff (like colours) to generate the puzzle image. One awkward build
dependency removed.
This means that --screenshot no longer works in GTK 2, which I don't
care about, because it only needs to run on _one_ platform.
Simon Tatham [Tue, 13 Nov 2018 22:06:19 +0000 (22:06 +0000)]
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.
Simon Tatham [Tue, 13 Nov 2018 21:58:14 +0000 (21:58 +0000)]
Unruly, Group: reference-count the 'immutable' array.
I noticed this during the bool trawl: both of these games have an
array of flags indicating which grid squares are immutable starting
clues, and copy it in every call to dup_game, which is completely
unnecessary because it doesn't change during play. So now each one
lives in a reference-counted structure, as per my usual practice in
similar cases elsewhere.
Simon Tatham [Tue, 13 Nov 2018 21:54:11 +0000 (21:54 +0000)]
Add missing binary 'matching' to .gitignore.
Simon Tatham [Tue, 13 Nov 2018 21:51:49 +0000 (21:51 +0000)]
Add a missing const in unfinished/sokoban.c.
I noticed this when I temporarily enabled compilation of all the
unfinished puzzles while doing the bool trawl.
Simon Tatham [Tue, 13 Nov 2018 21:45:44 +0000 (21:45 +0000)]
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.
Simon Tatham [Tue, 13 Nov 2018 21:44:02 +0000 (21:44 +0000)]
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.
Simon Tatham [Tue, 13 Nov 2018 21:43:11 +0000 (21:43 +0000)]
Adopt C99 bool in the grid.c API.
More or less trivially: the only affected declaration is the
has_incentre flag in struct grid_face.
Simon Tatham [Tue, 13 Nov 2018 21:42:28 +0000 (21:42 +0000)]
Adopt C99 bool in the shared Latin-square API.
latin_check now returns bool, and latin_solver_diff_set takes a bool
'extreme' flag. Should be non-disruptive.
Simon Tatham [Tue, 13 Nov 2018 21:41:45 +0000 (21:41 +0000)]
Adopt C99 bool in the tree234 API.
The only affected function here is splitpos234, which I don't think
these puzzles are even using at the moment.
Simon Tatham [Tue, 13 Nov 2018 21:40:49 +0000 (21:40 +0000)]
Adopt C99 bool in misc.c functions.
The 'decode' flag to obfuscate_bitmap and the 'wrap' flag to
move_cursor are the only ones affected here.
Simon Tatham [Tue, 13 Nov 2018 21:40:23 +0000 (21:40 +0000)]
Adopt C99 bool in the findloop API.
This shouldn't be a disruptive change at all: findloop_run and
findloop_is_loop_edge now return bool in place of int, but client code
should automatically adjust without needing any changes.
Simon Tatham [Tue, 13 Nov 2018 21:39:45 +0000 (21:39 +0000)]
Adopt C99 bool in the edsf API.
Now the flag passed to edsf_merge to say whether two items are the
same or opposite is a bool, and so is the flag returned via a pointer
argument from edsf_canonify.
The latter requires client code to be updated to match (otherwise
you'll get a pointer type error), so I've done that update in Loopy,
which is edsf's only current in-tree client.
Simon Tatham [Tue, 13 Nov 2018 21:38:53 +0000 (21:38 +0000)]
Adopt C99 bool in the printing API.
Not many changes here: the 'dotted' flag passed to print_line_dotted
is bool, and so is the printing_in_colour flag passed to
print_get_colour. Also ps_init() takes a bool.
line_dotted is also a method in the drawing API structure, but it's
not actually filled in for any non-print-oriented implementation of
that API. So only front ends that do platform-specific _printing_
should need to make a corresponding change. In-tree, for example,
windows.c needed a fix because it prints via Windows GDI, but gtk.c
didn't have to do anything, because its CLI-based printing facility
just delegates to ps.c.
Simon Tatham [Tue, 13 Nov 2018 21:37:09 +0000 (21:37 +0000)]
Adopt C99 bool in the midend API.
This changes parameters of midend_size and midend_print_puzzle, the
return types of midend_process_key, midend_wants_statusbar,
midend_can_format_as_text_now and midend_can_{undo,redo}, the 'bval'
field in struct config_item, and finally the return type of the
function pointer passed to midend_deserialise and identify_game.
The last of those changes requires a corresponding fix in clients of
midend_deserialise and identify_game, so in this commit I've also
updated all the in-tree front ends to match. I expect downstream front
ends will need to do the same when they merge this change.
Simon Tatham [Tue, 13 Nov 2018 21:34:42 +0000 (21:34 +0000)]
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.
Simon Tatham [Tue, 13 Nov 2018 21:31:32 +0000 (21:31 +0000)]
Add a #include of <stdbool.h>.
This is the first commit in a series which will adopt C99 bool
throughout the code base where it makes sense to do so.
Simon Tatham [Wed, 7 Nov 2018 19:16:00 +0000 (19:16 +0000)]
Undead: remove an unused structure field.
I noticed that state->common, which is shared between all the game
states in an undo chain, has a 'solved' flag in it. That's not right -
solvedness as a property of a particular state on the chain belongs in
the game_state itself, and having-at-one-point-been-solved-ness as a
persistent property of the whole chain belongs in the game_ui.
Fortunately, the game isn't actually doing it wrong:
state->common->solved is set once and then never read, so it must have
been left in from early abandoned code. Now removed.
Simon Tatham [Tue, 6 Nov 2018 18:34:47 +0000 (18:34 +0000)]
Fix an inaccurate comment.
latin_solver_diff_set takes a boolean input parameter to indicate
whether the 'Extreme'-level variant of set elimination is permitted.
But it's commented in the header file as having a boolean _output_
parameter which it sets if it _ended up_ using that variant. Probably
the latter was how it worked in an early draft, and I changed my mind
later without changing the comment.
Simon Tatham [Tue, 6 Nov 2018 18:33:21 +0000 (18:33 +0000)]
Fix a misuse of errno.
In menu_save_event, we checked ctx.error to see if an errno value had
been left in it by the savefile_write callback, but if so, then we
were passing the _current_ value of errno to strerror() in place of
the saved value in ctx.error.
This may well have been benign, but I spotted it in an eyeball review
just now and thought I'd better fix it before it bit anyone.
Simon Tatham [Sat, 6 Oct 2018 17:22:42 +0000 (18:22 +0100)]
Fix OSX build failure from latest XCode update.
To begin with, the toolchain no longer lets me build for x86-32 -
apparently MacOS is now 64-bit only.
Also, the linker now gives an error about a missing libgcc_s variant
for -mmacosx-version-min=10.4, and indeed 10.5. So I've bumped the
minimum supported OS version to 10.6.
That in turn required some changes in osx.m itself, because bumping
the min OS version caused some API deprecations to show up. Luckily I
turned out to have left myself a comment some time ago telling me what
I was going to need to do about one of them :-)
Jacob Nevins [Sun, 23 Sep 2018 15:36:30 +0000 (16:36 +0100)]
Net: highlight more errors in locked tiles.
If a locked tile has an edge pointing at a barrier, or at another
locked tile without a matching edge, colour that edge red.
Jacob Nevins [Sun, 23 Sep 2018 15:36:24 +0000 (16:36 +0100)]
Net: rename 'loop' to 'err' in UI code.
In preparation for highlighting other kinds of errors.
No intended functional change.
Simon Tatham [Fri, 21 Sep 2018 07:55:21 +0000 (08:55 +0100)]
Dominosa: some more solver thoughts.
I've been playing this game a fair bit recently, and it's probably
time I jotted down some of the deductions I've been doing in my own
brain that the game doesn't know about. (Also I had an algorithmic
thought about the area-parity technique.)
Michael Quevillon [Thu, 13 Sep 2018 04:19:32 +0000 (00:19 -0400)]
cube.c: Prohibit unsolvable single row/column game
For cube games, the minimum for any dimension should be 2, as there is
no net of the cube that is only one row/column. The previous logic
would permit a 1x7 game (for example) that could never be solved.
Simon Tatham [Tue, 24 Jul 2018 17:38:00 +0000 (18:38 +0100)]
Fix docs link from the JS Rectangles page.
It pointed to rectangles.html, which doesn't exist, in place of
rect.html which does.
Simon Tatham [Fri, 20 Jul 2018 18:21:52 +0000 (19:21 +0100)]
Tracks: stop drawing background for clues in game_print.
This makes the clue numbers actually visible in the printed output,
instead of black on black.
Simon Tatham [Thu, 21 Jun 2018 18:02:21 +0000 (19:02 +0100)]
Fix return value from newgame_undo_deserialise_read.
The read function used by midend_deserialise and friends is expected
never to perform a partial read (the main deserialisation code always
knows how many bytes it can expect to see), so it's specified to
return simply TRUE or FALSE for success/failure, rather than the
number of bytes read.
This probably wasn't breaking anything, since in the case of
deserialising from an internal memory buffer a short read could only
arise due to an outright bug constructing the buffer. But now I've
spotted it, I should fix it.
Simon Tatham [Thu, 21 Jun 2018 17:54:08 +0000 (18:54 +0100)]
Fix NUL-termination bug in saving from Javascript.
The JS code that retrieves the save-file data from emcc.c doesn't
receive a separate length value, but instead expects the data to be in
the form of a NUL-terminated string. But emcc.c wasn't NUL-terminating
it, so the save data could come out with random cruft on the end.
Lennard Sprong [Thu, 14 Jun 2018 19:45:26 +0000 (21:45 +0200)]
misc.c: Fix implementation of free_keys.
The previous version attempted to free the first element multiple times.
Simon Tatham [Fri, 1 Jun 2018 05:51:17 +0000 (06:51 +0100)]
Parallelise the build script.
Using the new feature I added to bob where it defines the variable
'nproc' to give you a sensible value to use with make -j.
Simon Tatham [Fri, 1 Jun 2018 06:22:55 +0000 (07:22 +0100)]
Fix Makefile.nestedvm so that it works with make -j.
Instead of repeatedly reusing the file name 'PuzzleEngine.class' in
the main build directory, now each puzzle's NestedVM translation is
left in a separate subdirectory so that they don't collide with each
other. A bonus is that we don't have to rename the file back and forth
between the rule that builds it and the one that consumes it.
Josh Lee [Tue, 29 May 2018 12:09:01 +0000 (08:09 -0400)]
Enable 64-bit osx build and fix a warning.
OS X is beginning to show a warning when a 32-bit application is
opened, so it's high time that this gets enabled. Fix a clang warning
exposed by this build.
Josh Lee [Tue, 29 May 2018 12:08:21 +0000 (08:08 -0400)]
Enable high resolution on osx
This consists of setting a flag in the Info.plist. Everything in the
game is resolution-independent, of course, and has been thoroughly
tested on other platforms. The only issue I found is cosmetic: The
rounded window corners become more dramatic and don't look as good
against the square status bar.
Increasing the resolution also exposes two graphical quirks that I
don't think are new. Curved rails in Tracks seem to be made of short
segments that don't quite connect, but I don't see this on Android,
and on closer inspection this is already present on low resolution in
OS X. Lines in Untangle, and also Galaxies, that are at a multiple of
45 degrees seem thinner than other lines, but I also see this on
Android — I think it's just more obvious on high resolution, and could
be adjusted with antialiasing. Everything else looks as it should, for
example when moving a window between low and high dpi displays.
Simon Tatham [Mon, 14 May 2018 17:18:28 +0000 (18:18 +0100)]
Bump the source and target versions used in javac.
I've just upgraded my build machine to Ubuntu 18.04, which has come
with a version of javac that complains about both -source 1.3 and
-target 1.3. Both are surely pretty out of date anyway, so the path of
least resistance is to just increase them to the earliest version that
javac doesn't currently complain is deprecated.
Simon Tatham [Wed, 9 May 2018 15:08:46 +0000 (16:08 +0100)]
Stop using deprecated gdk_beep().
Switched over to gdk_display_beep(), which should work in any GTK2 or
GTK3 environment. (This code base doesn't care about GTK1 any more.)
Simon Tatham [Sat, 28 Apr 2018 11:02:55 +0000 (12:02 +0100)]
Buildscr: make long parts of the build conditionalisable.
If I want to rebuild just the Javascript puzzles (for example) in
circumstances where I don't expect to need a great many
edit-compile-link cycles, it's easier to get bob to do it for me than
to remember how to set up the development tools on my path. But it
takes ages to run the whole build script if I also have to wait for
the Windows, Mac and Java puzzles to be built, not to mention the
initial Unix build that runs for no purpose other than generating the
icon images.
So now I can run the build with various time-consuming parts
conditioned out, for development purposes. Of course, the default is
still to build absolutely everything.
Simon Tatham [Sat, 28 Apr 2018 11:02:43 +0000 (12:02 +0100)]
latin.c: remove a rogue array overrun.
Oops! This was left over from an early development version of commits
4408476b7 and
000ebc507, in which I initially arranged for each
adjacency list to be terminated with the sentinel value -1 instead of
separately storing an array of the lists' lengths.
I later changed the representation to make randomising the algorithm
easier (it's much easier to shuffle an array uniformly at random if
you _don't_ have to faff endlessly to work out its length). But this
write of a no-longer- needed sentinel value in the client code must
have survived the rewrite by mistake, and also somehow evaded all my
pre-commit testing with valgrind and asan.
A user reported that the Towers Javascript version was crashing on
startup, and I think this is the cause, because it seems to fix it for
me.
Simon Tatham [Wed, 25 Apr 2018 18:24:06 +0000 (19:24 +0100)]
C89 build fixes.
Recent changes introduced a couple of non-C89-compatible mixed
declarations and code.
Franklin Wei [Tue, 24 Apr 2018 22:00:08 +0000 (18:00 -0400)]
Make static keyword come first everywhere.
I somehow missed these instances as well. Oh well.
Franklin Wei [Mon, 23 Apr 2018 23:54:43 +0000 (19:54 -0400)]
Move `static' keyword to beginning of declaration.
Rockbox's GCC warns about this with -Wextra.
Franklin Wei [Mon, 23 Apr 2018 23:43:14 +0000 (19:43 -0400)]
Add request_keys() to the rest of the unfinished games.
I just realized that only "Group" received the request_keys() field in the
game struct for some reason.
Simon Tatham [Mon, 23 Apr 2018 17:42:13 +0000 (18:42 +0100)]
Build fix: stop initialising an auto char array.
Checking with the standards, I think this is legal C99, but not legal
C89 - and we are compiling in C89 mode. Why _every_ version of gcc
didn't object, given all the warning and pedantry options, I'm not
sure, but one did, so I should fix it.
Franklin Wei [Tue, 17 Apr 2018 20:18:16 +0000 (16:18 -0400)]
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.
Simon Tatham [Sat, 21 Apr 2018 16:03:10 +0000 (17:03 +0100)]
Remove maxflow completely.
Its ability to solve general network flow problems was never actually
used in this code base; it was _always_ used for the restricted
problem of finding a matching in an unweighted bipartite graph. So now
I've switched all its clients over to the new matching.c, maxflow is
no longer needed.
Simon Tatham [Sat, 21 Apr 2018 15:51:03 +0000 (16:51 +0100)]
Convert Tents to use matching instead of maxflow.
Tents needs to construct maximal matchings in two different
situations. One is the completion check during play, in which the
existence of a perfect matching between tents and trees is part of the
win condition; the other is the initial grid generation, in which we
find a _maximal_ matching between the tents we've already placed and
all the possible neighbouring squares that are candidates for the tree
positions. Both of those are switched over.
Simon Tatham [Sun, 22 Apr 2018 12:48:07 +0000 (13:48 +0100)]
Use the new matching() for latin.c.
The new client code is a lot smaller and nicer, because we can throw
away the col[] and num[] permutations entirely.
Of course, this also means that every puzzle that incorporated latin.c
now has to link against matching.c instead of maxflow.c - but since I
centralised that secondary dependency into Recipe a few commits ago,
it's trivial to switch them all over at once.
Simon Tatham [Wed, 18 Apr 2018 19:28:17 +0000 (20:28 +0100)]
Implementation of the Hopcroft-Karp algorithm.
This is a dedicated algorithm for finding a maximal matching in a
bipartite graph.
Previously I've been solving that problem in this code base using
maxflow.c, modelling the graph matching problem as a restricted case
of the optimal network flow problem and then using a full-strength
algorithm for the latter. That's overkill, and also algorithmically
wasteful - the H-K algorithm is asymptotically better, because it can
find multiple augmenting paths in each pass (hence getting the maximum
benefit out of each expensive breadth-first search), as a consequence
of not having to worry about lower- or higher-value augmenting paths
in this restricted problem.
So I expect this algorithm to be faster, at least in principle or in
large cases, when it takes over the jobs currently being done by
maxflow. But that's not the only benefit. Another is that the data
representation is better suited to the problems actually being solved,
which should simplify all the call sites; a third is that I've
incorporated randomisation of the generated matching into the H-K
module itself, which will further save effort at each call site
because they will no longer have to worry about permuting the
algorithm's input - they just have to pass it a random_state and it
will take care of that internally.
This commit only introduces the new matching.c and builds a test
utility for it. I haven't yet migrated any clients of maxflow.
Simon Tatham [Sun, 22 Apr 2018 15:45:34 +0000 (16:45 +0100)]
Recipe: centralise dependencies for latin.c.
It's silly to have every puzzle using latin.c separately specify in
its .R file the list of additional modules that latin.c depends on, or
for that matter to have them all have to separately know how to adjust
that for the STANDALONE_SOLVER mode of latin.c.
So I've centralised a new pair of definitions into the core Recipe
file, called LATIN and LATIN_SOLVER, and now a client of latin.c need
only ask for that to get all the necessary dependencies too.
Also, while I'm here, I've moved the non-puzzle-specific 'latincheck'
test program out of unequal.R into the central Recipe.
Simon Tatham [Sun, 22 Apr 2018 12:58:27 +0000 (13:58 +0100)]
Move fgetline out into misc.c.
I'm about to want to use it outside the GTK front end.
Simon Tatham [Tue, 17 Apr 2018 17:30:48 +0000 (18:30 +0100)]
Galaxies: clarify wording of completion condition.
A user mailed me today having found it less than clear from the docs
that Galaxies will only accept a solution if the set of filled-in grid
edges consists of _exactly_ the ones that separate two distinct
regions, rather than consisting of _at least_ those and perhaps others
which neither break rotational symmetry or disconnect any region.
Stephen Clavering [Thu, 12 Apr 2018 20:15:17 +0000 (21:15 +0100)]
Fix two bugs in Range's solver_reasoning_recursion().
Firstly, it tries to use newstate after freeing it. I haven't come up
with any way of actually triggering that bug. I suppose you'd need a
grid that required recursion to solve, which the generator of course
does not normally create.
Secondly, it stores M_BLACK or M_WHITE in the grid before doing the
recursion, whereas it should store BLACK or WHITE. I believe that since
it tries M_BLACK first, which is zero, and since it's trying it on an
undecided square (also zero), this leads to infinite recursion, and an
eventual crash once you run out of stack space.
You can (sometimes) trigger this by asking for a hint on a grid where
you've already made a mistake.
e.g. on the puzzle desc below there's a "9" at (13,7). If you place a
black tile to its immediate left and right - (12,7) and (14,7) - then
press 'h', you get a beachball and then crash (on macOS at least - I
presume other OSes are similar).
15x15:i5g4d16a7a7c3b3b11f11_15d3p8b7_8b4h18c4d13b4i7a16k9a9i4b2d10c14h11b10_10b17p6d8_7f9b8b11c10a2a5d5g7i
Simon Tatham [Sun, 8 Apr 2018 16:55:59 +0000 (17:55 +0100)]
Solo: add a missing params constraint for X puzzles.
Michael Quevillon points out that neither 2x1 nor 3x1 Solo can be made
into an X Sudoku puzzle, on the grounds that whatever number goes in
one corner of the grid is ruled out from both ends (and the centre, if
any) of the opposing diagonal, and hence the X constraint can't be
satisfied.
(Also fixed a spurious full stop on a neighbouring line.)
Simon Tatham [Sun, 25 Mar 2018 21:27:38 +0000 (22:27 +0100)]
Fix false-positive completion detection in X Solo.
Spotted by Michael Quevillon. After checking the set of digits
appearing in one of the two main grid diagonals, we weren't clearing
the used[] array before using it to check the same thing about the
other diagonal. So if the first diagonal we check has one of
everything, then the second will be misidentified as correct (for the
purposes of game-completion detection, although not error
highlighting) even if it doesn't have one of everything.
Simon Tatham [Mon, 26 Feb 2018 20:49:57 +0000 (20:49 +0000)]
towerssolver: always print solver diagnostics in -v mode.
The branch of the code that claimed the puzzle to be ambiguous was not
also re-running the solver with diagnostics enabled, so that if a user
tries to use this tool to hand-design a puzzle, they do not get
feedback on what the multiple legal solutions actually are.
Simon Tatham [Mon, 26 Feb 2018 20:49:14 +0000 (20:49 +0000)]
latin.c: dump every solution found during recursion.
In solver_show_working mode, we were logging all the deductions,
guesswork and backtracking, but not printing out the actual solution
(if any) reached at the end of each branch of the tree.
Adrian Heine [Sat, 20 Jan 2018 12:05:18 +0000 (13:05 +0100)]
Create 96x96 icons for gnome-shell
Simon Tatham [Sat, 9 Dec 2017 21:28:41 +0000 (21:28 +0000)]
Forbid undo-of-new-game after midend_set_config.
This is another situation in which the midend's state, at the time
midend_new_game tries to save it, is not such as to generate a viable
serialisation. In this case, it's because after the user enters a game
id into the Game > Specific or Game > Random Seed dialog box,
midend_set_config will have _already_ started overwriting important
fields of the midend such as me->desc and me->seed, before
midend_new_game is even entered.
In fact this caused an assertion failure (thanks to Lennard Sprong for
reporting it), because one of those fields was set to NULL, so the
resulting serialisation buffer was incomplete, leading to a
deserialisation error later. But I was lucky: if I hadn't been, the
serialisation might have been complete, and valid according to the
deserialisation code, but would have contained a mixture of the new
game's description and the old one's move chain, which would surely
have had far stranger results.
For the moment, I'm fixing this by simply not storing a serialisation
in that situation. Perhaps a nicer approach might be to store one
before starting to overwrite midend fields, and then have
midend_new_game swap it in if it's already been generated. That way
you _could_ still undo past an action like this. But preventing the
assertion failure is a good start.
Simon Tatham [Wed, 29 Nov 2017 22:27:09 +0000 (22:27 +0000)]
Mark the 32-bit Windows build as runnable on XP.
By the same method I do it in PuTTY: manually set the Windows
subsystem version id to an earlier one than the default.
Simon Tatham [Sun, 26 Nov 2017 20:12:15 +0000 (20:12 +0000)]
Reinstate 32-bit Windows builds of Puzzles.
I've built a set of 32-bit binaries, a 32-bit zip file and a 32-bit
MSI, all delivered into a 'w32' output directory.
Simon Tatham [Sat, 18 Nov 2017 19:54:10 +0000 (19:54 +0000)]
Permit redoing past an undone New Game action.
Now, when we undo a New Game by deserialising the stored version of
the previous game, we start by serialising the _current_ game into a
second serialisation buffer in the midend. Then, if the user tries to
redo back past that undo action, we can re-serialise the earlier game
and re-deserialise the newer one.
A few users had complained (with various degrees of politeness) at the
lack of this ability, because in true xkcd #1172 style, it broke their
workflow. Specifically, if you were a fan of holding down 'u' to undo
all the way back to the start of your current game, you'd have
overshot into the previous game and then have no way to return to the
game you wanted to be at the start of.
This slightly changes the previous interaction of Redo with New Game.
Previously, New Game would save the entire undo chain of the
serialised game, including anything forward of the current position;
so if you took actions move1,move2,undo,newgame then the serialised
game state would include both move1 and move2 with the current
position between them; hence, an undo would restore the old game to a
position just after move1, and then a redo action would re-enact
move2. Now, midend_purge_states is called before serialising the old
game, so in that scenario move2 would be completely lost as a side
effect of the new-game action. (Just as it would be if you made any
_other_ undoable move in that situation.)
Conversely, making a move in the old game after you've undone back
into it will now wipe out the record of the later game, so you can't
redo into it any more.
Simon Tatham [Sat, 18 Nov 2017 19:36:10 +0000 (19:36 +0000)]
Refactor to make me->newgame_undo a small struct.
The three related buf/len/size fields are now a sub-structure of the
main midend, and the serialise/deserialise functions that address
those fields now take a pointer to that sub-structure rather than to
the midend as a whole. This will make it easy for me to drop in a
second substructure alongside it, for redo, and reuse those read and
write functions by just changing the context pointer.
Simon Tatham [Sat, 18 Nov 2017 15:22:49 +0000 (15:22 +0000)]
Standardise character encoding of source tree on UTF-8.
Editing LICENCE just now, I happened to notice that the accented
letter in Jonas Kölker's name was encoded in ISO 8859-1, as is the
occurrence of the same name in filling.c - but _not_ the one in
guess.c, which was in UTF-8 already. That seems needlessly confusing,
so let's sort it out. Now every text file in this git repository is
suitable for interpreting as UTF-8.
Simon Tatham [Sat, 18 Nov 2017 15:16:40 +0000 (15:16 +0000)]
New grid type: the trihexagonal tiling, or 'kagome lattice'.
Regular hexagons and equilateral triangles in strict alternation, with
two of each interleaved around each vertex.
https://en.wikipedia.org/wiki/Trihexagonal_tiling
Thanks to Michael Quevillon for the patch.
Simon Tatham [Sat, 28 Oct 2017 10:36:47 +0000 (11:36 +0100)]
Solo: remove some overzealous assertions in the solver.
There were a couple of places where we enforced by assertion that our
solver state had not become inconsistent, on the assumption that some
previous piece of solver would have detected that the puzzle was
impossible before getting to that place in the code.
But in fact the combination of Killer and Unreasonable modes falsified
at least two of those assumptions: 'solo --test-solve --generate 100
3x3kdu#12345' triggered an assertion failure in solver_set, and with
that one fixed, the same command triggered a second failure in
solver_killer_sums.
In both cases, the fix is simply to return -1 for 'puzzle is
inconsistent', which will cause the Unreasonable recursive solver to
abandon that branch of its search tree, backtrack, and try a different
guess at some previous square.
Thanks to Anders Höglund for the report.
Simon Tatham [Sat, 28 Oct 2017 07:55:33 +0000 (08:55 +0100)]
Map: stop storing pixel coordinates in game_ui.
The fields (ui->dragx,ui->dragy) stored the pixel coordinates of the
visible cursor (if any), no matter whether that cursor was being
displayed in response to a mouse dragging action or arrow-key
activity. But this meant that resizing the window while the keyboard
cursor was visible would cause the cursor to be drawn in totally the
wrong place in the newly resized window: you get a new drawstate with
a fresh blitter (so at least no part of the old-size window is
accidentally 'restored' on to the new-size one), but the ui fields
saying where _next_ to draw the cursor would still have bogus values
left over from the previous window size.
To fix this, I've arranged that we simply don't use ui->dragx and
ui->dragy any more in keyboard cursor mode: instead, we leave it to
game_redraw to spot that the keyboard cursor is visible and compute
its pixel coordinates for display.
A knock-on effect is that when we need to know which region is under
the cursor during interpret_move, we can't use the previous strategy
of just calling region_from_coords(ui->dragx, ui->dragy) regardless of
which kind of cursor is active. So I've split that function up into an
inner section taking a tile and a displacement from the tile's centre
and an outer part which derives those from real pixel coordinates in
the case where the cursor is originating from a mouse drag; then
there's a new alternative outer part which derives the same (tile,
displacement) pair purely from the game_ui keyboard cursor data
without having to explicitly translate into pixels and back in the
middle. Probably a less fragile strategy anyway.
Simon Tatham [Tue, 24 Oct 2017 18:39:11 +0000 (19:39 +0100)]
Build fixes for GTK2.
I had left one mention of the new GTK3-only variable
'awaiting_resize_ack' unprotected by #ifdefs. Also, the GTK2 version
of message_box() was missing some consts in its prototype, presumably
because when I had that #ifdeffed out the compiler didn't warn me
about those ones.
Simon Tatham [Thu, 19 Oct 2017 23:33:54 +0000 (00:33 +0100)]
Unequal: run check_complete() after a hint move.
A user sent in the game id
7:0,0L,0,0,0L,0L,1,0U,0U,0D,0UD,0,0,7,0,0D,0,0D,0,0,0,0,0,0,0,0,0,0D,0,0,3,0,0U,0,0,0,0,2,6,0,0U,0,0,0,7,2,0,0U,5L,
starting from which, one press of 'h' will fill in a 2 in the top left
corner and possibilities 345 to its right; if you then fill in 5 there
and press 'h' again, the hint system fills in the whole of an apparent
solution which isn't the one a proper use of the Solve feature would
give you - except that it's not actually a second solution, because
one < clue on the top row is violated. Without this fix, that < failed
to light up red, making the puzzle look ambiguous if you're not paying
enough attention to spot it.
Stefan Bühler [Sat, 7 Oct 2017 21:21:45 +0000 (23:21 +0200)]
fix loop condition
prev[sink] == 0 means there was a path found with the last step being a
forward edge to the sink, and the edge being at index 0 in the array.
This is a valid path (the same as any other path indicated by prev[sink]
> 0).
Simon Tatham [Fri, 6 Oct 2017 18:49:05 +0000 (19:49 +0100)]
Fix assertion failure if you Undo right at startup.
The initial call to midend_new_game() was creating a partial
serialisation containing no game states at all, which meant that if
your first UI action was an undo operation, the game would try to
deserialise that and complain that it was incomplete. Now we detect
that in advance and don't create a useless serialisation in the first
place.
Simon Tatham [Sun, 1 Oct 2017 13:45:12 +0000 (14:45 +0100)]
Make the code base clean under -Wwrite-strings.
I've also added that warning option and -Werror to the build script,
so that I'll find out if I break this property in future.
Simon Tatham [Sun, 1 Oct 2017 13:04:47 +0000 (14:04 +0100)]
Assorted char * -> const char * API changes.
I went through all the char * parameters and return values I could see
in puzzles.h by eye and spotted ones that surely ought to have been
const all along.
Simon Tatham [Sun, 1 Oct 2017 12:53:24 +0000 (13:53 +0100)]
Return error messages as 'const char *', not 'char *'.
They're never dynamically allocated, and are almost always string
literals, so const is more appropriate.
Simon Tatham [Sun, 1 Oct 2017 12:38:35 +0000 (13:38 +0100)]
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.
Simon Tatham [Sun, 1 Oct 2017 11:52:12 +0000 (12:52 +0100)]
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.
Simon Tatham [Sun, 1 Oct 2017 13:50:58 +0000 (14:50 +0100)]
Fix an int->pointer cast warning in windows.c.
If I increase clang-cl's warning pickiness, it starts objecting to my
cast to HMENU from a (potentially, in 64 bits) smaller integer type.
Actually I don't think there's a problem there - all the integer ids I
cast to HMENU are nice small numbers and a widening cast is just fine.
But I can suppress the warning by using INT_PTR instead of int in the
prototype for mkctrl, so it's easiest just to do that.
Simon Tatham [Sun, 1 Oct 2017 12:52:16 +0000 (13:52 +0100)]
Make newgame_undo_buf 'char *', not 'void *'.
This fixes a compile error under -pedantic at the point where we do
pointer arithmetic on it.
Simon Tatham [Sun, 1 Oct 2017 09:22:35 +0000 (10:22 +0100)]
Forbid undo of new-game if it would change the params.
The newgame_undo data was being saved on every call to
midend_new_game, including the one just after midend_set_params when a
new puzzle preset was selected. So you could select a new preset from
the menu, and then press 'u', and the midend would _try_ to undo that
operation and restore the previous game with a different set of
parameters.
This would do the wrong thing in the front end, because front ends in
general will not be expecting that a change of game parameters might
result from an arbitrary keyboard event - they won't be expecting to
have to call the function that moves the highlight in the game-type
menu, for example, and they _certainly_ won't be expecting that a
window resize might be necessary in response to a random keystroke.
One possible response would be to fix all the front ends so that they
_are_ prepared for either of those consequences of a keystroke event,
and then it would be possible to undo not only the New Game menu
option and the 'n' key but also undo any selection of a preset from
the game-type menu, or even a full re-customisation of the game
settings. But that would be quite an upheaval even in _my_ front end
collection, and also probably be awkward for downstream front ends, so
until I'm convinced of the value of going to all the effort, the
simpler approach is just to disallow undoing a new game in those
situations.
(This does mean that re-selecting the _already active_ game preset
from the type menu will be treated as an undoable new-game event,
which I think is an acceptable UI oddity.)
Simon Tatham [Sun, 1 Oct 2017 08:15:24 +0000 (09:15 +0100)]
Style tweaks to the newgame_undo patch.
I've renamed the new midend variables to match my usual naming
convention of using 'size' for the total buffer size and 'len' for the
amount of currently used space (and a couple of other variables to
match those in turn), partly for consistency and also because the name
'midend_undo_used' made me half-expect it to be a boolean. The buffer
itself is called 'midend_undo_buf', again to avoid making it sound
like a function or flag.
Buffer growth is still geometric but less aggressive (factor of 5/4
each time instead of 2), in the hope of wasting less memory on low-RAM
platforms; newgame_undo_deserialise_read should have been static, and
now is; newgame_undo_buf is initialised using NULL rather than 0 so it
doesn't look like an integer, and is freed with the rest of the
midend.
And I think we _should_ enforce by assertion that midend_deserialise
didn't return an error, because there's no reason it ever should in
this situation (unlike when reading from a file, where the user might
have provided the wrong file or a corrupted one). This immediately
allowed me to spot a bug in the existing deserialisation code :-)
Ian Jackson [Sat, 30 Sep 2017 18:50:49 +0000 (19:50 +0100)]
midend: Allow "new game" to be undone
It is annoying when one intends to choose "restart" and chooses "new
game" instead. Right now, the puzzle one wanted to try again is
discarded.
To fix this we are going to have to save a lot more information than a
normal game state. Handily, we already have the serialise/deserialise
machinery.
The advantage of using this is that the previous game is easily saved
in its entirety, including its own undo history, and also probably in
a more compact format.
The (de)serialisation interface is rather clunky for use with a memory
target. Sadly none of the existing implementations of a resizing
memory array have been conveniently brought out into puzzles.h, and I
think that that's beyond the scope of what I wanted to do here.
We don't serialise the new game undo serialisation data. So
loading/saving doesn't preserve any "new game" undo, as does "new
game" twice (and as does context switching on a Palm Pilot).
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Simon Tatham [Sun, 1 Oct 2017 08:59:50 +0000 (09:59 +0100)]
midend_deserialise: accept an extra validation function.
This will let me do a 'conditional deserialisation' operation, in
which we fully decode the serialised data and then (assuming that gave
no errors) decide whether or not to actually install it based on some
arbitrary condition.
I don't think there's any possible use for the extra check function
_outside_ midend.c, so I've left the API for front ends as it is; the
externally visible midend_deserialise() doesn't have the new
parameter, and only midend_deserialise_internal() includes it.
Simon Tatham [Sun, 1 Oct 2017 08:50:22 +0000 (09:50 +0100)]
midend_deserialise: keep deserialised data in a struct.
Lots of the local variables in midend_deserialise are now fields of a
structure which contains everything that is _going_ to be written into
the midend once we finish validating it all. This makes it easy to
keep all that data together, and (in future) pass it to other
functions all in one go.
No functional change.
Simon Tatham [Sun, 1 Oct 2017 08:15:49 +0000 (09:15 +0100)]
deserialise: use the right one of {,c}params.
The serialised game stores a long-term and a short-term parameter
structure, which correspond to me->params (the thing that gets used by
the next New Game command) and me->curparams (the thing that _was_
used to generate _this_ game). So data relevant to the current game
ought to be validated against the latter, but in fact I was
accidentally passing the former to several validation calls.
I think this probably avoided causing a problem because typically
params and cparams don't differ very much: the usual reason why
they're not the same is that somebody has manually entered a game
description involving an incomplete description of the parameters
(lacking generation-specific details like difficulty level), but by
the very fact that those incomplete descriptions have to contain
_enough_ information to understand a specific game description,
copying just those parts of the description into the long-term params
structure makes the two similar enough that validation won't fail.
However, testing an upcoming patch which calls midend_deserialise at a
more difficult moment (specifically, just after midend_set_params,
meaning that the two params structures can now differ _arbitrarily_)
reveals my error. Fixed to use cparams where that's the right thing.
Ian Jackson [Sat, 30 Sep 2017 18:50:48 +0000 (19:50 +0100)]
tracks: Make error clue background white
This makes them stand out more.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Ian Jackson [Sat, 30 Sep 2017 18:50:47 +0000 (19:50 +0100)]
tracks: Greyscale colour initialisation: line up columns
This makes it much easier to see the commonality in these formulaic
lines.
Whitespace change only.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Ian Jackson [Sat, 30 Sep 2017 18:50:46 +0000 (19:50 +0100)]
tracks.c: draw_clue: Introduce bg parameter
No functional change.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Simon Tatham [Sat, 30 Sep 2017 21:02:39 +0000 (22:02 +0100)]
Fix changing puzzle size in a maximised GTK3 window.
While working on the Net scalability today I noticed that changing
preset from (say) 13x11 to 5x5 in GTK3 Net while the window is
maximised does not have the desired effect (that being that, since the
maximised window does not change size, the new puzzle size is instead
scaled to fit neatly in the existing window).
A git bisect suggests that this was a side effect of commit
8dfe5cec3;
it looks as if there was a useful side effect of setting fe->area as
the 'geometry widget' for fe->window, namely, that any attempt to
resize the window thereafter (even if it had no effect on the window
size) would trigger a configure event on the geometry widget, so we'd
get a notification of our new size even if it was the same as our old
size.
But that 'geometry widget' feature is deprecated, so I have to work
around it another way. Fortunately, I've found a fallback event that
still does occur, namely "size_allocate" on fe->window. So I'm
trapping that as well and using it as an indication that a configure
event won't be forthcoming.
Simon Tatham [Sat, 30 Sep 2017 20:18:52 +0000 (21:18 +0100)]
Fix auto-selection of presets in GTK.
In commit
a7dc17c42 I apparently introduced two bugs in
changed_preset(). Firstly, the Custom menu option was being written
into the 'found' variable in nearly all cases, because it has a NULL
user-data pointer which caused it to take the wrong branch of an if
statement due to an erroneous complex condition. Secondly, having
written _something_ into 'found', I had set it to inactive rather than
active due to forgetting to change a FALSE into a TRUE.
Now when I start up Net with my usual nonstandard default parameters
(I like the 13x11 wrapping, so I set NET_DEFAULT=13x11w in my
environment), the right menu entry comes up ticked.
Simon Tatham [Sat, 30 Sep 2017 15:40:18 +0000 (16:40 +0100)]
Net: rewrite the drawing code for better scalability.
Previously, the network, grid edges and barriers were all drawn with
fixed line thickness of one pixel, or three pixels in the case of
wires (one central one in a lit-up colour and a black border pixel on
each side). This worked badly on high-DPI displays, or in any other
environment where you expand the window noticeably.
I've tried a few times before to fix this, but the problem was always
that the Net drawing code was complicated and confusing, because Net
was one of the founding puzzles in this collection and its drawing
code predated a lot of the sensible organisation and best-practice
doctrines I've come up with since then and used in the later puzzles.
(The best example of this was the way that redrawing a grid square
always redrew _all_ the surrounding borders, which is very confusing
in the light of my later practice of dividing up the grid into
disjoint regions that are always redrawn with clipping.) It was hard
to make any change to the Net graphics with the code in that state; I
tried several times and decided I couldn't sensibly make it work
without throwing it all away and rewriting from scratch, which I was
always reluctant to do.
But not any more! Since Ian sent some patches to improve the graphics
scaling in Tracks, and since Net was at the top of my personal
wishlist of games that needed the same treatment, I've decided it's
time to do just that.
So, this commit throws out all of Net's previous redraw code, and
replaces it with something in the more modern style I usually adopt in
new puzzles. The new draw_tile() doesn't read data out of the game
state willy-nilly; instead it takes a single word of bit-flags
describing everything about the tile it's drawing, and makes all its
decisions based on that word. And the main redraw loop constructs a
whole array of flags words describing what it would _like_ the grid to
look like, and then calls draw_tile() for every square whose word
doesn't match the one that was previously drawn there. (With the one
exception that the presence of the TILE_ROTATING flag in either the
old or new word forces a redraw _even_ if the two words are identical,
because that's how the move animation works.)
The new graphics look more or less the same at the default resolution
(there may be a pixel difference here or there but I don't think it's
noticeable if so), but now they scale up properly if you enlarge or
maximise the puzzle window.
Simon Tatham [Fri, 29 Sep 2017 18:20:49 +0000 (19:20 +0100)]
Net: reference-count the barriers array.
Net is one of the very oldest puzzles in the collection, and has
apparently been physically copying the complete collection of totally
immutable barrier data in every game state since 2004. About time it
stopped, I think!
Simon Tatham [Fri, 29 Sep 2017 16:22:56 +0000 (17:22 +0100)]
tracks: thicken the lines of the grid itself.
Since these lines are always orthogonal, it's easier to draw them
using draw_rect than draw_thick_line.
Previously, the grid lines were drawn just inside the top and left
edges of the region redrawn by draw_square(), so only the bottom and
right edges of the whole grid were not covered by any draw_square
call. To avoid having to shift the logical grid centre, I'm now
drawing parts of the grid outline on all four sides of the
draw_square() region, so that half the thickened grid edge protrudes
on every side of the grid as a whole.
In the process of splitting up the grid line width into the part on
the top and left edges and the part on the bottom and right, I've
renamed the confusingly named BORDER_WIDTH define (which wasn't used
anyway) to a set of things that make it clear that they're referring
to the grid lines as opposed to the border of the whole puzzle.
Simon Tatham [Fri, 29 Sep 2017 16:07:08 +0000 (17:07 +0100)]
Fix a typo in devel.but.
Ian Jackson [Fri, 29 Sep 2017 13:29:58 +0000 (14:29 +0100)]
tracks: Roughly double the thickness of the "no track" crosses
The default of 1/30 is rather thin, and probably wasn't chosen
deliberately (since it was just inherited from the default 1-pixel
line width, and the preferred tile size).
Thicker crosses stand out more and make play easier.
Use 1/16 since it's a rounder number than 1/15 :-).
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>