You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

20 KiB

Game Of Life

--> Reveal the secret of life with these four simple rules! <--

Game of Life (sometimes just Life) is probably the most famous cellular automaton (mathematical simulation set on a grid of cells), which with only four simple rules gives rise to a world with patterns that seem to behave similarly to simple biological life. It was invented in 1970 by John Conway, a famous mathematician who researched games, and has since gained popularity among programmers, mathematicians and other nerds as it's not only scientifically valuable, it is also awesome and fun to play around with as a kind of sandbox toy; it is very easy to program, play around with, modify and can be explored to great depth. The word game is used because what we have here is really a zero-player mathematical game, which is furthermore completely deterministic (there is no randomness), so we only choose some starting state of the world and then watch the game "play itself". Game of Life is similar systems such as rule 110 and Langton's ant.

. . . . . . . . . . . []. . . . . . . .        . . . . . . . . . . [][]. []. . . . . .
. . . [][]. . . . . . [][][][]. . . . .        . . . [][]. . . . . . [][][][]. . . . .
. . []. . []. . . . . . . . []. . . . .        . . []. . []. . . . . []. . [][]. . . .
. . []. . []. . . . . []. . []. . . . .        . [][]. . []. . . . . . []. []. . . . .
. . [][]. [][]. . . . . [][]. . . . . .        . . [][]. . []. . . . . [][]. . . . . .
. . . . [][]. . . . . . . . . . . . . .        . . . [][][][]. . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . []. . . . . . . . . [][]. . . .  ___\  . . . . . . . . . . . . . . [][]. . . .
. . . . . []. . . . . . . . [][]. . . .     /  . . . []. []. . . . . . . . [][]. . . .
. . . [][][]. . . . . . . . . . . . . .        . . . . [][]. . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . []. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . []. . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . []. . . . .        . . . . . . . . . . . . . [][][]. . . .
. . . . . . . . . . []. . . []. . . . .        . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . []. . . . . . . . .        . . . . . . . . . . [][]. . . . . . . .

Two consecutive frames of the game, notice glider at middle left, which keeps moving bottom right, still life at middle right, which just stands still, and oscillator at bottom right, which switches from vertical to horizontal and back.

Study of the game goes extremely deep, people are discovering new patterns and concepts. See e.g. LifeWiki at https://conwaylife.com/wiki/Main_Page, Catalogue at https://catagolue.hatsya.com or Life Lexicon at https://conwaylife.com/ref/lexicon/lex.htm. The Catalogue claims to have discovered 478223 distinct types of objects so far, LifeWiki has 1651 documented patterns (many with just boring numeric names, other ones with funny names like Cthulhu, En retard, Meatball, p58 toadsucker, Overweight spaceship, Why not, even Unix lol).

The following is a sum up of some basic properties of the game, many of which should show why it is so significant:

  • There are only four rules and they are extremely simple but at the same time they give rise to extremely complex behavior, demonstrating emergent behavior, a very important concept.
  • It resembles biological life, showing structures that come to life, move, replicate, merge, die, organize into bigger structures etc., showing that life may really arise from a few simple rules.
  • It is a class 4 cellular automaton, meaning its behavior is "interesting": sometimes it behaves orderly, sometimes in chaotic ways, sometimes the state stabilizes quickly, sometimes it evolves in complex ways for very long time etc.
  • It shows many other phenomena such as chaotic behavior, non-chaotic behavior, self-replication, self-organization, oscillation, self hosting (you can program Game of Life in Game of Life!) etc.
  • It is Turing complete, meaning the game can be used as a universal computer, with the same computational power as any other computer.
  • It is very fun to play around with. There are many creatures to discover, observe and document (making this kind of a Pokémon game lol?).
  • ...

Where to try it out? You can easily write your own game of life simulator, it's very simple -- you can even just copy paste the code below ;) However a nice, more advanced FOSS program is e.g. Golly.

Rules

We have an infinite, regular two dimensional grid of cells, each of which may be in one of two states: alive or dead. Each cell considers its nearest eight cells its neighbors. The game runs in discrete steps, or rounds. Every round each cell counts its neighbors' states and will set its state for the next round according to these rules:

