FINAL GAME SUBMISSION

Got the man chaser to work!
Moved all global variables to nav_game.py so easier to adjust later
This commit is contained in:
Chris Mekelburg 2024-12-14 20:55:55 -05:00
parent ba4a255ca1
commit 7e5420d8bb
14 changed files with 162 additions and 62 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,74 +2,88 @@ from man import Man
from snack import Snack
from mine import Mine
from random import shuffle
from chaser import Man_Chaser
'''This class sets up the board initially, but also is responsible for adding snacks
and mines after they are consumed. Also increases mines as game gets more difficult.'''
'''Values to call later, sets no more than 100 mines, and each list represent a level [score, num_mines],
which is called in mine_counter below.'''
MAX_MINES = 100
LEVELS = [
[5, 10],
[10, 20],
[15, 30],
[20, 40],
[30,50],
[40,60],
[50,70],
[60,80],
[70,90]
]
"""This class sets up the board initially, but also is responsible for adding snacks
and mines after they are consumed. Also increases mines as game gets more difficult."""
class Board:
display = False
snack=False
mine=False
chaser=False
def __init__(self,width, height,num_snacks,state):
def __init__(self,width, height,num_snacks,num_chasers,MAX_MINES,LEVELS,state):
self.width = width
self.height = height
self.num_snacks = num_snacks
self.num_chasers = num_chasers
self.MAX_MINES = MAX_MINES
self.LEVELS = LEVELS
self.state = state
def get_agents(self,num_snacks,state):
'''Sets up all the agents (snacks, mines, and man) in the game. First, determines mines needed by
def get_agents(self,num_snacks,num_chasers,state):
"""Sets up all the agents (snacks, mines, man, and chasers) in the game. First, determines mines needed by
calling the mine_counter function. Then generates a list of all posiitons, randomizes them, and assigns
the man to the first random position, the snacks to the next num_snack positions, and then the mines
to the required number of posiitons as specified in mine_counter. Returns these agents so they can be
positioned on the board.'''
the man to the first random position, the snacks to the next num_snack positions, the mines
to the required number of posiitons as specified in mine_counter, and then the chasers. Returns these
agents so they can be positioned on the board."""
num_mines = self.mine_counter(state)
all_positions = self.get_all_positions()
shuffle(all_positions)
man= [Man(all_positions[0])]
snacks = [Snack(p) for p in all_positions[1:(num_snacks+1)]]
mines = [Mine(p) for p in all_positions[(num_snacks + 2):(num_snacks + num_mines +2)]]
agents = man + snacks + mines + [self]
chaser = [Man_Chaser(p) for p in all_positions[(num_snacks + num_mines + 2):(num_snacks + num_mines + num_chasers + 2)]]
agents = man + snacks + mines + chaser + [self]
return agents
def play_turn(self,game):
'''Checks after every turn (turn is length of time specified in retro) if the game needs snacks
or mines.'''
#game.log(game.state['Score'])
"""Checks after every turn (turn is length of time specified in retro) if the game needs snacks
or mines or a chaser."""
game.log(game.state['Score'])
while self.game_needs_snacks(game):
self.add_snack(game)
while self.game_needs_mines(game):
self.add_mine(game)
while self.game_needs_chaser(game):
self.add_chaser(game)
def add_chaser(self,game):
"""Adds a chaser by finidng all the positions, randomizing them, and adding the chaser
to the first random position."""
all_positions=self.get_all_positions()
shuffle(all_positions)
index =0
while not game.is_empty(all_positions[index]):
index = index + 1
chaser = Man_Chaser(all_positions[index])
game.add_agent(chaser)
def game_needs_chaser(self,game):
"""Returns true when the number of chasers in the game is less than the number of chasers
specified in nav_game.py."""
return self.count_chaser(game) < self.num_chasers
def count_chaser(self,game):
"""Counts the number of chases currently in the game."""
chasers = [a for a in game.agents if a.chaser]
return len(chasers)
def mine_counter(self,state):
'''Determines how many mines should be in the game, based on the score of the game. Refers to the
"""Determines how many mines should be in the game, based on the score of the game. Refers to the
LEVELS list above which contains the score and a correspponding number of mines. This function
puts a certain number of mines down based on the score. Currently, does not reduce the number
of mines when the score lowers after hitting a mine. Returns the number of mines needed. '''
of mines when the score lowers after hitting a mine. Returns the number of mines needed."""
score = state['Score']
for limit, n in LEVELS:
for limit, n in self.LEVELS:
if score < limit:
return n
return MAX_MINES
return self.MAX_MINES
def add_mine(self,game):
'''Adds a mine by finding all positions, randomizing them, and then adding a mine to the
first non-occupied position.'''
"""Adds a mine by finding all positions, randomizing them, and then adding a mine to the
first non-occupied position."""
all_positions=self.get_all_positions()
shuffle(all_positions)
index =0
@ -79,18 +93,18 @@ class Board:
game.add_agent(mine)
def count_mines(self,game):
'''Counts the number of mines currently in the game.'''
"""Counts the number of mines currently in the game."""
mines= [a for a in game.agents if a.mine]
return len(mines)
def game_needs_mines(self,game):
'''Returns true when the number of mines in the game is less than the number of mines
required based on the score.'''
"""Returns true when the number of mines in the game is less than the number of mines
required based on the score."""
return self.count_mines(game) < self.mine_counter(game.state)
def add_snack(self,game):
'''Adds a snack by finding all positions, randomizing them, and then adding a snack to the
first non-occupied position.'''
"""Adds a snack by finding all positions, randomizing them, and then adding a snack to the
first non-occupied position."""
all_positions=self.get_all_positions()
shuffle(all_positions)
index =0
@ -100,17 +114,17 @@ class Board:
game.add_agent(snack)
def count_snacks(self,game):
'''Counts the number of snacks currently in the game.'''
"""Counts the number of snacks currently in the game."""
snacks= [a for a in game.agents if a.snack]
return len(snacks)
def game_needs_snacks(self,game):
'''Returns true when the number of snacks in the game is less than the number of snacks specified
in nav-game.py. In a future version, this number of snacks could vary as the game gets more difficult.'''
"""Returns true when the number of snacks in the game is less than the number of snacks specified
in nav-game.py. In a future version, this number of snacks could vary as the game gets more difficult."""
return self.count_snacks(game) < self.num_snacks
def get_all_positions(self):
'''Finds all coordinate positions in the game and adds them to a list of positions.'''
"""Finds all coordinate positions in the game and adds them to a list of positions."""
positions=[]
for i in range(self.width):
for j in range(self.height):

58
gamefiles/chaser.py Normal file
View File

@ -0,0 +1,58 @@
from helpers import add, distance, get_occupant
from random import random, choice
class Man_Chaser:
"""Represents a man_chaser, coming after the man. Modelled after the beast in the Beast game"""
character = "H"
color = "yellow"
probability_of_moving = 0.03
probability_of_random_move = 0.2
deadly = True
snack=False
mine = False
chaser = True
def __init__(self, position):
self.position = position
def play_turn(self, game):
if self.should_move():
possible_moves = []
for position in self.get_adjacent_positions():
if game.is_empty(position) and game.on_board(position):
possible_moves.append(position)
if possible_moves:
if self.should_move_randomly():
self.position = choice(possible_moves)
else:
self.position = self.choose_best_move(possible_moves, game)
#chaser catching the man score penalty is covered in man.py unlike the beast game
def get_adjacent_positions(self):
"""Returns a list of all adjacent positions, including diagonals
"""
positions = []
for i in [-1, 0, 1]:
for j in [-1, 0, 1]:
if i or j:
positions.append(add(self.position, (i, j)))
return positions
def should_move(self):
return random() < self.probability_of_moving
def should_move_randomly(self):
return random() < self.probability_of_random_move
def choose_best_move(self, possible_moves, game):
man = game.get_agent_by_name("man")
move_distances = [[distance(man.position, move), move] for move in possible_moves]
shortest_distance, best_move = sorted(move_distances)[0]
return best_move
def die(self, game):
"""Removes the chaser if the man runs into him, man death is described in man.py"""
game.remove_agent(self)

View File

@ -1,15 +1,13 @@
'''List of helper functions to move the man, none used in this game, but here in
case needed for future version'''
"""List of helper functions to move the man_chaser, adopted from Chris's beast game."""
'''def add(vec0, vec1):
def add(vec0, vec1):
"""Adds two vectors.
Got tired of doing this by hand.
"""
x0, y0 = vec0
x1, y1 = vec1
return (x0 + x1, y0 + y1)
'''
def get_occupant(game, position):
"""Returns the agent at position, if there is one.
@ -21,7 +19,7 @@ def get_occupant(game, position):
if position in positions_with_agents:
agents_at_position = positions_with_agents[position]
return agents_at_position[0]
'''
def distance(vec0, vec1):
"""Returns the distance between two vectors, using the
"manhattan distance," or the distance if you can only
@ -30,4 +28,4 @@ def distance(vec0, vec1):
"""
x0, y0 = vec0
x1, y1 = vec1
return abs(x1 - x0) + abs(y1 - y0)'''
return abs(x1 - x0) + abs(y1 - y0)

View File

@ -5,8 +5,8 @@ from snack import Snack
width=25
height=25
'''Establishes keyboard arrow keys as direction vectors so each arrow key corresponds with movement
in a certain direction on the board.'''
"""Establishes keyboard arrow keys as direction vectors so each arrow key corresponds with movement
in a certain direction on the board."""
direction_vectors = {
"KEY_RIGHT": (1, 0),
"KEY_UP": (0, -1),
@ -19,21 +19,24 @@ class Man:
color = "blue"
snack=False
mine=False
chaser=False
name= "man"
def __init__(self,position):
self.position = position
def handle_keystroke(self, keystroke, game):
'''Describes how a keystroke is received'''
"""Describes how a keystroke is received"""
if keystroke.name in direction_vectors:
vector = direction_vectors[keystroke.name]
self.try_to_move(vector, game)
def try_to_move(self, vector, game):
'''Checks if a space is avialable to move. Also adds 1 point if the space
"""Checks if a space is avialable to move. Also adds 1 point if the space
contains a snack and then removes the snack. Deducts 10 points if the
space contains a mine. Checks that the score is not negative and if it
is, ends the game. Also stores the score so it can be used to determine if more mines are needed'''
space contains a mine. Deducts 20 points if the space contains a chaser.
Checks that the score is not negative and if it is, ends the game. Also
stores the score so it can be used to determine if more mines are needed"""
x,y = self.position
vx,vy = vector
future_position = (x +vx, y+vy)
@ -49,6 +52,13 @@ class Man:
if obstacle.snack:
game.state['Score'] += 1
game.remove_agent(agents[0])
elif obstacle.chaser:
game.state['Score'] -= 20
game.remove_agent(agents[0])
if game.state['Score'] <0:
self.die(game)
else:
pass
else:
game.state['Score'] -= 10
game.remove_agent(agents[0])
@ -60,7 +70,7 @@ class Man:
self.position = future_position
def die(self,game):
'''Indicates what happens when the game is over.'''
"""Indicates what happens when the game is over."""
game.state["Message"] = "Game Over"
self.color = "black"
game.end()

