作为初学者在 python 中尝试实施 'Game of Life' 时出现问题
Problem trying to implement 'Game of Life' in python as a beginner
所以我是 python 的绝对初学者,并尝试实施 Conway 的人生游戏。
我根本没有使用任何库,我的网格只是一个由 1 和 0 组成的 50x50 矩阵。
我得到的 next_gen 输出与预期输出不匹配,但我不明白为什么,我们将不胜感激。
这是我的代码:
def alive_neighbours(grid, r, c):
count = 0
if grid[r-1][c-1] == 1:
count += 1
if grid[r-1][c] == 1:
count += 1
if grid[r-1][c+1] == 1:
count += 1
if grid[r][c-1] == 1:
count += 1
if grid[r][c+1] == 1:
count += 1
if grid[r+1][c-1] == 1:
count += 1
if grid[r+1][c] == 1:
count += 1
if grid[r+1][c+1] == 1:
count += 1
return count
grid = [[0 for i in range(50)] for j in range(50)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass # I am yet to define edge case
else:
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
continue
elif grid[r][c] == 1 and (neighbour == 2 or 3):
continue
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
else:
continue
grid = new_grid[:]
def printf(grid):
for r in range(50):
for c in range(50):
if grid[r][c] == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
next_gen(grid)
我也试过重写我的 next_gen 函数,但是使用它矩阵绝对没有变化
next_gen:
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass
else:
if grid[r][c] == 1 and neighbour == 2 or 3:
continue
if grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
if grid[r][c] == 1:
new_grid[r][c] = 0
continue
grid = new_grid[:]
您的代码实际上有很多问题,但第一个也是主要的问题是您更新的网格从未 return 发送给调用者。
这里:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
grid = new_grid[:]
在函数中,grid
是一个局部名称。在函数末尾重新绑定这个名称只会影响局部名称,它不会对全局名称做任何事情。你应该 read this reference article for more in-depth explanations.
你想要的是 return 调用方的网格:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
# return the new grid to the caller
return new_grid
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
# replace previous grid with the new one
grid = next_gen(grid)
对于其他一些问题,这:
if grid[r][c] == 1 and neighbour == 2 or 3:
并不像您认为的那样。
neighbour == 2 or 3
部分实际执行为(neighbour == 2) or 3
。现在 or
运算符 return 要么是第一个不为假的操作数,要么是最后一个操作数。请注意,"that is not false" 表示 "that does not have a false value in a boolean context"(所有 Python 对象都有一个 "truth" 值,对于数字,所有数字都是真实的,除了零)。所以最后,如果neighbours
和2
不同,neighbor的值== 2 or 3is
3, whatever the value of
neighbor`:
>>> foo
1
>>> foo == 1 or 3
True
>>> foo == 2 or 3
3
>>>
并且由于 3
为真,即使 neighbours
实际上是 1 或 4 或 5 等,表达式也将具有真值...
TL;DR:您想要:
`neighbour == 2 or neighbour == 3`
或更简单地说:
`neighbour in (2, 3)`
正如 b运行o 在他的回答中所说,您的代码中存在一些问题,他已经讲述了您的网格问题以及如何在函数中分配给它实际上将本地范围版本指向新网格,而不是全球范围的网格。他还介绍了如何解决这个问题。
您将遇到的另一个问题是,您已经理解仅执行 new_grid = grid
将意味着 new_grid 和网格点在同一个列表中。因此,为了防止这种情况,您已正确完成 new_grid = grid[:]
,因为这将在内存中创建一个新列表并从网格列表中复制数据。然而,这是一个浅拷贝,因此您将创建一个新的列表对象,但将所有列表引用复制到您的列表中。我们可以通过对列表进行浅表复制然后更改新列表中的值来证明这一点。
grid_size = 2
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
new_grid = grid[:]
new_grid[1][1] = "R"
print("grid:", grid)
print("newg:", new_grid)
#output#
grid: [[0, 0], [0, 'R']]
newg: [[0, 0], [0, 'R']]
因此您可以看到,更改一个中的内部列表将更改另一个中的内部列表。所以你需要做一个列表的深拷贝,这样你就不会在你去的时候改变原来的网格。由于康威状态基于原始网格状态,并且正方形的变化不应该影响其他状态。我认为您已经意识到这个概念。
我还对 alive neighbors 进行了更改以简化它。下面是一个快速改编草稿。当你 运行 你应该看到你的滑翔机飞向右下角
from copy import deepcopy
def alive_neighbours(grid, r, c):
differences = (0, -1, +1)
cells_in_square = [(r + a, c + b) for a in differences for b in differences]
total = 0
for x,y in cells_in_square[1:]:
try:
if x >=0 and y>=0:
total += grid[x][y]
except IndexError as ie:
pass #ignore index errors as at the edge of the grid
return total
def next_gen(grid):
new_grid = deepcopy(grid)
for r in range(len(grid)):
for c in range(len(grid)):
neighbour = alive_neighbours(grid, r, c)
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
return new_grid
def printf(grid):
for r in grid:
for c in r:
if c == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
while True:
x = (input("press enter to see next grid: "))
if x:
break
printf(grid)
grid = next_gen(grid)
更新
除此之外,您从下面开始的滑翔机是一个很酷的爆炸器的良好开端
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][24] = 1
grid[26][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][26] = 1
grid[28][25] = 1
所以我是 python 的绝对初学者,并尝试实施 Conway 的人生游戏。 我根本没有使用任何库,我的网格只是一个由 1 和 0 组成的 50x50 矩阵。 我得到的 next_gen 输出与预期输出不匹配,但我不明白为什么,我们将不胜感激。
这是我的代码:
def alive_neighbours(grid, r, c):
count = 0
if grid[r-1][c-1] == 1:
count += 1
if grid[r-1][c] == 1:
count += 1
if grid[r-1][c+1] == 1:
count += 1
if grid[r][c-1] == 1:
count += 1
if grid[r][c+1] == 1:
count += 1
if grid[r+1][c-1] == 1:
count += 1
if grid[r+1][c] == 1:
count += 1
if grid[r+1][c+1] == 1:
count += 1
return count
grid = [[0 for i in range(50)] for j in range(50)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass # I am yet to define edge case
else:
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
continue
elif grid[r][c] == 1 and (neighbour == 2 or 3):
continue
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
else:
continue
grid = new_grid[:]
def printf(grid):
for r in range(50):
for c in range(50):
if grid[r][c] == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
next_gen(grid)
我也试过重写我的 next_gen 函数,但是使用它矩阵绝对没有变化
next_gen:
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass
else:
if grid[r][c] == 1 and neighbour == 2 or 3:
continue
if grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
if grid[r][c] == 1:
new_grid[r][c] = 0
continue
grid = new_grid[:]
您的代码实际上有很多问题,但第一个也是主要的问题是您更新的网格从未 return 发送给调用者。
这里:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
grid = new_grid[:]
在函数中,grid
是一个局部名称。在函数末尾重新绑定这个名称只会影响局部名称,它不会对全局名称做任何事情。你应该 read this reference article for more in-depth explanations.
你想要的是 return 调用方的网格:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
# return the new grid to the caller
return new_grid
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
# replace previous grid with the new one
grid = next_gen(grid)
对于其他一些问题,这:
if grid[r][c] == 1 and neighbour == 2 or 3:
并不像您认为的那样。
neighbour == 2 or 3
部分实际执行为(neighbour == 2) or 3
。现在 or
运算符 return 要么是第一个不为假的操作数,要么是最后一个操作数。请注意,"that is not false" 表示 "that does not have a false value in a boolean context"(所有 Python 对象都有一个 "truth" 值,对于数字,所有数字都是真实的,除了零)。所以最后,如果neighbours
和2
不同,neighbor的值== 2 or 3is
3, whatever the value of
neighbor`:
>>> foo
1
>>> foo == 1 or 3
True
>>> foo == 2 or 3
3
>>>
并且由于 3
为真,即使 neighbours
实际上是 1 或 4 或 5 等,表达式也将具有真值...
TL;DR:您想要:
`neighbour == 2 or neighbour == 3`
或更简单地说:
`neighbour in (2, 3)`
正如 b运行o 在他的回答中所说,您的代码中存在一些问题,他已经讲述了您的网格问题以及如何在函数中分配给它实际上将本地范围版本指向新网格,而不是全球范围的网格。他还介绍了如何解决这个问题。
您将遇到的另一个问题是,您已经理解仅执行 new_grid = grid
将意味着 new_grid 和网格点在同一个列表中。因此,为了防止这种情况,您已正确完成 new_grid = grid[:]
,因为这将在内存中创建一个新列表并从网格列表中复制数据。然而,这是一个浅拷贝,因此您将创建一个新的列表对象,但将所有列表引用复制到您的列表中。我们可以通过对列表进行浅表复制然后更改新列表中的值来证明这一点。
grid_size = 2
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
new_grid = grid[:]
new_grid[1][1] = "R"
print("grid:", grid)
print("newg:", new_grid)
#output#
grid: [[0, 0], [0, 'R']]
newg: [[0, 0], [0, 'R']]
因此您可以看到,更改一个中的内部列表将更改另一个中的内部列表。所以你需要做一个列表的深拷贝,这样你就不会在你去的时候改变原来的网格。由于康威状态基于原始网格状态,并且正方形的变化不应该影响其他状态。我认为您已经意识到这个概念。
我还对 alive neighbors 进行了更改以简化它。下面是一个快速改编草稿。当你 运行 你应该看到你的滑翔机飞向右下角
from copy import deepcopy
def alive_neighbours(grid, r, c):
differences = (0, -1, +1)
cells_in_square = [(r + a, c + b) for a in differences for b in differences]
total = 0
for x,y in cells_in_square[1:]:
try:
if x >=0 and y>=0:
total += grid[x][y]
except IndexError as ie:
pass #ignore index errors as at the edge of the grid
return total
def next_gen(grid):
new_grid = deepcopy(grid)
for r in range(len(grid)):
for c in range(len(grid)):
neighbour = alive_neighbours(grid, r, c)
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
return new_grid
def printf(grid):
for r in grid:
for c in r:
if c == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
while True:
x = (input("press enter to see next grid: "))
if x:
break
printf(grid)
grid = next_gen(grid)
更新
除此之外,您从下面开始的滑翔机是一个很酷的爆炸器的良好开端
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][24] = 1
grid[26][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][26] = 1
grid[28][25] = 1