[Python] Pygame Snake Splitting Bug [closed]

Discussão em 'Python' iniciado por Stack, Abril 7, 2025.

  1. Stack

    Stack Membro Participativo

    import pygame
    import random
    pygame.init()

    #game is 64 cells x 32 cells
    width = 64
    height = 32

    WIDTH = width * 20
    HEIGHT = height * 20
    FPS = 60

    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Snake")

    clock = pygame.time.Clock()

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BEIGE = (232, 220, 202)
    ORANGE = (255, 165, 0)

    blockHeight = HEIGHT / height
    blockWidth = WIDTH / width

    class Fruit:
    def __init__(self):
    self.xpos = random.randint(1, width) * blockWidth
    self.ypos = random.randint(0, height - 1) * blockHeight
    self.FruitRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Fruit = pygame.draw.rect(screen, ORANGE, self.FruitRect)

    def __str__(self):
    return f"fruit: x: {self.xpos}, y: {self.ypos}"

    def draw(self):
    self.FruitRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Fruit = pygame.draw.rect(screen, ORANGE, self.FruitRect)

    def eat(self, snakePos):
    self.xpos = random.randint(1, width) * blockWidth
    self.ypos = random.randint(0, height - 1) * blockHeight
    if (self.xpos, self.ypos) in snakePos:
    while (self.xpos, self.ypos) in snakePos:
    self.xpos = random.randint(1, width) * blockWidth
    self.ypos = random.randint(0, height - 1) * blockHeight

    def returnPos(self):
    return self.xpos, self.ypos

    class Head:
    def __init__(self):
    self.xpos = width // 2 * blockWidth
    self.ypos = height // 2 * blockHeight
    self.HeadRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Head = pygame.draw.rect(screen, RED, self.HeadRect)

    def __str__(self):
    return f"head: x: {self.xpos}, y: {self.ypos}"

    def draw(self):
    self.HeadRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Head = pygame.draw.rect(screen, RED, self.HeadRect)

    def update(self, xFac, yFac):
    self.xpos += blockWidth * xFac
    self.ypos += blockHeight * yFac

    def returnPos(self):
    return self.xpos, self.ypos

    class Body:
    def __init__(self, xpos, ypos):
    self.xpos = xpos
    self.ypos = ypos
    self.BodyRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Body = pygame.draw.rect(screen, GREEN, self.BodyRect)

    def __str__(self):
    return f"body: x: {self.xpos}, y: {self.ypos}"

    def draw(self):
    self.BodyRect = pygame.Rect(self.xpos, self.ypos, blockWidth, blockHeight)
    self.Body = pygame.draw.rect(screen, GREEN, self.BodyRect)

    def update(self, xFac, yFac):
    self.xpos += blockWidth * xFac
    self.ypos += blockHeight * yFac

    def returnPos(self):
    return self.xpos, self.ypos


    def main():
    run = True
    screen.fill(BEIGE)
    snake = Head()
    yVel = 0
    xVel = 0
    time = 0
    fruits = [Fruit(), Fruit(), Fruit(), Fruit(), Fruit()]
    snakeParts = [snake]
    snakeVels = [(xVel, yVel)]
    while run:
    clock.tick(FPS)
    time += 1
    if time == FPS:
    time = 0

    for fruit in fruits:
    if snake.returnPos() == fruit.returnPos():
    snakePos = []
    for snakePart in snakeParts:
    snakePos.append(snakePart.returnPos())
    fruit.eat(snakePos)
    snakeParts.append(Body(snakeParts[-1].returnPos()[0], snakeParts[-1].returnPos()[1])) #add a body part
    snakeVels.append((0, 0)) #the velocity of the new body part is 0
    fruit.draw()

    for snakePart in snakeParts:
    if snakePart.returnPos() == snake.returnPos() and snakePart != snakeParts[0] and len(snakeParts) != 2:
    run = False #if the snake hits itself, the game ends
    snakePart.draw()
    if snake.returnPos()[0] < 0 or snake.returnPos()[0] + blockWidth > WIDTH or snake.returnPos()[1] < 0 or snake.returnPos()[1] + blockHeight > HEIGHT:
    run = False #if the snake goes off the edge of the screen, the game ends
    pygame.display.update()

    if time == 0 or time == FPS // 2: #twice a second...
    screen.fill(BEIGE)
    #update the velocoties (add one new velcoity at the front and remove the last one)
    snakeVels.insert(0, (xVel, yVel))
    if snakeVels[-1] == (0, 0) and len(snakeParts) != 1:
    #if the last velocity is 0, make the second to last velocity 0 in order to be able to remove the last velocity
    #this is a list instead of a tuple so next iteration it is removed and the velocity does not stay at 0
    snakeVels[-2] = [0, 0]
    snakeVels.remove(snakeVels[-1])
    for i in range(len(snakeParts)):
    snakeParts.update(snakeVels[0], snakeVels[1]) #update the position of each body part based on its corresponding velocity

    #my attempted debugging
    print(snakeVels)
    snakePartsString = []
    for snakePart in snakeParts:
    snakePartsString.append(str(snakePart))
    print(snakePartsString)

    for event in pygame.event.get():
    if event.type == pygame.QUIT:
    run = False
    if event.type == pygame.KEYDOWN:
    if event.key == pygame.K_LEFT:
    xVel = -1
    yVel = 0
    if event.key == pygame.K_RIGHT:
    xVel = 1
    yVel = 0
    if event.key == pygame.K_UP:
    xVel = 0
    yVel = -1
    if event.key == pygame.K_DOWN:
    xVel = 0
    yVel = 1

    main()
    pygame.quit()



    The issue is when you enter multiple inputs (press arrows) in quick succession, the snake will sometimes split. Even when you're not entering inputs it can split. Entering inputs just increases the chance of it splitting. I think that the issue is with the event handling or the way velocities are added, removed, and used in the snakeVels list. When printing the snakeVels list, you can see that the velocities aren't always changed properly after turns.

    Continue reading...

Compartilhe esta Página