From 787cfc9603b6fadab6cf2c36a5609ae369022c4f Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 24 May 2004 13:07:28 +0000 Subject: [PATCH] Added the beginnings of a hacking guide. git-svn-id: svn://svn.tartarus.org/sgt/puzzles@4255 cda61777-01e9-0310-a592-d414129be87e --- HACKING.but | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 HACKING.but diff --git a/HACKING.but b/HACKING.but new file mode 100644 index 0000000..f6c93f0 --- /dev/null +++ b/HACKING.but @@ -0,0 +1,172 @@ +\cfg{text-indent}{0} +\cfg{text-width}{72} +\cfg{text-title-align}{left} +\cfg{text-chapter-align}{left} +\cfg{text-chapter-numeric}{true} +\cfg{text-chapter-suffix}{. } +\cfg{text-chapter-underline}{-} +\cfg{text-section-align}{0}{left} +\cfg{text-section-numeric}{0}{true} +\cfg{text-section-suffix}{0}{. } +\cfg{text-section-underline}{0}{-} +\cfg{text-section-align}{1}{left} +\cfg{text-section-numeric}{1}{true} +\cfg{text-section-suffix}{1}{. } +\cfg{text-section-underline}{1}{-} +\cfg{text-versionid}{0} + +\title Hacking guide for Simon Tatham's puzzle collection + +\C{newpuz} Guide to writing a new puzzle + +Start by copying \cw{nullgame.c}. This contains all the function +definitions and stubs that should be necessary to at least compile. +Some things are fine as they are unless you do something that +requires a change (for example, \cw{dup_params()} can usually be +left as it is since game parameters often don't have any +variable-size elements that need to be dynamically allocated); other +things are sure to need changing (for example, the params structure +is likely to need to contain at least one actual variable). Anything +marked \q{FIXME} really needs changing before you have a working +game. + +\e{DO NOT EDIT THE MAKEFILES.} Edit \c{Recipe} instead, and then +re-run \cw{mkfiles.pl}. The individual makefiles are automatically +generated by this mechanism, so editing them directly will not +produce a usable patch. + +\H{newpuz-arch} General architecture tips + +Think carefully about which data structures need to contain which +parts of the game information. + +\b \c{game_state} should contain everything that holds the current +state of play in a specific game. The mid-end maintains one of these +for every move the player has made, and moves back and forwards +along the list when you use Undo and Redo. So anything you would +expect to have restored when you undo needs to go in this state. + +\b \c{game_params} should contain parameters the user can set before +generating a new game. For example, if the game is played on a grid +of variable size, \cw{game_params} contains the grid size. +(\cw{game_state} will \e{also} need to contain the grid size. You +might even wish to have \cw{game_state} contain a \cw{game_params} +member.) + +\b \c{game_ui} contains aspects of the game's user interface which +are not expected to be restored in an undo operation. For example, +if you have a basically mouse-clicky sort of game (such as Net) but +you want to provide a cursor which can be moved with the arrow keys, +then putting the location of the cursor in \c{game_ui} is +reasonable. Or if the game allows you to drag things around the +display, then the current state of dragging is something that can go +in \c{game_ui}. Simple games don't need a \cw{game_ui} structure at +all. + +\b \c{game_drawstate} contains things you know about the current +state of the game's display. For example, if your display is made up +of tiles and you want to redraw as few as possible, you might want +to have \c{game_drawstate} contain a description of the last tile +you drew at every position, so that you can compare it to the new +tile and avoid redrawing tiles that haven't changed. + +\H{newpuz-seed} Designing a game seed + +The game seed is the part of the game ID (what you type in when you +select \q{Game -> Specific}) which comes \e{after} the colon. It +should uniquely specify the starting state of a game, given a set of +game parameters (which are encoded separately, before the colon). + +Try to imagine all the things a user might want to use the game seed +for, and build as much capability into it as possible. + +For a start, if it's feasible for the game seed to \e{directly} +encode the starting position, it should simply do so. This is a +better approach than encoding a random number seed which is used to +randomly generate the game in \cw{new_game()}, because it allows the +user to make up their own game seeds. This property is particularly +useful if the puzzle is an implementation of a well-known game, in +which case existing instances of the puzzle might be available which +a user might want to transcribe into game seeds in order to play +them conveniently. I recommend this technique wherever you can +sensibly use it: \cw{new_game_seed()} should do all the real +thinking about creating a game seed, and \cw{new_game()} should +restrict itself to simply parsing the text description it returns. + +However, sometimes this is genuinely not feasible; Net, for example, +uses the random-number seed approach, because I decided the full +state of even a moderately large Net game is just too big to be +sensibly cut-and-pasted by users. However, even the Net seeds have a +useful property. The order of grid generation in Net is: + +\b First the game sets up a valid completed Net grid. + +\b Then it makes a list of every edge with no connection across it. +These edges are eligible to become barriers. + +\b Then the grid is shuffled by randomly rotating every tile. + +\b Then the game multiplies the number of barrier-candidate edges by +the barrier probability in order to decide how many barriers to +create. + +\b Finally, it picks that many edges out of the barrier candidate +list, removing each edge from the list as it selects it. + +The effect of this is that the actual barrier locations are chosen +\e{last}, which means that if you change the barrier rate and then +enter the same random number seed, \e{only} the barriers change. +Furthermore, if you do this, the barrier sets will be nested (i.e. +the version with more barriers will contain every barrier from the +one with fewer), so that selecting 10 barriers and then 20 barriers +will not give a user 30 pieces of information, only 20. + +\H{newpuz-redraw} Designing a drawing routine + +Front end implementations are required to remember all data drawn by +the game. That is, a game redraw routine MUST never be called simply +because part of the game window was briefly obscured; the front end +is required to remember what the game last drew in that area of the +window, and redraw it itself without bothering the game module. + +Many games will need some form of animation when transferring +between one \cw{game_state} and the next. This is achieved by having +\cw{game_anim_length()} analyse two adjacent game states, decide how +long the linking animation between them should last, and return this +duration in seconds. Then \cw{game_redraw()} will be passed the two +game states plus an indication of how far through the animation it +is, and can do its drawing appropriately. + +\e{Be aware that you will be required to animate on undo}. If you +are at game state A and the user makes a move creating game state B, +then your redraw function will be passed both states A and B, in +that order, and will be expected to animate between them if your +game needs animation. However, if the user then hits Undo, your +redraw function will be passed states B and A, in \e{that} order, +and will be expected to perform the reverse animation. + +This is easy enough for some games. In Fifteen, for example, it's +simply a matter of examining the two game states to work out what +has changed between them, and drawing each tile some proportion of +the way between its starting and finishing positions. + +In Sixteen, things are more difficult. You could examine the grid to +work out which tiles had been moved and decide which way they had +been moved, but this would be disconcerting to the user in some +cases. In a 2xN game of Sixteen, rotating a two-tile row left or +right has the same end result but should look different during the +enimation; so the Sixteen \cw{game_state} in fact stores an extra +piece of information giving the direction of the last move. So when +making a normal move, \cw{game_redraw()} can know which way round it +is expected to animate a two-tile rotation. + +However, even this doesn't fix the undo case. When +\cw{game_redraw()} is passed a pair of game states in the right +chronological order, the second one contains the direction field +which corresponds to the actual difference between the states. +However, when it is passed a pair of states in the opposite order +due to an undo, it should be looking in the \e{first} one to find +the direction field. Sixteen solves this by also storing the current +move count in the game state, so that \cw{game_redraw()} can compare +the two move counts to work out whether it's drawing an undo or not, +and look in the right place for the direction field. -- 2.11.0