generated from mwc/project_game
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:
BIN
__pycache__/asteroid.cpython-313.pyc
Normal file
BIN
__pycache__/asteroid.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/asteroid_spawner.cpython-313.pyc
Normal file
BIN
__pycache__/asteroid_spawner.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/bullet.cpython-313.pyc
Normal file
BIN
__pycache__/bullet.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/spaceship.cpython-313.pyc
Normal file
BIN
__pycache__/spaceship.cpython-313.pyc
Normal file
Binary file not shown.
36
asteroid.py
Normal file
36
asteroid.py
Normal 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
30
asteroid_spawner.py
Normal 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, 2–3 = 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
31
bullet.py
Normal 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
23
nav_game.py
Normal 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
59
spaceship.py
Normal 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 # don’t 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()
|
||||||
Reference in New Issue
Block a user