How The Game Is Played

http://blogs.sun.com/gameguy/date/20060726 Wednesday July 26, 2006

Game Progrmaming 100: A text adventure, part 1

On JavaGaming.org recently the issue came up of how to write a text adventure.
A text adventure is a nice first game coding project that is simple, small and well contained.  I thought thus it might make a nice blog topic.  So here is part one of JADVENTURE: The Java Adventure Game Engine.

Part 1: The Fundementals

Fundementally, a single player text adventure is just a Finite State Automaton (FSA). A good complete definition of a FiniteStateAutomaton can be found on wikipedia here. For our purposes though a simpler definition will suffice.

A Finite State Automata (FSA), also called a "state machine" is a machine that exists in one and only one state at a time.  It takes as input an event. An event causes a transition to a new state. Part of that transition may be to generate output.

FSA transitions can be described by a 2D array where the X coordinate is the state, the Y coordinate is the event and contents of the cell is the transition which would be a new X coordinate to change to plus output or other action information. This array is often called a "transition table".

Lets imagine for a moment that  you have a world of two rooms. NorthRoom and SouthRoom and 2 events-- "Go North" and "Go South".

Your state transition table might look like this:
North Room South Room
Go North do nothing set state to North Room
Go South set state to South Room do nothing

Now this by itself is not enough to be the adventure game ebcause there is no room description yet. No output.

Typically, an assumed part of every transition to a new state in such a game is "print descritpion of new state." You can keep that descritpion in another table like this:

Room Description
North Room A big spacious cavern with a door to the south
South Room A tiny little hole with a door to the north.

Thats all you need to walk between two rooms. But in practice using tags like "North Room" and "Go North" in your data structures is difficult in most language. In most languages it is more natural to assign ordinal values to such things. So in the above tables "North Room" is room 0 and "South Room" room 1. As all that is trypically part of the data for the game the conversion to ordinals can be done at game-build time and all you need at runtime are the actual ordinal numbers in their respective tables like this:

0 1
Go North do nothing set state to 0
Go South set state to 1 do nothing

Room Number Description
0 A big spacious cavern with a door to the south
0 A tiny little hole with a door to the north.

That gets us most of the way but we also want to convert "go north" to event 0 and "go south" to event 1 so our final table looks like this:

0 1
0 do nothing set state to 0
1 set state to 1 do nothing

The events however are going to be text input from the user. How will we identify the event numbers. The answer is that we use a parser that returns an event number. Modern text adventure parsers can be quite complex. For a first cut though we will just use a parser that recognizes only "go north" and "go south". That will get us up and running and then next blog I will discuss a more capable parser.

The code for everything we've done so far is below.

/**
 *
 * <p>Title: SimpleTextAdventure.java</p>
 * @author Jeff Kesselman
 * @version 1.0
 */
package com.worldwizards;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 *
 * <p>Title: SimpleTextAdventure.java</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004 Sun Microsystems, Inc.</p>
 * <p>Company: Sun Microsystems, Inc</p>
 * @author Jeff Kesselman
 * @version 1.0
 */
public class SimpleTextAdventure {
    static final String[] commands = {
        "go north","go south"
    };
    static final String[] descriptions = {
        "a big spacious cavern with a door to the south.",
        "a tiny little hole with a door to the north."
    };
    static final int INVALID_EVENT = Integer.MIN_VALUE;
    static final int[][] transitionTable = {
        {INVALID_EVENT,1},
        {0,INVALID_EVENT}
    };
   
    private int currentState=0;
   
    /**
     * @param args
     */
   
    public SimpleTextAdventure() {
        printStateDescription();
        BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        while(true){
            try {
                String input = consoleReader.readLine();
                int event = parseInput(input);
                if (event>=0) { // recognized
                    if (doTransition(event)){ // chnaged state
                        printStateDescription();
                    } else {
                        System.out.println("I can't do that right now.");
                    }
                } else {
                    System.out.println("I don't know how to do that.");
                }
            } catch (IOException e) {
               
                e.printStackTrace();
            }
           
        }
       
    }
    /**
     * @param event
     * @return
     */
    private boolean doTransition(int event) {
        int newstate = transitionTable[currentState][event];
        if (newstate >= 0){
            currentState = newstate;
            return true;       
        }
        return false;
    }
    /**
     * @param input
     * @return
     */
    private int parseInput(String input) {
        for(int i=0;i<commands.length;i++){
            if (input.equalsIgnoreCase(commands[i])){
                return i;
            }
        }
       
        return -1;
    }
    /**
     *
     */
    private void printStateDescription() {
        System.out.println("You are in "+descriptions[currentState]);
       
    }
   
    public static void main(String[] args) {
        new SimpleTextAdventure();

    }

}

Comments:

Post a Comment:
Comments are closed for this entry.