Hello and welcome to Part 3 of my Classic Snake Game tutorial!
Here's what we have so far...
The game starts with a baby snake in the middle of the terminal.
The player is able to control the snake direction using keyboard arrows
and the game ends when the snake hits the game area border.
The game can be restarted by pressing the space bar key or ended by pressing Ctrl+c.
In this video I will show you how to feed and grow the snake and keep score.
I know that this tutorial is getting long so I'll try to keep this one a little lighter.
Snake food will be represented by random integers,
which will appear at random locations on the screen.
All food will be stored in a global dictionary object,
with the screen point coordinates used as a lookup key.
I will write a dedicated function called grow_food to take care of the food.
Food location is chosen randomly within the game area, inside the bordered outline.
Food cannot appear on the snake and existing food locations are excluded as well.
If the [food] location is determined to be valid,
the food value is generated between 1 and 9 and it's stored in memory,
and then displayed on the screen.
If the location generated is not valid, because it is already used by the snake
or another number, a different location needs to be generated.
The process of generating a valid location will be repeated until it is successful.
Although I'm using the "while True" syntax, this is not an infinite loop.
In most cases a valid location will be generated right away
or within a few iterations.
The break statement will exit the loop when a valid location is found.
Let's have a look at this in action!
A new number is generated and displayed on the screen
every time the game is started.
If the snake passes over the number, the number is erased.
If the number is not erased by the snake, it remains on the screen after the game is restarted.
A number erased by the snake (or eaten if you prefer) needs to be replaced by a new number.
We need to check every time the snake moves whether the new snake location contains a number
If that's the case, a new number is generated
and the number erased by the snake on the screen is removed from the food dictionary.
That looks better, although the numbers from the previous game
remain on the screen and I don't want that.
The food dictionary is cleared every time the game starts,
so whatever happens on the screen needs to match it.
There is a useful curses function called clear,
which will ensure that the game area is blank with every game start.
Now that the numbers are displayed we can keep the score!
I will make sure that the score always starts at 0 when the game starts,
but the actual score keeping code will go into a dedicated function.
You may recall that the terminal has the top line reserved.
This is where the score will be displayed.
Since the arrow keys are located on the right hand-side of the keyboard,
I will display the score on the right-hand side of the terminal .
The score counter has six digits reserved for display,
allowing the game score to go up to a million points,
which is sufficient considering the size of the typical game area.
The score board is a separate entity from the game area so any content placed there
needs to be refreshed separately.
The score needs to be updated every time the snake eats the food.
The current score is incremented by the number eaten and the scoreboard is updated.
I would like to have a high score record.
If the current score exceeds the high score the current score becomes the high score.
The high score will be placed in the middle of the scoreboard,
formatted the same way as the Player 1 score.
The high score will be saved in a file when the game ends.
This is a text file, so the numerical high score value needs to be converted to a string before writing it to the file.
The opposite needs to be done when the game loads.
There is a chance that the file may not exist,
which will normally trigger a "file not found" exception.
This would crash the game and therefore needs to be handled.
In this scenario the high score will be 0 if there is a problem with the file.
This ensures that the hi_score variable is always initialized.
The file content needs to be converted to numerical value as the game performs
arithmetical operations on the score.\
Let's have a look at!
The high score [file] has been updated when the game ended.
You can use the cut command to see what's inside of the file.
Next time I start the game the high score is loaded and displayed on the scoreboard
The file will be updated each time the high score is beat.
Now that the snake has food it can grow!
The basic snake movement relies on continuous extending of the snake body forward.
So far the snake does not grow, as each one segment added
is cancelled out by erasing the tail.
The updated version of this game will check whether a number has been found
and insert additional segments in the snake body when that happens.
The growth list will hold all segments that the snake body will grow by.
The number of elements in the growth list will be equal to the number of segments to add to the snake.
The move_snake function always starts growth with one element,
as the snake needs to grow by at least one segment in order to move forward
This entire process will be repeated as many times as there are growth segments.
If the snake encounters a number while growing, the growth list will be extended further.
If the new snake location contains a number, the game generates a new number as before,
but in the updated version of this function,
it will be possible for multiple numbers to be generated
before a screen refresh happens,
so I need to refresh the screen each time the number is generated.
The growth list is extended by a number of elements which is equal to the number encountered.
For example, if the number is 3, the growth will have now 4 elements.
The number 1 (the digit 1), which I use refers to a single snake segment.
The new segments are inserted at the beginning of the growth list.
This particular syntax uses the slicing feature available with Python lists.
This is equivalent to using the insert method.
The snake body is extended by an element in memory and this needs to be reflected on the screen.
This code does not change - it's just like before.
However, the code that erases the tail needs to be done only once,
after the growing is complete.
I need to move it outside of this snake growing loop.
The growth list itself needs to be shortened once the snake is extended by a segment.
Look at it as I'm simply taking a segment from the growth and transferring it onto the actual snake body.
The existing code that checks whether the snake hits the game border
needs to be updated as well.
There is no point to continue growing the snake when the game is over.
The break statement will exit the while loop mid growth in such case
With the snake now growing, I need to address an issue with the snake movement control.
Right now the snake can be moved in four directions.
This is fine where the snake is just one segment long,
but it looks odd otherwise.
In a few moments I will add code that will detect whether the snake bites itself,
triggering a game over screen,
but I prefer if the reversing of the snake is not possible.
I will check for that situation within the keyboard processing routine.
This condition ensures that the arrow key press is ignored when attempting to reverse the snake.
There is one exception though - when the game just starts,
which is indicated by a score of 0.
The snake should be able to move in four directions as before.
The game is almost finished!
Let's add the code that will check whether the snake bites itself.
This is actually very easy.
All that needs to be added is an extra condition to the game area collision check.
Strictly speaking, the snake needs to be at least 5 segments long for that to happen,
so I only check for collisions with the body of the snake starting at segment index 4.
Why 4 and not 5? Because the index starts at 0.
As the last thing here, let's make the game a bit more interesting
by adding multiple numbers to the screen.
My game will have that set up proportional to the screen height:
one number for two lines of screen.
The double underscore ( __ ) is just a dummy variable name.
So, there you have it! My complete snake game.
I know this tutorial was long, but I hope that you have learned something!
If you are up to it, check out the next part, which will add colour support to this game.
Please hit the like button, and thanks for watching!