View File

@ -1,11 +1,12 @@
'''Sets up the mine object.'''
"""Sets up the mine object."""
class Mine:
character = "*"
color = "green"
snack=False
mine=True
chaser=False
def __init__(self,position):

View File

@ -1,18 +1,35 @@
from retro.game import Game
from board import Board
'''This contains the main info for setting up and running the game.'''
"""This contains the main info for setting up and running the game."""
width = 25
height = 25
state= {"Score": 0}
num_snacks = 10
board = Board(width,height,num_snacks,state) #sets the original board
num_chasers = 5
"""Values to call later, sets no more than 100 mines, and each list represent a level [score, num_mines],
which is called in mine_counter in board.py."""
MAX_MINES = 100
LEVELS = [
[5, 10],
[10, 20],
[15, 30],
[20, 40],
[30,50],
[40,60],
[50,70],
[60,80],
[70,90]
]
board = Board(width,height,num_snacks,num_chasers,MAX_MINES,LEVELS,state) #sets the original board
game = Game(
board.get_agents(num_snacks,state),
board.get_agents(num_snacks,num_chasers,state),
state,
board_size = (width, height),debug=False
)
game.play()

View File

@ -1,10 +1,12 @@
'''Sets up the snack object'''
"""Sets up the snack object"""
class Snack:
character = "o"
color = "red"
snack=True
mine=False
chaser=False
def __init__(self,position):