首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >康威在Python3中的生命游戏

康威在Python3中的生命游戏
EN

Code Review用户
提问于 2015-02-26 13:42:35
回答 1查看 8.5K关注 0票数 7

我用Python实现了一个编程问题的游戏,有两个不同的版本:一个是简单的脚本,另一个是带有字典的类来初始化各种参数。

下面是一个类的版本:

代码语言:javascript
复制
# 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)

而没有:

代码语言:javascript
复制
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)

我觉得这些文件可能有点广泛。我应该减少评论的数量吗?还有,还有其他的错误或惯例我错过了吗?

EN

回答 1

Code Review用户

回答已采纳

发布于 2015-02-26 14:47:02

我没有机会运行代码,也没有发现任何错误,所以这个答案只涉及风格和设计问题。

下面的大部分内容都与风格指南有关,您应该阅读它,并至少考虑下面的内容。例如,您应该使用一致的引用样式( '",而不是当前的组合)和命名(例如,类应该是GameOfLife)。

代码语言:javascript
复制
# Object oriented implementaition of Conway's Game of life

这应该是一个docstring,而不仅仅是一个注释;模块可以(而且应该!)也有文档字符串。另外,在非OOP版本(它还应该有类的docstring )中缺少了它。

imports应该按字母顺序排列(至少在两个版本之间是一致的!):

代码语言:javascript
复制
import os
import random
import time

拥有文档字符串是非常有用的,但是它们通常应该包括接口,而不是实现。从广义上告诉用户函数的作用,并提供适当参数、返回值和可能出现的错误的详细信息是很好的。关于函数如何工作的详细信息,比如“使用python's with关键字”,并不是这样;这增加了维护开销(如果实现发生变化,您必须更改docstring,而不仅仅是在接口更改时),并给出他们甚至不需要知道的用户信息。

就评论而言,是的,你有太多的评论,而且(更重要的)是评论错误的东西。注释不应该描述代码的功能,例如:

代码语言:javascript
复制
# Swapping this generation with the next
cur_gen, next_gen = next_gen, cur_gen

这是多余的;注释只提供了代码本身的更多信息(您可以假设读过代码的人知道的Python与您知道的一样多)。相反,应该在不清楚代码为什么会这样做的地方使用注释。

常量应该有UPPERCLASS_WITH_UNDERSCORES名称,通常在脚本顶部而不是在if __name__ == '__main__':块中定义。

与其接受和填充空列表,read_grid可能应该接受文件名并返回列表,即:

代码语言:javascript
复制
this_gen = []
read_grid(this_gen)

应成为:

代码语言:javascript
复制
this_gen = read_grid('grid.txt')  # or use a constant e.g. FILE_NAME

同样,init_grid应该考虑维度或列表;提供两者都是不必要的重复信息复制。为什么要预先指定大小,而不是只使用文件中的任何内容?

通常,您应该直接迭代序列,而不是使用索引。

代码语言:javascript
复制
temp = []
for i in range(len(line) - 1):
    if line[i] == "*":
        temp.append(1)
    elif line[i] == ".":
        temp.append(0)

应:

代码语言:javascript
复制
temp = []
for char in line:
    if char == "*":
        temp.append(1)
    elif char == ".":
        temp.append(0)

您还可以查看清单理解,这将使您能够简化许多循环,以及像zipenumerate这样的工具。

我倾向于将if __name__ == '__main__'下的大部分代码分解为一个单一的入口点函数,并为用户的输入添加验证(参见请求用户输入,直到他们给出有效的响应为止。)。然后,文件的最后一部分简单地变成:

代码语言:javascript
复制
if __name__ == '__main__':
    main()

对于类版本,我将创建两个类方法,from_filefrom_size来创建一个新实例。另外,可以将一些设置分解到类属性中:

代码语言:javascript
复制
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选项提取通用代码,即:

代码语言:javascript
复制
cur_gen = []
simulation.init_grid(cur_gen)
simulation.start_simulation(cur_gen)

我还建议将代作为实例属性,而不是将它们作为参数传递。

票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/82657

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档