The Context
In Python 3.5, I'm making a function to generate a map with different biomes - a 2-dimensional list with the first layer representing the lines of the Y-axis and the items representing items along the X-axis.
Example:
[ ["A1", "B1", "C1"], ["A2", "B2", "C2"], ["A3", "B3", "C3"] ]
This displays as:
A1 B1 C1 A2 B2 C2 A3 B3 C3
The Goal
A given position on the map should be more likely to be a certain biome if its neighbours are also that biome. So, if a given square's neighbours are all Woods, that square is almost guaranteed to be a Woods.
My Code (so far)
All the biomes are represented by classes (woodsBiome
, desertBiome
, fieldBiome
). They all inherit from baseBiome
, which is used on its own to fill up a grid.
My code is in the form of a function. It takes the maximum X and Y coordinates as parameters. Here it is:
def generateMap(xMax, yMax):
areaMap = [] # this will be the final result of a 2d list
# first, fill the map with nothing to establish a blank grid
xSampleData = [] # this will be cloned on the X axis for every Y-line
for i in range(0, xMax):
biomeInstance = baseBiome()
xSampleData.append(biomeInstance) # fill it with baseBiome for now, we will generate biomes later
for i in range(0, yMax):
areaMap.append(xSampleData)
# now we generate biomes
yCounter = yMax # because of the way the larger program works. keeps track of the y-coordinate we're on
for yi in areaMap: # this increments for every Y-line
xCounter = 0 # we use this to keep track of the x coordinate we're on
for xi in yi: # for every x position in the Y-line
biomeList = [woodsBiome(), desertBiome(), fieldBiome()]
biomeProbabilities = [0.0, 0.0, 0.0]
# biggest bodge I have ever written
if areaMap[yi-1][xi-1].isinstance(woodsBiome):
biomeProbabilities[0] += 0.2
if areaMap[yi+1][xi+1].isinstance(woodsBiome):
biomeProbabilities[0] += 0.2
if areaMap[yi-1][xi+1].isinstance(woodsBiome):
biomeProbabilities[0] += 0.2
if areaMap[yi+1][xi-1].isinstance(woodsBiome):
biomeProbabilities[0] += 0.2
if areaMap[yi-1][xi-1].isinstance(desertBiome):
biomeProbabilities[1] += 0.2
if areaMap[yi+1][xi+1].isinstance(desertBiome):
biomeProbabilities[1] += 0.2
if areaMap[yi-1][xi+1].isinstance(desertBiome):
biomeProbabilities[1] += 0.2
if areaMap[yi+1][xi-1].isinstance(desertBiome):
biomeProbabilities[1] += 0.2
if areaMap[yi-1][xi-1].isinstance(fieldBiome):
biomeProbabilities[2] += 0.2
if areaMap[yi+1][xi+1].isinstance(fieldBiome):
biomeProbabilities[2] += 0.2
if areaMap[yi-1][xi+1].isinstance(fieldBiome):
biomeProbabilities[2] += 0.2
if areaMap[yi+1][xi-1].isinstance(fieldBiome):
biomeProbabilities[2] += 0.2
choice = numpy.random.choice(biomeList, 4, p=biomeProbabilities)
areaMap[yi][xi] = choice
return areaMap
Explanation:
As you can see, I'm starting off with an empty list. I add baseBiome
to it as a placeholder (up to xi == xMax
and yi == 0
) in order to generate a 2D grid that I can then cycle through.
I create a list biomeProbabilities
with different indices representing different biomes. While cycling through the positions in the map, I check the neighbours of the chosen position and adjust a value in biomeProbabilities
based on its biome.
Finally, I use numpy.random.choice()
with biomeList
and biomeProbabilities
to make a choice from biomeList
using the given probabilities for each item.
My Question
How can I make sure that the sum of every item in biomeProbabilities
is equal to 1 (so that numpy.random.choice
will allow a random probability choice)? There are two logical solutions I see:
a) Assign new probabilities so that the highest-ranking biome is given 0.8
, then the second 0.4
and the third 0.2
b) Add or subtract equal amounts to each one until the sum == 1
Which option (if any) would be better, and how would I implement it?
Also, is there a better way to get the result without resorting to the endless if
statements I've used here?