Skip to content

Instantly share code, notes, and snippets.

@jwinget
Created February 26, 2023 02:04
Show Gist options
  • Select an option

  • Save jwinget/5cfdb382fa0e50b210f09b9b06f206a6 to your computer and use it in GitHub Desktop.

Select an option

Save jwinget/5cfdb382fa0e50b210f09b9b06f206a6 to your computer and use it in GitHub Desktop.
Conway's game of life in R
start_game <- function(side_length) {
# Boards are always square
x <- rep(c(TRUE, FALSE), length.out = side_length^2)
# Generate random probability for cells to start alive or dead
p <- runif(min = 0, max = 1, n = side_length^2)
# Set up the board
board <- sample(x, size = side_length^2, replace = TRUE, prob = p)
# Pre-calculate neighbor squares for all grid positions
neighbors <- purrr::map(1:length(board), ~calc_neighbors(.x, side_length))
game <- (list(board = board,
neighbors = neighbors))
# Game loop
for(i in seq_along(1:1E3)) {
draw_board(game$board)
Sys.sleep(0.05)
game <- update_game(game)
}
}
calc_neighbors <- function(idx, side_length) {
# Given an index, generate its neighbors
out <- c(
idx + 1,
idx - 1,
idx + side_length - 1,
idx + side_length,
idx + side_length + 1,
idx - side_length - 1,
idx - side_length,
idx - side_length + 1
)
# Handle edge cases
# The values to remove are based on the order in the "out" object
rem <- c(rep(TRUE,8))
if (idx %% side_length == 0){
# Right edge
rem[c(1,5,8)] <- FALSE
}
if (idx %% side_length == 1) {
# Left edge
rem[c(2,3,6)] <- FALSE
}
if (idx <= side_length) {
# Top edge
rem[6:8] <- FALSE
}
if (idx > (side_length^2 - side_length)) {
# Bottom edge
rem[3:5] <- FALSE
}
# This (accidentally) treats the world as wraparound
return(out[which(rem)])
}
draw_board <- function(board) {
side_length <- sqrt(length(board))
image(matrix(board, ncol = side_length))
}
update_game <- function(game) {
neighbor_counts <- purrr::imap(game$board, ~{
sum(game$board[game$neighbors[[.y]]])
}) |>
unlist()
#---- DA RULEZ ----
# Any live cell with two or three live neighbours survives.
# Any dead cell with three live neighbours becomes a live cell.
# All other live cells die in the next generation. Similarly, all other dead cells stay dead.
new_board <- purrr::map2(game$board, neighbor_counts, ~{
if(.x == TRUE) {
# Live cell
if(.y ==2 || .y == 3) {
TRUE
} else { FALSE }
} else if (.x == FALSE) {
# Dead cell
if(.y == 3) {
TRUE
} else { FALSE }
}
}) |> unlist()
# No need to change/recalculate neighbors
return(list(board = new_board,
neighbors = game$neighbors))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment