Menu Close

A basic terrain generator in Go (Part 1)

I haven’t gushed about Go much on this blog1, but I thought of writing a
small series of posts on a little piece of code I cooked up while I was
supposed to be doing other, more important things2.

Concept

So I was sitting there, zoning out and browsing one of my favourite subreddits,
when I thought about how terrain can be generated. And I thought if we were to
take a flat plane that sliced through the mountains at progressively decreasing
heights, you’d first get a few isolated spots (the peaks of the mountains), and on
the next iteration you’d always have the areas adjacent to these peaks showing up
(because mountain tops don’t float in thin air).

Computationally, if the map was a 2D array, we could randomly set a few elements
as peaks, and then generate the rest of the mountain around those “peak” elements.
Of course, with each iteration you would also have the possibility of running into
peaks that weren’t high enough to be captured at the earlier iteration, so we
should keep randomly sprinkling peaks among the (not already assigned) elements
in the map.

Pseudocode

The pseudocode for the most basic version would look something like this:

# map initialised to 0s
map[height][width] for elevation <- 5 to 0 for h <- 0 to height for w <- 0 to width # if element is already assigned value, skip if map[h][w] > 0 continue # if array element next to higher elevation # or picked as a peak randomly if map[h][w] next to elevation + 1 or random() map[h][w] <- elevation

Seems simple enough, right?

Go Implementation

The initialisations, loops and randomly picking an element to be a peak (and
printing the result out) seem straightforward enough, so let’s get those out
of the way first and create our main.go file:

package main import ( "fmt" "math/rand" "time"
) const width, height, elevation = 16, 16, 9 // we set the random chance of a peak occuring to 5%
const peakProbability = 5 var fullMap [height][width]int func main() { // rand needs to be seeded, so we set the current // nanosecond timestamp as the seed rand.Seed(time.Now().UnixNano()) // iterate down from max elevation, assigning vals for e := elevation; e > 0; e-- { for h := 0; h < height; h++ { for w := 0; w < width; w++ { // if the element has already been // assigned, skip it if fullMap[h][w] > 0 { continue } // if the random value meets our criteria, // it's a peak if rand.Intn(100) < peakProbability { fullMap[h][w] = e } } } } // print out map for h := 0; h < height; h++ { fmt.Println(fullMap[h]) }
}

We run the code with go run main.go, and get the following output:

First Iteration

To quote Chernobyl3, it’s “Not great, not terrible“.


In the next part we’ll look at a quick and dirty way to implement a
way to look at elements adjacent to our current element (the part of our
pseudocode that we didn’t implement), and assign values to it—and better
visualisation of the terrain map.

The full code for this part can be found on Github.


  1. Mostly because this blog has been inactive for the last 2 years, but
    more on that in a later post. 

  2. My biggest gripe about my otherwise perfect workplace is that I don’t
    get to write Go for a living. 

  3. Which I haven’t watched yet! 🙈 

%d bloggers like this: