diff options
| author | Franklin Wei <git@fwei.tk> | 2018-04-24 18:06:44 -0400 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2018-04-24 19:06:30 -0400 |
| commit | 8f23493e08febc09c370b1d90d891953fa43ddf7 (patch) | |
| tree | 99fcf3e896c96c969bf2b424af8e327a3cf74787 /apps/plugins/puzzles/src/HACKING | |
| parent | ef0fb52113447c15f97eb8c707f56db4074eb578 (diff) | |
| download | rockbox-8f23493e08febc09c370b1d90d891953fa43ddf7.zip rockbox-8f23493e08febc09c370b1d90d891953fa43ddf7.tar.gz rockbox-8f23493e08febc09c370b1d90d891953fa43ddf7.tar.bz2 rockbox-8f23493e08febc09c370b1d90d891953fa43ddf7.tar.xz | |
puzzles: resync with upstream
This brings the upstream version to b3da238 (though some of my own
changes are included on top of that).
Change-Id: Ida73e8cd86765413147ce891af3cc2b7aeda2b2a
Diffstat (limited to 'apps/plugins/puzzles/src/HACKING')
| -rw-r--r-- | apps/plugins/puzzles/src/HACKING | 4661 |
1 files changed, 4661 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/HACKING b/apps/plugins/puzzles/src/HACKING new file mode 100644 index 0000000..e877280 --- /dev/null +++ b/apps/plugins/puzzles/src/HACKING @@ -0,0 +1,4661 @@ +#Developer documentation for Simon Tatham's puzzle collection + +This is a guide to the internal structure of Simon Tatham's Portable +Puzzle Collection (henceforth referred to simply as `Puzzles'), for +use by anyone attempting to implement a new puzzle or port to a new +platform. + +This guide is believed correct as of r6190. Hopefully it will be updated +along with the code in future, but if not, I've at least left this +version number in here so you can figure out what's changed by tracking +commit comments from there onwards. + +#1. Introduction + +The Puzzles code base is divided into four parts: a set of +interchangeable front ends, a set of interchangeable back ends, a +universal `middle end' which acts as a buffer between the two, and a +bunch of miscellaneous utility functions. In the following sections I +give some general discussion of each of these parts. + +#1.1. Front end + +The front end is the non-portable part of the code: it's the bit that +you replace completely when you port to a different platform. So it's +responsible for all system calls, all GUI interaction, and anything else +platform-specific. + +The current front ends in the main code base are for Windows, GTK and +MacOS X; I also know of a third-party front end for PalmOS. + +The front end contains main() or the local platform's equivalent. Top- +level control over the application's execution flow belongs to the front +end (it isn't, for example, a set of functions called by a universal +main() somewhere else). + +The front end has complete freedom to design the GUI for any given +port of Puzzles. There is no centralised mechanism for maintaining the +menu layout, for example. This has a cost in consistency (when I _do_ +want the same menu layout on more than one platform, I have to edit +two pieces of code in parallel every time I make a change), but the +advantage is that local GUI conventions can be conformed to and local +constraints adapted to. For example, MacOS X has strict human interface +guidelines which specify a different menu layout from the one I've used +on Windows and GTK; there's nothing stopping the OS X front end from +providing a menu layout consistent with those guidelines. + +Although the front end is mostly caller rather than the callee in its +interactions with other parts of the code, it is required to implement +a small API for other modules to call, mostly of drawing functions for +games to use when drawing their graphics. The drawing API is documented +in chapter 3; the other miscellaneous front end API functions are +documented in section 4.34. + +#1.2. Back end + +A `back end', in this collection, is synonymous with a `puzzle'. Each +back end implements a different game. + +At the top level, a back end is simply a data structure, containing a +few constants (flag words, preferred pixel size) and a large number of +function pointers. Back ends are almost invariably callee rather than +caller, which means there's a limitation on what a back end can do on +its own initiative. + +The persistent state in a back end is divided into a number of data +structures, which are used for different purposes and therefore likely +to be switched around, changed without notice, and otherwise updated by +the rest of the code. It is important when designing a back end to put +the right pieces of data into the right structures, or standard midend- +provided features (such as Undo) may fail to work. + +The functions and variables provided in the back end data structure are +documented in chapter 2. + +#1.3. Middle end + +Puzzles has a single and universal `middle end'. This code is common to +all platforms and all games; it sits in between the front end and the +back end and provides standard functionality everywhere. + +People adding new back ends or new front ends should generally not need +to edit the middle end. On rare occasions there might be a change that +can be made to the middle end to permit a new game to do something not +currently anticipated by the middle end's present design; however, this +is terribly easy to get wrong and should probably not be undertaken +without consulting the primary maintainer (me). Patch submissions +containing unannounced mid-end changes will be treated on their merits +like any other patch; this is just a friendly warning that mid-end +changes will need quite a lot of merits to make them acceptable. + +Functionality provided by the mid-end includes: + + - Maintaining a list of game state structures and moving back and + forth along that list to provide Undo and Redo. + + - Handling timers (for move animations, flashes on completion, and in + some cases actually timing the game). + + - Handling the container format of game IDs: receiving them, picking + them apart into parameters, description and/or random seed, and + so on. The game back end need only handle the individual parts + of a game ID (encoded parameters and encoded game description); + everything else is handled centrally by the mid-end. + + - Handling standard keystrokes and menu commands, such as `New Game', + `Restart Game' and `Quit'. + + - Pre-processing mouse events so that the game back ends can rely on + them arriving in a sensible order (no missing button-release events, + no sudden changes of which button is currently pressed, etc). + + - Handling the dialog boxes which ask the user for a game ID. + + - Handling serialisation of entire games (for loading and saving a + half-finished game to a disk file, or for handling application + shutdown and restart on platforms such as PalmOS where state is + expected to be saved). + +Thus, there's a lot of work done once by the mid-end so that individual +back ends don't have to worry about it. All the back end has to do is +cooperate in ensuring the mid-end can do its work properly. + +The API of functions provided by the mid-end to be called by the front +end is documented in chapter 4. + +#1.4. Miscellaneous utilities + +In addition to these three major structural components, the Puzzles code +also contains a variety of utility modules usable by all of the above +components. There is a set of functions to provide platform-independent +random number generation; functions to make memory allocation easier; +functions which implement a balanced tree structure to be used as +necessary in complex algorithms; and a few other miscellaneous +functions. All of these are documented in chapter 5. + +#1.5. Structure of this guide + +There are a number of function call interfaces within Puzzles, and this +guide will discuss each one in a chapter of its own. After that, chapter +6 discusses how to design new games, with some general design thoughts +and tips. + +#2. Interface to the back end + +This chapter gives a detailed discussion of the interface that each back +end must implement. + +At the top level, each back end source file exports a single global +symbol, which is a `const struct game' containing a large number of +function pointers and a small amount of constant data. This structure is +called by different names depending on what kind of platform the puzzle +set is being compiled on: + + - On platforms such as Windows and GTK, which build a separate binary + for each puzzle, the game structure in every back end has the same + name, `thegame'; the front end refers directly to this name, so that + compiling the same front end module against a different back end + module builds a different puzzle. + + - On platforms such as MacOS X and PalmOS, which build all the puzzles + into a single monolithic binary, the game structure in each back end + must have a different name, and there's a helper module `list.c' + (constructed automatically by the same Perl script that builds the + Makefiles) which contains a complete list of those game structures. + +On the latter type of platform, source files may assume that the +preprocessor symbol `COMBINED' has been defined. Thus, the usual code to +declare the game structure looks something like this: + + #ifdef COMBINED + #define thegame net /* or whatever this game is called */ + #endif + + const struct game thegame = { + /* lots of structure initialisation in here */ + }; + +Game back ends must also internally define a number of data structures, +for storing their various persistent state. This chapter will first +discuss the nature and use of those structures, and then go on to give +details of every element of the game structure. + +#2.1. Data structures + +Each game is required to define four separate data structures. This +section discusses each one and suggests what sorts of things need to be +put in it. + +#2.1.1. `game_params' + +The `game_params' structure contains anything which affects the +automatic generation of new puzzles. So if puzzle generation is +parametrised in any way, those parameters need to be stored in +`game_params'. + +Most puzzles currently in this collection are played on a grid of +squares, meaning that the most obvious parameter is the grid size. Many +puzzles have additional parameters; for example, Mines allows you to +control the number of mines in the grid independently of its size, Net +can be wrapping or non-wrapping, Solo has difficulty levels and symmetry +settings, and so on. + +A simple rule for deciding whether a data item needs to go in +`game_params' is: would the user expect to be able to control this data +item from either the preset-game-types menu or the `Custom' game type +configuration? If so, it's part of `game_params'. + +`game_params' structures are permitted to contain pointers to subsidiary +data if they need to. The back end is required to provide functions to +create and destroy `game_params', and those functions can allocate and +free additional memory if necessary. (It has not yet been necessary to +do this in any puzzle so far, but the capability is there just in case.) + +`game_params' is also the only structure which the game's compute_size() +function may refer to; this means that any aspect of the game which +affects the size of the window it needs to be drawn in must be stored in +`game_params'. In particular, this imposes the fundamental limitation +that random game generation may not have a random effect on the window +size: game generation algorithms are constrained to work by starting +from the grid size rather than generating it as an emergent phenomenon. +(Although this is a restriction in theory, it has not yet seemed to be a +problem.) + +#2.1.2. `game_state' + +While the user is actually playing a puzzle, the `game_state' structure +stores all the data corresponding to the current state of play. + +The mid-end keeps `game_state's in a list, and adds to the list every +time the player makes a move; the Undo and Redo functions step back and +forth through that list. + +Therefore, a good means of deciding whether a data item needs to go in +`game_state' is: would a player expect that data item to be restored on +undo? If so, put it in `game_state', and this will automatically happen +without you having to lift a finger. If not - for example, the deaths +counter in Mines is precisely something that does _not_ want to be reset +to its previous state on an undo - then you might have found a data item +that needs to go in `game_ui' instead. + +During play, `game_state's are often passed around without an +accompanying `game_params' structure. Therefore, any information in +`game_params' which is important during play (such as the grid size) +must be duplicated within the `game_state'. One simple method of doing +this is to have the `game_state' structure _contain_ a `game_params' +structure as one of its members, although this isn't obligatory if you +prefer to do it another way. + +#2.1.3. `game_drawstate' + +`game_drawstate' carries persistent state relating to the current +graphical contents of the puzzle window. The same `game_drawstate' +is passed to every call to the game redraw function, so that it can +remember what it has already drawn and what needs redrawing. + +A typical use for a `game_drawstate' is to have an array mirroring the +array of grid squares in the `game_state'; then every time the redraw +function was passed a `game_state', it would loop over all the squares, +and physically redraw any whose description in the `game_state' (i.e. +what the square needs to look like when the redraw is completed) did +not match its description in the `game_drawstate' (i.e. what the square +currently looks like). + +`game_drawstate' is occasionally completely torn down and reconstructed +by the mid-end, if the user somehow forces a full redraw. Therefore, no +data should be stored in `game_drawstate' which is _not_ related to the +state of the puzzle window, because it might be unexpectedly destroyed. + +The back end provides functions to create and destroy `game_drawstate', +which means it can contain pointers to subsidiary allocated data if it +needs to. A common thing to want to allocate in a `game_drawstate' is a +`blitter'; see section 3.1.13 for more on this subject. + +#2.1.4. `game_ui' + +`game_ui' contains whatever doesn't fit into the above three structures! + +A new `game_ui' is created when the user begins playing a new instance +of a puzzle (i.e. during `New Game' or after entering a game ID etc). It +persists until the user finishes playing that game and begins another +one (or closes the window); in particular, `Restart Game' does _not_ +destroy the `game_ui'. + +`game_ui' is useful for implementing user-interface state which is not +part of `game_state'. Common examples are keyboard control (you wouldn't +want to have to separately Undo through every cursor motion) and mouse +dragging. See section 6.3.2 and section 6.3.3, respectively, for more +details. + +Another use for `game_ui' is to store highly persistent data such as +the Mines death counter. This is conceptually rather different: where +the Net cursor position was _not important enough_ to preserve for the +player to restore by Undo, the Mines death counter is _too important_ to +permit the player to revert by Undo! + +A final use for `game_ui' is to pass information to the redraw function +about recent changes to the game state. This is used in Mines, for +example, to indicate whether a requested `flash' should be a white flash +for victory or a red flash for defeat; see section 6.3.5. + +#2.2. Simple data in the back end + +In this section I begin to discuss each individual element in the back +end structure. To begin with, here are some simple self-contained data +elements. + +#2.2.1. `name' + + const char *name; + +This is a simple ASCII string giving the name of the puzzle. This name +will be used in window titles, in game selection menus on monolithic +platforms, and anywhere else that the front end needs to know the name +of a game. + +#2.2.2. `winhelp_topic' + + const char *winhelp_topic; + +This member is used on Windows only, to provide online help. Although +the Windows front end provides a separate binary for each puzzle, it has +a single monolithic help file; so when a user selects `Help' from the +menu, the program needs to open the help file and jump to the chapter +describing that particular puzzle. + +Therefore, each chapter in `puzzles.but' is labelled with a _help topic_ +name, similar to this: + + \cfg{winhelp-topic}{games.net} + +And then the corresponding game back end encodes the topic string (here +`games.net') in the `winhelp_topic' element of the game structure. + +#2.3. Handling game parameter sets + +In this section I present the various functions which handle the +`game_params' structure. + +#2.3.1. default_params() + + game_params *(*default_params)(void); + +This function allocates a new `game_params' structure, fills it with the +default values, and returns a pointer to it. + +#2.3.2. fetch_preset() + + int (*fetch_preset)(int i, char **name, game_params **params); + +This function is one of the two APIs a back end can provide to populate +the `Type' menu, which provides a list of conveniently accessible preset +parameters for most games. + +The function is called with `i' equal to the index of the preset +required (numbering from zero). It returns FALSE if that preset does +not exist (if `i' is less than zero or greater than the largest preset +index). Otherwise, it sets `*params' to point at a newly allocated +`game_params' structure containing the preset information, sets `*name' +to point at a newly allocated C string containing the preset title (to +go on the `Type' menu), and returns TRUE. + +If the game does not wish to support any presets at all, this function +is permitted to return FALSE always. + +If the game wants to return presets in the form of a hierarchical menu +instead of a flat list (and, indeed, even if it doesn't), then it may +set this function pointer to NULL, and instead fill in the alternative +function pointer preset_menu (section 2.3.3). + +#2.3.3. preset_menu() + + struct preset_menu *(*preset_menu)(void); + +This function is the more flexible of the two APIs by which a back end +can define a collection of preset game parameters. + +This function simply returns a complete menu hierarchy, in the form +of a `struct preset_menu' (see section 4.15) and further submenus (if +it wishes) dangling off it. There are utility functions described in +section 5.2 to make it easy for the back end to construct this menu. + +If the game has no need to return a hierarchy of menus, it may instead +opt to implement the fetch_preset() function (see section 2.3.2). + +The game need not fill in the `id' fields in the preset menu structures. +The mid-end will do that after it receives the structure from the game, +and before passing it on to the front end. + +#2.3.4. encode_params() + + char *(*encode_params)(const game_params *params, int full); + +The job of this function is to take a `game_params', and encode it in +a string form for use in game IDs. The return value must be a newly +allocated C string, and _must_ not contain a colon or a hash (since +those characters are used to mark the end of the parameter section in a +game ID). + +Ideally, it should also not contain any other potentially controversial +punctuation; bear in mind when designing a string parameter format +that it will probably be used on both Windows and Unix command lines +under a variety of exciting shell quoting and metacharacter rules. +Sticking entirely to alphanumerics is the safest thing; if you really +need punctuation, you can probably get away with commas, periods or +underscores without causing anybody any major inconvenience. If you +venture far beyond that, you're likely to irritate _somebody_. + +(At the time of writing this, all existing games have purely +alphanumeric string parameter formats. Usually these involve a letter +denoting a parameter, followed optionally by a number giving the value +of that parameter, with a few mandatory parts at the beginning such as +numeric width and height separated by `x'.) + +If the `full' parameter is TRUE, this function should encode absolutely +everything in the `game_params', such that a subsequent call to +decode_params() (section 2.3.5) will yield an identical structure. +If `full' is FALSE, however, you should leave out anything which +is not necessary to describe a _specific puzzle instance_, i.e. +anything which only takes effect when a new puzzle is _generated_. +For example, the Solo `game_params' includes a difficulty rating used +when constructing new puzzles; but a Solo game ID need not explicitly +include the difficulty, since to describe a puzzle once generated it's +sufficient to give the grid dimensions and the location and contents +of the clue squares. (Indeed, one might very easily type in a puzzle +out of a newspaper without _knowing_ what its difficulty level is in +Solo's terminology.) Therefore, Solo's encode_params() only encodes the +difficulty level if `full' is set. + +#2.3.5. decode_params() + + void (*decode_params)(game_params *params, char const *string); + +This function is the inverse of encode_params() (section 2.3.4). It +parses the supplied string and fills in the supplied `game_params' +structure. Note that the structure will _already_ have been allocated: +this function is not expected to create a _new_ `game_params', but to +modify an existing one. + +This function can receive a string which only encodes a subset of the +parameters. The most obvious way in which this can happen is if the +string was constructed by encode_params() with its `full' parameter set +to FALSE; however, it could also happen if the user typed in a parameter +set manually and missed something out. Be prepared to deal with a wide +range of possibilities. + +When dealing with a parameter which is not specified in the input +string, what to do requires a judgment call on the part of the +programmer. Sometimes it makes sense to adjust other parameters to bring +them into line with the new ones. In Mines, for example, you would +probably not want to keep the same mine count if the user dropped the +grid size and didn't specify one, since you might easily end up with +more mines than would actually fit in the grid! On the other hand, +sometimes it makes sense to leave the parameter alone: a Solo player +might reasonably expect to be able to configure size and difficulty +independently of one another. + +This function currently has no direct means of returning an error if the +string cannot be parsed at all. However, the returned `game_params' is +almost always subsequently passed to validate_params() (section 2.3.11), +so if you really want to signal parse errors, you could always have a +`char *' in your parameters structure which stored an error message, and +have validate_params() return it if it is non-NULL. + +#2.3.6. free_params() + + void (*free_params)(game_params *params); + +This function frees a `game_params' structure, and any subsidiary +allocations contained within it. + +#2.3.7. dup_params() + + game_params *(*dup_params)(const game_params *params); + +This function allocates a new `game_params' structure and initialises it +with an exact copy of the information in the one provided as input. It +returns a pointer to the new duplicate. + +#2.3.8. `can_configure' + + int can_configure; + +This boolean data element is set to TRUE if the back end supports +custom parameter configuration via a dialog box. If it is TRUE, then +the functions configure() and custom_params() are expected to work. See +section 2.3.9 and section 2.3.10 for more details. + +#2.3.9. configure() + + config_item *(*configure)(const game_params *params); + +This function is called when the user requests a dialog box for +custom parameter configuration. It returns a newly allocated array of +config_item structures, describing the GUI elements required in the +dialog box. The array should have one more element than the number of +controls, since it is terminated with a C_END marker (see below). Each +array element describes the control together with its initial value; the +front end will modify the value fields and return the updated array to +custom_params() (see section 2.3.10). + +The config_item structure contains the following elements: + + char *name; + int type; + union { /* type-specific fields */ } u; + +`name' is an ASCII string giving the textual label for a GUI control. It +is _not_ expected to be dynamically allocated. + +`type' contains one of a small number of `enum' values defining what +type of control is being described. The usable member of the union field +`u' depends on `type'. The valid type values are: + +`C_STRING' + + Describes a text input box. (This is also used for numeric input. + The back end does not bother informing the front end that the box is + numeric rather than textual; some front ends do have the capacity + to take this into account, but I decided it wasn't worth the extra + complexity in the interface.) + + For controls of this type, `u.string' contains a single field + + char *sval; + + which stores a dynamically allocated string representing the + contents of the input box. + +`C_BOOLEAN' + + Describes a simple checkbox. + + For controls of this type, `u.boolean' contains a single field + + int bval; + + which is either TRUE or FALSE. + +`C_CHOICES' + + Describes a drop-down list presenting one of a small number of fixed + choices. + + For controls of this type, `u.choices' contains two fields: + + const char *choicenames; + int selected; + + `choicenames' contains a list of strings describing the choices. + The very first character of `sval' is used as a delimiter when + processing the rest (so that the strings `:zero:one:two', + `!zero!one!two' and `xzeroxonextwo' all define a three-element list + containing `zero', `one' and `two'). + + `selected' contains the index of the currently selected element, + numbering from zero (so that in the above example, 0 would mean + `zero' and 2 would mean `two'). + + Note that `u.choices.choicenames' is _not_ dynamically allocated, + unlike `u.string.sval'. + +`C_END' + + Marks the end of the array of `config_item's. There is no associated + member of the union field `u' for this type. + +The array returned from this function is expected to have filled in the +initial values of all the controls according to the input `game_params' +structure. + +If the game's `can_configure' flag is set to FALSE, this function is +never called and need not do anything at all. + +#2.3.10. custom_params() + + game_params *(*custom_params)(const config_item *cfg); + +This function is the counterpart to configure() (section 2.3.9). It +receives as input an array of `config_item's which was originally +created by configure(), but in which the control values have since been +changed in accordance with user input. Its function is to read the new +values out of the controls and return a newly allocated `game_params' +structure representing the user's chosen parameter set. + +(The front end will have modified the controls' _values_, but there will +still always be the same set of controls, in the same order, as provided +by configure(). It is not necessary to check the `name' and `type' +fields, although you could use assert() if you were feeling energetic.) + +This function is not expected to (and indeed _must not_) free the input +`config_item' array. (If the parameters fail to validate, the dialog box +will stay open.) + +If the game's `can_configure' flag is set to FALSE, this function is +never called and need not do anything at all. + +#2.3.11. validate_params() + + const char *(*validate_params)(const game_params *params, + int full); + +This function takes a `game_params' structure as input, and checks that +the parameters described in it fall within sensible limits. (At the very +least, grid dimensions should almost certainly be strictly positive, for +example.) + +Return value is NULL if no problems were found, or alternatively a (non- +dynamically-allocated) ASCII string describing the error in human- +readable form. + +If the `full' parameter is set, full validation should be performed: any +set of parameters which would not permit generation of a sensible puzzle +should be faulted. If `full' is _not_ set, the implication is that +these parameters are not going to be used for _generating_ a puzzle; so +parameters which can't even sensibly _describe_ a valid puzzle should +still be faulted, but parameters which only affect puzzle generation +should not be. + +(The `full' option makes a difference when parameter combinations are +non-orthogonal. For example, Net has a boolean option controlling +whether it enforces a unique solution; it turns out that it's impossible +to generate a uniquely soluble puzzle with wrapping walls and width +2, so validate_params() will complain if you ask for one. However, +if the user had just been playing a unique wrapping puzzle of a more +sensible width, and then pastes in a game ID acquired from somebody else +which happens to describe a _non_-unique wrapping width-2 puzzle, then +validate_params() will be passed a `game_params' containing the width +and wrapping settings from the new game ID and the uniqueness setting +from the old one. This would be faulted, if it weren't for the fact that +`full' is not set during this call, so Net ignores the inconsistency. +The resulting `game_params' is never subsequently used to generate a +puzzle; this is a promise made by the mid-end when it asks for a non- +full validation.) + +#2.4. Handling game descriptions + +In this section I present the functions that deal with a textual +description of a puzzle, i.e. the part that comes after the colon in a +descriptive-format game ID. + +#2.4.1. new_desc() + + char *(*new_desc)(const game_params *params, random_state *rs, + char **aux, int interactive); + +This function is where all the really hard work gets done. This is +the function whose job is to randomly generate a new puzzle, ensuring +solubility and uniqueness as appropriate. + +As input it is given a `game_params' structure and a random state +(see section 5.1 for the random number API). It must invent a puzzle +instance, encode it in string form, and return a dynamically allocated C +string containing that encoding. + +Additionally, it may return a second dynamically allocated string +in `*aux'. (If it doesn't want to, then it can leave that parameter +completely alone; it isn't required to set it to NULL, although doing +so is harmless.) That string, if present, will be passed to solve() +(section 2.7.4) later on; so if the puzzle is generated in such a way +that a solution is known, then information about that solution can be +saved in `*aux' for solve() to use. + +The `interactive' parameter should be ignored by almost all puzzles. +Its purpose is to distinguish between generating a puzzle within a GUI +context for immediate play, and generating a puzzle in a command-line +context for saving to be played later. The only puzzle that currently +uses this distinction (and, I fervently hope, the only one which will +_ever_ need to use it) is Mines, which chooses a random first-click +location when generating puzzles non-interactively, but which waits +for the user to place the first click when interactive. If you think +you have come up with another puzzle which needs to make use of this +parameter, please think for at least ten minutes about whether there is +_any_ alternative! + +Note that game description strings are not required to contain an +encoding of parameters such as grid size; a game description is +never separated from the `game_params' it was generated with, so any +information contained in that structure need not be encoded again in the +game description. + +#2.4.2. validate_desc() + + const char *(*validate_desc)(const game_params *params, + const char *desc); + +This function is given a game description, and its job is to validate +that it describes a puzzle which makes sense. + +To some extent it's up to the user exactly how far they take the phrase +`makes sense'; there are no particularly strict rules about how hard the +user is permitted to shoot themself in the foot when typing in a bogus +game description by hand. (For example, Rectangles will not verify that +the sum of all the numbers in the grid equals the grid's area. So a user +could enter a puzzle which was provably not soluble, and the program +wouldn't complain; there just wouldn't happen to be any sequence of +moves which solved it.) + +The one non-negotiable criterion is that any game description which +makes it through validate_desc() _must not_ subsequently cause a crash +or an assertion failure when fed to new_game() and thence to the rest of +the back end. + +The return value is NULL on success, or a non-dynamically-allocated C +string containing an error message. + +#2.4.3. new_game() + + game_state *(*new_game)(midend *me, const game_params *params, + const char *desc); + +This function takes a game description as input, together with its +accompanying `game_params', and constructs a `game_state' describing the +initial state of the puzzle. It returns a newly allocated `game_state' +structure. + +Almost all puzzles should ignore the `me' parameter. It is required by +Mines, which needs it for later passing to midend_supersede_game_desc() +(see section 2.11.2) once the user has placed the first click. I +fervently hope that no other puzzle will be awkward enough to require +it, so everybody else should ignore it. As with the `interactive' +parameter in new_desc() (section 2.4.1), if you think you have a reason +to need this parameter, please try very hard to think of an alternative +approach! + +#2.5. Handling game states + +This section describes the functions which create and destroy +`game_state' structures. + +(Well, except new_game(), which is in section 2.4.3 instead of under +here; but it deals with game descriptions _and_ game states and it had +to go in one section or the other.) + +#2.5.1. dup_game() + + game_state *(*dup_game)(const game_state *state); + +This function allocates a new `game_state' structure and initialises it +with an exact copy of the information in the one provided as input. It +returns a pointer to the new duplicate. + +#2.5.2. free_game() + + void (*free_game)(game_state *state); + +This function frees a `game_state' structure, and any subsidiary +allocations contained within it. + +#2.6. Handling `game_ui' + +#2.6.1. new_ui() + + game_ui *(*new_ui)(const game_state *state); + +This function allocates and returns a new `game_ui' structure for +playing a particular puzzle. It is passed a pointer to the initial +`game_state', in case it needs to refer to that when setting up the +initial values for the new game. + +#2.6.2. free_ui() + + void (*free_ui)(game_ui *ui); + +This function frees a `game_ui' structure, and any subsidiary +allocations contained within it. + +#2.6.3. encode_ui() + + char *(*encode_ui)(const game_ui *ui); + +This function encodes any _important_ data in a `game_ui' structure in +string form. It is only called when saving a half-finished game to a +file. + +It should be used sparingly. Almost all data in a `game_ui' is not +important enough to save. The location of the keyboard-controlled +cursor, for example, can be reset to a default position on reloading +the game without impacting the user experience. If the user should +somehow manage to save a game while a mouse drag was in progress, then +discarding that mouse drag would be an outright _feature_. + +A typical thing that _would_ be worth encoding in this function is the +Mines death counter: it's in the `game_ui' rather than the `game_state' +because it's too important to allow the user to revert it by using Undo, +and therefore it's also too important to allow the user to revert it by +saving and reloading. (Of course, the user could edit the save file by +hand... But if the user is _that_ determined to cheat, they could just +as easily modify the game's source.) + +#2.6.4. decode_ui() + + void (*decode_ui)(game_ui *ui, const char *encoding); + +This function parses a string previously output by encode_ui(), and +writes the decoded data back into the provided `game_ui' structure. + +#2.6.5. changed_state() + + void (*changed_state)(game_ui *ui, const game_state *oldstate, + const game_state *newstate); + +This function is called by the mid-end whenever the current game state +changes, for any reason. Those reasons include: + + - a fresh move being made by interpret_move() and execute_move() + + - a solve operation being performed by solve() and execute_move() + + - the user moving back and forth along the undo list by means of the + Undo and Redo operations + + - the user selecting Restart to go back to the initial game state. + +The job of changed_state() is to update the `game_ui' for consistency +with the new game state, if any update is necessary. For example, +Same Game stores data about the currently selected tile group in its +`game_ui', and this data is intrinsically related to the game state it +was derived from. So it's very likely to become invalid when the game +state changes; thus, Same Game's changed_state() function clears the +current selection whenever it is called. + +When anim_length() or flash_length() are called, you can be sure that +there has been a previous call to changed_state(). So changed_state() +can set up data in the `game_ui' which will be read by anim_length() and +flash_length(), and those functions will not have to worry about being +called without the data having been initialised. + +#2.7. Making moves + +This section describes the functions which actually make moves in +the game: that is, the functions which process user input and end up +producing new `game_state's. + +#2.7.1. interpret_move() + + char *(*interpret_move)(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button); + +This function receives user input and processes it. Its input parameters +are the current `game_state', the current `game_ui' and the current +`game_drawstate', plus details of the input event. `button' is either +an ASCII value or a special code (listed below) indicating an arrow or +function key or a mouse event; when `button' is a mouse event, `x' and +`y' contain the pixel coordinates of the mouse pointer relative to the +top left of the puzzle's drawing area. + +(The pointer to the `game_drawstate' is marked `const', because +`interpret_move' should not write to it. The normal use of that pointer +will be to read the game's tile size parameter in order to divide mouse +coordinates by it.) + +interpret_move() may return in three different ways: + + - Returning NULL indicates that no action whatsoever occurred in + response to the input event; the puzzle was not interested in it at + all. + + - Returning the special value UI_UPDATE indicates that the input event + has resulted in a change being made to the `game_ui' which will + require a redraw of the game window, but that no actual _move_ was + made (i.e. no new `game_state' needs to be created). + + - Returning anything else indicates that a move was made and that + a new `game_state' must be created. However, instead of actually + constructing a new `game_state' itself, this function is required to + return a string description of the details of the move. This string + will be passed to execute_move() (section 2.7.2) to actually create + the new `game_state'. (Encoding moves as strings in this way means + that the mid-end can keep the strings as well as the game states, + and the strings can be written to disk when saving the game and fed + to execute_move() again on reloading.) + +The return value from interpret_move() is expected to be dynamically +allocated if and only if it is not either NULL _or_ the special string +constant `UI_UPDATE'. + +After this function is called, the back end is permitted to rely on some +subsequent operations happening in sequence: + + - execute_move() will be called to convert this move description into + a new `game_state' + + - changed_state() will be called with the new `game_state'. + +This means that if interpret_move() needs to do updates to the `game_ui' +which are easier to perform by referring to the new `game_state', it can +safely leave them to be done in changed_state() and not worry about them +failing to happen. + +(Note, however, that execute_move() may _also_ be called in other +circumstances. It is only interpret_move() which can rely on a +subsequent call to changed_state().) + +The special key codes supported by this function are: + +LEFT_BUTTON, MIDDLE_BUTTON, RIGHT_BUTTON + + Indicate that one of the mouse buttons was pressed down. + +LEFT_DRAG, MIDDLE_DRAG, RIGHT_DRAG + + Indicate that the mouse was moved while one of the mouse buttons was + still down. The mid-end guarantees that when one of these events is + received, it will always have been preceded by a button-down event + (and possibly other drag events) for the same mouse button, and no + event involving another mouse button will have appeared in between. + +LEFT_RELEASE, MIDDLE_RELEASE, RIGHT_RELEASE + + Indicate that a mouse button was released. The mid-end guarantees + that when one of these events is received, it will always have been + preceded by a button-down event (and possibly some drag events) for + the same mouse button, and no event involving another mouse button + will have appeared in between. + +CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT + + Indicate that an arrow key was pressed. + +CURSOR_SELECT + + On platforms which have a prominent `select' button alongside their + cursor keys, indicates that that button was pressed. + +In addition, there are some modifiers which can be bitwise-ORed into the +`button' parameter: + +MOD_CTRL, MOD_SHFT + + These indicate that the Control or Shift key was pressed alongside + the key. They only apply to the cursor keys, not to mouse buttons or + anything else. + +MOD_NUM_KEYPAD + + This applies to some ASCII values, and indicates that the key code + was input via the numeric keypad rather than the main keyboard. Some + puzzles may wish to treat this differently (for example, a puzzle + might want to use the numeric keypad as an eight-way directional + pad), whereas others might not (a game involving numeric input + probably just wants to treat the numeric keypad as numbers). + +MOD_MASK + + This mask is the bitwise OR of all the available modifiers; you can + bitwise-AND with ~MOD_MASK to strip all the modifiers off any input + value. + +#2.7.2. execute_move() + + game_state *(*execute_move)(const game_state *state, char *move); + +This function takes an input `game_state' and a move string as output +from interpret_move(). It returns a newly allocated `game_state' which +contains the result of applying the specified move to the input game +state. + +This function may return NULL if it cannot parse the move string (and +this is definitely preferable to crashing or failing an assertion, since +one way this can happen is if loading a corrupt save file). However, it +must not return NULL for any move string that really was output from +interpret_move(): this is punishable by assertion failure in the mid- +end. + +#2.7.3. `can_solve' + + int can_solve; + +This boolean field is set to TRUE if the game's solve() function does +something. If it's set to FALSE, the game will not even offer the +`Solve' menu option. + +#2.7.4. solve() + + char *(*solve)(const game_state *orig, const game_state *curr, + const char *aux, const char **error); + +This function is called when the user selects the `Solve' option from +the menu. + +It is passed two input game states: `orig' is the game state from the +very start of the puzzle, and `curr' is the current one. (Different +games find one or other or both of these convenient.) It is also passed +the `aux' string saved by new_desc() (section 2.4.1), in case that +encodes important information needed to provide the solution. + +If this function is unable to produce a solution (perhaps, for example, +the game has no in-built solver so it can only solve puzzles it invented +internally and has an `aux' string for) then it may return NULL. If it +does this, it must also set `*error' to an error message to be presented +to the user (such as `Solution not known for this puzzle'); that error +message is not expected to be dynamically allocated. + +If this function _does_ produce a solution, it returns a move string +suitable for feeding to execute_move() (section 2.7.2). Like a (non- +empty) string returned from interpret_move(), the returned string should +be dynamically allocated. + +#2.8. Drawing the game graphics + +This section discusses the back end functions that deal with drawing. + +#2.8.1. new_drawstate() + + game_drawstate *(*new_drawstate)(drawing *dr, + const game_state *state); + +This function allocates and returns a new `game_drawstate' structure for +drawing a particular puzzle. It is passed a pointer to a `game_state', +in case it needs to refer to that when setting up any initial data. + +This function may not rely on the puzzle having been newly started; a +new draw state can be constructed at any time if the front end requests +a forced redraw. For games like Pattern, in which initial game states +are much simpler than general ones, this might be important to keep in +mind. + +The parameter `dr' is a drawing object (see chapter 3) which the +function might need to use to allocate blitters. (However, this isn't +recommended; it's usually more sensible to wait to allocate a blitter +until set_size() is called, because that way you can tailor it to the +scale at which the puzzle is being drawn.) + +#2.8.2. free_drawstate() + + void (*free_drawstate)(drawing *dr, game_drawstate *ds); + +This function frees a `game_drawstate' structure, and any subsidiary +allocations contained within it. + +The parameter `dr' is a drawing object (see chapter 3), which might be +required if you are freeing a blitter. + +#2.8.3. `preferred_tilesize' + + int preferred_tilesize; + +Each game is required to define a single integer parameter which +expresses, in some sense, the scale at which it is drawn. This is +described in the APIs as `tilesize', since most puzzles are on a +square (or possibly triangular or hexagonal) grid and hence a sensible +interpretation of this parameter is to define it as the size of one grid +tile in pixels; however, there's no actual requirement that the `tile +size' be proportional to the game window size. Window size is required +to increase monotonically with `tile size', however. + +The data element `preferred_tilesize' indicates the tile size which +should be used in the absence of a good reason to do otherwise (such as +the screen being too small, or the user explicitly requesting a resize +if that ever gets implemented). + +#2.8.4. compute_size() + + void (*compute_size)(const game_params *params, int tilesize, + int *x, int *y); + +This function is passed a `game_params' structure and a tile size. It +returns, in `*x' and `*y', the size in pixels of the drawing area that +would be required to render a puzzle with those parameters at that tile +size. + +#2.8.5. set_size() + + void (*set_size)(drawing *dr, game_drawstate *ds, + const game_params *params, int tilesize); + +This function is responsible for setting up a `game_drawstate' to draw +at a given tile size. Typically this will simply involve copying the +supplied `tilesize' parameter into a `tilesize' field inside the draw +state; for some more complex games it might also involve setting up +other dimension fields, or possibly allocating a blitter (see section +3.1.13). + +The parameter `dr' is a drawing object (see chapter 3), which is +required if a blitter needs to be allocated. + +Back ends may assume (and may enforce by assertion) that this function +will be called at most once for any `game_drawstate'. If a puzzle needs +to be redrawn at a different size, the mid-end will create a fresh +drawstate. + +#2.8.6. colours() + + float *(*colours)(frontend *fe, int *ncolours); + +This function is responsible for telling the front end what colours the +puzzle will need to draw itself. + +It returns the number of colours required in `*ncolours', and the return +value from the function itself is a dynamically allocated array of three +times that many `float's, containing the red, green and blue components +of each colour respectively as numbers in the range [0,1]. + +The second parameter passed to this function is a front end handle. +The only things it is permitted to do with this handle are to call the +front-end function called frontend_default_colour() (see section 4.39) +or the utility function called game_mkhighlight() (see section 5.5.7). +(The latter is a wrapper on the former, so front end implementors only +need to provide frontend_default_colour().) This allows colours() to +take local configuration into account when deciding on its own colour +allocations. Most games use the front end's default colour as their +background, apart from a few which depend on drawing relief highlights +so they adjust the background colour if it's too light for highlights to +show up against it. + +Note that the colours returned from this function are for _drawing_, +not for printing. Printing has an entirely different colour allocation +policy. + +#2.8.7. anim_length() + + float (*anim_length)(const game_state *oldstate, + const game_state *newstate, + int dir, game_ui *ui); + +This function is called when a move is made, undone or redone. It is +given the old and the new `game_state', and its job is to decide whether +the transition between the two needs to be animated or can be instant. + +`oldstate' is the state that was current until this call; `newstate' +is the state that will be current after it. `dir' specifies the +chronological order of those states: if it is positive, then the +transition is the result of a move or a redo (and so `newstate' is the +later of the two moves), whereas if it is negative then the transition +is the result of an undo (so that `newstate' is the _earlier_ move). + +If this function decides the transition should be animated, it returns +the desired length of the animation in seconds. If not, it returns zero. + +State changes as a result of a Restart operation are never animated; the +mid-end will handle them internally and never consult this function at +all. State changes as a result of Solve operations are also not animated +by default, although you can change this for a particular game by +setting a flag in `flags' (section 2.10.7). + +The function is also passed a pointer to the local `game_ui'. It may +refer to information in here to help with its decision (see section +6.3.7 for an example of this), and/or it may _write_ information about +the nature of the animation which will be read later by redraw(). + +When this function is called, it may rely on changed_state() having been +called previously, so if anim_length() needs to refer to information in +the `game_ui', then changed_state() is a reliable place to have set that +information up. + +Move animations do not inhibit further input events. If the user +continues playing before a move animation is complete, the animation +will be abandoned and the display will jump straight to the final state. + +#2.8.8. flash_length() + + float (*flash_length)(const game_state *oldstate, + const game_state *newstate, + int dir, game_ui *ui); + +This function is called when a move is completed. (`Completed' +means that not only has the move been made, but any animation which +accompanied it has finished.) It decides whether the transition from +`oldstate' to `newstate' merits a `flash'. + +A flash is much like a move animation, but it is _not_ interrupted by +further user interface activity; it runs to completion in parallel with +whatever else might be going on on the display. The only thing which +will rush a flash to completion is another flash. + +The purpose of flashes is to indicate that the game has been completed. +They were introduced as a separate concept from move animations because +of Net: the habit of most Net players (and certainly me) is to rotate a +tile into place and immediately lock it, then move on to another tile. +When you make your last move, at the instant the final tile is rotated +into place the screen starts to flash to indicate victory - but if you +then press the lock button out of habit, then the move animation is +cancelled, and the victory flash does not complete. (And if you _don't_ +press the lock button, the completed grid will look untidy because there +will be one unlocked square.) Therefore, I introduced a specific concept +of a `flash' which is separate from a move animation and can proceed in +parallel with move animations and any other display activity, so that +the victory flash in Net is not cancelled by that final locking move. + +The input parameters to flash_length() are exactly the same as the ones +to anim_length(). + +Just like anim_length(), when this function is called, it may rely on +changed_state() having been called previously, so if it needs to refer +to information in the `game_ui' then changed_state() is a reliable place +to have set that information up. + +(Some games use flashes to indicate defeat as well as victory; Mines, +for example, flashes in a different colour when you tread on a mine from +the colour it uses when you complete the game. In order to achieve this, +its flash_length() function has to store a flag in the `game_ui' to +indicate which flash type is required.) + +#2.8.9. status() + + int (*status)(const game_state *state); + +This function returns a status value indicating whether the current game +is still in play, or has been won, or has been conclusively lost. The +mid-end uses this to implement midend_status() (section 4.26). + +The return value should be +1 if the game has been successfully solved. +If the game has been lost in a situation where further play is unlikely, +the return value should be -1. If neither is true (so play is still +ongoing), return zero. + +Front ends may wish to use a non-zero status as a cue to proactively +offer the option of starting a new game. Therefore, back ends should +not return -1 if the game has been _technically_ lost but undoing and +continuing is still a realistic possibility. + +(For instance, games with hidden information such as Guess or Mines +might well return a non-zero status whenever they reveal the solution, +whether or not the player guessed it correctly, on the grounds that a +player would be unlikely to hide the solution and continue playing after +the answer was spoiled. On the other hand, games where you can merely +get into a dead end such as Same Game or Inertia might choose to return +0 in that situation, on the grounds that the player would quite likely +press Undo and carry on playing.) + +#2.8.10. redraw() + + void (*redraw)(drawing *dr, game_drawstate *ds, + const game_state *oldstate, + const game_state *newstate, + int dir, const game_ui *ui, + float anim_time, float flash_time); + +This function is responsible for actually drawing the contents of +the game window, and for redrawing every time the game state or the +`game_ui' changes. + +The parameter `dr' is a drawing object which may be passed to the +drawing API functions (see chapter 3 for documentation of the drawing +API). This function may not save `dr' and use it elsewhere; it must only +use it for calling back to the drawing API functions within its own +lifetime. + +`ds' is the local `game_drawstate', of course, and `ui' is the local +`game_ui'. + +`newstate' is the semantically-current game state, and is always non- +NULL. If `oldstate' is also non-NULL, it means that a move has recently +been made and the game is still in the process of displaying an +animation linking the old and new states; in this situation, `anim_time' +will give the length of time (in seconds) that the animation has already +been running. If `oldstate' is NULL, then `anim_time' is unused (and +will hopefully be set to zero to avoid confusion). + +`flash_time', if it is is non-zero, denotes that the game is in the +middle of a flash, and gives the time since the start of the flash. See +section 2.8.8 for general discussion of flashes. + +The very first time this function is called for a new `game_drawstate', +it is expected to redraw the _entire_ drawing area. Since this often +involves drawing visual furniture which is never subsequently altered, +it is often simplest to arrange this by having a special `first time' +flag in the draw state, and resetting it after the first redraw. + +When this function (or any subfunction) calls the drawing API, it is +expected to pass colour indices which were previously defined by the +colours() function. + +#2.9. Printing functions + +This section discusses the back end functions that deal with printing +puzzles out on paper. + +#2.9.1. `can_print' + + int can_print; + +This flag is set to TRUE if the puzzle is capable of printing itself +on paper. (This makes sense for some puzzles, such as Solo, which can +be filled in with a pencil. Other puzzles, such as Twiddle, inherently +involve moving things around and so would not make sense to print.) + +If this flag is FALSE, then the functions print_size() and print() will +never be called. + +#2.9.2. `can_print_in_colour' + + int can_print_in_colour; + +This flag is set to TRUE if the puzzle is capable of printing itself +differently when colour is available. For example, Map can actually +print coloured regions in different _colours_ rather than resorting to +cross-hatching. + +If the `can_print' flag is FALSE, then this flag will be ignored. + +#2.9.3. print_size() + + void (*print_size)(const game_params *params, float *x, float *y); + +This function is passed a `game_params' structure and a tile size. It +returns, in `*x' and `*y', the preferred size in _millimetres_ of that +puzzle if it were to be printed out on paper. + +If the `can_print' flag is FALSE, this function will never be called. + +#2.9.4. print() + + void (*print)(drawing *dr, const game_state *state, int tilesize); + +This function is called when a puzzle is to be printed out on paper. It +should use the drawing API functions (see chapter 3) to print itself. + +This function is separate from redraw() because it is often very +different: + + - The printing function may not depend on pixel accuracy, since + printer resolution is variable. Draw as if your canvas had infinite + resolution. + + - The printing function sometimes needs to display things in a + completely different style. Net, for example, is very different as + an on-screen puzzle and as a printed one. + + - The printing function is often much simpler since it has no need to + deal with repeated partial redraws. + +However, there's no reason the printing and redraw functions can't share +some code if they want to. + +When this function (or any subfunction) calls the drawing API, the +colour indices it passes should be colours which have been allocated by +the print_*_colour() functions within this execution of print(). This is +very different from the fixed small number of colours used in redraw(), +because printers do not have a limitation on the total number of colours +that may be used. Some puzzles' printing functions might wish to +allocate only one `ink' colour and use it for all drawing; others might +wish to allocate _more_ colours than are used on screen. + +One possible colour policy worth mentioning specifically is that a +puzzle's printing function might want to allocate the _same_ colour +indices as are used by the redraw function, so that code shared between +drawing and printing does not have to keep switching its colour indices. +In order to do this, the simplest thing is to make use of the fact that +colour indices returned from print_*_colour() are guaranteed to be in +increasing order from zero. So if you have declared an `enum' defining +three colours COL_BACKGROUND, COL_THIS and COL_THAT, you might then +write + + int c; + c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); + c = print_mono_colour(dr, 0); assert(c == COL_THIS); + c = print_mono_colour(dr, 0); assert(c == COL_THAT); + +If the `can_print' flag is FALSE, this function will never be called. + +#2.10. Miscellaneous + +#2.10.1. `can_format_as_text_ever' + + int can_format_as_text_ever; + +This boolean field is TRUE if the game supports formatting a game state +as ASCII text (typically ASCII art) for copying to the clipboard and +pasting into other applications. If it is FALSE, front ends will not +offer the `Copy' command at all. + +If this field is TRUE, the game does not necessarily have to support +text formatting for _all_ games: e.g. a game which can be played on +a square grid or a triangular one might only support copy and paste +for the former, because triangular grids in ASCII art are just too +difficult. + +If this field is FALSE, the functions can_format_as_text_now() (section +2.10.2) and text_format() (section 2.10.3) are never called. + +#2.10.2. `can_format_as_text_now()' + + int (*can_format_as_text_now)(const game_params *params); + +This function is passed a `game_params' and returns a boolean, which is +TRUE if the game can support ASCII text output for this particular game +type. If it returns FALSE, front ends will grey out or otherwise disable +the `Copy' command. + +Games may enable and disable the copy-and-paste function for different +game _parameters_, but are currently constrained to return the same +answer from this function for all game _states_ sharing the same +parameters. In other words, the `Copy' function may enable or disable +itself when the player changes game preset, but will never change during +play of a single game or when another game of exactly the same type is +generated. + +This function should not take into account aspects of the game +parameters which are not encoded by encode_params() (section 2.3.4) +when the `full' parameter is set to FALSE. Such parameters will not +necessarily match up between a call to this function and a subsequent +call to text_format() itself. (For instance, game _difficulty_ should +not affect whether the game can be copied to the clipboard. Only the +actual visible _shape_ of the game can affect that.) + +#2.10.3. text_format() + + char *(*text_format)(const game_state *state); + +This function is passed a `game_state', and returns a newly allocated C +string containing an ASCII representation of that game state. It is used +to implement the `Copy' operation in many front ends. + +This function will only ever be called if the back end field +`can_format_as_text_ever' (section 2.10.1) is TRUE _and_ the function +can_format_as_text_now() (section 2.10.2) has returned TRUE for the +currently selected game parameters. + +The returned string may contain line endings (and will probably want +to), using the normal C internal `\n' convention. For consistency +between puzzles, all multi-line textual puzzle representations should +_end_ with a newline as well as containing them internally. (There are +currently no puzzles which have a one-line ASCII representation, so +there's no precedent yet for whether that should come with a newline or +not.) + +#2.10.4. wants_statusbar + + int wants_statusbar; + +This boolean field is set to TRUE if the puzzle has a use for a textual +status line (to display score, completion status, currently active +tiles, etc). + +#2.10.5. `is_timed' + + int is_timed; + +This boolean field is TRUE if the puzzle is time-critical. If so, the +mid-end will maintain a game timer while the user plays. + +If this field is FALSE, then timing_state() will never be called and +need not do anything. + +#2.10.6. timing_state() + + int (*timing_state)(const game_state *state, game_ui *ui); + +This function is passed the current `game_state' and the local +`game_ui'; it returns TRUE if the game timer should currently be +running. + +A typical use for the `game_ui' in this function is to note when the +game was first completed (by setting a flag in changed_state() - see +section 2.6.5), and freeze the timer thereafter so that the user can +undo back through their solution process without altering their time. + +#2.10.7. `flags' + + int flags; + +This field contains miscellaneous per-backend flags. It consists of the +bitwise OR of some combination of the following: + +BUTTON_BEATS(x,y) + + Given any x and y from the set {LEFT_BUTTON, MIDDLE_BUTTON, + RIGHT_BUTTON}, this macro evaluates to a bit flag which indicates + that when buttons x and y are both pressed simultaneously, the mid- + end should consider x to have priority. (In the absence of any such + flags, the mid-end will always consider the most recently pressed + button to have priority.) + +SOLVE_ANIMATES + + This flag indicates that moves generated by solve() (section 2.7.4) + are candidates for animation just like any other move. For most + games, solve moves should not be animated, so the mid-end doesn't + even bother calling anim_length() (section 2.8.7), thus saving some + special-case code in each game. On the rare occasion that animated + solve moves are actually required, you can set this flag. + +REQUIRE_RBUTTON + + This flag indicates that the puzzle cannot be usefully played + without the use of mouse buttons other than the left one. On some + PDA platforms, this flag is used by the front end to enable right- + button emulation through an appropriate gesture. Note that a puzzle + is not required to set this just because it _uses_ the right button, + but only if its use of the right button is critical to playing the + game. (Slant, for example, uses the right button to cycle through + the three square states in the opposite order from the left button, + and hence can manage fine without it.) + +REQUIRE_NUMPAD + + This flag indicates that the puzzle cannot be usefully played + without the use of number-key input. On some PDA platforms it + causes an emulated number pad to appear on the screen. Similarly to + REQUIRE_RBUTTON, a puzzle need not specify this simply if its use of + the number keys is not critical. + +#2.11. Things a back end may do on its own initiative + +This section describes a couple of things that a back end may choose +to do by calling functions elsewhere in the program, which would not +otherwise be obvious. + +#2.11.1. Create a random state + +If a back end needs random numbers at some point during normal play, it +can create a fresh `random_state' by first calling `get_random_seed' +(section 4.35) and then passing the returned seed data to random_new(). + +This is likely not to be what you want. If a puzzle needs randomness in +the middle of play, it's likely to be more sensible to store some sort +of random state within the `game_state', so that the random numbers are +tied to the particular game state and hence the player can't simply keep +undoing their move until they get numbers they like better. + +This facility is currently used only in Net, to implement the `jumble' +command, which sets every unlocked tile to a new random orientation. +This randomness _is_ a reasonable use of the feature, because it's non- +adversarial - there's no advantage to the user in getting different +random numbers. + +#2.11.2. Supersede its own game description + +In response to a move, a back end is (reluctantly) permitted to call +midend_supersede_game_desc(): + + void midend_supersede_game_desc(midend *me, + char *desc, char *privdesc); + +When the user selects `New Game', the mid-end calls new_desc() +(section 2.4.1) to get a new game description, and (as well as using +that to generate an initial game state) stores it for the save file +and for telling to the user. The function above overwrites that +game description, and also splits it in two. `desc' becomes the new +game description which is provided to the user on request, and is +also the one used to construct a new initial game state if the user +selects `Restart'. `privdesc' is a `private' game description, used to +reconstruct the game's initial state when reloading. + +The distinction between the two, as well as the need for this function +at all, comes from Mines. Mines begins with a blank grid and no +idea of where the mines actually are; new_desc() does almost no +work in interactive mode, and simply returns a string encoding the +`random_state'. When the user first clicks to open a tile, _then_ Mines +generates the mine positions, in such a way that the game is soluble +from that starting point. Then it uses this function to supersede the +random-state game description with a proper one. But it needs two: one +containing the initial click location (because that's what you want to +happen if you restart the game, and also what you want to send to a +friend so that they play _the same game_ as you), and one without the +initial click location (because when you save and reload the game, you +expect to see the same blank initial state as you had before saving). + +I should stress again that this function is a horrid hack. Nobody should +use it if they're not Mines; if you think you need to use it, think +again repeatedly in the hope of finding a better way to do whatever it +was you needed to do. + +#3. The drawing API + +The back end function redraw() (section 2.8.10) is required to draw +the puzzle's graphics on the window's drawing area, or on paper if the +puzzle is printable. To do this portably, it is provided with a drawing +API allowing it to talk directly to the front end. In this chapter I +document that API, both for the benefit of back end authors trying to +use it and for front end authors trying to implement it. + +The drawing API as seen by the back end is a collection of global +functions, each of which takes a pointer to a `drawing' structure (a +`drawing object'). These objects are supplied as parameters to the back +end's redraw() and print() functions. + +In fact these global functions are not implemented directly by the front +end; instead, they are implemented centrally in `drawing.c' and form a +small piece of middleware. The drawing API as supplied by the front end +is a structure containing a set of function pointers, plus a `void *' +handle which is passed to each of those functions. This enables a single +front end to switch between multiple implementations of the drawing API +if necessary. For example, the Windows API supplies a printing mechanism +integrated into the same GDI which deals with drawing in windows, and +therefore the same API implementation can handle both drawing and +printing; but on Unix, the most common way for applications to print +is by producing PostScript output directly, and although it would be +_possible_ to write a single (say) draw_rect() function which checked +a global flag to decide whether to do GTK drawing operations or output +PostScript to a file, it's much nicer to have two separate functions and +switch between them as appropriate. + +When drawing, the puzzle window is indexed by pixel coordinates, with +the top left pixel defined as (0,0) and the bottom right pixel (w-1,h- +1), where `w' and `h' are the width and height values returned by the +back end function compute_size() (section 2.8.4). + +When printing, the puzzle's print area is indexed in exactly the same +way (with an arbitrary tile size provided by the printing module +`printing.c'), to facilitate sharing of code between the drawing and +printing routines. However, when printing, puzzles may no longer assume +that the coordinate unit has any relationship to a pixel; the printer's +actual resolution might very well not even be known at print time, so +the coordinate unit might be smaller or larger than a pixel. Puzzles' +print functions should restrict themselves to drawing geometric shapes +rather than fiddly pixel manipulation. + +_Puzzles' redraw functions may assume that the surface they draw on is +persistent_. It is the responsibility of every front end to preserve +the puzzle's window contents in the face of GUI window expose issues +and similar. It is not permissible to request that the back end redraw +any part of a window that it has already drawn, unless something has +actually changed as a result of making moves in the puzzle. + +Most front ends accomplish this by having the drawing routines draw on a +stored bitmap rather than directly on the window, and copying the bitmap +to the window every time a part of the window needs to be redrawn. +Therefore, it is vitally important that whenever the back end does any +drawing it informs the front end of which parts of the window it has +accessed, and hence which parts need repainting. This is done by calling +draw_update() (section 3.1.11). + +Persistence of old drawing is convenient. However, a puzzle should be +very careful about how it updates its drawing area. The problem is that +some front ends do anti-aliased drawing: rather than simply choosing +between leaving each pixel untouched or painting it a specified colour, +an antialiased drawing function will _blend_ the original and new +colours in pixels at a figure's boundary according to the proportion of +the pixel occupied by the figure (probably modified by some heuristic +fudge factors). All of this produces a smoother appearance for curves +and diagonal lines. + +An unfortunate effect of drawing an anti-aliased figure repeatedly +is that the pixels around the figure's boundary come steadily more +saturated with `ink' and the boundary appears to `spread out'. Worse, +redrawing a figure in a different colour won't fully paint over the old +boundary pixels, so the end result is a rather ugly smudge. + +A good strategy to avoid unpleasant anti-aliasing artifacts is to +identify a number of rectangular areas which need to be redrawn, clear +them to the background colour, and then redraw their contents from +scratch, being careful all the while not to stray beyond the boundaries +of the original rectangles. The clip() function (section 3.1.9) comes in +very handy here. Games based on a square grid can often do this fairly +easily. Other games may need to be somewhat more careful. For example, +Loopy's redraw function first identifies portions of the display which +need to be updated. Then, if the changes are fairly well localised, it +clears and redraws a rectangle containing each changed area. Otherwise, +it gives up and redraws the entire grid from scratch. + +It is possible to avoid clearing to background and redrawing from +scratch if one is very careful about which drawing functions one +uses: if a function is documented as not anti-aliasing under some +circumstances, you can rely on each pixel in a drawing either being left +entirely alone or being set to the requested colour, with no blending +being performed. + +In the following sections I first discuss the drawing API as seen by the +back end, and then the _almost_ identical function-pointer form seen by +the front end. + +#3.1. Drawing API as seen by the back end + +This section documents the back-end drawing API, in the form of +functions which take a `drawing' object as an argument. + +#3.1.1. draw_rect() + + void draw_rect(drawing *dr, int x, int y, int w, int h, + int colour); + +Draws a filled rectangle in the puzzle window. + +`x' and `y' give the coordinates of the top left pixel of the rectangle. +`w' and `h' give its width and height. Thus, the horizontal extent of +the rectangle runs from `x' to `x+w-1' inclusive, and the vertical +extent from `y' to `y+h-1' inclusive. + +`colour' is an integer index into the colours array returned by the back +end function colours() (section 2.8.6). + +There is no separate pixel-plotting function. If you want to plot a +single pixel, the approved method is to use draw_rect() with width and +height set to 1. + +Unlike many of the other drawing functions, this function is guaranteed +to be pixel-perfect: the rectangle will be sharply defined and not anti- +aliased or anything like that. + +This function may be used for both drawing and printing. + +#3.1.2. draw_rect_outline() + + void draw_rect_outline(drawing *dr, int x, int y, int w, int h, + int colour); + +Draws an outline rectangle in the puzzle window. + +`x' and `y' give the coordinates of the top left pixel of the rectangle. +`w' and `h' give its width and height. Thus, the horizontal extent of +the rectangle runs from `x' to `x+w-1' inclusive, and the vertical +extent from `y' to `y+h-1' inclusive. + +`colour' is an integer index into the colours array returned by the back +end function colours() (section 2.8.6). + +From a back end perspective, this function may be considered to be part +of the drawing API. However, front ends are not required to implement +it, since it is actually implemented centrally (in misc.c) as a wrapper +on draw_polygon(). + +This function may be used for both drawing and printing. + +#3.1.3. draw_line() + + void draw_line(drawing *dr, int x1, int y1, int x2, int y2, + int colour); + +Draws a straight line in the puzzle window. + +`x1' and `y1' give the coordinates of one end of the line. `x2' and `y2' +give the coordinates of the other end. The line drawn includes both +those points. + +`colour' is an integer index into the colours array returned by the back +end function colours() (section 2.8.6). + +Some platforms may perform anti-aliasing on this function. Therefore, +do not assume that you can erase a line by drawing the same line over +it in the background colour; anti-aliasing might lead to perceptible +ghost artefacts around the vanished line. Horizontal and vertical lines, +however, are pixel-perfect and not anti-aliased. + +This function may be used for both drawing and printing. + +#3.1.4. draw_polygon() + + void draw_polygon(drawing *dr, int *coords, int npoints, + int fillcolour, int outlinecolour); + +Draws an outlined or filled polygon in the puzzle window. + +`coords' is an array of (2*npoints) integers, containing the `x' and `y' +coordinates of `npoints' vertices. + +`fillcolour' and `outlinecolour' are integer indices into the colours +array returned by the back end function colours() (section 2.8.6). +`fillcolour' may also be -1 to indicate that the polygon should be +outlined only. + +The polygon defined by the specified list of vertices is first filled in +`fillcolour', if specified, and then outlined in `outlinecolour'. + +`outlinecolour' may _not_ be -1; it must be a valid colour (and front +ends are permitted to enforce this by assertion). This is because +different platforms disagree on whether a filled polygon should include +its boundary line or not, so drawing _only_ a filled polygon would +have non-portable effects. If you want your filled polygon not to +have a visible outline, you must set `outlinecolour' to the same as +`fillcolour'. + +Some platforms may perform anti-aliasing on this function. Therefore, do +not assume that you can erase a polygon by drawing the same polygon over +it in the background colour. Also, be prepared for the polygon to extend +a pixel beyond its obvious bounding box as a result of this; if you +really need it not to do this to avoid interfering with other delicate +graphics, you should probably use clip() (section 3.1.9). You can rely +on horizontal and vertical lines not being anti-aliased. + +This function may be used for both drawing and printing. + +#3.1.5. draw_circle() + + void draw_circle(drawing *dr, int cx, int cy, int radius, + int fillcolour, int outlinecolour); + +Draws an outlined or filled circle in the puzzle window. + +`cx' and `cy' give the coordinates of the centre of the circle. `radius' +gives its radius. The total horizontal pixel extent of the circle is +from `cx-radius+1' to `cx+radius-1' inclusive, and the vertical extent +similarly around `cy'. + +`fillcolour' and `outlinecolour' are integer indices into the colours +array returned by the back end function colours() (section 2.8.6). +`fillcolour' may also be -1 to indicate that the circle should be +outlined only. + +The circle is first filled in `fillcolour', if specified, and then +outlined in `outlinecolour'. + +`outlinecolour' may _not_ be -1; it must be a valid colour (and front +ends are permitted to enforce this by assertion). This is because +different platforms disagree on whether a filled circle should include +its boundary line or not, so drawing _only_ a filled circle would +have non-portable effects. If you want your filled circle not to +have a visible outline, you must set `outlinecolour' to the same as +`fillcolour'. + +Some platforms may perform anti-aliasing on this function. Therefore, do +not assume that you can erase a circle by drawing the same circle over +it in the background colour. Also, be prepared for the circle to extend +a pixel beyond its obvious bounding box as a result of this; if you +really need it not to do this to avoid interfering with other delicate +graphics, you should probably use clip() (section 3.1.9). + +This function may be used for both drawing and printing. + +#3.1.6. draw_thick_line() + + void draw_thick_line(drawing *dr, float thickness, + float x1, float y1, float x2, float y2, + int colour) + +Draws a line in the puzzle window, giving control over the line's +thickness. + +`x1' and `y1' give the coordinates of one end of the line. `x2' and `y2' +give the coordinates of the other end. `thickness' gives the thickness +of the line, in pixels. + +Note that the coordinates and thickness are floating-point: the +continuous coordinate system is in effect here. It's important to be +able to address points with better-than-pixel precision in this case, +because one can't otherwise properly express the endpoints of lines with +both odd and even thicknesses. + +Some platforms may perform anti-aliasing on this function. The precise +pixels affected by a thick-line drawing operation may vary between +platforms, and no particular guarantees are provided. Indeed, even +horizontal or vertical lines may be anti-aliased. + +This function may be used for both drawing and printing. + +If the specified thickness is less than 1.0, 1.0 is used. This ensures +that thin lines are visible even at small scales. + +#3.1.7. draw_text() + + void draw_text(drawing *dr, int x, int y, int fonttype, + int fontsize, int align, int colour, + const char *text); + +Draws text in the puzzle window. + +`x' and `y' give the coordinates of a point. The relation of this point +to the location of the text is specified by `align', which is a bitwise +OR of horizontal and vertical alignment flags: + +ALIGN_VNORMAL + + Indicates that `y' is aligned with the baseline of the text. + +ALIGN_VCENTRE + + Indicates that `y' is aligned with the vertical centre of the + text. (In fact, it's aligned with the vertical centre of normal + _capitalised_ text: displaying two pieces of text with ALIGN_VCENTRE + at the same y-coordinate will cause their baselines to be aligned + with one another, even if one is an ascender and the other a + descender.) + +ALIGN_HLEFT + + Indicates that `x' is aligned with the left-hand end of the text. + +ALIGN_HCENTRE + + Indicates that `x' is aligned with the horizontal centre of the + text. + +ALIGN_HRIGHT + + Indicates that `x' is aligned with the right-hand end of the text. + +`fonttype' is either FONT_FIXED or FONT_VARIABLE, for a monospaced +or proportional font respectively. (No more detail than that may be +specified; it would only lead to portability issues between different +platforms.) + +`fontsize' is the desired size, in pixels, of the text. This size +corresponds to the overall point size of the text, not to any internal +dimension such as the cap-height. + +`colour' is an integer index into the colours array returned by the back +end function colours() (section 2.8.6). + +This function may be used for both drawing and printing. + +The character set used to encode the text passed to this function is +specified _by the drawing object_, although it must be a superset of +ASCII. If a puzzle wants to display text that is not contained in ASCII, +it should use the text_fallback() function (section 3.1.8) to query the +drawing object for an appropriate representation of the characters it +wants. + +#3.1.8. text_fallback() + + char *text_fallback(drawing *dr, const char *const *strings, + int nstrings); + +This function is used to request a translation of UTF-8 text into +whatever character encoding is expected by the drawing object's +implementation of draw_text(). + +The input is a list of strings encoded in UTF-8: nstrings gives the +number of strings in the list, and strings[0], strings[1], ..., +strings[nstrings-1] are the strings themselves. + +The returned string (which is dynamically allocated and must be freed +when finished with) is derived from the first string in the list that +the drawing object expects to be able to display reliably; it will +consist of that string translated into the character set expected by +draw_text(). + +Drawing implementations are not required to handle anything outside +ASCII, but are permitted to assume that _some_ string will be +successfully translated. So every call to this function must include +a string somewhere in the list (presumably the last element) which +consists of nothing but ASCII, to be used by any front end which cannot +handle anything else. + +For example, if a puzzle wished to display a string including a +multiplication sign (U+00D7 in Unicode, represented by the bytes C3 97 +in UTF-8), it might do something like this: + + static const char *const times_signs[] = { "\xC3\x97", "x" }; + char *times_sign = text_fallback(dr, times_signs, 2); + sprintf(buffer, "%d%s%d", width, times_sign, height); + draw_text(dr, x, y, font, size, align, colour, buffer); + sfree(buffer); + +which would draw a string with a times sign in the middle on platforms +that support it, and fall back to a simple ASCII `x' where there was no +alternative. + +#3.1.9. clip() + + void clip(drawing *dr, int x, int y, int w, int h); + +Establishes a clipping rectangle in the puzzle window. + +`x' and `y' give the coordinates of the top left pixel of the clipping +rectangle. `w' and `h' give its width and height. Thus, the horizontal +extent of the rectangle runs from `x' to `x+w-1' inclusive, and the +vertical extent from `y' to `y+h-1' inclusive. (These are exactly the +same semantics as draw_rect().) + +After this call, no drawing operation will affect anything outside the +specified rectangle. The effect can be reversed by calling unclip() +(section 3.1.10). The clipping rectangle is pixel-perfect: pixels within +the rectangle are affected as usual by drawing functions; pixels outside +are completely untouched. + +Back ends should not assume that a clipping rectangle will be +automatically cleared up by the front end if it's left lying around; +that might work on current front ends, but shouldn't be relied upon. +Always explicitly call unclip(). + +This function may be used for both drawing and printing. + +#3.1.10. unclip() + + void unclip(drawing *dr); + +Reverts the effect of a previous call to clip(). After this call, all +drawing operations will be able to affect the entire puzzle window +again. + +This function may be used for both drawing and printing. + +#3.1.11. draw_update() + + void draw_update(drawing *dr, int x, int y, int w, int h); + +Informs the front end that a rectangular portion of the puzzle window +has been drawn on and needs to be updated. + +`x' and `y' give the coordinates of the top left pixel of the update +rectangle. `w' and `h' give its width and height. Thus, the horizontal +extent of the rectangle runs from `x' to `x+w-1' inclusive, and the +vertical extent from `y' to `y+h-1' inclusive. (These are exactly the +same semantics as draw_rect().) + +The back end redraw function _must_ call this function to report any +changes it has made to the window. Otherwise, those changes may not +become immediately visible, and may then appear at an unpredictable +subsequent time such as the next time the window is covered and re- +exposed. + +This function is only important when drawing. It may be called when +printing as well, but doing so is not compulsory, and has no effect. +(So if you have a shared piece of code between the drawing and printing +routines, that code may safely call draw_update().) + +#3.1.12. status_bar() + + void status_bar(drawing *dr, const char *text); + +Sets the text in the game's status bar to `text'. The text is copied +from the supplied buffer, so the caller is free to deallocate or modify +the buffer after use. + +(This function is not exactly a _drawing_ function, but it shares with +the drawing API the property that it may only be called from within the +back end redraw function, so this is as good a place as any to document +it.) + +The supplied text is filtered through the mid-end for optional rewriting +before being passed on to the front end; the mid-end will prepend the +current game time if the game is timed (and may in future perform other +rewriting if it seems like a good idea). + +This function is for drawing only; it must never be called during +printing. + +#3.1.13. Blitter functions + +This section describes a group of related functions which save and +restore a section of the puzzle window. This is most commonly used to +implement user interfaces involving dragging a puzzle element around the +window: at the end of each call to redraw(), if an object is currently +being dragged, the back end saves the window contents under that +location and then draws the dragged object, and at the start of the next +redraw() the first thing it does is to restore the background. + +The front end defines an opaque type called a `blitter', which is +capable of storing a rectangular area of a specified size. + +Blitter functions are for drawing only; they must never be called during +printing. + +#3.1.13.1. blitter_new() + + blitter *blitter_new(drawing *dr, int w, int h); + +Creates a new blitter object which stores a rectangle of size `w' by `h' +pixels. Returns a pointer to the blitter object. + +Blitter objects are best stored in the `game_drawstate'. A good time to +create them is in the set_size() function (section 2.8.5), since it is +at this point that you first know how big a rectangle they will need to +save. + +#3.1.13.2. blitter_free() + + void blitter_free(drawing *dr, blitter *bl); + +Disposes of a blitter object. Best called in free_drawstate(). (However, +check that the blitter object is not NULL before attempting to free it; +it is possible that a draw state might be created and freed without ever +having set_size() called on it in between.) + +#3.1.13.3. blitter_save() + + void blitter_save(drawing *dr, blitter *bl, int x, int y); + +This is a true drawing API function, in that it may only be called from +within the game redraw routine. It saves a rectangular portion of the +puzzle window into the specified blitter object. + +`x' and `y' give the coordinates of the top left corner of the saved +rectangle. The rectangle's width and height are the ones specified when +the blitter object was created. + +This function is required to cope and do the right thing if `x' and `y' +are out of range. (The right thing probably means saving whatever part +of the blitter rectangle overlaps with the visible area of the puzzle +window.) + +#3.1.13.4. blitter_load() + + void blitter_load(drawing *dr, blitter *bl, int x, int y); + +This is a true drawing API function, in that it may only be called from +within the game redraw routine. It restores a rectangular portion of the +puzzle window from the specified blitter object. + +`x' and `y' give the coordinates of the top left corner of the rectangle +to be restored. The rectangle's width and height are the ones specified +when the blitter object was created. + +Alternatively, you can specify both `x' and `y' as the special value +BLITTER_FROMSAVED, in which case the rectangle will be restored to +exactly where it was saved from. (This is probably what you want to do +almost all the time, if you're using blitters to implement draggable +puzzle elements.) + +This function is required to cope and do the right thing if `x' and +`y' (or the equivalent ones saved in the blitter) are out of range. +(The right thing probably means restoring whatever part of the blitter +rectangle overlaps with the visible area of the puzzle window.) + +If this function is called on a blitter which had previously been saved +from a partially out-of-range rectangle, then the parts of the saved +bitmap which were not visible at save time are undefined. If the blitter +is restored to a different position so as to make those parts visible, +the effect on the drawing area is undefined. + +#3.1.14. print_mono_colour() + + int print_mono_colour(drawing *dr, int grey); + +This function allocates a colour index for a simple monochrome colour +during printing. + +`grey' must be 0 or 1. If `grey' is 0, the colour returned is black; if +`grey' is 1, the colour is white. + +#3.1.15. print_grey_colour() + + int print_grey_colour(drawing *dr, float grey); + +This function allocates a colour index for a grey-scale colour during +printing. + +`grey' may be any number between 0 (black) and 1 (white); for example, +0.5 indicates a medium grey. + +The chosen colour will be rendered to the limits of the printer's +halftoning capability. + +#3.1.16. print_hatched_colour() + + int print_hatched_colour(drawing *dr, int hatch); + +This function allocates a colour index which does not represent a +literal _colour_. Instead, regions shaded in this colour will be hatched +with parallel lines. The `hatch' parameter defines what type of hatching +should be used in place of this colour: + +HATCH_SLASH + + This colour will be hatched by lines slanting to the right at 45 + degrees. + +HATCH_BACKSLASH + + This colour will be hatched by lines slanting to the left at 45 + degrees. + +HATCH_HORIZ + + This colour will be hatched by horizontal lines. + +HATCH_VERT + + This colour will be hatched by vertical lines. + +HATCH_PLUS + + This colour will be hatched by criss-crossing horizontal and + vertical lines. + +HATCH_X + + This colour will be hatched by criss-crossing diagonal lines. + +Colours defined to use hatching may not be used for drawing lines or +text; they may only be used for filling areas. That is, they may be +used as the `fillcolour' parameter to draw_circle() and draw_polygon(), +and as the colour parameter to draw_rect(), but may not be used as the +`outlinecolour' parameter to draw_circle() or draw_polygon(), or with +draw_line() or draw_text(). + +#3.1.17. print_rgb_mono_colour() + + int print_rgb_mono_colour(drawing *dr, float r, float g, + float b, float grey); + +This function allocates a colour index for a fully specified RGB colour +during printing. + +`r', `g' and `b' may each be anywhere in the range from 0 to 1. + +If printing in black and white only, these values will be ignored, and +either pure black or pure white will be used instead, according to the +`grey' parameter. (The fallback colour is the same as the one which +would be allocated by print_mono_colour(grey).) + +#3.1.18. print_rgb_grey_colour() + + int print_rgb_grey_colour(drawing *dr, float r, float g, + float b, float grey); + +This function allocates a colour index for a fully specified RGB colour +during printing. + +`r', `g' and `b' may each be anywhere in the range from 0 to 1. + +If printing in black and white only, these values will be ignored, and +a shade of grey given by the `grey' parameter will be used instead. +(The fallback colour is the same as the one which would be allocated by +print_grey_colour(grey).) + +#3.1.19. print_rgb_hatched_colour() + + int print_rgb_hatched_colour(drawing *dr, float r, float g, + float b, float hatched); + +This function allocates a colour index for a fully specified RGB colour +during printing. + +`r', `g' and `b' may each be anywhere in the range from 0 to 1. + +If printing in black and white only, these values will be ignored, and +a form of cross-hatching given by the `hatch' parameter will be used +instead; see section 3.1.16 for the possible values of this parameter. +(The fallback colour is the same as the one which would be allocated by +print_hatched_colour(hatch).) + +#3.1.20. print_line_width() + + void print_line_width(drawing *dr, int width); + +This function is called to set the thickness of lines drawn during +printing. It is meaningless in drawing: all lines drawn by draw_line(), +draw_circle and draw_polygon() are one pixel in thickness. However, in +printing there is no clear definition of a pixel and so line widths must +be explicitly specified. + +The line width is specified in the usual coordinate system. Note, +however, that it is a hint only: the central printing system may choose +to vary line thicknesses at user request or due to printer capabilities. + +#3.1.21. print_line_dotted() + + void print_line_dotted(drawing *dr, int dotted); + +This function is called to toggle the drawing of dotted lines during +printing. It is not supported during drawing. + +The parameter `dotted' is a boolean; TRUE means that future lines drawn +by draw_line(), draw_circle and draw_polygon() will be dotted, and FALSE +means that they will be solid. + +Some front ends may impose restrictions on the width of dotted lines. +Asking for a dotted line via this front end will override any line width +request if the front end requires it. + +#3.2. The drawing API as implemented by the front end + +This section describes the drawing API in the function-pointer form in +which it is implemented by a front end. + +(It isn't only platform-specific front ends which implement this API; +the platform-independent module `ps.c' also provides an implementation +of it which outputs PostScript. Thus, any platform which wants to do PS +printing can do so with minimum fuss.) + +The following entries all describe function pointer fields in a +structure called `drawing_api'. Each of the functions takes a `void *' +context pointer, which it should internally cast back to a more useful +type. Thus, a drawing _object_ (`drawing *)' suitable for passing to +the back end redraw or printing functions is constructed by passing a +`drawing_api' and a `void *' to the function drawing_new() (see section +3.3.1). + +#3.2.1. draw_text() + + void (*draw_text)(void *handle, int x, int y, int fonttype, + int fontsize, int align, int colour, + const char *text); + +This function behaves exactly like the back end draw_text() function; +see section 3.1.7. + +#3.2.2. draw_rect() + + void (*draw_rect)(void *handle, int x, int y, int w, int h, + int colour); + +This function behaves exactly like the back end draw_rect() function; +see section 3.1.1. + +#3.2.3. draw_line() + + void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, + int colour); + +This function behaves exactly like the back end draw_line() function; +see section 3.1.3. + +#3.2.4. draw_polygon() + + void (*draw_polygon)(void *handle, int *coords, int npoints, + int fillcolour, int outlinecolour); + +This function behaves exactly like the back end draw_polygon() function; +see section 3.1.4. + +#3.2.5. draw_circle() + + void (*draw_circle)(void *handle, int cx, int cy, int radius, + int fillcolour, int outlinecolour); + +This function behaves exactly like the back end draw_circle() function; +see section 3.1.5. + +#3.2.6. draw_thick_line() + + void draw_thick_line(drawing *dr, float thickness, + float x1, float y1, float x2, float y2, + int colour) + +This function behaves exactly like the back end draw_thick_line() +function; see section 3.1.6. + +An implementation of this API which doesn't provide high-quality +rendering of thick lines is permitted to define this function pointer +to be NULL. The middleware in drawing.c will notice and provide a low- +quality alternative using draw_polygon(). + +#3.2.7. draw_update() + + void (*draw_update)(void *handle, int x, int y, int w, int h); + +This function behaves exactly like the back end draw_update() function; +see section 3.1.11. + +An implementation of this API which only supports printing is permitted +to define this function pointer to be NULL rather than bothering to +define an empty function. The middleware in drawing.c will notice and +avoid calling it. + +#3.2.8. clip() + + void (*clip)(void *handle, int x, int y, int w, int h); + +This function behaves exactly like the back end clip() function; see +section 3.1.9. + +#3.2.9. unclip() + + void (*unclip)(void *handle); + +This function behaves exactly like the back end unclip() function; see +section 3.1.10. + +#3.2.10. start_draw() + + void (*start_draw)(void *handle); + +This function is called at the start of drawing. It allows the front end +to initialise any temporary data required to draw with, such as device +contexts. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.11. end_draw() + + void (*end_draw)(void *handle); + +This function is called at the end of drawing. It allows the front end +to do cleanup tasks such as deallocating device contexts and scheduling +appropriate GUI redraw events. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.12. status_bar() + + void (*status_bar)(void *handle, const char *text); + +This function behaves exactly like the back end status_bar() function; +see section 3.1.12. + +Front ends implementing this function need not worry about it +being called repeatedly with the same text; the middleware code in +status_bar() will take care of this. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.13. blitter_new() + + blitter *(*blitter_new)(void *handle, int w, int h); + +This function behaves exactly like the back end blitter_new() function; +see section 3.1.13.1. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.14. blitter_free() + + void (*blitter_free)(void *handle, blitter *bl); + +This function behaves exactly like the back end blitter_free() function; +see section 3.1.13.2. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.15. blitter_save() + + void (*blitter_save)(void *handle, blitter *bl, int x, int y); + +This function behaves exactly like the back end blitter_save() function; +see section 3.1.13.3. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.16. blitter_load() + + void (*blitter_load)(void *handle, blitter *bl, int x, int y); + +This function behaves exactly like the back end blitter_load() function; +see section 3.1.13.4. + +Implementations of this API which do not provide drawing services may +define this function pointer to be NULL; it will never be called unless +drawing is attempted. + +#3.2.17. begin_doc() + + void (*begin_doc)(void *handle, int pages); + +This function is called at the beginning of a printing run. It gives the +front end an opportunity to initialise any required printing subsystem. +It also provides the number of pages in advance. + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.18. begin_page() + + void (*begin_page)(void *handle, int number); + +This function is called during printing, at the beginning of each page. +It gives the page number (numbered from 1 rather than 0, so suitable for +use in user-visible contexts). + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.19. begin_puzzle() + + void (*begin_puzzle)(void *handle, float xm, float xc, + float ym, float yc, int pw, int ph, float wmm); + +This function is called during printing, just before printing a single +puzzle on a page. It specifies the size and location of the puzzle on +the page. + +`xm' and `xc' specify the horizontal position of the puzzle on the page, +as a linear function of the page width. The front end is expected to +multiply the page width by `xm', add `xc' (measured in millimetres), and +use the resulting x-coordinate as the left edge of the puzzle. + +Similarly, `ym' and `yc' specify the vertical position of the puzzle as +a function of the page height: the page height times `ym', plus `yc' +millimetres, equals the desired distance from the top of the page to the +top of the puzzle. + +(This unwieldy mechanism is required because not all printing systems +can communicate the page size back to the software. The PostScript back +end, for example, writes out PS which determines the page size at print +time by means of calling `clippath', and centres the puzzles within +that. Thus, exactly the same PS file works on A4 or on US Letter paper +without needing local configuration, which simplifies matters.) + +pw and ph give the size of the puzzle in drawing API coordinates. The +printing system will subsequently call the puzzle's own print function, +which will in turn call drawing API functions in the expectation that an +area pw by ph units is available to draw the puzzle on. + +Finally, wmm gives the desired width of the puzzle in millimetres. (The +aspect ratio is expected to be preserved, so if the desired puzzle +height is also needed then it can be computed as wmm*ph/pw.) + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.20. end_puzzle() + + void (*end_puzzle)(void *handle); + +This function is called after the printing of a specific puzzle is +complete. + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.21. end_page() + + void (*end_page)(void *handle, int number); + +This function is called after the printing of a page is finished. + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.22. end_doc() + + void (*end_doc)(void *handle); + +This function is called after the printing of the entire document is +finished. This is the moment to close files, send things to the print +spooler, or whatever the local convention is. + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.23. line_width() + + void (*line_width)(void *handle, float width); + +This function is called to set the line thickness, during printing only. +Note that the width is a float here, where it was an int as seen by the +back end. This is because drawing.c may have scaled it on the way past. + +However, the width is still specified in the same coordinate system as +the rest of the drawing. + +Implementations of this API which do not provide printing services may +define this function pointer to be NULL; it will never be called unless +printing is attempted. + +#3.2.24. text_fallback() + + char *(*text_fallback)(void *handle, const char *const *strings, + int nstrings); + +This function behaves exactly like the back end text_fallback() +function; see section 3.1.8. + +Implementations of this API which do not support any characters outside +ASCII may define this function pointer to be NULL, in which case the +central code in drawing.c will provide a default implementation. + +#3.3. The drawing API as called by the front end + +There are a small number of functions provided in drawing.c which the +front end needs to _call_, rather than helping to implement. They are +described in this section. + +#3.3.1. drawing_new() + + drawing *drawing_new(const drawing_api *api, midend *me, + void *handle); + +This function creates a drawing object. It is passed a `drawing_api', +which is a structure containing nothing but function pointers; and also +a `void *' handle. The handle is passed back to each function pointer +when it is called. + +The `midend' parameter is used for rewriting the status bar contents: +status_bar() (see section 3.1.12) has to call a function in the mid- +end which might rewrite the status bar text. If the drawing object +is to be used only for printing, or if the game is known not to call +status_bar(), this parameter may be NULL. + +#3.3.2. drawing_free() + + void drawing_free(drawing *dr); + +This function frees a drawing object. Note that the `void *' handle is +not freed; if that needs cleaning up it must be done by the front end. + +#3.3.3. print_get_colour() + + void print_get_colour(drawing *dr, int colour, int printincolour, + int *hatch, float *r, float *g, float *b) + +This function is called by the implementations of the drawing API +functions when they are called in a printing context. It takes a colour +index as input, and returns the description of the colour as requested +by the back end. + +`printincolour' is TRUE iff the implementation is printing in colour. +This will alter the results returned if the colour in question was +specified with a black-and-white fallback value. + +If the colour should be rendered by hatching, `*hatch' is filled with +the type of hatching desired. See section 3.1.15 for details of the +values this integer can take. + +If the colour should be rendered as solid colour, `*hatch' is given a +negative value, and `*r', `*g' and `*b' are filled with the RGB values +of the desired colour (if printing in colour), or all filled with the +grey-scale value (if printing in black and white). + +#4. The API provided by the mid-end + +This chapter documents the API provided by the mid-end to be called by +the front end. You probably only need to read this if you are a front +end implementor, i.e. you are porting Puzzles to a new platform. If +you're only interested in writing new puzzles, you can safely skip this +chapter. + +All the persistent state in the mid-end is encapsulated within a +`midend' structure, to facilitate having multiple mid-ends in any +port which supports multiple puzzle windows open simultaneously. Each +`midend' is intended to handle the contents of a single puzzle window. + +#4.1. midend_new() + + midend *midend_new(frontend *fe, const game *ourgame, + const drawing_api *drapi, void *drhandle) + +Allocates and returns a new mid-end structure. + +The `fe' argument is stored in the mid-end. It will be used when calling +back to functions such as activate_timer() (section 4.36), and will be +passed on to the back end function colours() (section 2.8.6). + +The parameters `drapi' and `drhandle' are passed to drawing_new() +(section 3.3.1) to construct a drawing object which will be passed to +the back end function redraw() (section 2.8.10). Hence, all drawing- +related function pointers defined in `drapi' can expect to be called +with `drhandle' as their first argument. + +The `ourgame' argument points to a container structure describing a game +back end. The mid-end thus created will only be capable of handling that +one game. (So even in a monolithic front end containing all the games, +this imposes the constraint that any individual puzzle window is tied to +a single game. Unless, of course, you feel brave enough to change the +mid-end for the window without closing the window...) + +#4.2. midend_free() + + void midend_free(midend *me); + +Frees a mid-end structure and all its associated data. + +#4.3. midend_tilesize() + + int midend_tilesize(midend *me); + +Returns the `tilesize' parameter being used to display the current +puzzle (section 2.8.3). + +#4.4. midend_set_params() + + void midend_set_params(midend *me, game_params *params); + +Sets the current game parameters for a mid-end. Subsequent games +generated by midend_new_game() (section 4.8) will use these parameters +until further notice. + +The usual way in which the front end will have an actual `game_params' +structure to pass to this function is if it had previously got it from +midend_get_presets() (section 4.15). Thus, this function is usually +called in response to the user making a selection from the presets menu. + +#4.5. midend_get_params() + + game_params *midend_get_params(midend *me); + +Returns the current game parameters stored in this mid-end. + +The returned value is dynamically allocated, and should be freed when +finished with by passing it to the game's own free_params() function +(see section 2.3.6). + +#4.6. midend_size() + + void midend_size(midend *me, int *x, int *y, int user_size); + +Tells the mid-end to figure out its window size. + +On input, `*x' and `*y' should contain the maximum or requested size +for the window. (Typically this will be the size of the screen that the +window has to fit on, or similar.) The mid-end will repeatedly call the +back end function compute_size() (section 2.8.4), searching for a tile +size that best satisfies the requirements. On exit, `*x' and `*y' will +contain the size needed for the puzzle window's drawing area. (It is +of course up to the front end to adjust this for any additional window +furniture such as menu bars and window borders, if necessary. The status +bar is also not included in this size.) + +Use `user_size' to indicate whether `*x' and `*y' are a requested size, +or just a maximum size. + +If `user_size' is set to TRUE, the mid-end will treat the input size as +a request, and will pick a tile size which approximates it _as closely +as possible_, going over the game's preferred tile size if necessary to +achieve this. The mid-end will also use the resulting tile size as its +preferred one until further notice, on the assumption that this size was +explicitly requested by the user. Use this option if you want your front +end to support dynamic resizing of the puzzle window with automatic +scaling of the puzzle to fit. + +If `user_size' is set to FALSE, then the game's tile size will never go +over its preferred one, although it may go under in order to fit within +the maximum bounds specified by `*x' and `*y'. This is the recommended +approach when opening a new window at default size: the game will use +its preferred size unless it has to use a smaller one to fit on the +screen. If the tile size is shrunk for this reason, the change will not +persist; if a smaller grid is subsequently chosen, the tile size will +recover. + +The mid-end will try as hard as it can to return a size which is +less than or equal to the input size, in both dimensions. In extreme +circumstances it may fail (if even the lowest possible tile size gives +window dimensions greater than the input), in which case it will return +a size greater than the input size. Front ends should be prepared +for this to happen (i.e. don't crash or fail an assertion), but may +handle it in any way they see fit: by rejecting the game parameters +which caused the problem, by opening a window larger than the screen +regardless of inconvenience, by introducing scroll bars on the window, +by drawing on a large bitmap and scaling it into a smaller window, or by +any other means you can think of. It is likely that when the tile size +is that small the game will be unplayable anyway, so don't put _too_ +much effort into handling it creatively. + +If your platform has no limit on window size (or if you're planning to +use scroll bars for large puzzles), you can pass dimensions of INT_MAX +as input to this function. You should probably not do that _and_ set the +`user_size' flag, though! + +The midend relies on the frontend calling midend_new_game() (section +4.8) before calling midend_size(). + +#4.7. midend_reset_tilesize() + + void midend_reset_tilesize(midend *me); + +This function resets the midend's preferred tile size to that of the +standard puzzle. + +As discussed in section 4.6, puzzle resizes are typically 'sticky', +in that once the user has dragged the puzzle to a different window +size, the resulting tile size will be remembered and used when the +puzzle configuration changes. If you _don't_ want that, e.g. if you +want to provide a command to explicitly reset the puzzle size back to +its default, then you can call this just before calling midend_size() +(which, in turn, you would probably call with `user_size' set to FALSE). + +#4.8. midend_new_game() + + void midend_new_game(midend *me); + +Causes the mid-end to begin a new game. Normally the game will be a +new randomly generated puzzle. However, if you have previously called +midend_game_id() or midend_set_config(), the game generated might be +dictated by the results of those functions. (In particular, you _must_ +call midend_new_game() after calling either of those functions, or else +no immediate effect will be visible.) + +You will probably need to call midend_size() after calling this +function, because if the game parameters have been changed since the +last new game then the window size might need to change. (If you know +the parameters _haven't_ changed, you don't need to do this.) + +This function will create a new `game_drawstate', but does not actually +perform a redraw (since you often need to call midend_size() before +the redraw can be done). So after calling this function and after +calling midend_size(), you should then call midend_redraw(). (It is not +necessary to call midend_force_redraw(); that will discard the draw +state and create a fresh one, which is unnecessary in this case since +there's a fresh one already. It would work, but it's usually excessive.) + +#4.9. midend_restart_game() + + void midend_restart_game(midend *me); + +This function causes the current game to be restarted. This is done by +placing a new copy of the original game state on the end of the undo +list (so that an accidental restart can be undone). + +This function automatically causes a redraw, i.e. the front end can +expect its drawing API to be called from _within_ a call to this +function. Some back ends require that midend_size() (section 4.6) is +called before midend_restart_game(). + +#4.10. midend_force_redraw() + + void midend_force_redraw(midend *me); + +Forces a complete redraw of the puzzle window, by means of discarding +the current `game_drawstate' and creating a new one from scratch before +calling the game's redraw() function. + +The front end can expect its drawing API to be called from within a call +to this function. Some back ends require that midend_size() (section +4.6) is called before midend_force_redraw(). + +#4.11. midend_redraw() + + void midend_redraw(midend *me); + +Causes a partial redraw of the puzzle window, by means of simply calling +the game's redraw() function. (That is, the only things redrawn will be +things that have changed since the last redraw.) + +The front end can expect its drawing API to be called from within a call +to this function. Some back ends require that midend_size() (section +4.6) is called before midend_redraw(). + +#4.12. midend_process_key() + + int midend_process_key(midend *me, int x, int y, int button); + +The front end calls this function to report a mouse or keyboard event. +The parameters `x', `y' and `button' are almost identical to the ones +passed to the back end function interpret_move() (section 2.7.1), except +that the front end is _not_ required to provide the guarantees about +mouse event ordering. The mid-end will sort out multiple simultaneous +button presses and changes of button; the front end's responsibility +is simply to pass on the mouse events it receives as accurately as +possible. + +(Some platforms may need to emulate absent mouse buttons by means of +using a modifier key such as Shift with another mouse button. This tends +to mean that if Shift is pressed or released in the middle of a mouse +drag, the mid-end will suddenly stop receiving, say, LEFT_DRAG events +and start receiving RIGHT_DRAGs, with no intervening button release or +press events. This too is something which the mid-end will sort out for +you; the front end has no obligation to maintain sanity in this area.) + +The front end _should_, however, always eventually send some kind of +button release. On some platforms this requires special effort: Windows, +for example, requires a call to the system API function SetCapture() in +order to ensure that your window receives a mouse-up event even if the +pointer has left the window by the time the mouse button is released. +On any platform that requires this sort of thing, the front end _is_ +responsible for doing it. + +Calling this function is very likely to result in calls back to the +front end's drawing API and/or activate_timer() (section 4.36). + +The return value from midend_process_key() is non-zero, unless the +effect of the keypress was to request termination of the program. A +front end should shut down the puzzle in response to a zero return. + +#4.13. midend_colours() + + float *midend_colours(midend *me, int *ncolours); + +Returns an array of the colours required by the game, in exactly +the same format as that returned by the back end function colours() +(section 2.8.6). Front ends should call this function rather than +calling the back end's version directly, since the mid-end adds standard +customisation facilities. (At the time of writing, those customisation +facilities are implemented hackily by means of environment variables, +but it's not impossible that they may become more full and formal in +future.) + +#4.14. midend_timer() + + void midend_timer(midend *me, float tplus); + +If the mid-end has called activate_timer() (section 4.36) to request +regular callbacks for purposes of animation or timing, this is the +function the front end should call on a regular basis. The argument +`tplus' gives the time, in seconds, since the last time either this +function was called or activate_timer() was invoked. + +One of the major purposes of timing in the mid-end is to perform move +animation. Therefore, calling this function is very likely to result in +calls back to the front end's drawing API. + +#4.15. midend_get_presets() + + struct preset_menu *midend_get_presets(midend *me, int *id_limit); + +Returns a data structure describing this game's collection of preset +game parameters, organised into a hierarchical structure of menus and +submenus. + +The return value is a pointer to a data structure containing the +following fields (among others, which are not intended for front end +use): + + struct preset_menu { + int n_entries; + struct preset_menu_entry *entries; + /* and other things */ + }; + +Those fields describe the intended contents of one particular menu in +the hierarchy. `entries' points to an array of `n_entries' items, each +of which is a structure containing the following fields: + + struct preset_menu_entry { + char *title; + game_params *params; + struct preset_menu *submenu; + int id; + }; + +Of these fields, `title' and `id' are present in every entry, giving +(respectively) the textual name of the menu item and an integer +identifier for it. The integer id will correspond to the one returned +by `midend_which_preset' (section 4.16), when that preset is the one +selected. + +The other two fields are mutually exclusive. Each +`struct preset_menu_entry' will have one of those fields NULL and the +other one non-null. If the menu item is an actual preset, then `params' +will point to the set of game parameters that go with the name; if it's +a submenu, then `submenu' instead will be non-null, and will point at a +subsidiary `struct preset_menu'. + +The complete hierarchy of these structures is owned by the mid-end, +and will be freed when the mid-end is freed. The front end should not +attempt to free any of it. + +The integer identifiers will be allocated densely from 0 upwards, so +that it's reasonable for the front end to allocate an array which uses +them as indices, if it needs to store information per preset menu item. +For this purpose, the front end may pass the second parameter `id_limit' +to midend_get_presets as the address of an `int' variable, into which +midend_get_presets will write an integer one larger than the largest id +number actually used (i.e. the number of elements the front end would +need in the array). + +Submenu-type entries also have integer identifiers. + +#4.16. midend_which_preset() + + int midend_which_preset(midend *me); + +Returns the numeric index of the preset game parameter structure which +matches the current game parameters, or a negative number if no preset +matches. Front ends could use this to maintain a tick beside one of the +items in the menu (or tick the `Custom' option if the return value is +less than zero). + +The returned index value (if non-negative) will match the `id' +field of the corresponding struct preset_menu_entry returned by +`midend_get_presets()' (section 4.15). + +#4.17. midend_wants_statusbar() + + int midend_wants_statusbar(midend *me); + +This function returns TRUE if the puzzle has a use for a textual status +line (to display score, completion status, currently active tiles, time, +or anything else). + +Front ends should call this function rather than talking directly to the +back end. + +#4.18. midend_get_config() + + config_item *midend_get_config(midend *me, int which, + char **wintitle); + +Returns a dialog box description for user configuration. + +On input, which should be set to one of three values, which select which +of the various dialog box descriptions is returned: + +CFG_SETTINGS + + Requests the GUI parameter configuration box generated by the puzzle + itself. This should be used when the user selects `Custom' from the + game types menu (or equivalent). The mid-end passes this request on + to the back end function configure() (section 2.3.9). + +CFG_DESC + + Requests a box suitable for entering a descriptive game ID (and + viewing the existing one). The mid-end generates this dialog box + description itself. This should be used when the user selects + `Specific' from the game menu (or equivalent). + +CFG_SEED + + Requests a box suitable for entering a random-seed game ID (and + viewing the existing one). The mid-end generates this dialog box + description itself. This should be used when the user selects + `Random Seed' from the game menu (or equivalent). + +The returned value is an array of config_items, exactly as described +in section 2.3.9. Another returned value is an ASCII string giving a +suitable title for the configuration window, in `*wintitle'. + +Both returned values are dynamically allocated and will need to be +freed. The window title can be freed in the obvious way; the config_item +array is a slightly complex structure, so a utility function free_cfg() +is provided to free it for you. See section 5.3.6. + +(Of course, you will probably not want to free the config_item array +until the dialog box is dismissed, because before then you will probably +need to pass it to midend_set_config.) + +#4.19. midend_set_config() + + const char *midend_set_config(midend *me, int which, + config_item *cfg); + +Passes the mid-end the results of a configuration dialog box. `which' +should have the same value which it had when midend_get_config() was +called; `cfg' should be the array of `config_item's returned from +midend_get_config(), modified to contain the results of the user's +editing operations. + +This function returns NULL on success, or otherwise (if the +configuration data was in some way invalid) an ASCII string containing +an error message suitable for showing to the user. + +If the function succeeds, it is likely that the game parameters will +have been changed and it is certain that a new game will be requested. +The front end should therefore call midend_new_game(), and probably also +re-think the window size using midend_size() and eventually perform a +refresh using midend_redraw(). + +#4.20. midend_game_id() + + const char *midend_game_id(midend *me, const char *id); + +Passes the mid-end a string game ID (of any of the valid forms `params', +`params:description' or `params#seed') which the mid-end will process +and use for the next generated game. + +This function returns NULL on success, or otherwise (if the +configuration data was in some way invalid) an ASCII string containing +an error message (not dynamically allocated) suitable for showing to the +user. In the event of an error, the mid-end's internal state will be +left exactly as it was before the call. + +If the function succeeds, it is likely that the game parameters will +have been changed and it is certain that a new game will be requested. +The front end should therefore call midend_new_game(), and probably +also re-think the window size using midend_size() and eventually case a +refresh using midend_redraw(). + +#4.21. midend_get_game_id() + + char *midend_get_game_id(midend *me) + +Returns a descriptive game ID (i.e. one in the form +`params:description') describing the game currently active in the mid- +end. The returned string is dynamically allocated. + +#4.22. midend_get_random_seed() + + char *midend_get_random_seed(midend *me) + +Returns a random game ID (i.e. one in the form `params#seedstring') +describing the game currently active in the mid-end, if there is one. +If the game was created by entering a description, no random seed will +currently exist and this function will return NULL. + +The returned string, if it is non-NULL, is dynamically allocated. + +#4.23. midend_can_format_as_text_now() + + int midend_can_format_as_text_now(midend *me); + +Returns TRUE if the game code is capable of formatting puzzles of the +currently selected game type as ASCII. + +If this returns FALSE, then midend_text_format() (section 4.24) will +return NULL. + +#4.24. midend_text_format() + + char *midend_text_format(midend *me); + +Formats the current game's current state as ASCII text suitable for +copying to the clipboard. The returned string is dynamically allocated. + +If the game's `can_format_as_text_ever' flag is FALSE, or if its +can_format_as_text_now() function returns FALSE, then this function will +return NULL. + +If the returned string contains multiple lines (which is likely), it +will use the normal C line ending convention (\n only). On platforms +which use a different line ending convention for data in the clipboard, +it is the front end's responsibility to perform the conversion. + +#4.25. midend_solve() + + const char *midend_solve(midend *me); + +Requests the mid-end to perform a Solve operation. + +On success, NULL is returned. On failure, an error message (not +dynamically allocated) is returned, suitable for showing to the user. + +The front end can expect its drawing API and/or activate_timer() to be +called from within a call to this function. Some back ends require that +midend_size() (section 4.6) is called before midend_solve(). + +#4.26. midend_status() + + int midend_status(midend *me); + +This function returns +1 if the midend is currently displaying a game +in a solved state, -1 if the game is in a permanently lost state, or 0 +otherwise. This function just calls the back end's status() function. +Front ends may wish to use this as a cue to proactively offer the option +of starting a new game. + +(See section 2.8.9 for more detail about the back end's status() +function and discussion of what should count as which status code.) + +#4.27. midend_can_undo() + + int midend_can_undo(midend *me); + +Returns TRUE if the midend is currently in a state where the undo +operation is meaningful (i.e. at least one position exists on the undo +chain before the present one). Front ends may wish to use this to +visually activate and deactivate an undo button. + +#4.28. midend_can_redo() + + int midend_can_redo(midend *me); + +Returns TRUE if the midend is currently in a state where the redo +operation is meaningful (i.e. at least one position exists on the +redo chain after the present one). Front ends may wish to use this to +visually activate and deactivate a redo button. + +#4.29. midend_serialise() + + void midend_serialise(midend *me, + void (*write)(void *ctx, const void *buf, int len), void *wctx); + +Calling this function causes the mid-end to convert its entire internal +state into a long ASCII text string, and to pass that string (piece by +piece) to the supplied `write' function. + +Desktop implementations can use this function to save a game in any +state (including half-finished) to a disk file, by supplying a `write' +function which is a wrapper on fwrite() (or local equivalent). Other +implementations may find other uses for it, such as compressing the +large and sprawling mid-end state into a manageable amount of memory +when a palmtop application is suspended so that another one can run; in +this case write might want to write to a memory buffer rather than a +file. There may be other uses for it as well. + +This function will call back to the supplied `write' function a number +of times, with the first parameter (`ctx') equal to `wctx', and the +other two parameters pointing at a piece of the output string. + +#4.30. midend_deserialise() + + const char *midend_deserialise(midend *me, + int (*read)(void *ctx, void *buf, int len), void *rctx); + +This function is the counterpart to midend_serialise(). It calls the +supplied read function repeatedly to read a quantity of data, and +attempts to interpret that data as a serialised mid-end as output by +midend_serialise(). + +The read function is called with the first parameter (`ctx') equal +to `rctx', and should attempt to read `len' bytes of data into the +buffer pointed to by `buf'. It should return FALSE on failure or TRUE +on success. It should not report success unless it has filled the +entire buffer; on platforms which might be reading from a pipe or other +blocking data source, `read' is responsible for looping until the whole +buffer has been filled. + +If the de-serialisation operation is successful, the mid-end's internal +data structures will be replaced by the results of the load, and NULL +will be returned. Otherwise, the mid-end's state will be completely +unchanged and an error message (typically some variation on `save file +is corrupt') will be returned. As usual, the error message string is not +dynamically allocated. + +If this function succeeds, it is likely that the game parameters will +have been changed. The front end should therefore probably re-think the +window size using midend_size(), and probably cause a refresh using +midend_redraw(). + +Because each mid-end is tied to a specific game back end, this function +will fail if you attempt to read in a save file generated by a different +game from the one configured in this mid-end, even if your application +is a monolithic one containing all the puzzles. See section 4.31 for a +helper function which will allow you to identify a save file before you +instantiate your mid-end in the first place. + +#4.31. identify_game() + + const char *identify_game(char **name, + int (*read)(void *ctx, void *buf, int len), void *rctx); + +This function examines a serialised midend stream, of the same kind used +by midend_serialise() and midend_deserialise(), and returns the name +field of the game back end from which it was saved. + +You might want this if your front end was a monolithic one containing +all the puzzles, and you wanted to be able to load an arbitrary save +file and automatically switch to the right game. Probably your next step +would be to iterate through gamelist (section 4.33) looking for a game +structure whose name field matched the returned string, and give an +error if you didn't find one. + +On success, the return value of this function is NULL, and the game name +string is written into *name. The caller should free that string after +using it. + +On failure, *name is NULL, and the return value is an error message +(which does not need freeing at all). + +(This isn't strictly speaking a midend function, since it doesn't accept +or return a pointer to a midend. You'd probably call it just _before_ +deciding what kind of midend you wanted to instantiate.) + +#4.32. midend_request_id_changes() + + void midend_request_id_changes(midend *me, + void (*notify)(void *), void *ctx); + +This function is called by the front end to request notification by the +mid-end when the current game IDs (either descriptive or random-seed) +change. This can occur as a result of keypresses ('n' for New Game, for +example) or when a puzzle supersedes its game description (see section +2.11.2). After this function is called, any change of the game ids will +cause the mid-end to call notify(ctx) after the change. + +This is for use by puzzles which want to present the game description to +the user constantly (e.g. as an HTML hyperlink) instead of only showing +it when the user explicitly requests it. + +This is a function I anticipate few front ends needing to implement, so +I make it a callback rather than a static function in order to relieve +most front ends of the need to provide an empty implementation. + +#4.33. Direct reference to the back end structure by the front end + +Although _most_ things the front end needs done should be done by +calling the mid-end, there are a few situations in which the front end +needs to refer directly to the game back end structure. + +The most obvious of these is + + - passing the game back end as a parameter to midend_new(). + +There are a few other back end features which are not wrapped by the +mid-end because there didn't seem much point in doing so: + + - fetching the `name' field to use in window titles and similar + + - reading the `can_configure', `can_solve' and + `can_format_as_text_ever' fields to decide whether to add those + items to the menu bar or equivalent + + - reading the `winhelp_topic' field (Windows only) + + - the GTK front end provides a `--generate' command-line option which + directly calls the back end to do most of its work. This is not + really part of the main front end code, though, and I'm not sure it + counts. + +In order to find the game back end structure, the front end does one of +two things: + + - If the particular front end is compiling a separate binary per game, + then the back end structure is a global variable with the standard + name `thegame': + + extern const game thegame; + + - If the front end is compiled as a monolithic application containing + all the puzzles together (in which case the preprocessor symbol + COMBINED must be defined when compiling most of the code base), then + there will be two global variables defined: + + extern const game *gamelist[]; + extern const int gamecount; + + `gamelist' will be an array of `gamecount' game structures, declared + in the automatically constructed source module `list.c'. The + application should search that array for the game it wants, probably + by reaching into each game structure and looking at its `name' + field. + +#4.34. Mid-end to front-end calls + +This section describes the small number of functions which a front end +must provide to be called by the mid-end or other standard utility +modules. + +#4.35. get_random_seed() + + void get_random_seed(void **randseed, int *randseedsize); + +This function is called by a new mid-end, and also occasionally by game +back ends. Its job is to return a piece of data suitable for using as a +seed for initialisation of a new `random_state'. + +On exit, `*randseed' should be set to point at a newly allocated piece +of memory containing some seed data, and `*randseedsize' should be set +to the length of that data. + +A simple and entirely adequate implementation is to return a piece of +data containing the current system time at the highest conveniently +available resolution. + +#4.36. activate_timer() + + void activate_timer(frontend *fe); + +This is called by the mid-end to request that the front end begin +calling it back at regular intervals. + +The timeout interval is left up to the front end; the finer it is, the +smoother move animations will be, but the more CPU time will be used. +Current front ends use values around 20ms (i.e. 50Hz). + +After this function is called, the mid-end will expect to receive calls +to midend_timer() on a regular basis. + +#4.37. deactivate_timer() + + void deactivate_timer(frontend *fe); + +This is called by the mid-end to request that the front end stop calling +midend_timer(). + +#4.38. fatal() + + void fatal(const char *fmt, ...); + +This is called by some utility functions if they encounter a genuinely +fatal error such as running out of memory. It is a variadic function +in the style of printf(), and is expected to show the formatted error +message to the user any way it can and then terminate the application. +It must not return. + +#4.39. frontend_default_colour() + + void frontend_default_colour(frontend *fe, float *output); + +This function expects to be passed a pointer to an array of three +floats. It returns the platform's local preferred background colour +in those three floats, as red, green and blue values (in that order) +ranging from 0.0 to 1.0. + +This function should only ever be called by the back end function +colours() (section 2.8.6). (Thus, it isn't a _midend_-to-frontend +function as such, but there didn't seem to be anywhere else particularly +good to put it. Sorry.) + +#5. Utility APIs + +This chapter documents a variety of utility APIs provided for the +general use of the rest of the Puzzles code. + +#5.1. Random number generation + +Platforms' local random number generators vary widely in quality and +seed size. Puzzles therefore supplies its own high-quality random number +generator, with the additional advantage of giving the same results if +fed the same seed data on different platforms. This allows game random +seeds to be exchanged between different ports of Puzzles and still +generate the same games. + +Unlike the ANSI C rand() function, the Puzzles random number generator +has an _explicit_ state object called a `random_state'. One of these +is managed by each mid-end, for example, and passed to the back end to +generate a game with. + +#5.1.1. random_new() + + random_state *random_new(char *seed, int len); + +Allocates, initialises and returns a new `random_state'. The input data +is used as the seed for the random number stream (i.e. using the same +seed at a later time will generate the same stream). + +The seed data can be any data at all; there is no requirement to use +printable ASCII, or NUL-terminated strings, or anything like that. + +#5.1.2. random_copy() + + random_state *random_copy(random_state *tocopy); + +Allocates a new `random_state', copies the contents of another +`random_state' into it, and returns the new state. If exactly the +same sequence of functions is subseqently called on both the copy and +the original, the results will be identical. This may be useful for +speculatively performing some operation using a given random state, and +later replaying that operation precisely. + +#5.1.3. random_free() + + void random_free(random_state *state); + +Frees a `random_state'. + +#5.1.4. random_bits() + + unsigned long random_bits(random_state *state, int bits); + +Returns a random number from 0 to 2^bits-1 inclusive. `bits' should be +between 1 and 32 inclusive. + +#5.1.5. random_upto() + + unsigned long random_upto(random_state *state, unsigned long limit); + +Returns a random number from 0 to limit-1 inclusive. + +#5.1.6. random_state_encode() + + char *random_state_encode(random_state *state); + +Encodes the entire contents of a `random_state' in printable ASCII. +Returns a dynamically allocated string containing that encoding. This +can subsequently be passed to random_state_decode() to reconstruct the +same `random_state'. + +#5.1.7. random_state_decode() + + random_state *random_state_decode(char *input); + +Decodes a string generated by random_state_encode() and reconstructs an +equivalent `random_state' to the one encoded, i.e. it should produce the +same stream of random numbers. + +This function has no error reporting; if you pass it an invalid string +it will simply generate an arbitrary random state, which may turn out to +be noticeably non-random. + +#5.1.8. shuffle() + + void shuffle(void *array, int nelts, int eltsize, random_state *rs); + +Shuffles an array into a random order. The interface is much like ANSI C +qsort(), except that there's no need for a compare function. + +`array' is a pointer to the first element of the array. `nelts' is the +number of elements in the array; `eltsize' is the size of a single +element (typically measured using `sizeof'). `rs' is a `random_state' +used to generate all the random numbers for the shuffling process. + +#5.2. Presets menu management + +The function `midend_get_presets()' (section 4.15) returns a data +structure describing a menu hierarchy. Back ends can also choose to +provide such a structure to the mid-end, if they want to group their +presets hierarchically. To make this easy, there are a few utility +functions to construct preset menu structures, and also one intended for +front-end use. + +#5.2.1. preset_menu_new() + + struct preset_menu *preset_menu_new(void); + +Allocates a new `struct preset_menu', and initialises it to hold no menu +items. + +#5.2.2. preset_menu_add_submenu() + + struct preset_menu *preset_menu_add_submenu + (struct preset_menu *parent, char *title); + +Adds a new submenu to the end of an existing preset menu, and returns +a pointer to a newly allocated `struct preset_menu' describing the +submenu. + +The string parameter `title' must be dynamically allocated by the +caller. The preset-menu structure will take ownership of it, so the +caller must not free it. + +#5.2.3. preset_menu_add_preset() + + void preset_menu_add_preset + (struct preset_menu *menu, char *title, game_params *params); + +Adds a preset game configuration to the end of a preset menu. + +Both the string parameter `title' and the game parameter structure +`params' itself must be dynamically allocated by the caller. The preset- +menu structure will take ownership of it, so the caller must not free +it. + +#5.2.4. preset_menu_lookup_by_id() + + game_params *preset_menu_lookup_by_id + (struct preset_menu *menu, int id); + +Given a numeric index, searches recursively through a preset menu +hierarchy to find the corresponding menu entry, and returns a pointer to +its existing `game_params' structure. + +This function is intended for front end use (but front ends need not use +it if they prefer to do things another way). If a front end finds it +inconvenient to store anything more than a numeric index alongside each +menu item, then this function provides an easy way for the front end to +get back the actual game parameters corresponding to a menu item that +the user has selected. + +#5.3. Memory allocation + +Puzzles has some central wrappers on the standard memory allocation +functions, which provide compile-time type checking, and run-time error +checking by means of quitting the application if it runs out of memory. +This doesn't provide the best possible recovery from memory shortage, +but on the other hand it greatly simplifies the rest of the code, +because nothing else anywhere needs to worry about NULL returns from +allocation. + +#5.3.1. snew() + + var = snew(type); + +This macro takes a single argument which is a _type name_. It allocates +space for one object of that type. If allocation fails it will call +fatal() and not return; so if it does return, you can be confident that +its return value is non-NULL. + +The return value is cast to the specified type, so that the compiler +will type-check it against the variable you assign it into. Thus, this +ensures you don't accidentally allocate memory the size of the wrong +type and assign it into a variable of the right one (or vice versa!). + +#5.3.2. snewn() + + var = snewn(n, type); + +This macro is the array form of snew(). It takes two arguments; the +first is a number, and the second is a type name. It allocates space +for that many objects of that type, and returns a type-checked non-NULL +pointer just as snew() does. + +#5.3.3. sresize() + + var = sresize(var, n, type); + +This macro is a type-checked form of realloc(). It takes three +arguments: an input memory block, a new size in elements, and a type. +It re-sizes the input memory block to a size sufficient to contain that +many elements of that type. It returns a type-checked non-NULL pointer, +like snew() and snewn(). + +The input memory block can be NULL, in which case this function will +behave exactly like snewn(). (In principle any ANSI-compliant realloc() +implementation ought to cope with this, but I've never quite trusted it +to work everywhere.) + +#5.3.4. sfree() + + void sfree(void *p); + +This function is pretty much equivalent to free(). It is provided with a +dynamically allocated block, and frees it. + +The input memory block can be NULL, in which case this function will do +nothing. (In principle any ANSI-compliant free() implementation ought to +cope with this, but I've never quite trusted it to work everywhere.) + +#5.3.5. dupstr() + + char *dupstr(const char *s); + +This function dynamically allocates a duplicate of a C string. Like the +snew() functions, it guarantees to return non-NULL or not return at all. + +(Many platforms provide the function strdup(). As well as guaranteeing +never to return NULL, my version has the advantage of being defined +_everywhere_, rather than inconveniently not quite everywhere.) + +#5.3.6. free_cfg() + + void free_cfg(config_item *cfg); + +This function correctly frees an array of `config_item's, including +walking the array until it gets to the end and freeing any subsidiary +data items in each `u' sub-union which are expected to be dynamically +allocated. + +(See section 2.3.9 for details of the `config_item' structure.) + +#5.4. Sorted and counted tree functions + +Many games require complex algorithms for generating random puzzles, and +some require moderately complex algorithms even during play. A common +requirement during these algorithms is for a means of maintaining sorted +or unsorted lists of items, such that items can be removed and added +conveniently. + +For general use, Puzzles provides the following set of functions which +maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced tree +structure, with the property that all lookups, insertions, deletions, +splits and joins can be done in O(log N) time.) + +All these functions expect you to be storing a tree of `void *' +pointers. You can put anything you like in those pointers. + +By the use of per-node element counts, these tree structures have the +slightly unusual ability to look elements up by their numeric index +within the list represented by the tree. This means that they can be +used to store an unsorted list (in which case, every time you insert a +new element, you must explicitly specify the position where you wish to +insert it). They can also do numeric lookups in a sorted tree, which +might be useful for (for example) tracking the median of a changing data +set. + +As well as storing sorted lists, these functions can be used for storing +`maps' (associative arrays), by defining each element of a tree to be a +(key, value) pair. + +#5.4.1. newtree234() + + tree234 *newtree234(cmpfn234 cmp); + +Creates a new empty tree, and returns a pointer to it. + +The parameter `cmp' determines the sorting criterion on the tree. Its +prototype is + + typedef int (*cmpfn234)(void *, void *); + +If you want a sorted tree, you should provide a function matching this +prototype, which returns like strcmp() does (negative if the first +argument is smaller than the second, positive if it is bigger, zero if +they compare equal). In this case, the function addpos234() will not be +usable on your tree (because all insertions must respect the sorting +order). + +If you want an unsorted tree, pass NULL. In this case you will not be +able to use either add234() or del234(), or any other function such +as find234() which depends on a sorting order. Your tree will become +something more like an array, except that it will efficiently support +insertion and deletion as well as lookups by numeric index. + +#5.4.2. freetree234() + + void freetree234(tree234 *t); + +Frees a tree. This function will not free the _elements_ of the tree +(because they might not be dynamically allocated, or you might be +storing the same set of elements in more than one tree); it will just +free the tree structure itself. If you want to free all the elements of +a tree, you should empty it before passing it to freetree234(), by means +of code along the lines of + + while ((element = delpos234(tree, 0)) != NULL) + sfree(element); /* or some more complicated free function */ + +#5.4.3. add234() + + void *add234(tree234 *t, void *e); + +Inserts a new element `e' into the tree `t'. This function expects the +tree to be sorted; the new element is inserted according to the sort +order. + +If an element comparing equal to `e' is already in the tree, then the +insertion will fail, and the return value will be the existing element. +Otherwise, the insertion succeeds, and `e' is returned. + +#5.4.4. addpos234() + + void *addpos234(tree234 *t, void *e, int index); + +Inserts a new element into an unsorted tree. Since there is no sorting +order to dictate where the new element goes, you must specify where you +want it to go. Setting `index' to zero puts the new element right at the +start of the list; setting `index' to the current number of elements in +the tree puts the new element at the end. + +Return value is `e', in line with add234() (although this function +cannot fail except by running out of memory, in which case it will bomb +out and die rather than returning an error indication). + +#5.4.5. index234() + + void *index234(tree234 *t, int index); + +Returns a pointer to the `index'th element of the tree, or NULL if +`index' is out of range. Elements of the tree are numbered from zero. + +#5.4.6. find234() + + void *find234(tree234 *t, void *e, cmpfn234 cmp); + +Searches for an element comparing equal to `e' in a sorted tree. + +If `cmp' is NULL, the tree's ordinary comparison function will be used +to perform the search. However, sometimes you don't want that; suppose, +for example, each of your elements is a big structure containing a +`char *' name field, and you want to find the element with a given name. +You _could_ achieve this by constructing a fake element structure, +setting its name field appropriately, and passing it to find234(), +but you might find it more convenient to pass _just_ a name string to +find234(), supplying an alternative comparison function which expects +one of its arguments to be a bare name and the other to be a large +structure containing a name field. + +Therefore, if `cmp' is not NULL, then it will be used to compare `e' to +elements of the tree. The first argument passed to `cmp' will always be +`e'; the second will be an element of the tree. + +(See section 5.4.1 for the definition of the `cmpfn234' function pointer +type.) + +The returned value is the element found, or NULL if the search is +unsuccessful. + +#5.4.7. findrel234() + + void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); + +This function is like find234(), but has the additional ability to do a +_relative_ search. The additional parameter `relation' can be one of the +following values: + +REL234_EQ + + Find only an element that compares equal to `e'. This is exactly the + behaviour of find234(). + +REL234_LT + + Find the greatest element that compares strictly less than `e'. `e' + may be NULL, in which case it finds the greatest element in the + whole tree (which could also be done by index234(t, count234(t)-1)). + +REL234_LE + + Find the greatest element that compares less than or equal to `e'. + (That is, find an element that compares equal to `e' if possible, + but failing that settle for something just less than it.) + +REL234_GT + + Find the smallest element that compares strictly greater than `e'. + `e' may be NULL, in which case it finds the smallest element in the + whole tree (which could also be done by index234(t, 0)). + +REL234_GE + + Find the smallest element that compares greater than or equal + to `e'. (That is, find an element that compares equal to `e' if + possible, but failing that settle for something just bigger than + it.) + +Return value, as before, is the element found or NULL if no element +satisfied the search criterion. + +#5.4.8. findpos234() + + void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); + +This function is like find234(), but has the additional feature of +returning the index of the element found in the tree; that index is +written to `*index' in the event of a successful search (a non-NULL +return value). + +`index' may be NULL, in which case this function behaves exactly like +find234(). + +#5.4.9. findrelpos234() + + void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, + int *index); + +This function combines all the features of findrel234() and +findpos234(). + +#5.4.10. del234() + + void *del234(tree234 *t, void *e); + +Finds an element comparing equal to `e' in the tree, deletes it, and +returns it. + +The input tree must be sorted. + +The element found might be `e' itself, or might merely compare equal to +it. + +Return value is NULL if no such element is found. + +#5.4.11. delpos234() + + void *delpos234(tree234 *t, int index); + +Deletes the element at position `index' in the tree, and returns it. + +Return value is NULL if the index is out of range. + +#5.4.12. count234() + + int count234(tree234 *t); + +Returns the number of elements currently in the tree. + +#5.4.13. splitpos234() + + tree234 *splitpos234(tree234 *t, int index, int before); + +Splits the input tree into two pieces at a given position, and creates a +new tree containing all the elements on one side of that position. + +If `before' is TRUE, then all the items at or after position `index' are +left in the input tree, and the items before that point are returned in +the new tree. Otherwise, the reverse happens: all the items at or after +`index' are moved into the new tree, and those before that point are +left in the old one. + +If `index' is equal to 0 or to the number of elements in the input tree, +then one of the two trees will end up empty (and this is not an error +condition). If `index' is further out of range in either direction, the +operation will fail completely and return NULL. + +This operation completes in O(log N) time, no matter how large the tree +or how balanced or unbalanced the split. + +#5.4.14. split234() + + tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel); + +Splits a sorted tree according to its sort order. + +`rel' can be any of the relation constants described in section 5.4.7, +_except_ for REL234_EQ. All the elements having that relation to `e' +will be transferred into the new tree; the rest will be left in the old +one. + +The parameter `cmp' has the same semantics as it does in find234(): if +it is not NULL, it will be used in place of the tree's own comparison +function when comparing elements to `e', in such a way that `e' itself +is always the first of its two operands. + +Again, this operation completes in O(log N) time, no matter how large +the tree or how balanced or unbalanced the split. + +#5.4.15. join234() + + tree234 *join234(tree234 *t1, tree234 *t2); + +Joins two trees together by concatenating the lists they represent. All +the elements of `t2' are moved into `t1', in such a way that they appear +_after_ the elements of `t1'. The tree `t2' is freed; the return value +is `t1'. + +If you apply this function to a sorted tree and it violates the sort +order (i.e. the smallest element in `t2' is smaller than or equal to the +largest element in `t1'), the operation will fail and return NULL. + +This operation completes in O(log N) time, no matter how large the trees +being joined together. + +#5.4.16. join234r() + + tree234 *join234r(tree234 *t1, tree234 *t2); + +Joins two trees together in exactly the same way as join234(), but this +time the combined tree is returned in `t2', and `t1' is destroyed. The +elements in `t1' still appear before those in `t2'. + +Again, this operation completes in O(log N) time, no matter how large +the trees being joined together. + +#5.4.17. copytree234() + + tree234 *copytree234(tree234 *t, copyfn234 copyfn, + void *copyfnstate); + +Makes a copy of an entire tree. + +If `copyfn' is NULL, the tree will be copied but the elements will not +be; i.e. the new tree will contain pointers to exactly the same physical +elements as the old one. + +If you want to copy each actual element during the operation, you can +instead pass a function in `copyfn' which makes a copy of each element. +That function has the prototype + + typedef void *(*copyfn234)(void *state, void *element); + +and every time it is called, the `state' parameter will be set to the +value you passed in as `copyfnstate'. + +#5.5. Miscellaneous utility functions and macros + +This section contains all the utility functions which didn't sensibly +fit anywhere else. + +#5.5.1. TRUE and FALSE + +The main Puzzles header file defines the macros TRUE and FALSE, which +are used throughout the code in place of 1 and 0 (respectively) to +indicate that the values are in a boolean context. For code base +consistency, I'd prefer it if submissions of new code followed this +convention as well. + +#5.5.2. max() and min() + +The main Puzzles header file defines the pretty standard macros max() +and min(), each of which is given two arguments and returns the one +which compares greater or less respectively. + +These macros may evaluate their arguments multiple times. Avoid side +effects. + +#5.5.3. PI + +The main Puzzles header file defines a macro PI which expands to a +floating-point constant representing pi. + +(I've never understood why ANSI's <math.h> doesn't define this. It'd be +so useful!) + +#5.5.4. obfuscate_bitmap() + + void obfuscate_bitmap(unsigned char *bmp, int bits, int decode); + +This function obscures the contents of a piece of data, by cryptographic +methods. It is useful for games of hidden information (such as Mines, +Guess or Black Box), in which the game ID theoretically reveals all the +information the player is supposed to be trying to guess. So in order +that players should be able to send game IDs to one another without +accidentally spoiling the resulting game by looking at them, these games +obfuscate their game IDs using this function. + +Although the obfuscation function is cryptographic, it cannot properly +be called encryption because it has no key. Therefore, anybody motivated +enough can re-implement it, or hack it out of the Puzzles source, +and strip the obfuscation off one of these game IDs to see what lies +beneath. (Indeed, they could usually do it much more easily than that, +by entering the game ID into their own copy of the puzzle and hitting +Solve.) The aim is not to protect against a determined attacker; the aim +is simply to protect people who wanted to play the game honestly from +_accidentally_ spoiling their own fun. + +The input argument `bmp' points at a piece of memory to be obfuscated. +`bits' gives the length of the data. Note that that length is in _bits_ +rather than bytes: if you ask for obfuscation of a partial number of +bytes, then you will get it. Bytes are considered to be used from the +top down: thus, for example, setting `bits' to 10 will cover the whole +of bmp[0] and the _top two_ bits of bmp[1]. The remainder of a partially +used byte is undefined (i.e. it may be corrupted by the function). + +The parameter `decode' is FALSE for an encoding operation, and TRUE +for a decoding operation. Each is the inverse of the other. (There's +no particular reason you shouldn't obfuscate by decoding and restore +cleartext by encoding, if you really wanted to; it should still work.) + +The input bitmap is processed in place. + +#5.5.5. bin2hex() + + char *bin2hex(const unsigned char *in, int inlen); + +This function takes an input byte array and converts it into an +ASCII string encoding those bytes in (lower-case) hex. It returns a +dynamically allocated string containing that encoding. + +This function is useful for encoding the result of obfuscate_bitmap() in +printable ASCII for use in game IDs. + +#5.5.6. hex2bin() + + unsigned char *hex2bin(const char *in, int outlen); + +This function takes an ASCII string containing hex digits, and converts +it back into a byte array of length `outlen'. If there aren't enough +hex digits in the string, the contents of the resulting array will be +undefined. + +This function is the inverse of bin2hex(). + +#5.5.7. game_mkhighlight() + + void game_mkhighlight(frontend *fe, float *ret, + int background, int highlight, int lowlight); + +It's reasonably common for a puzzle game's graphics to use highlights +and lowlights to indicate `raised' or `lowered' sections. Fifteen, +Sixteen and Twiddle are good examples of this. + +Puzzles using this graphical style are running a risk if they just use +whatever background colour is supplied to them by the front end, because +that background colour might be too light to see any highlights on at +all. (In particular, it's not unheard of for the front end to specify a +default background colour of white.) + +Therefore, such puzzles can call this utility function from their +colours() routine (section 2.8.6). You pass it your front end handle, a +pointer to the start of your return array, and three colour indices. It +will: + + - call frontend_default_colour() (section 4.39) to fetch the front + end's default background colour + + - alter the brightness of that colour if it's unsuitable + + - define brighter and darker variants of the colour to be used as + highlights and lowlights + + - write those results into the relevant positions in the `ret' array. + +Thus, ret[background*3] to ret[background*3+2] will be set to RGB values +defining a sensible background colour, and similary `highlight' and +`lowlight' will be set to sensible colours. + +#6. How to write a new puzzle + +This chapter gives a guide to how to actually write a new puzzle: where +to start, what to do first, how to solve common problems. + +The previous chapters have been largely composed of facts. This one is +mostly advice. + +#6.1. Choosing a puzzle + +Before you start writing a puzzle, you have to choose one. Your taste +in puzzle games is up to you, of course; and, in fact, you're probably +reading this guide because you've _already_ thought of a game you want +to write. But if you want to get it accepted into the official Puzzles +distribution, then there's a criterion it has to meet. + +The current Puzzles editorial policy is that all games should be _fair_. +A fair game is one which a player can only fail to complete through +demonstrable lack of skill - that is, such that a better player in the +same situation would have _known_ to do something different. + +For a start, that means every game presented to the user must have _at +least one solution_. Giving the unsuspecting user a puzzle which is +actually impossible is not acceptable. (There is an exception: if the +user has selected some non-default option which is clearly labelled as +potentially unfair, _then_ you're allowed to generate possibly insoluble +puzzles, because the user isn't unsuspecting any more. Same Game and +Mines both have options of this type.) + +Also, this actually _rules out_ games such as Klondike, or the normal +form of Mahjong Solitaire. Those games have the property that even if +there is a solution (i.e. some sequence of moves which will get from +the start state to the solved state), the player doesn't necessarily +have enough information to _find_ that solution. In both games, it is +possible to reach a dead end because you had an arbitrary choice to make +and made it the wrong way. This violates the fairness criterion, because +a better player couldn't have known they needed to make the other +choice. + +(GNOME has a variant on Mahjong Solitaire which makes it fair: there +is a Shuffle operation which randomly permutes all the remaining tiles +without changing their positions, which allows you to get out of a +sticky situation. Using this operation adds a 60-second penalty to your +solution time, so it's to the player's advantage to try to minimise +the chance of having to use it. It's still possible to render the game +uncompletable if you end up with only two tiles vertically stacked, +but that's easy to foresee and avoid using a shuffle operation. This +form of the game _is_ fair. Implementing it in Puzzles would require +an infrastructure change so that the back end could communicate time +penalties to the mid-end, but that would be easy enough.) + +Providing a _unique_ solution is a little more negotiable; it depends +on the puzzle. Solo would have been of unacceptably low quality if it +didn't always have a unique solution, whereas Twiddle inherently has +multiple solutions by its very nature and it would have been meaningless +to even _suggest_ making it uniquely soluble. Somewhere in between, Flip +could reasonably be made to have unique solutions (by enforcing a zero- +dimension kernel in every generated matrix) but it doesn't seem like a +serious quality problem that it doesn't. + +Of course, you don't _have_ to care about all this. There's nothing +stopping you implementing any puzzle you want to if you're happy to +maintain your puzzle yourself, distribute it from your own web site, +fork the Puzzles code completely, or anything like that. It's free +software; you can do what you like with it. But any game that you want +to be accepted into _my_ Puzzles code base has to satisfy the fairness +criterion, which means all randomly generated puzzles must have a +solution (unless the user has deliberately chosen otherwise) and it must +be possible _in theory_ to find that solution without having to guess. + +#6.2. Getting started + +The simplest way to start writing a new puzzle is to copy `nullgame.c'. +This is a template puzzle source file which does almost nothing, but +which contains all the back end function prototypes and declares the +back end data structure correctly. It is built every time the rest of +Puzzles is built, to ensure that it doesn't get out of sync with the +code and remains buildable. + +So start by copying `nullgame.c' into your new source file. Then you'll +gradually add functionality until the very boring Null Game turns into +your real game. + +Next you'll need to add your puzzle to the Makefiles, in order to +compile it conveniently. _Do not edit the Makefiles_: they are created +automatically by the script `mkfiles.pl', from the file called `Recipe'. +Edit `Recipe', and then re-run `mkfiles.pl'. + +Also, don't forget to add your puzzle to `list.c': if you don't, then it +will still run fine on platforms which build each puzzle separately, but +Mac OS X and other monolithic platforms will not include your new puzzle +in their single binary. + +Once your source file is building, you can move on to the fun bit. + +#6.2.1. Puzzle generation + +Randomly generating instances of your puzzle is almost certain to be +the most difficult part of the code, and also the task with the highest +chance of turning out to be completely infeasible. Therefore I strongly +recommend doing it _first_, so that if it all goes horribly wrong you +haven't wasted any more time than you absolutely had to. What I usually +do is to take an unmodified `nullgame.c', and start adding code to +new_game_desc() which tries to generate a puzzle instance and print it +out using printf(). Once that's working, _then_ I start connecting it up +to the return value of new_game_desc(), populating other structures like +`game_params', and generally writing the rest of the source file. + +There are many ways to generate a puzzle which is known to be soluble. +In this section I list all the methods I currently know of, in case any +of them can be applied to your puzzle. (Not all of these methods will +work, or in some cases even make sense, for all puzzles.) + +Some puzzles are mathematically tractable, meaning you can work out in +advance which instances are soluble. Sixteen, for example, has a parity +constraint in some settings which renders exactly half the game space +unreachable, but it can be mathematically proved that any position +not in that half _is_ reachable. Therefore, Sixteen's grid generation +simply consists of selecting at random from a well defined subset of the +game space. Cube in its default state is even easier: _every_ possible +arrangement of the blue squares and the cube's starting position is +soluble! + +Another option is to redefine what you mean by `soluble'. Black Box +takes this approach. There are layouts of balls in the box which are +completely indistinguishable from one another no matter how many beams +you fire into the box from which angles, which would normally be grounds +for declaring those layouts unfair; but fortunately, detecting that +indistinguishability is computationally easy. So Black Box doesn't +demand that your ball placements match its own; it merely demands +that your ball placements be _indistinguishable_ from the ones it was +thinking of. If you have an ambiguous puzzle, then any of the possible +answers is considered to be a solution. Having redefined the rules in +that way, any puzzle is soluble again. + +Those are the simple techniques. If they don't work, you have to get +cleverer. + +One way to generate a soluble puzzle is to start from the solved state +and make inverse moves until you reach a starting state. Then you know +there's a solution, because you can just list the inverse moves you made +and make them in the opposite order to return to the solved state. + +This method can be simple and effective for puzzles where you get to +decide what's a starting state and what's not. In Pegs, for example, +the generator begins with one peg in the centre of the board and makes +inverse moves until it gets bored; in this puzzle, valid inverse moves +are easy to detect, and _any_ state that's reachable from the solved +state by inverse moves is a reasonable starting position. So Pegs just +continues making inverse moves until the board satisfies some criteria +about extent and density, and then stops and declares itself done. + +For other puzzles, it can be a lot more difficult. Same Game uses +this strategy too, and it's lucky to get away with it at all: valid +inverse moves aren't easy to find (because although it's easy to insert +additional squares in a Same Game position, it's difficult to arrange +that _after_ the insertion they aren't adjacent to any other squares of +the same colour), so you're constantly at risk of running out of options +and having to backtrack or start again. Also, Same Game grids never +start off half-empty, which means you can't just stop when you run out +of moves - you have to find a way to fill the grid up _completely_. + +The other way to generate a puzzle that's soluble is to start from the +other end, and actually write a _solver_. This tends to ensure that a +puzzle has a _unique_ solution over and above having a solution at all, +so it's a good technique to apply to puzzles for which that's important. + +One theoretical drawback of generating soluble puzzles by using a solver +is that your puzzles are restricted in difficulty to those which the +solver can handle. (Most solvers are not fully general: many sets of +puzzle rules are NP-complete or otherwise nasty, so most solvers can +only handle a subset of the theoretically soluble puzzles.) It's been +my experience in practice, however, that this usually isn't a problem; +computers are good at very different things from humans, and what the +computer thinks is nice and easy might still be pleasantly challenging +for a human. For example, when solving Dominosa puzzles I frequently +find myself using a variety of reasoning techniques that my solver +doesn't know about; in principle, therefore, I should be able to solve +the puzzle using only those techniques it _does_ know about, but this +would involve repeatedly searching the entire grid for the one simple +deduction I can make. Computers are good at this sort of exhaustive +search, but it's been my experience that human solvers prefer to do more +complex deductions than to spend ages searching for simple ones. So in +many cases I don't find my own playing experience to be limited by the +restrictions on the solver. + +(This isn't _always_ the case. Solo is a counter-example; generating +Solo puzzles using a simple solver does lead to qualitatively easier +puzzles. Therefore I had to make the Solo solver rather more advanced +than most of them.) + +There are several different ways to apply a solver to the problem of +generating a soluble puzzle. I list a few of them below. + +The simplest approach is brute force: randomly generate a puzzle, use +the solver to see if it's soluble, and if not, throw it away and try +again until you get lucky. This is often a viable technique if all +else fails, but it tends not to scale well: for many puzzle types, the +probability of finding a uniquely soluble instance decreases sharply +as puzzle size goes up, so this technique might work reasonably fast +for small puzzles but take (almost) forever at larger sizes. Still, if +there's no other alternative it can be usable: Pattern and Dominosa +both use this technique. (However, Dominosa has a means of tweaking the +randomly generated grids to increase the _probability_ of them being +soluble, by ruling out one of the most common ambiguous cases. This +improved generation speed by over a factor of 10 on the highest preset!) + +An approach which can be more scalable involves generating a grid and +then tweaking it to make it soluble. This is the technique used by Mines +and also by Net: first a random puzzle is generated, and then the solver +is run to see how far it gets. Sometimes the solver will get stuck; +when that happens, examine the area it's having trouble with, and make +a small random change in that area to allow it to make more progress. +Continue solving (possibly even without restarting the solver), tweaking +as necessary, until the solver finishes. Then restart the solver from +the beginning to ensure that the tweaks haven't caused new problems in +the process of solving old ones (which can sometimes happen). + +This strategy works well in situations where the usual solver failure +mode is to get stuck in an easily localised spot. Thus it works well +for Net and Mines, whose most common failure mode tends to be that most +of the grid is fine but there are a few widely separated ambiguous +sections; but it would work less well for Dominosa, in which the way you +get stuck is to have scoured the whole grid and not found anything you +can deduce _anywhere_. Also, it relies on there being a low probability +that tweaking the grid introduces a new problem at the same time as +solving the old one; Mines and Net also have the property that most of +their deductions are local, so that it's very unlikely for a tweak to +affect something half way across the grid from the location where it was +applied. In Dominosa, by contrast, a lot of deductions use information +about half the grid (`out of all the sixes, only one is next to a +three', which can depend on the values of up to 32 of the 56 squares in +the default setting!), so this tweaking strategy would be rather less +likely to work well. + +A more specialised strategy is that used in Solo and Slant. These +puzzles have the property that they derive their difficulty from not +presenting all the available clues. (In Solo's case, if all the possible +clues were provided then the puzzle would already be solved; in Slant +it would still require user action to fill in the lines, but it would +present no challenge at all). Therefore, a simple generation technique +is to leave the decision of which clues to provide until the last +minute. In other words, first generate a random _filled_ grid with all +possible clues present, and then gradually remove clues for as long as +the solver reports that it's still soluble. Unlike the methods described +above, this technique _cannot_ fail - once you've got a filled grid, +nothing can stop you from being able to convert it into a viable puzzle. +However, it wouldn't even be meaningful to apply this technique to (say) +Pattern, in which clues can never be left out, so the only way to affect +the set of clues is by altering the solution. + +(Unfortunately, Solo is complicated by the need to provide puzzles at +varying difficulty levels. It's easy enough to generate a puzzle of +_at most_ a given level of difficulty; you just have a solver with +configurable intelligence, and you set it to a given level and apply the +above technique, thus guaranteeing that the resulting grid is solvable +by someone with at most that much intelligence. However, generating a +puzzle of _at least_ a given level of difficulty is rather harder; if +you go for _at most_ Intermediate level, you're likely to find that +you've accidentally generated a Trivial grid a lot of the time, because +removing just one number is sufficient to take the puzzle from Trivial +straight to Ambiguous. In that situation Solo has no remaining options +but to throw the puzzle away and start again.) + +A final strategy is to use the solver _during_ puzzle construction: +lay out a bit of the grid, run the solver to see what it allows you to +deduce, and then lay out a bit more to allow the solver to make more +progress. There are articles on the web that recommend constructing +Sudoku puzzles by this method (which is completely the opposite way +round to how Solo does it); for Sudoku it has the advantage that you +get to specify your clue squares in advance (so you can have them make +pretty patterns). + +Rectangles uses a strategy along these lines. First it generates a grid +by placing the actual rectangles; then it has to decide where in each +rectangle to place a number. It uses a solver to help it place the +numbers in such a way as to ensure a unique solution. It does this by +means of running a test solver, but it runs the solver _before_ it's +placed any of the numbers - which means the solver must be capable of +coping with uncertainty about exactly where the numbers are! It runs +the solver as far as it can until it gets stuck; then it narrows down +the possible positions of a number in order to allow the solver to make +more progress, and so on. Most of the time this process terminates with +the grid fully solved, at which point any remaining number-placement +decisions can be made at random from the options not so far ruled out. +Note that unlike the Net/Mines tweaking strategy described above, this +algorithm does not require a checking run after it completes: if it +finishes successfully at all, then it has definitely produced a uniquely +soluble puzzle. + +Most of the strategies described above are not 100% reliable. Each +one has a failure rate: every so often it has to throw out the whole +grid and generate a fresh one from scratch. (Solo's strategy would +be the exception, if it weren't for the need to provide configurable +difficulty levels.) Occasional failures are not a fundamental problem in +this sort of work, however: it's just a question of dividing the grid +generation time by the success rate (if it takes 10ms to generate a +candidate grid and 1/5 of them work, then it will take 50ms on average +to generate a viable one), and seeing whether the expected time taken +to _successfully_ generate a puzzle is unacceptably slow. Dominosa's +generator has a very low success rate (about 1 out of 20 candidate grids +turn out to be usable, and if you think _that's_ bad then go and look +at the source code and find the comment showing what the figures were +before the generation-time tweaks!), but the generator itself is very +fast so this doesn't matter. Rectangles has a slower generator, but +fails well under 50% of the time. + +So don't be discouraged if you have an algorithm that doesn't always +work: if it _nearly_ always works, that's probably good enough. The one +place where reliability is important is that your algorithm must never +produce false positives: it must not claim a puzzle is soluble when it +isn't. It can produce false negatives (failing to notice that a puzzle +is soluble), and it can fail to generate a puzzle at all, provided it +doesn't do either so often as to become slow. + +One last piece of advice: for grid-based puzzles, when writing and +testing your generation algorithm, it's almost always a good idea _not_ +to test it initially on a grid that's square (i.e. w==h), because if the +grid is square then you won't notice if you mistakenly write `h' instead +of `w' (or vice versa) somewhere in the code. Use a rectangular grid for +testing, and any size of grid will be likely to work after that. + +#6.2.2. Designing textual description formats + +Another aspect of writing a puzzle which is worth putting some thought +into is the design of the various text description formats: the format +of the game parameter encoding, the game description encoding, and the +move encoding. + +The first two of these should be reasonably intuitive for a user to type +in; so provide some flexibility where possible. Suppose, for example, +your parameter format consists of two numbers separated by an `x' to +specify the grid dimensions (`10x10' or `20x15'), and then has some +suffixes to specify other aspects of the game type. It's almost always a +good idea in this situation to arrange that decode_params() can handle +the suffixes appearing in any order, even if encode_params() only ever +generates them in one order. + +These formats will also be expected to be reasonably stable: users will +expect to be able to exchange game IDs with other users who aren't +running exactly the same version of your game. So make them robust and +stable: don't build too many assumptions into the game ID format which +will have to be changed every time something subtle changes in the +puzzle code. + +#6.3. Common how-to questions + +This section lists some common things people want to do when writing a +puzzle, and describes how to achieve them within the Puzzles framework. + +#6.3.1. Drawing objects at only one position + +A common phenomenon is to have an object described in the `game_state' +or the `game_ui' which can only be at one position. A cursor - probably +specified in the `game_ui' - is a good example. + +In the `game_ui', it would _obviously_ be silly to have an array +covering the whole game grid with a boolean flag stating whether the +cursor was at each position. Doing that would waste space, would make +it difficult to find the cursor in order to do anything with it, and +would introduce the potential for synchronisation bugs in which you +ended up with two cursors or none. The obviously sensible way to store a +cursor in the `game_ui' is to have fields directly encoding the cursor's +coordinates. + +However, it is a mistake to assume that the same logic applies to the +`game_drawstate'. If you replicate the cursor position fields in the +draw state, the redraw code will get very complicated. In the draw +state, in fact, it _is_ probably the right thing to have a cursor flag +for every position in the grid. You probably have an array for the whole +grid in the drawstate already (stating what is currently displayed in +the window at each position); the sensible approach is to add a `cursor' +flag to each element of that array. Then the main redraw loop will look +something like this (pseudo-code): + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + int value = state->symbol_at_position[y][x]; + if (x == ui->cursor_x && y == ui->cursor_y) + value |= CURSOR; + if (ds->symbol_at_position[y][x] != value) { + symbol_drawing_subroutine(dr, ds, x, y, value); + ds->symbol_at_position[y][x] = value; + } + } + } + +This loop is very simple, pretty hard to get wrong, and _automatically_ +deals both with erasing the previous cursor and drawing the new one, +with no special case code required. + +This type of loop is generally a sensible way to write a redraw +function, in fact. The best thing is to ensure that the information +stored in the draw state for each position tells you _everything_ about +what was drawn there. A good way to ensure that is to pass precisely +the same information, and _only_ that information, to a subroutine that +does the actual drawing; then you know there's no additional information +which affects the drawing but which you don't notice changes in. + +#6.3.2. Implementing a keyboard-controlled cursor + +It is often useful to provide a keyboard control method in a basically +mouse-controlled game. A keyboard-controlled cursor is best implemented +by storing its location in the `game_ui' (since if it were in the +`game_state' then the user would have to separately undo every cursor +move operation). So the procedure would be: + + - Put cursor position fields in the `game_ui'. + + - interpret_move() responds to arrow keys by modifying the cursor + position fields and returning "". + + - interpret_move() responds to some sort of fire button by actually + performing a move based on the current cursor location. + + - You might want an additional `game_ui' field stating whether the + cursor is currently visible, and having it disappear when a mouse + action occurs (so that it doesn't clutter the display when not + actually in use). + + - You might also want to automatically hide the cursor in + changed_state() when the current game state changes to one in + which there is no move to make (which is the case in some types of + completed game). + + - redraw() draws the cursor using the technique described in section + 6.3.1. + +#6.3.3. Implementing draggable sprites + +Some games have a user interface which involves dragging some sort of +game element around using the mouse. If you need to show a graphic +moving smoothly over the top of other graphics, use a blitter (see +section 3.1.13 for the blitter API) to save the background underneath +it. The typical scenario goes: + + - Have a blitter field in the `game_drawstate'. + + - Set the blitter field to NULL in the game's new_drawstate() + function, since you don't yet know how big the piece of saved + background needs to be. + + - In the game's set_size() function, once you know the size of the + object you'll be dragging around the display and hence the required + size of the blitter, actually allocate the blitter. + + - In free_drawstate(), free the blitter if it's not NULL. + + - In interpret_move(), respond to mouse-down and mouse-drag events by + updating some fields in the game_ui which indicate that a drag is in + progress. + + - At the _very end_ of redraw(), after all other drawing has been + done, draw the moving object if there is one. First save the + background under the object in the blitter; then set a clip + rectangle covering precisely the area you just saved (just in case + anti-aliasing or some other error causes your drawing to go beyond + the area you saved). Then draw the object, and call unclip(). + Finally, set a flag in the game_drawstate that indicates that the + blitter needs restoring. + + - At the very start of redraw(), before doing anything else at all, + check the flag in the game_drawstate, and if it says the blitter + needs restoring then restore it. (Then clear the flag, so that this + won't happen again in the next redraw if no moving object is drawn + this time.) + +This way, you will be able to write the rest of the redraw function +completely ignoring the dragged object, as if it were floating above +your bitmap and being completely separate. + +#6.3.4. Sharing large invariant data between all game states + +In some puzzles, there is a large amount of data which never changes +between game states. The array of numbers in Dominosa is a good example. + +You _could_ dynamically allocate a copy of that array in every +`game_state', and have dup_game() make a fresh copy of it for every new +`game_state'; but it would waste memory and time. A more efficient way +is to use a reference-counted structure. + + - Define a structure type containing the data in question, and also + containing an integer reference count. + + - Have a field in `game_state' which is a pointer to this structure. + + - In new_game(), when creating a fresh game state at the start of a + new game, create an instance of this structure, initialise it with + the invariant data, and set its reference count to 1. + + - In dup_game(), rather than making a copy of the structure for the + new game state, simply set the new game state to point at the same + copy of the structure, and increment its reference count. + + - In free_game(), decrement the reference count in the structure + pointed to by the game state; if the count reaches zero, free the + structure. + +This way, the invariant data will persist for only as long as it's +genuinely needed; _as soon_ as the last game state for a particular +puzzle instance is freed, the invariant data for that puzzle will +vanish as well. Reference counting is a very efficient form of garbage +collection, when it works at all. (Which it does in this instance, of +course, because there's no possibility of circular references.) + +#6.3.5. Implementing multiple types of flash + +In some games you need to flash in more than one different way. Mines, +for example, flashes white when you win, and flashes red when you tread +on a mine and die. + +The simple way to do this is: + + - Have a field in the `game_ui' which describes the type of flash. + + - In flash_length(), examine the old and new game states to decide + whether a flash is required and what type. Write the type of flash + to the `game_ui' field whenever you return non-zero. + + - In redraw(), when you detect that `flash_time' is non-zero, examine + the field in `game_ui' to decide which type of flash to draw. + +redraw() will never be called with `flash_time' non-zero unless +flash_length() was first called to tell the mid-end that a flash was +required; so whenever redraw() notices that `flash_time' is non-zero, +you can be sure that the field in `game_ui' is correctly set. + +#6.3.6. Animating game moves + +A number of puzzle types benefit from a quick animation of each move you +make. + +For some games, such as Fifteen, this is particularly easy. Whenever +redraw() is called with `oldstate' non-NULL, Fifteen simply compares the +position of each tile in the two game states, and if the tile is not in +the same place then it draws it some fraction of the way from its old +position to its new position. This method copes automatically with undo. + +Other games are less obvious. In Sixteen, for example, you can't just +draw each tile a fraction of the way from its old to its new position: +if you did that, the end tile would zip very rapidly past all the others +to get to the other end and that would look silly. (Worse, it would look +inconsistent if the end tile was drawn on top going one way and on the +bottom going the other way.) + +A useful trick here is to define a field or two in the game state that +indicates what the last move was. + + - Add a `last move' field to the `game_state' (or two or more fields + if the move is complex enough to need them). + + - new_game() initialises this field to a null value for a new game + state. + + - execute_move() sets up the field to reflect the move it just + performed. + + - redraw() now needs to examine its `dir' parameter. If `dir' is + positive, it determines the move being animated by looking at the + last-move field in `newstate'; but if `dir' is negative, it has to + look at the last-move field in `oldstate', and invert whatever move + it finds there. + +Note also that Sixteen needs to store the _direction_ of the move, +because you can't quite determine it by examining the row or column in +question. You can in almost all cases, but when the row is precisely +two squares long it doesn't work since a move in either direction looks +the same. (You could argue that since moving a 2-element row left and +right has the same effect, it doesn't matter which one you animate; but +in fact it's very disorienting to click the arrow left and find the row +moving right, and almost as bad to undo a move to the right and find the +game animating _another_ move to the right.) + +#6.3.7. Animating drag operations + +In Untangle, moves are made by dragging a node from an old position to a +new position. Therefore, at the time when the move is initially made, it +should not be animated, because the node has already been dragged to the +right place and doesn't need moving there. However, it's nice to animate +the same move if it's later undone or redone. This requires a bit of +fiddling. + +The obvious approach is to have a flag in the `game_ui' which inhibits +move animation, and to set that flag in interpret_move(). The question +is, when would the flag be reset again? The obvious place to do so +is changed_state(), which will be called once per move. But it will +be called _before_ anim_length(), so if it resets the flag then +anim_length() will never see the flag set at all. + +The solution is to have _two_ flags in a queue. + + - Define two flags in `game_ui'; let's call them `current' and `next'. + + - Set both to FALSE in `new_ui()'. + + - When a drag operation completes in interpret_move(), set the `next' + flag to TRUE. + + - Every time changed_state() is called, set the value of `current' to + the value in `next', and then set the value of `next' to FALSE. + + - That way, `current' will be TRUE _after_ a call to changed_state() + if and only if that call to changed_state() was the result of a + drag operation processed by interpret_move(). Any other call to + changed_state(), due to an Undo or a Redo or a Restart or a Solve, + will leave `current' FALSE. + + - So now anim_length() can request a move animation if and only if the + `current' flag is _not_ set. + +#6.3.8. Inhibiting the victory flash when Solve is used + +Many games flash when you complete them, as a visual congratulation for +having got to the end of the puzzle. It often seems like a good idea to +disable that flash when the puzzle is brought to a solved state by means +of the Solve operation. + +This is easily done: + + - Add a `cheated' flag to the `game_state'. + + - Set this flag to FALSE in new_game(). + + - Have solve() return a move description string which clearly + identifies the move as a solve operation. + + - Have execute_move() respond to that clear identification by setting + the `cheated' flag in the returned `game_state'. The flag will + then be propagated to all subsequent game states, even if the user + continues fiddling with the game after it is solved. + + - flash_length() now returns non-zero if `oldstate' is not completed + and `newstate' is, _and_ neither state has the `cheated' flag set. + +#6.4. Things to test once your puzzle is written + +Puzzle implementations written in this framework are self-testing as far +as I could make them. + +Textual game and move descriptions, for example, are generated and +parsed as part of the normal process of play. Therefore, if you can make +moves in the game _at all_ you can be reasonably confident that the +mid-end serialisation interface will function correctly and you will +be able to save your game. (By contrast, if I'd stuck with a single +make_move() function performing the jobs of both interpret_move() and +execute_move(), and had separate functions to encode and decode a game +state in string form, then those functions would not be used during +normal play; so they could have been completely broken, and you'd never +know it until you tried to save the game - which would have meant you'd +have to test game saving _extensively_ and make sure to test every +possible type of game state. As an added bonus, doing it the way I did +leads to smaller save files.) + +There is one exception to this, which is the string encoding of the +`game_ui'. Most games do not store anything permanent in the `game_ui', +and hence do not need to put anything in its encode and decode +functions; but if there is anything in there, you do need to test game +loading and saving to ensure those functions work properly. + +It's also worth testing undo and redo of all operations, to ensure that +the redraw and the animations (if any) work properly. Failing to animate +undo properly seems to be a common error. + +Other than that, just use your common sense. + |