- This program is a Snake Game using Python GUI (turtle graphics). The game is composed of 3 main types of object: a snake, a monster and 9 food items. In the game, the player is going to use the 4 arrowkeys on the keyboard to move the snake, avoid hitting the monster and try to consume all food items on the canvas.
- The snake's body grows by the size equal to the value of food item it consumes.
- The player wins the game when his snake eats all the food without being captured by the monster, otherwise he loses. Further, the snake moves at a variable speed and its speed dramatically decreases when the body is extending.
My program contains these global variables:
- a
Screen
object to setup window size, coordinates and loop itself to react to mouse click and keystrokes - 4 global
Turtle
objects for graphically displaying the game status, welcome message, the snake and the monster integer
variablesg_contact
g_extend_tail_value
andg_time
to store the monster's contact time with snake body, how many tails the snake should expand, and the elapsed timestring
variableg_motion
to store the current motion of the snakebool
variablesg_initiate_game_module
,g_snake_caught
andg_player_wins
to give signals to the programlist
variableg_last_20_contact
, stores whether last 20 times refresh are considered as contact; is used to avoid continuous contacts, at default, it's [False] * 20list
variableg_food_list
stores all remaining food items in integer, e.g. [1,2,3,4]- The tails of the snake are achieved using
stamp()
.List
variableg_stamp_location_list
stores all stamp (tail) locations, e.g. [(0,0), (200,200)]
Each function in this program is regarded as a component and serves one purpose.
The functions' roles are concisely stated in their names.
- The
main()
function is the highest in status.- It calls
draw_border()
,set_turtles()
,display_status()
,display_startup_message()
to first set the screen components ready. - Then when screen is on click, starts the
game_module()
function.
- It calls
- The
game_module()
insidemain()
basically contains all configurations of the snake game.- It first calls
generate_food()
to generate randomized food items on the canvas. - Then it uses
onkey()
to bind keystrokes'Left'
,'Right'
,'Up'
,'Down'
,'space'
to event handler functions. - It then uses
ontimer()
to refresh the screen along with object movements on the screen. Including:time_refresh()
,snake_refresh()
,monster_refresh()
,check_win_lose_contact()
- The
time_refresh()
,snake_refresh()
,monster_refresh()
,check_win_lose_contact()
functions are the most important in this program. They use recursion to achieve loop. Every movement of the snake and monster, the change of time, the constant check of winning or losing, are achieved when refreshed.
- It first calls
The logic to motion the snake and monster
- Use
snake_refresh()
function to motion the snake.-
The checking process
- The check of whether the player has won or lost is left in the
check_win_lose_contact()
function, because that function has higher refresh rate (0.05s) and will be more accurate. - It makes predictions of the snake path to avoid head-to-head crash with the border. Achieved by making
g_snake_clone
, the clone of snake go one step further. If it exceeds the border, the snake won't move butg_motion
will still be the player's last command. - It then checks whether the snake contacts food, by evaluating its distance to every food item.
- The check of whether the player has won or lost is left in the
-
The moving snake and tail process
- It sets the heading directions of
g_snake_turtle
according tog_motion
- Let the tail move along with the snake by removing the earliest placed tail (stamp), and then stamp at the current location of
g_snake_turtle
. - Ultimately, the snake moves by calling
forward()
function, then update the screen - Recursion (calling itself at
ontimer(350)
)
- It sets the heading directions of
-
- Use
monster_refresh()
to motion the monster.-
The moving process
- In order to let the monster move in the closest way to the snake, check the angle between monster and snake using
towards()
- Set different heading directions according to the angle using
setheading()
, thenforward()
- Achieve variable speed by setting the recursion
ontimer()
in a random time
- In order to let the monster move in the closest way to the snake, check the angle between monster and snake using
-
The logic to extend the tail
- If the tail needs to be extended (
g_extend_tail_value != 0
), then do not remove the earliest placed tail (stamp), and makeg_extend_tail_value -= 1
- In this way, the tail isn't "extending" at the end of the body, instead it's "extending" at the head.
The logic to detect body contact between the snake and the monster
- Use
check_win_lose_contact()
function to check. -
Checking contact
- I used a
g_stamp_location_list
to save all locations of current stamps - Checks the monster's distance to every stamp using
distance()
method. - For any one of the distance, they're checked whether having contact with the monster.
- In order to garantee precise check for 2 squares, I used the angle between them and sine and cosine functions to help the check.
- In order to make coninuous overlapping be regarded as only one contact, I saves the recent 20 contact histories (1 second). Only when the contact occurs the first time in 1 second can it be regarded as a new contact.
- I used a
-
Checking win or lose
- The player wins if there's no remaining item in the
g_food_list
- The player loses when the 2 squares (monster and snake) are checked to be overlapping
- In order to garantee precise check for 2 squares, I used the angle between them and sine and cosine functions to help the check. As depicted in picture.
- The player wins if there's no remaining item in the
My program contians these functions handling different jobs:
- Function1:
set_turtles()
:- Set properties of turtles
- No parameter, returns
None
- Function2:
draw_border()
:- Display motion area and status bar borders
- Using the
border_turtle
- No parameter, returns
None
- Function3:
display_status()
:- Displays status bar information
- Including contact, time and motion
- No parameter, returns
None
- Function4:
generate_food()
:- Generate random locations of food
- and make sure every food item aligns well with the snake head, by restricting their locations to specific pixels
- Every food item is one turtle, named with their values
g_food_turtle_1
,g_food_turtle_2
and so on - No parameter, returns
None
- Functions:
move_snake_left()
,move_snake_right()
,move_snake_up()
,move_snake_down()
andpause_snake()
:- Act as event handlers
- Bound to
onkey()
methods ofScreen()
- No parameter, returns
None
- Function:
snake_refresh()
:- Refreshes the screen for every snake movement
- Before making movement, checks whether the snake contacts food, avoids head-to-head crash with the border
- Checks if the player has already won or lost by refering to
g_snake_caught()
and whether the length ofg_food_list
is 0. If so, ends the recursion. - It then makes predictions of the snake path to avoid head-to-head crash with the border. Achieved by making
g_snake_clone
, the clone of snake go one step further. If it exceeds the border,g_motion
is set toPaused
. - It then checks whether the snake contacts food, by evaluating its distance to every food item.
- It sets the heading directions of
g_snake_turtle
according tog_motion
- Let the tail move along with the snake by removing the earliest placed tail (stamp), and then stamp at the current location of
g_snake_turtle
. - Ultimately, the snake moves by calling
forward()
function, then updat the screen - Recursion (calling itself at
ontimer(350)
) - No parameter, returns
None
- Function:
monster_refresh()
:- Refreshes the screen for every monster movement
- Set different heading directions according to the angle using
setheading()
, thenforward()
- Achieve variable speed by setting the recursion
ontimer()
in a random time - No parameter, returns
None
- Function:
check_win_lose_contact()
:- Checks whether the player has won or lost. The player wins if there's no remaining item in the
g_food_list
. The player loses when the 2 squares (monster and snake) are checked to be overlapping - In order to garantee precise check for 2 squares, I used the angle between them and sine and cosine functions to help the check.
- For every stamp location, check whether the monster has contact with snake body. Stamp locations are already saved in
g_stamp_location_list
- Recursion using
ontimer(50)
- No parameter, returns
None
- Checks whether the player has won or lost. The player wins if there's no remaining item in the
- Remaining functions:
game_module()
andmain()
:- The game_module function contains all configurations of the game, calls functions to manipulate time, snake, monster, judges win or los; the main function calls it, plus some other setups