【问题标题】:How to recursively check for attributes in an object?如何递归检查对象中的属性?
【发布时间】:2014-03-16 03:51:09
【问题描述】:

我一直在做一个电路模拟器,有点像 Minecraft 的红石,但是是 2d 的。

到目前为止,我有电源和电线。电源为电线供电,稍后将用于为其他组件供电。

我面临的问题是,当电线从电源断开时,它会保持供电,因为它的所有邻居都通电。

这是图像中表示的问题:

电线未连接到电源

电线已连接并通电

电源已断开,但电线仍处于活动状态

这是来自电线的更新循环,显示如下:

def update(self):
    self.powered=False
    for b in blocks:
        #Find power sources/powered wires
        if not b.powered:
            continue
        adjacent = False
        if b.rect.collidepoint(self.rect.left,self.rect.top-5):
            adjacent = True
        if b.rect.collidepoint(self.rect.left,self.rect.bottom+5):
            adjacent = True
        if b.rect.collidepoint(self.rect.left-5,self.rect.top):
            adjacent = True
        if b.rect.collidepoint(self.rect.right+5,self.rect.top):
            adjacent = True
        if adjacent:
            self.powered = True
            break
    if not self.powered:
        pygame.draw.rect(screen, (0,0,0), self.rect, 0)
    else:
        pygame.draw.rect(screen, (200,0,0), self.rect, 0)

我需要电线由其他电线供电,但前提是它们都连接到电源..所以我想,如果我可以让每个电线段递归地检查它所连接的所有电线,以使确定它已连接到电源,那么我可以实现这一点。但我不知道该怎么做。

我尝试使用线对象内部的其他变量,例如self.sourced,它表示线是否连接到电源,但这导致只能为一根线供电。 (即使电源坏了,电线也停用了,所以它有点工作..)

无论如何,如果有人知道如何做到这一点,请把你的知识借给我。

这里是完整的源代码供参考:

import pygame
from pygame.locals import *

pygame.init()

screen=pygame.display.set_mode((640,480))
blocks=[]

class PowerSource(object):
    def __init__(self,pos):
        self.posx=pos[0]
        self.posy=pos[1]
        self.rect=pygame.Rect(self.posx,self.posy,32,32)
        self.powered=True
    def update(self):
        pygame.draw.rect(screen, (255,0,0), self.rect, 0)
    def repos(self):
        pass
class Circuit(object):
    def __init__(self,pos):
        self.powered=False
        self.posx=pos[0]
        self.posy=pos[1]
        self.rect=pygame.Rect(self.posx,self.posy,32,32)
    def update(self):
        self.powered=False
        for b in blocks:
            if not b.powered:
                continue
            adjacent = False
            if b.rect.collidepoint(self.rect.left,self.rect.top-5):
                adjacent = True
            if b.rect.collidepoint(self.rect.left,self.rect.bottom+5):
                adjacent = True
            if b.rect.collidepoint(self.rect.left-5,self.rect.top):
                adjacent = True
            if b.rect.collidepoint(self.rect.right+5,self.rect.top):
                adjacent = True
            if adjacent:
                self.powered = True
                break
        if not self.powered:
            pygame.draw.rect(screen, (0,0,0), self.rect, 0)
        else:
            pygame.draw.rect(screen, (200,0,0), self.rect, 0)
while True:
    place=1
    screen.fill((255,255,255))
    mse=pygame.mouse.get_pos()
    mse=((mse[0]/32)*32,(mse[1]/32)*32)
    pressed=pygame.mouse.get_pressed()
    if pressed==(1,0,0):
        pressed='L'
    elif pressed==(0,0,1):
        pressed='R'
    for b in blocks:
        b.update()
    pygame.draw.rect(screen, (255,0,0), (mse[0],mse[1],32,32), 2)
    for e in pygame.event.get():
        if e.type==QUIT:
            exit()
    key=pygame.key.get_pressed()
    if key[K_SPACE]:
        for b in blocks:
            if b.rect.collidepoint(mse):
                place=0
        if place==1:
            blocks.append(PowerSource(mse))
    if pressed=='L':
        for b in blocks:
            if b.rect.collidepoint(mse):
                place=0
        if place==1:
            blocks.append(Circuit(mse))

    elif pressed=='R':
        for b in blocks:
            if b.rect.collidepoint(mse):
                blocks.remove(b)
    pygame.display.flip()

【问题讨论】:

  • 这不是你的电源问题的一部分,但我认为如果你开始使用更好的数据结构来保存你的块,你会更轻松地处理所有事情。网格将是一种简单的方法(如果空间为空,则让它保持None)。或者,如果这有太多的内存开销,你可以使用更聪明的东西,比如四叉树。

标签: python recursion pygame circuit


【解决方案1】:

根据我的经验,简短的回答是向对象添加一个标志,指示它们是否已经在递归算法中进行了测试。这是因为你可以有循环。所以当你移除电源时,让它调用一个函数来“检查源”它接触的所有块。每个块将设置它自己的标志,说明“我已经被检查过”,然后再查看它是否有一个源作为邻居。如果它有一个源作为邻居,它会返回“是的,我有一个源”。如果没有,那么它会检查是否有任何邻居有来源,但前提是它们尚未被标记为已检查。然后它将查看是否有任何返回值是“是的,我有一个来源”,如果是,则返回“是”,如果不是,则返回“否”并关闭。

【讨论】:

    【解决方案2】:

    我认为您遇到了一些问题。

    您可能希望为您的块提供某种包含数据结构(另一个类,而不仅仅是一个列表),或者让您的块维护一个邻接列表。我认为后者可能更容易理解这个问题,尽管前者可能对你更好,因为你需要能够删除块(邻接列表有很多簿记)。这取决于你,但我选择邻接列表,因为它对我来说更容易。 :)

    一旦你把它写下来,你应该编写另一个方法作为你的递归入口点。它可以很简单。您可能需要做一些与您去过的地方保持同步的事情;在这里,我将它作为一个集合来完成,它是该方法的一个参数,假设一个名为 neighbors 的邻接列表。

    def is_connected_to_powered(self, visited=()):
        if self in visited:
            return False
        visited = set(visited)
        visited.add(self)
        for neighbor in self.neighbors:
            if neighbor.powered or neighbor.is_connected_to_powered(visited):
                return True
        return False
    

    这回答了这个问题,但我也可以考虑从一个公共基类(Block?)派生 Circuit 和 PowerSource,因为我怀疑拥有适当的 OO 会帮助你。

    【讨论】:

    • 如何创建邻接列表?好像是这样的吧? [,,,,0,0,,0,,0,,] 其中 _ 为 None ,而 0 是对象?
    • @SamTubb 这是一种方法,但也许更简单的方法是只将邻居存储在列表中,而不是None。例如,在构造函数中,您将一个名为neighbors 的成员变量初始化为一个空列表(甚至是一个集合)。当该块获得新邻居时,只需执行block.append(neighbor)(或add 用于集合)。我会研究 Graph 数据结构以帮助您理解,或者考虑构建一个类来帮助您管理 2D 块网格,因为您正在构建一个积木游戏(其他人也建议使用这样的包含结构)。
    猜你喜欢
    • 1970-01-01
    • 2015-12-10
    • 2021-01-01
    • 2020-11-18
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 2013-02-04
    相关资源
    最近更新 更多