首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >python需要帮助查看我的集合在迭代期间被更改的位置

python需要帮助查看我的集合在迭代期间被更改的位置
EN

Stack Overflow用户
提问于 2021-05-26 04:23:30
回答 1查看 50关注 0票数 0

我已经为cs50任务构建了一个扫雷算法。我的代码有身份问题。(在扫雷舰类中)

该程序同时需要python和runner.py,并使用“minesweeper.py runner.py”执行。

在使用python runner.py运行它时,它会给我一个RuntimeError:

代码语言:javascript
运行
AI代码解释
复制
Traceback (most recent call last):
  File "C:\Users\seng\Downloads\minesweeper\minesweeper\runner.py", line 220, in <module>
    ai.add_knowledge(move, nearby)
  File "C:\Users\seng\Downloads\minesweeper\minesweeper\minesweeper.py", line 230, in add_knowledge
    self.updateknowledge()
  File "C:\Users\seng\Downloads\minesweeper\minesweeper\minesweeper.py", line 274, in updateknowledge
    for safe in safes:
RuntimeError: Set changed size during iteration

下面是代码。minesweeper.py

代码语言:javascript
运行
AI代码解释
复制
import itertools
import random
import copy

class Minesweeper():
    """
    Minesweeper game representation
    """

    def __init__(self, height=8, width=8, mines=8):

        # Set initial width, height, and number of mines
        self.height = height
        self.width = width
        self.mines = set()

        # Initialize an empty field with no mines
        self.board = []
        for i in range(self.height):
            row = []
            for j in range(self.width):
                row.append(False)
            self.board.append(row)

        # Add mines randomly
        while len(self.mines) != mines:
            i = random.randrange(height)
            j = random.randrange(width)
            if not self.board[i][j]:
                self.mines.add((i, j))
                self.board[i][j] = True

        # At first, player has found no mines
        self.mines_found = set()

    def print(self):
        """
        Prints a text-based representation
        of where mines are located.
        """
        for i in range(self.height):
            print("--" * self.width + "-")
            for j in range(self.width):
                if self.board[i][j]:
                    print("|X", end="")
                else:
                    print("| ", end="")
            print("|")
        print("--" * self.width + "-")

    def is_mine(self, cell):
        i, j = cell
        return self.board[i][j]

    def nearby_mines(self, cell):
        """
        Returns the number of mines that are
        within one row and column of a given cell,
        not including the cell itself.
        """

        # Keep count of nearby mines
        count = 0

        # Loop over all cells within one row and column
        for i in range(cell[0] - 1, cell[0] + 2):
            for j in range(cell[1] - 1, cell[1] + 2):

                # Ignore the cell itself
                if (i, j) == cell:
                    continue

                # Update count if cell in bounds and is mine
                if 0 <= i < self.height and 0 <= j < self.width:
                    if self.board[i][j]:
                        count += 1

        return count

    def won(self):
        """
        Checks if all mines have been flagged.
        """
        return self.mines_found == self.mines


class Sentence():
    """
    Logical statement about a Minesweeper game
    A sentence consists of a set of board cells,
    and a count of the number of those cells which are mines.
    """

    def __init__(self, cells, count):
        self.cells = set(cells)
        self.count = count

    def __eq__(self, other):
        return self.cells == other.cells and self.count == other.count

    def __str__(self):
        return f"{self.cells} = {self.count}"

    def known_mines(self):
        """
        Returns the set of all cells in self.cells known to be mines.
        """
        if len(self.cells) == self.count:
            return self.cells

    def known_safes(self):
        """
        Returns the set of all cells in self.cells known to be safe.
        """
        if 0 == self.count:
            return self.cells

    def mark_mine(self, cell):
        """
        Updates internal knowledge representation given the fact that
        a cell is known to be a mine.
        """
        if cell in self.cells:
            self.cells.remove(cell)
            self.count -= 1

    def mark_safe(self, cell):
        """
        Updates internal knowledge representation given the fact that
        a cell is known to be safe.
        """
        if cell in self.cells:
            self.cells.remove(cell)


