# Tutorial #4

Subscribe to Tech With Tim!

### Collision With Snack

Now that we’ve created the snack we need to draw it and see if the head of our snake collides with it.

Our redrawWindow() function should now look like this:

```def redrawWindow(surface):
global rows, width, s, snack # ADD snack to this line
surface.fill((0,0,0))
s.draw(surface)
snack.draw(surface) # NEW
drawGrid(width,rows, surface)
pygame.display.update()
```

To check for collision we will add the following lines into our game loop within the main() function.

```if s.body[0] == snake.pos:  # Checks if the head collides with the snack
snack = cube(randomSnack(rows, s), color=(0,255,0))  # creates a new snack object
```

### Snake Class – addCube() Method

The last step to adding a cube is to add it to the end of our snake object. We will do this from the addCube() method.

```class snake():
...
tail = self.body[-1]
dx, dy = tail.dirnx, tail.dirny

# We need to know which side of the snake to add the cube to.
# So we check what direction we are currently moving in to determine if we
# need to add the cube to the left, right, above or below.
if dx == 1 and dy == 0:
self.body.append(cube((tail.pos[0]-1,tail.pos[1])))
elif dx == -1 and dy == 0:
self.body.append(cube((tail.pos[0]+1,tail.pos[1])))
elif dx == 0 and dy == 1:
self.body.append(cube((tail.pos[0],tail.pos[1]-1)))
elif dx == 0 and dy == -1:
self.body.append(cube((tail.pos[0],tail.pos[1]+1)))

# We then set the cubes direction to the direction of the snake.
self.body[-1].dirnx = dx
self.body[-1].dirny = dy
```

### Ending the Game

We lose the game when our snake object collides with itself. To check this we add the following line to our main() function inside the game loop.

```for x in range(len(s.body)):
if s.body[x].pos in list(map(lambda z:z.pos,s.body[x+1:])): # This will check if any of the positions in our body list overlap
print('Score: ', len(s.body))
message_box('You Lost!', 'Play again...')
s.reset((10,10))
break
```

### Snake Class – reset() Method

Now we will code the reset() method from within the snake class. All this will do is reset the snake so we can play again afterwards.

```class snake():
...
def reset(self, pos):
self.body = []
self.turns = {}
self.dirnx = 0
self.dirny = 1
```

### Displaying a Message Box

When the user losses we want to display a message box telling them that they lost.

We are going to do this using tkinter and inside of our message_box() function.

```import tkinter as tk
from tkinter import messagebox

def message_box(subject, content):
root = tk.Tk()
root.attributes("-topmost", True)
root.withdraw()
messagebox.showinfo(subject, content)
try:
root.destroy()
except:
pass
```

And now our game is finished!

### Full Code

```#Snake Tutorial Python

import math
import random
import pygame
import tkinter as tk
from tkinter import messagebox

class cube(object):
rows = 20
w = 500
def __init__(self,start,dirnx=1,dirny=0,color=(255,0,0)):
self.pos = start
self.dirnx = 1
self.dirny = 0
self.color = color

def move(self, dirnx, dirny):
self.dirnx = dirnx
self.dirny = dirny
self.pos = (self.pos[0] + self.dirnx, self.pos[1] + self.dirny)

def draw(self, surface, eyes=False):
dis = self.w // self.rows
i = self.pos[0]
j = self.pos[1]

pygame.draw.rect(surface, self.color, (i*dis+1,j*dis+1, dis-2, dis-2))
if eyes:
centre = dis//2
circleMiddle2 = (i*dis + dis -radius*2, j*dis+8)

class snake(object):
body = []
turns = {}
def __init__(self, color, pos):
self.color = color
self.dirnx = 0
self.dirny = 1

def move(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()

keys = pygame.key.get_pressed()

for key in keys:
if keys[pygame.K_LEFT]:
self.dirnx = -1
self.dirny = 0

elif keys[pygame.K_RIGHT]:
self.dirnx = 1
self.dirny = 0

elif keys[pygame.K_UP]:
self.dirnx = 0
self.dirny = -1

elif keys[pygame.K_DOWN]:
self.dirnx = 0
self.dirny = 1

for i, c in enumerate(self.body):
p = c.pos[:]
if p in self.turns:
turn = self.turns[p]
c.move(turn[0],turn[1])
if i == len(self.body)-1:
self.turns.pop(p)
else:
if c.dirnx == -1 and c.pos[0] <= 0: c.pos = (c.rows-1, c.pos[1])
elif c.dirnx == 1 and c.pos[0] >= c.rows-1: c.pos = (0,c.pos[1])
elif c.dirny == 1 and c.pos[1] >= c.rows-1: c.pos = (c.pos[0], 0)
elif c.dirny == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.rows-1)
else: c.move(c.dirnx,c.dirny)

def reset(self, pos):
self.body = []
self.turns = {}
self.dirnx = 0
self.dirny = 1

tail = self.body[-1]
dx, dy = tail.dirnx, tail.dirny

if dx == 1 and dy == 0:
self.body.append(cube((tail.pos[0]-1,tail.pos[1])))
elif dx == -1 and dy == 0:
self.body.append(cube((tail.pos[0]+1,tail.pos[1])))
elif dx == 0 and dy == 1:
self.body.append(cube((tail.pos[0],tail.pos[1]-1)))
elif dx == 0 and dy == -1:
self.body.append(cube((tail.pos[0],tail.pos[1]+1)))

self.body[-1].dirnx = dx
self.body[-1].dirny = dy

def draw(self, surface):
for i, c in enumerate(self.body):
if i ==0:
c.draw(surface, True)
else:
c.draw(surface)

def drawGrid(w, rows, surface):
sizeBtwn = w // rows

x = 0
y = 0
for l in range(rows):
x = x + sizeBtwn
y = y + sizeBtwn

pygame.draw.line(surface, (255,255,255), (x,0),(x,w))
pygame.draw.line(surface, (255,255,255), (0,y),(w,y))

def redrawWindow(surface):
global rows, width, s, snack
surface.fill((0,0,0))
s.draw(surface)
snack.draw(surface)
drawGrid(width,rows, surface)
pygame.display.update()

def randomSnack(rows, item):

positions = item.body

while True:
x = random.randrange(rows)
y = random.randrange(rows)
if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0:
continue
else:
break

return (x,y)

def message_box(subject, content):
root = tk.Tk()
root.attributes("-topmost", True)
root.withdraw()
messagebox.showinfo(subject, content)
try:
root.destroy()
except:
pass

def main():
global width, rows, s, snack
width = 500
rows = 20
win = pygame.display.set_mode((width, width))
s = snake((255,0,0), (10,10))
snack = cube(randomSnack(rows, s), color=(0,255,0))
flag = True

clock = pygame.time.Clock()

while flag:
pygame.time.delay(50)
clock.tick(10)
s.move()
if s.body[0].pos == snack.pos:
snack = cube(randomSnack(rows, s), color=(0,255,0))

for x in range(len(s.body)):
if s.body[x].pos in list(map(lambda z:z.pos,s.body[x+1:])):
print('Score: ', len(s.body))
message_box('You Lost!', 'Play again...')
s.reset((10,10))
break

redrawWindow(win)

main()
```

If this tutorial helped you please consider supporting myself and website by heading to the Support/Donate page. Any donation helps and is greatly appreciated.