commit 32a9b5a6706a561963806aa00e586edc4c7dfa1d Author: 2wenty1ne Date: Tue Nov 25 17:06:32 2025 +0100 Added basic structure and epsilon greedy diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00983fe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/__pycache__/ \ No newline at end of file diff --git a/game.py b/game.py new file mode 100644 index 0000000..cdea1a7 --- /dev/null +++ b/game.py @@ -0,0 +1,171 @@ +import pygame +import random +import math + +# Initialize pygame +pygame.init() + +# Define constants +SCREEN_WIDTH = 400 +SCREEN_HEIGHT = 400 +CELL_SIZE = 40 + +# Define colors +YELLOW = (255, 255, 0) +RED = (255, 0, 0) +WHITE = (255, 255, 255) +BLUE = (0, 0, 255) +BLACK = (0, 0, 0) + +# Labyrinth as a string +labyrinth = [ + "##########", + "#........#", + "#.##..##.#", + "#........#", + "##########" +] + +# Get labyrinth dimensions +ROWS = len(labyrinth) +COLS = len(labyrinth[0]) + +# Initialize game screen +screen = pygame.display.set_mode((COLS * CELL_SIZE, ROWS * CELL_SIZE)) +pygame.display.set_caption("Micro-Pacman") + +# Pacman class +class Pacman: + def __init__(self, x, y): + self.x = x + self.y = y + self.count = 0 + + def move(self, dx, dy): + new_x, new_y = self.x + dx, self.y + dy + if labyrinth[new_y][new_x] != "#": + self.x = new_x + self.y = new_y + + def draw(self): + radius = CELL_SIZE // 2 - 4 + start_angle = math.pi / 6 + end_angle = -math.pi / 6 + pygame.draw.circle(screen, YELLOW, (self.x * CELL_SIZE + CELL_SIZE // 2, self.y * CELL_SIZE + CELL_SIZE // 2), CELL_SIZE // 2 - 4) + # Calculate the points for the mouth + start_pos = (self.x* CELL_SIZE + CELL_SIZE // 2 + int(radius*1.3 * math.cos(start_angle)), + self.y* CELL_SIZE + CELL_SIZE // 2 - int(radius*1.3 * math.sin(start_angle))) + end_pos = (self.x* CELL_SIZE + CELL_SIZE // 2 + int(radius*1.3 * math.cos(end_angle)), + self.y* CELL_SIZE + CELL_SIZE // 2 - int(radius*1.3 * math.sin(end_angle))) + self.count += 1 + if self.count%2==0: + # Draw the mouth by filling a polygon + pygame.draw.polygon(screen, BLACK, [(self.x* CELL_SIZE + CELL_SIZE // 2, self.y* CELL_SIZE + CELL_SIZE // 2), start_pos, end_pos]) + +# Ghost class with pixel art +class Ghost: + # Define the pixel art for the ghost using strings + ghost_pixels = [ + " #### ", + "######", + "## # #", + "######", + "######", + "# # # " + ] + + def __init__(self, x, y): + self.x = x + self.y = y + + def move_towards_pacman(self, pacman): + if self.x < pacman.x and labyrinth[self.y][self.x + 1] != "#": + self.x += 1 + elif self.x > pacman.x and labyrinth[self.y][self.x - 1] != "#": + self.x -= 1 + elif self.y < pacman.y and labyrinth[self.y + 1][self.x] != "#": + self.y += 1 + elif self.y > pacman.y and labyrinth[self.y - 1][self.x] != "#": + self.y -= 1 + + def draw(self): + pixel_size = CELL_SIZE // len(self.ghost_pixels) # Size of each pixel in the ghost art + for row_idx, row in enumerate(self.ghost_pixels): + for col_idx, pixel in enumerate(row): + if pixel == "#": + pixel_x = self.x * CELL_SIZE + col_idx * pixel_size + pixel_y = self.y * CELL_SIZE + row_idx * pixel_size + pygame.draw.rect(screen, RED, (pixel_x, pixel_y, pixel_size, pixel_size)) + +# Draw walls and cookies +def draw_labyrinth(): + for y, row in enumerate(labyrinth): + for x, cell in enumerate(row): + if cell == "#": + pygame.draw.rect(screen, BLUE, (x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)) + elif cell == ".": + pygame.draw.circle(screen, WHITE, (x * CELL_SIZE + CELL_SIZE // 2, y * CELL_SIZE + CELL_SIZE // 2), 5) + +# Main game function +def main(): + clock = pygame.time.Clock() + + # Initialize Pacman and Ghost positions + pacman = Pacman(1, 1) + ghost = Ghost(COLS - 2, ROWS - 2) + + # Game loop + running = True + iter = 0 + while running: + screen.fill(BLACK) + iter = iter + 1 + # Handle events + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Handle Pacman movement + keys = pygame.key.get_pressed() + if keys[pygame.K_LEFT]: + pacman.move(-1, 0) + if keys[pygame.K_RIGHT]: + pacman.move(1, 0) + if keys[pygame.K_UP]: + pacman.move(0, -1) + if keys[pygame.K_DOWN]: + pacman.move(0, 1) + + if iter%3==0: + # Ghost moves towards Pacman + ghost.move_towards_pacman(pacman) + + # Check for collisions (game over if ghost catches pacman) + if pacman.x == ghost.x and pacman.y == ghost.y: + print("Game Over! The ghost caught Pacman.") + running = False + + # Eat cookies + if labyrinth[pacman.y][pacman.x] == ".": + labyrinth[pacman.y] = labyrinth[pacman.y][:pacman.x] + " " + labyrinth[pacman.y][pacman.x+1:] + + # Check if all cookies are eaten (game over) + if all("." not in row for row in labyrinth): + print("You Win! Pacman ate all the cookies.") + running = False + + # Draw the labyrinth, pacman, and ghost + draw_labyrinth() + pacman.draw() + ghost.draw() + + # Update display + pygame.display.flip() + + # Cap the frame rate + clock.tick(5) + + pygame.quit() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..2b6cf4f --- /dev/null +++ b/main.py @@ -0,0 +1,23 @@ +from util import epsilon_greedy, get_start_state + + +AMOUNT_RUNS = 10 +EPSILON = 0.1 + + +""" +state: (x_distance_to_ghost, y_distance_to_ghost, next_cookie_Direction) +action: Direction +q_value: (state, action) +""" +q_values = {} + + +# Amount of single runs +for x in range(AMOUNT_RUNS): + state = get_start_state() + + # Single run, until win or death + while(True): + action = epsilon_greedy(q_values, state, EPSILON) + diff --git a/util.py b/util.py new file mode 100644 index 0000000..891f740 --- /dev/null +++ b/util.py @@ -0,0 +1,46 @@ +from enum import Enum +import random + +class Direction(Enum): + UP = 0 + RIGHT = 1 + DOWN = 2 + LEFT = 3 + + +def get_start_state(): + first_direction_cookie = random.choice([True, False]) + if first_direction_cookie: + return (7, 2, Direction.DOWN) + + return(7, 2, Direction.RIGHT) + + +def epsilon_greedy(q_values, state, epsilon): + best_action = None + best_value = None + + states_for_epsilon = [] + + for (q_state, q_action), value in q_values.items(): + if q_state == state: + states_for_epsilon.append(q_action) + + if best_value is None: + best_value = value + best_action = q_action + continue + + if value > best_value: + best_value = value + best_action = q_action + + if random.random() < epsilon: + random_action = random.choice(states_for_epsilon) + return random_action + + return best_action + + +def take_action(state, action): + pass \ No newline at end of file