class MinesweeperAI():
    """
    Minesweeper game player
    """

    def __init__(self, height=8, width=8):

        # Set initial height and width
        self.height = height
        self.width = width

        # Keep track of which cells have been clicked on
        self.moves_made = set()

        # Keep track of cells known to be safe or mines
        self.mines = set()
        self.safes = set()

        # List of sentences about the game known to be true
        self.knowledge = []

    def mark_mine(self, cell):
        """
        Marks a cell as a mine, and updates all knowledge
        to mark that cell as a mine as well.
        """
        self.mines.add(cell)
        for sentence in self.knowledge:
            sentence.mark_mine(cell)

    def mark_safe(self, cell):
        """
        Marks a cell as safe, and updates all knowledge
        to mark that cell as safe as well.
        """
        self.safes.add(cell)
        for sentence in self.knowledge:
            sentence.mark_safe(cell)

    def add_knowledge(self, cell, count):
        """
        Called when the Minesweeper board tells us, for a given
        safe cell, how many neighboring cells have mines in them.

        This function should:
            1) mark the cell as a move that has been made
            2) mark the cell as safe
            3) add a new sentence to the AI's knowledge base
               based on the value of `cell` and `count`
            4) mark any additional cells as safe or as mines
               if it can be concluded based on the AI's knowledge base
            5) add any new sentences to the AI's knowledge base
               if they can be inferred from existing knowledge
        """
        #1
        self.moves_made.add(cell)
        #2
        self.mark_safe(cell)
        #3
        i, j = cell
        removecell = []
        addcell = [(i-1, j-1), (i-1, j), (i-1, j+1), 
                (i, j-1), (i, j+1), 
                (i+1, j-1), (i+1, j), (i+1, j + 1),]
        for c in addcell:
            if c[0] < 0 or c[0] > 7 or c[1] < 0 or c[1] > 7:
                removecell.append(c)

        for c in removecell:
            addcell.remove(c)

        removecell = []
        for c in addcell:
            if c in self.mines:
                removecell.append(c)

        count -= len(removecell)
        for c in removecell:
            addcell.remove(c)

        removecell = []
        for c in addcell:
            if c in self.safes:
                removecell.append(c)

        for c in removecell:
            addcell.remove(c)

        #need filter for empty
        newsentence = Sentence(addcell, count)
        if len(newsentence.cells) > 0:
            self.knowledge.append(newsentence)
        print("dfs")

        self.updateknowledge()
        print("2")
        self.inference()
        print("3")


        

    def inference(self):
        for sentence1 in self.knowledge:
            for sentence2 in self.knowledge:
                if sentence1.cells.issubset(sentence2.cells):
                    new_cells = sentence2.cells - sentence1.cells
                    new_count = sentence2.count - sentence1.count
                    new_sentence = Sentence(new_cells, new_count)
                    if new_sentence not in self.knowledge:
                        self.knowledge.append(new_sentence)
                        self.updateknowledge()



    def updateknowledge(self):
        keepgoing = True
        while keepgoing:
            keepgoing = False
            for sentence in self.knowledge:

                mines = sentence.known_mines()
                if mines:
                    keepgoing = True
                    for mine in mines:
                        self.mark_mine(mine)

                safes = sentence.known_safes()
                if safes:
                    keepgoing = True
                    for safe in safes:
                        self.mark_safe(safe)


    def make_safe_move(self):
        """
        Returns a safe cell to choose on the Minesweeper board.
        The move must be known to be safe, and not already a move
        that has been made.

        This function may use the knowledge in self.mines, self.safes
        and self.moves_made, but should not modify any of those values.
        """
        for safe in self.safes:
            if safe not in self.moves_made:
                return safe

        return None

    def make_random_move(self):
        """
        Returns a move to make on the Minesweeper board.
        Should choose randomly among cells that:
            1) have not already been chosen, and
            2) are not known to be mines
        """
        moves = len(self.moves_made) + len(self.mines)
        if moves == 64:
            return None
        while True:
            i = random.randrange(self.height)
            j = random.randrange(self.height)
            if (i, j) not in self.moves_made and (i, j) not in self.mines:
                return (i, j)

runner.py

代码语言:javascript
运行
AI代码解释
复制
import pygame
import sys
import time

from minesweeper import Minesweeper, MinesweeperAI

HEIGHT = 8
WIDTH = 8
MINES = 8

# Colors
BLACK = (0, 0, 0)
GRAY = (180, 180, 180)
WHITE = (255, 255, 255)

# Create game
pygame.init()
size = width, height = 600, 400
screen = pygame.display.set_mode(size)

# Fonts
OPEN_SANS = "assets/fonts/OpenSans-Regular.ttf"
smallFont = pygame.font.Font(OPEN_SANS, 20)
mediumFont = pygame.font.Font(OPEN_SANS, 28)
largeFont = pygame.font.Font(OPEN_SANS, 40)

# Compute board size
BOARD_PADDING = 20
board_width = ((2 / 3) * width) - (BOARD_PADDING * 2)
board_height = height - (BOARD_PADDING * 2)
cell_size = int(min(board_width / WIDTH, board_height / HEIGHT))
board_origin = (BOARD_PADDING, BOARD_PADDING)

# Add images
flag = pygame.image.load("assets/images/flag.png")
flag = pygame.transform.scale(flag, (cell_size, cell_size))
mine = pygame.image.load("assets/images/mine.png")
mine = pygame.transform.scale(mine, (cell_size, cell_size))

# Create game and AI agent
game = Minesweeper(height=HEIGHT, width=WIDTH, mines=MINES)
ai = MinesweeperAI(height=HEIGHT, width=WIDTH)

# Keep track of revealed cells, flagged cells, and if a mine was hit
revealed = set()
flags = set()
lost = False

# Show instructions initially
instructions = True

