Mia Duncan Mia Duncan - 13 days ago 8
Python Question

PYGAME snake game how to add length, how to eat food

I am able to get my snake to move in multiple directions and I can get my pellet to print on the screen. I have a few other issues though. My first issue is that the pellet will print at any random location and it won't correspond to the bit size of the snake head. My second issue is I don't know how to get my snake to "eat" the pellet. Third, I don't know how to add length to the snake once it does "eat" the pellet. I realize I should create an empty list and append, but I am not exactly sure how to do that. Any help or direction would be greatly appreciated.

import sys, pygame, random, itertools, time

blu = (37,102,151)
red = (175,31,36)
bla = (0,0,0)
whi = (255, 255, 255)

#pygame.mixer.init()
#pygame.mixer.music.load('crash.wav')
#pygame.mixer.music.play(-1)

class Board(object):

def __init__(self, screen):
self.screen = screen
self.size = screen.get_size()
self.bit = 32
self.keys= []
self.setStart()
self.snakeLength = Length_Snake(225)
self.snakeList = []

#sets snake in initial position and put pellet in random position
def setStart(self):
width = self.size
height = self.size
self.sStart = [20, 20]
self.snake = Snake(self.sStart)
self.pellet = Pellet(self.newPelletPosition())

def eaten(self):
self.snake.update()
#does snake eat pellet?
for i in range(0, self.snakeLength.length):
if self.board.collision(self.pellet.x, self.pellet.y, self.snakeLength.x[i], self.snakeLength.y[i], 32):
self.pellet.x = random.randint(2,9) * 32
self.pellet.y = random.randint(2,9) * 32
self.snakeLength.length = self.snakeLength.length + 1

#runs game and sets snake speed
def run(self):
running = time.time()
#pygame.mixer.init()
#pygame.mixer.music.load('FU2.wav')
#pygame.mixer.music.play(-1)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
self.move()
now = time.time()
if now >= running + .20:
running = now
self.update()
if self.boardBoundary() or self.snake.collision():
pygame.mixer.music.load('crash.wav')
pygame.mixer.music.play()
break
self.draw()

#allows us to use arrows for playing
def move(self):
keys = pygame.key.get_pressed()
if sum(keys) != 0:
self.keys = keys

#redraws pellet if appears where snake currently is
def update(self):
pelletCollision = self.pellet.position == self.snake.positions[0]
if pelletCollision:
self.pellet.position = self.newPelletPosition()
self.snake.update(self.directionKeys(), pelletCollision)

#allows us to use arrows while playing
def directionKeys(self):
if self.keys[pygame.K_LEFT]:
return [-self.bit, 0]
elif self.keys[pygame.K_RIGHT]:
return [self.bit, 0]
elif self.keys[pygame.K_UP]:
return [0, -self.bit]
elif self.keys[pygame.K_DOWN]:
return [0, self.bit]
else:
return [0, 0]

#gives pellet random position and avoids overlap with snake
def newPelletPosition(self):
x = self.randomPosition()
y = self.randomPosition()
for pos in self.snake.positions:
if [x, y] == pos:
return self.newPelletPosition()
return [x, y]

def randomPosition(self):
return random.randint(16,485)

#see if snakehead hits edge of screen !!
def boardBoundary(self):
snake_pos_x, snake_pos_y = self.snake.positions[0]
return snake_pos_x < 0 or snake_pos_x > self.size[0] - self.bit or snake_pos_y < 0 or snake_pos_y > self.size[1] - self.bit

#draws screen, pellet, snake
def draw(self):
self.screen.fill((bla))
self.pellet.draw(self.screen)
self.snake.draw(self.screen)
pygame.display.flip()

class Length_Snake(object):
x = [0]
y = [0]
updateTailCount = 0
updateTailCountMax = 2

#suggestion -15 for append
def __init__(self, length):
self.length = length
for i in range(0, 225):
self.x.append(-100)
self.y.append(-100)

def update(self):
self.updateTailCount = self.updateTailCount + 1
if self.updateTailCount > self.updateTailCountMax:
for i in range(self.snakeLength - 1 , 0, -1):
self.x[i] = self.x[i-1]
self.y[i] = self.y[i-1]

