chiark / gitweb /
sgt-puzzles.git
6 years agoAssorted char * -> const char * API changes.
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.

6 years agoReturn error messages as 'const char *', not 'char *'.
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.

6 years agoUse a proper union in struct config_item.
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.

6 years agoNew name UI_UPDATE for interpret_move's return "".
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.

6 years agoFix an int->pointer cast warning in windows.c.
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.

6 years agoMake newgame_undo_buf 'char *', not 'void *'.
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.

6 years agoForbid undo of new-game if it would change the params.
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.)

6 years agoStyle tweaks to the newgame_undo patch.
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 :-)

6 years agomidend: Allow "new game" to be undone
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>
6 years agomidend_deserialise: accept an extra validation function.
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.

6 years agomidend_deserialise: keep deserialised data in a struct.
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.

6 years agodeserialise: use the right one of {,c}params.
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.

6 years agotracks: Make error clue background white
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>
6 years agotracks: Greyscale colour initialisation: line up columns
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>
6 years agotracks.c: draw_clue: Introduce bg parameter
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>
6 years agoFix changing puzzle size in a maximised GTK3 window.
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.

6 years agoFix auto-selection of presets in GTK.
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.

6 years agoNet: rewrite the drawing code for better scalability.
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.

6 years agoNet: reference-count the barriers array.
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!

6 years agotracks: thicken the lines of the grid itself.
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.

6 years agoFix a typo in devel.but.
Simon Tatham [Fri, 29 Sep 2017 16:07:08 +0000 (17:07 +0100)]
Fix a typo in devel.but.

6 years agotracks: Roughly double the thickness of the "no track" crosses
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>
6 years agotracks: Scale thickness of "no track here" crosses
Ian Jackson [Fri, 29 Sep 2017 13:29:57 +0000 (14:29 +0100)]
tracks: Scale thickness of "no track here" crosses

Simply replace the calls to draw_line with calls to draw_thick_line.

We need to choose a thickness parameter.  The preferred tile size is
30, and the "draw_line" function draws a 1-pixel line, so the
thickness right now is 1/30 the tile size, at the preferred size.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
6 years agodraw_thick_line: Bound thickness by 1.0 below
Ian Jackson [Fri, 29 Sep 2017 13:29:56 +0000 (14:29 +0100)]
draw_thick_line: Bound thickness by 1.0 below

A line less than 1 pixel wide may not be visible.  So if a backend
wants to draw a line whose width scaled by the window size, that line
thickness ought to be at least 1.0.

That way if the scale is small, but still big enough that there is a
straightforward interpretation of the drawing primitives which is
legible, we implement that interpretation.

If a frontend draws a narrower line, making it wider might cause
drawing anomalies, due to the line now having a bigger bounding box.
These anomalies should occur only at small scales where currently the
display is not legible, and we should fix them as we notice them.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
6 years agoAvoid macro-generating a trailing comma in an enum.
Simon Tatham [Sun, 24 Sep 2017 15:56:18 +0000 (16:56 +0100)]
Avoid macro-generating a trailing comma in an enum.

gcc objects to this in -pedantic mode, which means other compilers are
technically entitled to object too if they like. Usually I try to
avoid it by putting a dummy value at the end of the enum, but I forgot
in this case.

(And I didn't notice, because _my_ local builds run without -pedantic,
on the grounds that configure.ac autodetects that my system's GTK
headers are not pedantic-clean. Oh well.)

6 years agoPattern: randomise rounding bias in generate().
Simon Tatham [Sat, 23 Sep 2017 18:22:37 +0000 (19:22 +0100)]
Pattern: randomise rounding bias in generate().

Now, with an odd grid size, we choose the posterisation threshold so
that half the time it delivers ceil(n/2) black squares and half the
time it delivers floor(n/2). Previously it only did the former, which
meant that asking Pattern to generate a 1x1 puzzle (with the bug in
the previous commit fixed) would always generate the one with a single
black square, and never the one with a single white square. Both are
trivial to solve, of course, but it seemed inelegant!

No change to the number of black squares in the puzzle solution can
constitute a spoiler for the player, of course, because that number is
trivial to determine without doing any difficult reasoning, just by
adding up all the clues in one dimension.

6 years agoPattern: missing special case in the solver.
Simon Tatham [Sat, 23 Sep 2017 18:17:36 +0000 (19:17 +0100)]
Pattern: missing special case in the solver.

We were filling in a row immediately as all-white if it had no clues
at all, but weren't filling in a row as all-black if it had a single
clue covering the entire row. Now we do both.

In particular, this caused the Pattern solver to be unable to take
advantage of one of the two kinds of totally obvious clue across the
_easy_ dimension of a trivial 1xN puzzle - and a special case of
_that_, as a user pointed out, is that the game generator hangs trying
to create a 1x1 puzzle, which ought to be the easiest thing in the
world!

6 years agoBuild test HTML wrapper pages for the Javascript puzzles.
Simon Tatham [Wed, 20 Sep 2017 09:22:28 +0000 (10:22 +0100)]
Build test HTML wrapper pages for the Javascript puzzles.

This should make it less annoying for me to do local testing of the JS
output of a build, before I push a change. There's a new
build.out/jstest directory containing .html files suitable for loading
in a local browser, which refer to the JS files via an appropriate
relative path to the existing build.out/js directory.

6 years agoMap Ctrl-Shift-Z to Redo.
Simon Tatham [Wed, 20 Sep 2017 15:38:31 +0000 (16:38 +0100)]
Map Ctrl-Shift-Z to Redo.

This is in addition to the existing keystrokes r, ^R and ^Y. I've
become used to Ctrl-Shift-Z in other GUI games, and my fingers keep
getting confused when my own puzzles don't handle it the same way.

6 years agoGenerate special fake keypresses from menu options.
Simon Tatham [Wed, 20 Sep 2017 09:13:36 +0000 (10:13 +0100)]
Generate special fake keypresses from menu options.