while True:

    # Check if game quit
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    screen.fill(BLACK)

    # Show game instructions
    if instructions:

        # Title
        title = largeFont.render("Play Minesweeper", True, WHITE)
        titleRect = title.get_rect()
        titleRect.center = ((width / 2), 50)
        screen.blit(title, titleRect)

        # Rules
        rules = [
            "Click a cell to reveal it.",
            "Right-click a cell to mark it as a mine.",
            "Mark all mines successfully to win!"
        ]
        for i, rule in enumerate(rules):
            line = smallFont.render(rule, True, WHITE)
            lineRect = line.get_rect()
            lineRect.center = ((width / 2), 150 + 30 * i)
            screen.blit(line, lineRect)

        # Play game button
        buttonRect = pygame.Rect((width / 4), (3 / 4) * height, width / 2, 50)
        buttonText = mediumFont.render("Play Game", True, BLACK)
        buttonTextRect = buttonText.get_rect()
        buttonTextRect.center = buttonRect.center
        pygame.draw.rect(screen, WHITE, buttonRect)
        screen.blit(buttonText, buttonTextRect)

        # Check if play button clicked
        click, _, _ = pygame.mouse.get_pressed()
        if click == 1:
            mouse = pygame.mouse.get_pos()
            if buttonRect.collidepoint(mouse):
                instructions = False
                time.sleep(0.3)

        pygame.display.flip()
        continue

    # Draw board
    cells = []
    for i in range(HEIGHT):
        row = []
        for j in range(WIDTH):

            # Draw rectangle for cell
            rect = pygame.Rect(
                board_origin[0] + j * cell_size,
                board_origin[1] + i * cell_size,
                cell_size, cell_size
            )
            pygame.draw.rect(screen, GRAY, rect)
            pygame.draw.rect(screen, WHITE, rect, 3)

            # Add a mine, flag, or number if needed
            if game.is_mine((i, j)) and lost:
                screen.blit(mine, rect)
            elif (i, j) in flags:
                screen.blit(flag, rect)
            elif (i, j) in revealed:
                neighbors = smallFont.render(
                    str(game.nearby_mines((i, j))),
                    True, BLACK
                )
                neighborsTextRect = neighbors.get_rect()
                neighborsTextRect.center = rect.center
                screen.blit(neighbors, neighborsTextRect)

            row.append(rect)
        cells.append(row)

    # AI Move button
    aiButton = pygame.Rect(
        (2 / 3) * width + BOARD_PADDING, (1 / 3) * height - 50,
        (width / 3) - BOARD_PADDING * 2, 50
    )
    buttonText = mediumFont.render("AI Move", True, BLACK)
    buttonRect = buttonText.get_rect()
    buttonRect.center = aiButton.center
    pygame.draw.rect(screen, WHITE, aiButton)
    screen.blit(buttonText, buttonRect)

    # Reset button
    resetButton = pygame.Rect(
        (2 / 3) * width + BOARD_PADDING, (1 / 3) * height + 20,
        (width / 3) - BOARD_PADDING * 2, 50
    )
    buttonText = mediumFont.render("Reset", True, BLACK)
    buttonRect = buttonText.get_rect()
    buttonRect.center = resetButton.center
    pygame.draw.rect(screen, WHITE, resetButton)
    screen.blit(buttonText, buttonRect)

    # Display text
    text = "Lost" if lost else "Won" if game.mines == flags else ""
    text = mediumFont.render(text, True, WHITE)
    textRect = text.get_rect()
    textRect.center = ((5 / 6) * width, (2 / 3) * height)
    screen.blit(text, textRect)

    move = None

    left, _, right = pygame.mouse.get_pressed()

    # Check for a right-click to toggle flagging
    if right == 1 and not lost:
        mouse = pygame.mouse.get_pos()
        for i in range(HEIGHT):
            for j in range(WIDTH):
                if cells[i][j].collidepoint(mouse) and (i, j) not in revealed:
                    if (i, j) in flags:
                        flags.remove((i, j))
                    else:
                        flags.add((i, j))
                    time.sleep(0.2)

    elif left == 1:
        mouse = pygame.mouse.get_pos()

        # If AI button clicked, make an AI move
        if aiButton.collidepoint(mouse) and not lost:
            move = ai.make_safe_move()
            if move is None:
                move = ai.make_random_move()
                if move is None:
                    flags = ai.mines.copy()
                    print("No moves left to make.")
                else:
                    print("No known safe moves, AI making random move.")
            else:
                print("AI making safe move.")
            time.sleep(0.2)

        # Reset game state
        elif resetButton.collidepoint(mouse):
            game = Minesweeper(height=HEIGHT, width=WIDTH, mines=MINES)
            ai = MinesweeperAI(height=HEIGHT, width=WIDTH)
            revealed = set()
            flags = set()
            lost = False
            continue

        # User-made move
        elif not lost:
            for i in range(HEIGHT):
                for j in range(WIDTH):
                    if (cells[i][j].collidepoint(mouse)
                            and (i, j) not in flags
                            and (i, j) not in revealed):
                        move = (i, j)

    # Make move and update AI knowledge
    if move:
        if game.is_mine(move):
            lost = True
        else:
            nearby = game.nearby_mines(move)
            revealed.add(move)
            ai.add_knowledge(move, nearby)

    pygame.display.flip()

我知道这就是错误发生的地方,用副本替换我的迭代对象可以解决这个问题。问题是我一开始就看不到我在哪里编辑集合。

代码语言:javascript
运行
AI代码解释
复制
class MinesweeperAI:
    def updateknowledge(self):
        ...
        safes = sentence.known_safes()
        if safes:
            keepgoing = True
            for safe in safes.copy():
                self.mark_safe(safe)

self.mark_safe(safe)通向(在扫雷舰类中)

代码语言:javascript
运行
AI代码解释
复制
self.safes.add(cell)
        for sentence in self.knowledge:
            sentence.mark_safe(cell)

sentence.mark_safe(cell)导致了

代码语言:javascript
运行
AI代码解释
复制
if cell in self.cells:
            self.cells.remove(cell)

那么,这对我正在迭代的safes集有什么影响呢?会很感谢你的建议

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-26 04:28:19

