
AGES - Adventure Gamebook Express Solver
========================================
version 2 (c) 2011 by Jim Grimwood

AGES is a set of four programs (written in ZX Spectrum BASIC + Z80
assembler) for generating solutions to adventure gamebooks such as the
Fighting Fantasy series. The four programs are:

* AGES-1 forward connections entry - where the details of locations & exits
  are entered from the book, and which generates a file of exit points
  from each location.
* AGES-2 reverse connections setup - which takes an AGES-1 file as input,
  and generates a file of entry points to each location.
* AGES-3 solution generator - which takes the AGES-1&2 files as input, and
  generates a solution file.
* AGES-4 change game parameters - which allows the book title, base file
  name and maximum number of locations to be changed and makes the necessary
  changes to the header record.

Other files in this ZIP archive are:

* AGESprogv2.TAP - the four AGES programs.
* FF01-WarlockOfFiretopMountain.TAP - sample data files for Warlock of Firetop Mountain.
* AGES-1/2/3/4v2.BAS - BASIC listings for the four AGES programs.
* AGES-1/2/3/4v2.ASM & AGES-0v2-shared.ASM - annotated assembler listings.
* FF01_F/B/S.TXT - printouts of sample data files from AGES-1/2/3.
* AGESv2-notes.TXT - program notes.


Programming notes
=================

Memory is allocated thus:
                                b0            b1            b2            mw
AGES-1: PROG|m/c|VARS|................|RAMTOP|n$ conds data|i$ exits data|work|P-RAMT
AGES-2: PROG|m/c|VARS|................|RAMTOP|y$ entry data|i$ exits data|work|P-RAMT
AGES-3: PROG|m/c|VARS|..|RAMTOP|y$ entry data|n$ conds data|i$ exits data|work|P-RAMT
AGES-4: PROG|m/c|VARS|..............................|RAMTOP|i$ exits data|work|P-RAMT

The eight main arrays + two data blocks used by the programs are:
* t$(48)        Header                                     48
* i$(500,6)     Location exits data index (forward)      3000
                data area (500 * avg.3 * 3)              4500
* y$(500,3)     Location entry data index (backward)     1500
                data area (500 * avg.3 * 3)              4500 = 13548

* c$(300,28)    Conditions: actions & descriptions       8400 (1)
* c$(300,9)     Conditions: actions                      2700 (3)
  s$(128,21)                descriptions                 2688 (3)

* n$(128,3)     Conditions & quantities data index        384
                data area (100 * avg.3 * 5)              1500
* g$(2000)      Conditions counters                      2000
* r$(128)       g$() workspace                            128 =  4012
                                                        -----
                                                        25960 bytes (1)
                                                        22948 bytes (3)
Other arrays are used for temporary workspace (see list at end of file).

HEADER ARRAY - t$()

* title of book                 length 32
* base name for saved files             8
* length of exits data area             2
* entered locations                     2
* maximum locations                     2
* length of conditions data area        2

AGES-3 won't run Update if (entered locations) < (maximum locations).

EXITS INDEX ARRAY - i$()

Each entry is indexed by a number 'n':
(1,2) nth location's start address (lo,hi) for exits info
(3)   nth location's length of exits info
(4)   nth location's From -> Goto conditions n$() data key
(5,6) nth location in solution

The exits info is a string of 3-byte entries (max.20 per location),
consisting of:
* exit number (lo,hi) from the indexed location
* hit count for that location+exit combination
The hit count is the number of times that location+exit combination has
been traversed while evaluating the solution.

CONDITIONS ARRAY - c$()

There are two versions of this array: a full size one used by AGES-1 and a
compact version created by AGES-2 for use by AGES-3.

Each row specifies a location containing a conditional exit and where the
exit condition is set. The fields for AGES-1 are:
From -  (2) (hi.lo) the number of the entry location
Goto -  (2) (hi.lo) the number of the conditional exit location
Reqs -  (2) (hi.lo) the number of the location where the required condition is set
Desc - (19) a description of the condition (an item found or action taken)
Code -  (1) description code (distinct for each different description)
            generated automatically by the program
Type -  (1) 'gt'  = value group 'g' of unit type 't'
            01-09=unit; 11-19=object valued 0-25; 21-29=object valued 26-50;
            31-39=object valued 51-75; etc... 91-99=key item; 00=anything else;
            +100 if an object's value has to be deducted from currency when taken
            (ie. if an object has to be purchased)
            10t-10t for unit 't' change applied to Initial rather than Current value
            20t-25t for unit 't' conditional calculation entries