This fixes an amusing UI bug that I think can currently only come up
in the unpublished puzzle 'Group', but there's no reason why other
puzzles _couldn't_ do the thing that triggers the bug, if they wanted
to.

Group has unusual keyboard handling, in that sometimes (when a cell is
selected for input and the key in question is valid for the current
puzzle size) the game's interpret_move function will eat keystrokes
like 'n' and 'u' that would otherwise trigger special UI events like
New Game or Undo.

The bug is that fake keypress events generated from the GUI menus
looked enough like those keystrokes that interpret_move would eat
those too. So if you start, say, a 16x16 Group puzzle, select an empty
cell, and then choose 'new game' from the menu, Group will enter 'n'
into the cell instead of starting a new game!

I've fixed this by inventing a new set of special keystroke values
called things like UI_NEWGAME and UI_UNDO, and having the GUI menus in
all my front ends generate those in place of 'n' and 'u'. So now the
midend can tell the difference between 'n' on the keyboard and New
Game from the menu, and so Group can treat them differently too. In
fact, out of sheer overcaution, midend.c will spot keystrokes in this
range and not even _pass_ them to the game back end, so Group
shouldn't be able to override these special events even by mistake.

One fiddly consequence is that in gtk.c I've had to rethink the menu
accelerator system. I was adding visible menu accelerators to a few
menu items, so that (for example) 'U' and 'R' showed up to the right
of Undo and Redo in the menu. Of course this had the side effect of
making them real functioning accelerators from GTK's point of view,
which activate the menu item in the same way as usual, causing it to
send whatever keystroke the menu item generates. In other words,
whenever I entered 'n' into a cell in a large Group game, this was the
route followed by even a normal 'n' originated from a real keystroke -
it activated the New Game menu item by mistake, which would then send
'n' by mistake instead of starting a new game!

Those mistakes cancelled each other out, but now I've fixed the
latter, I've had to fix the former too or else the GTK front end would
now undo all of this good work, by _always_ translating 'n' on the
keyboard to UI_NEWGAME, even if the puzzle would have wanted to treat
a real press of 'n' differently. So I've fixed _that_ in turn by
putting those menu accelerators in a GtkAccelGroup that is never
actually enabled on the main window, so the accelerator keys will be
displayed in the menu but not processed by GTK's keyboard handling.

(Also, while I was redoing this code, I've removed the logic in
add_menu_item_with_key that reverse-engineered an ASCII value into
Control and Shift modifiers plus a base key, because the only
arguments to that function were fixed at compile time anyway so it's
easier to just write the results of that conversion directly into the
call sites; and I've added the GTK_ACCEL_LOCKED flag, in recognition
of the fact that _because_ these accelerators are processed by a weird
mechanism, they cannot be dynamically reconfigured by users and
actually work afterwards.)

6 years agoCall game_id_change_notify_function after deserialisation.
Simon Tatham [Thu, 14 Sep 2017 18:06:44 +0000 (19:06 +0100)]
Call game_id_change_notify_function after deserialisation.

That's a case in which the current game IDs have changed, so the
midend ought to be calling the front-end function (if any) that
notifies it when that happens.

The only front end of mine that was affected by this missing call was
the Javascript one, which uses that callback to update the 'Link to
this puzzle' links below the game canvas - but, of course, that front
end didn't ever call midend_deserialise until this month, so no wonder
I never noticed before.

(But downstream front ends might be affected too, for all I know.)

6 years agoFix borders on the HTML menu bar.
Simon Tatham [Thu, 7 Sep 2017 17:44:58 +0000 (18:44 +0100)]
Fix borders on the HTML menu bar.

Commit ef39e6e17 made a goof in which the 'New game' button had no
border on the left and an accidental extra one on the right, which I'm
really not sure how I failed to spot when I tested it yesterday.

6 years agoHTML: move 'New game' back out of the drop-down menu.
Simon Tatham [Wed, 6 Sep 2017 20:49:39 +0000 (21:49 +0100)]
HTML: move 'New game' back out of the drop-down menu.

The only user to send me a comment today on the new layout said that
that menu item in particular is annoying to have hidden behind more
clicks, so by a vote of one to nothing, it's back out in the open.

6 years agoMake the images on the web page link to the JS puzzles.
Simon Tatham [Tue, 5 Sep 2017 20:02:18 +0000 (21:02 +0100)]
Make the images on the web page link to the JS puzzles.

I've been thinking for a while that it's about time I did that. Those
images used to link to the Java versions of the puzzles, back when
Java was the in-browser applet platform of choice; then I made them
not link to either one when it wasn't clear which system would win;
but now JS has clearly won, it's past time the images linked to the JS
puzzles, as the obviously sensible default.

6 years agoSupport for loading games in Javascript puzzles.
Simon Tatham [Tue, 5 Sep 2017 19:48:42 +0000 (20:48 +0100)]
Support for loading games in Javascript puzzles.

This is done by showing a dialog containing an <input type="file">
through which the user can 'upload' a save file - though, of course,
the 'upload' doesn't go to any HTTP server, but only into the mind of
the Javascript running in the same browser.

It would be even nicer to support drag-and-drop as an alternative UI
for getting the save file into the browser, but that isn't critical to
getting the first version of this feature out of the door.

6 years agoSupport for saving games in Javascript puzzles.
Simon Tatham [Tue, 5 Sep 2017 19:10:16 +0000 (20:10 +0100)]
Support for saving games in Javascript puzzles.

This is done by getting midend_serialise to produce the complete
saved-game file as an in-memory string buffer, and then encoding that
into a data: URI which we provide to the user as a hyperlink in a
dialog box. The hyperlink has the 'download' attribute, which means
clicking on it should automatically offer to save the file, and also
lets me specify a not-too-silly default file name.

