11a: ICA4, OOP and Pygame
Table of Contents
1. In-class assignment
1.1. Question 1
Included with this assignment is a file containing movie data for the year 2023. Save both IN THE SAME FOLDER on your laptop. Open the movie data file in Spyder (or any other editor) and have a look at how it is structured.
In this first question you don't have to load the file yet. Instead, you will complete a function that takes ONE movie description (one line of the file), and stores it into a dictionary. The function then returns that dictionary.
The dictionary should have four keys:
- "name":
- the name of the movie (the value should be a string)
- "theatres":
- number of theatres in which the movie released (an integer)
- "returns":
- the gross returns from ticket sales (an integer)
- "distributor":
- the name of the company that distributed the movie (a string)
Example: If input is " Cocaine Bear || 3571 || 64388510 || Universal Pictures ", then output should be:
{ "name": "Cocaine bear", "theatres": 3571, "returns": 64388510, "distributor": "Universal Pictures" }
s = " Cocaine Bear || 3571 || 64388510 || Universal Pictures " def parse_movie(description): var1 = description.split("||") return {"name": var1[0].strip(), "theatres": int(var1[1]), "returns": int(var1[2]), "distributor": var1[3].strip()} # Fill in the body of this function print(parse_movie(s)) #parse_movie("Cocaine Bear || 3571 || 64388510 || Universal Pictures")
1.2. Question 2
The next step is to make a function that can load the entire movie database. The function should take as input the name of a file containing movie data. It should then open the file and read all the movie descriptions. Convert each description into a dictionary using the function you wrote previously, and collect all results in a list.
Example:
given "Movies2023Assignment.txt" as input, the function should return a list like this:
[ {"name": "Triangle of Sadness", ...}, { "name": "Cocaine Bear", ... }, ...]
def parse_movie(description): var1 = description.split("||") return {"name": var1[0], "theatres": int(var1[1]), "returns": int(var1[2]), "distributor": var1[3]} def load_movies(filename): lst1 = [] with open(filename) as something: var1 = something.readlines() for line in var1: lst1.append(parse_movie(line)) return lst1 print(load_movies("/home/misha/git/programming-your-world/in-class-assignments/in-class-4/Movies2023_Assignment.txt"))
1.3. Question 3
Now we wish to know how many movies in 2023 were released in more than a given number of theatres. The function below receives as input a list of movie data as produced by the previous question, as well as a number n. It then returns how many movies were released in n or more theatres. You can use the given function "testquestion3" to check if your code works properly. There should be 55 movies that were released in 3000 or more theatres.
def parse_movie(description): var1 = description.split("||") return {"name": var1[0], "theatres": int(var1[1]), "returns": int(var1[2]), "distributor": var1[3]} def load_movies(filename): lst1 = [] with open(filename) as something: var1 = something.readlines() for line in var1: lst1.append(parse_movie(line)) return lst1 def movies_in_at_least_n_theatres(movies, n): something = 0 for i in movies: if i["theatres"] >= n: something += 1 return something def test_question3(): movies = load_movies("/home/misha/git/programming-your-world/in-class-assignments/in-class-4/Movies2023_Assignment.txt") return movies_in_at_least_n_theatres(movies, 3000) print(test_question3())
2. Individual project
3. Object oriented programming
3.1. Recap
class Block(): def __init__(self, height_input, width_input, x_input, y_input): self.height = height_input self.width = width_input self.x = x_input self.y = y_input def move(self, dx, dy): self.x += dx self.y += dy lst = [] for i in range(10): lst.append(Block(100,100,i,0)) for i in lst: i.move(0,-2) for i in lst: print(i.x, i.y)
4. Pygame
To understand what is going on below:
- According to PEP8, constants are written in all caps.
- To define a variable inside a function in a way that makes the variable also available outside the function, you can make the variable global:
def example(): global text text = "hi" example() print(text)
- For the rest:
- https://www.pygame.org/docs/
- pygame.Rect is an class definition that takes "left, top, width and height" and returns an object ("Rect") with those attributes.
import pygame block = pygame.Rect(4,-4,1,1) print(block.top)
- pygame.draw.rect is a method that takes a screen, color and Rect object as input, and draws that rectangle (with the indicated color) on the screen.
- pygame.display.setmode creates a display surface
- pygame.time.Clock(): creates a new Clock object that can be used to track an amount of time. The clock also provides several functions to help control a game's framerate.
- As the game is running, there is a stream of events.
- pygame.event.get() gets a sequence of ongoing events.
- Used to check discrete events (e.g. pressing a key once)
- pygame.key.getpressed() gets a sequence of boolean values representing the state of every key on the keyboard
- Used to keep track of longer events (e.g. holding down a key)
import pygame import sys # Constants SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 BLOCK_SIZE = 20 BACKGROUND_COLOR = (255, 255, 255) BLOCK_COLOR = (0, 0, 255) FPS = 60 class Block: def __init__(self, x, y): self.rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE) def draw(self, screen): pygame.draw.rect(screen, BLOCK_COLOR, self.rect) def move(self, dx, dy): self.rect.x += dx self.rect.y += dy # Initialize Pygame pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) clock = pygame.time.Clock() blocks = [] current_block = None def new_block(): global current_block x = (SCREEN_WIDTH - BLOCK_SIZE) // 2 y = (SCREEN_HEIGHT - BLOCK_SIZE) // 2 block = Block(x, y) blocks.append(block) current_block = block # Main game loop while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: new_block() elif event.key == pygame.K_q: pygame.quit() sys.exit() keys = pygame.key.get_pressed() if current_block: if keys[pygame.K_LEFT]: current_block.move(-5, 0) if keys[pygame.K_RIGHT]: current_block.move(5, 0) if keys[pygame.K_UP]: current_block.move(0, -5) if keys[pygame.K_DOWN]: current_block.move(0, 5) screen.fill(BACKGROUND_COLOR) for block in blocks: block.draw(screen) pygame.display.update() clock.tick(FPS)
5. image instead of block
import pygame import sys # Constants SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 BLOCK_SIZE = 20 BACKGROUND_COLOR = (255, 255, 255) BLOCK_COLOR = (0, 0, 255) FPS = 60 class ImageObject: def __init__(self, x, y, image_path, width, height): original_image = pygame.image.load(image_path).convert_alpha() self.image = pygame.transform.scale(original_image, (width, height)) self.rect = self.image.get_rect(topleft=(x, y)) def draw(self, screen): screen.blit(self.image, self.rect) def move(self, dx, dy): self.rect.x += dx self.rect.y += dy # Initialize Pygame pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) clock = pygame.time.Clock() objects = [] current_object = None def new_object(): global current_object x = (SCREEN_WIDTH - BLOCK_SIZE) // 2 y = (SCREEN_HEIGHT - BLOCK_SIZE) // 2 obj = ImageObject(x, y, "xxxxxxxxxxxxx", 100, 120) # add an image yourself objects.append(obj) current_object = obj # Main game loop while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: new_object() elif event.key == pygame.K_q: pygame.quit() sys.exit() keys = pygame.key.get_pressed() if current_object: if keys[pygame.K_LEFT]: current_object.move(-5, 0) if keys[pygame.K_RIGHT]: current_object.move(5, 0) if keys[pygame.K_UP]: current_object.move(0, -5) if keys[pygame.K_DOWN]: current_object.move(0, 5) screen.fill(BACKGROUND_COLOR) for obj in objects: obj.draw(screen) pygame.display.update() clock.tick(FPS)
6. Chasing
import pygame import sys # Constants SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600 BACKGROUND_COLOR = (255, 255, 255) FPS = 60 class ImageObject: def __init__(self, x, y, image_path, width, height): original_image = pygame.image.load(image_path).convert_alpha() self.image = pygame.transform.scale(original_image, (width, height)) self.rect = self.image.get_rect(topleft=(x, y)) def draw(self, screen): screen.blit(self.image, self.rect) def move(self, dx, dy): new_x = self.rect.x + dx new_y = self.rect.y + dy # Constrain within borders if 0 <= new_x <= SCREEN_WIDTH - self.rect.width: self.rect.x = new_x if 0 <= new_y <= SCREEN_HEIGHT - self.rect.height: self.rect.y = new_y # Initialize Pygame pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) clock = pygame.time.Clock() font = pygame.font.SysFont(None, 48) # Create image objects image_path = "xxxxxxxxxxxxxxxx" # Add your own image image_path2 = "xxxxxxxxxxxxx" # Add your own image object1 = ImageObject(200, 300, image_path, 100, 120) object2 = ImageObject(400, 300, image_path2, 100, 120) # Main game loop while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # Get key states keys = pygame.key.get_pressed() # Control object1 with arrow keys if keys[pygame.K_LEFT]: object1.move(-5, 0) if keys[pygame.K_RIGHT]: object1.move(5, 0) if keys[pygame.K_UP]: object1.move(0, -5) if keys[pygame.K_DOWN]: object1.move(0, 5) # Control object2 with WASD keys if keys[pygame.K_a]: object2.move(-5, 0) if keys[pygame.K_d]: object2.move(5, 0) if keys[pygame.K_w]: object2.move(0, -5) if keys[pygame.K_s]: object2.move(0, 5) # Check for collision if object1.rect.colliderect(object2.rect): text = font.render('Hit!', True, (255, 0, 0)) screen.fill(BACKGROUND_COLOR) screen.blit(text, ((SCREEN_WIDTH-text.get_width())//2, (SCREEN_HEIGHT-text.get_height())//2)) pygame.display.update() pygame.time.delay(2000) # Display "Hit!" for 2 seconds pygame.quit() sys.exit() # Drawing screen.fill(BACKGROUND_COLOR) object1.draw(screen) object2.draw(screen) pygame.display.update() clock.tick(FPS)