Value-  (1) 'vvu' = value 'vv' of an item in type 'u' units
            'vvv' = value 'vvv' of a generic unit entry (types 01-09)
            +100 if value has to be relative to Initial rather than Current value

For example, in Warlock of Firetop Mountain, exit from location 287 to
location 32 requires the player to have some cheese, which is found at
location 317, so the entry would read: 287 / 32 / 317 / cheese / 13 / 0.
Any of the three location numbers may be left as zero, if their value is
not known initially or they are not to be considered in the solution;
AGES-3 will ignore entries which are incomplete.

For the gold ingot example below, Type = 23 and Value = 31; thus:
g=2, t=3, vv=3, u=1 equates to:
value group = 2 (26-50), unit type = 3 (non-magic item),
value = (g-1)*25 + vv = (2-1)*25 + 3 = 28 of unit type 1 (GP)
Unit type numbers are assigned alphabetically for each distinct 2-character
description in CAPS, thus the example below has three: GP, IM, IT. These two-
character codes can be assigned any preferred values, with the proviso that
inventory items (ie. excluding currency & physical statistic units) must use
codes beginning with "I".

The fields for AGES-3 are separated into arrays c$() & s$(), to save unused
space in the description fields. The fields for AGES-3 are:

c$() actions
From -  (2) (hi.lo) the number of the entry location
Goto -  (2) (hi.lo) the number of the conditional exit location
Reqs -  (2) (hi.lo) the number of the location where the required condition is set
Code -  (1) (lo.hi) description code (s$() row number)
Type -  (1) 'gt'  = value group 'g' of unit type 't'
Value-  (1) 'vu' = value 'v' of an item in type 'u' units
            'vv' = value 'vv' of a generic unit entry (types 01-09)

s$() descriptions
Desc - (19) a description of the condition (one row per distinct description)
Rowc -  (2) c$() row number for the initial entry using this description

If a specific item has to be dropped or a condition cancelled then Reqs
should be entered as the source location and Value as -1. Items which do
not set any key condition should be entered with From=Goto=Reqs being the
item's location.

Some shortcuts when entering condition descriptions:
* ENTER keeps the current description
* x clears the line
* z keeps the line unchanged

Description prefixes:
* ! combat option;             eg. !Orc Chieftain
* * magic spell;               eg. *Levitation
* + weapon;                    eg. +branch
* ? action status;             eg. ?barrel not searched
* " word or phrase             eg. "Kelnor

Conditional calculation entries:
* # check roll of dice;        eg.
  #>=1,0,356    roll 1d6; return 1st value in list which is >= roll
* $ assign value to unit Type; eg.
  $=1,6,n       let unit Type n = 1d6+6
  $+1,6,n       let unit Type n = unit Type n + 1d6+6
  $*0,2,5-1,7+4 let unit 4 = unit 4 - 1, unit 7 = unit 7+4
* % check  value of unit Type; eg.
  %>=2,0,47     roll 2d6; if  unit4 and unit7 >= 2d6 return 1 else return 0
Conditional operators may be: < = > <= >= <>
Assignment  operators may be: - = + *

* '+Reqs' where Type<10 indicates gain 'Reqs' of unit 'Type'
* '-Reqs' where Type<10 indicates lose 'Reqs' of unit 'Type'
  'Reqs'=-128 indicates lose all of unit 'Type'
  'Goto' exit not allowed if 'Reqs' > quantity of unit 'Type'
Negative numbers are held as n+1000 in 2-byte fields and 256-n in 1-byte fields.

For example:
FromGotoReqsCodeTypeValuDescription
  1   1   1   .   4   0 $=1,6,4              Luck initial value (variable)
  1   1   1   . 220   1 %>=2,0,4             1.22.4 Luck check expression
  2  16   1   . 220  -1 LU                   1.22   exit if false (Unlucky)
  2 269   1   . 221  -1 LU                   1.22   exit if true (Lucky)
  1   1   1   .   5  10 PR                   Provisions initial value (fixed)
  1   1   1   . 210   2 $*0,2,5-1,7+4        1.21.5 Provisions update expression
  1   1   1   . 215  -1 PR                   1.21   Provisions update PR-1
  1   1   1   . 217   4 ST                   1.21   Provisions update ST+4
 58  15   1   . 210  -1 PR                   1.21.0 Provisions update action
 47  47  47   . 200   2 #>=1,0,56            47.20.0 roll check expression
 47 298  47   . 200   5 #@                   47.20.0 exit if roll comparison is 5
 47 158  47   . 200   6 #@                   47.20.0 exit if roll comparison is 6

