85 Commits

Author SHA1 Message Date
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
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
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
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
23a537243b 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.
2017-05-05 07:09:22 +01:00
2d33375027 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.
2017-04-26 22:06:20 +01:00
e3821d1f68 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.)
2017-04-26 21:55:35 +01:00
a7dc17c425 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.
2017-04-26 21:51:23 +01:00
ce6e3df99b 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.
2017-04-24 17:09:39 +01:00
e37d915f44 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 :-).
2017-04-24 17:09:39 +01:00
b31155b732 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).
2016-04-28 20:42:23 +01:00
32643fab55 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.)
2016-02-24 19:27:10 +00:00
24848706ed 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.
2016-02-24 19:22:57 +00:00
9b1b7e0f3a Don't overallocate colour memory in Loopy. 2015-10-03 16:58:05 +01:00
251b21c418 Giant const patch of doom: add a 'const' to every parameter in every
puzzle backend function which ought to have it, and propagate those
consts through to per-puzzle subroutines as needed.

I've recently had to do that to a few specific parameters which were
being misused by particular puzzles (r9657, r9830), which suggests
that it's probably a good idea to do the whole lot pre-emptively
before the next such problem shows up.

[originally from svn r9832]
[r9657 == 3b250baa02a7332510685948bf17576c397b8ceb]
[r9830 == 0b93de904a98f119b1a95d3a53029f1ed4bfb9b3]
2013-04-13 10:37:32 +00:00
0b93de904a Add 'const' to the game_params arguments in validate_desc and
new_desc. Oddities in the 'make test' output brought to my attention
that a few puzzles have been modifying their input game_params for
various reasons; they shouldn't do that, because that's the
game_params held permanently by the midend and it will affect
subsequent game generations if they modify it. So now those arguments
are const, and all the games which previously modified their
game_params now take a copy and modify that instead.

[originally from svn r9830]
2013-04-12 17:11:49 +00:00
4a1669b9ad Stop using CLUE2CHAR to translate clues into text; just do the obvious
sprintf in both locations (screen and print) that need it. Fixes a bug
in which clues greater than 9 came out as hex digits in printed
puzzles.

[originally from svn r9765]
2013-02-24 12:52:17 +00:00
e0f5e49265 Stop the analysis pass in Loopy's redraw routine from being
conditionalised on !ds->started, so that we still do all the looping
over everything even if we know it's all going to be redrawn. This is
because deciding how much needs redrawing is not the only important
thing in those loops - they also set up arrays like ds->clue_error,
which tell the individual redraw functions _what_ to draw.

Fixes a bug in which, if you start a Loopy game and make moves causing
a clue to light up red for an error and then save your game, loading
the same save file at the start of a Loopy run would fail to highlight
the erroneous clue.

(This commit diff looks large, but actually it changes almost nothing
but whitespace.)

[originally from svn r9751]
2013-01-19 18:56:07 +00:00
3b250baa02 New rule: interpret_move() is passed a pointer to the game_drawstate
basically just so that it can divide mouse coordinates by the tile
size, but is definitely not expected to _write_ to it, and it hadn't
previously occurred to me that anyone might try. Therefore,
interpret_move() now gets a pointer to a _const_ game_drawstate
instead of a writable one.

All existing puzzles cope fine with this API change (as long as the
new const qualifier is also added to a couple of subfunctions to which
interpret_move delegates work), except for the just-committed Undead,
which somehow had ds->ascii and ui->ascii the wrong way round but is
otherwise unproblematic.

[originally from svn r9657]
2012-09-09 18:40:12 +00:00
b16eece9fc New puzzle! Or rather, new-ish, because this one has been lying around
in the 'unfinished' directory for a while, and has now been finished
up thanks to James Harvey putting in some effort and galvanising me to
put in the rest. This is 'Pearl', an implementation of Nikoli's 'Masyu'.

The code in Loopy that generates a random loop along grid edges to use
as the puzzle solution has been abstracted out into loopgen.[ch] so
that Pearl can use it for its puzzle solutions too. I've also
introduced a new utility module called 'tdq' (for 'to-do queue').

[originally from svn r9379]
2012-01-22 14:14:26 +00:00
73daff3937 Changed my mind about midend_is_solved: I've now reprototyped it as
midend_status(), and given it three return codes for win, (permanent)
loss and game-still-in-play. Depending on what the front end wants to
use it for, it may find any or all of these three states worth
distinguishing from each other.

(I suppose a further enhancement might be to add _non_-permanent loss
as a fourth distinct status, to describe situations in which you can't
play further without pressing Undo but doing so is not completely
pointless. That might reasonably include dead-end situations in Same
Game and Pegs, and blown-self-up situations in Mines and Inertia.
However, I haven't done this at present.)

[originally from svn r9179]
2011-06-19 13:43:35 +00:00
004ef73480 Fix segfault in Loopy printing, introduced when I added the dynamic
arrays 'textx' and 'texty' to the game_drawstate but failed to
initialise them in the temporary drawstate used by game_print().
Thanks to Arun Giridhar for spotting this.

[originally from svn r9173]
2011-05-11 18:11:28 +00:00
89bfecaa5a Portability fixes, mostly from James for Palm purposes. Mostly
additions of missing 'static' and explicit 'void' in parameter lists,
plus one or two other things like explicitly casting chars in variadic
argument lists to int and using DBL_MAX if HUGE_VAL isn't available.

[originally from svn r9166]
2011-05-04 18:41:21 +00:00
2efc77d2fd Fix warnings generated by gcc 4.6.0 about variables set but not
thereafter read. Most of these changes are just removal of pointless
stuff or trivial reorganisations; one change is actually substantive,
and fixes a bug in Keen's clue selection (the variable 'bad' was
unreferenced not because I shouldn't have set it, but because I
_should_ have referenced it!).

[originally from svn r9164]
2011-05-04 18:22:14 +00:00
4bab5e531b Fix two memory leaks reported by Tiago Dionizio in recent Loopy
development.

[originally from svn r9163]
2011-04-26 13:44:27 +00:00
62c20496bf From James Harvey (via a period of collaborative polishing), a patch
to add two kinds of Penrose tiling to the grid types supported by
Loopy.

This has involved a certain amount of infrastructure work, because of
course the whole point of Penrose tilings is that they don't have to
be the same every time: so now grid.c has grown the capacity to
describe its grids as strings, and reconstitute them from those string
descriptions. Hence a Penrose Loopy game description consists of a
string identifying a particular piece of Penrose tiling, followed by
the normal Loopy clue encoding.

All the existing grid types decline to provide a grid description
string, so their Loopy game descriptions have not changed encoding.

[originally from svn r9159]
2011-04-24 09:10:52 +00:00
5e3de7d95b Move most of face_text_pos() into grid.c, leaving in loopy.c only the
part that converts from abstract grid coordinates into screen
coordinates. This should speed up window-resizing by eliminating
pointless reiteration of the complicated part of the algorithm: now
when a game_drawstate is renewed, only the conversion into screen
coordinates has to be redone.

[originally from svn r9157]
2011-04-23 11:44:43 +00:00
0a547b2451 Replace my brute-force algorithm in face_text_pos with a more complex
but faster and more mathematically sensible one.

[originally from svn r9156]
2011-04-23 11:44:41 +00:00
079e0d1328 Stop calling face_text_pos() for faces that don't need to have text in
them anyway. It's slow and pointless.

[originally from svn r9155]
2011-04-23 11:44:41 +00:00
5d503a52db Adjust the yellow used for LINE_UNKNOWN so that it's always a bit
darker than the background, because the Java front end demonstrates
that it's a bit eyewatering when it _matches_ the background.

[originally from svn r9145]
2011-04-05 17:45:48 +00:00
9ece2832ce Add a new deduction to Easy level, which is as small as I can make it
to have the effect of enabling large Easy-level grids to be
constructed in all grid types. Without this, some generations at Easy
level (e.g. 'loopy --generate 1 7x7t9de') can spin forever because
_even with all clues filled in_ the generated grids can't be solved at
that level.

[originally from svn r9143]
2011-04-03 07:59:35 +00:00
980880be1f Add a function to every game backend which indicates whether a game
state is in a solved position, and a midend function wrapping it.

(Or, at least, a situation in which further play is pointless. The
point is, given that game state, would it be a good idea for a front
end that does that sort of thing to proactively provide the option to
start a fresh game?)

[originally from svn r9140]
2011-04-02 16:19:12 +00:00
00a313f2d6 Rework the Loopy grid drawing algorithm so that it doesn't assume when
it clears a clipping region that it knows what features will need
redrawing in that region. Instead, I've moved all the clip/unclip/draw
functionality out into a game_redraw_in_rect() function which checks
_everything_ on the grid to see if it lies in the region.

As far as I can tell the effect only shows up in grid types that
aren't checked in, but it makes the code look nicer too.

(It would be nicer still to avoid the brute-force loop over the whole
grid checking it against the bounding box, particularly when we're
drawing in multiple bounding boxes. But this will do for the moment.)

[originally from svn r9138]
2011-04-02 15:19:29 +00:00
3c26b651a6 Improve the algorithm for figuring out where the number should be
drawn in a face: averaging the vertex positions works fine for regular
or roughly regular convex polygons, but it'll start being a pain for
odder or concave ones.

This is a kludgey brute-force algorithm; I have ideas about more
elegant ways of doing this job, but they're more fiddly, so I thought
I'd start with something that basically worked.

[originally from svn r9137]
2011-04-02 15:19:29 +00:00
86f3385d3d When a lot of edges meet the same point - and, in particular, when
they meet at sharp enough angles that the thick lines overlap
noticeably outside the circle of the point - redrawing the edges in a
different order each time looks ugly. Arrange to always redraw edges
in order of colour, so that empty lines are drawn before indeterminate
lines which in turn come before filled-in lines.

[originally from svn r9136]
2011-04-02 13:15:56 +00:00
7b2b742be8 Another patch from Chris Moore implementing two more grid types, both
involving dodecagons.

[originally from svn r9109]
2011-02-24 19:06:49 +00:00
53f6e4c6cb Patch from Chris Moore to implement an extra grid type, the 'floret'
pentagonal tiling.

[originally from svn r9107]
2011-02-23 20:05:42 +00:00
4b0a8c03a6 Patch from Mark Wooding to reorganise Loopy's redraw function to be
properly antialiasing-safe: all redraws are now done by clearing a
rectangle of the image, clipping to that rectangle, and redrawing
everything in it exactly once.

[originally from svn r8963]
2010-05-29 15:43:48 +00:00
9cd182ffa9 Patch from Mark Wooding to introduce a draw_thick_line() function in
the drawing API, for use by Loopy. It's optional: drawing.c will
construct an acceptable alternative using a filled polygon if the
front end doesn't provide it.

Net and Netslide previously had static functions called
draw_thick_line(), whose claim to the name is less justified and so
they've been renamed.

[originally from svn r8962]
2010-05-29 15:43:46 +00:00
8628a0630c Minor bug fixes from James Harvey.
[originally from svn r8785]
2009-12-17 18:20:32 +00:00
97477f0916 Patches from Frode Austvik to modify the effects of the mouse
buttons in several games if STYLUS_BASED is defined: in games where
you can set a puzzle element to 'on', 'off' or 'not yet set', when
it's hard to mimic a second mouse button, it's better to have the
one 'button' cycle between all three states rather than from 'on'
back to 'unset'.

[originally from svn r8784]
2009-12-17 18:16:42 +00:00
2ac017b62c Fix for the grid generation in the presence of particularly strange
grid types.

[originally from svn r8750]
2009-11-16 21:21:00 +00:00
cfdba00313 More defensive-coding fixes from James H.
[originally from svn r8605]
2009-07-01 22:01:21 +00:00
4ecc4f92d8 Patch from Mark Wooding (though somewhat tampered with by me): have
Loopy mark LINE_NO grid edges with very faint lines, instead of
leaving them totally undrawn. Helps in complex grid types where the
line layout isn't entirely obvious to a player not already familiar
with it. Disableable by setting LOOPY_FAINT_LINES=n in the
environment, just in case anyone turns out to seriously dislike it.
(You could probably disable it via LOOPY_COLOUR_6 too, but you'd
have to know the exact shade of your background to get that right.)

[originally from svn r8597]
2009-06-21 13:24:48 +00:00
fee17c3704 Patch from Lambros to make the Normal difficulty level easier, since
people have generally seemed to think Loopy is one of the more
difficult puzzles in the collection. There's a new level called
Tricky, between Normal and Hard, which is equivalent to the old
Normal.

[originally from svn r8398]
2009-01-07 23:07:11 +00:00
d9e39add3a Standalone solver for Loopy. Bit half-hearted, since the solver
doesn't have diagnostics embedded and the ASCII formatter can't
print non-square puzzles anyway; but it can grade difficulty, which
is what I most immediately want it for.

[originally from svn r8397]
2009-01-07 21:55:21 +00:00
f4bd45e7b9 Patch from Lambros to improve the generality of path-generation. In
particular, Great Hexagonal tilings previously had virtually every
(if not _actually_ every) hexagon on the inside of the path, and now
don't.

[originally from svn r8277]
2008-11-04 21:39:59 +00:00
734dc80c53 Lambros points out that trying to generate a 3x3 Cairo Easy grid
spins forever, but observes that raising the limit to 4x4 across all
grid types is not good for the complex grids like great-hexagonal.
Introduce per-grid minimum sizes using mad macro trickery.

(In fact, for each grid type I've put in a minimum size which _both_
dimensions must equal or exceed, plus another minimum size which _at
least one_ must equal or exceed; that permits both 3x4 and 4x3 Cairo
while disallowing 3x3.)

[originally from svn r8191]
2008-09-18 18:19:55 +00:00
ef6166e198 Patch from Lambros implementing error highlighting in Loopy.
[originally from svn r8190]
2008-09-18 15:33:13 +00:00