pygame-snake/main.py

181 lines
6.1 KiB
Python

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()