6 years agoFactor some HTML dialog functions out of emcclib.
Simon Tatham [Tue, 5 Sep 2017 19:09:56 +0000 (20:09 +0100)]
Factor some HTML dialog functions out of emcclib.

I'm about to want to call these from Javascript as well as from
Emscripten-compiled C, so I need versions of them that aren't wrapped
up in the Emscripten library object and also don't expect half their
parameters to be Emscripten-verse char pointers requiring calls to
Pointer_stringify.

The easiest way to achieve all of that is to turn the Emscripten-
ready versions of those functions in emcclib.js into tiny wrappers
around the JS versions, which do the pointer stringification and a
couple of other details like directing callbacks to the right C
functions.

6 years agoOrganise the JS menus/buttons bar more like a menu.
Simon Tatham [Tue, 5 Sep 2017 06:06:48 +0000 (07:06 +0100)]
Organise the JS menus/buttons bar more like a menu.

I'm about to introduce a few more options, and the button bar is
already a bit wide, so I think I should shrink it horizontally before
putting more stuff on it. So I've organised the options into something
more like the Game and Type submenus that the desktop versions use.

However, I haven't gone quite all the way, on the basis that the web
versions will be at least slightly playable on devices without much
keyboard, which makes it worth keeping the in-play actions (Undo,
Redo, and to a lesser extent Restart and Solve) accessible as
top-level buttons in a single click each.

As part of this change, I've also separated the menu bar into a
drop-down menus section and a top-level buttons section with a gap
between them, and put a signalling "..." on the end of the titles in
the former section.

This change also removes the class="left" on the game-type menu and
its submenus, which were previously there to deal with that menu being
all the way over on the far right of the menu bar. But the CSS for
those classes is still there in jspage.pl, and should still work if I
need it again in future.

6 years agoMines: show the number of safe squares left, if it's small.
Simon Tatham [Mon, 4 Sep 2017 18:50:43 +0000 (19:50 +0100)]
Mines: show the number of safe squares left, if it's small.

This is intended to make life easier for the _really_ dense grids in
which the generator algorithm falls back to a bare clearing, a tightly
packed section round the edges, and a fringe of deductions required in
between.

In that situation, you can deduce _in principle_ from the remaining-
mines counter that there are (say) one, or two, squares left to be
uncovered before everything remaining has to be a mine. And often the
game will require that deduction in order to solve it all by pure
logic. But actually doing it requires counting up the huge number of
covered squares in an irregularly shaped area and subtracting the mine
count in the status line, which is a real pain.

In fact, people failing to do that are the biggest source of (wrong)
bug reports about Mines games having no solution, so with any luck
this will make my own life easier.

6 years agoSwitch the Windows builds over to clang-cl.
Simon Tatham [Thu, 24 Aug 2017 18:12:54 +0000 (19:12 +0100)]
Switch the Windows builds over to clang-cl.

This also switches them to being 64-bit, which I think is
probably acceptable in this modern age, especially for such
a non-essential piece of software. If anyone complains I can
always reinstate a parallel 32-bit build.

To support the switch to 64-bit, this commit also changes the default
install directory in the MSI to 'Program Files' rather than 'Program
Files (x86)'.

6 years agoSet up a clang-cl makefile.
Simon Tatham [Thu, 24 Aug 2017 18:11:52 +0000 (19:11 +0100)]
Set up a clang-cl makefile.

Mostly just cribbed from the corresponding changes in PuTTY's
build setup, although since the two mkfile.pl scripts are not
_quite_ identical, I had to make a few tweaks.

6 years agoWin64-cleanness: switch to {Get,Set}WindowLongPtr.
Simon Tatham [Thu, 24 Aug 2017 18:11:13 +0000 (19:11 +0100)]
Win64-cleanness: switch to {Get,Set}WindowLongPtr.

6 years agoDiscontinue the Inno Setup Puzzles installer.
Simon Tatham [Thu, 24 Aug 2017 18:09:53 +0000 (19:09 +0100)]
Discontinue the Inno Setup Puzzles installer.

I'm getting rid of these installers in general, and also I'm about to
switch the Windows builds over to my new-look non-Windows non-Wine
system, which can't run the Inno Setup builder anyway.

6 years agoNet: fix assertion failure on insoluble puzzles.
Simon Tatham [Thu, 24 Aug 2017 18:38:29 +0000 (19:38 +0100)]
Net: fix assertion failure on insoluble puzzles.

The solver code still had an assumption, which must have dated before
the Solve menu option was introduced, that all puzzles presented to it
had at least one valid solution, and was enforcing that assumption by
assert(). Now the solver returns a more sensible failure code which
solve_game() can convert into a proper error message.

6 years agoMake cellsize a char.
Phil Bordelon [Mon, 5 Jun 2017 03:48:56 +0000 (23:48 -0400)]
Make cellsize a char.

Apparently new versions of GCC get grumpy if the possible range for a sprintf()
call exceeds MAXINT, which would never happen in actuality here due to the size
of the puzzles we're dealing with... but the compiler doesn't know that, of
course, so thinks that something may have gone horribly awry.  Changing it to a
char solves the problem neatly.

6 years agoSwitch to using Halibut's new direct .CHM generation.
Simon Tatham [Sat, 13 May 2017 17:55:52 +0000 (18:55 +0100)]
Switch to using Halibut's new direct .CHM generation.

This allows me to remove HTML Help Workshop completely from my build
dependencies, and good riddance!

6 years agoSwitch chiark URLs to https.
Simon Tatham [Sun, 7 May 2017 15:25:56 +0000 (16:25 +0100)]
Switch chiark URLs to https.

6 years agoFix infinite-loop bug in Loopy's autofollow feature.
Simon Tatham [Fri, 5 May 2017 06:09:22 +0000 (07:09 +0100)]
Fix infinite-loop bug in Loopy's autofollow feature.

