I did not realize this had to be three submissions. My first progress was the add lives to the asteroid game.

For this feature, I added a health system so the player has three
lives instead of losing instantly. In nav_game.py, I added "health": 3
to the game state, and in spaceship.py I created a take_damage method
that subtracts one life when the ship is hit. If the player's health
reaches zero, the game ends. This change is important because it
makes the game less punishing, gives the player more chances to
recover from mistakes, and makes gameplay feel more balanced and fun.
This commit is contained in:
angelotr
2025-12-08 23:39:17 -05:00
parent 3816c0bac0
commit 47a9e1abe3
9 changed files with 179 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

36
asteroid.py Normal file
View File

@@ -0,0 +1,36 @@
# asteroid.py
# ------------
# By MWC Contributors
# This module defines an asteroid agent class.
class Asteroid:
character = 'O'
def __init__(self, position, speed=2):
# speed = how often it moves (1 = every turn, 2 = every 2 turns, etc.)
self.position = position
self.speed = speed
def play_turn(self, game):
width, height = game.board_size
# Only move on some turns (for variable speed)
if game.turn_number % self.speed != 0:
return
x, y = self.position
# If at bottom, remove asteroid
if y == height - 1:
game.remove_agent(self)
else:
ship = game.get_agent_by_name('ship')
new_position = (x, y + 1)
# If we hit the ship, damage it and remove this asteroid
if new_position == ship.position:
if hasattr(ship, "take_damage"):
ship.take_damage(game)
game.remove_agent(self)
else:
self.position = new_position

30
asteroid_spawner.py Normal file
View File

@@ -0,0 +1,30 @@
# asteroid_spawner.py
# -------------------
# By MWC Contributors
# This module defines an AsteroidSpawner agent class.
from random import randint
from asteroid import Asteroid
class AsteroidSpawner:
display = False
def play_turn(self, game):
width, height = game.board_size
# Increase score each turn survived
game.state['score'] += 1
if self.should_spawn_asteroid(game.turn_number):
# Random x position at the top
x = randint(0, width - 1)
# Random speed: 1 = fast, 23 = slower
speed = randint(1, 3)
asteroid = Asteroid((x, 0), speed)
game.add_agent(asteroid)
def should_spawn_asteroid(self, turn_number):
# More asteroids as the game goes on
return randint(0, 1000) < turn_number

31
bullet.py Normal file
View File

@@ -0,0 +1,31 @@
from asteroid import Asteroid
class Bullet:
"""A bullet fired by the spaceship that moves upward and destroys asteroids."""
character = '|'
def __init__(self, position):
self.position = position
def play_turn(self, game):
# Move bullet up one row each turn
x, y = self.position
y -= 1
# If the bullet goes off the top, remove it
if y < 0:
game.remove_agent(self)
return
self.position = (x, y)
# Check if we hit an asteroid
# We loop over a copy of the agents list because we may remove agents
for agent in list(game.agents):
if isinstance(agent, Asteroid) and agent.position == self.position:
# Destroy the asteroid and the bullet
game.remove_agent(agent)
game.remove_agent(self)
# Reward points for hitting an asteroid
game.state["score"] += 10
return

23
nav_game.py Normal file
View File

@@ -0,0 +1,23 @@
# nav_game.py
# ------------
# By MWC Contributors
# This class implements a simple game where a spaceship avoids asteroids.
from spaceship import Spaceship
from asteroid import Asteroid
from retro.game import Game
from spaceship import Spaceship
from asteroid_spawner import AsteroidSpawner
board_size = (25, 25)
ship = Spaceship(board_size)
spawner = AsteroidSpawner()
#adds health to the game
state = {
"score":0,
"health":3 #3 lives
}
game = Game([ship, spawner], state, board_size=board_size)
game.play()

59
spaceship.py Normal file
View File

@@ -0,0 +1,59 @@
# spaceship.py
# ------------
# By MWC Contributors
# This module defines a spaceship agent class.
from bullet import Bullet
class Spaceship:
name = "ship"
character = '^'
def __init__(self, board_size):
board_width, board_height = board_size
self.position = (board_width // 2, board_height - 1)
def handle_keystroke(self, keystroke, game):
x, y = self.position
new_position = None
# Move LEFT
if keystroke.name in ("KEY_LEFT", "a", "A"):
new_position = (x - 1, y)
# Move RIGHT
elif keystroke.name in ("KEY_RIGHT", "d", "D"):
new_position = (x + 1, y)
# Move UP
elif keystroke.name in ("KEY_UP", "w", "W"):
new_position = (x, y - 1)
# Move DOWN
elif keystroke.name in ("KEY_DOWN", "s", "S"):
new_position = (x, y + 1)
# SHOOT (m or M)
elif keystroke.name in ("m", "M"):
bx, by = x, y - 1
if game.on_board((bx, by)):
bullet = Bullet((bx, by))
game.add_agent(bullet)
return # dont move on shoot
else:
return # ignore other keys
# If we are trying to move:
if new_position is not None and game.on_board(new_position):
if game.is_empty(new_position):
self.position = new_position
else:
# We bumped into something (probably an asteroid)
self.take_damage(game)
def take_damage(self, game):
"""Reduce health; end game if health reaches 0."""
game.state["health"] -= 1
if game.state["health"] <= 0:
game.end()