Snake - Part 7: SNAKE!

What a player…

Well, what is a game of snake without a snake, I might hear you asking.
FEAR NOT! Help is on it’s way.

A basic requirement to have a player controlled snake move around our game, is to have a player controlled snake in the first place.
To facilitate this, we can copy the basic structure of our food class, giving us the following changes:

+ class player():
+     def __init__(self, _playing_field_size):
+         self._playing_field_size = _playing_field_size

Creating a player class which is aware of the size of our playing field.

  class Snake(Game):
+     def drawPlayer(self):
+         for coord in self.player.coords:
+             self.draw_cell(coord[0],coord[1],(0,250,0))

Creating a function responsible for taking the players coordinates and drawing the player accordingly.

  class Snake(Game):
      def draw(self):
          self.drawFood()
+         self.drawPlayer()

Extending the draw() function, to have the player actually drawn on screen.

Implementing some logic

Being able to draw our player obviously is of no use, if we don’t know where the player is.

You might have been able to take from the draw-function above, that we will store the players coordinates as a list of tuples. Such as:

[(5,5),(5,6),(5,7),(6,7),(6,6),(7,6)]

The way this works is, each tuple (x,y) stores the x and y coordinates of one point on our playing field.
When our snake moves, we calculate the new set of coordinates based on direction and add it to the list.
After that we remove the first and therefore oldest entry, which will make the snake seem to move.

To keep things simple, we will have the snake start at the center of the board.

Incidentally this mechanism is also where the growth after eating an apple is facilitated.
If the coordinates of our snakes head and the coordinates of an apple match, we simply skip removing the oldest entry, giving us a simple representation of growth.


Within our __init__() function we will need to define two more variables:

  • The coordinate liste
  • The direction of travel

Since it would severely outweigh it’s use, I haven’t bothered to come up with an ingenious algorithm for moving our players using matrix addition or whatnot.
Instead, let’s take the straightforward approach, defining every direction separately.

class player():
    def __init__(self, _playing_field_size):
        self._playing_field_size = _playing_field_size
        self.coords = [(_playing_field_size//2,_playing_field_size//2)]
        self.direction = "R"

    move_r = lambda d : (d[0]+1,d[1])
    move_l = lambda d : (d[0]-1,d[1])
    move_d = lambda d : (d[0],d[1]+1)
    move_u = lambda d : (d[0],d[1]-1)

    def move(self, grow:bool=False):
        if self.direction == "R":
            self.coords.append( player.move_r(self.coords[-1]) )
            if not grow: self.coords.pop(0)
        if self.direction == "U":
            self.coords.append( player.move_u(self.coords[-1]) )
            if not grow: self.coords.pop(0)
        if self.direction == "D":
            self.coords.append( player.move_d(self.coords[-1]) )
            if not grow: self.coords.pop(0)
        if self.direction == "L":
            self.coords.append( player.move_l(self.coords[-1]) )
            if not grow: self.coords.pop(0)

Movement

Lastly, we will need the ability to move the snake with our keyboard.

Since we do not have a clock, regularly advancing the game yet, I have mapped the backspace-button to move the snake.

To turn the snake I have opted to listen for presses of arrow-keys, to then pass the actual key pressed on to a new funtion of player, resetting the direction variable according to the key pressed.

      def on_event(self, event):
          if event.type == pygame.KEYDOWN:
              if event.key == pygame.K_RETURN:
                  self.food.reset()
+             if event.key == pygame.K_BACKSPACE:
+                 if self.player.coords[-1] == self.food.coords:
+                     self.player.move(True)
+                 else:
+                     self.player.move()
+             if event.key in (pygame.K_UP,pygame.K_DOWN, pygame.K_RIGHT,pygame.K_LEFT):
+                 self.player.turn(event.key)

Notice above how, if the snakes head is over the food the grow attribute of move() is set to True.

  class player():
+     def turn(self,event):
+         if event == pygame.K_UP:    self.direction = "U"
+         if event == pygame.K_DOWN:  self.direction = "D"
+         if event == pygame.K_RIGHT: self.direction = "R"
+         if event == pygame.K_LEFT:  self.direction = "L"