Thanks to Glen Sawyer for reporting it. This is surely a consequence
of separating interpret_move from execute_move - if I'd done things in
the more obvious way, then this bug would never have happened, because
once the autofollow code had gone once round the loop it would find
that the starting edge was no longer in the same state it was looking
for. But since we don't start changing the states of edges until
execute_move(), it's possible for interpret_move() to go round and
round a cycle for ever.

Fortunately, it can _only_ happen if the edge you clicked on was part
of a loop which is the whole of its connected component - i.e. every
vertex in the cycle has degree 2 - and therefore we don't need O(N)
space to detect it (e.g. by recording whether each edge has been
visited already), but instead we can simply check if we've come back
to the starting edge.

7 years agoNitpicks to the previous commit.
Simon Tatham [Sun, 30 Apr 2017 17:40:41 +0000 (18:40 +0100)]
Nitpicks to the previous commit.

We enforce by assertion that the target buffer size is nonzero before
subtracting 1 from it; the call to fatal() is replaced by another
assert so that it will give clearer diagnostic information if it
fails; the variable holding the return value of strlen should be
size_t and its declaration should be in a C90-compatible location.
Finally, the reason why the function needs to be exist is clarified.

7 years agoWork around non-compliant sprintf().
Franklin Wei [Fri, 28 Apr 2017 23:48:36 +0000 (19:48 -0400)]
Work around non-compliant sprintf().

Rockbox's sprintf() lacks the ability to left-justify a string. Fixed
by adding a copy_left_justfied() function to misc.c.

This is a new version of this commit, as the previous version broke
saving!

7 years agoLoopy: optional 'autofollow' UI feature.
Simon Tatham [Mon, 24 Apr 2017 17:32:57 +0000 (18:32 +0100)]
Loopy: optional 'autofollow' UI feature.

This is mostly intended to make play more convenient for grid types
like the new Great-Great-Dodecagonal, and other grids with very
high-degree faces, in which it's annoying to have to spend half a
dozen mouse clicks on filling in a path of edges round the outside of
one of those faces which clearly have to all be set (or clear) if any
one of them is.

For now, the new feature is enabled by yet another of my hacky
environment variables, called LOOPY_AUTOFOLLOW. You can set it to
"off", "fixed" or "adaptive", where "off" is currently the default
(hence, no user-visible change in the default behaviour from this
change). If set to 'fixed', then toggling the state of any edge will
automatically toggle any further edges which are in the same state and
share a degree-2 vertex of the underlying grid design with the
original one. In 'adaptive' mode, the game goes even further, and will
consider outgoing edges in LINE_NO state not to count for purposes of
deciding if a vertex has degree 2.

7 years agoUse the new hierarchical preset menu feature in Loopy.
Simon Tatham [Mon, 24 Apr 2017 16:06:36 +0000 (17:06 +0100)]
Use the new hierarchical preset menu feature in Loopy.

This is the game for which I bothered to introduce the feature at all.
Because of the large number of grid types, the presets menu was
getting quite unwieldy; but because the grid dimensions for each grid
type are more or less arbitrary, it's still useful to have at least
one reasonably sized example of each grid type. So I've compromised by
moving some of the grid types into a 'More' submenu.

(I'm not particularly wedded to _which_ settings deserve relegation. I
may change my mind and move things about later on.)

7 years agoRework the preset menu system to permit submenus.
Simon Tatham [Mon, 24 Apr 2017 15:00:24 +0000 (16:00 +0100)]
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.

7 years agoJavascript puzzles: switch to a CSS-based drop-down system.
Simon Tatham [Wed, 26 Apr 2017 13:39:45 +0000 (14:39 +0100)]
Javascript puzzles: switch to a CSS-based drop-down system.

The previous control buttons and dropdowns based on form elements were
always a bit ugly: partly in a purely visual sense, and partly because
of the nasty bodge I had to do with splitting the usual 'Custom' game
type menu item into two (to get round the fact that if an element of a
<select> is already selected, browsers won't send an event when it's
re-selected). Also, I'm about to want to introduce hierarchical
submenus in the Type menu, and <select> doesn't support that at all.

So here's a replacement system which does everything by CSS
properties, including the popping-up of menus when the mouse moves
over their parent menu item. (Thanks to the Internet in general for
showing me how that trick is done.)

7 years agoUse symbolic enum values in the Loopy presets array.
Simon Tatham [Mon, 24 Apr 2017 16:09:30 +0000 (17:09 +0100)]
Use symbolic enum values in the Loopy presets array.

The use of plain numbers was needlessly confusing, and in particular
made it too easy to make unintended changes to the existing Loopy
presets when inserting a new grid enum value anywhere other than at
the end of the list.

But in the course of doing this I realised that, against all
sensibleness, the numeric indices for grid types in grid.h and in
Loopy itself don't match up! Right now I don't want to get sidetracked
into fixing the structural confusion that made that happen in the
first place, but I've at least materialised Loopy's version of the
enum with clearly identifiable LOOPY_GRID_* names.

7 years agoNew Loopy tiling: 'Great Great Dodecagonal'.
Simon Tatham [Sun, 23 Apr 2017 06:41:46 +0000 (07:41 +0100)]
New Loopy tiling: 'Great Great Dodecagonal'.

Officially known as the '3-4-6-12 tiling', according to, e.g.,
https://en.wikipedia.org/wiki/3-4-6-12_tiling .

Thanks to Michael Quevillon for contributing this patch (and for
choosing a less hard-to-remember name for the tiling :-).

7 years agoNet: rework status line to cope with empty squares.
Simon Tatham [Mon, 13 Mar 2017 19:58:22 +0000 (19:58 +0000)]
Net: rework status line to cope with empty squares.

