Create order from chaos through experimentation and observation

Recently I discovered a relatively simple concept as it relates to basic competency over a particular skillset - to become competent you must first try to experiment. To experiment might mean to stumble around in a somewhat confused and uninformed state until you have a sense of orientation. That being said, I’ve been thinking about how that relates to the basic tenents of computer science competency and what it means to conduct an experiment - even in a foolish implementation - in spite of the fact that there is likely some better implementation somewhere else around the corner. Often I have heard from other software developers make the case that things move at such a rapid pace that they find it difficult to orient themself in a way where they can gain some competency over a new skillset. To that I say, its best to go back to the basics and keep things simple. There are a dozen different ways to implement a solution, but perhaps it can help to attempt to implement things in a foolish way such that you can understand and more importantly practice the fundamentals.

When you are approached with a problem, the best thing you can do is to state what is known, perhaps even axiomatic. Determine the difference between what is known to be absolutely true and what is considered an assumption. Test those assumptions so that you can reveal what can be knowable or unknowable. One such method of testing those assumptions or even to test yourself of what appears to be unclear or imprecise is: ask questions.

Game design and generating features

Let’s take the relatively introductory task of generating a 2D playable tilemap. The first question you will ask is:

  • how is that done?

Broad questions lead to broad answers. Let’s be more precise:

  • what makes up a 2D tilemap?

This is actually something we can figure out quite easily by observing an existing structure.

We can think about or observe patterns as we see them initially and otherwise. We can observe any general photo of a standard game tilemap contains different repeating elements such as grass, trees, rock, water, ore, wood and more. Sometimes when you encounter a problem that has a higher order complexity, its might be useful to reduce that complexity down to something more managable. Knowing that the tilemap contains a lot of individual items, we can take the basic approach of reducing it down to 4 different types:

  • rock, water, grass, trees

Stating these axiomatic elements we elevate the requirement that all tiles are derived from these elemental types. We don’t go into the detail of determining how water or grass is generated because the baseline starts at these elements. In a 3D generated game this line becomes less clear as each elemental item becomes more akin to an individual system in play. Due to the limitations of computing, we can’t go any further down to subatomic particle as what was meant to be a game system is attempting at a simulation of the universe. The line is drawn somewhere. That being said, we can make a choice about how far each basic element goes and whether its possible to create from that level. Furthermore, we have to have some level of focus on the task at hand. Generating a world that can be navigated.

Naive approach first

Once we have the basic tenants of what we are working with, we can play around with the configuration. In doing so, we can now navigate the space of what other researchers have done prior to us. This is where implementing and doing research works really well at times. It is important to know what we are searching for before we actually need to perform a search at all. That being said, it is still useful to begin by implementing basic naive solutions.

  • randomly generate tiles by selecting varying tile types
  • implement rules on top of our basic model (i.e. trees must be surrounded by grass tiles)

Once we have an idea of what is we would like to do, there are follow up questions that can be asked. Such as in the case of “randomly generated”, we can ask the question - what does it mean to randomly generate, how is it unique, what does the term random actually mean? The basic tenents of randomness is such that we have true randomness via physical phenomena and psuedo-random generation via deterministic mathematical formula. As a result, in the more deterministic case, there are a variety of options for psuedo-random generation.

In the phase of selecting the functions we want to use to generate the outcome, each of these functions have their own set of variables, thresholds and properties that can be used as seeds. In machine learning, it is often the case that the weights of varying functions have additional parameters that can be adjusted to create new results. These can be thought of as initial conditions or initialization parameters. In the example here, we can think of features such as:

  • how many possible tile spaces are there to display (size of the world)
  • how many tile types there are
  • how rare or abundant should a particular tile be (rarity)
  • are certain tiles only adjacent to other tile types

Finally, we have arrived at a tangible set of things we can experiment with.

* Elements: make up the fundamental building blocks
* Functions: make up the means for generating outcomes (functions of chaos and order)
* Rules/Variables: building logic, intentional design, and properties that adjust functions

