Score points

Step 5
Step completed?

Snake's tail extension management.
Score management and display.

When the snake stretches...

We will now look at the progression of the game and the increase in difficulty, which is correlated with the lengthening of the snake's tail. This happens every time the snake swallows an apple.

Let's start by writing a extendSnakeTail() function that will handle queue lengthening:

# ----------------------------------------------------------
# Snake management
# ----------------------------------------------------------

def extendSnakeTail():
    i = snake['head']
    n = snake['len']
    i = (i + 1) % n
    x = snake['x'][i]
    y = snake['y'][i]
    for _ in range(SNAKE_EXTENT):
        snake['x'].insert(i, x)
        snake['y'].insert(i, y)
    snake['len'] += SNAKE_EXTENT

This function is based on a new global variable that determines the additional tail length, i.e. the number of sections that will be added to the snake:

# ----------------------------------------------------------
# Global variables
# ----------------------------------------------------------

SNAKE_EXTENT = 2

The extendSnakeTail() function is relatively simple:

We start by positioning ourselves on the end of the snake's tail (which happens to be the box that immediately follows the head) at the level of the snake['x'] and snake['y'] lists:

i = snake['head']
n = snake['len']
i = (i + 1) % n

We then pick the position (x,y) of the end of the tail:

x = snake['x'][i]
y = snake['y'][i]

Then we will insert, still at the end of the tail, copies of x and y (SNAKE_EXTENT copies more precisely):

for _ in range(SNAKE_EXTENT):
    snake['x'].insert(i, x)
    snake['y'].insert(i, y)

Finally, we update the snake['len'] property that characterizes the length of the lists:

snake['len'] += SNAKE_EXTENT

Here is a diagram that summarizes the sequence of operations performed:

Gamebuino META

Therefore, the 2 additional sections initially occupy the same position as the end of the tail (since they are copies). They will gradually "unfold" as the snake moves forward on the grid.

All that remains is to integrate this routine into the scheduler:

# ----------------------------------------------------------
# Game management
# ----------------------------------------------------------

def tick():
    if game['mode'] == MODE_START:
        resetSnake()
        spawnApple()
        game['mode'] = MODE_READY
    elif game['mode'] == MODE_READY:
        handleButtons()
        moveSnake()
        if snakeHasMoved():
            game['mode'] = MODE_PLAY
    elif game['mode'] == MODE_PLAY:
        handleButtons()
        moveSnake()
        if didSnakeEatApple():
            extendSnakeTail()
            spawnApple()
        if didSnakeBiteItsTail() or didSnakeHitTheWall():
            game['mode'] = MODE_START

    draw()

The extension of the tail is done during the MODE_PLAY phase when the snake swallows an apple:

if didSnakeEatApple():
    extendSnakeTail()
    spawnApple()

There you go! Demonstration:

Démo

Perfect! Perfect! Now we can take care of the player's score...

Score management

To memorize the score as the game progresses, we will store it in the game engine:

# ----------------------------------------------------------
# Initialization
# ----------------------------------------------------------

game = {
    'mode':  MODE_START,
    'score': 0
}

Then we'll increment it every time the snake swallows an apple. And don't forget to reset it every time the game goes into the MODE_START phase:

# ----------------------------------------------------------
# Game management
# ----------------------------------------------------------

def tick():
    if game['mode'] == MODE_START:
        resetSnake()
        spawnApple()
        game['mode'] = MODE_READY
        game['score'] = 0
    elif game['mode'] == MODE_READY:
        handleButtons()
        moveSnake()
        if snakeHasMoved():
            game['mode'] = MODE_PLAY
    elif game['mode'] == MODE_PLAY:
        handleButtons()
        moveSnake()
        if didSnakeEatApple():
            game['score'] += 1
            extendSnakeTail()
            spawnApple()
        if didSnakeBiteItsTail() or didSnakeHitTheWall():
            game['mode'] = MODE_START

    draw()

Resetting the score at the beginning of the game:

if game['mode'] == MODE_START:
    resetSnake()
    spawnApple()
    game['mode'] = MODE_READY
    game['score'] = 0

Increase the score each time the snake swallows an apple:

if didSnakeEatApple():
    game['score'] += 1
    extendSnakeTail()
    spawnApple()

All that's left is to display the score! To do this, we will define a new global variable to determine its display color:

# ----------------------------------------------------------
# Global variables
# ----------------------------------------------------------

COLOR_SCORE = 0xffff

Then we will add a drawScore() function that will be responsible for displaying it on the screen, which we will call from the main method draw(), responsible for the global display of the game scene :

# ----------------------------------------------------------
# Graphic display
# ----------------------------------------------------------

def draw():
    clearScreen()
    drawWalls()
    drawSnake()
    drawScore()
    drawApple()

def drawScore():
    display.setColor(COLOR_SCORE)
    display.print(2, 2, game['score'])

The score is displayed at the coordinates (2,2).

Come on!... We save the script and can now admire the result:

Démo

We're almost done with our Snake!  

We still have to optimize things a little and add a few finishing touches to finish the game in style.

To the next step!

Steps