Another oddity involving an empty square is that if it coincides with
the source square for highlights (either by original design of the
game id, or because the player Ctrl-moves the source square into an
empty grid cell during play), then everything stops being lit up as
active. That's fine - you can still play the game using other
indications of error, such as the loop detection highlight - but it
looks silly for the status line to say 'Active: 1/lots'. So in that
situation I suppress the 'active' counter completely; it comes back
when you move the source square to somewhere it's _possible_ to
highlight more than one square.

While I'm at it, I've also removed the active counter in the case
where the game is completely solved, because in that situation it's
more or less unnecessary anyway, and that way the normal course of
play on the default small grid size doesn't overflow the available
status line space.

7 years agoNet: fix completion check if top left square is empty.
Simon Tatham [Sun, 12 Mar 2017 09:11:43 +0000 (09:11 +0000)]
Net: fix completion check if top left square is empty.

A hand-typed grid is permitted to use the square type '0' (never
generated by Net's own grid generator), which is a completely empty
square. This requires an adjustment to the completion checker, so that
such squares aren't required to be connected; otherwise, a grid
containing one would be permanently uncompletable.

However, the completion checker missed one case - it was
unconditionally checking that all squares are connected to the _top
left corner_, on the basis that (before I thought of the zero square)
any source square is as good as any other if what you really want to
know is whether they're all connected to each other. But that means
that if the top left square _is_ the empty one, things to wrong - e.g.
5x5:02c328ade11adb129d7c3e524 would fail to give a completion flash.

Fixed by starting the completion-checking search from the first
non-empty square we find.

7 years agoGTK API deprecation: in GTK 3.22, stop using gdk_cairo_create.
Simon Tatham [Mon, 27 Feb 2017 19:26:06 +0000 (19:26 +0000)]
GTK API deprecation: in GTK 3.22, stop using gdk_cairo_create.

This is another annoyingly removed function, replaced by a tower of
about four assorted objects you have to create in succession.

7 years agoGTK API deprecation: use GtkCssProvider for window background.
Simon Tatham [Mon, 27 Feb 2017 19:11:02 +0000 (19:11 +0000)]
GTK API deprecation: use GtkCssProvider for window background.

gdk_window_set_background_rgba is deprecated as of GTK 3.22, because
apparently you can't just _say_ any more 'here is what I want my
window's background colour to be in places where a widget isn't'.
Instead you have to provide a GtkStyleProvider which can be slotted
into a wobbly tower of other providers with associated priorities, so
that the user can override your choices if they really want to.

And the easiest way to constructc a GtkStyleProvider in turn is to
write *actual CSS* and get GTK to parse it, so I end up converting my
nice numeric RGB values into a complicated text format for another
part of _the same process_ to parse back into numbers. Sigh.

7 years agoAdd some missing calls to midend_redraw().
Simon Tatham [Tue, 27 Dec 2016 16:13:01 +0000 (16:13 +0000)]
Add some missing calls to midend_redraw().

I've just noticed that the GTK game window was not being redrawn when
changing between puzzle modes that don't involve a window resize - by
selecting a preset from the Type menu (e.g. changing between different
12x12 settings in Flood) or via the Custom menu.

It looks as if the bug was introduced in commit 8dfe5cec3, which
suggests that it was a side effect of the switch from
gtk_window_resize_to_geometry to plain gtk_window_resize. My guess is
that the implementation of the former function inside GTK might have
happened to trigger an unconditional window resize, while the latter
took the shortcut of doing nothing if the window was already the right
size; hence, resize_fe() would have been reliably generating a redraw
event without me having to ask for one, but now it doesn't, so I have
to trigger one myself any time I've just called resize_fe.

7 years agoFix missing error highlights (+ array underrun!) in Pearl.
Simon Tatham [Fri, 16 Dec 2016 18:33:10 +0000 (18:33 +0000)]
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).

7 years agoCorrect a logic error in Unequal game desc validation.
Simon Tatham [Sun, 11 Dec 2016 09:19:30 +0000 (09:19 +0000)]
Correct a logic error in Unequal game desc validation.

A user points out that the error check that should have detected a
non-digit where a digit should have been was never firing, due to an
&& that should have been ||.

I don't think it was a harmful error - the subsequent check that the
number was in range, plus the skipping past only digits to find the
next part of the string, combine to arrange that not many kinds of
invalid game id could actually get through.

But it did have the small effect that a 0 could be elided without
triggering an error, e.g. the game ids

  4:0,0,0,0,0,0L,0,0,0R,0U,0,0L,0,0,,3,
  4:0,0,0,0,0,0L,0,0,0R,0U,0,0L,0,0,0,3,

would both be accepted, and would be decoded into the same game, even
though the former should have failed syntax validation. Now it does.

7 years agoClarify conditions to avoid compiler errors
Khem Raj [Tue, 6 Dec 2016 01:22:00 +0000 (01:22 +0000)]
Clarify conditions to avoid compiler errors

Fix errors pointed out by clang

error: logical not is only applied to the left hand side of this bitwise operator [-Werror,-Wlogical-not-parentheses]
|         if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue;
|                               ^

Signed-off-by: Khem Raj <raj.khem@gmail.com>
7 years agoStop using deprecated GTK 3 geometry-based functions.
Simon Tatham [Sat, 3 Dec 2016 08:49:29 +0000 (08:49 +0000)]
Stop using deprecated GTK 3 geometry-based functions.

Now we work out for ourselves how the drawing-area size relates to the
overall window size, by adding on the height of fe->menubar and/or
fe->statusbar.

7 years agoClarify the Black Box rules slightly.
Simon Tatham [Sun, 30 Oct 2016 17:59:13 +0000 (17:59 +0000)]
Clarify the Black Box rules slightly.

Chris Boyle points out that two of the rules are implicitly intended
to be read as only applying if a previous rule hadn't already decided
what would happen, and suggested that since not all readers infer that
priority order, it would be better to explicitly make them mutually
exclusive so that there can be no confusion about which one applies.

