Turbo Pascal Text Adventure Primer cover: BEACON, the adventure the primer builds in Turbo Pascal 2.0, mid-game on the Tatung Einstein
← Back to Modules
module
18

Moving And Looking

Introduction

BEACON1 can describe a room. BEACON2 lets the player walk about the island, which means putting part one's work to use: reading the command a key at a time, splitting it, looking up each word in a table, and acting on the tokens.

Starting BEACON2 From BEACON1

Make a new work file, BEACON2. Open the editor, and read the whole of BEACON1 in with CONTROL and K, then CONTROL and R: Turbo asks Read block from file:, and you answer BEACON1. Now change it as below. Each game section starts this way from the last.

The Words

BEACON knows 55 words. They are in alphabetical order, so that S9's binary search can find any of them in six comparisons, and beside each is its token, S8's number for what it means:

TokensKindWords
1 to 6directionsN NORTH, S SOUTH, E EAST, W WEST, U UP, D DOWN
7 to 19other verbsGO, LOOK L, INVENTORY I, TAKE GET, DROP, EXAMINE X, OPEN PRISE, UNLOCK, LIGHT IGNITE, FILL, READ, QUIT Q, XYZZY
20 to 31nounsCROWBAR, LANTERN, OIL CAN, LOGBOOK LOG BOOK, MATCHES, KEY, DOOR, CUPBOARD, DRAWER, LAMP, BOAT, SHIP
40, 41prepositionsWITH, IN INTO
50noiseTHE, A, AN, AT, TO

Two things are chosen with care. A direction's token is its number in the exits: NORTH is 1 and Exits[1] is north, so moving needs no table of its own. And the token says what kind of word it is: under 20 a verb, under 40 a noun, under 50 a preposition, and 50 noise. S10 kept a separate list of kinds; here the number is enough.

Synonyms are simply two entries with the same token. A one-letter word is a synonym too, as in Alice and the March Hare.

All 55 go in now, even though most of the verbs do nothing until S19 and S20, so that the table is typed once and stays in order.

The Parts

GetLine is S4's line reader, taking only letters and spaces, and up to 38 of them: DELETE works, and the player's words appear in capitals.

Find is S9's binary search, returning the token rather than the place. Its table holds Str9s, so Parse cuts each word to nine letters before looking it up.

Parse is S10's, deciding kind by the token's size.

Walk takes a direction: if the exit is 0, You can't go that way., otherwise move and Look.

Act is a case on the verb. Anything not yet written gets You can't do that yet., through the case's else.

The Code

The top of the program, before Wrap, becomes:

program Beacon;
const
  Rooms = 12;
  Count = 55;
type
  Str255 = string[255];
  Str40 = string[40];
  Str16 = string[16];
  Str9 = string[9];
  Room = record
    Name: Str16;
    Exits: array[1..6] of Byte;
  end;
const
  Words: array[1..Count] of Str9 =
    ('A', 'AN', 'AT', 'BOAT', 'BOOK',
     'CAN', 'CROWBAR', 'CUPBOARD', 'D',
     'DOOR', 'DOWN', 'DRAWER', 'DROP',
     'E', 'EAST', 'EXAMINE', 'FILL',
     'GET', 'GO', 'I', 'IGNITE', 'IN',
     'INTO', 'INVENTORY', 'KEY', 'L',
     'LAMP', 'LANTERN', 'LIGHT', 'LOG',
     'LOGBOOK', 'LOOK', 'MATCHES', 'N',
     'NORTH', 'OIL', 'OPEN', 'PRISE',
     'Q', 'QUIT', 'READ', 'S', 'SHIP',
     'SOUTH', 'TAKE', 'THE', 'TO', 'U',
     'UNLOCK', 'UP', 'W', 'WEST',
     'WITH', 'X', 'XYZZY');
  Token: array[1..Count] of Byte =
    (50, 50, 50, 30, 23,
     22, 20, 27, 6,
     26, 6, 28, 11,
     3, 3, 12, 16,
     10, 7, 9, 15, 41,
     41, 9, 25, 8,
     29, 21, 15, 23,
     23, 8, 24, 1,
     1, 22, 13, 13,
     18, 18, 17, 2, 31,
     2, 10, 50, 50, 5,
     14, 5, 4, 4,
     40, 12, 19);
var
  Map: array[1..Rooms] of Room;
  Msg: file of Str255;
  Here, V, N1, P, N2: Integer;
  Quit: Boolean;
  Line: Str40;

Wrap, Say, Load and Look stay as they were. After Load, add:

procedure GetLine(var S: Str40);
var
  Ch: Char;
begin
  S := '';
  repeat
    Read(Kbd, Ch);
    Ch := UpCase(Ch);
    if (Ch in [#8, #25])
       and (Length(S) > 0) then
    begin
      Write(#8, ' ', #8);
      S[0] := Pred(S[0]);
    end
    else if (Ch in [' ', 'A'..'Z'])
       and (Length(S) < 38) then
    begin
      Write(Ch);
      S := S + Ch;
    end;
  until Ch = #13;
  Writeln;
end;

function Find(W: Str9): Integer;
var
  Lo, Hi, Mid, Found: Integer;
begin
  Found := 0;
  Lo := 1;
  Hi := Count;
  while (Found = 0) and (Lo <= Hi) do
  begin
    Mid := (Lo + Hi) div 2;
    if Words[Mid] = W then
      Found := Token[Mid]
    else if Words[Mid] < W then
      Lo := Mid + 1
    else
      Hi := Mid - 1;
  end;
  Find := Found;
end;

function Parse(S: Str40): Boolean;
var
  W: Str40;
  K, Pl: Integer;
  Ok: Boolean;
begin
  V := 0; N1 := 0; P := 0; N2 := 0;
  Ok := True;
  S := S + ' ';
  while Ok and (S <> '') do
  begin
    Pl := Pos(' ', S);
    W := Copy(S, 1, Pl - 1);
    Delete(S, 1, Pl);
    if W <> '' then
    begin
      K := Find(Copy(W, 1, 9));
      if K = 0 then
      begin
        Writeln('I don''t know the word ',
                W, '.');
        Ok := False;
      end
      else if K < 20 then
        V := K
      else if K < 40 then
      begin
        if P = 0 then
          N1 := K
        else
          N2 := K;
      end
      else if K < 50 then
        P := K;
    end;
  end;
  Parse := Ok;
end;

After Look, add:

procedure Walk(D: Integer);
var
  Dest: Integer;
begin
  Dest := Map[Here].Exits[D];
  if Dest = 0 then
    Writeln('You can''t go that way.')
  else
  begin
    Here := Dest;
    Look;
  end;
end;

procedure Act;
begin
  case V of
    1..6: Walk(V);
    7: if (N1 > 0) or (P > 0) then
         Writeln('Which way?')
       else
         Writeln('Go where?');
    8: Look;
    18: Quit := True;
  else
    Writeln('You can''t do that yet.');
  end;
end;

And the main program becomes:

begin
  Load;
  Here := 1;
  Quit := False;
  Writeln('BEACON');
  Look;
  repeat
    Write('> ');
    GetLine(Line);
    if Parse(Line) then
      if V = 0 then
        Writeln('There is no verb in that.')
      else
        Act;
  until Quit;
  Close(Msg);
end.

Starting from

BEACON1, read into a new work file BEACON2 as above. Save before you run.

What you should see

The beach, and a >. N takes you to the cliff path, W to the keeper's front room, L looks again. GO NORTH works, because NORTH is the verb that counts; GO alone asks Go where?. TAKE LAMP gets You can't do that yet., FROB gets I don't know the word FROB., and Q ends the game.

Change One Thing

  • In Walk, take out the Look; after Here := Dest;. Walk north, then type L. Where are you?
  • In Act, change 1..6: to 1..4:. What do U and D do now?
  • In Wrap, change both 40s to 30.

Exercises

18.1 When there is no way in the direction asked, list the ways there are: You can go: WEST. (The words for the directions are in the table: NORTH is Words[35].)

18.2 Count the commands typed, and print the count after Q.

18.3 Print each room's name in capitals.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
Error 5: ')' expected in the tableMore words than Count says, or fewer.

Summary

BEACON2 reads a command a key at a time, splits it with Pos, finds each word by binary search in a sorted table of 55, and works out verb, noun, preposition and noun from the tokens' sizes. A direction's token is its exit number, so moving is one line.

Next

S19, Things: six things to find, carry and drop.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.