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.
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.
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.
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.
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.
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.
Now midend.c directly tests the returned pointer for equality to this
value, instead of checking whether it's the empty string.
A minor effect of this is that games may now return a dynamically
allocated empty string from interpret_move() and treat it as just
another legal move description. But I don't expect anyone to be
perverse enough to actually do that! The main purpose is that it
avoids returning a string literal from a function whose return type is
a pointer to _non-const_ char, i.e. we are now one step closer to
being able to make this code base clean under -Wwrite-strings.
Previously, the network, grid edges and barriers were all drawn with
fixed line thickness of one pixel, or three pixels in the case of
wires (one central one in a lit-up colour and a black border pixel on
each side). This worked badly on high-DPI displays, or in any other
environment where you expand the window noticeably.
I've tried a few times before to fix this, but the problem was always
that the Net drawing code was complicated and confusing, because Net
was one of the founding puzzles in this collection and its drawing
code predated a lot of the sensible organisation and best-practice
doctrines I've come up with since then and used in the later puzzles.
(The best example of this was the way that redrawing a grid square
always redrew _all_ the surrounding borders, which is very confusing
in the light of my later practice of dividing up the grid into
disjoint regions that are always redrawn with clipping.) It was hard
to make any change to the Net graphics with the code in that state; I
tried several times and decided I couldn't sensibly make it work
without throwing it all away and rewriting from scratch, which I was
always reluctant to do.
But not any more! Since Ian sent some patches to improve the graphics
scaling in Tracks, and since Net was at the top of my personal
wishlist of games that needed the same treatment, I've decided it's
time to do just that.
So, this commit throws out all of Net's previous redraw code, and
replaces it with something in the more modern style I usually adopt in
new puzzles. The new draw_tile() doesn't read data out of the game
state willy-nilly; instead it takes a single word of bit-flags
describing everything about the tile it's drawing, and makes all its
decisions based on that word. And the main redraw loop constructs a
whole array of flags words describing what it would _like_ the grid to
look like, and then calls draw_tile() for every square whose word
doesn't match the one that was previously drawn there. (With the one
exception that the presence of the TILE_ROTATING flag in either the
old or new word forces a redraw _even_ if the two words are identical,
because that's how the move animation works.)
The new graphics look more or less the same at the default resolution
(there may be a pixel difference here or there but I don't think it's
noticeable if so), but now they scale up properly if you enlarge or
maximise the puzzle window.
Net is one of the very oldest puzzles in the collection, and has
apparently been physically copying the complete collection of totally
immutable barrier data in every game state since 2004. About time it
stopped, I think!
The solver code still had an assumption, which must have dated before
the Solve menu option was introduced, that all puzzles presented to it
had at least one valid solution, and was enforcing that assumption by
assert(). Now the solver returns a more sensible failure code which
solve_game() can convert into a proper error message.
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.
Another oddity involving an empty square is that if it coincides with
the source square for highlights (either by original design of the
game id, or because the player Ctrl-moves the source square into an
empty grid cell during play), then everything stops being lit up as
active. That's fine - you can still play the game using other
indications of error, such as the loop detection highlight - but it
looks silly for the status line to say 'Active: 1/lots'. So in that
situation I suppress the 'active' counter completely; it comes back
when you move the source square to somewhere it's _possible_ to
highlight more than one square.
While I'm at it, I've also removed the active counter in the case
where the game is completely solved, because in that situation it's
more or less unnecessary anyway, and that way the normal course of
play on the default small grid size doesn't overflow the available
status line space.
A hand-typed grid is permitted to use the square type '0' (never
generated by Net's own grid generator), which is a completely empty
square. This requires an adjustment to the completion checker, so that
such squares aren't required to be connected; otherwise, a grid
containing one would be permanently uncompletable.
However, the completion checker missed one case - it was
unconditionally checking that all squares are connected to the _top
left corner_, on the basis that (before I thought of the zero square)
any source square is as good as any other if what you really want to
know is whether they're all connected to each other. But that means
that if the top left square _is_ the empty one, things to wrong - e.g.
5x5:02c328ade11adb129d7c3e524 would fail to give a completion flash.
Fixed by starting the completion-checking search from the first
non-empty square we find.
I've removed the old algorithm (the one described as 'footpath dsf' in
the findloop.c appendix comment, though I hadn't thought of that name
at the time), and replaced it with calls to the new API.
This should have no functional effect: there weren't any known bugs in
the previous loop-finder that affected currently supported play modes.
But this generality improvement means that non-orientable playing
surfaces could be supported in the future, which would have confused
the old algorithm. And Net, being the only puzzle as yet that's played
on a torus, is perhaps the one most likely to want to generalise
further at some point.
I unthinkingly transplanted into Net the same loop-finding algorithm
used in Loopy and Slant, which identifies the connected components
into which the grid lines divide the plane, and marks an edge as part
of a loop iff it separates two different components. This works fine
for a planar graph, but in Net's wrapping mode, it's possible to have
loops which do not have this property - e.g. a loop can go off the top
of the grid and back on the bottom to join up with itself, and then
it _doesn't_ disconnect the surface into two components.
(In principle, this kind of problem can turn up in any topological
space with a non-trivial H_1 homology group, which is why it fails on
the torus to which Net's wrapping mode corresponds, but not on the
plane or sphere. I think it's forgivable that I hadn't expected
homology to be the cause of any bug in practical code ever!)
Fixed by inventing yet another dsf-based loop-finding algorithm, this
one based on tracing round the outside of connected components of the
graph. It's still not _fully_ general, in that this one still depends
on the graph being drawn on an orientable surface (so it'll need
another rewrite if I ever add Mobius strip or Klein bottle modes for
Net...), but it's fairly simple to state and implement, and detects
loops that the previous implementation did not, such as the one in the
starting position of 3x3w:1a39ac6a8 .
Loops are detected using the same dsf technique I ended up using in
Slant, and highlighted in red (whether or not the connected component
they belong to is currently powered).
Should make life a little bit easier for someone who's filled in most
of the grid to a nice uniform cyan and finds one piece left over - now
they have some idea where to start looking for their mistake.
We also take care not to generate any loops in the starting position,
on grounds of politeness (don't accuse the user of a mistake before
they've even had a chance to make one).
Loop detection does not contribute to the code that decides whether
the puzzle is complete, because there's no need - if all squares are
connected together, then there can't be any loops anyway, by graph
theory.
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]
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]
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]
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]
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]
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]
CURSOR_SELECT2 button (to, respectively, toggle a "definitely not
light" dot and to rotate in the opposite direction from
CURSOR_SELECT).
[originally from svn r8299]
actual behaviour change: Untangle now permits dragging with the
right mouse button, which has exactly the same effect as it does
with the left. (Harmless on desktop platforms, but helpful when
"right-click" is achieved by press-and-hold; now the drag takes
place even if you hesitate first.)
[originally from svn r8177]
_conditionally_ able to format the current puzzle as text to be sent
to the clipboard. For instance, if a game were to support playing on
a square grid and on other kinds of grid such as hexagonal, then it
might reasonably feel that only the former could be sensibly
rendered in ASCII art; so it can now arrange for the "Copy" menu
item to be greyed out depending on the game_params.
To do this I've introduced a new backend function
(can_format_as_text_now()), and renamed the existing static backend
field "can_format_as_text" to "can_format_as_text_ever". The latter
will cause compile errors for anyone maintaining a third-party front
end; if any such person is reading this, I apologise to them for the
inconvenience, but I did do it deliberately so that they'd know to
update their front end.
As yet, no checked-in game actually uses this feature; all current
games can still either copy always or copy never.
[originally from svn r8161]
is mostly done with ifdefs in windows.c; so mkfiles.pl generates a
new makefile (Makefile.wce) and Recipe enables it, but it's hardly
any different from Makefile.vc apart from a few definitions at the
top of the files.
Currently the PocketPC build is not enabled in the build script, but
with any luck I'll be able to do so reasonably soon.
[originally from svn r7337]
structures, meaning that ad-hoc initialisation now doesn't work.
Hence, this checkin converts all ad-hoc dsf initialisations into
calls to dsf_init() or snew_dsf(). At least, I _hope_ I've caught
all of them.
[originally from svn r6888]
which didn't actually need it. It was originally introduced in
Fifteen to suppress animation on Solve moves, but midend.c now does
that centrally unless the game specifically instructs it otherwise.
Therefore, just_used_solve is obsolete in all games which previously
used it. (Mines was even worse: it scrupulously maintained the
correctness of the field but never used it!)
Untangle is exempt from this cleanup: its `just_solved' field is
used to change the _length_ of the animation on Solve moves, not to
suppress it entirely, and so it has to stay.
[originally from svn r6419]
function, since it took no parameters by which to vary its decision,
and in any case it's hard to imagine a game which only
_conditionally_ wants a status bar. Changed it into a boolean data
field in the backend structure.
[originally from svn r6417]
was actually using it, and also it wasn't being called again for
different game states or different game parameters, so it would have
been a mistake to depend on anything in that game state. Games are
now expected to commit in advance to a single fixed list of all the
colours they will ever need, which was the case in practice already
and simplifies any later port to a colour-poor platform. Also this
change has removed a lot of unnecessary faff from midend_colours().
[originally from svn r6416]
game_print(), wherever feasible. This fixes a specific bug in Loopy
(James H's new field ds->linewidth wasn't being set up, leading to
corrupted print output), but I've made the change in all affected
files because it also seems like a generally good idea to encourage
it for future games, to prevent other problems of this type.
There is one slight snag, which is that Map _can't_ do this because
its game_set_size() also initialises a blitter. I could fix this by
abstracting the common parts of Map's game_set_size() out into a
subfunction called by game_set_size() and also called directly by
game_print(); alternatively, I could introduce a means of
determining whether a `drawing *' was for screen or printing use.
Not sure which yet.
[originally from svn r6340]
as seen by the back ends from the one implemented by the front end,
and shoved a piece of middleware (drawing.c) in between to permit
interchange of multiple kinds of the latter. I've also added a
number of functions to the drawing API to permit printing as well as
on-screen drawing, and retired print.py in favour of integrated
printing done by means of that API.
The immediate visible change is that print.py is dead, and each
puzzle now does its own printing: where you would previously have
typed `print.py solo 2x3', you now type `solo --print 2x3' and it
should work in much the same way.
Advantages of the new mechanism available right now:
- Map is now printable, because the new print function can make use
of the output from the existing game ID decoder rather than me
having to replicate all those fiddly algorithms in Python.
- the new print functions can cope with non-initial game states,
which means each puzzle supporting --print also supports
--with-solutions.
- there's also a --scale option permitting users to adjust the size
of the printed puzzles.
Advantages which will be available at some point:
- the new API should permit me to implement native printing
mechanisms on Windows and OS X.
[originally from svn r6190]
nikoli.co.jp (which has quite a few puzzles that they don't seem to
have bothered to translate into English).
Minor structural change: the disjoint set forest code used in the
Net solver has come in handy again, so I've moved it out into its
own module dsf.c.
[originally from svn r6155]
whether the timer is currently going is no longer solely dependent
on the current game_state: it can be dependent on more persistent
information stored in the game_ui. In particular, Mines now freezes
the timer permanently once you complete a grid for the first time,
so that you can then backtrack through your solution process without
destroying the information about how long it took you the first time
through.
[originally from svn r6088]
- reinstate the initialisation of ds->w and ds->h in guess.c, which
I'd accidentally removed during game_size() refactoring
- reorganise Net's interpret_move() so that my uncommitted patch
for drag-based UI (which he uses on the Palm port) will apply
more easily
- the interpret_move() changes make it easy to have a single move
type which rotates a tile by 180 degrees, so this is now provided
via the `F' key (but there's no spare button available to provide
it via the mouse).
[originally from svn r6070]
encode_params(). This is necessary for cases where generation-time parameters
that are normally omitted from descriptive IDs can place restrictions on other
parameters; in particular, when the default value of a relevant generation-time
parameter is not the one used to generate the descriptive ID, validation could
reject self-generated IDs (e.g., Net `5x2w:56182ae7c2', and some cases in
`Pegs').
[originally from svn r6068]