C# Port of ‘Micropuzzle’ Text Adventure

Tags: csharp

Micropuzzle (or ‘Micro Puzzle’) is a game from Usborne’s 1984 ‘Weird Computer Games’, a genre of literature from the days when all home computers booted to a BASIC interpreter, so there was a market for books containing the listing for simple games you could type in and play yourself. One such game was ‘Micropuzzle’, a simple text adventure, but the code had been obfuscated to hide any clues you might glean as you entered the code. As it turns out, the game is very unintuitive and it seems unlikely anyone ever completed it without unpicking the code for clues. On a nostaligia trip I recalled never having finished it and wondered if I could port it over to C# then use a modern IDE to refactor it into something designed to be understood, and using simple concepts a beginner could follow.

Map

Puzzles

Problem Solution
Can’t climb down from table Tie thread to tree and climb down thread
Mouse won’t let you past Leave cheese for mouse
Key does not fit through mouse hole Put on train and send train through tunnel
Cat won’t let you past Release fly from bottle to distract cat
Terminal is locked Unlock with key
Terminal deactivated Enter code
Don’t know code Read paper
Can’t find paper Open box
Need to use Maximiser Activate terminal, point remote control at maximiser, press button

Traps

Nowadays it’s regarded poor game design to incorporate death or dead ends into adventure games, but being the product of its time, here’s the traps:

Trap Problem
Eat cheese No longer able to use cheese to move mouse
Use or examine terminal without key Self-destruct activated
Release fly from bottle in a room without cat Can no longer use fly to distract cat

Game Data

Room descriptions

We store the descriptions for each of the rooms on the map in an array as follows:

var roomDescriptions = new string[] {
    null, // Room 0 not used
    "INSIDE THE MOUSEHOLE - IT IS VERY DARK IN HERE", // 1
    "AT A MOUSEHOLE IN A CORNER OF THE ROOM", // 2
    "ON THE EDGE OF A HIGH TABLE", // 3
    "AT THE BACK OF A HALLWAY", // 4
    "IN A STORAGE ROOM", // 5
    "IN THE KITCHEN", // 6
    "FURTHER DOWN A DARK SMELLY TUNNEL", // 7
    "BY A RAILWAY SIDING", // 8
    "AT THE BASE OF A TALL PLASTIC TREE ON THE EDGE OF A HIGH TABLE", // 9
    "OUTSIDE THE OPEN DOOR OF AN ODDLY PROPORTIONED HOUSE", // 10
    "IN A YELLOW FRONT ROOM", // 11
    "BY A TV SET AND A RECORDER", // 12
    "AT THE END OF A DARK TUNNEL", // 13
    "BY A LARGE SWITCH CONNECTED TO THE RAILWAY TRACKS", // 14
    "ALONGSIDE THE WINDING TRACK", // 15
    "AT THE END OF THE LINE-THE TRACK DISAPPEARS THROUGH A HOLE IN THE WALL",
    "BELOW A WHOLE WALL OF OVERSIZED VIDEO SCREENS", // 17
    "STANDING ON THE MAXIMISER PAD", // 18
    "ON A SHELF OF DISTURBING APPARATUS - THERE IS A STOOL NEARBY", // 19
    "ON A SHORT STEP STOOL", // 20
    "ON THE FLOOR OF AN OVERTURNED BOX OF BROKEN ELECTRONIC PARTS", // 21
    "AT A HOLE IN THE WALL FROM WHICH A RAILWAY LINE EMERGES", // 22
    "AT THE BASE OF A SWIVEL CHAIR", // 23
    "STANDING ON A COMPUTER TERMINAL WITH A SECURITY LOCK" // 24
};

The numbers 1-24 will refer to each of this rooms, but we want to also use 0 to be a place to store discarded items, and 25 to refer to items in the player’s inventory, so let’s set up some const values to refer to these numbers by:

// Use the numbers 1-24 for each of the rooms above but also define some
// special locations
const int Deleted = 0;
const int PlayerInventory = 25;

Objects

We similarly can store all the the objects in the game as an array:

var Nouns = new string[] {
    null, "KEY", "CASSETTE", "CHEESE", "PAPER", "THREAD", "REMOTE-CONTROL",
    "BOTTLE", "TRAIN", "CAT", "DOOR", "SWITCH", "TUNNEL", "TREE", "HOLE",
    "MOUSE", "VIDEO", "COMPUTER", "GROCER", "TERMINAL", "111", "BUTTON",
    "MAXIMISER", "TV", "BOX", "NORTH", "SOUTH", "WEST", "EAST", "UP", "DOWN",
    "STOOL"
};      

Note the first 8 are items that the player can move around. The others are just anything the player can interact with, we’ll keep a note of this number in a const:

const int numberOfItems = 8;

Also we’ll set up a series of consts to help with indexing any item in the Nouns array:

// Create constants to help with indexing Nouns array
const int Key = 1;
const int Cassette = 2;
const int Cheese = 3;
const int Paper = 4;
const int Thread = 5;
const int RemoteControl = 6;
const int Bottle = 7;
const int Train = 8;
const int Cat = 9;            
const int Switch = 11;            
const int Tree = 13;            
const int Video = 16;
const int Computer = 17;
const int Grocer = 18;
const int Terminal = 19;
const int Code111 = 20;
const int Button = 21;
const int Maximiser = 22;
const int TV = 23;
const int Box = 24;
const int North = 25;
const int South = 26;
const int West = 27;
const int East = 28;
const int Up = 29;
const int Down = 30;
const int Stool = 31;

This way if we’re wanting to refer to, e.g. the Terminal in our code, we can call it Terminal instead of 19 and it will be easier to remember what Terminal means than 19.

Game Flow

  1. Initialise the game state to the starting position.
  2. Display details of room player is currently in
  3. Take input from player
  4. Split player input into a verb, e.g. “GET” and an object, e.g. “THREAD”.
  5. Run the appropriate routine for the verb
  6. Modify the state of the game according to the actions the player wants to take.
  7. Update the message to the player informing them of the result of their actions
  8. Go to 2.

Game State

At any one time, the entire state of the game is stored in a series of variables :

Variable Description
currentRoom Stores the number of the room the player is currently in
itemFlags[] Array storing whether each item has been used or not
itemLocation[] Array storing the room each item is currently located in (1-24), or 0 if it has been used/deleted, or 25 if it is in the player’s inventory.
GameFlags A series of flags CatCleared, BottleOpened, etc. storing which tasks have been carried out

When the program is first run, we need to initialise all of these to be the state they should be in at the start of the game:

var currentRoom = 11;

var ItemFlags = new bool[] {
    false, // Item 0 not used
    false,
    true,  // 2.Cassette starts hidden in VCR
    false,
    true,  // 4.Paper starts hidden in box
    false,
    false,
    false,
    false  
};

// Initialise starting locations for each item
var ItemLocation = new int[] {
    -1, // Item 0 not used
    16, 12, 3, 21, 5, 17, 19, 14
};

// Initialise game flags. These keep track of which puzzles the player has
// solved
bool CatCleared = false;
bool BottleOpened = false;
bool TerminalUnlocked = false;
bool GameCompleted = false;
bool KeyOnTrain = false;
bool SelfDestructTriggered = false;

User Input

The following code is used to:

// Get your instructions and split them into two words
Console.WriteLine("WHAT WILL YOU DO NOW");
var userCommand = Console.ReadLine().ToUpperInvariant();

// Split the user input into the first word (verb) and the rest (noun)
var commandArray = userCommand.Split(" ", 2);
var verb = commandArray[0];
var noun = "";            
if (commandArray.Length > 1) noun = commandArray[1];

// Check second word
var objectNumber = Array.IndexOf(Nouns, noun);

Process input

We then use the switch command to direct the program according to the verb the player entered, e.g.:

// Selects case depending on the verb you typed
switch (verb)
{
case "GET":          
    // Process all GET commands
case "OPEN":
    // Process all OPEN command
// etc.
default:
    // Player entered invalid command
}

Moving between rooms

We can define each of the directions we can move from each room as an array as follows:

var exits = new string[] { null, // Room 0 not used
        "S,E", "S,W", "S,E", "S,W,E", "W,E", "S,W", "N,S", "N,S", "N,E", "N,W,E",
        "E,W", "N,W", "N,S", "N,E", "E,W", "W", "S", "S", "N,D", "U,D", "E,U",
        "E,W", "N,E,W", "N,W"
};

E.g. room 1 on the map we can either move South (to room 7) or East (to room 2), hence we set the exits to be “S,E”.

We can note that due to the grid nature of our map:

// Check whether player can move in direction and do so
var MovedInValidDirection = false;
if (exits[currentRoom].Contains("N") && direction == "N")
    { currentRoom -= 6; MovedInValidDirection = true; }
if (exits[currentRoom].Contains("S") && direction == "S")
    { currentRoom += 6; MovedInValidDirection = true; }
if (exits[currentRoom].Contains("W") && direction == "W")
    { currentRoom -= 1; MovedInValidDirection = true; }
if (exits[currentRoom].Contains("E") && direction == "E")
    { currentRoom += 1; MovedInValidDirection = true; }
if (exits[currentRoom].Contains("U") && direction == "U")
    { currentRoom -= 1; MovedInValidDirection = true; }
if (exits[currentRoom].Contains("D") && direction == "D")
    { currentRoom += 1; MovedInValidDirection = true; }

Open Box Puzzle

One of the puzzles is to open the box to reveal the paper, so we need to check:

If all of the above is true, then we can set the Item Flag for the Paper to false to make it visible, and give the player a message to let them know something happened.

case "OPEN":
    // If the player opens the box then reveal the paper
    if (currentRoom == 21 && objectNumber == Box)
    {
        ItemFlags[Paper] = false;
        message = "DUST SETTLES";
    }

Bugs in the original

I’ve removed some bugs from the original BASIC code:

See also