import pygame import sys import random import numpy as np import math class State: def __init__(self, name, value, variable_type, description): self.name = name self.value = value self.variable_type = variable_type self.description = description class StateManager: def __init__(self): # height of the gameplay screen self.SCREEN_HEIGHT = int(1000) # width of the gameplay screen self.SCREEN_WIDTH = int(1000) # fps of the gameplay screen self.FPS = int(60) # the score of the (human) player self.score = int(0) # a boolean variable indicating whether the game has ended or not self.game_over = bool(False) # the x and y position of the agent self.agent_position = {"x": 100, "y": 250} # the x and y position of the green dot self.green_dot_position = {"x": 400, "y": 300} # the x and y position of the red puck self.red_puck_position = {"x": 700, "y": 300} # The radius of the blue circle representing the agent self.agent_radius = int(20) # The color of the blue circle representing the agent, in RGB format self.agent_color = tuple(tuple((0, 0, 255))) # The speed at which the agent moves self.agent_speed = int(5) # The direction of the agent's movement based on arrow keys pressed self.agent_direction = {'up': False, 'down': False, 'left': False, 'right': False} # The radius of the green dot to be drawn on the screen self.green_dot_radius = int(10) # The color of the green dot, in RGB format (green color, non-white as the background is white) self.green_dot_color = tuple(tuple((0, 255, 0))) # Interval in seconds to change the green dot position self.green_dot_movement_interval = float(1.0) # Timestamp of the last position change of the green dot self.green_dot_last_moved_time = float(0.0) # Padding from edges of the screen to ensure the green dot is fully visible when moved, should be at least equal to the radius self.green_dot_padding = int(10) # The radius of the red puck to be drawn on the screen self.red_puck_radius = int(10) # The color of the red puck, in RGB format self.red_puck_color = tuple(tuple((255, 0, 0))) # The speed at which the red puck moves towards the agent self.red_puck_speed = float(1.0) # Distance of the agent from the green dot self.distance_agent_to_green_dot = float(0.0) # Distance of the agent from the red puck self.distance_agent_to_red_puck = float(0.0) # The amount of score to increment or decrement upon reaching or avoiding specific objects, such as the green dot and red puck self.score_change_rate = int(1) # The font size for displaying the score self.score_font_size = int(30) # The font color for displaying the score, in RGB format self.score_font_color = tuple(tuple((0, 0, 0))) # The top-left position for the score display on the screen self.score_position = tuple(tuple((10, 10))) # Boolean flag to indicate if the agent has collided with the green dot self.is_collision_with_green_dot = bool(False) # Bounds within which the green dot can be randomized minus padding self.green_dot_bounds = tuple(tuple((self.green_dot_padding, self.SCREEN_WIDTH - self.green_dot_padding, self.green_dot_padding, self.SCREEN_HEIGHT - self.green_dot_padding))) def handle_keyboard_input(state_manager, event): if event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: state_manager.agent_direction['up'] = True elif event.key == pygame.K_DOWN: state_manager.agent_direction['down'] = True elif event.key == pygame.K_LEFT: state_manager.agent_direction['left'] = True elif event.key == pygame.K_RIGHT: state_manager.agent_direction['right'] = True elif event.type == pygame.KEYUP: if event.key == pygame.K_UP: state_manager.agent_direction['up'] = False elif event.key == pygame.K_DOWN: state_manager.agent_direction['down'] = False elif event.key == pygame.K_LEFT: state_manager.agent_direction['left'] = False elif event.key == pygame.K_RIGHT: state_manager.agent_direction['right'] = False def update_agent_position(state_manager): if state_manager.agent_direction['up']: state_manager.agent_position['y'] = max(state_manager.agent_position['y'] - state_manager.agent_speed, state_manager.agent_radius) if state_manager.agent_direction['down']: state_manager.agent_position['y'] = min(state_manager.agent_position['y'] + state_manager.agent_speed, state_manager.SCREEN_HEIGHT - state_manager.agent_radius) if state_manager.agent_direction['left']: state_manager.agent_position['x'] = max(state_manager.agent_position['x'] - state_manager.agent_speed, state_manager.agent_radius) if state_manager.agent_direction['right']: state_manager.agent_position['x'] = min(state_manager.agent_position['x'] + state_manager.agent_speed, state_manager.SCREEN_WIDTH - state_manager.agent_radius) def update_green_dot_position(state_manager): current_time = pygame.time.get_ticks() / 1000 if current_time - state_manager.green_dot_last_moved_time >= state_manager.green_dot_movement_interval: max_x = state_manager.SCREEN_WIDTH - state_manager.green_dot_padding - state_manager.green_dot_radius max_y = state_manager.SCREEN_HEIGHT - state_manager.green_dot_padding - state_manager.green_dot_radius new_x = random.randint(state_manager.green_dot_padding, max_x) new_y = random.randint(state_manager.green_dot_padding, max_y) state_manager.green_dot_position['x'] = new_x state_manager.green_dot_position['y'] = new_y state_manager.green_dot_last_moved_time = current_time def update_red_puck_position(state_manager): x_diff = state_manager.agent_position['x'] - state_manager.red_puck_position['x'] y_diff = state_manager.agent_position['y'] - state_manager.red_puck_position['y'] distance = (x_diff ** 2 + y_diff ** 2) ** 0.5 if distance != 0: normalized_x = x_diff / distance normalized_y = y_diff / distance else: normalized_x, normalized_y = 0, 0 state_manager.red_puck_position['x'] += normalized_x * state_manager.red_puck_speed state_manager.red_puck_position['y'] += normalized_y * state_manager.red_puck_speed def calculate_and_update_score(state_manager): state_manager.distance_agent_to_green_dot = ((state_manager.agent_position['x'] - state_manager.green_dot_position['x']) ** 2 + (state_manager.agent_position['y'] - state_manager.green_dot_position['y']) ** 2) ** 0.5 state_manager.distance_agent_to_red_puck = ((state_manager.agent_position['x'] - state_manager.red_puck_position['x']) ** 2 + (state_manager.agent_position['y'] - state_manager.red_puck_position['y']) ** 2) ** 0.5 if state_manager.distance_agent_to_green_dot <= state_manager.agent_radius + state_manager.green_dot_radius: state_manager.score += state_manager.score_change_rate if state_manager.distance_agent_to_red_puck <= state_manager.agent_radius + state_manager.red_puck_radius: state_manager.score -= state_manager.score_change_rate def handle_collision_and_relocate_green_dot(state_manager): # Calculate the distance between the agent and the green dot dx = state_manager.agent_position['x'] - state_manager.green_dot_position['x'] dy = state_manager.agent_position['y'] - state_manager.green_dot_position['y'] distance = (dx ** 2 + dy ** 2) ** 0.5 # Check for collision if distance < state_manager.agent_radius + state_manager.green_dot_radius: # Collision detected state_manager.is_collision_with_green_dot = True state_manager.score += 1 # Randomize green dot's position within bounds state_manager.green_dot_position['x'] = random.randint(state_manager.green_dot_bounds[0], state_manager.green_dot_bounds[1]) state_manager.green_dot_position['y'] = random.randint(state_manager.green_dot_bounds[2], state_manager.green_dot_bounds[3]) else: state_manager.is_collision_with_green_dot = False def render_agent(state_manager): agent_pos = (state_manager.agent_position['x'], state_manager.agent_position['y']) pygame.draw.circle(state_manager.screen, state_manager.agent_color, agent_pos, state_manager.agent_radius) def render_green_dot(state_manager): green_dot_pos = (state_manager.green_dot_position['x'], state_manager.green_dot_position['y']) pygame.draw.circle(state_manager.screen, state_manager.green_dot_color, green_dot_pos, state_manager.green_dot_radius) def render_red_puck(state_manager): red_puck_pos = (state_manager.red_puck_position['x'], state_manager.red_puck_position['y']) pygame.draw.circle(state_manager.screen, state_manager.red_puck_color, red_puck_pos, state_manager.red_puck_radius) def render_score(state_manager): font = pygame.font.Font(None, state_manager.score_font_size) score_surface = font.render('Score: ' + str(state_manager.score), True, state_manager.score_font_color) state_manager.screen.blit(score_surface, state_manager.score_position) class Game(): def __init__(self): self.state_manager = StateManager() self.state_manager.screen = pygame.display.set_mode((self.state_manager.SCREEN_WIDTH, self.state_manager.SCREEN_HEIGHT)) def run(self, event): state_manager = self.state_manager if event.type == pygame.QUIT: return False # Detect when the arrow keys are pressed or released. Update the 'agent_direction' dictionary in the StateManager to reflect the current state of the arrow keys (e.g., setting the 'up' key to True if the UP arrow key is pressed). handle_keyboard_input(state_manager, event) # call all the logics # Update the 'agent_position' based on the current 'agent_direction' and 'agent_speed'. If an arrow key is pressed (as reflected in the 'agent_direction' dictionary), move the agent in that direction by 'agent_speed' units per frame, ensuring the agent remains within the boundaries of the gameplay screen. update_agent_position(state_manager) # This function should check if the current time has reached the interval set for the green dot's movement. If the interval has passed, it will update the 'green_dot_position' with new random x and y coordinates that are still within the bounds of the gameplay screen, making sure the dot does not move off the screen or too close to the edge (accounting for 'green_dot_padding'). After changing the position, the function should reset the 'green_dot_last_moved_time' to the current time. update_green_dot_position(state_manager) # Calculate the vector required to move the red puck towards the agent's current position, update the 'red_puck_position' accordingly, and ensure that the red puck moves at the defined 'red_puck_speed'. The movement should be a simple linear interpolation (lerp) between the red puck's current position and the agent's position, scaled by 'red_puck_speed'. The red puck's position is updated every frame to create a smooth following effect without teleporting directly to the agent's position. update_red_puck_position(state_manager) # Increase the score if the agent is close to the green dot and decrease the score if the agent is close to the red puck. 'Closeness' is defined as the agent being within a specific radius from these objects. The function continuously calculates the distance from the agent to both the green dot and red puck and adjusts the score by adding or subtracting the 'score_change_rate' depending on proximity. calculate_and_update_score(state_manager) # Check for collision between the agent and the green dot. If a collision is detected, randomize the green dot's position within the gameplay screen bounds while respecting the defined padding to prevent it from spawning too close to the edges. handle_collision_and_relocate_green_dot(state_manager) # Fill the screen with white state_manager.screen.fill((255, 255, 255)) # Render a blue circle on the screen representing the agent, based on the agent's current position stored in 'agent_position'. The circle should have the color specified by 'agent_color', and the radius given by 'agent_radius'. render_agent(state_manager) # Render the green dot on the screen at the position specified by the 'green_dot_position' state, using the specified 'green_dot_color' and 'green_dot_radius'. render_green_dot(state_manager) # This function should render the red puck on the screen at the coordinates specified by 'red_puck_position', with the color defined in 'red_puck_color' and the radius given by 'red_puck_radius'. The puck should be drawn as a circle. render_red_puck(state_manager) # Renders the updated score in the top-left corner of the gameplay screen. This function retrieves the current score from the state_manager, creates a text surface with the rendered score using a specified font and color, and then blits this text surface to the screen at the top-left position defined in 'score_position'. render_score(state_manager) return True if __name__ == "__main__": game = Game() pygame.init() global event running = True while running: event = pygame.event.poll() running = game.run(event) pygame.display.flip() pygame.quit()