aboutsummaryrefslogtreecommitdiff
path: root/pearl.c (follow)
Commit message (Collapse)AuthorAge
* Refactor `button & ~MOD_MASK' as `STRIP_BUTTON_MODIFIERS(button)'.Franklin Wei2024-07-31
| | | | | | | | | This refactors all instances of bitwise-ANDs with `~MOD_MASK'. There is a handful of more complex instances I left unchanged (in cube.c, midend.c, and twiddle.c), since those AND with `~MOD_MASK | MOD_NUM_KEYPAD' or similar. I don't think it's worth writing a macro for those cases. Also document this new macro's usage in devel.but.
* move_cursor(): handle visible flag; return useful valueBen Harris2023-08-09
| | | | | | | | | | | | | | | | | This adds an extra parameter to move_cursor() that's an optional pointer to a bool indicating whether the cursor is visible. This allows for centralising the common idiom of having the keyboard cursor become visible when a cursor key is pressed. Consistently with the vast majority of existing puzzles, the cursor moves even if it was invisible before, and becomes visible even if it can't move. The function now also returns one of the special constants that can be returned by interpret_move(), so that the caller can correctly return MOVE_UI_UPDATE or MOVE_NO_EFFECT without needing to carefully check for changes itself. Callers are updated only to the extent that they all pass NULL as the new argument. Most of them could now be substantially simplified.
* Pearl: re-use a single grid structure when generatingBen Harris2023-08-06
| | | | | | | | | | | | | | Pearl generally has to generate quite a lot of candidate loops before it can find one that makes a viable puzzle. Before this change it generated a new grid structure for each of those candidate loops. The result was that grid_new() accounted for over 5% of the puzzle-generation time. Pulling grid_new() out of the loop-generation loop makes "pearl --generate 100 8x8dt#0" about 6% faster on my laptop, while producing precisely the same output. Most of this change is just renaming the "grid" variable in new_clues() so it doesn't collide with the typedef of the same name.
* grid.c: allocate face/edge/dot arrays incrementally.Simon Tatham2023-07-07
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously, the 'faces', 'edges' and 'dots' arrays in a grid structure were arrays of actual grid_face, grid_edge and grid_dot structures, physically containing all the data about the grid. But they also referred to each other by pointers, which meant that they were hard to realloc larger (you'd have to go through and rewrite all the pointers whenever the base of an array moved). And _that_ meant that every grid type had to figure out a reasonably good upper bound on the number of all those things it was going to need, so that it could allocate those arrays the right size to begin with, and not have to grow them painfully later. For some grid types - particularly the new aperiodic ones - that was actually a painful part of the job. So I think enough is enough: counting up how much memory you're going to need before using it is a mug's game, and we should instead realloc on the fly. So now g->faces, g->edges and g->dots have an extra level of indirection. Instead of being arrays of actual structs, they're arrays of pointers, and each struct in them is individually allocated. Once a grid_face has been allocated, it never moves again, even if the array of pointers changes size. The old code had a common idiom of recovering the array index of a particular element by subtracting it from the array base, e.g. writing 'f - g->faces' or 'e - g->edges'. To make that lookup remain possible, I've added an 'index' field to each sub-structure, which is initialised at the point where we decide what array index it will live at. This should involve no functional change, but the code that previously had to figure out max_faces and max_dots up front for each grid type is now completely gone, and nobody has to solve those problems in advance any more.
* Pearl: slightly better handling of clicks outside the gridBen Harris2023-06-26
| | | | | | | | | | | | | | | In Pearl, a mouse-down outside the grid sets ui->ndragcoords to -1. The intended effect of this is to make sure that future drags are ignored, so you can't try to draw a line starting off the grid. However, this also has the effect of clearing any in-progress drag. This can happen if there's a keyboard "drag" in progress at the time. This is arguably wrong, but much more wrong was that interpret_move returned MOVE_UNUSED (and previously NULL) in this case. That meant that the display didn't get updated to show the abandonment of the drag, or the removal of the keyboard cursor that also happened. This commit changes MOVE_UNUSED to MOVE_UI_UPDATE so that at least the effect is correctly visible.
* Distinguish MOVE_UNUSED from MOVE_NO_EFFECT in PearlBen Harris2023-06-16
|
* 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().
* Require games to accept new_ui(NULL) if they have preferences.Simon Tatham2023-04-23
| | | | | | | | | | | This will be necessary in the next commit, so that the midend can make a game_ui out of nothing in order to store preferences in it. The only game actually affected by this requirement is Pearl, which has a preference (GUI style) and also allocates space based on the game_state's grid size to store the coordinates of a path being dragged, so if there is no game_state, it can't do the latter - which is OK, because it also won't be expected to.
* 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.
* Move per-puzzle ad-hoc getenv preferences into game_ui.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Environment variables that set specific settings of particular puzzles, such as SLANT_SWAP_BUTTONS, LIGHTUP_LIT_BLOBS and LOOPY_AUTOFOLLOW, now all affect the game behaviour via fields in game_ui instead of being looked up by getenv in the individual functions that need to know them. The purpose of this refactoring is to put those config fields in a place where other more user-friendly configuration systems will also be able to access them, once I introduce one. However, for the moment, there's no functional change: I haven't _yet_ changed how the user sets those options. They're still set by environment variables alone. All I'm changing here is where the settings are stored inside the code, and exactly when they're read out of the environment to put into the game_ui. Specifically, the getenvs now happen during new_ui(). Or rather, in all the puzzles I've changed here, they happen in a subroutine legacy_prefs_override() called from within new_ui(), after it's set up the default values for the settings, and then gives the environment a chance to override them. Or rather, legacy_prefs_override() only actually calls getenv the first time, and after that, it's cached the answers it got. In order to make the override functions less wordy, I've altered the prototype of getenv_bool so that it returns an int rather than a bool, and takes its default return value in the same form. That way you can set the default to something other than 0 or 1, and find out whether a value was present at all. This commit only touches environment configuration specific to an individual puzzle. The midend also has some standard environment-based config options that apply to all puzzles, such as colour scheme and default presets and preset-menu extension. I haven't handled those yet.
* 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.
* New shared function, getenv_bool()Ben Harris2023-03-22
| | | | | | | | | | | | | This provides a standard way to get a boolean from an environment variable. It treats the variable as true iff its value begins with 'y' or 'Y', like most of the current implementations. The function takes a default value which it returns if the environment variable is undefined. This replaces the various ad-hoc tests of environment variable scattered around and mostly doesn't change their behaviour. The exceptions are TOWERS_2D in Towers and DEBUG_PUZZLES in the Windows front end. Both of those were treated as true if they were defined at all, but now follow the same rules as other boolean environment variables.
* Fix missing statics and #includes on variables.Simon Tatham2023-02-18
| | | | | | | | | | | | | | | | | | | | | | | After Ben fixed all the unwanted global functions by using gcc's -Wmissing-declarations to spot any that were not predeclared, I remembered that clang has -Wmissing-variable-declarations, which does the same job for global objects. Enabled it in -DSTRICT=ON, and made the code clean under it. Mostly this was just a matter of sticking 'static' on the front of things. One variable was outright removed ('verbose' in signpost.c) because after I made it static clang was then able to spot that it was also unused. The more interesting cases were the ones where declarations had to be _added_ to header files. In particular, in COMBINED builds, puzzles.h now arranges to have predeclared each 'game' structure defined by a puzzle backend. Also there's a new tiny header file gtk.h, containing the declarations of xpm_icons and n_xpm_icons which are exported by each puzzle's autogenerated icon source file and by no-icon.c. Happily even the real XPM icon files were generated by our own Perl script rather than being raw xpm output from ImageMagick, so there was no difficulty adding the corresponding #include in there.
* Check state is valid at the end of a move in PearlBen Harris2023-02-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A Pearl move string contains a sequence of sub-moves, each of which can affect the state of the connection between the centre of a square and one of its edges. interpret_move() generates these in pairs so that the two halves of a connection between the centres of adjacent squares stay in the same state. If, however, a save file contains mismatched half-moves, execute_move() should ideally return NULL rather than causing an assertion failure. This has to be checked at the end of the whole move string, so I've arranged for check_completion() to return a boolean indicating whether the current state (and hence the move preceding it) is valid. It now returns 'false' when a connection stops at a square boundary or when it goes off the board. These conditions used to be assertion failures, and now they just cause the move to be rejected. This supersedes the check for off-board connections added in 15f4fa8, since now check_completion() can check for off-board links for the whole board at once. This save file trivially demonstrates the problem, causing "dsf_update_completion: Assertion `state->lines[bc] & F(dir)' failed" without this fix: SAVEFILE:41:Simon Tatham's Portable Puzzle Collection GAME :5:Pearl PARAMS :5:6x6t0 CPARAMS :5:6x6t0 DESC :17:BbBfWceBbWaBWWgWB NSTATES :1:2 STATEPOS:1:2 MOVE :6:R1,0,0
* Pearl: fix bounds check in previous commit.Simon Tatham2023-02-05
| | | | Ahem. That's what I get for testing the fix on a square puzzle.
* Pearl: fix assertion failure on bad puzzle.Simon Tatham2023-02-05
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Similarly to the previous commit, if you started Pearl with at least some kinds of invalid puzzle (e.g. "6x6:abBfWcWWrBa") and then pressed 'h' to get hints, you could provoke an assertion failure. But this time the assertion wasn't in the solver itself; the solver gave up gracefully and didn't crash, but it _did_ leave the links between squares in the game_state in an inconsistent state, in that one square was marked as linking to its neighbour without the neighbour also linking back to it. This caused the /* should have reciprocal link */ assertion in dsf_update_completion to fail, when that was called from check_completion after the solver had finished, to decide whether the puzzle was now solved. In this commit, I arrange that whether or not pearl_solve returns a grid layout that's legal by the rules of the _puzzle_, it at least returns one that's legal by the rules of the _data representation_, in that every link between squares is either bidirectional or absent. This is a better solution than just removing the assertion, because if the inconsistent data were allowed to persist, it would lead to further problems in gameplay. For example, if you just remove that assertion instead of this fix and press 'h' on the example puzzle id above, you'll find that the non-reciprocal links are actually visible, in the form of several thick lines that stop at a grid square boundary instead of connecting two square-centres. (It looks even sillier if you set PEARL_GUI_LOOPY=y.) That's a situation that can't be created by a normal move, and if you try to make normal moves after it (e.g. click one of the weird edges), you'll find that both sides of the half-link get toggled, so now it's a half-link the other way round. So not only can't you _create_ this situation in normal play, you can't get rid of it either! That assertion in dsf_update_completion was commented out at one point, and I put it back in commit c5500926bf7458a saying that if it failed I'd like to know about it. And indeed, I'm glad I did, because this kind of unfixable wrongness in the resulting game_state was worth noticing and getting rid of!
* Forbid lines off the grid in PearlBen Harris2023-02-02
| | | | | | | | | | | | | | | | | | | | | | While they couldn't be generated in normal play, execute_move() would permit lines and marks across the edge of the grid that would then generate assertion failures ("dsf_update_completion: Assertion `INGRID(state, bx, by)' failed."). I've added a check to execute_move() that after updating a square, the square doesn't have any lines or marks that leave the grid. This save file demonstrated the problem: SAVEFILE:41:Simon Tatham's Portable Puzzle Collection VERSZON :1:1 GAME :5:Pearl PARAMS :5:5x6dt CPARAMS :5:5x6dt DESC :6:eeeeee NSTATES :1:2 STATEPOS:1:1 MOVE :6:F1,4,2
* 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.
* Last-ditch maximum size limit for PearlBen Harris2023-01-15
| | | | | This makes sure that width * height <= INT_MAX, which it rather needs to be.
* Pearl: make PEARL_GUI_LOOPY affect printed output.Simon Tatham2022-12-11
| | | | | | This display style is perfectly playable on paper (proof: it works for Loopy), so there's no reason not to support both modes in both output routines.
* 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.
* pearl: Return NULL when Backspace or Escape does nothingBen Harris2022-11-18
| | | | | | When there's no drag in progress, cancelling the drag has no effect. Returning NULL lets the front-end know this, which in particular means the Backspace key can leave the app in KaiOS.
* Pearl: Require width or height to be at least 6 for TrickyBen Hutchings2022-07-31
| | | | | | | | | Josh Triplett reported: > If I ask pearl to generate a 5x5 tricky puzzle, it runs forever. I find that 5x6 or 6x5 works, so set the minimum accordingly. References: https://bugs.debian.org/667963
* Pearl: reorder helper functions.Simon Tatham2022-01-27
| | | | | | interpret_ui_drag is now called from update_ui_drag, so it makes more sense to have the former appear first in the file, together with its comment explaining the expected usage.
* Pearl: permit drawing a whole loop in one drag.Simon Tatham2022-01-27
| | | | | | | | | | | | A user reported recently that they were trying this as an extra challenge (solve the whole puzzle mentally and then draw it in finished form in one UI action). But the backtracking behaviour of Pearl's dragging mode meant that the loop erased itself as soon as the drag came back to a revisited position. In this commit I fix that by making the exception that you can unconditionally return to the start point of the drag, _provided_ that in doing so you don't create a grid cell of degree > 2.
* Build a lot of conditioned-out test and helper programs.Simon Tatham2021-05-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Most of these aren't especially useful, but if we're going to have them in the code base at all, we should at least ensure they compile: bit-rotted conditioned-out code is of no value. One of the new programs is 'galaxieseditor', which borrows most of the Galaxies code but changes the UI so that you can create and remove _dots_ instead of edges, and then run the solver to see whether it can solve the puzzle you've designed. Unlike the rest, this is a GUI helper tool, using the 'guiprogram' cmake function introduced in the previous commit. The programs are: - 'combi', a test program for the utility module that generates all combinations of n things - 'divvy', a test program for the module that divides a rectangle at random into equally-sized polyominoes - 'penrose-test', a test program for the Penrose-tiling generator used in Loopy, which outputs an SVG of a piece of tiling - 'penrose-vector', a much smaller test program for the vector arithmetic subroutines in that code - 'sort-test', a test of this code base's local array sorting routine - 'tree234-test', the exhaustive test code that's been in tree234.c all along. Not all of them compiled first time. Most of the fixes were the usual kind of thing: fixing compiler warnings by removing unused variables/functions, bringing uses of internal APIs up to date. A notable one was that galaxieseditor's interpret_move() modified the input game state, which was an error all along and is now detected by me having made it a const pointer; I had to replace that with an extra wrinkle in the move-string format, so that now execute_move() makes the modification. The one I'm _least_ proud of is squelching a huge number of format-string warnings in tree234-test by interposing a variadic function without __attribute__((printf)).
* 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.
* 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.
* 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.
* 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.
* Fix missing error highlights (+ array underrun!) in Pearl.Simon Tatham2016-12-16
| | | | | | | | | | | | I was accidentally re-checking the value of component_state[comp] after resetting comp to the special value -1, which caused a failure to highlight stray path-shaped components if they existed in addition to a large loop component. (Plus, of course, it's just illegal no matter what visible behaviour it does or doesn't cause in practice.) Fixed by adjusting the code to more closely match the version in Loopy (I wonder how I managed to add two pieces of code in commit b31155b73 without noticing this difference between them).
* Account for disconnected paths in Loopy and Pearl error highlights.Simon Tatham2016-04-28
| | | | | | | | | | | | | | | | | | | | | | | | In commits 24848706e and adc54741f, I revamped the highlighting of erroneous connected components of those two puzzles' solution graphs in cases where a non-solution loop existed, so that the largest component was considered correct and the smaller ones lit up in red. I intended this to work in the cases where you have most of a correct solution as one component and a small spurious loop as another (in which case the latter lights up red), or conversely where your mostly correct component was joined into a loop leaving a few edges out (in which case the left-out edges again light up red). However, a user points out that I overlooked the case where your mostly correct solution is not all one component! If you've got lots of separate pieces of path, and one tiny loop that's definitely wrong, it's silly to light up all but the longest piece of path as if they're erroneous. Fixed by treating all the non-loop components as one unit for these purposes. So if there is at least one loop and it isn't the only thing on the board, then we _either_ light up all loops (if they're all smaller than the set of non-loop paths put together), _or_ light up everything but the largest loop (if that loop is the biggest thing on the board).
* Pearl: revise loop detection similarly to Loopy.Simon Tatham2016-02-24
| | | | | | | | | | | | | | | | | | Pearl has more or less the same attitude to loops as Loopy does, in that a loop is required in the solution but some loops during play want to be highlighted as errors. So it makes sense to use the same strategy for loop highlighting. I've cloned-and-hacked the code from Loopy rather than abstracting it out, because there were some fiddly differences in application (like checking of untouched clues in Pearl). Perhaps some day I can come back and make it all neater. Also, while I'm here, I've cleaned up the loop_length field in game_state, which was carefully set up by check_completion() but never actually used for anything. (If I remember rightly, it was going to be used for a fancy victory flash which never saw the light of day.)
* Pearl: reinstate a conditioned-out assertion.Simon Tatham2016-02-24
| | | | | | | | | I think this assertion must have been put under '#if 0' during early development, and accidentally never taken out once the game started actually working. Putting it back in doesn't cause the self-tests to fail, so I'm reinstating it - if it does fail, I'd like to know about it!