Rest in peace, John Horton Conway

There are a ton of different Game of Life interpreters out there! A ton!

Currently for you, getting an interpreter is just a dozen types and a couple of clicks away.

But wait, do you notice a common thing in the interpreters? Every interpreter is graphical, i.e there are only images and no ASCII text!

The Problem

Fun fact: I was in the middle of creating a 2DF interpreter in my favourite practical programming language while writing this question. 2DF has a command that performs a step of Game of Life on its memory field. I needed to implement that command in my code. I have not done it yet, so this question is technically an actual question rather than a challenge.

I have a list of some points or coordinates on a Cartesian plane. Each point has an X and a Y value. This is your input! Input can be formated in anyway (you can use any type of formatting, so specify your format in your answers).

Example input format:

[[0, 0], [1, 0], [2, 0], [2, 1], [1, 2]]

This is a representation of a period of Glider (The Hacker Logo). Draw the points on a grid, and you will see a shape that resembles this:

A CGoL Glider

What I want for output is the same list, but modified! These are the modification steps.

  • A new point is added if it has exactly three existing neighbours.
  • An existing point is removed if it has more than three or less than two existing neighbours.

What is a neighbour?

A neighbour of a point X is defined as a point adjacent to X either horizontally, vertically, or diagonaly. The neighbours of point \$(2, 3)\$ are:

  • \$(1, 3)\$
  • \$(1, 2)\$
  • \$(2, 2)\$
  • \$(3, 2)\$
  • \$(3, 3)\$
  • \$(3, 4)\$
  • \$(2, 4)\$
  • \$(1, 4)\$

In this case, the output should be similar to this:

[[1, 0], [2, 0], [2, 1], [0, 1], [1, -1]]


Write a program or implement a function that takes a list of points, and prints or returns a list of points that displays the modified list according to the above modification rules (The order of the output list does not matter).

Oh, and be sure to make the code as short as possible! (code-golf)

P.S It would be great if you included an explanation of your program in your answer!

Some Example Test Cases

Example STDIN                                  Example STDOUT
[[0, 0]]                           =>          []
[[0, 0], [0, 1]]                   =>          []
[[0, 0], [0, 1], [1, 0]]           =>          [[0, 0], [0, 1], [1, 0], [1, 1]]
[[0, 0], [0, 1], [1, 0], [1, 1]]   =>          [[0, 0], [0, 1], [1, 0], [1, 1]]
[[0, 0], [0, 1], [0, -1]]          =>          [[0, 0], [1, 0], [-1, 0]]
[[1, 0], [1, 1], [1, 2]]           =>          [[2, 1], [1, 1], [0, 1]]

Good Luck! :)

  • 2
    \$\begingroup\$ While there are many related challenges, since there is significant missing information from the post, I didn't feel confident picking one as a duplicate. This is currently at least missing generating rules, and grid size. \$\endgroup\$ Commented Sep 24, 2020 at 19:14
  • 1
    \$\begingroup\$ The last test case has a -1 in the input. Is that intentional? \$\endgroup\$
    – Adám
    Commented Sep 24, 2020 at 19:54
  • 1
    \$\begingroup\$ @LuisMendo It can only ever expand by 1 in each direction. \$\endgroup\$
    – Adám
    Commented Sep 24, 2020 at 20:02
  • 6
    \$\begingroup\$ The problem statement should probably specify a) if coordinates outside the bounds of the existing points should be included (seems to be true given the test cases, but this should be explicit), and b) what exactly conway's GOL is, because a user trying to answer this challenge should not realistically be expected to just know what that is and a challenge should be self-containing. Other than that, I'm surprised this isn't a duplicate given how many GOL challenges there are, but if it's cleared up a bit I will remove my close vote. \$\endgroup\$
    – hyper-neutrino
    Commented Sep 24, 2020 at 20:15
  • 3
    \$\begingroup\$ Will the input necessarily contain [0,0]? If not, it would be good to have examples where the bounding box is shifted away from the origin. \$\endgroup\$
    – xnor
    Commented Sep 25, 2020 at 0:14

