2185 Commits

Author SHA1 Message Date
14e1e05510 Introduce a new dsf_equivalent() function.
Not very interesting, but the idiom for checking equivalence via two
calls to dsf_canonify is cumbersome enough to be worth abbreviating.
2023-04-20 18:39:35 +01:00
088fdeee38 Remove conditioned-out dsf diagnostic code.
print_dsf was declared in puzzles.h, but never called, and its
definition was commented out. So it probably wouldn't still have
worked anyway. The other commented-out printfs in that file don't look
very useful either, and they just mean more stuff will need messing
about with as I continue to refactor.
2023-04-20 17:30:03 +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
dad2f35502 Store a size field inside the DSF type.
This permits bounds-checking of all inputs to dsf_canonify and
dsf_merge, so that any out-of-range values will provoke assertion
failure instead of undefined behaviour.
2023-04-20 17:30:01 +01:00
095224d571 Actually make DSF an opaque structure type.
This makes good on all the previous preparatory commits, which I did
separately so that each one individually has a reasonably readable
diff, and all the mechanical changes are separated out from the
rewrites that needed actual thought.

Still no functional change, however: the DSF type wraps nothing but
the same int pointer that 'DSF *' used to store directly.
2023-04-20 17:23:23 +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
7abf85a9c6 Remove a direct use of dsf internals in Filling.
The expression 'dsf[foo] >> 2' already has a sensible wrapper
function, but Filling wasn't bothering to call it.
2023-04-20 17:22:25 +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
16f997d34c Stop putting dsfs in existing scratch int arrays.
I'm going to work towards turning 'dsf' into an opaque type, so that I
can improve its implementation without breaking clients. The first
step is to deal manually with puzzles that currently reuse a single
array of ints for multiple purposes, some of which are dsf and some
are not.

In divvy_rectangle_attempt, 'tmp' was used as an int array and later a
dsf, and I've made a new 'tmpdsf' to be the latter.

In Dominosa, solver->pc_scratch2 was sometimes a dsf, and now there's
a new solver->dsf_scratch.

In Map, parse_edge_list() needed a dsf internally and then never
exported it; it expected to be passed an array of 2*w*h ints and used
the second half as a dsf. Now it expects only w*h ints, and allocates
its own dsf internally, freeing it again before returning.

And in Tents, find_errors() was allocating a single block of 2*w*h
ints and using the second half of it as a dsf, apparently just to save
one malloc. Now we malloc and free the dsf separately.
2023-04-20 14:28:22 +01:00
6c02b72d76 Remove an unnecessary extern function declaration.
unfinished/separate.c had its own declaration of divvy_rectangle(),
duplicating the one in puzzles.h. Probably that was where the
declaration originally lived, before I moved it out into the main
header.
2023-04-20 14:08:54 +01:00
0d86fe4b74 Move obfuscator tests into obfusc.c.
I just found these self-tests lying around in mines.c under an #ifdef
that nobody ever enables. Let's put them somewhere more sensible! We
already have a separate tool for working with the obfuscation system
in a puzzle-independent way, and it seems reasonable to put them in
there.
2023-04-16 08:44:33 +01:00
6fb890e0ea Reference my just-published article about aperiodic tilings.
In commit 8d6647548f7d005 I added the Hats grid type to Loopy, and
mentioned in the commit message that I was very pleased with the
algorithm I came up with.

In fact, I was so pleased with it that I've decided it deserves a
proper public writeup. So I've spent the Easter weekend producing one:

  https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-tilings/

In this commit I adjust the header comments in both penrose.c and
hat.c to refer to the article (replacing a previous comment in
penrose.c to a much less polished page containing a copy of my
jotting-grade personal notes that I sent James Harvey once). Also,
added some code to hatgen.c to output Python hat descriptions in a
similar style to hat-test, which I used to generate a couple of the
more difficult diagrams in the new article, and didn't want to lose.
2023-04-10 14:59:05 +01:00
71cf891fdc Don't allow zero clues in Pattern
Some nonogram implementations allow zero clues so that a row or column
with a single zero clue is equivalent to one with no clues, that is it
has no black squares in it.  Pattern, however, doesn't interpret them
like this and treats a puzzle with a zero clue as insoluble, so it's
not helpful to permit them.

Permitting zero clues also confuses Pattern's memory allocation so
that it can suffer a buffer overrun.  As an example, before this
commit a build with AddressSanitizer would report a buffer overrun
with the description "1:0/0.0" because it tries to put two clues in a
row that can have a maximum of one.
2023-04-08 20:08:16 +01:00
a4c6f21b8e Net: validate co-ordinates in decode_ui()
The offset and centre location should be within the grid.  Otherwise the
redraw code will suffer an assertion failure.  This save file
demonstrates the problem:

SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
VERSION :1:1
GAME    :3:Net
PARAMS  :4:5x5w
CPARAMS :4:5x5w
DESC    :25:9893e85285bb72e6de5182741
UI      :9:O0,0;C6,6
NSTATES :1:1
STATEPOS:1:1
2023-04-08 20:08:16 +01:00
9be7db547a Add a game_state argument to decode_ui()
Some games would like a way to check that the parameters in the encoded
UI string are consistent with the game parameters.  Since this might
depend on the current state of the game (this being what changed_state()
is for), implement this by adding a game_state parameter to decode_ui().
Nothing currently uses it, though Guess usefully could.
2023-04-08 20:08:16 +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
e411db788c Net: assert that cx and cy are in range in compute_active()
This avoids an out-of-range heap write shortly afterwards.  An assertion
failure is better than a buffer overrun, but still not ideal.  Fixing
the problem properly will require fairly wide-ranging changes, though.

The bug can be demonstrated by loading this save file into a build with
AddressSanitizer:

SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
VERSION :1:1
GAME    :3:Net
PARAMS  :4:5x5w
CPARAMS :4:5x5w
DESC    :25:9893e85285bb72e6de5182741
UI      :9:O0,0;C6,6
NSTATES :1:1
STATEPOS:1:1
2023-04-08 20:08:16 +01:00
d505f08f67 js: explicitly tell Emscripten which browsers to target
Emscripten has settings indicating which browser versions it should
build code for.  These are now by default slightly newer than I'd been
targeting with my hand-written JavaScript.  They also don't include
Firefox 48, which KaiOS 2.5 is based on.

This commit adds CMake variables to set the minimum versions that we
pass to Emscripten.  They default to the earliest versions with
WebAssembly support, except that Firefox 48 is also supported.

I think the main consequence of this change is to stop Emscripten using
sign-extension and mutable-globals in WebAssembly, which it's done by
default since version 3.1.26.
2023-04-06 23:05:52 +01:00
b5f87e6175 js: set -s ENVIRONMENT=web in Emscripten
Puzzles only runs in Web browsers, so there's no need to include
support for Node or (for now at least) running in a Web worker.  This
removes about 5 kiB of code from the boilerplate JavaScript.
2023-04-06 23:05:52 +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
d9355041a5 Remove obsolete testbuild.c.
It was originally used for probing the available warning flags in the
compiler, by adding them one by one and making sure this test file
still compiled.

But that whole mechanism was removed in commit 306fab356e357ef, and
since then, testbuild.c has been unused. Belatedly throw it away.
2023-04-06 07:07:30 +01:00
7a66855947 KaiOS: include extra copyright notices in manual
The KaiOS build includes compiled versions of various Emscripten library
files.  These are generally under the MIT licence like Puzzles itself.
The MIT licence requires that the licence, and the copyright notice, be
"included in all copies or substantial portions of the Software."

Since each KaiOS package includes the full manual, which already
contains the licence for Puzzles itself, adding the copyright notices
there seems like the best approach.  I've done this by providing an
additional input file that contains the licences for source files used
by a current Emscripten build.  More automation might be nice, but the
set of copyright notices is unlikely to change very much.  There are
basically one for Emscripten, one for musl, and a few for odd bits of
third-party code embedded in musl.
2023-04-06 00:21:43 +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
4fcc1ea601 js: stop using EXTRA_EXPORTED_RUNTIME_METHODS
Current Emscripten has deprecated it in favour of
EXPORTED_RUNTIME_METHODS, and using that avoids generating warnings.
2023-04-03 22:49:27 +01:00
4de5d20368 js: use the "load" event for loading save files
This is in place of the "loadend" event.  In Chromium, (and in the
specification), "loadend" is triggered not only when the file is
loaded but also when loading fails.  Obviously when loading fails we
don't want to try to parse the (nonexistent) resulting file.

Using the "load" event works better, since it's only fired on success,
and we can also have an "error" handler to report problems with
loading files, albeit with no detail at all.

This doesn't seem to make any difference in Firefox, which in my
testing fires "load" and "loadend" on success and nothing at all on
failure.
2023-04-03 22:11:42 +01:00
36c282aaa9 js: Load save files into the C side incrementally
Before this commit, JavaScript Puzzles loaded a save file by pushing the
entire file onto the Emscripten stack and then reading it from there.
This worked tolerably for typical save files, but Emscripten's stack
defaults to only having 64 kiB of space.  That meant that trying to load
something that wasn't a real save file tended to cause a stack overflow.
I expect that at least some real save files would suffer from the same
problem.

The stack overflow would generally cause a JavaScript exception and then
leave the stack pointer outside the stack, so that any future attempt to
call into C would fail as well.

To fix this, arrange that the C function for reading data from the save
file calls out to JavaScript.  The JavaScript can then copy just the
requested data into the caller's buffer.  We can't pass a JavaScript
function pointer to C, but since only one file can be loaded at a time,
we can just have a global variable that's the current loading callback.

There might still be a problem if you try to load a stupendously large
file, since I think FileReader.readAsArrayBuffer() reads the whole file
into the browser's RAM.  It works on my laptop with files up to a few
hundred megabytes, though.
2023-04-03 21:09:57 +01:00
8d3a93ce47 js: load games using FileReader.readAsArrayBuffer()
Using .readAsText() meant that trying to load a non-text file (for
instance something that's not a save file at all) would generate an
"RuntimeError: index out of bounds".  This would then leave the
Emscripten runtime in a broken state.

It might even be possible for a real save file not to be valid UTF-8,
for instance if it came from a platform that used a different character
encoding for random seeds.

There's still a problem with opening very large files, apparently
because Emscripten tries to stuff the entire file onto the C stack.
That will probably have to be fixed by properly exposing the incremental
file-loading API to JavaScript.
2023-04-02 21:17:11 +01:00
2499eb47fa hat-test: more scaling and clipping options.
This adds the ability to turn off hat-test's normal scaling of the
bounding box to fit on an A4 page, which I intended for printing test
patches (but never actually found a need to print one). The --unscaled
mode seems more useful if you're planning to turn the output into an
image, e.g. to use as a desktop background.

Also added --clip, which generates a rectangle completely covered in
hats (i.e. shows any hat that overlaps the output rectangle at all),
as opposed to the normal mode which omits any hat that doesn't fit
_entirely_ in the output rectangle (more similar to what Loopy wants).

Actually generating a desktop background by this method is still a bit
fiddly to get right, but it's better than before.
2023-04-02 14:35:12 +01:00
7956148591 hat-test: fix array underrun.
Having _checked_ whether a hat index in my four-colouring maps was -1, I
then went ahead and used it as an array index anyway, oops!
2023-04-02 14:35:12 +01:00
2296d6f078 Remove penrose_count_tiles().
Nothing uses it, and that's not surprising, because if anything had,
we'd have noticed that it never actually got written!
2023-04-02 14:35:12 +01:00
83244294f5 Move other test main()s out of library source files.
Having stated the principle in the previous commit, I should apply it
consistently. A source file linked into the Puzzles library of common
support code should not also define a main() under ifdef.

This commit only goes as far as the _library_ support modules. It
would be a much bigger job to do the same for all the actual _puzzles_
that have test main()s or standalone-solver main()s. And it's not
necessary, because modifying one of those source files only triggers a
rebuild of _one_ puzzle, not absolutely everything. (Not to mention
that it's quite likely the puzzle and the test main() will need to be
modified in conjunction anyway.)

As in the previous commit, this has required exposing a few internal
API functions as global, and maybe editing them a bit. In particular,
the one-shot internal function that divvy_rectangle() loops on until
it succeeds is now exposed as divvy_rectangle_attempt(), which means
the test program doesn't have to condition a failure counter into the
real function.

I've thrown away penrose-vector-test completely, because that didn't
look like a test program with any ongoing use at all - it was surely
vestigial, while James was getting the vector representation up and
running in the first place.
2023-04-02 14:35:12 +01:00
71e1776094 Move hat-test into its own source file.
I noticed while hacking on hat-test recently that it's quite awkward
to be compiling a test main() program that lives in a source file also
built into the Puzzles support library, because every modification to
main() also triggers a rebuild of the library, and thence of all the
actual puzzles. So it's better if such a test main() has its own
source file.

In order to make hat-test work standalone, I've had to move a lot of
hat.c's internal declarations out into a second header file. This also
means making a bunch of internal functions global, which means they're
also in the namespace of programs other than hat-test, which means in
turn that they should have names with less implicit context.
2023-04-02 14:35:12 +01:00
0bd1a80578 Magnets: add a check that magnets don't wrap between lines
There was nothing in Magnet's description validation to prevent there
being the left end of a magnet at the right end of a row and the right
end of a magnet at the left end of the row below.  Indeed as far as I
can such a game (e.g. 3x3:..2,2..,...,1.1,TLRB*LRLR) plays entirely
correctly except that one magnet is discontinuous.

While this worked, it was entirely an artefact of the particular memory
layout that Magnets uses and shouldn't have been allowed, so I've added
an additional validation rule to stop it.
2023-04-01 20:44:47 +01:00
91735e5019 Correct a range check in Magnets' layout verification
Squares in the grid are numbered from 0, so the upper limit check
needs to use "<=" rather than "<".  Without this, invalid descriptions
can cause a read overrun off the end of the board.
2023-03-31 20:45:37 +01:00
1af1204b9c hat-test: option to generate four-coloured hat tilings.
This commit is purely frivolous even by Puzzles standards, in that
it's totally unrelated to any actual puzzle. But I know at least one
person has already used the 'hat-test' tool in this code base to
generate a patch of hat tiling for decorative purposes, so it's useful
in its own right. Also, now that I've worked out _how_ to do this,
it's a shame not to keep the code.

Of course, any tiling of the plane _can_ be four-coloured, just by the
Four Colour Theorem. But for a tiling with structure it's nicer if the
colouring is related to the structure in some way. And there's a
reasonably nice explicit construction that does just that: the paper
introducing the tiling observes that if each reflected hat is fused
with a particular one of its neighbours, the resulting tiling is
graph-theoretically equivalent to a tiling of the plane by hexagons.
And _that_ tiling can be three-coloured, in a unique way up to colour
choices. This induces a four-colouring of the hat tiling in which the
reflected hats have a colour to themselves, and everything else is
coloured the same as its corresponding hexagon in the three-colouring.

Actually implementing this turns out not to be too difficult using my
coordinate system. I hand-wrote tables giving a patch of colouring for
each of the four kitemaps; then, whenever two kitemaps meet, you can
determine how the colours map to each other by looking at the
overlapping tiles. So I can have hat-test work out the colour of each
tile as it goes.

So hat-test now supports a '--fourcolour' option to apply this
colouring to the output tiling.
2023-03-31 19:29:28 +01:00
52d801a06a Require a grid description for hats grid
Without this, you can cause a null-pointer dereference by passing Loopy
a game description with no grid description, like "10x10t16:".
2023-03-31 15:35:50 +01:00
e6aa7ab6dd hat-test: allow choosing a random number seed.
The default one is always the same, because the main purpose of this
tool is debugging. But one person has already wanted to use it for
actually generating a tiling patch for another use, so let's make it
easier to vary the randomness!
2023-03-30 08:45:06 +01:00
73dab39bf5 Hats: choose the tiling's starting hat more uniformly.
This fills in the missing piece of commit 6f75879e9fe7cb5, which was
trying to make the output patches of tiling as uniformly random as
possible across the whole space of possible ones. I fixed every
_intermediate_ step in the algorithm, but forgot the starting one!
2023-03-30 08:37:17 +01:00
796d0f372f Hats: factor out the parent-choosing system.
NFC, but I'm about to want to use it again elsewhere.
2023-03-30 08:34:57 +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
827051dafe hat-test: alternative data output mode to write Python.
This mode emits a sequence of calls to an imaginary Python function.
Should be useful to anyone wanting to post-process the tiling in any
way.
2023-03-28 20:51:26 +01:00
828c7da785 hat-test: allow specifying tiling size on the command line.
I'm tired of recompiling every time I want a different size of test
patch.
2023-03-28 20:51:26 +01:00
22417efad6 Hats tiling: make hat-test draw each hat in one go.
By introducing a second callback between the client of hat.c and the
maybe_report_hat function, I enable the test main() to provide a
different version of that callback, so that instead of enumerating
each kite, it can directly generate a Postscript path per actual hat.
This should make it more useful to people wanting to generate hat
patterns for any other purpose.

The internal callback gets more details than the external one; in
particular, it receives a HatCoords, so that it can colour hats based
on their position in the hierarchical structure.
2023-03-28 20:51:26 +01:00
6f75879e9f Hats tiling: more uniform parent selection.
This tweak improves the uniformity of the generated patches of hat
tiling, by selecting from (the closest 32-bit approximation I can get
to) the limiting probability distribution of finite patches in the
whole plane.

This shouldn't invalidate any grid description that contains enough
coordinates to uniquely specify a piece of tiling - in particular, any
generated by the game itself. But if anyone's been brave enough to
hand-type a grid description in the last two days and left off some of
the coordinates, then those might be invalidated.
2023-03-28 20:51:02 +01:00
2b1167d82a Fix references to the renamed 'auxiliary' directory.
I renamed it in a hurry this morning after the first report of a git
error message on Windows. Now I realise that several source files
referred to the old name, and also need fixing.
2023-03-27 19:31:14 +01:00
0af537d2c0 Rename the 'aux' subdirectory to avoid Windows restrictions.
James Harvey points out that Windows still forbids calling a file
'aux' in any context. Even a directory. Gaaah.
2023-03-27 09:23:41 +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
255744676c KaiOS: be more careful detecting the presence of KaiAds
Now that we catch JavaScript errors and report them to the user, I could
tell that the way we were detecting the presence of getKaiAd() didn't
work because it caused an alert every time a build without KaiAds was
started.  Detecting the presence of a global variable is slightly
tricky, but explicitly looking for it on "window" works and isn't very
ugly.
2023-03-22 22:54:19 +00:00