问题是代码在循环"safes“时是”标记safes“。

尝试更改:

代码语言:javascript
运行
AI代码解释
复制
for safe in self.safes:

至:

代码语言:javascript
运行
AI代码解释
复制
for safe in list(self.safes):

这将使"safes“的副本循环,使底层集可用于更新。

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

https://stackoverflow.com/questions/67698497

复制
相关文章
如何理解掩码、反掩码、通配符「建议收藏」
一、掩码 在掩码中,1表示精确匹配,0表示随机 1和0,永远不交叉; 1永远在左边,0永远在右边; 在配置IP地址以及路由的时候,会使用掩码; 二、反掩码 在反掩码中,1表示随机,0表示精确匹配 0和1,永远不交叉; 0永远在左边,1永远在右边; 在路由协议的配置中,通过network命令进行网段宣告时,会使用 三、通配符 在统配符中,1表示随机,0表示精确匹配 0和1的位置,没有任何的固定限制 可以连续,可以交叉 在ACL中,使用的通配符 通配符掩码表
全栈程序员站长
2022/09/09
3K0
如何计算子网掩码
本文提供视频讲解,详细见:(https://www.bilibili.com/video/BV1oT4y17745)
研究僧
2020/07/12
6.1K0
掩码、通配符与反掩码
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/161565.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/09
7640
掩码、通配符与反掩码
反掩码与通配符掩码[通俗易懂]
掩码我们学数通的应该都很熟悉,我们刚刚学习IP的时候肯定都学过,这里就不在叙述。
全栈程序员站长
2022/09/15
4.2K0
反掩码与通配符掩码[通俗易懂]
什么是子网掩码?超过254个ip地址如何设置子网掩码?
对于ip地址我们前面通过多次文章,大家都有一定的理解,不过通过的留言,有部分朋友还是对子网掩码、ip地址的网段有些疑问,那么今天我们一起来解下这方面的内容。
网络技术联盟站
2023/03/13
3.3K0
什么是子网掩码?超过254个ip地址如何设置子网掩码?
通配符掩码
在访问控制列表中,通配符掩码来指定主机、特定网络或网络的一部分,首先要理解块大小,其用于指定地址范围
全栈程序员站长
2022/09/07
9820
如何购买比特币,比特币的价值何在?
对于不玩游戏的人来说,刚一听到比特币这个名字还以为是和游戏币的价值差不多,只能在游戏里使用的虚拟货币,经过稍微的了解后发现原来比特币现下这么火还是有一定的原因的。如何购买比特币,比特币的价值何在?来了解一下吧!
用户8739990
2021/07/12
9620
如何购买比特币,比特币的价值何在?
子网掩码和通配符掩码的区别
说白了子网掩码的工作原理就是,它拥有和主机IP地址一样的位数,每一位与对应的ip地址位进行“与”操作,得出的结果就是主机所在的子网,打个比方,192.168.1.1 255.255.255.0这是一个标准的C类网络,子网掩码/24,所以它的网络位也就是所在的子网就是192.168.1.0,计算过程如下:
全栈程序员站长
2022/09/15
1.2K0
彻底澄清子网掩码、反掩码、通配符掩码以及ospf network命令误区
示例1:deny ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255
全栈程序员站长
2022/09/09
2.6K1
彻底澄清子网掩码、反掩码、通配符掩码以及ospf network命令误区
理解通配符掩码
通配符掩码是一个32位比特数,以点分十进制表示,告诉路由器数据包IP地址的哪些比特需要和access-list命令中给定的IP地址相匹配。
全栈程序员站长
2022/09/15
1.2K0
网络号 IP地址 子网掩码如何计算
1.Internet上每一台计算机都有唯一的地址来标识它的身份,即IP地址,使用域名其实也是要转化为IP地址的。
双面人
2019/06/02
4.3K0
子网掩码,反掩码与通配符之间的区别
1:子网掩码与反掩码的区别: 反掩码就是通配符掩码 通过标记0和1告诉设备应该匹配到哪位copy。 由于跟子网掩码刚好相zd反,所以也叫反掩码 例如掩码是255.255.255.0 wildcard-mask 就是0.0.0.255 255.255.255.248 反掩就是0.0.0.7 2:通配符掩码,ospf和Acl这儿用通配符掩码也不是每家的交换机都这么做,像cisco 3550就是用的子网掩码,所以不是一定的。
全栈程序员站长
2022/09/09
1.2K0
不改变比特币, 如何扩容?
在过去的十年中,对于行业论坛和从业媒体来说,扩容一直是永恒的主题。在这篇文章中,我将假设过去十年中一直被人们谈论的扩容方案,都没有命中要害,我将提出另一种框架。我认为,「机构式扩容」是一个被忽视了的扩容方向,而且它的实现很有可能不需要损害比特币的可靠性。
区块链大本营
2019/05/14
7270
不改变比特币, 如何扩容?
什么是通配符掩码
通配符掩码(Wildcard Mask) — 通配符掩码(Wildcard Mask)是一个32位的数量,用在与一个IP地址的联合上来决定在一个IP地址的那个位应该不忽略,在将那个地址与另一个IP地址相比时。一个通配符掩码在设置接入列表时被指定。
全栈程序员站长
2022/09/15
1.6K0
Signal之掩码操作
Kernel里,每个Task都有针对Signal的掩码(Mask)。掩码值为1表示拦截该Signal,即不处理Signal;掩码值为0表示会处理该Signal。而且默认情况下每个Task都会处理发给自己的Signal,只不过默认的处理方案是SIG_IGN(丢弃/忽略)。因此,要对Signal有所反应,就需要手动挂接Signal的处理机制了。今天看看Mask相关的操作
Taishan3721
2020/07/06
1.7K0
子网掩码详解
参考博客:https://blog.csdn.net/jason314/article/details/5447743
梅花
2022/05/11
2.2K0
你到底该如何看待比特币?
导 语 上周末的大跌,让许多比特币的反对者站了出来,许多人又开始大肆唱空比特币,新闻铺天盖地,其中最著名的应该算是人民日报头版《颠倒众生的比特币将载入金融史》更是被广为流传。其实币圈的老鸟们根本没当回事,这不,立马又涨回10万了,只是可怜了许多新韭菜,不知道他们现在做何感想...... 本来不打算再谈关于比特币是否有价值等这些问题,只是看到最近有大批新人入圈,又逢本次的大幅震荡,还是把这个问题拿出来再跟大家分享一次,因为大部分人还是不会出门左转这个操作。 A 两个问题的答案 首先,两个新人最常问到的
企鹅号小编
2018/01/23
9290
你到底该如何看待比特币?
子网掩码是什么 子网掩码的计算方法
如今,互联网已经走进千家万户,基本上每家每户都会安装一个WiFi,有的人家里甚至安装两个。安装过无线网络的朋友都知道,路由器在刚开始使用时需要进行设置。而在设置时,大家都会看到设置界面的子网掩码栏。那么究竟什么是子网掩码?这是怎么得出来的呢?下面就来为大家介绍一下。
用户8739990
2021/07/12
2.2K0
子网掩码是什么 子网掩码的计算方法
通配符和通配符掩码
  在路由器的配置中,经常出现通配符。和子网掩码一样,都是以“0”或“1”表示,不过与子网掩码所表示的意思却不一样。
全栈程序员站长
2022/09/15
1.7K0
通配符掩码的计算
我不太清楚通配符掩码具体是怎么定义的,但是在大多数初学者的印象中通配符掩码就是子网掩码取反,在网上搜索了一下也没有什么具体解释,下面是摘自百度百科的解释:
全栈程序员站长
2022/09/15
6560

相似问题

Informix到SQL属性约束

11

如何在Informix中创建影子表

23

如何在Informix中创建BTS索引

10

如何在INFORMIX sql查询中创建allowMultiQueries

10

如何检查Informix中是否有主键约束的列是否可以为空

10
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档