The Origin of Species

We still aren’t simulating anything yet, but we’re getting there, slowly but surely.

When I started this project, I knew simulating life forms would be an essential part of it–not just the intelligent ones that would give rise to civilizations, but the plants and animals that form the critical ecosystems and food chains to¬†support those civilizations. I got suck for quite a while on exactly how to accomplish it. Early on, I decided I would want to use some kind of DNA-like system, in which a string of data represented everything SagaSim needed to know about the species. It took me a long time to figure out how best to implement this, but ultimately I ended up devising a rather simple system for it.

I decided to use a string of bits–1s and 0s–to define my species’ DNA. Then I needed to figure out how many to use. I ended up going with 24, since it provides over 16 million possibilities but is not so many bits as to be unmanageable. Then, I had to determine the best way to describe the bits themselves. I created what is, for lack of a better term, a “DNA specification.” Through the use of a rather simple text file, I can create all the DNA bits and describe what specific patterns mean. The file format is rather simple:

[bit number],[bit type]
[0 value,1 value] OR [plain text description, if bit patterns are defined]
[bit pattern],[implied trait]
[bit pattern],[implied trait]
...

So, the first line is the bit number (literally, the position of that bit in the DNA string), followed by a comma, then the “bit type.” There are two bit types:

  • Generalization — A bit that will assign a particular class/type to this species.
  • Association — A bit that will assign a particular attribute/property to this species.

The second line can be formatted one of two ways: it can specify the meaning of the 0 value and the 1 value, separated by a comma; or, it can be a plain text description of this bit’s purpose, if we’re going to use bit masks. After the second line, there can be any number of lines of bit masks. Each bit mask is a sequence of 0s, 1s, and question marks, followed by a comma, then the generalization/association implied by that pattern. Any 1s and 0s in the mask must match exactly in order to apply, question marks indicate that that bit isn’t important for the purposes of the given trait.

Examples are helpful, so here are a few:

1,generalization
plant,animal
2,generalization
generalist,specialist
3,generalization
spore/seed,vertebrate/invertebrate
0?0,spore-bearing
0?1,seed-bearing
1?0,invertebrate
1?1,vertebrate
???,[none]
4,generalization
thornless/thorny,endothermic/ectothermic,small/large
0??1,thorny
1?11,endothermic
1?10,ectothermic
1?00,small bodied
1?01,large bodied
????,[none]

Pretty simple, right? They go all the way up to bit 24:

24,generalization
non-sapient/sapient
101?1???1?0????????11111,sapient
????????????????????????,[none]

You’ll notice that at the end of each list of patterns is an all-wildcard pattern followed by [none]. This is just our “catch-all” pattern to indicate that, if no other matching pattern is found, this bit serves no purpose for this species.

One thing I like about my implementation of the DNA system is that the specification is kept in a text file, and then imported into game at runtime, so it is inherently customizable and easy to update. I considered using XML or some other established, structured format, but I opted against adding such complexity and overhead. I may implement an intermediate XML format for SagaSim’s data files at some point in the future, though, for compatibility with other tools as well as general ease-of-use.

Once a species has been created, all of its traits are kept as part of a species object. This is really just a placeholder until the full species object is created, which will contain all the actions and attributes endowed to that species. What it can do at this point is spit out a description of itself. Here is one for a randomly-generated species:

Species sagasimus testus
Generalizations:
animal
generalist
vertebrate
endothermic
aquatic
carnivore
egg-laying
scavenger
diurnal
domesticable
omnivore
non-migratory
venomous
schooling
Associations:
fur

The DNA string for it is:

101100010011111001111111

And while I admit this is a feature that’s probably not worth much except the amusement factor, I also wrote a mechanism to render a given DNA string as a graphic. Here is the (greatly enlarged) sprite of the DNA string above:

sagasimus testus sprite

Voila!

The algorithm for this is actually really simple. I create an 8×8 image (64 pixels), use the 24 bit DNA string to generate the color (8 bits for red, 8 for green, 8 for blue), then start on the second row and draw four pixels per line, starting from the left. After four pixels are drawn, I go to the next line and keep going, until I have rendered all 24 bits/pixels. I also “mirror” the pixels on the right side of the image, so the sprite is symmetrical. This is very, very simplistic (even silly) but it provides a unique sprite for every species in SagaSim. I plan to use them, or more likely a modified version of them, in the actual simulation.

Just for fun, here’s a 600×600 randomized set of species:

random species set

Set of random species’ sprites.

I still have a couple of issues left to iron out, such as resource contention over the DNA database, but what I have so far seems adequate enough to proceed.

It may be a while until the next update, as I need to write the actual class/type system to go along with the DNA system. The good news is, this means I will actually have some simulation code in place! I will most likely break the coding of the species simulation into multiple blog entries, as I expect it to be somewhat complicated and I’d like to talk about each ability and attribute of the species.

Project Breakdown

This may not be a super exciting entry, as I will be carving up SagaSim into a bunch of pieces to determine what I need to tackle individually. This list will probably change over time, and once I settle on a good project management system, I’ll turn all of them into tasks/projects in it. Gotta have something to motivate and organize here. With my blather out of the way, on to the actual breakdown.

I envision SagaSim as being made up of multiple high-level modules, and then submodules. There are two overarching components to the entire project: the world generation, from the terrain all the way down to individual characters; and the simulation, which controls the interactions of everything that’s been generated.

  • World Generator
    • Noise Generator
    • Noise Normalizer
    • Terrain Generator
    • Climate Generator
    • River Generator
    • Biome Generator
    • Resource Generator
    • Species Generator
      • DNA Model
      • Generalization Classes
      • Species Metaprogram
      • Species Placement Module
    • Civilization Generator
      • Technology Model
      • Sociology Model
        • Religion Model
      • Economy Model
      • Policy Model
      • Civilization Placement Module
    • Character Generator
      • Personality Generator
      • Trait Model
      • Family Model
  • Simulation Engine
    • Species Simulator
    • Civilization Simulator
      • Sociology Simulator
        • Attitude Simulator
      • Economy Simulator
        • Resource Simulator
        • Labor Simulator
        • Commerce Simulator
      • Political Simulator
        • War Simulator
        • Policy Simulator
      • Religion Simulator
      • Technology Simulator
      • Character Simulator
        • Interpersonal Simulator
        • Family Simulator
    • World Simulator
      • Disaster Simulator
      • Disease Simulator

 

Hot Air About Water

Water is obviously an important chemical for any world to sustain life as we know it. It’s why so much of SagaSim’s world generation revolves around the distribution of water. Up to this point, we have oceans and inland lakes, but we’ve not discussed rivers, nor assignment of biomes. Both of these are influenced by climate, and biomes are also influenced by rivers. I will discuss how each is generated.

River Run

Let’s start by generating another new world, because that’s just so much fun. (You’ve probably gotten the impression by now that the world generation process is fairly disposable and I can make new ones quickly and easily. You’d be right!) Let’s start with the terrain and climate maps, since those will determine the behavior of the river generator.

terrain for river demo

Terrain to be used for river demo.

climate for river demo

Climate map to be used for river demo.

After the river generation process is run, the river map is stored as a single image, where blue pixels signify the presence of a river and black pixels signify… well, nothing. They aren’t important to the river map. When overlaid the terrain and climate maps, you can see the rivers pretty easily and the ways they flow should make logical sense:

terrain map with rivers

Terrain map with rivers overlaid.

climate map with rivers

Climate map with rivers overlaid.

You can see how they flow down from high ground to low ground, either working toward the coast or toward the lowest point they can reach. Sometimes, multiple rivers will meet at the same low point and join together. Sometimes, a river may flow around a low level long enough to create a lake. So, how are they actually generated?