15 Answers 15


APL (Dyalog Extended), 34 32 bytes (SBCS)

Anonymous tacit prefix function. Requires ⎕IO←0 (zero-based indexing).

⌊/{⍺-1-⍸⌂life 0,∘⌽∘⍉⍣4⍸⍣¯1∧⍵-⍺}⊢

Try it online!

⌊/{}⊢ call the following anonymous lambda with the smallest existing x and y coordinates as left argument () and the full list as right argument ():

⍵-⍺ subtract smallest point from all points (scales so every number is non-negative)

 sort ascending

⍸⍣¯1 generate a Boolean matrix with 1s in those positions (lit. inverse of "ɩndices of 1s")

0⍣4 repeat four times with zero as left argument:

  …∘⍉ transpose, then

   …∘⌽ mirror, then (this constitutes a 90° clockwise turn)

    …, concatenate zeros to the left edge

⌂life compute the next Game of Life generation

ɩndices of 1s

1- subtract those from 1

⍺- subtract those from the offset of the lowest x and y coordinates

  • 9
    \$\begingroup\$ That ⌂life built-in is venturing into Mathematica territory! Next step: goat identification! \$\endgroup\$
    – Shaggy
    Commented Sep 24, 2020 at 21:59
  • 1
    \$\begingroup\$ Thank John Scholes \$\endgroup\$
    – Razetime
    Commented Sep 25, 2020 at 16:30

Husk, 17 bytes


Try it online! Note that the interpreter doesn't like extra spaces in inputs.


uṠfo≈6#-¹×z+π2ṡ1D   Input is a list of lists, e.g. L=[[0,0],[1,0],[2,1]]
                D   Repeat twice: X=[[0,0],[1,0],[2,1],[0,0],[1,0],[2,1]]
              ṡ1    Symmetric range to 1: [-1,0,1]
            π2      Cartesian second power: [[-1,-1],[-1,0],…,[1,1]]
         ×          Pick all elements from this and X and combine with
          z+        zipping by addition: [[-1,-1],[0,-1],…,[3,2]]
       -¹           Remove one occurrence of each element of L.
                    Call the result Y.
  f                 Filter by condition:
 Ṡ    #             number of occurrences in Y
   o≈6              is close to 6 (so 5, 6 or 7).
u                   Remove duplicates: [[1,1],[1,0]]

R, 164 156 bytes

Note: now outgolfed in R by my own other answer


Try it online!

Function that accepts and returns coordinates as columns of a 2-row matrix.

Works by creating a matrix & filling it at the specified coordinates, then calculating the next generation & outputting the indices of filled elements.


function(p){            # p = matrix with coords in columns
 m=matrix(F,            # create a matrix, filled with FALSE...
  u<-diff(              #  with row number u equal to the difference...
  r<-range(p))+3,       #  between the range r of values in p, plus 3,
  u)                    #  and the same number of columns
                        #  (so it's a square matrix with dimensions 1 bigger
                        #  than the largest range of p)
 m[t(p-r[1]+2)]=T       # Now, set the elements of m at coordinates p + offset to TRUE
 t(which(               # Then, return the coordinates of elements that satisfy...
  m[]<-(s=              #  m, filled with...
   sapply(1:u^2,        #   the results for each of 1..u^2 (each element of m)...
    function(j)         #   of a function taking argument j...
     sum(m[(j+          #   and returning the sum of elements of m at position j...
     -3:5%/%3*u+-2:0    #   plus the elements at all adjacent positions...
     )%%u^2             #   (modulo the matrix size u^2 to wrap-around the edges)
     +1])               #   +1 (R uses 1-based indices),
   ))==3                #  is equal to 3 (3 neighbours, or filled cell with 2 neighbours)
   |m&s==4,         #  or is itself TRUE and sum is equal to 4 (filled cell with 3 neighbours)
   T))                  # (arr.ind=TRUE = return coordinates, rather than linear indices), 
  +r[1]-2}              # minus the offset used in the first place.