7 years agoFix completion checking in Killer Solo.
Simon Tatham [Fri, 28 Oct 2016 17:57:41 +0000 (18:57 +0100)]
Fix completion checking in Killer Solo.

The check_valid() function was not verifying that each Killer cage
summed to the right thing! Thanks to Chris Goodyer for spotting it. I
wonder how nobody else has, in 8 years.

8 years agoAccount for disconnected paths in Loopy and Pearl error highlights.
Simon Tatham [Thu, 28 Apr 2016 19:42:23 +0000 (20:42 +0100)]
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).

8 years agoRe-run mkauto.sh from within makedist.sh.
Simon Tatham [Thu, 28 Apr 2016 19:34:59 +0000 (20:34 +0100)]
Re-run mkauto.sh from within makedist.sh.

It isn't necessary to cause the right files to _exist_, because
makedist.sh is run from Buildscr which has already run mkauto. But it
turns out it _is_ important to get the relative timestamps of
Makefile.in and Makefile.am the right way round, otherwise somebody
who unpacks the tarball and runs 'configure' and 'make' will find make
tries to rebuild Makefile.in because it thinks Makefile.am is newer -
and if they don't have the right automake installed, or any automake,
that will fail.

8 years agoExplicitly set RGB colourspace in icon.pl's use of convert.
Simon Tatham [Sun, 24 Apr 2016 06:30:20 +0000 (07:30 +0100)]
Explicitly set RGB colourspace in icon.pl's use of convert.

This is that annoying feature of up-to-date 'convert' in which
converting to or from a PNG file defaults to returning RGB values that
have been 'helpfully' gamma-corrected (or some such) from the exact
data stored in the source file to some nonsense you didn't want.

Usually the worst this causes is slightly washed-out looking graphics,
but in this case, since my entire aim was to squash the image into a
specific set of exact RGB values so as to turn it into a paletted
Windows icon file, it caused an actual build failure when the next
loop in icon.pl couldn't find the gamma-corrected values in its
expected palette map, and no wonder.

8 years agoImprove 'illegal colour' error message in icon.pl.
Simon Tatham [Sun, 24 Apr 2016 06:29:25 +0000 (07:29 +0100)]
Improve 'illegal colour' error message in icon.pl.

It actually went off this morning, after an upgrade of ImageMagick,
and I found that it contained both unprintable characters in the
colour description and the wrong variable in the coordinate display.

8 years agoUpdate build script for Inno Setup 5.5.9.
Simon Tatham [Sat, 9 Apr 2016 10:44:03 +0000 (11:44 +0100)]
Update build script for Inno Setup 5.5.9.

Between 5.5.6 and 5.5.9 the default output file name changed. To
defend against that potentially happening again, I'm now explicitly
specifying the output file name in the .iss source file (or rather, in
winiss.pl, which constructs it).

8 years agos/Subversion/git/ in README.
Simon Tatham [Mon, 14 Mar 2016 19:56:18 +0000 (19:56 +0000)]
s/Subversion/git/ in README.

Thanks to Kevin Buzzard for spotting that relic of an outmoded version
control system.

8 years agoAdd a .htaccess redirection for the new .msi file.
Simon Tatham [Fri, 11 Mar 2016 19:25:11 +0000 (19:25 +0000)]
Add a .htaccess redirection for the new .msi file.

Ahem. Left this out of yesterday's commit.

8 years agoUse WiX to generate an MSI-based Puzzles installer.
Simon Tatham [Thu, 10 Mar 2016 18:37:24 +0000 (18:37 +0000)]
Use WiX to generate an MSI-based Puzzles installer.

I've reused most of the install script I wrote for PuTTY recently,
minus the selectable-features dialog, and plus some preliminary Mason
templating to automatically build the right set of puzzle binaries
into the installer.

Stable GUIDs are autogenerated by the same technique I use in PuTTY's
Visual Studio project file generation: hash a fixed pile of randomly
generated bits (that is, randomly generated _once_ and used forever)
with each filename or other identifier and use those as your random
number source.

8 years agoUpdate Buildscr to use the new 'with' mechanism.
Simon Tatham [Tue, 1 Mar 2016 18:42:42 +0000 (18:42 +0000)]
Update Buildscr to use the new 'with' mechanism.

8 years agoTracks: fix further completion-checking loopholes.
Simon Tatham [Fri, 26 Feb 2016 06:59:46 +0000 (06:59 +0000)]
Tracks: fix further completion-checking loopholes.

A user pointed out that Tracks could sometimes flash for completion
when there wasn't even a full path from A to B! And it looks as if
that wasn't even a mistake I introduced with the loop-checking revamp
this week.

Now I _think_ it's complete: we set ret=FALSE in check_completion
wherever we also produce an error highlight, and also whenever there
is no path connecting A with B. And if there is a path connecting A
with B, then any square not on the path becomes an error highlight.

8 years agoPearl: revise loop detection similarly to Loopy.
Simon Tatham [Wed, 24 Feb 2016 19:36:41 +0000 (19:36 +0000)]
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.)

8 years agoPearl: reinstate a conditioned-out assertion.
Simon Tatham [Wed, 24 Feb 2016 19:31:54 +0000 (19:31 +0000)]
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!

8 years agoLoopy: be friendlier to right-click-less playing style.
Simon Tatham [Wed, 24 Feb 2016 19:27:10 +0000 (19:27 +0000)]
Loopy: be friendlier to right-click-less playing style.

Some people don't bother to use the right-click UI action that marks a
line as 'definitely not' rather than the initial default yellow
'unknown'. Previously, Loopy gave those people a UI annoyance for some
classes of mistake they made during solving: it would reliably
highlight a clue square with too _many_ edges around it, but not one
with too few - because in normal right-click-ful play, a clue with too
few LINE_YES only becomes an error when there aren't enough
LINE_UNKNOWN around it to potentially become the remaining YESes in
future.