Selecting functions and conducting an experiment

Experimentation helps us build competency through practice. Once we have gone through the process of breaking the problem down into fundamental building blocks, at least to the degree to which we are observing them, we can proceed to select some or some set of models. But rather than sit all day thinking about it, its good to conduct at least something applicable before we can ask further questions: we have to apply something in order to make additional observations.

let tileTypes = ['water', 'sand', 'grass', 'tree'];
let tileCount = tiles.length;
let worldTileSize = 100 * 100;
let tiles = [];
let fn = /* experimenting function */
let fnParams = /* experimenting function initialization parameters */
for (let i = 0; i < worldTileSize; i++) {
    tiles[i] = fn(fnParams, { tiles, i, worldTileSize, tileCount, tileTypes });
}

This is straightforward but it gives us an idea of how we might start playing with models. First, we have an intuitive understanding that the layout of the world has an inherent unpredictability to it while at the same time remaining structured and connected over time. Furthermore, the real world demonstrates self-similarity and repeating patterns all over the place. We can use this observation in order to formulate better models down the road. By stating that the world is inherently connected and changes over time, we can then make the case that our model should also reflect (at least in part) this sort of gradient transition. For instance, if our world included mountains, it would be odd to have a single tile that had a high heightmap surrounded by all low flat tiles. We intuitively understand that a mountain range has peaks and valleys and there is a gradient from the bottom to the top. Finally, we can also make the case that life itself is an agent of chaos, and in this we make the observation that the layout of the world necessarily is formed by the changes that the agents in that world make to it. A river necessarily erodes limestone over time to form a canyon; heat and volcanic ash form new islands…etc.

Given these observations and intuitive understandings we now have a basis for some mathematical models we can play around with:

* Gradients / Smoothing
* Noise and Randomness
* Self-Similar Structures
* Cellular Life / Agents

Exploring realistic models and gameplay constraints

Some constraints are going to be based on computing requirements, some constraints are going to be due to intentional design constraints (such as where you want resources to be available), others are going to be due to the inherent possibility space. The key is understanding the difference between possibility spaces and actual desirable spaces to generate. In world building, this means of course that tradeoffs and constraints should be made to accomodate for desirable outcomes. Different games are going to require more time and effort in intentionally designing the world, while others like minecraft might be so open ended that noise functions are all you might end up needing. It’s important to make this distinction early on as it effects general gameplay outcomes. In a game like Age of Empires, you might find that the distribution of resources must be balanced to accomodate for gameplay. All constraints are necessarily initial conditions for the different functions of generating the world. Apart from this, it also might be good to think about world building in terms of the different systems that make it up.

System building begins and ends in terms of the functional processes that ultimately guide or define behavior. Combining systems allows us to generate emergent effects that we may not have initially seen happen. For instance, we might be able to generate different deterministic topographical models of the terrain but the river systems, weather patterns, and chemical makeup of that terrain might forge new topology, deltas, rock formations, caves patterns, and underground rivers that we could have no good way of predicting how they would be formed. Why is this all useful to know? Well it’s useful to know in so much that we can separate out different tasks in our world building model. We can separate the task of generating a terrain map from the climate system, the climate system may generate effects that lead us to flow of water, watersheds might generate sedement build up and carrying of resources, flora and fauna might gather as a result of watersheds. Generating a world in this sense becomes a balancing act between functional design, simulations, and drawing inspiration from physical sciences.

Some topics I would like to elaborate further on the future of this discussion:

  • Psuedorandomness, Noise, Quasirandomness
  • Self-Similarity, Fractals
  • Cellular Automata, Agents

References

  • Various functions with different distributions for Math.random: https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/
  • Generative designs (book of shaders): https://thebookofshaders.com/10/
  • Random.org https://www.random.org/randomness/
  • RNG in Machine Learning
  • Generative and Possibility Space http://www.possibilityspace.org/tutorial-generative-possibility-space/