Writing a Text Adventure Game in Go - Part 1
Mar 19, 2016 · 6 minute read · CommentsIntro (Location and Movement)
So I have always liked coding games. I started many years ago on the Amiga computer a friend and I coded a small turn based space conquest game that we called Cosmic Conquest and it was a blast. Since that time I have not had serious time to put into games but when I picked up Go I figured it would be a nice way to learn the language and do something fun. Combine that with some internet research where I found plenty of python text adventure game tutorials so I will present here a 4 or 5 part series on developing not only one text adventure game but more of a system that you can re-use to create multiple text adventures with different scenarios.
Part 1 - Where but why, the world will end in 10 minutes, Go Go Go
Location, Location, Location
The most common concept in any adventure game is the Location and how to get from one place to another so this
logically follows that we need a struct in go to keep track of locations and help with where are you? and where
can you go? questions. We will also add some basic events that can happen
Locations, Transitions and Events will be declared in a struct and kept in maps for easier retrieval and less confusion. We could use pointer to other locations and events but then sometimes the code gets more confusing so I chose to avoid pointers as much as possible. The use of maps lets me create an instance of a room and access it from anywhere using the “key” from the map. Checkout the definitions below
type Location struct {
Description string
Transitions []string
Events []string
}
var locationMap = map[string]*Location{
"Bridge": {"You are on the bridge of a spaceship sitting in the Captain's chair.", []string{"Ready Room", "Turbo Lift"}, []string{"alienAttack"}},
"Ready Room": {"The Captain's ready room.", []string{"Bridge"}, []string{}},
"Turbo Lift": {"A Turbo Lift that takes you anywhere in the ship.", []string{"Bridge", "Lounge", "Engineering"}, []string{"android"}},
"Engineering": {"You are in engineering where you see the star drive", []string{"Turbo Lift"}, []string{"alienAttack"}},
"Lounge": {"You are in the lounge, you feel very relaxed", []string{"Turbo Lift"}, []string{"relaxing"}},
}
If you look at the declarations above you can see that the locationMap contains a key which is a short name
for the location and then in the struct itself you have the Description, a list of keys for locations you can
go from here and the list of “key” of events that can happen here.
Events (Boom or the world will end in 10 minutes)
Let’s add events to this now to complete the picture.
type Event struct {
Type string
Chance int
Description string
Health int
Evt string
}
var evts = map[string]*Event{
"alienAttack": {Chance: 20, Description: "An alien beams in front of you and shoots you with a ray gun.", Health: -50, Evt: "doctorTreatment"},
"doctorTreatment": {Chance: 10, Description: "The doctor rushes in and inject you with a health boost.", Health: +30, Evt: ""},
"android": {Chance: 50, Description: "Data is in the turbo lift and says hi to you", Health: 0, Evt: ""},
"relaxing": {Chance: 100, Description: "In the lounge you are so relaxed that your health improves.", Health: +10, Evt: ""},
}
Again we follow the same pattern to declare the events and the list of events that can happen in this game.
An Event is simply the percent chance that the event can occur, the description, it’s effect on our
hero’s health and finally if an event can be triggered by this event it is declared here (In this way you can chain
events to a logical conclusion).
If we look at the events that we have created there is a percentage chance the event will happen and also there is result to the hero’s health so to process this when the events “might” happen in the game we need a struct function to process all that so here it is:
func (e *Event) ProcessEvent() int {
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
if e.Chance >= r1.Intn(100) {
hp := e.Health
if e.Type == "Combat" {
fmt.Println("Combat Event")
}
fmt.Printf("\t%s\n", e.Description)
if e.Evt != "" {
hp = hp + evts[e.Evt].ProcessEvent()
}
return hp
}
return 0
}
The above code decides if the event happened, if it has a follow on event that needs to run and then return the effect on the hero’s health.
Game Loop Go
Now we are ready to ut it all together in the “game loop”. First, what is a game loop? Game Loops is a programming
loop that never ends and is characterized by printing valuable information to the player, waiting for his/her
imput and running the results of the action, the loop then restart at the beginning. So how do we do that in our
basic system here…
type Game struct {
Welcome string
Health int
CurrentLocation string
}
func (g *Game) Play() {
CurrentLocation = locationMap["Bridge"]
fmt.Println(g.Welcome)
for {
fmt.Println(CurrentLocation.Description) //Where are you?
g.ProcessEvents(CurrentLocation.Events) //Did anything happen here?
if g.Health <= 0 { //Are you dead?
fmt.Println("You are dead, game over!!!")
return
}
fmt.Printf("Health: %d\n", g.Health) //Print health information
fmt.Println("You can go to these places:") //Where can you go from here?
for index, loc := range CurrentLocation.Transitions {
fmt.Printf("\t%d - %s\n", index+1, loc)
}
i := 0
for i < 1 || i > len(CurrentLocation.Transitions) { //What would you like to do?
fmt.Printf("%s%d%s\n", "Where do you want to go (0 - to quit), [1...", len(CurrentLocation.Transitions), "]: ")
fmt.Scan(&i)
}
newLoc := i - 1
CurrentLocation = locationMap[CurrentLocation.Transitions[newLoc]] //Go to new location based on input
}
}
func (g *Game) ProcessEvents(events []string) {
for _, evtName := range events {
g.Health += evts[evtName].ProcessEvent()
}
}
func main() {
g := &Game{Health: 100, Welcome: "Welcome to the Starship Enterprise\n\n", shield: "Minor Shield", weapon: "Minor Raygun"}
g.Play()
}
In the above code we added a Game Struct to keep track of a welcome message and our hero’s health and Current Location. We also attached 2 methods to that called Play and ProcessEvents. Play is the actual game loop while process events handles loading events in the current location and running them.
Final words
Check out the complete working code in this Gist.
I hope you enjoyed this first part of writing a text adventure in Go and I look forward to writing and having you
read and comment on part 2 next week, when we will add real opponents and true combat to the game.
*** Sign up for my email list to keep in touch with all the interesting new happenings in the go community with the GolangNewsFeed