import argparse import logging import os import pickle from tkinter import Tk, simpledialog, messagebox import pygame from pygame import locals as pglocals from config import (RESOLUTION, MAP_RESOLUTION, SNAKE_COLOR, BACKGROUND_COLOR, FONT_COLOR, INITIAL_SNAKE_SIZE, SCORES_FILE, MAX_HIGH_SCORES_COUNT) from objects import Snake, Apple from utils import get_score_text, Direction parser = argparse.ArgumentParser() parser.add_argument('-v', '--verbose', help='Show debug logs', action='store_true') args = parser.parse_args() level = logging.INFO if args.verbose: level = logging.DEBUG logging.basicConfig(level=level) logger = logging.getLogger(__name__) pygame.init() Tk().wm_withdraw() # to hide the main window def main(): clock = pygame.time.Clock() logger.debug('pygame initialized') if not pygame.font: logger.warning('Fonts disabled') if not pygame.mixer: logger.warning('Sound disabled') screen = pygame.display.set_mode(RESOLUTION) # type: pygame.Surface pygame.display.set_caption('Snake') scores = [] if os.path.isfile(SCORES_FILE): with open(SCORES_FILE, 'rb') as scores_file: scores = pickle.load(scores_file) logger.info(f'Previous high scores : {scores}') # Retry loop while True: screen.fill(BACKGROUND_COLOR) snake = Snake() screen.fill(SNAKE_COLOR, snake.head) apple = Apple() apple.display(screen) score = 0 score_text, score_rect = get_score_text(score) screen.blit(score_text, score_rect) screen.fill(FONT_COLOR, pygame.Rect(0, MAP_RESOLUTION[1], MAP_RESOLUTION[0], 5)) pygame.display.flip() if scores: display_scores(scores) # Main game loop while True: for event in pygame.event.get(): if event.type == pglocals.QUIT: logger.warning('Received QUIT event') return if event.type == pglocals.KEYDOWN: if event.key == pglocals.K_DOWN or event.key == pglocals.K_s: snake.direction = Direction.DOWN elif event.key == pglocals.K_LEFT or event.key == pglocals.K_q: snake.direction = Direction.LEFT elif event.key == pglocals.K_UP or event.key == pglocals.K_z: snake.direction = Direction.UP elif event.key == pglocals.K_RIGHT or event.key == pglocals.K_d: snake.direction = Direction.RIGHT elif event.key == pglocals.K_ESCAPE: pause = True while pause: clock.tick(5) for subevent in pygame.event.get(): if subevent.type == pglocals.KEYDOWN and subevent.key == pglocals.K_ESCAPE: pause = False break dirty_rects = snake.move(screen, apple) if snake.dead: logger.info(f'Vous avez perdu ! Score : {score}') break if snake.head.colliderect(apple.rect): apple.renew() score += apple.score logger.debug(f'Apple eaten, new score : {score}') screen.fill(BACKGROUND_COLOR, score_rect) old_score_rect = score_rect score_text, score_rect = get_score_text(score) screen.blit(score_text, score_rect) dirty_rects.append(score_rect.union(old_score_rect)) dirty_rects.append(apple.display(screen)) pygame.display.update(dirty_rects) # Run faster as snake grows clock.tick(10 - INITIAL_SNAKE_SIZE + len(snake.slots)) display_end_screen(screen, score) scores = save_score(score, scores) restart = False while not restart: for event in pygame.event.get(): if event.type == pglocals.QUIT: logger.info('Received QUIT event') logger.info(f'High scores : {scores}') return if event.type == pglocals.KEYDOWN: if event.key == pglocals.K_r: logger.info('Restarting game') restart = True elif event.key == pglocals.K_s: display_scores(scores) clock.tick(5) def display_scores(scores): scores_str = '\n'.join(map(lambda s: f"{s['player']} : {s['score']}", scores)) messagebox.showinfo("Tableau des scores", scores_str) def save_score(score, scores): username = simpledialog.askstring( "Enregistrer le score", f"Entrez votre nom d'utilisateur ou laissez vide pour ne pas enregistrer." ) if username: scores.append({'player': username, 'score': score}) logger.info(f'Saving {username}: {score}') scores = sorted(scores, key=lambda x: x['score'], reverse=True)[:MAX_HIGH_SCORES_COUNT] logger.info(f'New high scores : {scores}') with open(SCORES_FILE, 'wb') as scores_file: pickle.dump(scores, scores_file) else: logger.debug('Not saving score') return scores def display_end_screen(screen, score): screen.fill(BACKGROUND_COLOR) font = pygame.font.Font(None, 60) text = font.render(f"PERDU ! Score : {score}", 1, FONT_COLOR) text_rect = text.get_rect() # type: pygame.Rect text_rect.center = screen.get_rect().center screen.blit(text, text_rect) font = pygame.font.Font(None, 30) text = font.render(f"R pour recommencer", 1, FONT_COLOR) text_rect = text.get_rect() # type: pygame.Rect text_rect.center = screen.get_rect().center text_rect.top += 45 screen.blit(text, text_rect) text = font.render(f"S pour les scores", 1, FONT_COLOR) text_rect = text.get_rect() # type: pygame.Rect text_rect.center = screen.get_rect().center text_rect.top += 70 screen.blit(text, text_rect) pygame.display.flip() if __name__ == '__main__': main()