This commit is contained in:
bpot
2026-06-12 10:49:57 -04:00
parent b9f3a22436
commit 5663a93521
22 changed files with 755 additions and 5 deletions

144
Tron/Bike.py Normal file
View File

@@ -0,0 +1,144 @@
from Trail import TrailSegment
class Bike:
"""An Agent representing the snake's head. When the game starts, you control
the snake head using the arrow keys. The Bike always has a direction, and
will keep moving in that direction every turn. When you press an arrow key,
you change the Bike's direction.
Attributes:
name: "Snake head"
position: (0,0)
character: ``'v'`` Depending on the snake head's direction, its character
changes to ``'<'``, ``'^'``, ``'>'``, or ``'v'``.
next_segment: Initially ``None``, this is a reference to a SnakeBodySegment.
growing: When set to True, the snake will grow a new segment on its next move.
"""
RIGHT = (1, 0)
UP = (0, -1)
LEFT = (-1, 0)
DOWN = (0, 1)
next_segment = None
growing = False
turn_counter = 0
GROW_INTERVAL = 5 # The bike will grow 1 segment longer every 5 turns.
def __init__(self, player_number, start_position, start_direction):
self.player_number = player_number
self.position = start_position
self.direction = start_direction
self.name = f"Bike {player_number}"
if self.player_number == 1:
self.color = "bright_blue"
else:
self.color = "bright_red"
# Adjust starting character based on starting direction
if start_direction == self.RIGHT: self.character = '>'
elif start_direction == self.LEFT: self.character = '<'
elif start_direction == self.UP: self.character = '^'
elif start_direction == self.DOWN: self.character = 'v'
def play_turn(self, game):
"""On each turn, the snake head uses its position and direction to figure out
its next position. If the snake head is able to move there (it's on the board and
not occuppied by part of the snake's body), it moves.
Then, if the snake head is on the Apple, the Apple moves to a new random position
and ``growing`` is set to True.
Now we need to deal with two situations. First, if ``next_segment`` is not None, there is
a SnakeBodySegment attached to the head. We need the body to follow the head,
so we call ``self.next_segment.move``, passing the head's old position
(this will be the body's new position), a reference to the game, and a value for
``growing``. If the snake needs to grow, we need to pass this information along
the body until it reaches the tail--this is where the next segment will be attached.
If there is no ``next_segment`` but ``self.growing`` is True, it's time to add
a body! We set ``self.next_segment`` to a new SnakeBodySegment, set its
position to the head's old position, and add it to the game. We also add 1 to the
game's score.
"""
# 1. Increment turn counter and check if it's time to grow
self.turn_counter += 1
if self.turn_counter >= self.GROW_INTERVAL:
self.growing = True
self.turn_counter = 0 # Reset the counter for the next growth cycle
x, y = self.position
dx, dy = self.direction
if self.can_move((x+dx, y+dy), game):
self.position = (x+dx, y+dy)
if self.next_segment:
self.next_segment.move((x, y), game, growing=self.growing)
elif self.growing:
self.next_segment = TrailSegment(1, (x, y), self.player_number)
game.add_agent(self.next_segment)
self.growing = False
else:
game.end()
print(f"Player {self.player_number} died")
def handle_keystroke(self, keystroke, game):
"""Checks whether one of the arrow keys has been pressed.
If so, sets the Bike's direction and character.
"""
x, y = self.position
# --- PLAYER 1 CONTROLS (Arrow Keys) ---
if self.player_number == 1:
if keystroke.name == "KEY_RIGHT" and self.direction != self.LEFT:
self.direction = self.RIGHT
self.character = '>'
elif keystroke.name == "KEY_UP" and self.direction != self.DOWN:
self.direction = self.UP
self.character = '^'
elif keystroke.name == "KEY_LEFT" and self.direction != self.RIGHT:
self.direction = self.LEFT
self.character = '<'
elif keystroke.name == "KEY_DOWN" and self.direction != self.UP:
self.direction = self.DOWN
self.character = 'v'
# --- PLAYER 2 CONTROLS (WASD Keys) ---
elif self.player_number == 2:
key_name = str(keystroke)
if keystroke == "d" and self.direction != self.LEFT:
self.direction = self.RIGHT
self.character = '>'
elif keystroke == "w" and self.direction != self.DOWN:
self.direction = self.UP
self.character = '^'
elif keystroke == "a" and self.direction != self.RIGHT:
self.direction = self.LEFT
self.character = '<'
elif keystroke == "s" and self.direction != self.UP:
self.direction = self.DOWN
self.character = 'v'
def can_move(self, position, game):
on_board = game.on_board(position)
empty = game.is_empty(position)
return on_board and empty
'''
def is_on_apple(self, position, game):
apple = game.get_agent_by_name("Apple")
return apple.position == position
'''

62
Tron/Trail.py Normal file
View File

@@ -0,0 +1,62 @@
class TrailSegment:
"""Finally, we need an Agent for the snake's body segments.
SnakeBodySegment doesn't have ``play_turn`` or ``handle_keystroke`` methods because
it never does anything on its own. It only moves when the Bike, or the previous
segment, tells it to move.
Arguments:
segment_id (int): Keeps track of how far back this segment is from the head.
This is used to give the segment a unique name, and also to keep track
of how many points the player earns for eating the next apple.
position (int, int): The initial position.
Attributes:
character: '*'
next_segment: Initially ``None``, this is a reference to a TrailSegment
when this segment is not the last one in the snake's Trail.
"""
character = '*'
next_segment = None
def __init__(self, segment_id, position, player_number): # Added player_number
self.segment_id = segment_id
self.player_number = player_number
# Give it a completely unique name per player
self.name = f"P{player_number} Trail segment {segment_id}"
self.position = position
if self.player_number == 1:
self.color = "bright_blue"
else:
self.color = "bright_red"
def move(self, new_position, game, growing=False):
"""When Bike moves, it sets off a chain reaction, moving all its
Trail segments. Whenever the head or a Trail segment has another segment
(``next_segment``), it calls that segment's ``move`` method.
This method updates the TrailSegment's position. Then, if
``self.next_segment`` is not None, calls that segment's ``move`` method.
If there is no next segment and ``growing`` is True, then we set
``self.next_segment`` to a new SnakeBodySegment in this segment's old
position, and update the game's score.
Arguments:
new_position (int, int): The new position.
game (Game): A reference to the current game.
growing (bool): (Default False) When True, the snake needs to
add a new segment.
"""
old_position = self.position
self.position = new_position
if self.next_segment:
self.next_segment.move(old_position, game, growing=growing)
elif growing:
self.next_segment = TrailSegment(self.segment_id + 1, old_position, self.player_number)
game.add_agent(self.next_segment)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
Tron/nav_game.py Normal file
View File

@@ -0,0 +1,18 @@
# cd ~/Desktop/making_with_code/mwc1/unit3/project_game/Tron
# python nav_game.py
from random import randint
from retro.game import Game
from Bike import Bike
if __name__ == '__main__':
# Initialize Player 1: Player #1, starting at position (5, 8), moving RIGHT
p1 = Bike(player_number=1, start_position=(5, 8), start_direction=Bike.RIGHT)
# Initialize Player 2: Player #2, starting at position (25, 8), moving LEFT
p2 = Bike(player_number=2, start_position=(25, 8), start_direction=Bike.LEFT)
# Create and start the game with both bikes
game = Game([p1, p2], {"*": "Play on!"}, board_size=(32, 16), framerate=8)
game.play()