Last active
February 5, 2019 06:53
-
-
Save shreyas-v-agnihotri/8274d5b1196097650a464e6bc2145b44 to your computer and use it in GitHub Desktop.
Coding sample #1 for DALI Lab application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Simple retro snake game | |
# Instructions: Space to start, WASD to move, P to pause, Q to quit | |
# Author: Shreyas Agnihotri | |
# Credit to Dartmouth COSC department for cs1lib graphics library | |
# March 2018 | |
from cs1lib import * | |
from point import * | |
import math | |
import random | |
# Global constants | |
WINDOW_WIDTH = 600 | |
WINDOW_HEIGHT = 600 | |
FRAME_RATE = 10 | |
BOX_WIDTH = 20 # Standard width of grid tile | |
START_BODY_LENGTH = 5 | |
# Global variables | |
box_list = [] # Represents snake body; array of points for top corner of each body piece | |
game_on = False | |
first_game = True | |
accepting_key = True # Records whether keyboard commands should be registered | |
score = 0 | |
end_score = score | |
direction = "right" # Snake starts on left side of screen and moves right | |
paused = False | |
needs_food = True | |
food = Point(0, 0) # Top corner of grid square representing food (default top left square) | |
# Inserts points into snake body array (number determined by global constant) | |
def create_initial_snake(): | |
global box_list | |
for i in range(START_BODY_LENGTH): | |
box_list.insert(0, Point(int(i * BOX_WIDTH), int(math.ceil((WINDOW_HEIGHT / BOX_WIDTH) / 2)) * BOX_WIDTH)) | |
# Creates black background | |
def set_background(): | |
set_clear_color(0, 0, 0) # black | |
clear() | |
# Draws orange circles at each point in body array | |
def draw_snake(): | |
for box in box_list: | |
set_fill_color(.9, .52, .25) # orange | |
disable_stroke() | |
draw_circle(box.get_x() + int(BOX_WIDTH/2), box.get_y() + int(BOX_WIDTH/2), int(BOX_WIDTH/2)) | |
enable_stroke() | |
# Draws cyan rectangle at current position of food | |
def draw_food(): | |
set_fill_color(77/256, 255/256, 219/256) # cyan | |
draw_rectangle(food.get_x(), food.get_y(), BOX_WIDTH, BOX_WIDTH) | |
# Updates food coordinate to new location on grid outside of snake body | |
def update_food(): | |
# Places food at snake head temporarily | |
food.set_coordinates(box_list[0].get_x(), box_list[0].get_y()) | |
in_snake = True | |
# While food coordinate is within snake | |
while(in_snake): | |
counter = 0 | |
# Sets food position to random box within game grid | |
food_x = BOX_WIDTH * random.randint(0, WINDOW_WIDTH / BOX_WIDTH - 1) | |
food_y = BOX_WIDTH * random.randint(0, WINDOW_HEIGHT / BOX_WIDTH - 1) | |
food.set_coordinates(food_x, food_y) | |
# Checks that food has not been placed inside snake body | |
for i in range(0, len(box_list), 1): | |
if(box_list[i].get_x() == food.get_x() and box_list[i].get_y() == food.get_y()): | |
counter = counter + 1 | |
if(counter == 0): | |
in_snake = False | |
# Draws white grid lines across window | |
def draw_grid(): | |
set_stroke_color(1, 1, 1) # white | |
# Vertical grid lines | |
for i in range(1, int(WINDOW_WIDTH/BOX_WIDTH) + 1): | |
line_x = BOX_WIDTH * i | |
draw_line(line_x, 0, line_x, WINDOW_HEIGHT) | |
#Horizontal grid lines | |
for i in range(1, int(WINDOW_HEIGHT/BOX_WIDTH) + 1): | |
line_y = BOX_WIDTH * i | |
draw_line(0, line_y, WINDOW_WIDTH, line_y) | |
# Processes user-inputted key-press and implements appropriate command | |
def press(key): | |
global direction, paused, accepting_key, game_on, end_score | |
# Gameplay controls | |
if(not paused and accepting_key): # Check that game is accepting key to avoid double-commands within one frame | |
# Changes direction based on key press, if direction change is valid | |
# Snake cannot immediately reverse direction (i.e. from down to up) | |
if(key == "w" and direction != "down"): # W key moves snake up | |
direction = "up" | |
accepting_key = False | |
elif(key == "a" and direction != "right"): # A key moves snake left | |
direction = "left" | |
accepting_key = False | |
elif(key == "s" and direction != "up"): # S key moves snake down | |
direction = "down" | |
accepting_key = False | |
elif(key == "d" and direction != "left"): # D key moves snake right | |
direction = "right" | |
accepting_key = False | |
# Game menu controls | |
if(key == "p"): # P key pauses/unpauses game | |
paused = not paused | |
if key == "q": # Q key quits program | |
cs1_quit() | |
if key == " ": # Space key starts game | |
game_on = True | |
# Shifts points in snake body array by 1 grid unit | |
def update_snake(): | |
# Shifts every snake unit to take on position of the next | |
for i in range(len(box_list)-1, 0, -1): | |
temp_x = box_list[i-1].get_x() | |
temp_y = box_list[i-1].get_y() | |
box_list[i].set_coordinates(temp_x,temp_y) | |
# Moves head by one unit based on current snake direction | |
head_x = box_list[0].get_x() | |
head_y = box_list[0].get_y() | |
if(direction == "right"): | |
box_list[0].set_coordinates(head_x + BOX_WIDTH, head_y) | |
if (direction == "left"): | |
box_list[0].set_coordinates(head_x - BOX_WIDTH, head_y) | |
if(direction == "down"): | |
box_list[0].set_coordinates(head_x, head_y + BOX_WIDTH) | |
if(direction == "up"): | |
box_list[0].set_coordinates(head_x, head_y - BOX_WIDTH) | |
# Checks if snake has collided with food; if so, adds unit to snake body | |
def check_eaten(): | |
global score, needs_food | |
# Check if snake head is at same position as food | |
if(box_list[0].get_x() == food.get_x() and box_list[0].get_y() == food.get_y()): | |
needs_food = True | |
score += 1 | |
# Store coordinates of current snake tail | |
end_x = box_list[len(box_list) - 1].get_x() | |
end_y = box_list[len(box_list) - 1].get_y() | |
# If single-length snake, add new body unit to opposite side of head (compared to moving direction) | |
if(len(box_list) == 1): | |
if(direction == "up"): | |
box_list.append(Point(end_x, end_y + BOX_WIDTH)) | |
elif(direction == "down"): | |
box_list.append(Point(end_x, end_y - BOX_WIDTH)) | |
elif(direction == "left"): | |
box_list.append(Point(end_x + BOX_WIDTH, end_y)) | |
elif(direction == "right"): | |
box_list.append(Point(end_x - BOX_WIDTH, end_y)) | |
# If not single-length snake, could have corner in body) | |
# Add new body unit to opposite side of last and second-to-last units (compared to moving direction) | |
else: | |
next_x = box_list[len(box_list)-2].get_x() | |
next_y = box_list[len(box_list)-2].get_y() | |
(x, y) = (end_x - next_x, end_y - next_y) | |
if(x == 0 and y > 0): | |
box_list.append(Point(end_x, end_y + BOX_WIDTH)) | |
elif(x == 0 and y < 0): | |
box_list.append(Point(end_x, end_y - BOX_WIDTH)) | |
elif(x > 0 and y == 0): | |
box_list.append(Point(end_x + BOX_WIDTH, end_y)) | |
elif(x < 0 and y == 0): | |
box_list.append(Point(end_x - BOX_WIDTH, end_y)) | |
# Checks if snake has collided with a wall; if so, ends game | |
def check_wall(): | |
global game_on, first_game, end_score | |
head_x = box_list[0].get_x() | |
head_y = box_list[0].get_y() | |
# Check head location for right, left, bottom, and top wall | |
if(head_x == WINDOW_WIDTH - BOX_WIDTH and direction == "right"): | |
game_on = False | |
first_game = False | |
end_score = score | |
if(head_x == 0 and direction == "left"): | |
game_on = False | |
first_game = False | |
end_score = score | |
if (head_y == WINDOW_HEIGHT - BOX_WIDTH and direction == "down"): | |
game_on = False | |
first_game = False | |
end_score = score | |
if (head_y == 0 and direction == "up"): | |
game_on = False | |
first_game = False | |
end_score = score | |
# Checks if snake has collided with its own body; if so, ends game | |
def check_contact(): | |
global game_on, first_game, end_score | |
head_x = box_list[0].get_x() | |
head_y = box_list[0].get_y() | |
# Check that no unit in snake body is at same position as head | |
for i in range(1, len(box_list), 1): | |
if (box_list[i].get_x() == head_x and box_list[i].get_y() == head_y): | |
game_on = False | |
first_game = False | |
end_score = score | |
# Creates, draws, and updates game for every frame | |
def game(): | |
global needs_food, accepting_key, score, box_list, first_game, direction | |
set_background() | |
if not game_on: | |
# Lists score if a game has already been playes | |
if not first_game: | |
game_over_message_x = int(WINDOW_WIDTH * 2 / 5) - WINDOW_WIDTH/BOX_WIDTH | |
game_over_message_y = WINDOW_HEIGHT/2 - WINDOW_HEIGHT/10 | |
draw_text("Game over! Your score was: " + str(end_score), game_over_message_x, game_over_message_y) | |
# Resets all variables to defaults and prints starting instructions | |
score = 0 | |
box_list = [] | |
direction = "right" | |
create_initial_snake() | |
needs_food = True | |
set_stroke_color(1, 1, 1) | |
start_instructions_x = int(WINDOW_WIDTH * 2/5) | |
start_instructions_y = WINDOW_HEIGHT/2 | |
control_instructions_x = int(WINDOW_WIDTH * 29/90) | |
control_instructions_y = WINDOW_HEIGHT/2 + WINDOW_HEIGHT/BOX_WIDTH | |
draw_text("Press space to begin!", start_instructions_x, start_instructions_y) | |
draw_text("WASD to move, P to pause, Q to quit", control_instructions_x, control_instructions_y) | |
if game_on: | |
# Updates positions | |
if(not paused): | |
update_snake() | |
accepting_key = True | |
if(needs_food): | |
update_food() | |
needs_food = False | |
# Draws game interface | |
draw_grid() | |
draw_food() | |
draw_snake() | |
# Handles gameplay | |
check_eaten() | |
check_wall() | |
check_contact() | |
# Displays score | |
draw_text("Score: " + str(score), WINDOW_WIDTH - 75, BOX_WIDTH) | |
start_graphics(game, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, key_press=press, framerate=FRAME_RATE) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment