(2x2 with four-way symmetry must be either all-black (trivial) or
all-white (ambiguous). --bjh21)
(cherry picked from Android port, commit
27ae898e118b0a31a98d393bf56aa138845123e6)
(The restriction on 2x2 puzzles is because the board layer-out doesn't
use neutral pieces on such small boards, and the only soluble 2x2 boards
have neutral pieces. I haven't investigated the Tricky size limit, but
it seems entirely reasonable that all the smaller boards are too easy.
--bjh21)
(cherry picked from Android port, commit
133794977a13767e0c1596be6a5b26f2cf2d1fd1)
If you accidentally selected a cell that was part of a completed area,
it was hard to notice that you'd done so.
(cherry picked from Android port, commit
ca08cd832952cefd9a3b545f13785d7054a3e1f6)
Before, it worked by scaling all the colour components, which worked
reasonably for colours in the vicinity of white, but failed badly on a
black background. Now it generates the highlight and lowlight colours
by mixing the background colour with white and black respectively.
Where there isn't enough headroom, it adjusts the background by mixing
in a negative amount of white or black, which makes sense geometrically
if not in paint.
There is still a problem that green and yellow colours don't end up with
bright enough highlights. I think this is because the code doesn't
understand that green is brighter than red or blue.
This means that Range, Singles, and Tracks can now use the default
background colour even if it's close to white. In the case of Singles
I've left a dummy entry in the colour list so as not to renumber the
rest and break everyone's environment variables. If Singles ever needs
a new colour it can re-use that slot.
The "mkhighlight" functions can now take -1 as the requested highlight
or lowlight colour as an indication that no such colour should be
returned. If the caller doesn't want a highlight colour, there's not
much point in darkening the base colour to make it distinct from the
highlight, so don't do that.
This means that the background colours in Palisade and Untangle are no
longer unnecessarily darkened.
I was hoping that I could treat SoftLeft and SoftRight the same in C and
arrange to filter one of them out in JavaScript, but that turned out to
be impractical.
This is activated by putting the puzzle in an element with id
"puzzlecanvascontain". In that case, the puzzle's default size is as
close to filling that element as is achievable. Unlike in the normal
mode, this sets the CSS size of the canvas directly.
Because it might take a little while for the page to settle down after
loading, and because the size of the viewport might change, this listens
for "resize" and "load" events, and only bothers changing anything when
the page is fully loaded.
Waiting for the document to be complete might be a problem if we had
images and so forth that we could plausibly be waiting for, but we
don't.
This allows the HTML/CSS to decide that it would like a different
background colour without the need to recompile. The default if the CSS
specifies no colour (and hence the canvas is transparent) is the same
grey as before.
This means that the calculated font properties of the HTML canvas now
control what font is used. The size is overridden, and for monospaced
text so is the family.
I'd like to be able to also specify the monospaced font, maybe using a
CSS variable, but that looks like being quite a lot of extra complexity.
My experience when testing this was that constructing a valid "font"
string for a canvas context is prone to breakage, but broke in a way
that left the font unchanged, so we always set a simple specification
first before trying to construct one from CSS.
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.
The basic tile size in Tracks is required to be a multiple of 6, which
means that for small tile sizes the steps are rather large. With the
standard border widths, this means that the default 8-by-8 puzzle can be
246 pixels wide (tilesize == 24) or 184 pixels wide (tilesize == 18).
This is particularly annoying if you happen to have a 240-pixel-wide
screen.
This commit allows the puzzle to reduce or remove the borders at small
tile sizes, on the grounds that a slightly narrower border is acceptable
if it avoids needing to use a smaller tile size. It encodes the border
width in (tilesize % 6), and is thus enabled when the default border
width is 5 or less. Above that size (which is a tilesize of 48), I
assume the steps in tile size aren't big enough to be a serious problem.
The former grey was almost indistinguishable from its background colours
even on a good screen. I've separated the cursor colour from the grid
colour and made it a lot darker. This gives a contrast ratio over 3.0
even against a darkened tile.
The cursor is still hard to see against trackwork, so maybe something
that isn't grey would be even better.
The keyboard code was prone to adding null items to the undo history,
and was also unreadable. Rather than fix it, I've replaced it with a
jump to the mouse drop handling, lightly enhanced to reject drops on
things that aren't tiles.
Now rather than mucking around with the cursor keys, you can just type a
four-digit number and press Enter. Of course, if you still want to muck
around with the cursor keys they work the same as before.
Since Backspace was already assigned to clear the peg under the cursor,
I haven't co-opted it for the obvious action of clearing the peg to the
left of the cursor and moving the cursor left. The left arrow key is a
reasonable alternative anyway.
For consistency, 'L' now labels the pegs with numbers rather than
letters, and is documented.
It's annoying having to move it to the left each time. I suppose I
could enter the second guess in reverse order, but then I'd need to move
the cursor all the way to the right to submit it, which is just as bad.
This has been broken since 2015. It was accidentally using
"IS_CURSOR_SELECT(button)" in place of "button == CURSOR_SELECT" and
these are not the same thing.
It's sometimes useful to be able to have an HTML element that visually
forms an extension of the puzzle's border. By putting the puzzle's
colour 0 (which we assume to be its background) into a CSS variable,
such elements can do something like "background-color:
var(--puzzle-background)" to get that effect even if the puzzle uses a
non-default background colour.
At least one puzzle does no actual decoding in decode_ui, but does
re-initialise some fields. This is unnecessary because the mid-end only
calls decode_ui() with a game_ui it just allocated using new_ui().
By constructing the <input type=file> off screen and activating it from
JavaScript, we can jump straight to the browser's upload dialogue box
without interposing our own one. This gives a smoother experience, and
also avoids the difficult-to-handle <input type=file> ever being
visible.
Instead of an align=center HTML attribute, we now centre its contents
using CSS. Also, this element contains all the important contents of
the page, so it seems appropriate to us the HTML5 <main> element for
this.
There's not much point in re-requesting the drawing context from the
offscreen canvas at the start of each drawing operation. The canvas
keeps the context around and returns it on every call to getContext(),
so we may as well just keep our reference to it too. This does mean
that the front-end won't detect puzzles drawing outside of a redraw
operation, but I think it's the mid-end's job to assert things like
that.
Rumours that I'm doing this because I had a mysterious bug whereby ctx
was unexpectedly null are entirely true.
This is more compact than carefully drawing it on a canvas in
JavaScript. More importantly, the SVG resize handle scales nicely as
the page is zoomed, or on high-DPI screens.
At the moment, the nice SVG resize handle is removed by JavaScript,
but we need to wait a little while for everyone to get the new HTML
cached before changing that.