Each distinct formula for a conditional calculation (prefixes "#$%") should
be entered just once with a distinct Type code, either with From=Goto=1, or
else using the location number at which they first occur (eg. 47 above). Entries
reference a formula by using the same Type code and prefix, in the range 200-255.

Some more examples:
FromGotoReqsCodeTypeValuDescription
  1   1   1   .   1  30 GP                   gain 30 type 1 Gold Pieces
 12  12  12   .   3  -2 IT                   lose  2 type 3 Item - ordinary
 14  14  14   .   2  -1 IM                   lose  1 type 2 Item - magic
 14  14  14   .   1  -3 GP                   lose  3 type 1 Gold Pieces
 16 211 261   . 112  21 Anti-Poison Potion   reqs Item - magic (value 2GP)
 24 377   0   .   0   0 !Pygmies             goto 377 combat with Pygmies
 95 173  78   .  12   0 Dust of Levitation   reqs Item - magic (no value)
242 242 242   .  23  31 gold ingot           gain Item - ordinary (value 28GP)
133 374   1   .  10   0 ?gauntlet not worn   reqs Condition
146 146 146   .  13   0 piece of stone       gain Item - ordinary (no value)

Summary of column usage:
From = entry location
Goto = exit  location
Reqs (depending upon content of Type & Desc)
     = location of required key item or expression
  or = From=Goto=Reqs
  or = zero
Code = record key assigned by program
Type = unit type t for inventory counters
  or = item group & type  gt
  or = item group & type 1gt for purchase items
  or = item group & type 2gt for expression checks
  or = item group not referencing inventory g0, 1g0, 2g0
  or = zero
Value= 0vv = unit current quantity modifier
  or = 1vv = unit initial quantity modifier
  or = check value for an expression
  or = vu = unit value v in units u
  or = zero

EXIT CONDITIONS & ACTIONS INDEX ARRAY - n$()

Each entry is indexed by the value held in 'i$(n,4)' where n=From:
(1,2) nth location's start address (lo,hi) for entries
(3)   nth location's length of entries

An exit condition entry consists of:
* Goto location number (lo,hi) from the indexed From location
* Reqs location number (lo,hi) for that Goto number
* c$() row number (lo,hi) for that condition
The required number g$(Goto,1) of Reqs conditions must have been set prior
to or at the From location before the Goto exit may be taken.

An exit action entry consists of:
* Goto location number (lo,hi) from the indexed From location
* Reqs exit action (lo,hi) where values >1000 indicate -ve (value-1000)
* c$() row number (lo,hi) for that condition
The required quantity k(qap,2) must be available if the adjustment is -ve
before the Goto exit may be taken.

CONDITIONS KEYS CROSS-REFERENCE ARRAY - g$()

This variable length row array groups conditions by location, with cross-
references back to c$(). To reference from BASIC the required entries
(from & goto) are found via a m/code routine and copied to the one-row
fixed length r$() array.

From -> Goto conditions counters (only set up for locations with conditions):
Record header (fixed length):
 (2)  1 =  location number
 (1)    =  Goto location's conditions required count (total)
 (1). 4 =  Goto location's conditions required count (selected)
 (1)    =  From location's conditions set      count (total)
 (1). 6 =  From location's conditions set      count (selected)
 (1)    =  From location's conditions reset    count (total)
 (1). 8 =  From location's conditions reset    count (selected)
Record detail (variable length: (maximum 20 2-byte entries of each type)
detail length = CODE r$(3)+CODE r$(5)+CODE r$(7)
(009-048)  list of c$() possible condition keys to check
           derived from n$(From) required at 'Goto'
(049-088)  list of c$() possible condition keys to set
           derived from n$(From) set at = 'Reqs'
(089-128)  list of c$() specific condition keys to reset
           derived from n$(From) reset at = 'Goto' was set at '-Reqs'

When copied to r$() the entry detail fields are each padded out to
length 40 with zeroes.

After entering 'Goto' to 'From': 'n' = r$(From)
* set r$(6) conditions & reset r$(8) conditions
  where r$(51-90) gives list of c$() possible condition keys to set
    and r$(91-130)gives list of c$() specific condition keys to reset
    and m$() gives list of possible additional conditions to reset

Before leaving 'From' to 'Goto': 'n' = r$(Goto)
* check r$(4) conditions are set
  where r$(11-50) gives list of c$() possible condition keys to check
  where m$() gives list of conditions currently set


Machine code address variables
==============================

Shared
------
Variable           Description
--------           -----------
mcpos          mc  adjust absolute addresses
mcstak         ms  stack function parameters
mcputt         mp  put a copy of header array named in t$ into workspace
mcgett         mg  get a copy of header array named in t$ from workspace
mcmove         mm  move a chunk of the exits data
mcupdt         mu  update a range of shifted i$() array entries
mcinit         mi  initialise a string array
mckopy         mk  copy an area of memory
mctrap         mt  error trap
[dataspace]        entry/exit/condition data
mcwk           mw  start  of machine code workspace

AGES-1 additions
----------------
mclen          ml  length of machine code area (including Shared)
mcsort         mo  sort conditions array
mcbuild        mn1 build conditions & quantities cross-ref array
   "           mn2                "

AGES-2 additions
----------------
mcupdt2            update a range of index array rows
mcinity            initialise y$() array
mcupdt3            build y$() backward connections from i$()
mccheckl           check for unconnected locations in y$()

AGES-3 additions
----------------
mctopram       mo  change RAMTOP while preserving variables & GOSUBs
mcback         mcb search backwards from a key point until another key point is found
mcfrwd         mcf trace the forward route between the two key points
mccomp         mcc compress the route between the two key points
mcsave         mcs save the route between the two key points

AGES-4 additions
----------------
none


----------


CONVERSION NOTES

old t$(5,32) aka.x$()
= title + file name + locs.length + locs.ent. + locs.max

new t$(48)
= title + file name + locs.length + locs.ent. + locs.max + cons.length


old i$(locs.max,3) aka.y$()
= loc.start + loc.length
+ i$() data start,length: exit ...

new i$(500,6)
= loc.start + loc.length + n$() key + solution step
+ i$() data 65368,4500  : exit + hits ...


new n$(100,3)
= loc.start + loc.length
+ n$() data 59368,1500: exit + reqs + c$() key ...


--- c$(200,26)
= from + goto + reqs + desc ...


---------- constants (partial list)

kA  = 10
kB	= 12
kC  = 100
kD	= 28
kE  = 128
kF	= 14
kG  = 256
kH  = 16
kK	= 20
kL	= 17
kM  = 1000
kN	= 18
kR  = 32
kT	= 33
kU	= 40
kY	= 25
kZ  = 26

---------- variables (partial list)

back	= bk
cbat	= cb
cdat	= cd
clen	= cl
cons	= co
crow	= cr
edat	= ed
edif	= ef
elen	= el
emax	= em
enum	= en
exit	= ex
from	= fm
gkey	= gk
goto	= go
hitc	= hc
hitk	= hk
hitp	= hp
hits	= hs
hitt	= ht
ilen	= il
irow	= ir
jdat	= jd
jlen	= jl
jpos	= jp
ldif	= lf
lej	= lj
mnot	= mn
mrow	= mr
mset	= me
mtop	= mz
nkey	= nk
nlen	= nl
noex	= nx
npos	= np
nrow	= nr
pnum	= pn
reqs	= rq
setc	= sc
setn	= sn
sets	= se
skip	= sk
skips	= ss
start	= sa
steps	= sz
stj	= sj
stop	= so
zero	= zo

a$(em,2)      b2 location pointers (key:seq) -or- sequence numbers (key:loc)
b$(32)        spaces
c$(300,28) c *conditions: (key:con.sequential) actions & descriptions
c$(300,9)  c3*conditions: (key:con.sequential) actions
d$(cr,1)     *key item exclusion markers (key:con; data:null,"!")
e$            error message
f$(8)         file name
g$(2000)     *Goto conditions keys (key:loc)
h$(em,2)      b0 key item pointers (key:from; data:key item list address)
i$(500,6)  i  exits data index (key:loc/seq; data:address,length,n$key,solution.loc; link:exit,hits)
j$(em)        route markers (key:loc; data:"!","+","-"[,"+","-"...])
k$            unit item codes
l$            temp string
M$(16,6)     *CONDITION MARKERS: (KEY:SEQUENTIAL; DATA:CON,SET.LOC,RESET.LOC)
n$(128,3)  n *conditions & counters data index (key:sequential; data:address,length; link:goto,reqs,con)
o$            prompt selection
p$            main screen options
q$            main screen selection
r$(128)       g$() expanded row workspace
s$(128,21) s *conditions:  (key:sequential; data:description,con) descriptions
t$(48)     t  exits data header
U$(EM)       *KEY ITEM MARKERS (KEY:LOC; DATA:" "," "+CON)
v$(em,2)      route between the two key points (key:seq; data:loc) (uncompressed)
w$(256,2)     route between the two key points (key:seq; data:loc) (compressed)
x$            temp string
y$(500,3)  y  entry data index (key:loc/seq; data:address,length; link:entry,hits)
z$(5)         nulls string

e(28)         error message addresses


** END OF FILE **