current state next state alive if next state dead if
alive 2 or 3 live neighbors else (under/overpopulation)
dead 3 live neighbors else

This is basically it as for the definition of rules -- as we can see, the game is extremely simple from this point of view. All that remains is now to examine the consequences of these rules, to study what kind of structures arise in the world, how they behave, interact, what statistical properties the world has, what simulations it can run etc.

Rule Implications And Properties

There are thousands of documented structures, kind of "life forms" or "species" behaving in interesting ways, that can exist in game of life, some appear commonly and naturally (from a randomly initialized start state), some are rare and often have to be manually created. Common classes of such structures include still life (structures that persist and won't disappear on their own, some may just persist without even changing -- possibly simplest such structure is a 2x2 block of live cells), oscillators (structures that stay in place and periodically change their appearance, one of the simplest is blinker, a 1x3 block of live cells) and space ships (structures moving through space on their own, one of the simplest and most famous is glider), further include for example guns (structures that produce, or "shoot" space ships), puffers (kind of spaceships that leave a trail behind them), waves, rotors, crawlers etc. Some patterns can self replicate (create an identical copy of themselves), some can grow without limit. Here are some basic life structures:

    []                   []        [][]        [][]  [][]
      []     [][]        []      []    []        []  []
  [][][]     [][]        []        [][]        [][]  [][]

  glider     block     blinker    beehive    mirrored table
  
  
              [][]             [][][][]        [][]
              [][]             []      []      []
                               []                    []
          [][][][]               []    []          [][]
  [][]  []        []
  [][]  [][]      []           lightweight      beacon
        []    []  []  [][]      spaceship
        []  []    []  [][]                   [][]
          [][][][]                         []
                                                 []
          [][]  pin wheel                    [][]
          [][]                                     toad

A typical run of the game from a randomly initialized grid looks a bit similar to the evolution of our Universe: initially (after the Big Bang) the world exhibits a chaotic behavior, looking like a random noise of cells switching on and off seemingly without order, then, after a short while, more orderly patterns of the three basic kinds (still lives, oscillators and space ships) emerge and start interacting, either by being too close to each other or by shooting space ships and hitting each other -- this is a kind of "golden age" of life (interesting events, for example spontaneous emergence of symmetry). After some time this usually settles on a repeating set of states with just still life and oscillators, far enough from each other to influence each other (a kind of heat death of the universe).

Staying at analogies with our Universe, game of life also recognizes its analogy to speed of light (or speed of life), the fastest speed by which information can propagate through the game of life universe -- in the basic version this speed is simply one cell per turn (as any possible pattern, no matter how complex, can only ever influence its immediately neighboring cells in one turn). This speed is exploited by some algorithms to optimize the game's simulation.

As game of life is Turing complete, some things about it are undecidable, for example whether a given pattern will ever appear.

Statistical properties: the following experiments were performed in a world with 128x128 cells and wrapping space. From a random starting state with 50% live cells populations mostly seem to soon somewhat stabilize at around a little bit more than one third of cells being alive. The following shows 16 runs, noting percentage of live cells after each 256 steps (notice how in one case a population just dies out immediately and in another it very much struggles to stay alive):

run 1:  50% 41% 28% 30% 37% 32% 32% 30% 34% 28% 33% 35% 33% 34% 28% 36% 40%
run 2:  50% 42% 28% 38% 32% 32% 40% 43% 39% 26% 34% 35% 38% 25% 37% 29% 44%
run 3:  50% 35% 29% 35% 38% 34% 31% 33% 30% 32% 35% 34% 39% 45% 42% 34% 34%
run 4:  50% 30% 45% 28% 32% 25% 36% 34% 32% 44% 29% 28% 37% 34% 31% 30% 27%
run 5:  50% 29% 29% 23% 29% 37% 39% 28% 35% 28% 32% 43% 43% 20% 20% 31% 34%
run 6:  50% 25% 37% 75% 12% 12% 37% 12% 25% 37% 75% 12% 12% 37% 12% 25% 37%
run 7:  50% 75% 1%  1%  3%  1%  3%  3%  6%  1%  3%  3%  6%  3%  6%  6%  12%
run 8:  50% 0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%  0%
run 9:  50% 42% 31% 35% 21% 31% 28% 32% 33% 35% 30% 35% 30% 33% 47% 30% 28%
run 10: 50% 35% 30% 25% 39% 30% 33% 35% 30% 33% 38% 30% 24% 35% 28% 31% 35%
run 11: 50% 37% 35% 29% 43% 42% 27% 37% 41% 39% 29% 36% 34% 34% 35% 42% 39%
run 12: 50% 34% 39% 43% 41% 37% 39% 37% 38% 32% 35% 30% 32% 33% 32% 32% 28%
run 13: 50% 25% 37% 75% 12% 12% 37% 12% 25% 37% 75% 12% 12% 37% 12% 25% 37%
run 14: 50% 29% 29% 23% 29% 37% 39% 28% 35% 28% 32% 43% 43% 20% 20% 31% 34%
run 15: 50% 27% 27% 32% 36% 36% 35% 35% 33% 22% 21% 40% 31% 29% 46% 34% 31%
run 16: 50% 33% 39% 37% 34% 43% 32% 38% 36% 45% 32% 35% 28% 35% 32% 35% 43%

Code/Programming

Programming a simple version of game of life is quite easy and may take just 10 minutes, however if we aim for greatly optimized efficiency (speed, memory) or features such as truly infinite space or reversible time, matters will of course get very complex. Let's start with the simple way.

The following is a simple C implementation of a wrapping version of game of life (i.e. the world is not actually infinite):

#include <stdio.h>

#define WORLD_SIZE 20

unsigned char world[WORLD_SIZE * WORLD_SIZE];

unsigned char getCell(int x, int y)
{
  return world[y * WORLD_SIZE + x] & 0x01;
}

void setCell(int x, int y)
{
  world[y * WORLD_SIZE + x] |= 0x01;
}

int main(void)
{
  unsigned char random = 30;

  for (int i = 0; i < WORLD_SIZE * WORLD_SIZE; ++i)
  {
    world[i] = random > 127;
    random = random * 13 + 22;
  }

  char in = 0;
  int step = 0;

  while (in != 'q')
  {
    unsigned char *cell = world;

    for (int y = 0; y < WORLD_SIZE; ++y)
    {
      int yU = y == 0 ? (WORLD_SIZE - 1) : (y - 1),
          yD = (y + 1) % WORLD_SIZE,
          xL = WORLD_SIZE - 1,
          xR = 1;

      for (int x = 0; x < WORLD_SIZE; ++x)
      {
        int neighbors =
          getCell(xL,yU) + getCell(x,yU) + getCell(xR,yU) +
          getCell(xL,y)  +                 getCell(xR,y)  +
          getCell(xL,yD) + getCell(x,yD) + getCell(xR,yD);

        if ((*cell) & 0x01)
        {
          putchar('[');
          putchar(']');

          if (neighbors == 2 || neighbors == 3)
            *cell |= 0x02;
        }
        else
        {
          putchar('.');
          putchar(' ');

          if (neighbors == 3)
            *cell |= 0x02;
        }

        xL = x;
        xR = (x + 2) % WORLD_SIZE;

        cell++;
      }

      putchar('\n');
    }
  
    for (int i = 0; i < WORLD_SIZE * WORLD_SIZE; ++i)
      world[i] >>= 1;

    printf("\nstep %d\n",step);
    puts("press RETURN to step, Q to quit");

    step++;
    in = getchar();
  }

  return 0;
}

The world cells are kept in the world array -- each cell holds the current state in the lowest bit. We perform drawing and update of the world at the same time. Notice especially that while updating the cells, we mustn't overwrite the cell's current state until its neighbors have been updated as well! Not realizing this is a common beginner error and results in so called naive implementation. We avoid this error by first storing each cell's next state in the second lowest bit (keeping its current state in the lowest bit), and then, after all cells have been updated, we iterate over all cells again and perform one bit shift to the left (making the computed next states into current states).

For real serious projects there exist highly optimized algorithms such as QuickLife and HashLife -- if you are aiming to create a state-of-the-art program, check them out. Here we will not be discussing them further as the are beyond the scope of this article.

Implementing infinite world: it is possible to program the game so that the world has no boundaries (or possibly has boundaries imposed only by maximum values of the used integer data type; these could of course be removed by using an advanced arbitrary size integer type). The immediate straightforward idea is to simply resize the world when we need more space, i.e initially we allocate some space to the world (let's say 128x128 cells) and once a cell comes to life outside this area we resize it by allocating more memory -- of course this resizing should happen by some bigger step than one because the pattern will likely grow further (so we may resize e.g. from 128x128 right to 256x256). This can of course be highly inefficient, a single glider traveling far away in one direction may cause resizing the world to astronomical size; therefore more smartness can be applied, for example we may allocate spaces by big tiles (let's say 64x64) wherever they are needed (and of course deallocate/free the ones that no longer have any live cells) -- this will require a lot of code for managing the tiles and being able to actually quickly simulate such representation of the world. It would also be possible to have no world array at all but rather only keep a list of cells that are alive, each one storing its coordinates -- this might of course become inefficient for a big number of live cells, however good optimization could make this approach bearable; a basic optimization here would have to focus on very quick determination of each cell's neighbor count, which could be achieved e.g. by keeping the list of the cells sorted (e.g. from northwestmost to southeastmost). Another idea (used e.g. by the QuickLife algorithm) is to use a dynamic tree to represent the world.

Some basic optimization ideas are following: firstly, as shown in the code above, even though we could theoretically only allocate 1 bit for each cell, it is better to store each cell as a whole byte or possibly a whole integer (which will help memory alignment and likely speed up the simulation greatly) -- this also comes with the great feature of being able to store the current state in the lowest bit and older states in higher bits, which firstly allows rewinding time a few states back (which as seen below will be useful further) and secondly we don't need an extra array for performing the cell updates. Next, as another simplest optimization, we may try to skip big empty areas of the world during the update (however watch out for the border where a new cell can spawn due to a neighboring pattern). We may take this further and also skip areas that contain static, unchanging still life -- this could all be done e.g. by dividing the world into tiles (let's say 64x64) and keeping a record about each tile. This can be taken yet further and also detect e.g. periodically repeating still life (such as blinkers); if for example we know a tile contains pattern that repeats with period 2 and we are able to rewind time one step back (which we can easily do, as shown above), we can simply do this step back in time instead of simulating the whole cell. Next we may try to use dynamic programming, e.g. caches and hash tables to keep results of recently performed big pattern simulations to reuse in the future, so that we don't have to simulate them again (i.e. for example remembering how a glider evolved from one frame to another so that next frame we simply copy-paste the result instead of actually simulating each cell again); HashLife algorithm is doing something like this. Also try to focus greatly on the bottle necks such as counting the cell's neighbors -- it will be greatly worth it if you speed this code up, even for the cost of using more memory, i.e. consider things like loop unrolling, function inlining and look up tables for counting the neighbors. Further speedup may be achieved by parallelization (multithreading, GPU, SIMD, ...) as isolated parts of the world may be simulated independently, though this will introduce hardware dependencies and bloat and is therefore discouraged.

