Forum Discussion
Utilizing Excel's turing capabilities to create Conway's 'Game of Life'
I've updated the Conway Lambda in the first post and replaced the workbook. The issue was IFERROR was resolving to "" when it should have been 0.
In re:
Stepλ(board)
=LAMBDA(r, c,
LET(
n, 18,
state, INDEX(board, r, c),
onboard, SIGN(MOD(r + δr, n + 1)) * SIGN(MOD(c + δc, n + 1)),
count, SUM(IF(onboard, INDEX(board, r + δr, c + δc), 0)) - state,
IF(state, SUM(SIGN(count = {2, 3})), SIGN(count = 3))
)
)
This a really clever use of the functions available! I like how you've really simplified the logic by approaching it this way. My use of IF-AND-OR is slowing calculations a bit.
Below is my MAKEARRAY version. I've compared the results up to 20 iterations against my original and Peter's solution and they're identical.
'Conway with MAKEARRAY
=LAMBDA(matrix,iterations,IF(
iterations = 0,
matrix,
Conway(
LET(
height, ROWS(matrix),
width, COLUMNS(matrix),
CheckNeighbors, LAMBDA(r, c,
LET(
grid_val, INDEX(matrix, r, c),
ext_rows, SEQUENCE(3, , r - 1),
ext_cols, SEQUENCE(, 3, c - 1),
Square3x3, INDEX(matrix, ext_rows, ext_cols),
neighbors, IF(
OR(r = 1, c = 1, r = height, c = width),
0,
SUM(Square3x3) - grid_val
),
IF(
AND(grid_val = 0, neighbors = 3),
1,
IF(AND(grid_val = 1, OR(neighbors = 2, neighbors = 3)), 1, 0)
)
)
),
MAKEARRAY(height, width, CheckNeighbors)
),
iterations - 1
)
))
My goal was to simplify things - mainly the directional checks. For this version I'm supplying the Lambda with a 'padded' matrix:
With MAKEARRAY I then 'offset' from the 'grid-val' with INDEX and the rest becomes simpler.
- Patrick2788Jan 29, 2024Silver Contributor
Here are the timings for each solution:
timer used: vba - fullrecalc
100 iterations - average of 5 times
Original - 3.33 seconds
PB - .6189 seconds
MAKEARRAY- .8585 seconds
- on a 36x150 board - 10 iterations - about 8 seconds- m_tarlerFeb 01, 2024Bronze Contributor
Patrick2788 Again a fun and interesting challenge but I went a very different route (I know imagine that). Here is my solution without any LAMBDA inside:
LIFE=LAMBDA(matrix, iterations, LET( matrix2, IF(iterations > 1, Life(matrix, iterations - 1), matrix), m, IFERROR(VSTACK(0, HSTACK(0, matrix2, 0), 0), 0), neighbors, VSTACK(0, HSTACK(0, m)) + VSTACK(0, m) + VSTACK(0, DROP(m, , 1)) + DROP(m, , 1) + HSTACK(0, m) + HSTACK(0, DROP(m, 1)) + DROP(m, 1) + DROP(m, 1, 1), alive_neighbors, neighbors * m, --DROP( DROP(((neighbors = 3) + ((alive_neighbors > 1) * (alive_neighbors < 4)) > 0), 1, 1), -2, -2 ) )
- Patrick2788Feb 02, 2024Silver Contributor
I like the way you terminate the loop immediately within the first parameter ('matrix2') of LET. My usual go-to is terminating the function with an IF before the function is called in predicable fashion. It's good to see it done smoothly with your formula.
Pad the matrix to make sure each cell has 8 adjacent cells and some clever stacking and addition to find the neighbors. The use of DROP here is a throwback to ctrl+shift+enter arrays and it's elegant here and no logical IF/AND/OR needed! I like the economy of this formula. Thanks for sharing!