生命游戏 - 如何通过鼠标点击激活细胞?

Game of Life - How can I activate cells with mouseclicks?

python(和编程)的新手,但我正在尝试创建一个生活游戏。我现在是一个生命游戏,从随机数量的活动细胞开始,但我希望能够通过单击它们来激活或停用细胞。

我尝试了几种方法,我知道我必须使用 pygame.MOUSEBUTTONDOWN。我从中获取坐标,但后来我不知道如何使用这些坐标将单元格值从 1 更改为 0 或从 0 更改为 1。

目标是为“'random mode'”(现在)和“'active mode'”创建一个切换,用户可以在其中 select 个单元格。

下面是我目前的代码:

import pygame
import random
import sys

grid_size = width, height = 400, 400
cell_size = 10
color_dead = 0, 0, 0  
color_alive = 255, 0, 0  
fps_max = 10


class GameOfLife:
    def __init__(self):
      #The screen
        pygame.init()
        pygame.display.set_caption("Game of Life - Created by ") 
        self.FPSCLOCK = pygame.time.Clock()
        self.screen = pygame.display.set_mode(grid_size)
        self.clear_screen()  # you clear the screen before it starts running
        pygame.display.flip() #Update the full display Surface to the screen
        self.last_update_completed = 0

        self.active_grid = 0
        self.num_cols = int(width / cell_size)
        self.num_rows = int(height / cell_size)
        self.grids = []
        self.init_grids()
        self.set_grid()
        self.paused = False
        self.game_over = False

    def init_grids(self):
        def create_grid():
            rows = []
            for row_num in range(self.num_rows):
                list_of_columns = [0] * self.num_cols
                rows.append(list_of_columns)
            return rows

        self.grids.append(create_grid())
        self.grids.append(create_grid())
        self.active_grid = 0

    #set_grid(0)  = all dead
    #set_grid(1) = all alive
    #set_grid() = random
    #set_grid(None) = random
    def set_grid(self, value=None, grid =0):
       for r in range(self.num_rows):
           for c in range(self.num_cols):
                if value is None:
                    cell_value = random.choice([0,1])
                else:
                    cell_value = value
                self.grids[grid][r][c] = cell_value

    def draw_grid(self):
        self.clear_screen()  # you clear the screen before it starts running
        for c in range(self.num_cols):
            for r in range(self.num_rows):
                if self.grids[self.active_grid][r][c] == 1:
                    color = color_alive
                else:
                    color = color_dead

                #pygame.draw.rect(self.screen, color, ((c * cell_size + (cell_size / 2)),(r * cell_size + (cell_size / 2)), cell_size, cell_size) )
                pygame.draw.circle(self.screen,
                                  color,
                                   (int(c * cell_size + (cell_size / 2)),
                                   int(r * cell_size + (cell_size / 2))),
                               int(cell_size / 2), 0)
        pygame.display.flip()

    def clear_screen(self):
        self.screen.fill(color_dead)

    def get_cell(self, r, c):
        try:
            cell_value = self.grids[self.active_grid][r][c]
        except:
            #print("Couldn't get cell value: row: %d, col %d" % (r, c))
            cell_value = 0
        return cell_value

    def check_cell_neighbors(self, row_index, col_index):
        # Get the number of alive cells surrounding the current cell
        # self.grids[self.active_grid][r][c]   #is the current cell
        num_alive_neighbors = 0
        num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1)
        num_alive_neighbors += self.get_cell(row_index - 1, col_index)
        num_alive_neighbors += self.get_cell(row_index - 1, col_index + 1)
        num_alive_neighbors += self.get_cell(row_index, col_index - 1)
        num_alive_neighbors += self.get_cell(row_index, col_index + 1)
        num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1)
        num_alive_neighbors += self.get_cell(row_index + 1, col_index)
        num_alive_neighbors += self.get_cell(row_index + 1, col_index + 1)

        #print(num_alive_neighbors)
        #print("alive neighbors: %d")

# Rules
#1 Any live cell with fewer than two live neighbours dies, as if by underpopulation.
#2 Any live cell with two or three live neighbours lives on to the next generation.
#3 Any live cell with more than three live neighbours dies, as if by overpopulation.
#4 Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.


        if self.grids[self.active_grid][row_index][col_index] == 1: #Alive
            if num_alive_neighbors > 3:
                return 0  # it dies of overpopulation # More than three live neighbors, rule number 3.
            if num_alive_neighbors < 2:
                return 0  # it dies of underpopulation = Rule number 1 = fewer than two live neighbors
            if num_alive_neighbors == 2 or num_alive_neighbors == 3:  # If there are 3 or 4 neighbors, and the cell is alive, it stays alive.
                return 1  # Rule number 2. Two or three live neighbours, it continuous to live.
        elif self.grids[self.active_grid][row_index][col_index] == 0: #Dead
            if num_alive_neighbors ==3:
                return 1 #It comes to life.
        return self.grids[self.active_grid][row_index][col_index]

    def update_generation(self):
        """
        Inspect current generation state, prepare next generation
        :return:
        """
        self.set_grid(0, self.inactive_grid())
        for r in range(self.num_rows - 1):
            for c in range(self.num_cols - 1):
                next_gen_state = self.check_cell_neighbors(r, c)
                # Set inactive grid future cell state
                self.grids[self.inactive_grid()][r][c] = next_gen_state  # if it is zero, than is is 1. if it is 1, it is gonna be 0. Picks the offgrid.
        self.active_grid = self.inactive_grid()


        #inspect the current active generation
        # update the inactive grid to store next generation
        #swap out the active grid
        #self.set_grid(None) #This means that you randomize the grid

    def inactive_grid(self):
        return (self.active_grid + 1) % 2

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                mousepos_x, mousepos_y = event.pos
                print(event.pos[0])
                print(cell_size)
                #Get the position of the mouseclick

                #print(mousepos_x, mousepos_y)
                click = pygame.mouse.get_pos()
                #print(click)


            if event.type == pygame.KEYDOWN:
                if event.unicode == 's':
                    if self.paused:
                        self.paused = False
                        print("unpaused")
                    else:
                        self.paused = True
                        print("paused")
                #Randomizin the grid
                elif event.unicode == 'r':
                    print("randomizing the grid")
                    self.active_grid = 0
                    self.set_grid(None, self.active_grid) #randomizing
                    self.set_grid(0,self.inactive_grid()) #set to 0.
                    self.draw_grid() #Even if it is paused.
                # Quitfunction
                elif event.unicode == 'q':
                    print("Quitting the grid")
                    self.game_over = True


                # print(event.unicode)
                # print("Key pressed")
                # print(event.unicode)

            # if event is keypress of "s" then pause the loop/game.
            #if event is keypress "r" then randomize grid
            # if event is keypress of "q"then quit
            if event.type == pygame.QUIT:
                sys.exit()

    def run(self):
        while True:
            if self.game_over:
                return #So if it is game_over by pressing Q, you leave the loop.
            self.handle_events()  # when you run, you want to handle the events
            if self.paused:
                continue
            self.update_generation()  # Upgrade the generation
            self.draw_grid()  # and draw the grid
            self.FPSCLOCK.tick(fps_max)

if __name__ == "__main__":
    game = GameOfLife()
    game.run()

当您获得鼠标的 x 和 y 位置时,您可以索引单元格列表并将该值更改为 1,因此在您的事件处理代码中:

def handle_events(self):
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            mousepos_x, mousepos_y = event.pos
            self.grids[mousepos_y][mousepos_x] = 1 # Index Y rows down, X columns to the right

只要每个单元格都是 1 个像素,这应该会更改该值,因此它会显示在 screen/also 逻辑上。

一种相对快速的方法是简单地进行逆向计算,该计算用于计算 draw_grid() 方法中每个单元格圆的中心点。

这是我稍微修改过的一个版本,以强调计算该点的 x 和 y 坐标所涉及的数学(存储在元组 posn 中):

def draw_grid(self):
    self.clear_screen()  # you clear the screen before it starts running
    for c in range(self.num_cols):
        for r in range(self.num_rows):
            if self.grids[self.active_grid][r][c] == 1:
                color = color_alive
            else:
                color = color_dead

            posn = (int(c * cell_size + cell_size / 2),
                    int(r * cell_size + cell_size / 2))
            pygame.draw.circle(self.screen, color, posn, int(cell_size / 2), 0)

    pygame.display.flip()

下面是如何在事件处理方法中根据事件的鼠标位置计算行和列:

def handle_events(self):
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            mousepos_x, mousepos_y = event.pos
            r, c = ((mousepos_x - cell_size / 2) // cell_size,
                    (mousepos_y - cell_size / 2) // cell_size)
            print(event.pos, '->', (r, c))  # Show result.

        if event.type == pygame.KEYDOWN:
            if event.unicode == 's':
                ...