为什么这个函数会产生混乱的结果而不是计算康威的生命游戏?
Why does this function produce chaotic results instead of calculating Conway's Game Of Life?
我正在 Ruby 写康威的人生游戏。该游戏基于一个二维布尔数组,其中 true 代表活细胞,false 代表死细胞。 (例如
arr = [
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, true, false, false, false],
[false, false, false, false, true, false, false],
[false, false, true, true, true, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false]
]
将用于制作滑翔机)
按照规则,我相信你们大多数人都知道,它应该创造一个反复向右和底部改革的模式。但是,当我 运行 这段代码时,它会产生一个爆炸性的混乱模式。
这是我的主要功能,它计算新的棋盘状态:
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, false])[x_counter-1],
(arr[y_counter-1] || [false, false, false])[x_counter],
(arr[y_counter-1]|| [false, false, false])[x_counter+1],
(arr[y_counter]|| [false, false, false])[x_counter-1],
(arr[y_counter]|| [false, false, false])[x_counter+1],
(arr[y_counter+1]|| [false, false, false])[x_counter-1],
(arr[y_counter+1]|| [false, false, false])[x_counter],
(arr[y_counter+1]|| [false, false, false])[x_counter+1],
]
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, true], #
[false, false, false]] #
所以我们预计该数字为 3
。
row, col = [4, 3]
row
#=> 4
col
#=> 3
rows = ([row-1, 0].max..[row+1, arr.size-1].min).to_a
#=> [3, 4, 5]
cols = ([col-1, 0].max..[col+1, arr.first.size-1].min).to_a
#=> [2, 3, 4]
neighbors = rows.product(cols) - [[row, col]]
#=> [[3, 2], [3, 3], [3, 4], [4, 2], [4, 4], [5, 2], [5, 3], [5, 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], [3, 2], [3, 4], [4, 2], [4, 3], [4, 4], [5, 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)
中使用),使用 to_die
和 to_alive
以上。
arr = grid.dup.map(&:dup)
update_grid(arr, cells_to_flip)
display_grid(arr)
我们看到所有活细胞死亡,[3, 2]
和 [5, 3]
处的死细胞复活。[=40=]
我们现在可以编写一个方法来玩游戏了。回想一下,在每次迭代中,如果活邻居少于两个或多于三个,活细胞就会死亡,而如果死细胞恰好有三个活邻居,它就会复活。
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)
这显示网格三次,间隔两秒,然后退出,因为在最后一次迭代后没有进一步的更改。
我正在 Ruby 写康威的人生游戏。该游戏基于一个二维布尔数组,其中 true 代表活细胞,false 代表死细胞。 (例如
arr = [
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, true, false, false, false],
[false, false, false, false, true, false, false],
[false, false, true, true, true, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false],
[false, false, false, false, false, false, false]
]
将用于制作滑翔机)
按照规则,我相信你们大多数人都知道,它应该创造一个反复向右和底部改革的模式。但是,当我 运行 这段代码时,它会产生一个爆炸性的混乱模式。
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, false])[x_counter-1],
(arr[y_counter-1] || [false, false, false])[x_counter],
(arr[y_counter-1]|| [false, false, false])[x_counter+1],
(arr[y_counter]|| [false, false, false])[x_counter-1],
(arr[y_counter]|| [false, false, false])[x_counter+1],
(arr[y_counter+1]|| [false, false, false])[x_counter-1],
(arr[y_counter+1]|| [false, false, false])[x_counter],
(arr[y_counter+1]|| [false, false, false])[x_counter+1],
]
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, true], #
[false, false, false]] #
所以我们预计该数字为 3
。
row, col = [4, 3]
row
#=> 4
col
#=> 3
rows = ([row-1, 0].max..[row+1, arr.size-1].min).to_a
#=> [3, 4, 5]
cols = ([col-1, 0].max..[col+1, arr.first.size-1].min).to_a
#=> [2, 3, 4]
neighbors = rows.product(cols) - [[row, col]]
#=> [[3, 2], [3, 3], [3, 4], [4, 2], [4, 4], [5, 2], [5, 3], [5, 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], [3, 2], [3, 4], [4, 2], [4, 3], [4, 4], [5, 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)
中使用),使用 to_die
和 to_alive
以上。
arr = grid.dup.map(&:dup)
update_grid(arr, cells_to_flip)
display_grid(arr)
我们看到所有活细胞死亡,[3, 2]
和 [5, 3]
处的死细胞复活。[=40=]
我们现在可以编写一个方法来玩游戏了。回想一下,在每次迭代中,如果活邻居少于两个或多于三个,活细胞就会死亡,而如果死细胞恰好有三个活邻居,它就会复活。
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)
这显示网格三次,间隔两秒,然后退出,因为在最后一次迭代后没有进一步的更改。