1384 Commits

Author SHA1 Message Date
2adf0052d6 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.
2018-02-26 20:49:57 +00:00
2270ee116d 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.
2018-02-26 20:49:14 +00:00
43b9eb1472 Create 96x96 icons for gnome-shell 2018-01-21 19:03:38 +00:00
5247edd16d 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.
2017-12-09 21:28:41 +00:00
4f8a4f7d7f 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.
2017-11-29 22:27:09 +00:00
3cf09c2615 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.
2017-11-26 21:11:35 +00:00
ee8ea9b978 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.
2017-11-18 20:08:34 +00:00
e2514a72e6 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.
2017-11-18 20:05:07 +00:00
5c6fcf10a3 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.
2017-11-18 15:27:37 +00:00
8af0c29615 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.
2017-11-18 15:26:13 +00:00
69773d855b 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.
2017-10-28 11:47:12 +01:00
6da8dc91a2 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.
2017-10-28 09:04:39 +01:00
efcc00ffef 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.
2017-10-24 19:39:11 +01:00
38ea1599fe 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.
2017-10-20 00:33:54 +01:00
00e23909f8 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).
2017-10-07 22:36:49 +01:00
f6b2f47ef3 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.
2017-10-06 19:49:05 +01:00
a58c1b216b 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.
2017-10-01 16:35:40 +01:00
3276376d1b 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.
2017-10-01 16:35:00 +01:00
b3243d7504 Return error messages as 'const char *', not 'char *'.
They're never dynamically allocated, and are almost always string
literals, so const is more appropriate.
2017-10-01 16:34:41 +01:00
de67801b0f Use a proper union in struct config_item.
This allows me to use different types for the mutable, dynamically
allocated string value in a C_STRING control and the fixed constant
list of option names in a C_CHOICES.
2017-10-01 16:34:41 +01:00
eeb2db283d New name UI_UPDATE for interpret_move's return "".
Now midend.c directly tests the returned pointer for equality to this
value, instead of checking whether it's the empty string.

A minor effect of this is that games may now return a dynamically
allocated empty string from interpret_move() and treat it as just
another legal move description. But I don't expect anyone to be
perverse enough to actually do that! The main purpose is that it
avoids returning a string literal from a function whose return type is
a pointer to _non-const_ char, i.e. we are now one step closer to
being able to make this code base clean under -Wwrite-strings.
2017-10-01 15:18:14 +01:00
edcf839d4c 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.
2017-10-01 15:18:14 +01:00
8ea15f3b35 Make newgame_undo_buf 'char *', not 'void *'.
This fixes a compile error under -pedantic at the point where we do
pointer arithmetic on it.
2017-10-01 13:52:16 +01:00
db313b3948 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.)
2017-10-01 10:38:30 +01:00
9f6114e272 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 :-)
2017-10-01 10:38:01 +01:00
b9b73adb53 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>
2017-10-01 10:38:01 +01:00
9e62c710df 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.
2017-10-01 10:38:01 +01:00
c0503d4873 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.
2017-10-01 10:03:50 +01:00
cdf1639531 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.
2017-10-01 09:51:01 +01:00
aafb1b4983 tracks: Make error clue background white
This makes them stand out more.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
2017-09-30 22:20:27 +01:00
b98d93ac55 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>
2017-09-30 22:20:27 +01:00
03d464dc65 tracks.c: draw_clue: Introduce bg parameter
No functional change.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
2017-09-30 22:20:27 +01:00
f03e8d30a0 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.
2017-09-30 22:02:39 +01:00
4cf2241f4f 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.
2017-09-30 21:18:52 +01:00
84d3fd2bd8 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.
2017-09-30 16:40:18 +01:00
3aa4516a68 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!
2017-09-29 19:20:49 +01:00
74aff6741b 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.
2017-09-29 17:40:00 +01:00
c2a8a60a09 Fix a typo in devel.but. 2017-09-29 17:07:08 +01:00
05938e1f64 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>
2017-09-29 17:01:41 +01:00
9b0069b137 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>
2017-09-29 17:01:41 +01:00
a0435df8aa 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>
2017-09-29 17:01:41 +01:00
df3b9cb845 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.)
2017-09-24 16:56:18 +01:00
ff218728c6 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.
2017-09-23 19:24:16 +01:00
b8313181a6 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!
2017-09-23 19:23:10 +01:00
61e7111784 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.
2017-09-20 18:03:44 +01:00
d72db91888 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.
2017-09-20 18:03:44 +01:00
e4d05c36d9 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.)
2017-09-20 18:01:52 +01:00
666c528326 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.)
2017-09-14 19:06:44 +01:00
a0a581c8b5 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.
2017-09-07 18:44:58 +01:00
ef39e6e173 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.
2017-09-06 21:49:39 +01:00