aboutsummaryrefslogtreecommitdiff
path: root/tracks.c (follow)
Commit message (Collapse)AuthorAge
* Fully remove references to "Train Tracks"Franklin Wei2020-12-07
| | | | | Calling it "Tracks" in most places but "Train Tracks" in others leads to confusion.
* Add method for frontends to query the backend's cursor location.Franklin Wei2020-12-07
| | | | | | | | | | | | | | | | 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.
* Tracks: fix a small memory leak.Simon Tatham2020-02-26
| | | | Spotted by Leak Sanitiser while I was testing the standalone solver.
* Tracks: add reverse neighbour deduction in hard mode.Simon Tatham2020-02-26
| | | | | | | | | | | | | | | | | This is the contrapositive of the deduction introduced in the previous commit. Previously I said: a square A can have some edges blocked in such a way that you know it can't be filled without a particular one of its neighbours B also being filled. And then, if you know the row containing A and B only has one filled square left to find, then it can't be A. This commit adds the obvious followup: if you know the row only has one _empty_ square left, then for the same reason, it can't be B! I'm putting this in at the new Hard difficulty, mostly out of guesswork rather than rigorous play-testing, because I don't remember ever having _observed_ myself making this deduction in the past. I'm open to changing the settings if someone has a good argument for it.
* Tracks: new parity-based deduction.Simon Tatham2020-02-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is another deduction I've known about in principle for ages but the game didn't implement. In the simplest case, it's obvious: if you can draw a line across the grid that separates the track endpoints from each other, and the track doesn't yet cross that line at all, then it's going to have to cross it at _some_ point. So when you've narrowed down to only one possible crossing place, you can fill it in as definite. IF the track already crosses your line and goes back again, the same rule still applies: _some_ part of your track is on one side of the line, and needs to get to the other. A more sensible way of expressing this is to say that the track must cross the boundary an _odd_ number of times if the two endpoints are on opposite sides of it. And conversely, if you've drawn a line across the grid that both endpoints are on the _same_ side of, then the track must cross it an _even_ number of times - every time it goes to the 'wrong' side (where the endpoints aren't), it will have to come back again eventually. But this doesn't just apply to a _line_ across the grid. You can pick any subset you like of the squares of the grid, and imagine the closed loop bounding that subset as your 'line'. (Or the _set_ of closed loops, in the most general case, because your subset doesn't _have_ to be simply connected - or even connected at all - to make this argument valid.) If your boundary is a closed loop, then both endpoints are always on the same side of that boundary - namely, the outside - and so the track has to cross the boundary an even number of times. So any time you can identify such a subset in which all but one boundary edge is already filled in, you can fill in the last one by parity. (This most general boundary-based system takes in all the previous cases as special cases. In the original case where it looks as if you need odd parity for a line across the grid separating the endpoints, what you're really doing is drawing a closed loop around one half of the grid, and considering the actual endpoint itself to be the place where the track leaves that region again - so, looked at that way, the parity is back to even.) The tricky part of implementing this is to avoid having to iterate over all subsets of the grid looking for one whose boundary has the right property. Luckily, we don't have to: a nice way to look at it is to define a graph whose vertices are grid squares, with neighbouring squares joined by a _graph_ edge if the _grid_ edge between those squares is not yet in a known state. Then we're looking for an edge of that graph which if removed would break it up into more separate components than it's already in. That is, we want a _bridge_ in the graph - which we can find all of in linear time using Tarjan's bridge-finding algorithm, conveniently implemented already in this collection in findloop.c. Having found a bridge edge of that graph, you imagine removing it, and find one of the two connected components it's just broken its previous component up into. That's your subset of grid squares, and now you can count track crossings around the boundary and fill in the bridge edge by parity. When I actually came to implement this, it turned out that the very first puzzle it generated was actually hard for me to solve, because as it turns out, this general analysis is much better at identifying opportunities to use this deduction than I am. A straight line right across the grid is often obvious: a few squares tucked into a complicated half-solved piece of the worldl, not so much. So I'm introducing a new Hard difficulty level, and putting this solution technique in there.
* Tracks: new neighbour-based deduction.Simon Tatham2020-02-26
| | | | | | | | | | | | | This is a deduction I've been using in my own head for years: if you only have one remaining filled square to put in a row, then it can't be any square that has two adjacent edges blocked, because if that square contains anything at all then it would have to be a corner piece, and a corner piece forces the square next to it to be filled as well. I ran across a puzzle today that this implementation couldn't solve, but I solved it fine by hand and found the deduction I was using that wasn't implemented here. Now it is.
* Tracks: add standalone solver program.Simon Tatham2020-02-26
| | | | | | | Having one of these makes it much easier to debug what's going on when the solver can't solve something. Also, now the solver can grade the difficulty of a puzzle, it's useful to expose that feature in a command-line tool.
* Tracks: make solver return max difficulty used.Simon Tatham2020-02-26
| | | | | | | This should speed up game generation, because now we don't have to run the solver _twice_ whenever we want to check that the grid has exactly the intended difficulty. Instead, we can just run it once and check the max_diff output.
* Add missing 'static' to game-internal declarations.Simon Tatham2018-11-13
| | | | | | | | | Another thing I spotted while trawling the whole source base was that a couple of games had omitted 'static' on a lot of their internal functions. Checking with nm, there turned out to be quite a few more than I'd spotted by eye, so this should fix them all. Also added one missing 'const', on the lookup table nbits[] in Tracks.
* Use C99 bool within source modules.Simon Tatham2018-11-13
| | | | | | | | | | 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.
* Replace TRUE/FALSE with C99 true/false throughout.Simon Tatham2018-11-13
| | | | | | 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.
* Adopt C99 bool in the game backend API.Simon Tatham2018-11-13
| | | | | | | | | | | 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.
* Tracks: stop drawing background for clues in game_print.Simon Tatham2018-07-20
| | | | | This makes the clue numbers actually visible in the printed output, instead of black on black.
* Add a request_keys() function with a midend wrapper.Franklin Wei2018-04-22
| | | | | | | | 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.
* Return error messages as 'const char *', not 'char *'.Simon Tatham2017-10-01
| | | | | They're never dynamically allocated, and are almost always string literals, so const is more appropriate.
* Use a proper union in struct config_item.Simon Tatham2017-10-01
| | | | | | 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.
* New name UI_UPDATE for interpret_move's return "".Simon Tatham2017-10-01
| | | | | | | | | | | | | 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.
* tracks: Make error clue background whiteIan Jackson2017-09-30
| | | | | | This makes them stand out more. Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* tracks: Greyscale colour initialisation: line up columnsIan Jackson2017-09-30
| | | | | | | | | This makes it much easier to see the commonality in these formulaic lines. Whitespace change only. Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* tracks.c: draw_clue: Introduce bg parameterIan Jackson2017-09-30
| | | | | | No functional change. Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* tracks: thicken the lines of the grid itself.Simon Tatham2017-09-29
| | | | | | | | | | | | | | | | | | | Since these lines are always orthogonal, it's easier to draw them using draw_rect than draw_thick_line. Previously, the grid lines were drawn just inside the top and left edges of the region redrawn by draw_square(), so only the bottom and right edges of the whole grid were not covered by any draw_square call. To avoid having to shift the logical grid centre, I'm now drawing parts of the grid outline on all four sides of the draw_square() region, so that half the thickened grid edge protrudes on every side of the grid as a whole. In the process of splitting up the grid line width into the part on the top and left edges and the part on the bottom and right, I've renamed the confusingly named BORDER_WIDTH define (which wasn't used anyway) to a set of things that make it clear that they're referring to the grid lines as opposed to the border of the whole puzzle.
* tracks: Roughly double the thickness of the "no track" crossesIan Jackson2017-09-29
| | | | | | | | | | | | The default of 1/30 is rather thin, and probably wasn't chosen deliberately (since it was just inherited from the default 1-pixel line width, and the preferred tile size). Thicker crosses stand out more and make play easier. Use 1/16 since it's a rounder number than 1/15 :-). Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* tracks: Scale thickness of "no track here" crossesIan Jackson2017-09-29
| | | | | | | | | | Simply replace the calls to draw_line with calls to draw_thick_line. We need to choose a thickness parameter. The preferred tile size is 30, and the "draw_line" function draws a 1-pixel line, so the thickness right now is 1/30 the tile size, at the preferred size. Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* Rework the preset menu system to permit submenus.Simon Tatham2017-04-26
| | | | | | | | | | | | | | | | | | | | 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.
* Clarify conditions to avoid compiler errorsKhem Raj2016-12-06
| | | | | | | | | | Fix errors pointed out by clang error: logical not is only applied to the left hand side of this bitwise operator [-Werror,-Wlogical-not-parentheses] | if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue; | ^ Signed-off-by: Khem Raj <raj.khem@gmail.com>
* Tracks: fix further completion-checking loopholes.Simon Tatham2016-02-26
| | | | | | | | | | | | A user pointed out that Tracks could sometimes flash for completion when there wasn't even a full path from A to B! And it looks as if that wasn't even a mistake I introduced with the loop-checking revamp this week. Now I _think_ it's complete: we set ret=FALSE in check_completion wherever we also produce an error highlight, and also whenever there is no path connecting A with B. And if there is a path connecting A with B, then any square not on the path becomes an error highlight.
* Tracks: tighten up a small loophole in completion checking.Simon Tatham2016-02-24
| | | | | | | | If you had a single connected path linking the source to the destination but _also_ had a spurious edge elsewhere in the grid, then the spurious edge would be highlighted as an error, but it wouldn't inhibit declaring the game complete and showing the victory flash.
* Tracks: use the new findloop for loop detection.Simon Tatham2016-02-24
| | | | | | | Tracks's previous loop detector was very basic, and only bothered to highlight one loop, even if the player managed to create more than one at a time. Now we highlight all of them.
* Fix premature completion flash in Tracks.Simon Tatham2015-10-23
| | | | | | | | | | | | | Commit 44e2690ab loosened check_completion's idea of what made a square count as 'having track in it' for purposes of checking violations of the row/column counts. Unfortunately, that loosened notion also applied to the check for the game being complete - so the game would announce a win as soon as you had every square shaded, even if you hadn't actually laid all the exact track positions down. Now we separately count up the number of track-ish squares and the number of fully completed ones, and use the former for error checking and the latter for completion checking.
* Highlight clue errors in Tracks in some more situations.Jonas Kölker2015-10-21
| | | | | | | | | - Count any square as having a track either if the square is marked as such (rendered as a different background), or if at least one adjacent edge is marked as containing a segment of train track (rendered as train tracks if they're placed, else as an '='). - Do the same counting in rows and columns.
* New puzzle from James Harvey: 'Tracks'.Simon Tatham2015-02-08