mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Shiny new developer documentation to replace the old sketchy HACKING
guide. [originally from svn r6142]
This commit is contained in:
182
HACKING.but
182
HACKING.but
@ -1,182 +0,0 @@
|
|||||||
\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-params} Notes on parameters
|
|
||||||
|
|
||||||
You need to define a textual format for the game parameters (the part
|
|
||||||
before the \q{:} or \q{#} in descriptive and random IDs respectively).
|
|
||||||
|
|
||||||
The per-game parameter encoding function \cw{encode_params()} is
|
|
||||||
passed an argument \c{full}. This serves two purposes:
|
|
||||||
|
|
||||||
\b You can suppress inclusion of parameters that only affect game
|
|
||||||
generation, and thus would have no effect in a descriptive ID, in the
|
|
||||||
ID displayed by \q{Game -> Specific} if \c{full} is \cw{FALSE}.
|
|
||||||
|
|
||||||
\b You can ensure that a particular parameter entered as part of a
|
|
||||||
game ID does not persist when a new puzzle is generated, for instance
|
|
||||||
if you think that a player would not want it to persist beyond a
|
|
||||||
single game. An example is the \q{expansion factor} in Rectangles.
|
|
||||||
|
|
||||||
When generating a new puzzle instance, give some thought to the order
|
|
||||||
in which parameters are processed. For example, 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-descid} Notes on descriptive IDs
|
|
||||||
|
|
||||||
The descriptive game ID is the part that comes after the colon in the
|
|
||||||
ID accessed through \q{Game -> Specific}. It does not need to be
|
|
||||||
especially concise, but it should be designed to remain compatible
|
|
||||||
with new versions of the puzzle.
|
|
||||||
|
|
||||||
Try to imagine all the things a user might want to use the descriptive
|
|
||||||
ID for, and build as much capability into it as possible. At a minimum,
|
|
||||||
you need to be able to generate one of these given a random number
|
|
||||||
source; however, if auto-generation capability is limited, give some
|
|
||||||
thought to the possibility of a user making up their own descriptive
|
|
||||||
IDs. 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.
|
|
||||||
|
|
||||||
\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.
|
|
||||||
|
|
||||||
For this reason, in the redraw functions you are provided with an
|
|
||||||
extra argument \c{dir} which tells you which state was chronologically
|
|
||||||
first; \c{dir} is +1 for a normal move and -1 for an undo.
|
|
@ -3,5 +3,5 @@ all: puzzles.hlp puzzles.txt HACKING
|
|||||||
puzzles.hlp puzzles.txt: puzzles.but
|
puzzles.hlp puzzles.txt: puzzles.but
|
||||||
halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but
|
halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but
|
||||||
|
|
||||||
HACKING: HACKING.but
|
HACKING: devel.but
|
||||||
halibut --text=HACKING HACKING.but
|
halibut --text=HACKING devel.but
|
||||||
|
Reference in New Issue
Block a user