Conway's Game of Life: Difference between revisions

From APL Wiki
Jump to navigation Jump to search
m (1 revision imported)
No edit summary
Line 1: Line 1:
'''[[wikipedia:Conway's Game of Life|Conway's Game of Life]]''' is a well-known cellular automaton in which each generation of a population 'evolves' from the previous one according to a set of predefined rules.
'''[[wikipedia:Conway's Game of Life|Conway's Game of Life]]''' is a well-known cellular automaton in which each generation of a population 'evolves' from the previous one according to a set of predefined rules.


Here is [[John Scholes]]' famous one-line APL function takes a boolean [[matrix]] as argument and returns a boolean matrix with the new generation.  
Here is [[John Scholes]]' famous one-line APL function takes a boolean [[matrix]] as argument and returns a boolean matrix with the new generation.


<source lang=apl>
<source lang=apl>
Line 249: Line 249:


The final expression ends with a bit of housekeeping using <source lang=apl inline>↑</source> to turn the result back into a simple array (it's currently nested).
The final expression ends with a bit of housekeeping using <source lang=apl inline>↑</source> to turn the result back into a simple array (it's currently nested).
== Historical implementations ==
In October 1971, exactly one year after Martin Gardner's publication of Conway's Game of Life, [[APL Quote Quad]] published an 7-line interactive implementation by Jean Jacques Duby<ref>Jean Jacques Duby: Conway's Game "Life", '''APL Quote Quad''', Vol. III, No. 2 & 3. October 1, 1971.</ref>
After that, several people submitted shorter implementations to APL Quote Quad, including a 6-line function by W. J. Jones (Syracuse University Computing Center, USA) and a 4-line function by D. Bonyun (Dept. of Computer Science at Arcadia University, Canada).<ref>W. J. Jones: Game of Life; D Bonyun: Game of Life, both '''APL Quote Quad''' Vol III No. 5. June 9, 1972.</ref>


== External links ==
== External links ==
Line 254: Line 260:
* [http://youtube.com/watch?v=a9xAKttWgP4 John Scholes' famous video] on YouTube
* [http://youtube.com/watch?v=a9xAKttWgP4 John Scholes' famous video] on YouTube
* [http://dfns.dyalog.com/n_life.htm John Scholes' notes], as part of the [[dfns workspace]], includes a more in-depth treatment
* [http://dfns.dyalog.com/n_life.htm John Scholes' notes], as part of the [[dfns workspace]], includes a more in-depth treatment
== References ==

Revision as of 15:01, 2 March 2020

Conway's Game of Life is a well-known cellular automaton in which each generation of a population 'evolves' from the previous one according to a set of predefined rules.

Here is John Scholes' famous one-line APL function takes a boolean matrix as argument and returns a boolean matrix with the new generation.

      Life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}

The Glider

One interesting pattern that occurs in Life is a Glider:

      glider←3 3⍴1 1 1 1 0 0 0 1 0
      glider
1 1 1
1 0 0
0 1 0

To see how the Glider evolves over the generations, we need to give it some space to move around. The Game of Life takes place on an infinite two-dimensional grid, but that might be a bit large for our computer! Let's start the Glider off in the bottom right-hand corner of a 10 x 10 grid:

      grid←¯10 ¯10↑glider
      grid
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 1 0

After one generation the pattern becomes:

      Life grid
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 1 1 0
0 0 0 0 0 0 0 1 0 1
0 0 0 0 0 0 0 0 0 0

The Glider has slightly changed shape and also moved up and to the left.

Visualising it

To make the pattern easier to see, we can print a symbol where there's a cell and a · otherwise, for example:

      Show←{'·⌺'[⎕IO+⍵]}
      Show grid
··········
··········
··········
··········
··········
··········
··········
·······⌺⌺⌺
·······⌺··
········⌺·

Here's how the Glider evolves over the first ten generations:

      Show¨{Life⍣⍵⊢grid}¨0,⍳9
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│··········│··········│··········│··········│··········│··········│··········│··········│··········│··········│
│··········│··········│··········│··········│··········│··········│··········│··········│··········│··········│
│··········│··········│··········│··········│··········│··········│··········│··········│··········│··········│
│··········│··········│··········│··········│··········│··········│··········│··········│··········│··········│
│··········│··········│··········│··········│··········│··········│··········│··········│··········│······⌺···│
│··········│··········│··········│··········│··········│·······⌺··│······⌺⌺··│······⌺⌺··│·····⌺⌺⌺··│·····⌺⌺···│
│··········│········⌺·│·······⌺⌺·│·······⌺⌺·│······⌺⌺⌺·│······⌺⌺··│······⌺·⌺·│·····⌺⌺···│·····⌺····│·····⌺·⌺··│
│·······⌺⌺⌺│·······⌺⌺·│·······⌺·⌺│······⌺⌺··│······⌺···│······⌺·⌺·│······⌺···│·······⌺··│······⌺···│··········│
│·······⌺··│·······⌺·⌺│·······⌺··│········⌺·│·······⌺··│··········│··········│··········│··········│··········│
│········⌺·│··········│··········│··········│··········│··········│··········│··········│··········│··········│
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘

How the APL code works

If you're completely new to APL, this explanation is probably not the place to start, rather check out our introductions. However, you should be able to get some idea of what's happening even if the details are not yet clear. Just bear in mind that APL evaluates expressions from right to left unless you use parentheses.

To compute a new generation in the Game of Life we first need to find out how many neighbours each live cell has. To keep things simple let's consider the following 5-by-5 grid:

      currentGeneration←5 5⍴0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 1 0 0
      currentGeneration
0 0 0 0 0
0 0 1 1 0
0 1 1 0 0
0 0 1 0 0
0 0 0 0 0

Rotation

If we rotate each row of the grid one place left by using APL's Rotate function , each element is replaced by its right-hand neighbour. In other words, the new grid will have a 1 for all cells whose right-hand neighbour was 1:

      1⌽currentGeneration
0 0 0 0 0
0 1 1 0 0
1 1 0 0 0
0 1 0 0 0
0 0 0 0 0

Similarly we can rotate right to find the left-hand neighbour by using ¯1⌽currentGeneration. By using APL's Outer Product operator ∘. together with the Rotate function we can get a set of three grids containing the left-hand neighbour, the original grid, and the right-hand neighbour:

      ¯1 0 1∘.⌽⊂currentGeneration
┌─────────┬─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 1│0 0 1 1 0│0 1 1 0 0│
│0 0 1 1 0│0 1 1 0 0│1 1 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
└─────────┴─────────┴─────────┘

Extending to two dimensions

Now we can use a similar trick but rotate up and down by using APL's First Axis Rotate function . Used with an Outer Product this gives us a series of 9 grids:

     ¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┬─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 1│0 0 1 1 0│0 1 1 0 0│
│0 0 1 1 0│0 1 1 0 0│1 1 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
├─────────┼─────────┼─────────┤
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 1│0 0 1 1 0│0 1 1 0 0│
│0 0 1 1 0│0 1 1 0 0│1 1 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
├─────────┼─────────┼─────────┤
│0 0 0 1 1│0 0 1 1 0│0 1 1 0 0│
│0 0 1 1 0│0 1 1 0 0│1 1 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
└─────────┴─────────┴─────────┘

The grid in the centre is our original grid, and the others give the neighbours above, below, left, right and diagonally.

Summing it up

Now we can find out how many neighbours each cell in the original grid has by summing the corresponding elements in each of the 9 grids. For a live cell this will give us 1 more than the number of neighbours - e.g. a 4 in the result means a cell has three neighbours. For a dead cell the result is just the number of neighbours:

      +/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 1 2 2 1│
│1 3 4 3 1│
│1 4 5 4 1│
│1 3 3 2 0│
│0 1 1 1 0│
└─────────┘

For a cell to exist in the next generation, one of two rules must be met:

  • Any live cell with two neighbours lives, unchanged, to the next generation
  • Any cell, live or dead, with exactly three neighbours is live in the next generation

This means that all cells which might be live in the next generation will have a sum of 3 (from the first rule) or 3 or 4 (from the second rule).

So we're interested in cells with a sum of 3 or 4, which we can find as follows:

     3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│
│0 1 0 1 0│0 0 1 0 0│
│0 0 0 0 0│0 1 0 1 0│
│0 1 1 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│
└─────────┴─────────┘

The next step is best understood by first considering the two scores 4 and 3 separately

(a) A score of 4 corresponds either to a live cell with three neighbours or a dead cell with four neighbours. Only in the former case do we get a cell in the next generation - i.e. we get a cell if the score is 4 and the cell is currently live. Here we're using APL's And function,

    (⊂currentGeneration)∧4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 0 0 0 0│
│0 0 1 0 0│
│0 1 0 0 0│
│0 0 0 0 0│
│0 0 0 0 0│
└─────────┘

(b) A score of three corresponds either to a live cell with two neighbours or a dead cell with three neighbours. In either case we get a cell in the next generation:

      3=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 0 0 0 0│
│0 1 0 1 0│
│0 0 0 0 0│
│0 1 1 0 0│
│0 0 0 0 0│
└─────────┘

We can write this slightly redundantly by and-ing with 1 (meaning "and the cell is alive or dead") for reasons which will become apparent shortly:

      1∧3=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 0 0 0 0│
│0 1 0 1 0│
│0 0 0 0 0│
│0 1 1 0 0│
│0 0 0 0 0│
└─────────┘

So the next step is to say that a cell is live if either test (a) or test (b) is met. We need to use APL's Or function, . We could write this rather long-windedly as:

     (1∧3=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration)∨(⊂currentGeneration)∧4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 0 0 0 0│
│0 1 1 1 0│
│0 1 0 0 0│
│0 1 1 0 0│
│0 0 0 0 0│
└─────────┘

Refactoring

But we can take advantage of APL's Inner Product operator to do the and operations of each test and then or the result - the ∨.∧ in the following:

     1 currentGeneration∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂currentGeneration
┌─────────┐
│0 0 0 0 0│
│0 1 1 1 0│
│0 1 0 0 0│
│0 1 1 0 0│
│0 0 0 0 0│
└─────────┘

The final expression ends with a bit of housekeeping using to turn the result back into a simple array (it's currently nested).

Historical implementations

In October 1971, exactly one year after Martin Gardner's publication of Conway's Game of Life, APL Quote Quad published an 7-line interactive implementation by Jean Jacques Duby[1]

After that, several people submitted shorter implementations to APL Quote Quad, including a 6-line function by W. J. Jones (Syracuse University Computing Center, USA) and a 4-line function by D. Bonyun (Dept. of Computer Science at Arcadia University, Canada).[2]

External links

References

  1. Jean Jacques Duby: Conway's Game "Life", APL Quote Quad, Vol. III, No. 2 & 3. October 1, 1971.
  2. W. J. Jones: Game of Life; D Bonyun: Game of Life, both APL Quote Quad Vol III No. 5. June 9, 1972.