It begins by selecting a pixel–any pixel–from the climate map. Each climate is either considered rainy or not. A river can only begin on a rainy climate, so the randomly-chosen pixel is checked, and if it falls on a rainy climate, then it is considered a valid starting point for our river. Then, the river generation process begins. The pixels surrounding our chosen starting point are examined. If any of the surrounding pixels are lower than the current one, they are added to the list of possible successor pixels. Out of that list, the generator always picks the lowest one available. If two or more pixels are tied for the lowest, one is chosen at random. Then, the process is run again from the next pixel. This is done a set number of times, based on the following formula:

river length < (width * height * 0.005)

Given our existing map dimensions (600×465), each river will run for up to 1395 pixels. That may sound like a lot, but bear in mind that once a river reaches the ocean, it has nowhere else to go, and once it reaches its lowest point, it will simply pool around that. Rivers can never climb uphill. The length limiter is admittedly arbitrary. One could generate much longer rivers by making that number larger. I’ve found that 0.001 makes them too short, but 0.005 produces lengthy yet not overwhelming rivers.

This process is run to generate a set number of rivers. I use 100 as a default, but it’s a user-defined option so you can have as many or as few as you like.

Intro to Biomatics

Biomes are easily the most complex part of the world generation, because they bring together all the other elements we’ve created so far: terrain, climate, and rivers. The available biome types are:

  • Tundra
  • Taiga
  • Montane Grass/Shrubland
  • Temperate Coniferous Forest
  • Temperate Broadleaf Forest
  • Tropical Coniferous Forest
  • Mixed Forest
  • Coastal Forest
  • Tropical Broadleaf Forest
  • Temperate Grassland/Savanna/Shrubland
  • Tropical Grassland/Savanna/Shrubland
  • Desert
  • Flooded Grassland/Savanna
  • Riparian
  • Wetland

Each biome type is associated with one or more climate types and one or more terrain types. For a given land pixel, the possible biome types are determined based on the terrain, climate, and whether any adjacent pixels are river or ocean pixels. In particular, riparian biomes must be adjacent to/on a river, and coastal forest biomes must be adjacent to an ocean. Once we have the list of possible biomes for this pixel, the logic is then very much like climate logic: surrounding pixels are examined for their biomes, and the biome generator will either pick one of those or (less likely) pick a random one from the possible biomes for this pixel. This produces a “banding” effect similar to the climate generator. Like the climate generator, the biome generator currently doesn’t wrap seamlessly at the left and right edges of the map. Unlike the climate generator, we only make a single pass to generate the biomes, rather than several. These issues will likely be corrected/modified at some point in the future, but they are sufficient for now.

The final biome map, with the rivers overlaid, looks like this:

biome map with rivers

Biome map with rivers overlaid.

The color key for the map is as follows:

  • Tundra
  • Taiga
  • Montane Grassland/Shrubland
  • Temperate Coniferous Forest
  • Temperate Broadleaf Forest
  • Tropical Coniferous Forest
  • Mixed Forest
  • Coastal Forest
  • Tropical Broadleaf Forest
  • Temperate Grassland/Savanna/Shrubland
  • Tropical Grassland/Savanna/Shrubland
  • Desert
  • Flooded Grassland/Savanna
  • Riparian
  • Wetland

These classifications are all based on the World Wildlife Federation’s terrestrial biome system. No sense reinventing the wheel here, right?

Believe it or not, at this point we’ve taken care of the basics of world generation. We have a planet with a terrain map, we’ve laid out its climate, waterways, and biomes, and now we’re ready to start populating it with forms of life: plants and animals! Those will be discussed next time.

Note: In the course of working on this entry, I realized there was a bug in the river generator, which I’ve since fixed, though the images were generated with the buggy version. Let them serve as a lesson that there are always improvements to be made. Future blog entries will use the corrected river generation algorithm.