Search code examples
rplotautomata

Plotting Automata Grid Iterations in R


I am an experienced R programmer, but beginner with programming automata scripts (in fact I just learned about them recently). I've produced a script that calculates (based on some minimal rules from the wikipedia page on Conway's game of life) the grid-like position for any given starting position. I am fairly confident that it's working at least as far as the data, but I can't figure out the best way to kind of animate or make these plots change in the viewer- in fact I have no idea what the best way to visualize a sort of NxM grid in this way.

Script in Repo

library(dplyr)

grid <- matrix(0,nrow=8,ncol=8) %>% as.data.frame()
positions <- c(1,2,3,4,5,6,7,8)

colnames(grid) <- positions

input_row <- 4
input_col <- 4

grid[input_row-1,input_col] <- 1
grid[input_row,input_col+1] <- 1
grid[input_row+1,input_col+1] <- 1
grid[input_row+1,input_col] <- 1
grid[input_row+1,input_col-1] <- 1


capture_index <- NULL

for(cycle in 1:5){

for(row in 1:nrow(grid)){
  
  for(col in 1:ncol(grid)){
    
    input_row <- row
    input_col <- col 
    
    neighbor_1 <- grid[input_row-1,input_col-1] 
    neighbor_2 <- grid[input_row-1,input_col] 
    neighbor_3 <- grid[input_row-1,input_col + 1] 
    
    neighbor_4 <- grid[input_row,input_col-1] 
    neighbor_5 <- grid[input_row,input_col+1] 
    
    neighbor_6 <- grid[input_row+1,input_col-1] 
    neighbor_7 <- grid[input_row+1,input_col] 
    neighbor_8 <- grid[input_row+1,input_col + 1] 
    
    
    neighbor_sum <- sum(neighbor_1,neighbor_2,neighbor_3,
                        neighbor_4,neighbor_5,neighbor_6,
                        neighbor_7,neighbor_8)
    
    capture_index <- rbind(capture_index,data.frame(row,col,
                                                    current_state = grid[row,col],
                                                    neighbor_sum))
    
  }
  
}

capture_index[is.na(capture_index)] <- 0

process <- capture_index %>% mutate(is_alive = current_state == 1) %>%
  mutate(living_survive = ifelse(is_alive == TRUE & neighbor_sum %in% c(2,3),
                                 'STAY ALIVE','NOTHING')) %>%
  mutate(dead_born = ifelse(is_alive == FALSE & neighbor_sum ==3,
         'BE BORN','NOTHING')) 

process$neighbor_sum[is.na(process$neighbor_sum)] <- 0
process$dead_born[is.na(process$dead_born)] <- 'NOTHING'

#Next Steps
process <- process %>% mutate(next_state = 
                ifelse(is_alive == FALSE & dead_born ==  'BE BORN',
                       1,
                ifelse(is_alive == TRUE & living_survive == 'STAY ALIVE',
                       1,0)))
        
for(i in 1:nrow(process)){
  
  grid[process[i,'row'],process[i,'col']] <- process[i,'next_state']
  
}        

}

For whatever # of cycles you choose, the final result will be a 'grid' object that reflects the new positioning based on rules. The current setting just iterates until they all reach some area in the bottom-right and collapse to a square- but of course this all changes with your initial set up and iteration strategy.

It would be nice making this algorithm somewhat visual.

Thanks in advance! This is my first time submitting a question or answer on stackoverflow- I hope it was okay and any feedback is appreciated!


Solution

  • An obvious choice would be to use gganimate to animate a geom_tile of your grid. The easiest way to do this is to store each iteration of grid in a list.

    For example, if you create an empty list called result_list before your line for(cycle in 1:5), then make the final line in this loop result_list[[cycle]] <- grid, you will store each iteration.

    To get these into a format that can be used by gganimate, you will need to pivot each data frame then bind them together, like this:

    df <- bind_rows(lapply(result_list, function(x) {
      tidyr::pivot_longer(x, everything(), names_to = 'x') %>% 
        mutate(y = rep(8:1, each = 8))
      }), .id = 'time')
    

    Then the plotting code is fairly straighforward:

    library(gganimate)
    
    ggplot(df, aes(x, y, fill = factor(value))) +
      geom_tile(color = 'gray80') +
      scale_fill_manual(values = c('white', 'black')) +
      coord_equal() +
      theme_void() +
      theme(legend.position = 'none') +
      transition_states(as.numeric(df$time), transition_length = 0)
    

    enter image description here