120 Commits

Author SHA1 Message Date
8237b02be4 Loopy: fix redraw issue due to enlarged dots.
dot_bbox() wasn't taking into account the new size of the dots, so
sometimes a rectangle of the grid would be redrawn without a partial
dot it should have contained, because nothing had noticed that that
dot overlapped that rectangle.

Actually I'm not sure why this bug wasn't happening _before_ I
enlarged the dots, because the previous code seemed to think dots had
a fixed size in pixels regardless of tile size, which wasn't even
true _before_ my recent commit 4de9836bc8c36cd. Perhaps it did occur,
just never while I was watching.
2023-05-07 21:41:50 +01:00
4de9836bc8 Loopy: slightly increase the size of dots.
In the Hats tiling, each tile has two consecutive edges collinear.
When both edges are turned on, i.e. drawn in black just like the dot,
it becomes _just slightly_ tricky to spot the dot in the middle of
that straight line, which is important if you're counting edges around
the face to check a clue.

Increasing the radius from 2/32 to 3/32 of tile size is far too big.
2.5/32 seems reasonable, though.
2023-04-29 14:10:40 +01:00
0058331aeb New backend functions: get_prefs and set_prefs.
These are similar to the existing pair configure() and custom_params()
in that get_prefs() returns an array of config_item describing a set
of dialog-box controls to present to the user, and set_prefs()
receives the same array with answers filled in and implements the
answers. But where configure() and custom_params() operate on a
game_params structure, the new pair operate on a game_ui, and are
intended to permit GUI configuration of all the settings I just moved
into that structure.

However, nothing actually _calls_ these routines yet. All I've done in
this commit is to add them to 'struct game' and implement them for the
functions that need them.

Also, config_item has new fields, permitting each config option to
define a machine-readable identifying keyword as well as the
user-facing description. For options of type C_CHOICES, each choice
also has a keyword. These keyword fields are only defined at all by
the new get_prefs() function - they're left uninitialised in existing
uses of the dialog system. The idea is to use them when writing out
the user's preferences into a configuration file on disk, although I
haven't actually done any of that work in this commit.
2023-04-23 13:25:06 +01:00
0d1a1f08ba Move per-puzzle ad-hoc getenv preferences into game_ui.
Environment variables that set specific settings of particular
puzzles, such as SLANT_SWAP_BUTTONS, LIGHTUP_LIT_BLOBS and
LOOPY_AUTOFOLLOW, now all affect the game behaviour via fields in
game_ui instead of being looked up by getenv in the individual
functions that need to know them.

The purpose of this refactoring is to put those config fields in a
place where other more user-friendly configuration systems will also
be able to access them, once I introduce one. However, for the moment,
there's no functional change: I haven't _yet_ changed how the user
sets those options. They're still set by environment variables alone.
All I'm changing here is where the settings are stored inside the
code, and exactly when they're read out of the environment to put into
the game_ui.

Specifically, the getenvs now happen during new_ui(). Or rather, in
all the puzzles I've changed here, they happen in a subroutine
legacy_prefs_override() called from within new_ui(), after it's set up
the default values for the settings, and then gives the environment a
chance to override them. Or rather, legacy_prefs_override() only
actually calls getenv the first time, and after that, it's cached the
answers it got.

In order to make the override functions less wordy, I've altered the
prototype of getenv_bool so that it returns an int rather than a bool,
and takes its default return value in the same form. That way you can
set the default to something other than 0 or 1, and find out whether a
value was present at all.

This commit only touches environment configuration specific to an
individual puzzle. The midend also has some standard environment-based
config options that apply to all puzzles, such as colour scheme and
default presets and preset-menu extension. I haven't handled those
yet.
2023-04-23 13:25:06 +01:00
a4fca3286f Pass a game_ui to compute_size, print_size and print.
I'm about to move some of the bodgy getenv-based options so that they
become fields in game_ui. So these functions, which could previously
access those options directly via getenv, will now need to be given a
game_ui where they can look them up.
2023-04-21 16:18:04 +01:00
ce203c12ef Loopy: draw even faint lines using draw_thick_line.
When playing on a high-DPI screen and running Loopy at a large tile
size to compensate, the faint lines along grid edges in LINE_NO state
are exceptionally hard to see, because they're still only one pixel
wide even when everything else has been expanded.

This has been true for ages, but it's more significant now, because
the new Hats tiling mode needs a lot of counting of edges in all
states (since the hats have 14 edges in total!), and it's awkward not
to be able to see exactly where the LINE_NO edges are, or where an
edge between two other tiles meets this hat's outline.
2023-04-20 19:41:46 +01:00
c5e253a9f9 Reorganise the dsf API into three kinds of dsf.
This is preparing to separate out the auxiliary functionality, and
perhaps leave space for making more of it in future.

The previous name 'edsf' was too vague: the 'e' stood for 'extended',
and didn't say anything about _how_ it was extended. It's now called a
'flip dsf', since it tracks whether elements in the same class are
flipped relative to each other. More importantly, clients that are
going to use the flip tracking must say so when they allocate the dsf.

And Keen's need to track the minimal element of an equivalence class
is going to become a non-default feature, so there needs to be a new
kind of dsf that specially tracks those, and Keen will have to call it.

While I'm here, I've renamed the three dsf creation functions so that
they start with 'dsf_' like all the rest of the dsf API.
2023-04-20 18:39:41 +01:00
348aac4c85 Remove size parameter from dsf init and copy functions.
Now that the dsf knows its own size internally, there's no need to
tell it again when one is copied or reinitialised.

This makes dsf_init much more about *re*initialising a dsf, since now
dsfs are always allocated using a function that will initialise them
anyway. So I think it deserves a rename.
2023-04-20 17:30:03 +01:00
89c438e149 Declare all dsfs as a dedicated type name 'DSF'.
In this commit, 'DSF' is simply a typedef for 'int', so that the new
declaration form 'DSF *' translates to the same type 'int *' that dsfs
have always had. So all we're doing here is mechanically changing type
declarations throughout the code.
2023-04-20 17:23:21 +01:00
f21c7d2766 Consistently use snew_dsf to allocate dsfs.
All remaining cases where a dsf was allocated via snewn(foo, int) are
removed by this change.
2023-04-20 17:22:23 +01:00
11a8149d67 Use a dedicated copy function to copy dsfs.
Previously we were duplicating the contents of a dsf using straight-up
memcpy. Now there's a dsf_copy function wrapping the same memcpy.

For the moment, this still has to take a size parameter, because the
size isn't stored inside the dsf itself. But once we make a proper
data type, it will be.
2023-04-20 17:21:54 +01:00
bb561ee3b1 Use a dedicated free function to free dsfs.
No functional change: currently, this just wraps the previous sfree
call.
2023-04-20 17:21:12 +01:00
418cb3a567 Make encode_ui() and decode_ui() optional in back-ends
The majority of back-ends define encode_ui() to return NULL and
decode_ui() to do nothing.  This commit allows them to instead specify
the relevant function pointers as NULL, in which case the mid-end won't
try to call them.

I'm planning to add a parameter to decode_ui(), and if I'm going to have
to touch every back-end's version of decode_ui(), I may as well ensure
that most of them never need to be touched again.  And obviously
encode_ui() should go the same way for symmetry.
2023-04-08 20:08:16 +01:00
3b9cafa09f Fall back to <math.h> if <tgmath.h> doesn't work.
This fixes a build failure introduced by commit 2e48ce132e011e8
yesterday.

When I saw that commit I expected the most likely problem would be in
the NestedVM build, which is currently the thing with the most most
out-of-date C implementation. And indeed the NestedVM toolchain
doesn't have <tgmath.h> - but much more surprisingly, our _Windows_
builds failed too, with a compile error inside <tgmath.h> itself!

I haven't looked closely into the problem yet. Our Windows builds are
done with clang, which comes with its own <tgmath.h> superseding the
standard Windows one. So you'd _hope_ that clang could make sense of
its own header! But perhaps the problem is that this is an unusual
compile mode and hasn't been tested.

My fix is to simply add a cmake check for <tgmath.h> - which doesn't
just check the file's existence, it actually tries compiling a file
that #includes it, so it will detect 'file exists but is mysteriously
broken' just as easily as 'not there at all'. So this makes the builds
start working again, precisely on Ben's theory of opportunistically
using <tgmath.h> where possible and falling back to <math.h>
otherwise.

It looks ugly, though! I'm half tempted to make a new header file
whose job is to include a standard set of system headers, just so that
that nasty #ifdef doesn't have to sit at the top of almost all the
source files. But for the moment this at least gets the build working
again.
2023-04-06 07:08:04 +01:00
2e48ce132e Replace <math.h> with <tgmath.h> throughout
C89 provided only double-precision mathematical functions (sin() etc),
and so despite using single-precision elsewhere, those are what Puzzles
has traditionally used.  C99 introduced single-precision equivalents
(sinf() etc), and I hope it's been long enough that we can safely use
them.  Maybe they'll even be faster.

Rather than directly use the single-precision functions, though, we use
the magic macros from <tgmath.h> that automatically choose the precision
of mathematical functions based on their arguments.  This has the
advantage that we only need to change which header we include, and thus
that we can switch back again if some platform has trouble with the new
header.
2023-04-04 21:43:25 +01:00
4720eeb1aa Loopy: widen clip rectangle for redrawing clues.
The new Hats tiling generates a lot of clues that are 2-digit numbers.
At large puzzle sizes, the previous clip rectangle didn't quite
include the ends of such a number, meaning that if the number had to
be redrawn in red to highlight an error, the leftmost and rightmost
parts of the text would remain black.
2023-03-28 20:51:26 +01:00
8d6647548f Loopy / grid.c: new grid type, 'Hats'.
The big mathematical news this month is that a polygon has been
discovered that will tile the plane but only aperiodically. Penrose
tiles achieve this with two tile types; it's been an open question for
decades whether you could do it with only one tile. Now someone has
announced the discovery of such a thing, so _obviously_ this
mathematically exciting tiling ought to be one of the Loopy grid
options!

The polygon, named a 'hat' by its discoverers, consists of the union
of eight cells of the 'Kites' periodic tiling that Loopy already
implements. So all the vertex coordinates of the whole tiling are
vertices of the Kites grid, which makes handling the coordinates in an
exact manner a lot easier than Penrose tilings.

What's _harder_ than Penrose tilings is that, although this tiling can
be generated by a vaguely similar system of recursive expansion, the
expansion is geometrically distorting, which means you can't easily
figure out which tiles can be discarded early to save CPU. Instead
I've come up with a completely different system for generating a patch
of tiling, by using a hierarchical coordinate system to track a
location within many levels of the expansion process without ever
simulating the process as a whole. I'm really quite pleased with that
technique, and am tempted to try switching the Penrose generator over
to it too - except that we'd have to keep the old generator around to
stop old game ids being invalidated, and also, I think it would be
slightly trickier without an underlying fixed grid and without
overlaps in the tile expansion system.

However, before coming up with that, I got most of the way through
implementing the more obvious system of actually doing the expansions.
The result worked, but was very slow (because I changed approach
rather than try to implement tree-pruning under distortion). But the
code was reusable for two other useful purposes: it generated the
lookup tables needed for the production code, and it also generated a
lot of useful diagrams. So I've committed it anyway as a supporting
program, in a new 'aux' source subdirectory, and in aux/doc is a
writeup of the coordinate system's concepts, with all those diagrams.
(That's the kind of thing I'd normally put in a huge comment at the
top of the file, but doing all those diagrams in ASCII art would be
beyond miserable.)

From a gameplay perspective: the hat polygon has 13 edges, but one of
them has a vertex of the Kites tiling in the middle, and sometimes two
other tile boundaries meet at that vertex. I've chosen to represent
every hat as having degree 14 for Loopy purposes, because if you only
included that extra vertex when it was needed, then people would be
forever having to check whether this was a 13-hat or a 14-hat and it
would be nightmarish to play.

Even so, there's a lot of clicking involved to turn all those fiddly
individual edges on or off. This grid is noticeably nicer to play in
'autofollow' mode, by setting LOOPY_AUTOFOLLOW in the environment to
either 'fixed' or 'adaptive'. I'm tempted to make 'fixed' the default,
except that I think it would confuse players of ordinary square Loopy!
2023-03-26 20:32:38 +01:00
09c15f206e New shared function, getenv_bool()
This provides a standard way to get a boolean from an environment
variable.  It treats the variable as true iff its value begins with 'y'
or 'Y', like most of the current implementations.  The function takes a
default value which it returns if the environment variable is undefined.

This replaces the various ad-hoc tests of environment variable scattered
around and mostly doesn't change their behaviour.  The exceptions are
TOWERS_2D in Towers and DEBUG_PUZZLES in the Windows front end.  Both of
those were treated as true if they were defined at all, but now follow
the same rules as other boolean environment variables.
2023-03-22 16:06:18 +00:00
26c7f3aa28 Miscellaneous const fixes
These are cases where -Wcast-qual complained and the only change needed
was to add or remove a "const" (or sometimes an entire cast).
2023-02-18 23:14:12 +00:00
dbced097ac Fix unused variable warnings from clang.
If you enable -DSTRICT=ON in cmake and also build with clang, it
reports a couple of variables set but not otherwise used. One was
genuinely unused ('loop_found' in loop_deductions in Loopy); the other
is used by debug statements that are usually but not always compiled
out.
2023-02-18 08:55:13 +00:00
e336513be7 Loopy: free the grid description string if it's invalid 2023-02-13 20:49:05 +00:00
11b631ea87 Don't leak grids in Loopy's validate_desc() 2023-02-13 09:45:08 +00:00
789e11f8f8 Remove various unused game functions
If can_configure is false, then the game's configure() and
custom_params() functions will never be called.  If can_solve is false,
solve() will never be called.  If can_format_as_text_ever is false,
can_format_as_text_now() and text_format() will never be called.  If
can_print is false, print_size() and print() will never be called.  If
is_timed is false, timing_state() will never be called.

In each case, almost all puzzles provided a function nonetheless.  I
think this is because in Puzzles' early history there was no "game"
structure, so the functions had to be present for linking to work.  But
now that everything indirects through the "game" structure, unused
functions can be left unimplemented and the corresponding pointers set
to NULL.

So now where the flags mentioned above are false, the corresponding
functions are omitted and the function pointers in the "game" structures
are NULL.
2023-01-31 23:25:05 +00:00
ccd579e72e Loopy: Specify can_solve as true, rather than 1 2023-01-31 22:17:03 +00:00
d71bba1a17 Limit maximum grid size in Loopy
Every grid shape has its own limit, so this involved adding a new
interface between loopy.c and grid.c.  The limits are based on ensuring
that the co-ordinate system of the grid doesn't overflow INT_MAX and
neither do the lengths of the face and dot arrays.

Though now I come to look at it I think the actual limits of grid.c are
much lower.  Hmm.
2023-01-15 16:24:27 +00:00
a3310ab857 New backend function: current_key_label()
This provides a way for the front end to ask how a particular key should
be labelled right now (specifically, for a given game_state and
game_ui).  This is useful on feature phones where it's conventional to
put a small caption above each soft key indicating what it currently
does.

The function currently provides labels only for CURSOR_SELECT and
CURSOR_SELECT2.  This is because these are the only keys that need
labelling on KaiOS.

The concept of labelling keys also turns up in the request_keys() call,
but there are quite a few differences.  The labels returned by
current_key_label() are dynamic and likely to vary with each move, while
the labels provided by request_keys() are constant for a given
game_params.  Also, the keys returned by request_keys() don't generally
include CURSOR_SELECT and CURSOR_SELECT2, because those aren't necessary
on platforms with pointing devices.  It might be possible to provide a
unified API covering both of this, but I think it would be quite
difficult to work with.

Where a key is to be unlabelled, current_key_label() is expected to
return an empty string.  This leaves open the possibility of NULL
indicating a fallback to button2label or the label specified by
request_keys() in the future.

It's tempting to try to implement current_key_label() by calling
interpret_move() and parsing its output.  This doesn't work for two
reasons.  One is that interpret_move() is entitled to modify the
game_ui, and there isn't really a practical way to back those changes
out.  The other is that the information returned by interpret_move()
isn't sufficient to generate a label.  For instance, in many puzzles it
generates moves that toggle the state of a square, but we want the label
to reflect which state the square will be toggled to.  The result is
that I've generally ended up pulling bits of code from interpret_move()
and execute_move() together to implement current_key_label().

