17
\$\begingroup\$

The challenge is to find the shortest implementation of the game of life in 3D (example). These are the rules:

Cells (in this case, cubes) with only 1 or less neighbours die, as if by lonliness.
If exactly 5 cells surround an empty cell, they breed and fill it.
If a cell has 8 or more neighbours, it dies from overcrowding.

Make it at least a 10x10x10, where the layers are outputted individually like this:

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 X 0 0 X 0 0 0 0 0
0 0 X X X 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

Of course, a graphic 3D simulation is also accepted
The starting position may be hardcoded but it must work if it is changed to any starting position. It must be able to calculate any amount of generations, and the user must be able to manually ask for the next generation.

Shortest code in characters wins!

I made my own implementation of this for any (cube) size: http://jensrenders.site88.net/life3D.htm You can use this to test, and you can base your code on mine, although I didn't comment it.

\$\endgroup\$
  • 1
    \$\begingroup\$ Implied the code-golf tag from your statement find the shortest implementation. Please check if it is what you want. You should also give some details about how to input, how many cycles, animated yes/no, ... because it is essential for code-golf to have a robust specification. \$\endgroup\$ – Howard Feb 28 '14 at 16:22
  • \$\begingroup\$ @Howard I added some more specifications, and yes, forgot the code-golf tag ;) thanks for that. \$\endgroup\$ – Jens Renders Feb 28 '14 at 16:30
  • \$\begingroup\$ @PeterTaylor Yes exactly 5, i'll edit it. \$\endgroup\$ – Jens Renders Feb 28 '14 at 16:55
  • \$\begingroup\$ I would add specifics about the output format (e.g. each cell must be separated by a space as in your example, one newline between each grid layer of the output, living and dead cells must be represented by different characters, and those must be visible characters.) Also, bear in mind that if framed as code-golf, you are unlikely to get graphic simulations. \$\endgroup\$ – Jonathan Van Matre Feb 28 '14 at 17:35
  • \$\begingroup\$ Did you really mean to forbid all loopholes discussed in that meta thread, or only those meeting the (dis)approval criteria (+5 score, at least twice as many upvotes as downvotes)? Because I'm sure I could totally think of some pretty interesting "loopholes" to discuss... ;-) \$\endgroup\$ – Ilmari Karonen Feb 28 '14 at 18:53
14
\$\begingroup\$

Mathematica - 120 bytes

g=CellularAutomaton[{(l=Flatten@#;c=l[[14]];n=Total@Drop[l,{14}];Which[n<2||n>7,0,n==5||c==1,1,0<1,0])&,{},{1,1,1}},##]&

Certainly not a contender for the win, but that was not my intention. Also, this could probably be golfed down significantly by just figuring out the rule number. I just really wanted to write a visualisation (although I'm actually sure there are already tons out there). So here we go):

animateGol3d[size_, i_, n_] := 
  ListAnimate[
    Graphics3D[
      Cuboid /@ Position[#, 1], 
      PlotRange -> {{0, size}, {0, size}, {0, size}} + 1
    ] & /@ g[i, n]
  ];

And after experimenting with a bunch of initial conditions, I got things like the following:

enter image description here

And here is one with a grid size of 20x20x20. This took a few seconds to simulate and render:

enter image description here

By the way, this assumes periodic boundary conditions.

\$\endgroup\$
  • \$\begingroup\$ Are those really entering a equilibrium state, or it's just the animation stopping? If the former, you gave me some neat ideas... \$\endgroup\$ – Kroltan Nov 2 '14 at 20:34
  • 1
    \$\begingroup\$ @Kroltan It's been a while, but I'm pretty sure they were reaching equilibrium. \$\endgroup\$ – Martin Ender Nov 2 '14 at 20:44
  • 1
    \$\begingroup\$ Nice, thanks. Individual slices of the equilibrium look very room-map-y for say, a rougelike game. \$\endgroup\$ – Kroltan Nov 2 '14 at 20:48
12
\$\begingroup\$

APL, 46

It took me some time, but I got it down to 46 chars:

{(5=m)∨⍵∧3>|5.5-m←⊃+/,i∘.⌽i∘.⊖(i←2-⍳3)⌽[2]¨⊂⍵}

This is a function that takes a boolean 3D matrix of any size and computes the next generation, according to the given rules. Boundary conditions were not specified, so I chose to wrap around the other side, as in toroidal space.

Explanation

{                           ⊂⍵}   Take the argument matrix and enclose it in a scalar
               (i←2-⍳3)           Prepare an array with values -1 0 1 and call it i
                       ⌽[2]¨      Shift the matrix along the 2nd dim. by each of -1 0 1
           i∘.⊖                   Then for each result do the same along the 1st dimension
       i∘.⌽                       And for each result again along the 3rd dimension
 m←⊃+/,                           Sum element-wise all 27 shifted matrices and call it m

The intermediate result m is a matrix with the same shape as the original matrix, which counts for each element how many cells are alive in its 3×3×3 neighbourhood, including itself. Then:

           |5.5-m   For each element (x) in m, take its distance from 5.5
       ⍵∧3>         If that distance is <3 (which means 3≤x≤8) and the original cell was 1,
 (5=m)∨             or if the element of m is 5, then the next generation cell will be 1.

Example

Define a random 4×4×4 matrix with about 1/3 cells = 1 and compute its 1st and 2nd generation. The ⊂[2 3] at the front is just a trick to print the planes horizontally instead of vertically:

      ⊂[2 3] m←1=?4 4 4⍴3
 1 0 0 0  1 0 1 0  1 0 1 0  0 0 0 1 
 1 1 0 0  0 0 0 0  0 0 0 1  1 0 1 0 
 0 0 0 0  0 1 0 0  0 0 0 0  0 0 1 0 
 1 1 0 0  0 0 0 1  1 0 0 1  0 0 1 0 
      ⊂[2 3] {(5=m)∨⍵∧3>|5.5-m←⊃+/,i∘.⌽i∘.⊖(i←2-⍳3)⌽[2]¨⊂⍵} m
 0 0 0 0  0 0 1 0  1 0 1 0  0 0 0 0 
 1 0 0 0  0 0 1 0  0 0 0 0  1 0 1 0 
 0 0 0 0  0 1 0 0  0 0 0 0  0 0 1 0 
 1 1 0 0  0 0 0 0  1 0 0 0  0 0 1 0 
      ⊂[2 3] {(5=m)∨⍵∧3>|5.5-m←⊃+/,i∘.⌽i∘.⊖(i←2-⍳3)⌽[2]¨⊂⍵}⍣2⊢ m
 0 0 1 0  1 0 1 0  1 0 1 0  0 0 0 0 
 1 0 1 0  0 0 1 1  0 0 0 0  1 0 1 0 
 1 0 0 0  1 1 0 0  0 0 1 0  1 0 1 0 
 1 1 1 0  1 0 0 1  1 0 1 0  0 0 1 0 
\$\endgroup\$
  • \$\begingroup\$ +1 Very nice answer! and indeed, boundaries were not specified so wrap around is allowed. \$\endgroup\$ – Jens Renders Feb 28 '14 at 23:11
9
\$\begingroup\$

J - 42 char

We are assuming a toroidal board (wraps around) in all three dimensions. J's automatic display of results appears to follow the output spec, using 1 for live cells and 0 for dead. This code works on boards of any width, length, and height (can be 10x10x10, 4x5x6, and so on).

(((1&<*<&8)@-*]+.5=-)~[:+/(,{3#<i:1)|.&><)

An explanation follows:

  • ,{3#<i:1 - Subexpression of the list of offsets for the cell and all its neighbours.
    • <i:1 - The list of integers between 1 and -1 inclusive.
    • ,{3# - Make three copies of the list (3#), and take the Cartesian product (,{).
  • (,{3#<i:1)|.&>< - For each set of 3D offsets, shift the array. At a cost of 3 characters, you can change |.&> to |.!.0&> to not have wrap-around.
  • [:+/ - Sum the all the shifted boards together.
  • ((1&<*<&8)@-*]+.5=-)~ - The long outer verb was a hook so it receives the board on the left and the right, and the side on the right we've been shifting and summing. The ~ swaps this around for this inner verb.
    • 5=- - 1 in each cell that the sum-of-shifted-boards minus the original board (i.e. the neighbour count) equals 5, and 0 in all others.
    • ]+. - Logical OR the above with the original board.
    • (1&<*<&8) - 1 if the number being compared between 1 and 8 exclusive, 0 otherwise.
    • (1&<*<&8)@-* - Compare (as above) the neighbour count, and multiply (i.e. logical AND when the domain is only 1 or 0) the logical OR result by this.

Usage is as with the APL, just apply the function to the initial board for each step. J has a functional-power operator ^: to make this easy.

   life =: (((1&<*<&8)@-*]+.5=-)~[:+/(,{3#<i:1)|.&><)  NB. for convenience
   board =: 1 = ?. 4 4 4 $ 4  NB. "random" 4x4x4 board with approx 1/4 ones
   <"2 board  NB. we box each 2D plane for easier viewing
+-------+-------+-------+-------+
|0 0 0 0|1 1 0 0|0 1 0 0|0 0 1 0|
|0 1 0 0|0 0 0 0|0 0 0 1|1 0 0 0|
|0 0 0 0|0 0 1 0|0 1 0 0|0 0 0 1|
|1 1 0 0|1 0 0 0|0 0 0 1|0 1 1 0|
+-------+-------+-------+-------+
   <"2 life board
+-------+-------+-------+-------+
|0 0 0 0|0 1 0 1|0 1 0 0|0 0 1 0|
|1 1 1 1|0 0 0 0|0 0 0 1|1 1 0 0|
|0 0 0 0|0 0 1 1|0 1 0 0|0 0 0 1|
|1 0 0 0|1 0 0 1|0 0 0 1|0 1 1 1|
+-------+-------+-------+-------+
   <"2 life^:2 board  NB. two steps
+-------+-------+-------+-------+
|0 0 0 0|0 1 0 0|0 1 0 0|0 0 0 0|
|0 1 0 0|0 0 0 0|0 0 0 1|0 1 0 0|
|0 0 0 0|0 0 0 0|0 1 0 0|0 0 0 0|
|0 0 0 0|0 0 0 1|0 0 0 0|0 1 1 1|
+-------+-------+-------+-------+
   <"2 life^:3 board  NB. etc
+-------+-------+-------+-------+
|0 1 0 0|1 1 1 0|0 1 0 0|0 1 0 0|
|0 1 0 0|1 0 1 0|1 0 1 0|1 1 1 0|
|1 0 0 0|0 0 0 0|0 1 0 0|0 1 0 0|
|0 0 1 0|0 0 0 0|0 1 0 0|0 1 1 0|
+-------+-------+-------+-------+

I say "random" because the ?. primitive gives reproducible random results by using a fixed seed every time. ? is the true RNG.

\$\endgroup\$
  • \$\begingroup\$ Curse J and its foul |. verb!! Good job. \$\endgroup\$ – Tobia Mar 1 '14 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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