diff --git a/gamefiles/__pycache__/board.cpython-312.pyc b/gamefiles/__pycache__/board.cpython-312.pyc index f730361..60c6fb0 100644 Binary files a/gamefiles/__pycache__/board.cpython-312.pyc and b/gamefiles/__pycache__/board.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/chaser.cpython-312.pyc b/gamefiles/__pycache__/chaser.cpython-312.pyc new file mode 100644 index 0000000..6293caa Binary files /dev/null and b/gamefiles/__pycache__/chaser.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/helpers.cpython-312.pyc b/gamefiles/__pycache__/helpers.cpython-312.pyc new file mode 100644 index 0000000..3e09687 Binary files /dev/null and b/gamefiles/__pycache__/helpers.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/man.cpython-312.pyc b/gamefiles/__pycache__/man.cpython-312.pyc index a325f5d..e796b41 100644 Binary files a/gamefiles/__pycache__/man.cpython-312.pyc and b/gamefiles/__pycache__/man.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/man_chaser.cpython-312.pyc b/gamefiles/__pycache__/man_chaser.cpython-312.pyc new file mode 100644 index 0000000..f006920 Binary files /dev/null and b/gamefiles/__pycache__/man_chaser.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/mine.cpython-312.pyc b/gamefiles/__pycache__/mine.cpython-312.pyc index a8247ff..96b044f 100644 Binary files a/gamefiles/__pycache__/mine.cpython-312.pyc and b/gamefiles/__pycache__/mine.cpython-312.pyc differ diff --git a/gamefiles/__pycache__/snack.cpython-312.pyc b/gamefiles/__pycache__/snack.cpython-312.pyc index 1064758..7500d29 100644 Binary files a/gamefiles/__pycache__/snack.cpython-312.pyc and b/gamefiles/__pycache__/snack.cpython-312.pyc differ diff --git a/gamefiles/board.py b/gamefiles/board.py index 6712f2e..0f379bb 100644 --- a/gamefiles/board.py +++ b/gamefiles/board.py @@ -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): diff --git a/gamefiles/chaser.py b/gamefiles/chaser.py new file mode 100644 index 0000000..12822fd --- /dev/null +++ b/gamefiles/chaser.py @@ -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) + \ No newline at end of file diff --git a/gamefiles/helpers.py b/gamefiles/helpers.py index 0e88a9e..c6e0acb 100644 --- a/gamefiles/helpers.py +++ b/gamefiles/helpers.py @@ -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)''' \ No newline at end of file + return abs(x1 - x0) + abs(y1 - y0) \ No newline at end of file diff --git a/gamefiles/man.py b/gamefiles/man.py index 5078fa4..e1eb632 100644 --- a/gamefiles/man.py +++ b/gamefiles/man.py @@ -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() diff --git a/gamefiles/mine.py b/gamefiles/mine.py index ca10ae3..3d82280 100644 --- a/gamefiles/mine.py +++ b/gamefiles/mine.py @@ -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): diff --git a/gamefiles/nav_game.py b/gamefiles/nav_game.py index 8e3ef85..10f511e 100644 --- a/gamefiles/nav_game.py +++ b/gamefiles/nav_game.py @@ -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() \ No newline at end of file diff --git a/gamefiles/snack.py b/gamefiles/snack.py index 629fa4e..7db5af5 100644 --- a/gamefiles/snack.py +++ b/gamefiles/snack.py @@ -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):