gpt4 book ai didi

python - 对列表中的 numpy 数组进行 += 操作会增加其他列表元素。 Np数组似乎是独立设置的

转载 作者:行者123 更新时间:2023-12-01 00:43:55 28 4
gpt4 key购买 nike

我开始用Python编写一个蛇游戏来熟悉pygame。然而我并没有走得太远,因为我遇到了一个问题。当我尝试向给定方向移动蛇头时就会发生这种情况。我创建了一个“Snake”类,其中包含一个标准化的 numpy 数组,表示蛇头在四个方向中的哪个方向移动,蛇的 body 作为段数组,其中最后一个元素是蛇头。每个段都是一个字典,包含一个描述其位置的 numpy 数组和一个描述其颜色的 pygame 颜色。也许创建一个“Segment”类会更聪明,但它确实有效。好吧,除了这个问题,用这一行代替:

self.snake.body[-1]["pos"] = self.snake.body[-1]["pos"] + self.snake.direction

用这一行:

self.snake.body[-1]["pos"] += self.snake.direction

也更改倒数第二个段的位置。围绕上述声明:

print(self.snake.body[-2]["pos"])

告诉我变化发生在这一行。我不明白它如何对其他指数产生影响。以下是当 6 段蛇向右移动时,段位置应如何发展:

0 [1 0]
1 [2 0]
2 [3 0]
3 [4 0]
4 [5 0]
5 [6 0]
0 [2 0]
1 [3 0]
2 [4 0]
3 [5 0]
4 [6 0]
5 [7 0]
0 [3 0]
1 [4 0]
2 [5 0]
3 [6 0]
4 [7 0]
5 [8 0]

这是当我使用 a += b 而不是 a = a + b 时会发生的情况:

0 [1 0]
1 [2 0]
2 [3 0]
3 [4 0]
4 [6 0]
5 [6 0]
0 [2 0]
1 [3 0]
2 [4 0]
3 [7 0]
4 [7 0]
5 [7 0]
0 [3 0]
1 [4 0]
2 [8 0]
3 [8 0]
4 [8 0]
5 [8 0]

我明白了,因为我的代码如何移动蛇,一个错误的位置会沿着线传播,但我不明白它是如何发生的。

这是设置段的代码部分:

def __init__(self, length):
self.length = length
self.body = []
for idx in range(length):
self.body.append({"pos": np.array([idx, 0]), "color": pg.Color(0, 255, 0)})

我正在 pipelinev 版本 2018.11.15.dev0 中运行最新的 Manjaro Linux(基本上是 Arch)、python 3.7.3 和 pygame 1.9.6。

这是整个代码:(忽略有关时间的内容,这是我被告知要分开的两个问题的第一部分)

import sys
import time
import numpy as np
import pygame as pg
pg.init()

debug_use_broken_addition = False
debug_print_segment_pos = False # prints every segments position at each step to show it breaking
debug_use_pg_time_wait = True # uses time.sleep instead if false, as per documentation both should not hog cpu, seems like they do anyway
debug_use_pg_clock_tick = False # overrides debug_use_pg_time_wait, simplifies run function tremendously, also hogs cpu
debug_print_fps = False

def debug_weird_division(n, d):
return n / d if d else 0

class Snake:
def __init__(self, length):
self.length = length
self.body = []
for idx in range(length):
self.body.append({"pos": np.array([idx, 0]), "color": pg.Color(0, 255, 0)})
self.body[-1]["color"] = pg.Color(255, 0, 0)
self.direction = np.array([1, 0])
self.speed = 2. # grid pixels / second

class Game:
def __init__(self, size):
self.size = self.width, self.height = size
self.screen = pg.display.set_mode(self.size)

if debug_use_pg_clock_tick:
self.clock = pg.time.Clock()
else:
self.prevTime = 0
self.prevSleepDuration = 0

self.dtAcc = 0
self.snake = Snake(6)

def handleEvent(self, event):
if event.type == pg.QUIT: sys.exit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_w:
self.snake.direction = np.array([0, -1])
elif event.key == pg.K_a:
self.snake.direction = np.array([-1, 0])
elif event.key == pg.K_s:
self.snake.direction = np.array([0, 1])
elif event.key == pg.K_d:
self.snake.direction = np.array([1, 0])

def update(self, dt): #comments this way =>
self.dtAcc += dt
if self.dtAcc >= 1 / self.snake.speed: # only move the snake in steps that are defined by its speed \
self.dtAcc -= 1 / self.snake.speed # more accurate than setting it to 0

for idx, segment in enumerate(self.snake.body[:-1]): # place each segment on the position of the segment in front of it \
segment["pos"] = self.snake.body[idx + 1]["pos"] # doing this for all except the first one, the head, and moving the \
# head seperately afterwards, makes the snake behave like it should
if debug_use_broken_addition:
self.snake.body[-1]["pos"] += self.snake.direction # this doesn't work
else:
self.snake.body[-1]["pos"] = self.snake.body[-1]["pos"] + self.snake.direction # this works

if debug_print_segment_pos:
for idx, segment in enumerate(self.snake.body):
print(idx, segment["pos"])

def draw(self):
self.screen.fill((0, 0, 0))
for segment in self.snake.body:
pg.draw.rect(self.screen, segment["color"], (segment["pos"][0] * 12, segment["pos"][1] * 12, 10, 10))
pg.display.flip()

def run(self, framerate):
while 1:
if debug_use_pg_clock_tick:
dt = self.clock.tick(framerate) / 1000
else:
self.time = time.time() # time in seconds
dt = (self.time - self.prevTime) if self.prevTime != 0 else 0 # make exception if running for the first time
self.prevTime = self.time
self.sleepDuration = max(0, self.prevSleepDuration - (dt - 1 / framerate)) # reduce previous sleep duration by difference between \
# actual and ideal dt to account for time spent not sleeping \
# sometimes overcompensates, would work better if single-frame \
# spikes in dt were ignored or averaged out
self.prevSleepDuration = self.sleepDuration
if debug_use_pg_time_wait:
pg.time.wait(int(self.sleepDuration * 1000))
else:
time.sleep(self.sleepDuration)

if debug_print_fps:
print("fps: %s" % (debug_weird_division(1, dt)))

for event in pg.event.get():
game.handleEvent(event)

game.update(dt)
game.draw()

game = Game((600, 400))
game.run(30)

最佳答案

您的问题出在这些行上(调试if并删除注释):

  for idx, segment in enumerate(self.snake.body[:-1]):
segment["pos"] = self.snake.body[idx + 1]["pos"]

self.snake.body[-1]["pos"] += self.snake.direction

这里的问题是,您将对现有位置数组的引用从蛇 body 的一段复制到下一段。重要的是,在第一次迭代之后,第二段的位置 (self.snake.body[-2]["pos"]) 将成为对头部段位置的引用。

当您稍后使用 += 代码更新头部段的位置时,该修改就会就地发生。您最终不会得到一个新的头部数组,就像您使用 + 而不是 += 处理代码一样。因此,您可以看到已收到对同一数组的引用的每个段中的修改(每次更新后又多了一个段)。

你可以做一个更简单的例子来更清楚地看到这类问题:

head_pos = [0]      # create a mutable object

body_pos = head_pos # make another reference to the same object

print(body_pos) # prints [0]
head_pos.append(1) # modify the list in place
print(body_pos) # prints [0, 1], even though we haven't changed body_pos directly

关于python - 对列表中的 numpy 数组进行 += 操作会增加其他列表元素。 Np数组似乎是独立设置的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57136444/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com