librarygames/trek-adventure/TrekAdventure.xtl
Trek adventure's rules, a library: trek-adventure.xtl (a scripted walkthrough), play.xtl (at the terminal) and the web page all use these. "Star Trek: Decaying Orbit": you wake alone on the Enterprise's bridge, its orbit decaying; find the toolkit and the engine coupler, prep the relay in engineering, install the coupler before the turns run out, and deal with the Klingon boarders on the way.
The game is tables, not branches: every line of text is one row of a table, with a key (a room, an event, an ending) and a condition on the state; a room's description or a message is the rows whose key it is and whose condition holds, picked by one mask. The rooms' exits are a 9 by 9 table, the items' places a vector.
The state is one vector: 1 room, 2 turns left, items held 3 phaser, 4 ID card, 5 toolkit, 6 coupler, 7 tribble (1 = carried), 8 relay prepped, 9 boarders aboard, 10 won, 11 dead, 12 the last event (the key of what to say).
The text
ˡt̲ext : Unit -> Box Char
Every line the game prints, numbered as the rooms, items and events refer to them.
ˡt̲ext ← { @ → ( "" ⍝ 300 "*** STAR TREK: DECAYING ORBIT ***" ⍝ 300 "" ⍝ 300 "YOU AWAKEN ALONE ON THE BRIDGE OF THE" ⍝ 300 "U.S.S. ENTERPRISE. RED LIGHTS FLASH." ⍝ 300 "THE SHIP SHUDDERS." ⍝ 300 "" ⍝ 300 "COMPUTER: WARNING - STABLE ORBIT LOST" ⍝ 300 "COMPUTER: ENGINES OFFLINE" ⍝ 300 "COMPUTER: ORBITAL DECAY IN PROGRESS" ⍝ 300 "" ⍝ 300 "RESTORE ENGINE POWER BEFORE THE SHIP" ⍝ 300 "BURNS UP. PICK 8 FOR HELP." ⍝ 300 "" ⍝ 300 "BRIDGE" ⍝ 1 "THE BRIDGE IS DESERTED. THE MAIN" ⍝ 1 "VIEWSCREEN SHOWS A PLANET GROWING" ⍝ 1 "LARGER. A DAMAGED CONSOLE SPARKS." ⍝ 1 "THE TURBOLIFT DOORS STAND OPEN." ⍝ 1 "A PHASER LIES UNDER THE CAPTAIN'S CHAIR." ⍝ 1 if 3=0 "EXITS: 2=TURBOLIFT" ⍝ 1 "TURBOLIFT" ⍝ 2 "THE TURBOLIFT HUMS ON EMERGENCY" ⍝ 2 "POWER. DECK BUTTONS GLOW DIMLY." ⍝ 2 "EXITS: 1=BRIDGE 3=CORRIDOR 6=ENGINEERING" ⍝ 2 "CORRIDOR" ⍝ 3 "A CORRIDOR STRETCHES FORE AND AFT." ⍝ 3 "EMERGENCY LIGHTS PULSE ALONG THE WALLS." ⍝ 3 "EXITS: 2=TURBOLIFT 4=QUARTERS" ⍝ 3 " 5=STORAGE 7=ARMORY" ⍝ 3 " 9=COMPUTER" ⍝ 3 "QUARTERS" ⍝ 4 "CREW QUARTERS ARE IN DISARRAY." ⍝ 4 "A LOCKER HANGS OPEN. PERSONAL EFFECTS" ⍝ 4 "FLOAT IN LOW GRAVITY." ⍝ 4 "A SECURITY ID CARD GLINTS UNDER A BUNK." ⍝ 4 if 4=0 "EXITS: 3=CORRIDOR" ⍝ 4 "STORAGE" ⍝ 5 "CARGO CONTAINERS ARE STRAPPED TO" ⍝ 5 "THE DECK." ⍝ 5 "A TOOLKIT LIES BY A MAINTENANCE CRATE." ⍝ 5 if 5=0 "AN ENGINE COUPLER RESTS ON A SHELF." ⍝ 5 if 6=0 "SOMETHING FURRY SQUEAKS - A TRIBBLE." ⍝ 5 if 7=0 "EXITS: 3=CORRIDOR 8=SHUTTLE" ⍝ 5 "ENGINEERING" ⍝ 6 "ENGINEERING IS HOT AND NOISY." ⍝ 6 "THE WARP SYSTEMS ARE OFFLINE." ⍝ 6 "A PLASMA RELAY PANEL HANGS OPEN." ⍝ 6 if 8=0 "THE RELAY PANEL IS PATCHED BUT NOT" ⍝ 6 if 8=1 "YET ACTIVATED." ⍝ 6 if 8=1 "EXITS: 2=TURBOLIFT" ⍝ 6 "ARMORY" ⍝ 7 "RACKS LINE THE WALLS. MOST WEAPONS" ⍝ 7 "ARE MISSING. A SPARE POWER CELL" ⍝ 7 "SITS IN A CRADLE." ⍝ 7 "EXITS: 3=CORRIDOR" ⍝ 7 "SHUTTLE BAY" ⍝ 8 "THE SHUTTLE BAY IS DARK AND COLD." ⍝ 8 "A SHUTTLE HANGS IN PLACE, BUT LAUNCH" ⍝ 8 "SYSTEMS ARE DEAD." ⍝ 8 "DISTANT METALLIC FOOTSTEPS ECHO." ⍝ 8 if 9=1 "EXITS: 5=STORAGE" ⍝ 8 "COMPUTER CORE" ⍝ 9 "BANKS OF PANELS FLICKER IN STANDBY." ⍝ 9 "A MONITOR SCROLLS SECURITY ALERTS." ⍝ 9 "EXITS: 3=CORRIDOR" ⍝ 9 "?WHAT" ⍝ 10 "YOU CAN'T GO THAT WAY." ⍝ 11 "ARMORY DOOR LOCKED - NEED ID CARD." ⍝ 12 " (NOTHING HERE TO TAKE)" ⍝ 20 "YOU DON'T SEE THAT HERE." ⍝ 21 "YOU ALREADY HAVE IT." ⍝ 22 "TAKEN: PHASER." ⍝ 23 "TAKEN: ID CARD." ⍝ 24 "TAKEN: TOOLKIT." ⍝ 25 "TAKEN: ENGINE COUPLER." ⍝ 26 "THE TRIBBLE PURRS SOFTLY. TAKEN." ⍝ 27 "NOTHING HAPPENS." ⍝ 30 "YOU DON'T HAVE A TOOLKIT." ⍝ 31 "YOU TIGHTEN FITTINGS BUT THE COUPLER" ⍝ 32 "IS STILL MISSING." ⍝ 32 "RELAY IS ALREADY PREPPED." ⍝ 33 "YOU REMOVE THE BURNT RELAY COVER" ⍝ 34 "AND PREP THE MOUNT." ⍝ 34 "YOU DON'T HAVE THE ENGINE COUPLER." ⍝ 35 "YOU NEED TOOLS TO INSTALL IT." ⍝ 36 "YOU MUST PREP THE RELAY PANEL FIRST." ⍝ 37 "YOU INSTALL THE ENGINE COUPLER AND" ⍝ 38 "LOCK IT DOWN." ⍝ 38 "MAIN IMPULSE POWER SURGES BACK ONLINE." ⍝ 38 "COMPUTER: ORBIT STABILIZED." ⍝ 38 "COMPUTER: SECURITY LOG..." ⍝ 39 " 'INTRUDER ALERT. BOARDING DETECTED.'" ⍝ 39 " 'CREW EVACUATED OR CAPTURED.'" ⍝ 39 " 'KLINGON SEARCH PARTIES EXPECTED.'" ⍝ 39 "A MAP HIGHLIGHTS STORAGE AND" ⍝ 39 "ENGINEERING AS PRIORITY SYSTEMS." ⍝ 39 "YOU DON'T HAVE A TRIBBLE." ⍝ 40 "NO TARGET. THE TRIBBLE SQUEAKS." ⍝ 41 "YOU HURL THE TRIBBLE." ⍝ 42 "THE KLINGONS FREEZE. ONE SHOUTS:" ⍝ 42 "'GET IT AWAY FROM ME!'" ⍝ 42 "IN THE CONFUSION, THEY RETREAT." ⍝ 42 "YOU DON'T HAVE A PHASER." ⍝ 50 "YOU FIRE AT NOTHING IN PARTICULAR." ⍝ 51 "YOU STUN ONE KLINGON - BUT ANOTHER" ⍝ 52 "RETURNS FIRE!" ⍝ 52 "YOU ARE CARRYING:" ⍝ 60 " PHASER" ⍝ 61 if 3=1 " ID CARD" ⍝ 61 if 4=1 " TOOLKIT" ⍝ 61 if 5=1 " ENGINE COUPLER" ⍝ 61 if 6=1 " TRIBBLE" ⍝ 61 if 7=1 " NOTHING" ⍝ 62 when nothing is carried "" ⍝ 80 "COMMANDS (PICK A NUMBER):" ⍝ 80 " 1 LOOK - REDESCRIBE ROOM" ⍝ 80 " 2 INV - LIST INVENTORY" ⍝ 80 " 3 GO - THEN ENTER ROOM#" ⍝ 80 " 4 TAKE - THEN ENTER ITEM#" ⍝ 80 " 5 USE - THEN ENTER ITEM#" ⍝ 80 " 6 THROW - HURLS THE TRIBBLE" ⍝ 80 " 7 FIRE - FIRE THE PHASER" ⍝ 80 " 8 HELP - THIS HELP" ⍝ 80 " 0 QUIT - RESIGN COMMAND" ⍝ 80 "" ⍝ 80 "ROOM# IS THE NUMBER PRINTED NEXT TO" ⍝ 80 "EACH EXIT. GOAL: FIX ENGINES IN" ⍝ 80 "ENGINEERING BEFORE ORBIT DECAYS." ⍝ 80 "COMPUTER: INTERNAL SENSORS DETECT BOARDERS." ⍝ 101 "TWO KLINGONS EMERGE FROM THE SHADOWS!" ⍝ 102 "ONE RAISES A DISRUPTOR." ⍝ 102 "YOU HAVE ONLY MOMENTS TO ACT." ⍝ 102 "YOU HEAR KLINGON VOICES NEARBY." ⍝ 103 "" ⍝ 201 "THE DECK STEADIES BENEATH YOUR FEET." ⍝ 201 "ON THE VIEWSCREEN, THE PLANET SLOWLY" ⍝ 201 "RECEDES." ⍝ 201 "COMPUTER: ORBIT STABLE." ⍝ 201 "COMPUTER: EMERGENCY CONDITION ENDED." ⍝ 201 "" ⍝ 201 "*** YOU SAVED THE ENTERPRISE. ***" ⍝ 201 "" ⍝ 202 "A BURST OF DISRUPTOR FIRE SLAMS" ⍝ 202 "INTO THE BULKHEAD BESIDE YOU." ⍝ 202 "THE NEXT SHOT IS MORE ACCURATE." ⍝ 202 "" ⍝ 202 "*** YOU HAVE DIED. ***" ⍝ 202 "" ⍝ 203 "THE SHIP SHAKES VIOLENTLY." ⍝ 203 "FLAMES ERUPT FROM DEAD CONSOLES." ⍝ 203 "THE ENTERPRISE ENTERS THE UPPER" ⍝ 203 "ATMOSPHERE." ⍝ 203 "" ⍝ 203 "*** YOU WERE TOO LATE. ***" ⍝ 203 "" ⍝ 204 "*** COMMAND RESIGNED ***" ⍝ 204 ) }
ˡk̲eys : Num a => Unit -> a
Each line's key: a room (1 to 9), an event (10 to 99), the clock (101 to 103), an ending (201 to 204), the opening (300).
ˡk̲eys ← { @ → ( 300 300 300 300 300 300 300 300 300 300 300 300 300 300 1 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 8 8 8 8 8 8 9 9 9 9 10 11 12 20 21 22 23 24 25 26 27 30 31 32 32 33 34 34 35 36 37 38 38 38 38 39 39 39 39 39 39 40 41 42 42 42 42 50 51 52 52 60 61 61 61 61 61 62 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 101 102 102 102 103 201 201 201 201 201 201 201 201 202 202 202 202 202 202 203 203 203 203 203 203 203 204 204 ) }
ˡf̲lags : Num a => Unit -> a
Each line's condition: shown only when this state item (0: always) ...
ˡf̲lags ← { @ → ( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 5 6 7 0 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4 5 6 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) }
ˡv̲alues : Num a => Unit -> a
... has this value.
ˡv̲alues ← { @ → ( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) }
ˡk̲fv : Num a => Unit -> a
The table's three number columns as one 3-row matrix (key, condition item, condition value), as lib/Text reads it.
ˡk̲fv ← { @ → (3 c̲at t̲ally ˡk̲eys @) r̲eshape (ˡk̲eys @) c̲at (ˡf̲lags @) c̲at ˡv̲alues @ }
ˡl̲ines : Num a => Int -> a -> Int
The rows for keys k (one or many) in state s: the key matches, and the row's condition holds (lib/Text's l:h_olds).
ˡl̲ines ← { s k → w̲here ((ˡk̲eys @) m̲ember? k) ∧ (ˡk̲fv @) ˣh̲olds s }
ˡs̲ay : (Num a, Num b) => Int -> a -> b
Print the lines for keys k; how many were printed.
ˡs̲ay ← { s k → ˣl̲ines (s ˡl̲ines k) s̲elect ˡt̲ext @ }
The ship
ˡe̲xits : Num a => Unit -> a
Exits: row r, column x is 1 when room x can be reached from room r.
ˡe̲xits ← { @ → 9 9 r̲eshape ( 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 1 0 1 1 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 ) }
ˡi̲tems : Unit -> Box Char
The items, numbered as the TAKE menu numbers them, and where each lies.
ˡi̲tems ← { @ → "PHASER" "CARD" "TOOLKIT" "COUPLER" "TRIBBLE" }
ˡh̲omes : Num a => Unit -> a
Where each item lies at the start, by room.
ˡh̲omes ← { @ → 1 4 5 5 5 }
The state
ˡn̲ew : (Any a, Num b) => a -> b
A new game: in room 1 with 30 turns, nothing taken or done.
ˡn̲ew ← { x → 1 30 0 0 0 0 0 0 0 0 0 0 }
ˡe̲vent : a -> a
The last event (a code for the message).
ˡe̲vent ← { s → ˡevent s̲elect s }
ˡs̲tatus : (Num a, Truthy a) => a -> a
0 playing, 1 won, 2 dead, 3 out of time.
ˡs̲tatus ← { s → (ˡwon s̲elect s) m̲ax (2 × ˡdead s̲elect s) m̲ax 3 × 0 ≥ ˡt̲urns s }
ˡu̲pdate : Int -> Int -> Int
Several items of s changed at once (lib/State): iv is index, value, index, value, ...
ˡu̲pdate ← { s iv → s ˢu̲pdate iv }
ˡs̲aying : Int -> Int -> Int
Just the event changed: what to say next.
ˡs̲aying ← { s e → s ˡu̲pdate ˡevent c̲at e }
ˡp̲resent : Num a => a -> Int
The items here, by number: in this room and not carried.
ˡp̲resent ← { s → w̲here ((ˡh̲omes @) = ˡr̲oom s) ∧ 0 = 5 t̲ake 2 d̲rop s }
ˡr̲efusal : (Truthy a, Num b) => a -> b -> b
Of a row of checks that would refuse an action (1 where a check fails) and the event saying each, the first refusal: 0 when there is none.
ˡr̲efusal ← { fails events → f̲irst (fails r̲eplicate events) c̲at 0 }
A command
ˡm̲ove : Int -> Int -> Int
cx is the command and its argument (a room or an item number, or 0): 1 look, 2 inventory, 3 go, 4 take, 5 use, 6 throw, 7 fire, 8 help. The result is the new state, its event the key of what to say.
ˡm̲ove ← { s cx → c ← f̲irst cx x ← 2 s̲elect cx c = 1 ? s ˡs̲aying 0 c = 2 ? s ˡs̲aying 60 c = 3 ? s ˡg̲o x c = 4 ? s ˡt̲ake x c = 5 ? s ˡu̲se x c = 6 ? ˡt̲hrow s c = 7 ? ˡf̲ire s c = 8 ? s ˡs̲aying 80 s ˡs̲aying 10 }
ˡg̲o : Int -> Int -> Int
Go to room x: there must be an exit (a 1 in the exits table), and the armory wants the ID card.
ˡg̲o ← { s x → (x < 1) ∨ x > 9 ? s ˡs̲aying 11 e ← ((0 = x s̲elect (ˡr̲oom s) s̲elect ˡe̲xits @) c̲at (x = 7) ∧ 0 = ˡcard s̲elect s) ˡr̲efusal 11 12 0 < e ? s ˡs̲aying e s ˡu̲pdate ˡroom c̲at x c̲at ˡevent c̲at 0 }
ˡt̲ake : Int -> Int -> Int
Take item x: it must be here and not yet carried.
ˡt̲ake ← { s x → x = 0 ? s ˡs̲aying 20 (x < 1) ∨ x > 5 ? s ˡs̲aying 21 e ← (((ˡr̲oom s) ≠ x s̲elect ˡh̲omes @) c̲at 1 = (2 + x) s̲elect s) ˡr̲efusal 21 22 0 < e ? s ˡs̲aying e s ˡu̲pdate (2 + x) c̲at 1 c̲at ˡevent c̲at 22 + x }
ˡu̲se : Int -> Int -> Int
Use item x: the toolkit and the coupler in engineering, the computer in its core; anything else does nothing.
ˡu̲se ← { s x → r ← ˡr̲oom s (r = 6) ∧ x = 1 ? ˡt̲oolkit s (r = 6) ∧ x = 2 ? ˡc̲oupler s (r = 9) ∧ x = 3 ? s ˡs̲aying 39 s ˡs̲aying 30 }
ˡt̲oolkit : Int -> Int
The toolkit preps the relay: it needs the toolkit and the coupler, and the relay not yet prepped.
ˡt̲oolkit ← { s → e ← ((0 = ˡtoolkit s̲elect s) c̲at (0 = ˡcoupler s̲elect s) c̲at 1 = ˡrelay s̲elect s) ˡr̲efusal 31 32 33 0 < e ? s ˡs̲aying e s ˡu̲pdate ˡrelay c̲at 1 c̲at ˡevent c̲at 34 }
ˡc̲oupler : Int -> Int
The coupler, installed, wins: it needs the coupler, the toolkit and a prepped relay.
ˡc̲oupler ← { s → e ← ((0 = ˡcoupler s̲elect s) c̲at (0 = ˡtoolkit s̲elect s) c̲at 0 = ˡrelay s̲elect s) ˡr̲efusal 35 36 37 0 < e ? s ˡs̲aying e s ˡu̲pdate ˡwon c̲at 1 c̲at ˡevent c̲at 38 }
ˡt̲hrow : Int -> Int
Throwing the tribble makes the boarders retreat (it needs both).
ˡt̲hrow ← { s → e ← ((0 = ˡtribble s̲elect s) c̲at 0 = ˡboarders s̲elect s) ˡr̲efusal 40 41 0 < e ? s ˡs̲aying e s ˡu̲pdate ˡboarders c̲at 0 c̲at ˡtribble c̲at 0 c̲at ˡevent c̲at 42 }
ˡf̲ire : Int -> Int
Firing the phaser at the boarders is fatal (it needs both).
ˡf̲ire ← { s → e ← ((0 = ˡphaser s̲elect s) c̲at 0 = ˡboarders s̲elect s) ˡr̲efusal 50 51 0 < e ? s ˡs̲aying e s ˡu̲pdate ˡdead c̲at 1 c̲at ˡevent c̲at 52 }
ˡs̲aid : Num a => a -> a
The keys of what the last event says (the inventory adds what is carried, or "nothing").
ˡs̲aid ← { s → e ← ˡe̲vent s e ≠ 60 ? e 60 61 c̲at (0 = '+ r̲/ 5 t̲ake 2 d̲rop s) r̲eplicate 62 }
The clock
ˡc̲lock : Int -> Int
Each command takes a turn; at 21 turns left Klingons come aboard.
ˡc̲lock ← { s → t ← (ˡt̲urns s) − 1 s ˡu̲pdate ˡturns c̲at t c̲at ˡboarders c̲at (ˡboarders s̲elect s) m̲ax t = 21 }
ˡa̲larms : (Num a, Truthy a, Num b) => a -> b
What the clock says after a turn: the sensors (at 22), the Klingons in the shuttle bay, their voices in the corridor late in the game.
ˡa̲larms ← { s → t ← ˡt̲urns s r ← ˡr̲oom s b ← ˡboarders s̲elect s ((t = 22) c̲at (b ∧ r = 8) c̲at b ∧ (r = 3) ∧ t < 15) r̲eplicate 101 102 103 }