From a9fd143a464c8bd930b05ee6b13636492f900d86 Mon Sep 17 00:00:00 2001 From: JimmyCodeur Date: Sat, 31 Jan 2026 12:55:37 +0100 Subject: [PATCH 1/2] Ajout du mode multijoueur 4 joueurs au Snake Game MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout des touches F4 (2 joueurs) et F5 (4 joueurs) dans le menu - Configuration des 4 joueurs avec positions de spawn distinctes (coins de l'écran) - Couleurs distinctes: P1 (rose), P2 (cyan), P3 (orange), P4 (violet) - Contrôles multiples: Flèches (P1), ZQSD (P2), IJKL (P3), Pavé numérique 8456 (P4) - Détection des collisions entre serpents - Affichage des scores dans les 4 coins de l'écran - Système de victoire: dernier serpent survivant gagne - Génération de nourriture évitant tous les corps de serpents Co-Authored-By: Claude Sonnet 4.5 --- snake.py | 280 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 230 insertions(+), 50 deletions(-) diff --git a/snake.py b/snake.py index ae670af..2946a3f 100644 --- a/snake.py +++ b/snake.py @@ -3,10 +3,12 @@ import random class Snake: - def __init__(self, cell_size): + def __init__(self, cell_size, start_pos=(100, 100), color=(0, 255, 0)): self.cell_size = cell_size self.direction = (cell_size, 0) - self.body = [(100, 100), (90, 100), (80, 100)] + self.body = [start_pos, (start_pos[0] - cell_size, start_pos[1]), (start_pos[0] - 2*cell_size, start_pos[1])] + self.color = color + self.alive = True def move(self): new_head = (self.body[0][0] + self.direction[0], self.body[0][1] + self.direction[1]) @@ -45,8 +47,9 @@ def grow(self): self.body.append((0, 0)) def draw(self, screen): - for segment in self.body: - pygame.draw.rect(screen, green, pygame.Rect(segment[0], segment[1], self.cell_size, self.cell_size)) + if self.alive: + for segment in self.body: + pygame.draw.rect(screen, self.color, pygame.Rect(segment[0], segment[1], self.cell_size, self.cell_size)) def check_portal_collision(self, portal): head = self.body[0] @@ -77,8 +80,9 @@ def draw(self, screen): pygame.draw.rect(screen, gray, pygame.Rect(self.position[0], self.position[1], self.cell_size, self.cell_size)) class Score: - def __init__(self): + def __init__(self, player_number=1): self.score = 0 + self.player_number = player_number def increase_score(self, points): self.score += points @@ -86,10 +90,13 @@ def increase_score(self, points): def reset_score(self): self.score = 0 - def display_score(self, screen): + def display_score(self, screen, position=None): font = pygame.font.Font(None, 36) - text = font.render(f"Score: {self.score}", True, white) - screen.blit(text, (10, 10)) + text = font.render(f"P{self.player_number}: {self.score}", True, white) + if position: + screen.blit(text, position) + else: + screen.blit(text, (10, 10)) class PowerUp: def __init__(self, cell_size): @@ -136,22 +143,30 @@ def choose_difficulty(): sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_F1: - return "Facile", fps_facile + return "Facile", fps_facile, 1 elif event.key == pygame.K_F2: - return "Moyen", fps_moyen + return "Moyen", fps_moyen, 1 elif event.key == pygame.K_F3: - return "Difficile", fps_difficile + return "Difficile", fps_difficile, 1 + elif event.key == pygame.K_F4: + return "Facile", fps_facile, 2 + elif event.key == pygame.K_F5: + return "Facile", fps_facile, 4 screen.fill(black) font = pygame.font.Font(None, 36) text = font.render("Choisissez le niveau de difficulté:", True, white) + screen.blit(text, (width//4, height//2 - 100)) + text = font.render("F1 - Facile (1 joueur)", True, white) screen.blit(text, (width//4, height//2 - 50)) - text = font.render("F1 - Facile", True, white) + text = font.render("F2 - Moyen (1 joueur)", True, white) screen.blit(text, (width//4, height//2)) - text = font.render("F2 - Moyen", True, white) + text = font.render("F3 - Difficile (1 joueur)", True, white) screen.blit(text, (width//4, height//2 + 50)) - text = font.render("F3 - Difficile", True, white) + text = font.render("F4 - Mode 2 joueurs", True, white) screen.blit(text, (width//4, height//2 + 100)) + text = font.render("F5 - Mode 4 joueurs", True, white) + screen.blit(text, (width//4, height//2 + 150)) pygame.display.flip() def draw_centered_text(screen, text, font, color, y_offset=0): @@ -160,6 +175,25 @@ def draw_centered_text(screen, text, font, color, y_offset=0): text_rect.center = (width // 2, height // 2 + y_offset) screen.blit(text_surface, text_rect) +def check_snake_collision(snake, other_snakes): + head = snake.body[0] + for other_snake in other_snakes: + if other_snake != snake and other_snake.alive: + if head in other_snake.body: + return True + return False + +def generate_food_position_avoiding_snakes(food, snakes): + while True: + food.generate_position() + position_valid = True + for snake in snakes: + if snake.alive and food.position in snake.body: + position_valid = False + break + if position_valid: + break + def game_over_screen(): while True: for event in pygame.event.get(): @@ -208,11 +242,36 @@ def game_over_screen(): screen = pygame.display.set_mode((width, height)) pygame.display.set_caption("Snake Game") -difficulty, fps = choose_difficulty() +difficulty, fps, num_players = choose_difficulty() + +# Configuration des positions et couleurs pour chaque joueur +player_configs = [ + {"pos": (100, 100), "color": pink}, + {"pos": (width - 100, 100), "color": cyan}, + {"pos": (100, height - 100), "color": orange}, + {"pos": (width - 100, height - 100), "color": purple} +] + +# Score positions (coins de l'écran) +score_positions = [ + (10, 10), # Haut gauche + (width - 120, 10), # Haut droit + (10, height - 50), # Bas gauche + (width - 120, height - 50) # Bas droit +] + +# Initialisation des serpents et scores +snakes = [] +scores = [] +for i in range(num_players): + snake = Snake(cell_size, player_configs[i]["pos"], player_configs[i]["color"]) + snakes.append(snake) + score = Score(i + 1) + scores.append(score) -snake = Snake(cell_size) food = Food(cell_size) -score = Score() +generate_food_position_avoiding_snakes(food, snakes) + obstacles = [] power_ups = [] num_power_ups = 3 @@ -229,51 +288,172 @@ def game_over_screen(): portal = Porte(cell_size, (width - cell_size, height // 2)) +# Boucle de jeu principale 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_UP: - snake.change_direction((0, -cell_size)) - elif event.key == pygame.K_DOWN: - snake.change_direction((0, cell_size)) - elif event.key == pygame.K_LEFT: - snake.change_direction((-cell_size, 0)) - elif event.key == pygame.K_RIGHT: - snake.change_direction((cell_size, 0)) - - snake.move() - - if snake.check_collision(obstacles, width, height): - score.reset_score() - if not game_over_screen(): - pygame.quit() - sys.exit() - else: - snake = Snake(cell_size) - food = Food(cell_size) - obstacles = [] - if difficulty == "Moyen" or difficulty == "Difficile": - num_obstacles = 10 if difficulty == "Moyen" else 20 - for _ in range(num_obstacles): - obstacle = Obstacle(cell_size) - obstacles.append(obstacle) - - if snake.check_food_collision(food): - snake.grow() - food.generate_position() - score.increase_score(10) - + # Contrôles Joueur 1 - Flèches directionnelles + if event.key == pygame.K_UP and snakes[0].alive: + snakes[0].change_direction((0, -cell_size)) + elif event.key == pygame.K_DOWN and snakes[0].alive: + snakes[0].change_direction((0, cell_size)) + elif event.key == pygame.K_LEFT and snakes[0].alive: + snakes[0].change_direction((-cell_size, 0)) + elif event.key == pygame.K_RIGHT and snakes[0].alive: + snakes[0].change_direction((cell_size, 0)) + + # Contrôles Joueur 2 - ZQSD + if num_players >= 2: + if event.key == pygame.K_z and snakes[1].alive: + snakes[1].change_direction((0, -cell_size)) + elif event.key == pygame.K_s and snakes[1].alive: + snakes[1].change_direction((0, cell_size)) + elif event.key == pygame.K_q and snakes[1].alive: + snakes[1].change_direction((-cell_size, 0)) + elif event.key == pygame.K_d and snakes[1].alive: + snakes[1].change_direction((cell_size, 0)) + + # Contrôles Joueur 3 - IJKL + if num_players >= 3: + if event.key == pygame.K_i and snakes[2].alive: + snakes[2].change_direction((0, -cell_size)) + elif event.key == pygame.K_k and snakes[2].alive: + snakes[2].change_direction((0, cell_size)) + elif event.key == pygame.K_j and snakes[2].alive: + snakes[2].change_direction((-cell_size, 0)) + elif event.key == pygame.K_l and snakes[2].alive: + snakes[2].change_direction((cell_size, 0)) + + # Contrôles Joueur 4 - Pavé numérique 8456 + if num_players >= 4: + if event.key == pygame.K_KP8 and snakes[3].alive: + snakes[3].change_direction((0, -cell_size)) + elif event.key == pygame.K_KP5 and snakes[3].alive: + snakes[3].change_direction((0, cell_size)) + elif event.key == pygame.K_KP4 and snakes[3].alive: + snakes[3].change_direction((-cell_size, 0)) + elif event.key == pygame.K_KP6 and snakes[3].alive: + snakes[3].change_direction((cell_size, 0)) + + # Déplacer tous les serpents vivants + for snake in snakes: + if snake.alive: + snake.move() + + # Vérifier les collisions pour chaque serpent + for i, snake in enumerate(snakes): + if not snake.alive: + continue + + # Collision avec les murs ou son propre corps + if snake.check_collision(obstacles, width, height): + snake.alive = False + continue + + # Collision avec les autres serpents (mode multijoueur) + if num_players > 1 and check_snake_collision(snake, snakes): + snake.alive = False + continue + + # Collision avec la nourriture + if snake.check_food_collision(food): + snake.grow() + generate_food_position_avoiding_snakes(food, snakes) + scores[i].increase_score(10) + + # Vérifier s'il reste des serpents vivants + alive_snakes = [s for s in snakes if s.alive] + + # En mode multijoueur, vérifier s'il y a un gagnant + if num_players > 1: + if len(alive_snakes) == 0: + # Tous les serpents sont morts + if not game_over_screen(): + pygame.quit() + sys.exit() + else: + # Réinitialiser le jeu + snakes = [] + scores = [] + for i in range(num_players): + snake = Snake(cell_size, player_configs[i]["pos"], player_configs[i]["color"]) + snakes.append(snake) + score = Score(i + 1) + scores.append(score) + food = Food(cell_size) + generate_food_position_avoiding_snakes(food, snakes) + obstacles = [] + if difficulty == "Moyen" or difficulty == "Difficile": + num_obstacles = 10 if difficulty == "Moyen" else 20 + for _ in range(num_obstacles): + obstacle = Obstacle(cell_size) + obstacles.append(obstacle) + elif len(alive_snakes) == 1: + # Un seul serpent survivant - afficher le gagnant + winner_index = snakes.index(alive_snakes[0]) + screen.fill(black) + font = pygame.font.Font(None, 72) + draw_centered_text(screen, f"Joueur {winner_index + 1} gagne!", font, alive_snakes[0].color) + pygame.display.flip() + pygame.time.wait(3000) + + if not game_over_screen(): + pygame.quit() + sys.exit() + else: + # Réinitialiser le jeu + snakes = [] + scores = [] + for i in range(num_players): + snake = Snake(cell_size, player_configs[i]["pos"], player_configs[i]["color"]) + snakes.append(snake) + score = Score(i + 1) + scores.append(score) + food = Food(cell_size) + generate_food_position_avoiding_snakes(food, snakes) + obstacles = [] + if difficulty == "Moyen" or difficulty == "Difficile": + num_obstacles = 10 if difficulty == "Moyen" else 20 + for _ in range(num_obstacles): + obstacle = Obstacle(cell_size) + obstacles.append(obstacle) + else: + # Mode solo + if len(alive_snakes) == 0: + scores[0].reset_score() + if not game_over_screen(): + pygame.quit() + sys.exit() + else: + snakes = [Snake(cell_size, player_configs[0]["pos"], player_configs[0]["color"])] + food = Food(cell_size) + generate_food_position_avoiding_snakes(food, snakes) + obstacles = [] + if difficulty == "Moyen" or difficulty == "Difficile": + num_obstacles = 10 if difficulty == "Moyen" else 20 + for _ in range(num_obstacles): + obstacle = Obstacle(cell_size) + obstacles.append(obstacle) + + # Affichage screen.fill(black) - snake.draw(screen) + # Dessiner tous les serpents + for snake in snakes: + snake.draw(screen) + food.draw(screen) portal.draw(screen) + for obstacle in obstacles: obstacle.draw(screen) - score.display_score(screen) + + # Afficher les scores + for i, score in enumerate(scores): + score.display_score(screen, score_positions[i]) pygame.display.flip() pygame.time.Clock().tick(fps) From 70e7904a42181bc078338c541a8e1c5c68d758c7 Mon Sep 17 00:00:00 2001 From: JimmyCodeur Date: Sat, 31 Jan 2026 12:55:52 +0100 Subject: [PATCH 2/2] [Tickova] Add 4-player local multiplayer mode --- __pycache__/snake.cpython-312.pyc | Bin 0 -> 23945 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 __pycache__/snake.cpython-312.pyc diff --git a/__pycache__/snake.cpython-312.pyc b/__pycache__/snake.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..492f863a35d4cea751b025e77e395b001af1e388 GIT binary patch literal 23945 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 literal 0 HcmV?d00001