Julia 1.0, 121 91 bytes

f=g->[v for v=Set(h+[j,k] for h=g,j=-1:1,k=-1:1)if 2<sum(i->all(abs.(v-i).<2),g)<4+in(v,g)]

Try it online!


f=g->[v for v=                          # Array Comprehension of final result

    Set(h+[j,k] for h=g,j=-1:1,k=-1:1)  # Set Comprehension of all neighboring cells
                                        # of occupied cells, including self
    if 2<
        sum(i->all(abs.(v-i).<2),g)     # Count the number of neighbors, including self.
                                        # For i in g, count iff the distance
                                        # between v and i is less than 2
                                        # for all dimensions

        <4+in(v,g)                      # <4 if cell is empty, <5 if occupied
  • 1
    \$\begingroup\$ Welcome to CGCC and a really nice answer! This makes me want to learn Julia! \$\endgroup\$ Commented Sep 27, 2020 at 15:40

Wolfram Language, 92 bytes


#+1-Min@#->(1&/@#) converts the input to rules for SparseArray: #+1-Min@# generates the proper shift to make the indices positive and (1&/@#) makes a list of ones of the proper length.

This is then fed into CellularAutomaton to generate a step of Conway's Game of Life. The result is extracted and fed into Position to find the positions of living cells, then the index shift is reversed with -2+Min@# (it's minus 2 rather than minus 1 because CellularAutomaton adds padding to encompass all cells that could be affected).

Try it online!

  • \$\begingroup\$ 84 bytes. It didn't end up being needed, but D' is equal to 1&, and doesn't require parenthesizing. \$\endgroup\$
    – att
    Commented Sep 26, 2020 at 6:57

J, 59 bytes

