为什么这个函数会产生混乱的结果而不是计算 Conway's Game Of Life?

问题描述

我正在用 Ruby 编写康威的游戏人生。该游戏基于一个二维布尔数组,其中 true 代表一个活细胞,而 false 代表一个死细胞。 (例如

arr = [
    [false,false,false],[false,true,false]
]

将用于制作滑翔机) 按照规则,相信大多数人都知道,它应该创造一个向右和底部反复改革的格局。但是,当我运行此代码时,它会产生一个爆炸的混乱模式。

Gif with the chaotic result.

这是我的主要函数,它计算新的棋盘状态:

def iterate(arr) 
  arr = arr.dup
  new_arr = arr
  y_counter = 0
  arr.each do |elem|
    x_counter = 0
    elem.each do 
      count = 0
      neighbors = [
        (arr[y_counter-1] || [false,false])[x_counter-1],(arr[y_counter-1] || [false,false])[x_counter],(arr[y_counter-1]|| [false,false])[x_counter+1],(arr[y_counter]|| [false,(arr[y_counter+1]|| [false,]
      neighbors.each do |elem|
        count += 1 if elem
      end
      new_arr[y_counter][x_counter] = (count == 2) || (count == 3 && arr[y_counter][x_counter])
      x_counter+=1
  
    end
    
    y_counter+=1
  end
  new_arr
end

为什么它不起作用,我该怎么做才能解决它?

解决方法

考虑使用几种方法编写代码来执行不同的任务。首先,我们需要在终端中显示网格的图片。我们可能会这样做。

ALIVE = "?" 
DEAD = "?"
def display_grid(grid)
  last_col = grid.first.size
  system("clear")
  grid.each_index do |i|
    puts (0..last_col).map { |j| grid[i][j] ? ALIVE : DEAD }.join
  end
end
display_grid(grid)      

????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????

system("clear") 在 OS X 和 Linux 中清除终端。 system("cls") 在 Windows 中也是如此。我知道 Ruby v2.7 引入了一种跨平台的方式来做到这一点:

require 'io/console'
$stdout.clear_screen # or STDOUT.clear_screen

接下来,对于网格中的每个单元格,我们需要计算其存活的相邻单元格的数量。

def neighbors_alive(grid,(row,col))
   rows = ([row-1,0].max..[row+1,arr.size-1].min).to_a
   cols = ([col-1,0].max..[col+1,arr.first.size-1].min).to_a
   neighbors = rows.product(cols) - [cell]
   neighbors.count { |row,col| grid[row][col] }
end

例如:

neighbors_alive(grid,[0,0])  #=> 0 
neighbors_alive(grid,[1,1])  #=> 0
neighbors_alive(grid,[2,2])  #=> 1
neighbors_alive(grid,[3,3])  #=> 5
neighbors_alive(grid,[4,4])  #=> 2
neighbors_alive(grid,[5,5])  #=> 1
neighbors_alive(grid,[6,6])  #=> 0

对单元格[4,3]的计算如下,该单元格以以下3x3子数组为中心:

[false,false,true],# ??? 
[true,true,# ???
[false,false]] # ???

所以我们希望这个数字是 3

row,col = [4,3]
row
  #=> 4 
col
  #=> 3 
rows = ([row-1,arr.size-1].min).to_a
  #=> [3,4,5] 
cols = ([col-1,arr.first.size-1].min).to_a
  #=> [2,3,4] 
neighbors = rows.product(cols) - [[row,col]]
  #=> [[3,2],3],4],4]]
  #      ?       ?      ?      ?      ?       ?      ?      ?
neighbors.count { |row,col| grid[row][col] }
  #=> 3 

在每次迭代中,我们需要构建一个将死亡的活细胞 (to_die) 和将成为活的死细胞 (to_alive) 的数组。这可以按如下方式完成。

def transitions(grid)
  rows = 0..grid.size - 1
  cols = 0..grid.first.size - 1
  rows.each_with_object([]) do |i,cells_to_flip|
    cols.each do |j|
      cell = [i,j]
      n = neighbors_alive(grid,cell)
      if grid[i][j] # alive
        cells_to_flip << cell unless [2,3].include?(cell)
      else          # dead
        cells_to_flip << cell if n == 3
      end
    end
  end
end
cells_to_flip = transitions(grid)
  #=> [[2,3]] 

然后我们需要更新网格。

def update_grid(grid,cells_to_flip)
  cells_to_flip.each { |i,j| grid[i][j] = ! grid[i][j] }
end

让我们尝试使用 grid 的深层副本(因此我们不会修改原始 grid,它将在下面的 conway(grid) 中使用),使用 {{1} 的值}} 和 to_die 以上。

to_alive

我们看到所有活细胞都死了,arr = grid.dup.map(&:dup) update_grid(arr,cells_to_flip) display_grid(arr) ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [3,2] 处的死细胞又复活了。


我们现在可以编写一个方法来玩这个游戏。回想一下,在每次迭代中,如果活细胞少于两个或三个以上,活细胞就会死亡,如果活细胞恰好有三个,则死细胞会恢复生机。

[5,3]

让我们试试吧。

def conway(grid,delay,max_iterations)
   rows = 0..grid.size - 1
   cols = 0..grid.first.size - 1
   iterations = 0
   display_grid(grid)
   sleep(delay)
   loop do
     break if iterations == max_iterations
     iterations += 1
     cells_to_flip = transitions(grid)
     break if cells_to_flip.empty?
     update_grid(grid,cells_to_flip)
     display_grid(grid)
     sleep(delay)
   end
end

这会显示网格三次,间隔两秒,然后退出,因为在最后一次迭代后没有进一步的更改。

conway(grid,2,1_000_000)
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????
????????