class Snake(object):

def __init__(self, position, speed = [0,0]):
self.positions = [position]
self.speed = speed

#sets the position as long as the snake hasn't collided (.pop() removes and then returns)
def update(self, speed, pelletCollision = False):
self.set_speed(speed)
snake_pos = self.newSnakePosition()
self.positions.insert(0, snake_pos)
if not pelletCollision:
self.positions.pop()

#updates snake head position
def newSnakePosition(self):
current = self.positions[0]
return [current[0] + self.speed[0], current[1] + self.speed[1]]

#sets speed
def set_speed(self, speed):
if speed == [0, 0]:
pass
elif self.speed == [0, 0]:
self.speed = speed
elif abs(self.speed[0]) != abs(speed[0]):
self.speed = speed

#determines if snake collides with itself
def collision(self):
snake_pos = self.positions[0]
for position in self.positions[1:]:
if position == snake_pos:
return True
return False

# def eatCollision(self):
#if snake_pos_x, snake_pos_y = self.pellet.newPelletPosition[0]:
# return True
#return False

#draws snake
def draw(self, screen):
bit = pygame.Surface((32, 32))
bit.fill((whi))
for position in self.positions:
screen.blit(bit, position)

class Pellet(object):
def __init__(self, position):
self.bit = pygame.Surface((32, 32))
self.position = position

def draw(self,screen):
self.bit.fill((blu))
screen.blit(self.bit, self.position)

class Menu(object):

def __init__(self, screen, items, background_color = (bla), font_size = 32, font_color = (whi)):
self.screen = screen
self.screen_width = screen.get_rect().width
self.screen_height = screen.get_rect().height
self.background_color = background_color
self.font = pygame.font.SysFont("comicsanms", 32)
self.font_color = font_color
self.items = []

#enumerate makes loop clearer, returns an iterator
#for i in range(len(items)):
#for index, item in enumerate(items):
for index, item in enumerate(items):
words = self.font.render(item, 1, font_color)
width = words.get_rect().width
height = words.get_rect().height
pos_x = (self.screen_width / 2) - (width / 2)
loh = len(items) * height
pos_y = (self.screen_height / 2) - (loh / 2) + (index * height)
self.items.append([item, words, (width, height), (pos_x, pos_y)])

#runs options at the start and end of game
def run(self):
loop = True
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
loop = False
elif event.key == pygame.K_q:
loop = False
sys.exit()
self.screen.fill(self.background_color)
for name, label, (width, height), (pos_x, pos_y) in self.items:
self.screen.blit(label, (pos_x, pos_y))
pygame.display.flip()

#shows us stuff to do on game menu
def whilerun():
screen = pygame.display.set_mode((500, 500))

start_menu_stuff = ['ENTER to play', 'Q to quit']
end_menu_stuff = ['You Dead, Bro!','ENTER to play again'];
start_menu = Menu(screen, start_menu_stuff)
end_menu = Menu(screen, end_menu_stuff)
game = Board(screen)

while True:
start_menu.run()
game.run()
game.setStart()
end_menu.run()




if __name__ == "__main__":
pygame.init()
whilerun()

Answer

Keep snake as list of its segments/elements. Every element keeps segment position (x,y). First element is snake head. ie.

snake = [(10,10), (11,10), (12,10)]

When snake moves then you append new position of head at the beginning of list and you remove last element - and snake/list have the same length.

# new head position
snake.insert(0, (9,10))

# remove tail     
snake.pop()

When snake eats something then you don't remove last element from list. This way snake is longer.

# init data
make_longer = False

...

# snake eat sothing
make_longer = True

...

# new head position
snake.insert(0, (9,10))

# remove tail     
if make_longer:
    # skip removing last element once 
    make_longer = False
else:
    snake.pop()

If you want to make longer more than one element then don't remove last element more than once.

# init data
make_longer = 0

...

# snake eat sothing
# make_longer = 3
make_longer += 3 # better when snake eats more apples in short time

...

# new head position
snake.insert(0, (9,10))

# remove tail     
if make_longer > 0:
    make_longer -= 1
else:
    snake.pop()
Comments