(([(~.#~3=#/.~)@-.~[:,+/),[#~3 4 e.~[:+/[e."1+/)&(,j./~i:1)

Try it online!

Explanation later.


Python 3, 215 \$\cdots\$ 129 127 bytes

Saved a whopping 36 bytes thanks to att!!!
Saved 2 bytes thanks to ovs!!!

lambda l:{(a+c,b+d)for c in(-1,0,1)for d in(-1,0,1)for a,b in l if((a+c,b+d)in l)+4>sum(abs(a+c-e+(b+d-f)*1j)<2for e,f in l)>2}

Try it online!

Inputs of a list of points and returns the next generation as a list of points.

Explanation (before some golfs)

def f(z):               # input a list of complex numbers  

n={                     # create a set of neighbouring complex numbers
   p+                   # by adding to every point
    a+b*1j              # a vector of 
     for a in(-1,0,1)   # one left, 0, one right: all combined with each of
      for b in(-1,0,1)  # one down, 0, one up 
       for p in z};     # for all the input points

g=lambda                # create a function
   p:                   # mapping a point    
     sum(               # to the sum...   
         0<abs(a-p)<2   #     if the distance between them
                        #     is either 1 or root 2 
          for a in z)   # ...of the live points    

return[(p               # return a list of complex numbers
 for p in n             # for all the points in the neighborhood
  if g[p]==3            # if it has 3 neighbors 
   or g[p]==2and p in z]# or 2 neighbors and is alive
  • \$\begingroup\$ 129 bytes \$\endgroup\$
    – att
    Commented Sep 26, 2020 at 23:35
  • \$\begingroup\$ @att Beautiful - thanks! :D \$\endgroup\$
    – Noodle9
    Commented Sep 26, 2020 at 23:45

Jelly, 25 bytes


A monadic Link accepting a list of coordinates which yields the same after an iteration of the Game Of Life.

Try it online!


-r1ṗ2⁸+€Ẏċ€@Ḅe6,7,9ʋƇ,¥⁸Q - Link: list of coordinates, A
-r1                       - -1 inclusive range 1 -> [-1,0,1]
   ṗ2                     - Cartesian power two -> [0,0] and its the eight neighbours
     ⁸+€                  - add to each of A -> list of lists of an existing cell + its neighbours
        Ẏ                 - tighten -> list of all existing cells and their neighbours
                            call this B
                      ¥⁸  - last two links as a dyad - f(B, A):
                     ,    -   pair -> [B, A]
                    Ƈ     -   filter keep those c in B for which:
                   ʋ      -     last four links as a dyad - f(c, [B, A])
         ċ€@              -       count occurrence of c in each of [B, A]
                                  -> i.e. X = [n_neighbours + is_existing, is_existing]
            Ḅ             -       convert from binary -> 2×n_neighbours+3×is_existing
              6,7,9       -       [6,7,9]
             e            -       exists in? -> i.e. is X in [[3,0],[3,1],[4,1]]?
                        Q - deduplicate

Scala, 151 118 bytes

Uses stuff from the Julia answer by @Vole. Go upvote that!

r= -1 to 1
n=(for(i<-r;j<-r)yield(x+a+i,y+b+j))count g
if n==3|g(x+a,y+b)&n==4}yield(x+a,y+b)

Try it online!

TIO says 119 bytes, but that's because I have to use (x,y) to extract a Tuple2 instead of just x->y, which was added after Scala 2.10 (the version TIO uses).

Dotty, 117 bytes

g=>for{x->y<-g;r= -1 to 1;a<-r;b<-r;n=g.count((i,j)=>math.hypot(x+a-i,y+b-j)<2)if n==3|g(x+a,y+b)&n==4}yield(x+a,y+b)

Try it online


g =>
  for {
    x -> y <- g     //For every point in the grid,
    r = -1 to 1     //Create range from -1 to 1 to use later
    a <- r          //For every a in r 
    b <- r          //For every b in r ((x+a, y+b) is a neighbor of (x,y) or (x,y) itself)
    n = (for(i <- r; j <- r)
          yield (x + a + i, y + b + j) //(x+a, y+b) and all its neighbors
        ) count g   //How many of them are in g?
    if n == 3 | g(x + a, y + b) & n == 4 //Filter the cells that'll be live next round
    //If n=3, it's live and has 2 neighbors, or it's not live and has 3 neighbors, so it'll be live next round. 
    //Otherwise, check if it's live and has 3 neighbors
  } yield (x + a, y + b)  //Yield every such point
  • 1
    \$\begingroup\$ This is a really good explanation. \$\endgroup\$ Commented Sep 28, 2020 at 7:06

R, 132 125 122 bytes


Try it online!

A completely different approach to my other R answer, so posting separately.

Manipulates complex coordinates instead of filling a matrix.

Input is 2 vectors containing x- and y-coordinates of points. Output is a list containing 2 vectors of x- and y-coordinates.


q=x+1i*y,                   # convert x and y into complex coordinates q;
d=-3:5%/%3+1i*-1:1,         # create vector d of all differences to neighbouring cells:
                            #   (so, d = -1-i, -1, -1+i, -i, 0, i, 1-i  1, and 1+i)
n=q+rep(d,e=ncol(p)),       # n = coordinates of neighbouring cells, 
                            #   by adding d to each element of q
s=rowSums(outer(n,n,`==`)), # s = the number of copies of each element in n
                            #   and, therefore, the number of live neighbours it has (including itself) 
i=(s==3|s==4&n%in%q),       # i = indices in n of new generation of cells:
                            #   TRUE if neighbours (including self) ==3, 
                            #   or if neighbours (including self) ==4 AND it's a living cell (so: in q)
c=unique(n[i]))             # c = unique set of new generation of cells
list(Re(c),Im(c))           # output list containing Real & Imaginary parts of c
  • 1
    \$\begingroup\$ Related. \$\endgroup\$
    – Razetime
    Commented Sep 30, 2020 at 8:47
  • 1
    \$\begingroup\$ @Razetime that is very funny! \$\endgroup\$ Commented Sep 30, 2020 at 8:58

Jelly, 39 bytes


Try it online!

This is probably really bad


Wolfram Language (Mathematica), 70 bytes


Try it online!

Takes and returns a list of complex numbers.


JavaScript (ES10),  156  152 bytes


Try it online!


The helper function \$g\$ counts the number \$N\$ of cells around a reference position B[] = [x,y]. For golfing reasons, the reference position is counted as well. The value returned by \$g\$ is \$3-N\$. Therefore:

  • \$g\$ returns \$0\$ or \$-1\$ for a cell surrounded by \$2\$ or \$3\$ other cells respectively, which is characterized by (g() + 0.5 | 0) == 0
  • \$g\$ returns \$0\$ for an empty position surrounded by exactly \$3\$ cells

When it's called with a 2nd argument b[], \$g\$ also pushes into this array the coordinates of all cells that are currently off and must be turned on, provided that this is the first time they are encountered.


a =>                          // a[] = input array
a.flatMap(a =>                // for each coordinate pairs a[] in a[]:
  g(a, b = [])                //   invoke g with an empty array b[]
  + .5 | 0 ?                  //   if the result is neither -1 or 0:
    b                         //     return b[] without a[]
  :                           //   else:
    [...b, a],                //     return b[] with a[]
  g = (                       //   g is a recursive function taking:
    B,                        //     B[] = [x, y]
    b,                        //     b[] = list of coordinate pairs
    k = 9                     //     k = counter
  ) =>                        //
    k-- ?                     //     decrement k; if it was not 0:
      g(                      //       recursive call:
        B, b, k,              //         pass B[], b[] and k unchanged
        g[                    //
          A = [               //         define the coordinates A[] of the neighbor:
            B[0] + k % 3 - 1, //           x' = x + (k mod 3) - 1
            B[1] + ~-(k / 3)  //           y' = y + floor(k / 3) - 1
          ]                   //
        ] |                   //         abort if g[A] is already defined
        a.some(a =>           //         or A[] exists in a[]
          k = a + '' == A     //         (in which case k is set to true)
        )                     //
        | !b                  //         or b is not defined
        || g(B = A)           //         otherwise, invoke g with B = A and without
        ?                     //         the 2nd argument; if the result is not 0:
          0                   //           do nothing
        :                     //         else:
          g[B] = b.push(B)    //           append B[] to b[] and set g[B]
      ) - k                   //       end of recursive call; subtract k
    :                         //     else:
      3                       //       return 3 and stop the recursion
)                             // end of flatMap()

05AB1E, 30 bytes


Port of @Zgarb Husk answer, so make sure to upvote him as well.
The reason it's so much longer is due to less convenient builtins. One is that there isn't a remove_first builtin, so we'll use a replace_first builtin instead. In addition, the count doesn't vectorize on a list of pairs, so we have to join each string with a newline delimiter for both the replace_first and count builtins.

Try it online or verify all test cases.


2и                  # Repeat the (implicit) input-list twice
                    #  i.e. [[0,0],[1,0],[2,1]] → [[0,0],[1,0],[2,1],[0,0],[1,0],[2,1]]
  2Ý                # Push list [0,1,2]
    <               # Decrease each by 1: [-1,0,1]
     ©              # Store this list in variable `®` (without popping)
      ã             # Take the cartesian product of this list with itself
                    #  → [[-1,-1],[-1,0],[-1,1],[0,-1],[0,0],[0,1],[1,-1],[1,0],[1,1]]
       δ            # Apply double vectorized on the two lists:
        +           #  Add the values at the same positions in the pairs together
                    #   → [[[-1,-1],[-1,0],[-1,1],[0,-1],[0,0],[0,1],[1,-1],[1,0],[1,1]],
                    #      [[0,-1],[0,0],[0,1],[1,-1],[1,0],[1,1],[2,-1],[2,0],[2,1]],
                    #      [[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]],
                    #      [[-1,-1],[-1,0],[-1,1],[0,-1],[0,0],[0,1],[1,-1],[1,0],[1,1]],
                    #      [[0,-1],[0,0],[0,1],[1,-1],[1,0],[1,1],[2,-1],[2,0],[2,1]],
                    #      [[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]]
         €`         # Flatten it one level down to a list of pairs
D                   # Duplicate this list of pairs
 €»                 # Join each inner pair together with a newline delimiter
                    #  → ["1\n1","1\n0","1\n-1","0\n1","0\n0","0\n-1","-1\n1","-1\n0",
                    #     "-1\n-1","2\n1","2\n0","2\n-1","1\n1","1\n0","1\n-1","0\n1",
                    #     "0\n0","0\n-1","3\n2","3\n1","3\n0","2\n2","2\n1","2\n0",
                    #     "1\n2","1\n1","1\n0","1\n1","1\n0","1\n-1","0\n1","0\n0",
                    #     "0\n-1","-1\n1","-1\n0","-1\n-1","2\n1","2\n0","2\n-1","1\n1",
                    #     "1\n0","1\n-1","0\n1","0\n0","0\n-1","3\n2","3\n1","3\n0",
                    #     "2\n2","2\n1","2\n0","1\n2","1\n1","1\n0"]
   D                # Duplicate this list of strings
    I               # Push the input list of pairs
     €»             # Join each inner pair with a newline delimiter as well
                    #  → ["0\n0","1\n0","2\n1"]
       õ.;          # Replace every first occurrence of the input-pair with an empty string
                    #  → ["1\n1","","1\n-1","0\n1","","0\n-1","-1\n1","-1\n0",
                    #     "-1\n-1","","2\n0","2\n-1","1\n1","1\n0","1\n-1","0\n1",
                    #     "0\n0","0\n-1","3\n2","3\n1","3\n0","2\n2","2\n1","2\n0",
                    #     "1\n2","1\n1","1\n0","1\n1","1\n0","1\n-1","0\n1","0\n0",
                    #     "0\n-1","-1\n1","-1\n0","-1\n-1","2\n1","2\n0","2\n-1","1\n1",
                    #     "1\n0","1\n-1","0\n1","0\n0","0\n-1","3\n2","3\n1","3\n0",
                    #     "2\n2","2\n1","2\n0","1\n2","1\n1","1\n0"]
          s         # Swap so the other list of strings is at the top of the stack again
           ¢        # Count the amount of occurrences of each item
                    #  → [6,3,4,4,3,4,2,2,2,3,4,2,6,5,4,4,3,4,2,2,2,2,3,4,2,6,5,6,5,4,4,
                    #     3,4,2,2,2,3,4,2,6,5,4,4,3,4,2,2,2,2,3,4,2,6,5]
            ®       # Push list [-1,0,1] from variable `®`
             6+     # Add 6 to each: [5,6,7]
               s    # Swap so the list of counts are at the top of the stack
                å   # Check for each count if it occurs in the [5,6,7] list
                    #  → [1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,
                    #     0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1]
                 Ï  # Only keep the pairs at the truthy indices
                    #  → [[1,1],[1,1],[1,0],[1,1],[1,0],[1,1],[1,0],[1,1],[1,0],[1,1],[1,0]]
                  ê # Sort and uniquify the remaining pairs
                    #  → [[1,0],[1,1]]
                    # (after which the result is output implicitly)

Charcoal, 80 75 48 43 bytes


Try it online! Link is to verbose version of code. Just a boring port of @Zgarb's Husk answer. Outputs points using Charcoal's default format which is each coordinate on its own line with points double-spaced from each other. Explanation:


Loop through all neighbourhoods.


Loop through adjacent cells twice but the cell itself only once.


Push all of the resulting cells to the predefined empty list.


Print the first appearance of those cells that appear between 5 and 7 times.

Previous 80 75-byte more Charcoal-y solution:


Try it online! Link is to verbose version of code. Outputs points using Charcoal's default format which is each coordinate on its own line with points double-spaced from each other. Explanation:


Get a transposed copy of the points.


Replace each row of the transpose with a padded range.


Loop over each point.


Jump to that point.


Doubly increment each of the neighbours, limited to 9.


Increment the cell.


Loop over the padded ranges.


Jump to that point.


If it's between 5 and 7, then...


... save that point in the predefined empty list.


Clear the canvas and output the new points.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.