我用Python实现了一个编程问题的游戏,有两个不同的版本:一个是简单的脚本,另一个是带有字典的类来初始化各种参数。
下面是一个类的版本:
# Object oriented implementaition of Conway's Game of life
import random
import time
import os
class GOL():
def __init__(self, rows, cols, delay, num_generations,\
alive_cell="*", dead_cell="."):
self.rows = rows
self.cols = cols
self.delay = delay
self.generations = num_generations
self.alive_cell = alive_cell
self.dead_cell = dead_cell
def read_grid(self, array):
"""
Reads a given grid from a text file and sanitizes it to be used with the
script.
Keyword arguments:
array -- the array into which the grid is loaded.
Using python's with keyword the values of the grid are loaded into the array
line by line. Once the values are loaded, it checks for the boundaries and sets
them to -1
"""
with open("grid.txt", 'r') as f:
for line in f:
temp = []
for i in range(len(line) - 1):
if line[i] == "*":
temp.append(1)
elif line[i] == ".":
temp.append(0)
array += [temp]
print(array)
for i in range(len(array)):
for j in range(len(array[0])):
if (i == 0 or j == 0 or (i == len(array) - 1) or (j == len(array[0]) - 1)):
array[i][j] = -1
def init_grid(self, array):
for i in range(self.rows):
single_row = []
for j in range(self.cols):
if(i == 0 or j == 0 or (i == self.rows - 1) or ( j == self.cols - 1 )):
single_row.append(-1)
else:
ran = random.randint(0,3)
if ran == 0:
single_row.append(1)
else:
single_row.append(0)
array.append(single_row)
def start_simulation(self, cur_gen):
"""
This function runs the simulation.
Keyword arguments:
cur_gen -- the array representing the current generation
This function creates a temp array of same size as the cur_gen array with
random values. It prints the current generation,processses the next
generation and swaps the current genration with the next one and repeats
the process until it has finished running the simulation for num_gen
generations
"""
next_gen = []
self.init_grid(next_gen)
for gen in range(self.generations):
self.print_gen(cur_gen, gen)
self.process_next_gen(cur_gen, next_gen)
time.sleep(self.delay)
# Swapping this generation with the next
cur_gen, next_gen = next_gen, cur_gen
input("Simulation finished. Press any key to exit")
def process_next_gen(self, cur_gen, next_gen):
"""
Keyword arguments:
cur_gen -- array representing the current generation
next_gen -- array representing the next generation
Iterates over current generation array and sets the values for the
cells in the array for the next generation by processing the neighbors
of each cell in the current generation
"""
for i in range(1, self.rows-1):
for j in range(1, self.cols-1):
next_gen[i][j] = self.process_neighbors(i, j, cur_gen)
def process_neighbors(self, x, y, cur_gen):
"""
Returns the value for a given cell in the next generation
Keyword arguments:
x -- row coordinate of the current cell
y -- column coordinate of the current cell
cur_gen -- array representing the current generation
The function first iterates over all the neighbors of the given cell and
sets the neighbor_count variable to the number of alive cells.
It then checks the 4 rules of Conway's game of life and returns the value
of the cell( weather it is dead or alive ).
"""
neighbor_count = 0
# range() method in pyhton is exclusive, therefore to select the range between
# x-1, x+1 we need to set the right interval of the range() method to x+2
for i in range(x-1, x+2):
for j in range(y-1, y+2):
if not(i == x and j == y):
if cur_gen[i][j] != -1:
# The count is incremented by whatever value is contained by the
# neighboring cell. This can either be 0 or 1, but the total will
# always reflect the number of cells alive.
neighbor_count += cur_gen[i][j]
# Checking the 4 rules of game of life.
if cur_gen[x][y] == 1 and neighbor_count < 2:
return 0
if cur_gen[x][y] == 1 and neighbor_count > 3:
return 0
if cur_gen[x][y] == 0 and neighbor_count == 3:
return 1
else:
return cur_gen[x][y]
def print_gen(self, cur_gen, gen):
"""
Function to handle printing each generation
Keyword arguments:
rows -- number of rows in the array
cols -- number of columns in the array
cur_gen -- the array representing the current generation
gen -- the number of the current generation
Simple double for loop for iterating over contents of the array and
printing the representation of alive cells (*) and dead cells (.) to
STDOUT
"""
os.system("clear")
print("Conway's game of life simulation. Generation : " + str(gen + 1))
for i in range(self.rows):
for j in range(self.cols):
if cur_gen[i][j] == -1:
print("#", end = " ")
elif cur_gen[i][j] == 1:
print(self.alive_cell, end = " ")
elif cur_gen[i][j] == 0:
print(self.dead_cell, end = " ")
print("\n")
if __name__ == '__main__':
print("Select choice : ")
print("1: Read initial grid from file 'grid.txt'")
print("2: Generate random grind of size 11X40")
choice = int(input("Option: "))
# Reading the grid from file
if choice == 1:
# temp list for stroring the grid from file
sim_params = {
"rows" : 5,
"cols" : 10,
"delay" : 0.1,
"num_generations" : 2,
"dead_cell" : " "
}
simulation = GOL(**sim_params)
this_gen = []
simulation.read_grid(this_gen)
simulation.start_simulation(this_gen)
elif choice == 2:
# initalizing the starting grid of size 22X62.
sim_params = {
"rows" : 22,
"cols" : 62,
"delay" : 0.1,
"num_generations" : 100,
"dead_cell" : " "
}
simulation = GOL(**sim_params)
cur_gen = []
simulation.init_grid(cur_gen)
simulation.start_simulation(cur_gen)而没有:
import time
import random
import os
def read_grid(array):
"""
Reads a given grid from a text file and sanitizes it to be used with the
script.
Keyword arguments:
array -- the array into which the grid is loaded.
Using python's with keyword the values of the grid are loaded into the array
line by line. Once the values are loaded, it checks for the boundaries and sets
them to -1
"""
with open("grid.txt", 'r') as f:
for line in f:
temp = []
for i in range(len(line) - 1):
if line[i] == "*":
temp.append(1)
elif line[i] == ".":
temp.append(0)
array += [temp]
print(array)
for i in range(len(array)):
for j in range(len(array[0])):
if (i == 0 or j == 0 or (i == len(array) - 1) or (j == len(array[0]) - 1)):
array[i][j] = -1
def init_grid(rows, cols, array):
"""
Creates a array of the given size filling it with alive cells at random.
Keyword arguments:
rows -- number of rows of the array
cols -- number of cols of the array
array -- the array to fill with initial values.
It iterates over all the values possible within the given range and sets the
boundary values to -1. Then it fills the array with random alive(1) and dead (0)
cells.
"""
for i in range(rows):
single_row = []
for j in range(cols):
if(i == 0 or j == 0 or (i == rows - 1) or ( j == cols - 1 )):
single_row.append(-1)
else:
ran = random.randint(0,3)
if ran == 0:
single_row.append(1)
else:
single_row.append(0)
array.append(single_row)
def process_neighbors(x, y, cur_gen):
"""
Returns the value for a given cell in the next generation
Keyword arguments:
x -- row coordinate of the current cell
y -- column coordinate of the current cell
cur_gen -- array representing the current generation
The function first iterates over all the neighbors of the given cell and
sets the neighbor_count variable to the number of alive cells.
"""
neighbor_count = 0
# range() method in pyhton is exclusive, therefore to select the range between
# x-1, x+1 we need to set the right interval of the range() method to x+2
for i in range(x-1, x+2):
for j in range(y-1, y+2):
if not(i == x and j == y):
if cur_gen[i][j] != -1:
# The count is incremented by whatever value is contained by the
# neighboring cell. This can either be 0 or 1, but the total will
# always reflect the number of cells alive.
neighbor_count += cur_gen[i][j]
# Checking the 4 rules of game of life.
if cur_gen[x][y] == 1 and neighbor_count < 2:
return 0
if cur_gen[x][y] == 1 and neighbor_count > 3:
return 0
if cur_gen[x][y] == 0 and neighbor_count == 3:
return 1
else:
return cur_gen[x][y]
def process_next_gen(rows, cols, cur_gen, next_gen):
"""
Keyword arguments:
rows -- number of rows in the current generation array
cols -- number of cols in the current generation array
cur_gen -- array representing the current generation
next_gen -- array representing the next generation
Iterates over current generation array and sets the values for the
cells in the array for the next generation by processing the neighbors
of each cell in the current generation
"""
for i in range(0, rows-1):
for j in range(0, cols-1):
next_gen[i][j] = process_neighbors(i, j, cur_gen)
def print_gen(rows, cols, cur_gen, gen):
"""
Function to handle printing each generation
Keyword arguments:
rows -- number of rows in the array
cols -- number of columns in the array
cur_gen -- the array representing the current generation
gen -- the number of the current generation
Simple double for loop for iterating over contents of the array and
printing the representation of alive cells (*) and dead cells (.) to
STDOUT
"""
os.system("clear")
print("Conway's game of life simulation. Generation : " + str(gen + 1))
for i in range(rows):
for j in range(cols):
if cur_gen[i][j] == -1:
print("#", end = " ")
elif cur_gen[i][j] == 1:
print("*", end = " ")
elif cur_gen[i][j] == 0:
print(".", end = " ")
print("\n")
def start_simulation(rows, cols, cur_gen, num_gen, delay):
"""
This function runs the simulation.
Keyword arguments:
rows -- number of rows in the array
cols -- the number of columns in the array
cur_gen -- the array representing the current generation
num_gen -- the number of generations the simulation has to run for
delay -- time delay between the rendering of each generation
This function creates a temp array of same size as the cur_gen array with
random values. It prints the current generation,processses the next
generation and swaps the current genration with the next one and repeats
the process until it has finished running the simulation for num_gen
generations
"""
next_gen = []
init_grid(rows, cols, next_gen)
for gen in range(num_gen):
print_gen(rows, cols, cur_gen, gen)
process_next_gen(rows, cols, cur_gen, next_gen)
time.sleep(delay)
# Swapping this generation with the next
cur_gen, next_gen = next_gen, cur_gen
input("Simulation finished. Press any key to exit")
# Entry point for the script
if __name__ == '__main__':
# Setting and declaring constatns
_delay = 0.2
_num_gen = 100
_rows = 0
_cols = 0
print("Select choice : ")
print("1: Read initial grid from file 'grid.txt'")
print("2: Generate random grind of size 11X40")
choice = int(input("Option: "))
# Reading the grid from file
if choice == 1:
# temp list for stroring the grid from file
this_gen = []
read_grid(this_gen)
_rows = len(this_gen)
# All rows in the grid have the same number of columns
_cols = len(this_gen[0])
start_simulation(_rows, _cols, this_gen, _num_gen, _delay)
elif choice == 2:
# initalizing the starting grid of size 22X62.
_rows = 22
_cols = 62
this_gen = []
init_grid(_rows, _cols, this_gen)
start_simulation(_rows, _cols, this_gen, _num_gen, _delay)我觉得这些文件可能有点广泛。我应该减少评论的数量吗?还有,还有其他的错误或惯例我错过了吗?
发布于 2015-02-26 14:47:02
我没有机会运行代码,也没有发现任何错误,所以这个答案只涉及风格和设计问题。
下面的大部分内容都与风格指南有关,您应该阅读它,并至少考虑下面的内容。例如,您应该使用一致的引用样式( '或",而不是当前的组合)和命名(例如,类应该是GameOfLife)。
# Object oriented implementaition of Conway's Game of life这应该是一个docstring,而不仅仅是一个注释;模块可以(而且应该!)也有文档字符串。另外,在非OOP版本(它还应该有类的docstring )中缺少了它。
imports应该按字母顺序排列(至少在两个版本之间是一致的!):
import os
import random
import time拥有文档字符串是非常有用的,但是它们通常应该包括接口,而不是实现。从广义上告诉用户函数的作用,并提供适当参数、返回值和可能出现的错误的详细信息是很好的。关于函数如何工作的详细信息,比如“使用python's with关键字”,并不是这样;这增加了维护开销(如果实现发生变化,您必须更改docstring,而不仅仅是在接口更改时),并给出他们甚至不需要知道的用户信息。
就评论而言,是的,你有太多的评论,而且(更重要的)是评论错误的东西。注释不应该描述代码的功能,例如:
# Swapping this generation with the next
cur_gen, next_gen = next_gen, cur_gen这是多余的;注释只提供了代码本身的更多信息(您可以假设读过代码的人知道的Python与您知道的一样多)。相反,应该在不清楚代码为什么会这样做的地方使用注释。
常量应该有UPPERCLASS_WITH_UNDERSCORES名称,通常在脚本顶部而不是在if __name__ == '__main__':块中定义。
与其接受和填充空列表,read_grid可能应该接受文件名并返回列表,即:
this_gen = []
read_grid(this_gen)应成为:
this_gen = read_grid('grid.txt') # or use a constant e.g. FILE_NAME同样,init_grid应该考虑维度或列表;提供两者都是不必要的重复信息复制。为什么要预先指定大小,而不是只使用文件中的任何内容?
通常,您应该直接迭代序列,而不是使用索引。
temp = []
for i in range(len(line) - 1):
if line[i] == "*":
temp.append(1)
elif line[i] == ".":
temp.append(0)应:
temp = []
for char in line:
if char == "*":
temp.append(1)
elif char == ".":
temp.append(0)您还可以查看清单理解,这将使您能够简化许多循环,以及像zip和enumerate这样的工具。
我倾向于将if __name__ == '__main__'下的大部分代码分解为一个单一的入口点函数,并为用户的输入添加验证(参见请求用户输入,直到他们给出有效的响应为止。)。然后,文件的最后一部分简单地变成:
if __name__ == '__main__':
main()对于类版本,我将创建两个类方法,from_file和from_size来创建一个新实例。另外,可以将一些设置分解到类属性中:
class GameOfLife:
"""Represents a Game Of Life grid."""
ALIVE = '*'
DEAD = '.'
def __init__(self, array):
...
def run_simulation(self, delay, num_gen):
"""Actually run your simulation."""
...
@classmethod
def from_file(cls, filename):
"""Create a new instance from a source text file."""
...
@classmethod
def from_size(cls, width, height):
"""Create a new instance of a specified size."""
...
...这使您可以为两个choice选项提取通用代码,即:
cur_gen = []
simulation.init_grid(cur_gen)
simulation.start_simulation(cur_gen)我还建议将代作为实例属性,而不是将它们作为参数传递。
https://codereview.stackexchange.com/questions/82657
复制相似问题