aboutsummaryrefslogtreecommitdiff
path: root/tracks.c (follow)
Commit message (Collapse)AuthorAge
* Rename UI_UPDATE as MOVE_UI_UPDATEBen Harris2023-06-11
| | | | | | | | All the other constants named UI_* are special key names that can be passed to midend_process_key(), but UI_UPDATE is a special return value from the back-end interpret_move() function instead. This renaming makes the distinction clear and provides a naming convention for future special return values from interpret_move().
* New backend functions: get_prefs and set_prefs.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | 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.
* Pass a game_ui to compute_size, print_size and print.Simon Tatham2023-04-21
| | | | | | | 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.
* Reorganise the dsf API into three kinds of dsf.Simon Tatham2023-04-20
| | | | | | | | | | | | | | | | | | 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.
* Remove size parameter from dsf init and copy functions.Simon Tatham2023-04-20
| | | | | | | | | 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.
* Declare all dsfs as a dedicated type name 'DSF'.Simon Tatham2023-04-20
| | | | | | | 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.
* Consistently use snew_dsf to allocate dsfs.Simon Tatham2023-04-20
| | | | | All remaining cases where a dsf was allocated via snewn(foo, int) are removed by this change.
* Use a dedicated free function to free dsfs.Simon Tatham2023-04-20
| | | | | No functional change: currently, this just wraps the previous sfree call.
* Make encode_ui() and decode_ui() optional in back-endsBen Harris2023-04-08
| | | | | | | | | | | | 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.
* Fall back to <math.h> if <tgmath.h> doesn't work.Simon Tatham2023-04-06
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* Replace <math.h> with <tgmath.h> throughoutBen Harris2023-04-04
| | | | | | | | | | | | | | | 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.
* Add an environment variable to control initial cursor visibilityBen Harris2023-03-22
| | | | | | | | | | | | | If you define PUZZLES_INITIAL_CURSOR=y, puzzles that have a keyboard cursor will default to making it visible rather than invisible at the start of a new game. Behaviour is otherwise the same, so mouse actions will cause the cursor to vanish and keyboard actions will cause it to appear. It's just the default that has changed. The purpose of this is for use on devices and platforms where the primary or only means of interaction is keyboard-based. In those cases, starting with the keyboard cursor invisible is weird and a bit confusing.
* Tracks: missing \n in debug statement.Simon Tatham2023-03-10
|
* Tracks: set drag_s{x,y} even if starting off-gridChris Boyle2023-02-16
| | | | | | | | | | | Otherwise, if subsequent mouse/finger movement lines up with the previous drag attempt's start, then suddenly a drag is in progress from there, which is confusing. Fixes #588 (cherry picked from Android port, commit 8ce1bbe460d70a915caf2dbeb30354d22dc8a8ef)
* Avoid invalid moves when solving TracksBen Harris2023-02-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The solver, when it decided that an edge or square should be both TRACK and NOTRACK, would correctly decide that the puzzle was insoluble, but would also mark the edge with both flags in its working copy. This could then lead to assertion violations if that working copy of the board was used for something else, for instance if it was fed back into the solver. This couldn't happen in normal play, since failed solutions just cause the solve command to fail, but the diagnostic "H" command could trigger it from a save file, causing an assertion failure: "state->sflags[y*state->p.w + x] & S_CLUE". Now when the solver runs into this situation, it marks the puzzle as insoluble but doesn't set the invalid flag, so the board remains valid and future solve operations are safe. This save file is the one that demonstrated the problem: SAVEFILE:41:Simon Tatham's Portable Puzzle Collection GAME :12:Train Tracks PARAMS :5:6x6t0 CPARAMS :5:6x6t0 DESC :31:b0t9l,,S0,00,0,0,4,0,0,S0,0,0,0 NSTATES :1:8 STATEPOS:1:2 MOVE :1:H MOVE :1:H MOVE :1:H MOVE :1:H MOVE :1:H MOVE :1:H MOVE :1:H
* Remove various unused game functionsBen Harris2023-01-31
| | | | | | | | | | | | | | | | | | | | 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.
* Tracks: tighten up the 'illegal solve submoves' fix.Simon Tatham2023-01-19
| | | | | | | | | | Chris mentioned in the commit message that there was a risk that illegal moves might be permitted when playing on after a solve. So I've changed the condition so that it depends only on whether the move _currently being executed_ is a solve, rather than whether there was a solve action anywhere in the undo history. (Also, wrapped overlong lines while I was here.)
* Tracks: let solve make illegal movesChris Boyle2023-01-19
| | | | | | | | | | | | | | | | | Not only does it set the outer edges to NOTRACK, but it may also overwrite any mistakes the user has previously made elsewhere. Otherwise, the entire solve is rejected ("Solve unavailable" error on Android) if the user has made a single mistake, which is inconsistent with the other games. This may be giving a free pass to corrupted moves that occur after a solve, so this may still want tightening up in some way, but it's still limited to squares within the grid, so I agree with Ben's assessment that this is likely not to be exploitable. Fixes #584 (cherry picked from Android port, commit 33bd14fb6f7cd760e7218fffd90f3a266b1f4123)
* Tracks: make sure moves are valid in execute_move()Ben Harris2023-01-15
| | | | | | | | | | Tracks allowed moves in execute_move() that shouldn't have been allowed, like changing the state of the edges of the board. This moves couldn't be generated by interpret_move(), but could be loaded from a save file. Now execute_move() uses the same ui_can_flip_*() functions as interpret_move() to decide whether a particular move is allowed. This should prevent some assertion failures when loading corrupted save files.
* Last-ditch maximum size limit for TracksBen Harris2023-01-15
| | | | | This makes sure that width * height <= INT_MAX, which it rather needs to be.
* Slightly better macro usage for Tracks completion flashBen Harris2022-12-30
|
* Clear any existing Tracks flash data when generating itBen Harris2022-12-30
| | | | | | | | | | Simon points out that if you have an ambiguous puzzle then it might need different flash data for different solutions, so if you solve it one way, than manually unsolve it and solve it another way, the old flash data in the game state need to be cleared out when the new flash data are written. Tested by solving the hugely ambiguous "5x5:CwC,5,5,5,5,S5,S5,5,5,5,5".
* Tracks: Set the grid colour based on othersBen Harris2022-12-29
| | | | | | | | | | | COL_GRID used to be mid-way between the usual COL_BACKGROUND and COL_TRACK_BACKGROUND. But then I changed mkhighlight() so that it didn't make COL_BACKGROUND so dark and that made the grid lines indistinguishable from the track background. Now COL_GRID is generated from COL_BACKGROUND and COL_TRACK_BACKGROUND so as long as those are sufficiently distinct from each other, COL_GRID will be distinct from both of them.
* Fancier completion flash for TracksBen Harris2022-12-27
| | | | | | It now runs along the track from A to B, spending the first half of FLASH_TIME lighting all the segments and the second half extinguishing them.
* Tracks: Highlight more counting errors if track looks goodBen Harris2022-12-27
| | | | | | | | | | | | | | | | | Normally, Tracks puts error marks on row and column clues only when one of the existing track or no-track marks will have to be removed to satisfy the clue. This could lead to a situation where the player had built a track from A to B and got neither a win nor a highlighted error because the only error was in a row or column having too few track segments. This commit re-arranges the completion checking so that if there's a complete track from A to B and no spurious track then the game will highlight any clue that isn't matched by the actually laid track. This should mean that any solution with a track from A to B will either be a win or have a highlighted error. This should fix Android issue #266.
* Don't request a highlight colour in games that don't need oneBen Harris2022-12-14
| | | | | | | | 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.
* New backend function: current_key_label()Ben Harris2022-12-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* tracks: Allow a smaller or non-existent border at small tile sizesBen Harris2022-12-08
| | | | | | | | | | | | | | | | 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.
* tracks: Make the keyboard cursor more visibleBen Harris2022-12-08
| | | | | | | | | | 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.
* Centralise initial clearing of the puzzle window.Simon Tatham2021-04-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | I don't know how I've never thought of this before! Pretty much every game in this collection has to have a mechanism for noticing when game_redraw is called for the first time on a new drawstate, and if so, start by covering the whole window with a filled rectangle of the background colour. This is a pain for implementers, and also awkward because the drawstate often has to _work out_ its own pixel size (or else remember it from when its size method was called). The backends all do that so that the frontends don't have to guarantee anything about the initial window contents. But that's a silly tradeoff to begin with (there are way more backends than frontends, so this _adds_ work rather than saving it), and also, in this code base there's a standard way to handle things you don't want to have to do in every backend _or_ every frontend: do them just once in the midend! So now that rectangle-drawing operation happens in midend_redraw, and I've been able to remove it from almost every puzzle. (A couple of puzzles have other approaches: Slant didn't have a rectangle-draw because it handles even the game borders using its per-tile redraw function, and Untangle clears the whole window on every redraw _anyway_ because it would just be too confusing not to.) In some cases I've also been able to remove the 'started' flag from the drawstate. But in many cases that has to stay because it also triggers drawing of static display furniture other than the background.
* 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.