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.
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!
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.
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.)
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.
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.)
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.
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 :-).
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.
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.
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.
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.
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).
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.
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>
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.
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.
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.
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).
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.
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.
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.
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).
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.
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.
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.)
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!
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.)
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.
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.
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.
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...
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.
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.
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.
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.
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.
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.
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.)
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>
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.
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.
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.