Extensions, Modifications And Generalizations

Game of Life can be extended/generalized/modified in great number of ways, some notable are e.g.:

  • Larger Than Life (LTL): Extended neighborhood of cells; one variant is e.g. 9x9 neighbourhood, cell dies with 34 to 58 live neigbors, dead cell becomes live with 34 to 45 live neighbors. Produces interesting, more smooth, bubble-like patterns that can move in various angles.
  • Smooth Life: Continuous generalization of Game of Life, time steps are still discrete.
  • Lenia: A relatively recent, highly generalized continuous version of Game of Life -- it is yet more generalized version of Smooth Life where all variables are continuous, including space, time and states, AND furthermore adds multiple channels ("plains" of existence that interact with each other). This system produces incredible patterns and great many organisms.
  • different grid geometry, more states, additional rules, ...: Slight modifications one can make to experiment, e.g. trying out hexagonal grid, triangular grid, hyperbolic space, 3D and higher dimensional grids, more states (e.g. cells that remember their age) etc. Modifying the base rules is also possible, creating so called life-like automata: the basic game of life is denoted as B3/S23 (born with 3, stays alive with 2 or 3), some life-like variants include e.g. High Life which adds a rule that a dead cell with 6 live neighbors comes alive (B36/S23) -- this gives rise to a new pattern known as replicator.

See Also