Alongside the back-end function, there's a midend_current_key_label()
that's a thin wrapper around the back-end function.  It just adds an
assertion about which key's being requested and a default null
implementation so that back-ends can avoid defining the function if it
will do nothing useful.
2022-12-09 20:48:30 +00:00
ea4ba47662 Loopy: adjust clip rectangle for new line thickness.
It would have helped in the previous commit if I'd tried actually
_playing_ the game, not just admiring it in its initial state. When I
did, I found that lines weren't being fully overdrawn, which turned
out to be because the clip rectangle was being set too narrow.
2022-10-28 22:35:22 +01:00
ebb079eca0 Loopy: make line thicknesses scale with the canvas.
This is now important due to Ben's changes in the web frontend. On
high-DPI displays, the canvas is the same overall size as before, but
it's scaled up by increasing the game's tilesize rather than the
browser scaling the image after the game redraws.

Loopy ought to have been scaling its line thicknesses all along, but
forgot. Easily fixed.
2022-10-28 21:32:14 +01:00
56ef86f92b New grid type: compass dodecagonal
A grid based on dodecagons with square symmetry. In between dodecagons
there are 4 triangles around 1 square, which resembles a compass rose.
https://en.wikipedia.org/wiki/3-4-3-12_tiling
2021-04-22 06:24:23 +01:00
78bc9ea7f7 Add method for frontends to query the backend's cursor location.
The Rockbox frontend allows games to be displayed in a "zoomed-in"
state targets with small displays. Currently we use a modal interface
-- a "viewing" mode in which the cursor keys are used to pan around
the rendered bitmap; and an "interaction" mode that actually sends
keys to the game.

This commit adds a midend_get_cursor_location() function to allow the
frontend to retrieve the backend's cursor location or other "region of
interest" -- such as the player location in Cube or Inertia.

With this information, the Rockbox frontend can now intelligently
follow the cursor around in the zoomed-in state, eliminating the need
for a modal interface.
2020-12-07 19:40:06 +00:00
5f5b284c0b Use C99 bool within source modules.
This is the main bulk of this boolification work, but although it's
making the largest actual change, it should also be the least
disruptive to anyone interacting with this code base downstream of me,
because it doesn't modify any interface between modules: all the
inter-module APIs were updated one by one in the previous commits.
This just cleans up the code within each individual source file to use
bool in place of int where I think that makes things clearer.
2018-11-13 21:48:24 +00:00
a550ea0a47 Replace TRUE/FALSE with C99 true/false throughout.
This commit removes the old #defines of TRUE and FALSE from puzzles.h,
and does a mechanical search-and-replace throughout the code to
replace them with the C99 standard lowercase spellings.
2018-11-13 21:48:24 +00:00
20b56788bc Adopt C99 bool in the edsf API.
Now the flag passed to edsf_merge to say whether two items are the
same or opposite is a bool, and so is the flag returned via a pointer
argument from edsf_canonify.

The latter requires client code to be updated to match (otherwise
you'll get a pointer type error), so I've done that update in Loopy,
which is edsf's only current in-tree client.
2018-11-13 21:48:24 +00:00
a76d269cf2 Adopt C99 bool in the game backend API.
encode_params, validate_params and new_desc now take a bool parameter;
fetch_preset, can_format_as_text_now and timing_state all return bool;
and the data fields is_timed, wants_statusbar and can_* are all bool.
All of those were previously typed as int, but semantically boolean.

This commit changes the API declarations in puzzles.h, updates all the
games to match (including the unfinisheds), and updates the developer
docs as well.
2018-11-13 21:34:42 +00:00
60a929a250 Add a request_keys() function with a midend wrapper.
This function gives the front end a way to find out what keys the back
end requires; and as such it is mostly useful for ports without a
keyboard. It is based on changes originally found in Chris Boyle's
Android port, though some modifications were needed to make it more
flexible.
2018-04-22 17:04:50 +01: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
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