This tutorial is walk-through of the code for a classic Snake Game.
I post this in response to a few request for game tutorias in the Python “Tutorial Request” section.

The code is made with Python 3.4 and Pygame 1.9

Game objective

You maneuver a snake around on the screen using the arrow keys. Whenever you hit a target (here: an apple) the snake grows by one unit and the apple moves to a new (random) position. The game target is to get the longest possible snake.
If you hit the walls of the screen, the snake dies.
If you hit yourself (head hits tail), the snake dies. Take care not to hit yourself during U-turns.

In the two-player version, two snakes move simultaneously and compete to get to the apple. If you hit the other snake (your head hits other snakes tail), your snake dies.
In the two-player game, the winner is the one not dying first.

The snake(s) move freely around on the screen, not limited by a grid.

Game Analysis

The snake consists of a Head and a Tail.
In my game, each body-element is a circle, the circles are spaced such that the perimeter of two adjacent circles just touches.

Movement of the Head is simple, directed by the users press of the Arrows: Up, Down, Left and Right. Once a movement is started, it will continue until another Arrow-key is pressed, or until the snake hits a wall and dies.

Tail movements are more complicated. Say you have N tail elements, then the element number N move to the position of number N-1; N-1 moves to N-2; and so on until element number 1 moves to the head position, and then finally the head moves as described above.

Now, such motion requires all element to move a full diameter at each step which will look very clunky/abrupt – not what we want.
We want the movement to be only two pixels at a time (the value is a parameter in the code).
To make smooth tail movement in step length of 2 pixels, we insert a number of 'invisible' tail elements – just enough extra elements to keep the visible parts in the correct position.
Then, during Snake movement, we move all tail elements, both visible and invisible, according to the general 'tail-rule'.

The picture below show schematically the Head (red) and two visible tail elements (black) as well as 2 x 7 invisible tail elements. Try to imagine the movement of the 16 tail elements (last one [right] first) and finally a user directed movement of the Head.

With this analysis, we are ready for the coding.
Surface, Rect and collisions

Before we dive into the code, let’s see the full code. Try running it and get a feeling for the game.
Feel free to change the value of SIZE and STEP (in first section of the code), but for best result, STEP must be a divisor of SIZE.

The code uses 4 class types: Snake, SnakeElement, Apple and Game.
I'll start explaining the code for those four classes, and end by explaining the small main code.

class Snake
The Snake class consists of a number of methods.
def __init():
In def __init__() the start position is set. The color is the color of the head. The name is primarily a preparation for a later two-player version.
def reset():
In the reset() method the first SnakeElement (the head) is defined and placed in a list, 'snakeList'.
The (tail-)length is zero and all movement is stopped – the self.dir list has 4 Boolean elements indicating movement in direction down, up, left or right; they are here all set to 'False'.

def grow():
In the grow() method, a number (defined by ‘n_invisible’) of invisible body-elements are added to the snakeList and finally a single visible (BLACK) body-element is added. All the new elements are located at the position of the last existing element – during the next few movement steps the new tail element will gradually become visible.
The number of invisible element depends of SIZE and STEP, see below under main code.

def move():
In move() we just call the move_tail() and the move_head() methods.
def move_tail():
In a for-loop starting at the end of the tail, the tail-elements are moved to the position of the preceding element.

def move_head():
The line ”head = self.snakeList[0]” reduce subsequent typing and increases the readability.
The self.dir is expanded to the Boolean variables 'down', 'up', 'left', and 'right'; one of those will be True, the others will be False.
Finally, the head position is moved according to the self.dir direction.

def checkForCollision():
This method handles three different collision situations.

First check is for collision with screen border, this should not require further explanation.

Second check is for collision with the tail. As the circular body-elements are placed in a square surface, it is inevitable that surface elements overlap when the head turns. Thus, we need to disregard collisions between the head and the first few body-elements.
Only if the number of body-element exceed a threshold do we check for collision and only for elements with higher number than this threshold. The threshold is defined in main (the ‘n_noCollision’ parameter); I have chosen a 'free zone' of three body-elements (two tail elements).

The third check is for collision with 'apple' upon which the apple.reset() and self.grow() is invoked.

def draw():
We loop through the snakeList and call the draw() function of the each body-element, both visible and invisible.
A small text is rendered and blitted to the screen.

class SnakeElement
The SnakeElement class only have two methods, a large __init__() method and a smaller draw() method.

Basically, the SnakeElement defines and draws one of two types of graphical element, a visible body-element or an invisible one.
In the call to SnakeElement, the parameter 'show' can be set to True (= visible) or False (= invisible).
The invisible elements is only constituted of basic Surface and Rect specifications.

Omitting any further handling of invisible elements.
The visible part is handled as described in the tutorial mentioned a few lines above.

class Apple()
This class has simple __init__() and draw() methods; they do not require further explanation.

The reset() method is used both upon first initialization and after a hit with Snake; it defines a random position and makes sure that this position do not collide with the existing snake – otherwise a new position is found.

The GameState is constituted of a number of small methods. I think only the 'eventLoop()' method needs explanation.

def eventLoop():
The eventLoop() handles user input from keyboard.

As the Snake cannot turn 180 degree (it would instantly collide with itself), all directional input is checked for the opposite direction being active; only if this is not the case, the new direction is implemented in the snake.dir list.

The general functions
Two small general function are used: gradient() and againTxt().
The gradient() method is explained in the antialiasing tutorial mentioned above.
The againTxt() place a simple text on the screen if the snake dies.

The main code

I have placed all constants and basic Pygame declarations in the top of the code. Hopefully most of this is known stuff.
The values of 'n_invisible' and 'n_noCollision' are calculated using the two simple formulas.

In the bottom of the code I first import and transform the apple image (file attached).

Then the three class instances are declared: snake, apple and game.

snake.reset() and apple.reset() initializes the positions of snake and apple.

Finally, the simple main game-loop explained in the game-loop tutorial.

Comments
I find that this game code is quite straightforward. The 'big thing' here is not really in the code but rather in the game analysis. When I first constructed this game, it took quite a while to analyze and figure out how the snake movements are accomplished.

This is true for many other games; once the analysis is done, the actual coding is often not so difficult.

I first made this code more than two years ago – and have not touched it in this period. When I decided to make this tutorial, I was surprised by the amount of refactoring I felt necessary and beneficial. The refactoring affected large parts of the code including the core of the game.

I only mention this to underline that any program job can be coded in many ways. As long as you
can get your code to work you should be happy!
And then you should refactor! .. and refactor again.

So, I fully acknowledge the freedom of coding; I do hope, however, you find my code inspirational.

The age of the code is also explaining the use of camelCase used for some varialbe names; I have abandoned this in newer programs and now always use snake_case.
Ha, mentioning snake_case in a snake game. LOL.

The code is fairly rough, no start page or end page, text appears in the middle of the game screen.
I have left out much of the 'sugar', simply to keep the code volume down. You can see in my other tutorials how to spice-up the basic code core.

I plan to issue a part #2 of this tutorial, featuring two players each controlling a snake and competing to get to the apple(s) first.
Try to modify this tutorial to do this – it's a nice little learning challenge.