generated from mwc/project_game
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:
parent
ba4a255ca1
commit
7e5420d8bb
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue