When we want to create a new Gostars game, we want to enter the minimum of parameters. Specifically, we want to create a game by setting the size of the universe and density of stars but not the names of individual stars.
So how do we generate readable names from scratch?
Generating the names
Random strings
Let’s try creating totally random strings. Start with 26 letters and pick them at random until you get bored. Here’s some code that will do just that, select letters at random for random sized words. Give it a go here.
Here’s some words that I generated:
Pthc
Dnlbn
Ugfb
Vndmm
Ikvji
Leafd
Krxpzaelp
Ldwnps
Pmcnaxr
Rcucryh
These do certainly look alien, maybe a little too much! Leafd
and Krxpzaelp
look decent enough but I don’t know that I can pronounce any of the rest. This doesn’t seem friendly for our players.
Random phonemes
What if we could generate English sounding words by picking random ‘sounds’. By this I mean graphemes. Great idea, let’s try that - but it is a pain finding a complete list of graphemes! After a lot of looking I eventually found that the best resource is this.
The next tool to use does something similar to selecting random letters. Given a set of graphemes, select random ones a random number of times. The code to do this is here. You’ll need to supply a data file containing a grapheme on each line. Run it like this:
go run graphemetxt.go -source data.txt
Let’s take a look at what kind of names are generated:
guskngcklff
nbquwhcggrch
lkghged
seghssghcc
btgufflkghst
rgschdpn
jzgzwhsssfgh
lfzzrpsggguked
sejscftbbgezfpn
gueuthss
I’m not sure that’s any better.
Markov generator
Here we’ll break out the big guns. Markov chains look at an existing body of data and randomly select the next ‘state’ based on its probability of following the current one. In our case, each letter will be a state.
A quick search of the internet revealed a handy little library that will help us out here. Here is a program that will read in a data file with a new name per line to build a model of how names are constructed. To use it, type the following in to the command line:
go run tools/textgen/textgen.go -source <your data file>
We need some source data to seed the Markov Chain. For this I did a search for place names in the UK, I chose a list of English nouns and place names (counties and towns). As long as the place names are 1 per line, this will server as our data.
You can set the number of words with the words
parameter. There is another parameter too, the ‘prefix’. This is used to set how far ‘back’ back in the chain the algorithm looks when deciding what letter (state) to pick next. I’m still learning how this affects the output but using 2
gave some interesting output:
Implain-landear
Adamod
Mouningendroold
Anchal
Olelughtemplail
Ariabervaging
Hdrack
Entivult
Rimbe
Usholynx
That’s much better! Quite alien but somehow readable.
Some interesting features of this tool are:
- When we meet a newline, it’s the end of the word so pick a new location in the chain
- We filter out the original words by adding them to a set of known words
- Duplicate words are removed by only storing them in our set too
Using map
keys will work very well for a set.
Deploying the names
Now we have a large list of names, how do we get them in to our game? So this is where we take advantage of the fact that we’re usingBuffalo. It icludes a tool, packr
, that allows you to embed static resources in to the binary that you produce with buffalo build
. So first of all, we need to produce a list of names (you can find them here). So how do we make them available to the program?
Let’s start with how we will use the names when we need them. The first version of cut of Gostars created new stars when you created the game. When a client does a POST
against /games
, the handler looks at the size and star density of the universe defined in the request fields and generates a random number of stars at random positions.When we generate the star, we need to look ip a name from our list, preferably randomly. Otherwise every game would have the same star names. How do we solve this? Well, it turns out that Go has a solution to this. When you range
over map keys, Go will present them in a random order. To simplify our code as much as possible, I’m thinking we need to provide a type that will let us get an arbitrary number of these names from the map keys. Take a look at names.go
for a type that will do this.
The next question is, how do we populate our nameList
?When you are writing a Buffalo application, you don’t jump in to main
, you entry point as a developer is actions.App()
. This is where all the initialisation of resources happens. We need to create a package variable of type nameList
and instantiated it in App()
, passing in a string with all of the data in the file we created earlier. To do this, we create use a packr.Box
that refers to the data
directory in our repo. When we run buffalo build
, the packr
tool will scan the source code and detect where we call packr.NewBox(...)
and store the contents of this directory as data in the binary.To access files in this directory, we need to look them up in the box that they are stored in. For our star names, we just look up starnames.txt
from the box and pass this string to our nameList
.
When we create the stars, we have access to the static data generated by our Markov Chain tool and can draw on them quickly when we need them.
I hope this is useful.
Top comments (0)