diff --git a/Arena.py b/Arena.py new file mode 100644 index 00000000..05199575 --- /dev/null +++ b/Arena.py @@ -0,0 +1,19 @@ +#This will be the boundary in which our snake navigates + +class Arena(object): + """ + Defines a boundary object to confine the snake + + Takes attributes height, width, padding + padding refers to wall thickness + """ + + def __init__(self, width=640, height=800, padding = 20): + self.height = height + self.width = width + self.x = 0 + self.y = 0 + self.padding = padding + + def __str__(self): + return "The Boundary is %f by %f and is %f units thick" % (self.height, self.width, self.padding) diff --git a/Interactive Programming Reflection.pdf b/Interactive Programming Reflection.pdf new file mode 100644 index 00000000..d0c666b0 Binary files /dev/null and b/Interactive Programming Reflection.pdf differ diff --git a/Project Proposal.ipynb b/Project Proposal.ipynb new file mode 100644 index 00000000..221c6f4d --- /dev/null +++ b/Project Proposal.ipynb @@ -0,0 +1,44 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Project Proposal\n", + "\n", + "For our project we will create a standard calculator snake game, where the snake has to capture a mouse and its movement is controlled by arrow keys. The snake must not contact its own body or the boundary. \n", + "\n", + "Junwon's learning goal: Get familiar with pair programming and Pygame.\n", + "\n", + "Cusai's learning goal: Get familiar with creating user interfacing interactive elements\n", + "\n", + "Libraries that will be used: Pygame. More libraries will be used if needed.\n", + "\n", + "By the mid-project check in, we will try to create the frame of the game, where it will contain the space that contains the space, the \"mouse,\" and the snake. We will create classes that generate these figures, but we probably won't figure out the game mechanics by then (movement of snake, catching the \"mouse\", etc).\n", + "\n", + "The biggest risk is working on Github with someone else before. We hope that we don't have merge conflicts with the program and drag us from working on this project. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README.md b/README.md index 5f822327..7c82f9b8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # InteractiveProgramming This is the base repo for the interactive programming project for Software Design, Spring 2018 at Olin College. + +Note: Pygame must be installed to operate this code. + +To do so, type the following code in the command window: + +$ pip install pygame + +To run the code, type the following to the command window: + +$ python SnakeWithBoundary.py + +To play the game: + +1. Press Enter to start the game. + +2. Control the snake with keyboard arrows. DO NOT COLLIDE WITH THE BLUE BOUNDARY OR YOURSELF. + +3. Catch the "mouse." (the red block). Once you catch the mouse, your length will increase and the mouse will be placed in random location. + +4. To restart the game, close the window and type the given command again. + + +Link to project reflection: /home/junwonlee/GameProject/Link to Interactive Programming Reflection.pdf diff --git a/Snake.py b/Snake.py new file mode 100644 index 00000000..ff5bdab5 --- /dev/null +++ b/Snake.py @@ -0,0 +1,181 @@ +import pygame +from pygame.locals import * +import time + + +class SnakeGameWindowView(object): + + def __init__(self, model, width, height): + self.model = model + self.screen = pygame.display.set_mode((width, height)) + def draw(self): + """ + This function in the class will draw necessary materials + and update every change in the window. + """ + + self.screen.fill(pygame.Color(0, 0, 0)) + for material in self.model.Mouse: + pygame.draw.rect(self.screen, + pygame.Color(255, 0, 0), + pygame.Rect(material.x, material.y, material.height, material.width)) + for material in self.model.Snake: + pygame.draw.rect(self.screen, + pygame.Color(0, 255, 0), + pygame.Rect(material.x, material.y, material.height, material.width)) + pygame.display.update() + +class SnakeGameModel(object): + + def __init__(self): + """This funciton will create dimensions for + the blocks for the snake and the mouse.""" + self.Mouse = [] + self.Snake = [] + snake = Snake(100, 10, 300, 300) + self.Mouse.append(Mouse(10, 10, 100, 100)) + for i in range(int(snake.height / snake.width)): + self.Snake.append(Snake(snake.height / snake.width, 10, i*snake.width+snake.x, snake.y)) + def update(self): + """ + This function will update the function. + """ + for part in self.Snake: + part.update() + def __str__(self): + output_lines = [] + for p in self.Mouse: + output_lines.append(str(p)) + for p in self.Snake: + output_lines.append(str(p)) + return "\n".join(output_lines) + +class Mouse(object): + + def __init__(self, height, width, x, y): + """ + This creates a mouse object. + """ + self.height = height + self.width = width + self.x = x + self.y = y + + def __str__(self): + return 'Mouse is in %f, %f' % (self.x, self.y) + +class Snake(object): + + def __init__(self, height, width, x, y): + """ + This creates a snake object with position, length, width, and velocity. + """ + self.height = height + self.width = width + self.x = x + self.y = y + self.vx = 0.0 + self.vy = 0.0 + + def update(self): + self.x += self.vx + self.y += self.vy + def __str__(self): + return 'Snake is in %f, %f and is %f long.' % (self.x, self.y, self.height) + +class SnakeController(object): + + def __init__(self, model): + self.model = model + + def handle_event(self, event): + """ + This creates different operations for different keys in keyboard. + """ + if event.type != KEYDOWN: + return + if event.key == pygame.K_DOWN: + if abs(self.model.Snake[-1].vy) == -.1: + return + if self.model.Snake[-1].vx == -.1: + self.model.Snake.reverse() + while self.model.Snake[0].x != self.model.Snake[-1].x: + for i in range(len(self.model.Snake)-1): + self.model.Snake[i].x += self.model.Snake[i+1].x - self.model.Snake[i].x + self.model.Snake[i].y += self.model.Snake[i+1].y - self.model.Snake[i].y + if self.model.Snake[i].x == self.model.Snake[-1].x: + self.model.Snake[i].y += self.model.Snake[i].width + + if self.model.Snake[0].x == self.model.Snake[-1].x: + for part in self.model.Snake: + part.vy = .1 + part.vx = 0 + + + if event.key == pygame.K_LEFT: + if abs(self.model.Snake[-1].vx) == -.1: + return + if self.model.Snake[-1].vy == .1: + self.model.Snake.reverse() + while self.model.Snake[0].y != self.model.Snake[-1].y: + for i in range(len(self.model.Snake)-1): + self.model.Snake[i].x += self.model.Snake[i+1].x - self.model.Snake[i].x + self.model.Snake[i].y += self.model.Snake[i+1].y - self.model.Snake[i].y + if self.model.Snake[i].y == self.model.Snake[-1].y: + self.model.Snake[i].x -= self.model.Snake[i].width + + if self.model.Snake[0].y == self.model.Snake[-1].y: + for part in self.model.Snake: + part.vy = 0 + part.vx = -.1 + + if event.key == pygame.K_RIGHT: + if abs(self.model.Snake[-1].vx) == -.1: + return + if self.model.Snake[-1].vy == -.1: + self.model.Snake.reverse() + while self.model.Snake[0].y != self.model.Snake[-1].y: + for i in range(len(self.model.Snake)-1): + self.model.Snake[i].x += self.model.Snake[i+1].x - self.model.Snake[i].x + self.model.Snake[i].y += self.model.Snake[i+1].y - self.model.Snake[i].y + if self.model.Snake[i].y == self.model.Snake[-1].y: + self.model.Snake[i].x += self.model.Snake[i].width + + if self.model.Snake[0].y == self.model.Snake[-1].y: + for part in self.model.Snake: + part.vy = 0 + part.vx = .1 + + if event.key == pygame.K_UP: + if abs(self.model.Snake[-1].vy) == -.1: + return + if self.model.Snake[-1].vx == -.1: + self.model.Snake.reverse() + while self.model.Snake[0].x != self.model.Snake[-1].x: + for i in range(len(self.model.Snake)-1): + self.model.Snake[i].x += self.model.Snake[i+1].x - self.model.Snake[i].x + self.model.Snake[i].y += self.model.Snake[i+1].y - self.model.Snake[i].y + if self.model.Snake[i].x == self.model.Snake[-1].x: + self.model.Snake[i].y -= self.model.Snake[i].width + + if self.model.Snake[0].x == self.model.Snake[-1].x: + for part in self.model.Snake: + part.vx = 0 + part.vy = -.1 + +if __name__ == '__main__': + pygame.init() + model = SnakeGameModel() + window = SnakeGameWindowView(model, 640, 800) + run = True + control = SnakeController(model) + + while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + control.handle_event(event) + model.update() + window.draw() + time.sleep(0.001) + pygame.quit() diff --git a/SnakeWithBoundary.py b/SnakeWithBoundary.py new file mode 100644 index 00000000..4d0a0fa5 --- /dev/null +++ b/SnakeWithBoundary.py @@ -0,0 +1,295 @@ +import pygame +from pygame.locals import * +import time +import random +import sys + + +class SnakeGameWindowView(object): + """ + This class creates a window for the game. + """ + def __init__(self, model, width, height): + """ + This creates necessary model, screen and its width and height, and font. + """ + self.model = model + self.screen = pygame.display.set_mode((width, height)) + self.font = pygame.font.SysFont("arial", 50) + self.width = width + self.height = height + def draw(self): + """ + This function in the class will draw necessary materials + and update every change in the window. + """ + self.screen.fill(pygame.Color(0, 0, 0)) + for material in self.model.Mouse: + pygame.draw.rect(self.screen, + pygame.Color(255, 0, 0), + pygame.Rect(material.x, material.y, material.height, material.width)) + for material in self.model.Snake: + pygame.draw.rect(self.screen, + pygame.Color(0, 255, 0), + pygame.Rect(material.x, material.y, material.height, material.width), not material.head) + for material in self.model.Arena: + pygame.draw.rect(self.screen, + pygame.Color(0, 0, 255), + pygame.Rect(material.x, material.y, material.width, material.height), + material.padding) + play_score = self.font.render(str(self.model.Score.score), 1, (125, 125, 0)) + text_rect = play_score.get_rect(center=(self.width/2, self.height/2)) + if self.model.Score.score == 'Game Over': + self.screen.blit(play_score,text_rect) + restart = self.font.render("Try again next time!", 1, (125, 125, 0)) + text_rect2 = restart.get_rect(center = (self.width/2, self.height*3/4)) + self.screen.blit(restart, text_rect2) + else: + self.screen.blit(play_score,(self.width - 100 , 50)) + pygame.display.update() + + def draw2(self): + """ + This function draws the start screen of the game. + """ + self.screen.fill(pygame.Color(0, 0, 0)) + welcome2 = self.font.render("Welcome to Snake Game!", 1, (125, 125, 0)) + welcome = self.font.render("Press Enter to Start", 1, (125, 125, 0)) + text_rect = welcome.get_rect(center=(self.width/2, self.height/2)) + text_rect2 = welcome2.get_rect(center = (self.width/2, self.height/4)) + self.screen.blit(welcome,text_rect) + self.screen.blit(welcome2, text_rect2) + pygame.display.update() + + +class SnakeGameModel(object): + """ + This creates a model for this game. + """ + def __init__(self): + """This funciton will create dimensions for + the blocks for the snake and the mouse.""" + self.Mouse = [] + self.Snake = [] + self.Arena = [] + snake = Snake(100, 10, 300, 300) + self.Mouse.append(Mouse(10, 10, 100, 100)) + self.Arena.append(Arena()) + self.Score = Score() + for i in range(snake.n): + self.Snake.append(Snake(10, 10, i*snake.width+snake.x, snake.y, i == snake.n-1)) + def update(self): + """ + This function will update the function. + self.Snake[-1] represents the snake head + """ + if self.Snake[-1].vx != 0 or self.Snake[-1].vy != 0: + for i in range(len(self.Snake)-1): + self.Snake[i].x = self.Snake[i+1].x + self.Snake[i].y = self.Snake[i+1].y + self.Snake[-1].update() + + if self.Snake[-1].x == self.Mouse[0].x and self.Snake[-1].y == self.Mouse[0].y: + self.Mouse[0].x = random.randint(2,58) * 10 + self.Mouse[0].y = random.randint(2, 58) * 10 + self.Score.update() + + #Below is the snake growth logic + if self.Snake[1].x - self.Snake[0].x == 10: + self.Snake.insert(0, Snake(10, 10, self.Snake[0].x-10, self.Snake[0].y)) + if self.Snake[1].x - self.Snake[0].x == -10: + self.Snake.insert(0, Snake(10, 10, self.Snake[0].x+10, self.Snake[0].y)) + if self.Snake[1].y - self.Snake[0].y == 10: + self.Snake.insert(0, Snake(10, 10, self.Snake[0].x, self.Snake[0].y-10)) + if self.Snake[1].y - self.Snake[0].y == -10: + self.Snake.insert(0, Snake(10, 10, self.Snake[0].x, self.Snake[0].y+10)) + + print('The Snake gained length!') + print("Score: " + str(self.Score)) + + #Below is the snake collision logic + if abs(self.Snake[-1].x - Arena().width) < Arena().padding or abs(self.Snake[-1].y - Arena().height) < Arena().padding: + self.Snake[-1].vx = 0 + self.Snake[-1].vy = 0 + self.Score.update2() + + if self.Snake[-1].x < Arena().padding - 10 or self.Snake[-1].y < Arena().padding - 10: + self.Snake[-1].vx = 0 + self.Snake[-1].vy = 0 + self.Score.update2() + + for i in range(len(self.Snake)-1): + if self.Snake[i].x - self.Snake[-1].x == 0 and self.Snake[i].y - self.Snake[-1].y == 0: + self.Snake[-1].vx = 0 + self.Snake[-1].vy = 0 + self.Score.update2() + + + def __str__(self): + output_lines = [] + for p in self.Mouse: + output_lines.append(str(p)) + for p in self.Snake: + output_lines.append(str(p)) + for p in self.Arena: + output_lines.append(str(p)) + return "\n".join(output_lines) + +class Arena(object): + """ + Defines a boundary object to confine the snake + + Takes attributes height, width, padding + padding refers to wall thickness + """ + + def __init__(self, width=600, height=600, padding = 20): + self.height = height + self.width = width + self.x = 0 + self.y = 0 + self.padding = padding + + def __str__(self): + return "The Boundary is %f by %f and is %f thick" % (self.height, self.width, self.padding) + +class Mouse(object): + + def __init__(self, height, width, x, y): + """ + This creates a mouse object. + """ + self.height = height + self.width = width + self.x = x + self.y = y + + def __str__(self): + return 'Mouse is in %f, %f' % (self.x, self.y) + +class Snake(object): + + def __init__(self, height, width, x, y, h = False): + """ + This creates a snake object with position, length, width, and velocity. + If the segment represents the snakes head, self.head = 1 + """ + if h: + self.head = 1 + else: + self.head = 0 + + self.height = height + self.width = width + self.n = int(height / width) + self.x = x + self.y = y + self.vx = 10.0 + self.vy = 0.0 + + def update(self): + self.x += self.vx + self.y += self.vy + def __str__(self): + return 'Snake is in %f, %f and is %f long.' % (self.x, self.y, self.height) + +class Score(object): + def __init__(self, score = 0): + """ + This creates a score object that indicates the number of time it + catches the mouse. + """ + self.score = score + def update(self): + self.score += 1 + def update2(self): + """ + This specific update will happen when the snake collides with itself or + the boundary. + """ + self.score = 'Game Over' + def __str__(self): + return str(self.score) + +class SnakeController(object): + + def __init__(self, model): + self.model = model + + def handle_event(self, event): + """ + This creates different operations for different keys in keyboard. + """ + if event.type != KEYDOWN: + return + if self.model.Snake[-1].vy == 0 and self.model.Snake[-1].vx == 0: + return + if event.key == pygame.K_DOWN: + if abs(self.model.Snake[-1].vy) != 0: + return + + self.model.Snake[-1].vy = 10 + self.model.Snake[-1].vx = 0 + + + if event.key == pygame.K_LEFT: + if abs(self.model.Snake[-1].vx) != 0: + return + + self.model.Snake[-1].vy = 0 + self.model.Snake[-1].vx = -10 + + if event.key == pygame.K_RIGHT: + if abs(self.model.Snake[-1].vx) != 0: + return + + self.model.Snake[-1].vy = 0 + self.model.Snake[-1].vx = 10 + + if event.key == pygame.K_UP: + if abs(self.model.Snake[-1].vy) != 0: + return + + self.model.Snake[-1].vy = -10 + self.model.Snake[-1].vx = 0 + + + def handle_event2(self, event): + """ + This creates keyboard operation specifically for the start screen. + """ + if event.type != KEYDOWN: + return + +if __name__ == '__main__': + def mainloop(): + """ + This is the main loop that runs the function. + """ + pygame.init() + pygame.display.set_caption('Snake Game') + model = SnakeGameModel() + window = SnakeGameWindowView(model, 600, 600) + start = True + run = False + control = SnakeController(model) + while start: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + start = False + if event.type == KEYDOWN: + if event.key == pygame.K_RETURN: + run = True + start = False + control.handle_event2(event) + window.draw2() + time.sleep(0.1) + while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + control.handle_event(event) + model.update() + window.draw() + time.sleep(0.1) + mainloop() diff --git a/current_state.png b/current_state.png new file mode 100644 index 00000000..0b9ffaae Binary files /dev/null and b/current_state.png differ