diff --git a/snake_game_human.py b/snake_game_human.py index f51e9c0..eb0c84f 100644 --- a/snake_game_human.py +++ b/snake_game_human.py @@ -24,6 +24,8 @@ class Direction(Enum): BLOCK_SIZE = 20 SPEED = 20 +MAX_SPEED = 50 +SPEED_INCREMENT = 0.5 class SnakeGame: @@ -47,12 +49,35 @@ def __init__(self, w=640, h=480): self.food = None self._place_food() + # pause state + self.paused = False + self.game_over = False + self.game_started = False + + # high score + self.high_score = 0 + def _place_food(self): x = random.randint(0, (self.w-BLOCK_SIZE )//BLOCK_SIZE )*BLOCK_SIZE y = random.randint(0, (self.h-BLOCK_SIZE )//BLOCK_SIZE )*BLOCK_SIZE self.food = Point(x, y) if self.food in self.snake: self._place_food() + + def is_valid_direction(self, new_direction): + """Check if the new direction is valid (not opposite to current direction)""" + opposite_directions = { + Direction.RIGHT: Direction.LEFT, + Direction.LEFT: Direction.RIGHT, + Direction.UP: Direction.DOWN, + Direction.DOWN: Direction.UP + } + return new_direction != opposite_directions[self.direction] + + def get_current_speed(self): + """Calculate current speed based on score""" + current_speed = SPEED + (self.score * SPEED_INCREMENT) + return min(current_speed, MAX_SPEED) def play_step(self): # 1. collect user input @@ -61,37 +86,64 @@ def play_step(self): pygame.quit() quit() if event.type == pygame.KEYDOWN: - if event.key == pygame.K_LEFT: - self.direction = Direction.LEFT + if event.key == pygame.K_SPACE: + self.paused = not self.paused + elif event.key == pygame.K_LEFT: + if self.is_valid_direction(Direction.LEFT): + self.direction = Direction.LEFT elif event.key == pygame.K_RIGHT: - self.direction = Direction.RIGHT + if self.is_valid_direction(Direction.RIGHT): + self.direction = Direction.RIGHT elif event.key == pygame.K_UP: - self.direction = Direction.UP + if self.is_valid_direction(Direction.UP): + self.direction = Direction.UP elif event.key == pygame.K_DOWN: - self.direction = Direction.DOWN - - # 2. move - self._move(self.direction) # update the head - self.snake.insert(0, self.head) + if self.is_valid_direction(Direction.DOWN): + self.direction = Direction.DOWN + # 2. move (skip if paused) + if not self.paused: + self._move(self.direction) # update the head + self.snake.insert(0, self.head) + # 3. check if game over - game_over = False - if self._is_collision(): - game_over = True - return game_over, self.score - - # 4. place new food or just move - if self.head == self.food: - self.score += 1 - self._place_food() + if self._is_collision(): + self.game_over = True + + # 4. place new food or just move + if self.head == self.food: + self.score += 1 + self._place_food() + else: + self.snake.pop() else: - self.snake.pop() + game_over = False # 5. update ui and clock self._update_ui() - self.clock.tick(SPEED) + self.clock.tick(self.get_current_speed()) # 6. return game over and score - return game_over, self.score + return self.game_over, self.score + + def reset_game(self): + """Reset the game to initial state""" + # Update high score if current score is better + if self.score > self.high_score: + self.high_score = self.score + + self.direction = Direction.RIGHT + + self.head = Point(self.w/2, self.h/2) + self.snake = [self.head, + Point(self.head.x-BLOCK_SIZE, self.head.y), + Point(self.head.x-(2*BLOCK_SIZE), self.head.y)] + + self.score = 0 + self.food = None + self._place_food() + + self.paused = False + self.game_over = False def _is_collision(self): # hits boundary @@ -112,9 +164,115 @@ def _update_ui(self): pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE)) - text = font.render("Score: " + str(self.score), True, WHITE) - self.display.blit(text, [0, 0]) + # Display score at the top + score_text = font.render(f"Score: {self.score}", True, WHITE) + self.display.blit(score_text, [10, 10]) + + # Display current speed + speed_text = font.render(f"Speed: {self.get_current_speed():.1f}", True, WHITE) + self.display.blit(speed_text, [self.w - 220, 10]) + + # Display pause message + if self.paused: + pause_text = font.render("PAUSED - Press SPACE to Resume", True, WHITE) + text_rect = pause_text.get_rect(center=(self.w//2, self.h//2)) + pygame.draw.rect(self.display, (50, 50, 50), text_rect.inflate(20, 20)) + self.display.blit(pause_text, text_rect) + + # Display instructions at the bottom + instructions = [ + "Arrow Keys: Move | SPACE: Pause | ESC: Quit" + ] + instruction_font = pygame.font.Font('arial.ttf', 18) + for i, instruction in enumerate(instructions): + instruction_text = instruction_font.render(instruction, True, WHITE) + instruction_rect = instruction_text.get_rect(center=(self.w//2, self.h - 20)) + self.display.blit(instruction_text, instruction_rect) + pygame.display.flip() + + def display_start_screen(self): + """Display start screen with controls and wait for player to start""" + waiting = True + while waiting: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + quit() + if event.type == pygame.KEYDOWN: + return # Any key to start + + # Draw start screen + self.display.fill(BLACK) + + # Display title + title_text = pygame.font.Font('arial.ttf', 50).render("SNAKE GAME", True, WHITE) + title_rect = title_text.get_rect(center=(self.w//2, self.h//2 - 120)) + self.display.blit(title_text, title_rect) + + # Display controls + control_lines = [ + "CONTROLS:", + "Arrow Keys - Move the snake", + "SPACE - Pause/Resume", + "ESC - Quit game", + ] + control_font = pygame.font.Font('arial.ttf', 20) + for i, line in enumerate(control_lines): + control_text = control_font.render(line, True, WHITE) + control_rect = control_text.get_rect(center=(self.w//2, self.h//2 - 20 + (i * 30))) + self.display.blit(control_text, control_rect) + + # Display start instruction + start_text = pygame.font.Font('arial.ttf', 22).render("Press any key to start", True, BLUE2) + start_rect = start_text.get_rect(center=(self.w//2, self.h//2 + 120)) + self.display.blit(start_text, start_rect) + + pygame.display.flip() + self.clock.tick(SPEED) + + def display_game_over_screen(self): + """Display game over screen and handle user input for restart/quit""" + waiting = True + while waiting: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return False # Signal to quit the program + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE or event.key == pygame.K_r: + return True # Signal to restart + elif event.key == pygame.K_ESCAPE: + return False # Signal to quit + + # Draw game over screen + self.display.fill(BLACK) + + # Draw snake and food (frozen state) + for pt in self.snake: + pygame.draw.rect(self.display, BLUE1, pygame.Rect(pt.x, pt.y, BLOCK_SIZE, BLOCK_SIZE)) + pygame.draw.rect(self.display, BLUE2, pygame.Rect(pt.x+4, pt.y+4, 12, 12)) + pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE)) + + # Display score + score_text = font.render("Score: " + str(self.score), True, WHITE) + self.display.blit(score_text, [0, 0]) + + # Draw game over message + game_over_text = font.render("GAME OVER", True, RED) + game_over_rect = game_over_text.get_rect(center=(self.w//2, self.h//2 - 100)) + self.display.blit(game_over_text, game_over_rect) + + # Display final score and high score + final_score_text = font.render(f"Final Score: {self.score}", True, WHITE) + final_score_rect = final_score_text.get_rect(center=(self.w//2, self.h//2 - 40)) + self.display.blit(final_score_text, final_score_rect) + + high_score_display = font.render(f"High Score: {self.high_score}", True, WHITE) + high_score_rect = high_score_display.get_rect(center=(self.w//2, self.h//2)) + self.display.blit(high_score_display, high_score_rect) + + pygame.display.flip() + self.clock.tick(self.get_current_speed()) def _move(self, direction): x = self.head.x @@ -134,14 +292,22 @@ def _move(self, direction): if __name__ == '__main__': game = SnakeGame() + # Show start screen before game begins + game.display_start_screen() + game.game_started = True + # game loop while True: game_over, score = game.play_step() - if game_over == True: - break - + if game_over: + # Display game over screen and wait for user input + restart = game.display_game_over_screen() + if restart: + game.reset_game() + game.game_started = True + else: + break + print('Final Score', score) - - pygame.quit() \ No newline at end of file