问题
I am doing the standard Conway's Game of Life program using Python. I am having an issue when trying to count neighbors as I iterate through the array. I created print statements that print the succession of the if statement, as well as the value of count for each statement.
Here is my code: ( I have the questions inside the # throughout the code)
import random
numrows = 10
numcols = 10
def rnd():
rn = random.randint(0,1)
return rn
def initial():
grid = []
count = 0
for x in range(numrows):
grid.append([])
for y in range(numcols):
rand=random.randrange(1,3)
if(rand == 1):
grid[x].append('O')
else:
grid[x].append('-')
for x in grid:
print(*x, sep=' ',end="\n") #this prints the random 2d array
print("")
print("")
answer = 'y'
newgrid = []
count = 0
while(answer == 'y'): # I believe I am going through, checking neighbors
# and moving onto the next index inside these for
#loops below
for r in range(0,numrows):
grid.append([])
for c in range(0,numcols):
if(r-1 > -1 and c-1 > -1): #I use this to check out of bound
if(newgrid[r-1][c-1] == 'O'):#if top left location is O
count = count + 1 #should get count += 1
else:
count = count
print("top left check complete")
print(count)
if(r-1 > -1):
if(newgrid[r-1][c] == 'O'):
count = count + 1
else:
count = count
print("top mid check complete")
print(count)
if(r-1 > -1 and c+1 < numcols):
if(newgrid[r-1][c+1] == 'O'):
count = count + 1
else:
count = count
print("top right check complete")
print(count)
if(c-1 > -1 and r-1 > -1):
if(newgrid[r][c-1] == 'O'):
count = count + 1
else:
count = count
print("mid left check complete")
print(count)
if(r-1 > -1 and c+1 < numcols):
if(newgrid[r][c+1] == 'O'):
count = count + 1
else:
count = count
print("mid right check complete")
print(count)
if(r+1 < numrows and c-1 > -1):
if(newgrid[r+1][c-1] == 'O'):
count = count + 1
else:
count = count
print("bot left check complete")
print(count)
if(r+1 < numrows and c-1 > -1):
if(newgrid[r+1][c] == 'O'):
count = count + 1
else:
count = count
print("bot mid check complete")
print(count)
if(r+1 < numrows and c+1 < numcols):
if(newgrid[r+1][c+1] == 'O'):
count = count + 1
else:
count = count
print("bot right check complete")
print(count)
# I am not sure about the formatting of the code below, how do I know that
# the newgrid[r][c] location is changing? should it be according to the for-
# loop above? Or should it get it's own? If so, how could I construct it as
# to not interfere with the other loops and items of them?
if(newgrid[r][c] == '-' and count == 3):
newgrid[r][c] ='O'
elif(newgrid[r][c] == 'O' and count < 2):
newgrid[r][c] = '-'
elif(newgrid[r][c] == 'O' and (count == 2 or count == 3)):
newgrid[r][c] = 'O'
elif(newgrid[r][c] == 'O' and count > 3):
newgrid[r][c] = '-'
# I'm also confused how to go about printing out the 'new' grid after each
# element has been evaluated and changed. I do however know that after the
# new grid prints, that I need to assign it to the old grid, so that it can
# be the 'new' default grid. How do I do this?
for z in newgrid:
print(*z, sep=' ',end="\n")
answer = input("Continue? y or n( lower case only): ")
newgrid = grid
if(answer != 'y'):
print(" Hope you had a great life! Goodbye!")
initial()
Here is the current output and error message:
>>> initial() - O - - O - - O - - - O - - O - - - O O - O - - O - O O - O O - - O - - O O O O O - O O - - - O O - O - O - O - O - O - O - O O O O - - O - - - - - O O O - - O O O - O - - O - - - - - O O O - O - - - top left check complete 0 top mid check complete 0 top right check complete 0 mid left check complete 0 mid right check complete 0 bot left check complete 0 bot mid check complete 0 Traceback (most recent call last): File "<pyshell#68>", line 1, in <module> initial() File "C:\Users\Ted\Desktop\combined.py", line 86, in initial if(newgrid[r+1][c+1] == 'O'): IndexError: list index out of range
As I iterate through the random array to see what the neighbors are, it seems to be fine up until it moves over to [0][1] while checking the bot right neighbor.
Also, the mid right neighbor should + 1 to count as it is alive. However, even with succession of the if statement, count remains 0?
Question 1: How can I possibly know that my if conditions witll suffice for every instance of [r][c] for all sides of the array?
Question 2: Is my current method of checking out of bounds the best for my situation? Is there a way to make a "check all for out of bounds" before I even check the value?
I am at my wit's end at this point. Thanks in advance for the time taken to help answer my questions
回答1:
You are getting that index error because your newgrid
only contains a single empty row. And your testing for neighbours in newgrid
instead of in grid
(as Blckknght mentions in the comments). I've made a few repairs, but there's a lot more that can be done to improve this code. It looks like it's working now, but it's hard to tell when you're working with random Life forms. :) I suggest giving your program some way of using known Life patterns like blinkers and gliders to see that they behave correctly.
The simplest way to ensure that newgrid
is valid is to copy it from grid
. If we just do newgrid = grid
that simply makes newgrid
another name for the grid
object. To copy a list of lists properly we need to make copies of each of the internal lists. My new code does that with the copy_grid
function.
I've fixed a couple of minor bugs that you had in the if
tests in the section that counts neighbours, and I've simplified the logic that updates a cell from its neighbour count. I've also condensed the code that makes a random grid, and I've added a simple function that can read a Life pattern from a string and build a grid from it. This lets us test the code with a Glider. I've also added a function that makes an empty grid. The program doesn't currently use that function although I used it during my tests, and I guess it's a useful example. :)
import random
# Set a seed so that we get the same random numbers each time we run the program
# This makes it easier to test the program during development
random.seed(42)
numrows = 10
numcols = 10
glider = '''\
----------
--O-------
---O------
-OOO------
----------
----------
----------
----------
----------
----------
'''
# Make an empty grid
def empty_grid():
return [['-' for y in range(numcols)]
for x in range(numrows)]
# Make a random grid
def random_grid():
return [[random.choice('O-') for y in range(numcols)]
for x in range(numrows)]
# Make a grid from a pattern string
def pattern_grid(pattern):
return [list(row) for row in pattern.splitlines()]
# Copy a grid, properly!
def copy_grid(grid):
return [row[:] for row in grid]
# Print a grid
def show_grid(grid):
for row in grid:
print(*row)
print()
def run(grid):
show_grid(grid)
# Copy the grid to newgrid.
newgrid = copy_grid(grid)
while True:
for r in range(numrows):
for c in range(numcols):
# Count the neighbours, making sure that they are in bounds
count = 0
# Above this row
if(r-1 > -1 and c-1 > -1):
if(grid[r-1][c-1] == 'O'):
count += 1
if(r-1 > -1):
if(grid[r-1][c] == 'O'):
count += 1
if(r-1 > -1 and c+1 < numcols):
if(grid[r-1][c+1] == 'O'):
count += 1
# On this row
if(c-1 > -1):
if(grid[r][c-1] == 'O'):
count += 1
if(c+1 < numcols):
if(grid[r][c+1] == 'O'):
count += 1
# Below this row
if(r+1 < numrows and c-1 > -1):
if(grid[r+1][c-1] == 'O'):
count += 1
if(r+1 < numrows):
if(grid[r+1][c] == 'O'):
count += 1
if(r+1 < numrows and c+1 < numcols):
if(grid[r+1][c+1] == 'O'):
count += 1
# Update the cell in the new grid
if grid[r][c] == '-':
if count == 3:
newgrid[r][c] ='O'
else:
if count < 2 or count> 3:
newgrid[r][c] = '-'
# Copy the newgrid to grid
grid = copy_grid(newgrid)
show_grid(grid)
answer = input("Continue? [Y/n]: ")
if not answer in 'yY':
print(" Hope you had a great life! Goodbye!")
break
#grid = random_grid()
grid = pattern_grid(glider)
run(grid)
This code does work correctly, but there is still plenty of room for improvement. For example, here's an improved version of run()
that condenses the neighbour counting section by using a couple of loops.
def run(grid):
show_grid(grid)
# Copy the grid to newgrid.
newgrid = copy_grid(grid)
while True:
for r in range(numrows):
for c in range(numcols):
# Count the neighbours, making sure that they are in bounds
# This includes the cell itself in the count
count = 0
for y in range(max(0, r - 1), min(r + 2, numrows)):
for x in range(max(0, c - 1), min(c + 2, numcols)):
count += grid[y][x] == 'O'
# Update the cell in the new grid
if grid[r][c] == '-':
if count == 3:
newgrid[r][c] ='O'
else:
# Remember, this count includes the cell itself
if count < 3 or count > 4:
newgrid[r][c] = '-'
# Copy the newgrid to grid
grid = copy_grid(newgrid)
show_grid(grid)
answer = input("Continue? [Y/n]: ")
if not answer in 'yY':
print(" Hope you had a great life! Goodbye!")
break
回答2:
To count the neighbors, just follow this simple rule. You will have the row and col of the current cell. Using that form two arrays, one for rows and another for columns. Below would be your logic.
You can find the detailed implementation with a demo video here.
this.neibhours = function() {
var rows = [row-1, row, row+1];
var cols = [col-1, col, col+1];
neibhourCells = [];
for(var i=0; i < rows.length; i++) {
for(var j=0; j < cols.length; j++) {
if(!(col === cols[j] && row === rows[i])) {
var cell = new Cell(rows[i], cols[j])
if(cell.isOnGrid()) {
neibhourCells.push(cell);
}
}
}
}
return neibhourCells;
}
来源:https://stackoverflow.com/questions/40373222/conways-game-of-life-not-counting-neighbors-correctly