This change arranges that once the player closes the loop, _then_ we
light up underfilled clues, on the basis that adding any further edge
would be an obvious error, so it's no longer sensible to assume that
the user might be planning to come back and do so.

(It's not a very timely notification of errors - it's easy to imagine
someone making a mistake like this very near the start of play and
only finding out about it when they close the loop at the very end. I
discuss possible improvements in a comment, but I don't think any
improvement avoids that problem completely, so I think it may just be
a form of annoyance that right-click-less players have to live with.)

8 years agoLoopy: revamp loop detection, but not using findloop.
Simon Tatham [Wed, 24 Feb 2016 19:22:57 +0000 (19:22 +0000)]
Loopy: revamp loop detection, but not using findloop.

Loopy differs from the other recently-reworked puzzles in that it
doesn't disallow loops completely in the solution - indeed, one is
actually required! But not all loops are what you wanted, so you have
to be a bit more subtle in what you highlight as an error. And the new
findloop system doesn't make that easy, because it only answers the
question 'is this edge part of a loop?' and doesn't talk about loops
as a whole, or enumerate them.

But since I was working in this area anyway, I thought I might as well
have a think about it; and I've come up with a strategy that seems
quite sensible to me, which I describe in a big comment added in
loopy.c. In particular, the new strategy should make a more sensible
decision about whether to highlight the loop or the non-loop edges, in
cases where the user has managed to enter a loop plus some extra
stuff.

8 years agoTracks: tighten up a small loophole in completion checking.
Simon Tatham [Wed, 24 Feb 2016 19:18:30 +0000 (19:18 +0000)]
Tracks: tighten up a small loophole in completion checking.

If you had a single connected path linking the source to the
destination but _also_ had a spurious edge elsewhere in the grid, then
the spurious edge would be highlighted as an error, but it wouldn't
inhibit declaring the game complete and showing the victory flash.

8 years agoTracks: use the new findloop for loop detection.
Simon Tatham [Wed, 24 Feb 2016 19:14:31 +0000 (19:14 +0000)]
Tracks: use the new findloop for loop detection.

Tracks's previous loop detector was very basic, and only bothered to
highlight one loop, even if the player managed to create more than one
at a time. Now we highlight all of them.

8 years agoSlant: use the new findloop for loop detection.
Simon Tatham [Wed, 24 Feb 2016 19:10:16 +0000 (19:10 +0000)]
Slant: use the new findloop for loop detection.

The old face-dsf based loop detector is gone, and now we just call
findloop instead.

This is just a code cleanup: it doesn't fix any bugs that I know of.
In principle, it provides the same futureproofing we gained by making
the same change in Net, but Slant as a puzzle is less adaptable to
topologically interesting surfaces - in particular, you _can't_ play
it on any edgeless surface like a torus or Klein bottle, because no
filled grid can be loop-free in the first place. (The only way a
connected component can avoid having a loop surrounding it is if it
connects to the grid edge, so there has to _be_ a grid edge.) But you
could play Slant on a Mobius strip, I think, so perhaps one day...

8 years agoNet: use the new findloop for loop detection.
Simon Tatham [Wed, 24 Feb 2016 19:05:43 +0000 (19:05 +0000)]
Net: use the new findloop for loop detection.

I've removed the old algorithm (the one described as 'footpath dsf' in
the findloop.c appendix comment, though I hadn't thought of that name
at the time), and replaced it with calls to the new API.

This should have no functional effect: there weren't any known bugs in
the previous loop-finder that affected currently supported play modes.
But this generality improvement means that non-orientable playing
surfaces could be supported in the future, which would have confused
the old algorithm. And Net, being the only puzzle as yet that's played
on a torus, is perhaps the one most likely to want to generalise
further at some point.

8 years agoBridges: use the new findloop for loop detection.
Simon Tatham [Wed, 24 Feb 2016 19:01:42 +0000 (19:01 +0000)]
Bridges: use the new findloop for loop detection.

Bridges only needs a loop detector for its non-default 'don't allow
loops' mode. But the one it had was using the graph-pruning strategy,
which means it had the dumb-bell bug - two loops joined by a path
would highlight the path as well as the loops. Switching to the new
findloop system fixes that bug.

A side effect is that I've been able to remove the 'scratch' array
from the game_state, which was only used by the old loop finder, so
that should save memory.

8 years agoNew centralised loop-finder, using Tarjan's algorithm.
Simon Tatham [Wed, 24 Feb 2016 18:57:03 +0000 (18:57 +0000)]
New centralised loop-finder, using Tarjan's algorithm.

In the course of another recent project I had occasion to read up on
Tarjan's bridge-finding algorithm. This analyses an arbitrary graph
and finds 'bridges', i.e. edges whose removal would increase the
number of connected components. This is precisely the dual problem to
error-highlighting loops in games like Slant that don't permit them,
because an edge is part of some loop if and only if it is not a
bridge.

Having understood Tarjan's algorithm, it seemed like a good idea to
actually implement it for use in these puzzles, because we've got a
long and dishonourable history of messing up the loop detection in
assorted ways and I thought it would be nice to have an actually
reliable approach without any lurking time bombs. (That history is
chronicled in a long comment at the bottom of the new source file, if
anyone is interested.)

So, findloop.c is a new piece of reusable library code. You run it
over a graph, which you provide in the form of a vertex count and a
callback function to iterate over the neighbours of each vertex, and
it fills in a data structure which you can then query to find out
whether any given edge is part of a loop in the graph or not.

8 years agoAdd patternpicture to .gitignore.
Phil Bordelon [Sun, 14 Feb 2016 01:53:30 +0000 (20:53 -0500)]
Add patternpicture to .gitignore.

8 years agoUpdate documentation links.
Phil Bordelon [Sun, 14 Feb 2016 01:29:41 +0000 (20:29 -0500)]
Update documentation links.

Where possible (mostly with the Nikoli links), they've been updated to their
modern locations.  At least one link had to become a Wayback Machine link;
I didn't bother making the floodit.appspot.com link a Wayback one because
there's no content there without the backing of Google App Engine.  There
are other implementations online nowadays, of course, but I didn't want to
change the meaning of the text if at all possible.  In addition, I added
Flash warnings for the Nikoli pages that now use Flash for instructions.

8 years agoAdd missing casts to unsigned char inside ctype functions.
Simon Tatham [Mon, 1 Feb 2016 19:06:36 +0000 (19:06 +0000)]
Add missing casts to unsigned char inside ctype functions.

These are necessary because the argument to a ctype function cannot be
a negative value unless it's EOF. Thanks to Cygwin gcc for pointing
out the mistake, and to Patrick Shaughnessy for this patch.

8 years agoRename the docs section for Rectangles.
Simon Tatham [Thu, 7 Jan 2016 19:33:34 +0000 (19:33 +0000)]
Rename the docs section for Rectangles.

The web page currently assumes it's called 'rect' rather than
'rectangles', because the web-page building script uses the first
field of each line of gamedesc.txt, same as the Unix binary name.
Rather than add another confusingly-almost-identical field to that
file, it's easier to just rename this one docs section to make the
assumption of equality hold.

8 years agoFix a typo in the Black Box docs examples.
Simon Tatham [Thu, 7 Jan 2016 19:30:56 +0000 (19:30 +0000)]
Fix a typo in the Black Box docs examples.

A letter offset by one from a ball on the edge should be an R, not an
H. Thanks to Kevin Buzzard for pointing out the error.

8 years agoFix a valgrind warning in the Keen DIFF_HARD solver code.
Simon Tatham [Sun, 3 Jan 2016 09:51:15 +0000 (09:51 +0000)]
Fix a valgrind warning in the Keen DIFF_HARD solver code.

The solver's array ctx->iscratch[] is used for a completely different
purpose in the DIFF_HARD code, compared to what it's used for in
DIFF_EASY and DIFF_NORMAL. In particular, a different number of
elements of the array are used - but the code which sets up the array
was not correctly initialising all of them.

I was also unable to find any clear comment that even explained _that_
the purpose of the array was entirely different between the two cases,
let alone explaining _what_ the two purposes were. So I've written
some comments as part of this commit, which should make things a bit
less confusing next time. (Though not much, I admit.)

8 years agorect: Fix compiler errors about uninitialized use of variables
Khem Raj [Wed, 30 Dec 2015 23:53:36 +0000 (23:53 +0000)]
rect: Fix compiler errors about uninitialized use of variables

error: 'r2.x' may be used uninitialized in this function

Its happening when using gcc 5.3 with musl C library. its considering
the case when case falls into default and immediately after exiting
this there is a check if (r1.h > 0 && r1.w > 0) where r1 element is
used but not assigned anything.

GCC is not noticing the control flow where the initilization will
always work due to assertion call can be a function call from libc

Signed-off-by: Khem Raj <raj.khem@gmail.com>
8 years agoAdd a missing error message in Flood solve_game().
Simon Tatham [Thu, 24 Dec 2015 22:05:48 +0000 (22:05 +0000)]
Add a missing error message in Flood solve_game().

The only situation in which it actually can't find a solution is if
the puzzle is already solved, in which case it can at least fill in
*error to say so before it returns NULL.

8 years agoClarify in README that Makefile.vc is for nmake.
Simon Tatham [Fri, 18 Dec 2015 14:50:44 +0000 (14:50 +0000)]
Clarify in README that Makefile.vc is for nmake.

A user pointed out today that I hadn't actually said that!

8 years agoNew utility 'patternpicture', to build a Pattern from an xbm.
Simon Tatham [Fri, 11 Dec 2015 19:53:58 +0000 (19:53 +0000)]
New utility 'patternpicture', to build a Pattern from an xbm.

This utility works basically the same as galaxiespicture: you feed it
a .xbm bitmap on standard input, and it constructs a game ID which
solves to exactly that image. It will pre-fill some squares if that's
necessary to resolve ambiguity, or leave the grid completely blank if
it can.

8 years agoPattern: add a system of immutable pre-filled grid squares.
Simon Tatham [Fri, 11 Dec 2015 18:09:41 +0000 (18:09 +0000)]
Pattern: add a system of immutable pre-filled grid squares.

The game previously only supported numeric clues round the edge; but
if for some reason you really want a puzzle with a specific solution
bitmap and that bitmap doesn't happen to be uniquely soluble from only
its row and column counts, then this gives you a fallback approach of
pre-filling a few grid squares to resolve the ambiguities.

(This also applies if the puzzle is uniquely soluble *in principle*
but not by Pattern's limited solver - for example, Pattern has never
been able to solve 4x4:2/1/2/1/1.1/2/1/1 and still can't, but now it
can solve 4x4:2/1/2/1/1.1/2/1/1,Ap which has the hard part done for
it.)

Immutable squares are protected from modification during play, and
used as initial information by the solver.

8 years agoPattern: fix solver's handling of empty rows.
Simon Tatham [Fri, 11 Dec 2015 18:54:56 +0000 (18:54 +0000)]
Pattern: fix solver's handling of empty rows.

The algorithm for deducing how many squares in a row could be filled
in just from the initial clue set was focusing solely on _black_
squares, and forgot that if a row has a totally empty clue square then
everything in it can be filled in as white!

Now the solver can cope with puzzles such as 3x3:/1///1/ , where it
would previously have spuriously considered that it had no idea where
to start.

8 years agoPattern: make part of the game_state shared.
Simon Tatham [Thu, 10 Dec 2015 19:51:54 +0000 (19:51 +0000)]
Pattern: make part of the game_state shared.

The game_state now includes a pointer to a game_state_common
containing all the row and column clues, which is reference-counted
and therefore doesn't have to be physically copied in every dup_game.