Suppose that we have a series of games each of which allows the player to select a puzzle difficulty level. When the player plays a new game in the series, we want him to start out by default with the same difficulty level he faced earlier on, so we store this information in a small preferences file, as follows:
"Alien Invasion Part 23"
A difficulty is a kind of value. The difficulties are easy, moderate, hard, and fiendish.
Understand "use [difficulty] puzzles" as selecting difficulty. Selecting difficulty is an action out of world, applying to one difficulty.
Carry out selecting difficulty:
choose row 1 in the Table of Preference Settings;
now challenge level entry is difficulty understood;
say "Puzzles will be [challenge level entry] from now on."
The File of Preferences is called "prefs".
When play begins:
if File of Preferences exists:
read File of Preferences into the Table of Preference Settings;
choose row 1 in the Table of Preference Settings;
say "(The current puzzle difficulty is set to [challenge level entry].)"
Check quitting the game:
write File of Preferences from the Table of Preference Settings.
Table of Preference Settings
challenge level
easy
The Sewer Junction is a room.
Our preference file is restricted to a single option here for simplicity's sake, but we could keep track of more information -- whether the player preferred verbose or brief room descriptions, screen configurations, and so on.
If we were disposed to be somewhat crueler, we could use a similar method to make the player finish each episode of the series in order to "unlock" the next. All we would need to do is store a numerical password in our preferences file when the player finished a given level; the next level would check, say, the Table of Completed Levels for that password, and refuse to play unless the right number were present.
A tradition among Nethack-like computer games of the old school is that a player's death in a given place leaves a ghost behind to haunt subsequent players. Information about past lives is sometimes stored in a "bones file", and in this example we do exactly that, for a grievously unfair little dungeon.
To begin with, the labyrinth itself. We create a kind of value to remember possible means of death in these tunnels, and we assign a coordinate position in some grid to each location. (We do this because grid positions can safely be stored in tables saved out to external files, whereas room names cannot - they represent data which changes each time we amend the source.)
"Labyrinth of Ghosts"
Use scoring.
A demise is a kind of value. The demises are drowned, buried by a rockfall, pierced by an arrow and slain. The latest demise is a demise that varies.
A grid location is a kind of value. (1,19) specifies a grid location. A room has a grid location called coordinates.
The Gateway is a room. "For the foolhardy adventurer, the perilous labyrinth lies north, east or south." The coordinates are (6,6). The Tomb is east of the Gateway. The coordinates are (7,6). The Rockfall Cave is north of the Gateway. "This partly fallen cave may perhaps extend further north." The coordinates are (6,5). Instead of going north in the Rockfall Cave, have the player buried by a rockfall. The Archery Canyon is south of the Gateway. "No telling why this canyon is named after archery, but perhaps if you wait around you'll find out." The coordinates are (6,7). Instead of waiting in the Archery Canyon, have the player pierced by an arrow. The Rock Pool is east of the Tomb. The coordinates are (8,6). The cold mountain pool is in the Rock Pool. The cold pool is fixed in place. Instead of entering the cold mountain pool, have the player drowned.
Every turn when a random chance of 1 in 10 succeeds:
say "A dwarf appears out of nowhere, and throws a nasty little knife.";
have the player slain.
And as compensation for these hazards:
Some silver bars are in the Tomb. The emerald is in the Rock Pool. The platinum pyramid is in the Canyon.
Table of Point Values
item score
silver bars
|
3
|
platinum pyramid
|
10
|
emerald
|
4
|
Report taking an item listed in the Table of Point Values:
increase the score by the score entry;
blank out the whole row.
We are now ready for the actual undertaking. The Table of Ghostly Presences holds up to twenty death notices, and is initially blank. Deaths are sequentially numbered, and this number is stored in the sequence column.
Table of Ghostly Presences
haunted position
|
score at death
|
turns at death
|
manner of death
|
sequence
|
a grid location
|
a number
|
a number
|
a demise
|
a number
|
with 19 blank rows.
|
As the story file starts up, we look to see if a ghosts file already exists. If one does, we load up the Table of Ghostly Presences with it: and if not, as will be the case the first time the player explores, we leave the table blank. We sort the table so that it has earlier deaths (lower sequence numbers) first.
The File of Ghosts is called "ghosts".
When play begins:
if the File of Ghosts exists, read File of Ghosts into the Table of Ghostly Presences;
sort the Table of Ghostly Presences in sequence order.
How will ghosts manifest themselves? Because this is only a small example, we will simply tell the player that he senses something. If several ghosts are present in the same place, the most aggrieved (that is, the most recent) is sensed first...
After looking:
repeat through the Table of Ghostly Presences in reverse sequence order:
if the haunted position entry is the coordinates of the location, say "You sense the ghostly presence of an adventurer, [manner of death entry] with a score of [score at death entry] in [turns at death entry] turns."
(For instance, "You sense the ghostly presence of an adventurer, buried by a rockfall with a score of 10 in 5 turns.") That just leaves the rule for bumping off the player. When the Table is full, and there are already 20 ghosts, the one who died longest ago (with the lowest sequence count) is eliminated, and his row blanked out. (This will always be row 1 since we sorted the table in sequence order on reading it in.)
To have the player (sticky end - a demise):
let the new sequence number be 0;
repeat through the Table of Ghostly Presences:
let S be the sequence entry;
if S is greater than the new sequence number, let the new sequence number be S;
increment the new sequence number;
if the number of blank rows in the Table of Ghostly Presences is 0:
choose row 1 in the Table of Ghostly Presences;
blank out the whole row;
choose a blank row in the Table of Ghostly Presences;
now the sequence entry is the new sequence number;
now the manner of death entry is the sticky end;
now the turns at death entry is the turn count;
now the score at death entry is the score;
now the haunted position entry is the coordinates of the location;
write the File of Ghosts from the Table of Ghostly Presences;
now the latest demise is the sticky end;
end the story saying "You have been [latest demise]".
Strictly speaking we ought to worry that after 2,147,483,647 deaths, the sequence numbers would grow too large to store in a single value, and then the sequence of ghosts will be erratic. But it seems unlikely that anyone will play this example 2.1 billion times.
The trick here is that we need a table with text in order to keep track of the players' names.
Part 1 largely replicates the source from "Identity Theft"; new material starts at Part 2.
"Rubies"
Use scoring.
Part 1 - Collecting Names
The player's forename is a text that varies. The player's full name is a text that varies.
When play begins:
now the command prompt is "What is your name? > ".
To decide whether collecting names:
if the command prompt is "What is your name? > ", yes;
no.
After reading a command when collecting names:
if the number of words in the player's command is greater than 5:
say "[paragraph break]Who are you, a member of the British royal family? No one has that many names. Let's try this again.";
reject the player's command;
now the player's full name is the player's command;
now the player's forename is word number 1 in the player's command;
now the command prompt is ">";
say "Hi, [player's forename]!";
say "[banner text]";
move the player to the location;
reject the player's command.
Instead of looking when collecting names: do nothing.
Rule for printing the banner text when collecting names: do nothing.
Rule for constructing the status line when collecting names: do nothing.
Part 2 - Adding the Leaderboard
File of Leaderboard is called "leaderboard".
When play begins:
if the File of Leaderboard exists:
read File of Leaderboard into the Table of Leaders;
sort the Table of Leaders in reverse scored amount order.
When play ends:
choose row 10 in the Table of Leaders; [we've sorted the table, so the lowest score will be the one at the bottom]
if the score is greater than scored amount entry:
now name entry is the player's forename;
now the scored amount entry is the score;
show leaderboard;
write the File of Leaderboard from the Table of Leaders.
To show leaderboard:
sort the Table of Leaders in reverse scored amount order;
say "Current leading scores: [paragraph break]";
say fixed letter spacing;
repeat through Table of Leaders:
if scored amount entry is greater than 0:
say " [name entry]";
let N be 25 minus the number of characters in name entry; [here we want to space out the scores so they make a neat column]
if N is less than 1, now N is 1;
say N spaces;
say "[scored amount entry][line break]";
say variable letter spacing.
To say (N - a number) spaces:
repeat with index running from 1 to N:
say " ".
Table of Leaders
scored amount
|
name
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
0
|
"Smithee"
|
And now we introduce a scenario that allows different players to come up with different scores -- admittedly not a very interesting scenario, but it will do for now:
Part 3 - Scenario
Carry out taking something which is not handled:
increment score.
The Big Treasure Chamber is a room. It contains a ruby, an emerald, a gold tooth, an antique katana, and a silver coin.
Instead of waiting, end the story finally.
Test me with "get ruby / z".