Skip to content

Commit

Permalink
tests for station & test fixes (#31)
Browse files Browse the repository at this point in the history
* tests for station & test fixes

+ test_station made
+ fixed take_action in station to default pass
+ added examples for stations and occupiable_stations
+ fixed interact_controller with new example stations
+ added example stations to enums
+ changed item class
  - default durability now: None
  - changed order of json methods

* tests for master_controller

+ fixed master_controller
+ fixed match case in player and object_type setter
+ fixed tile match case
+ fixed game_board
+ fixed issue with game_object
+ user_client: capitalized CLIENT, added avatar to take_turn
+ added avatar to take_turn in base_client and base_client_2
+ changed current_world to self.world in engine
+ made temp game_board in generate_game

* extra test fixes

* Update player.py

* comment for station_receiver_example
  • Loading branch information
MoodyMan04 authored May 6, 2023
1 parent 8ca5d9e commit 37a3662
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 60 deletions.
2 changes: 1 addition & 1 deletion base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def team_name(self):
return 'Team Name'

# This is where your AI will decide what to do
def take_turn(self, turn, actions, world):
def take_turn(self, turn, actions, world, avatar):
"""
This is where your AI will decide what to do.
:param turn: The current turn of the game.
Expand Down
2 changes: 1 addition & 1 deletion base_client_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def team_name(self):
return 'Team Name'

# This is where your AI will decide what to do
def take_turn(self, turn, actions, world):
def take_turn(self, turn, actions, world, avatar):
"""
This is where your AI will decide what to do.
:param turn: The current turn of the game.
Expand Down
4 changes: 2 additions & 2 deletions game/client/user_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class UserClient:
def __init__(self):
self.debug_level = DebugLevel.client
self.debug_level = DebugLevel.CLIENT
self.debug = True

def print(self, *args):
Expand All @@ -15,5 +15,5 @@ def print(self, *args):
def team_name(self):
return "No_Team_Name_Available"

def take_turn(self, turn, actions, world):
def take_turn(self, turn, actions, world, avatar):
raise NotImplementedError("Implement this in subclass")
6 changes: 3 additions & 3 deletions game/common/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,10 @@ def from_json(self, data: dict) -> Self:
self.held_item = None
return self

match held_item['object_type']:
match ObjectType(held_item['object_type']):
case ObjectType.ITEM:
self.held_item = Item().from_json(data['held_item'])
self.held_item = Item().from_json(held_item)
case _:
raise ValueError(f'{self.__class__.__name__}.held_item needs to be an item.')
raise ValueError(f'held_item needs to be an item, not {held_item}.')

return self
3 changes: 3 additions & 0 deletions game/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class ObjectType(Enum):
OCCUPIABLE = auto()
STATION = auto()
OCCUPIABLE_STATION = auto()
STATION_EXAMPLE = auto()
STATION_RECEIVER_EXAMPLE = auto()
OCCUPIABLE_STATION_EXAMPLE = auto()


class ActionType(Enum):
Expand Down
4 changes: 2 additions & 2 deletions game/common/game_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ def to_json(self) -> dict:
data = dict()

data['id'] = self.id
data['object_type'] = self.object_type
data['object_type'] = self.object_type.value

return data

def from_json(self, data: dict) -> Self:
# It is recommended call this using super() in child implementations
self.id = data['id']
self.object_type = data['object_type']
self.object_type = ObjectType(data['object_type'])
return self

def obfuscate(self):
Expand Down
10 changes: 5 additions & 5 deletions game/common/items/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Item(GameObject):
*** Refer to avatar.py for a more in-depth explanation of how picking up items work with examples. ***
"""

def __init__(self, value: int = 1, durability: int | None = 100, quantity: int = 1, stack_size: int = 1):
def __init__(self, value: int = 1, durability: int | None = None, quantity: int = 1, stack_size: int = 1):
super().__init__()
self.__quantity = None # This is here to prevent an error
self.__durability = None # This is here to prevent an error
Expand All @@ -87,7 +87,7 @@ def stack_size(self) -> int:
return self.__stack_size

@durability.setter
def durability(self, durability: int | None):
def durability(self, durability: int | None) -> None:
if durability is not None and not isinstance(durability, int):
raise ValueError(f'{self.__class__.__name__}.durability must be an int or None.')
if durability is not None and self.stack_size != 1:
Expand Down Expand Up @@ -145,16 +145,16 @@ def pick_up(self, item: Self) -> Self | None:

def to_json(self) -> dict:
data: dict = super().to_json()
data['stack_size'] = self.stack_size
data['durability'] = self.durability
data['value'] = self.value
data['quantity'] = self.quantity
data['stack_size'] = self.stack_size
return data

def from_json(self, data: dict) -> Self:
super().from_json(data)
self.durability: int | None = data['durability']
self.value: int = data['value']
self.quantity: int = data['quantity']
self.stack_size: int = data['stack_size']
self.quantity: int = data['quantity']
self.value: int = data['value']
return self
7 changes: 4 additions & 3 deletions game/common/map/game_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ def generate_event(self, start: int, end: int) -> None:
self.event_active = random.randint(start, end)

def __from_json_helper(self, data: dict) -> GameObject:
match data['object_type']:
temp:ObjectType = ObjectType(data['object_type'])
match temp:
case ObjectType.WALL:
return Wall().from_json(data)
case ObjectType.OCCUPIABLE_STATION:
Expand All @@ -287,6 +288,6 @@ def from_json(self, data: dict) -> Self:
zip(data["location_vectors"], data["location_objects"])} if data["location_vectors"] is not None else None
self.walled: bool = data["walled"]
self.event_active: int = data['event_active']
self.game_map: list[list[Tile]] = list(
list(map(lambda tile: Tile().from_json(tile), y)) for y in temp) if temp is not None else None
self.game_map: list[list[Tile]] = [
[Tile().from_json(tile) for tile in y] for y in temp] if temp is not None else None
return self
14 changes: 7 additions & 7 deletions game/common/map/tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ def __init__(self, occupied_by: GameObject = None):

def from_json(self, data: dict) -> Self:
super().from_json(data)
occupied_by: dict = data['occupied_by']
occupied_by: dict | None = data['occupied_by']
if occupied_by is None:
self.occupied_by = None
return self
# Add all possible game objects that can occupy a tile here (requires python 3.10)
match occupied_by['object_type']:
match ObjectType(occupied_by['object_type']):
case ObjectType.AVATAR:
self.occupied_by: Avatar = Avatar().from_json(data['occupied_by'])
self.occupied_by: Avatar = Avatar().from_json(occupied_by)
case ObjectType.OCCUPIABLE_STATION:
self.occupied_by: OccupiableStation = OccupiableStation().from_json(data['occupied_by'])
self.occupied_by: OccupiableStation = OccupiableStation().from_json(occupied_by)
case ObjectType.STATION:
self.occupied_by: Station = Station().from_json(data['occupied_by'])
self.occupied_by: Station = Station().from_json(occupied_by)
case ObjectType.WALL:
self.occupied_by: Wall = Wall().from_json(data['occupied_by'])
self.occupied_by: Wall = Wall().from_json(occupied_by)
case _:
raise Exception(f'Could not parse occupied_by: {self.occupied_by}')
raise Exception(f'Could not parse occupied_by: {occupied_by}')
return self
10 changes: 5 additions & 5 deletions game/common/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, code: object | None = None, team_name: str | None = None, act
self.team_name: str | None = team_name
self.code: object = code
# self.action: Action = action
self.actions: list[ActionType] | list = actions
self.actions: list[ActionType] = actions
self.avatar: Avatar | None = avatar

@property
Expand Down Expand Up @@ -69,7 +69,7 @@ def avatar(self, avatar: Avatar) -> None:

@property
def object_type(self) -> ObjectType:
return self.object_type
return self.__object_type

@object_type.setter
def object_type(self, object_type: ObjectType) -> None:
Expand All @@ -83,7 +83,7 @@ def to_json(self):
data['functional'] = self.functional
# data['error'] = self.error # .to_json() if self.error is not None else None
data['team_name'] = self.team_name
data['actions'] = self.actions
data['actions'] = [act.value for act in self.actions]
data['avatar'] = self.avatar.to_json() if self.avatar is not None else None

return data
Expand All @@ -95,7 +95,7 @@ def from_json(self, data):
# self.error = data['error'] # .from_json(data['action']) if data['action'] is not None else None
self.team_name = data['team_name']

self.actions: list[ActionType] | list = data['actions']
self.actions: list[ActionType] = [ObjectType(action) for action in data['actions']]
avatar: Avatar | None = data['avatar']
if avatar is None:
self.avatar = None
Expand All @@ -110,7 +110,7 @@ def from_json(self, data):
# raise Exception(f'Could not parse action: {self.action}')

# match case for avatar
match avatar['object_type']:
match ObjectType(avatar['object_type']):
case ObjectType.AVATAR:
self.avatar = Avatar().from_json(data['avatar'])
case None:
Expand Down
10 changes: 5 additions & 5 deletions game/common/stations/occupiable_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ def from_json(self, data: dict) -> Self:
self.occupied_by = None
return self
# Add all possible game objects that can occupy a tile here (requires python 3.10)
match occupied_by['object_type']:
match ObjectType(occupied_by['object_type']):
case ObjectType.AVATAR:
self.occupied_by: Avatar = Avatar().from_json(data['occupied_by'])
self.occupied_by: Avatar = Avatar().from_json(occupied_by)
case ObjectType.OCCUPIABLE_STATION:
self.occupied_by: OccupiableStation = OccupiableStation().from_json(data['occupied_by'])
self.occupied_by: OccupiableStation = OccupiableStation().from_json(occupied_by)
case ObjectType.STATION:
self.occupied_by: Station = Station().from_json(data['occupied_by'])
self.occupied_by: Station = Station().from_json(occupied_by)
case _:
raise Exception(f'Could not parse occupied_by: {self.occupied_by}')
raise Exception(f'Could not parse occupied_by: {occupied_by}')
return self
13 changes: 13 additions & 0 deletions game/common/stations/occupiable_station_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from game.common.avatar import Avatar
from game.common.enums import ObjectType
from game.common.items.item import Item
from game.common.stations.occupiable_station import OccupiableStation

# create example of occupiable_station that gives item
class OccupiableStationExample(OccupiableStation):
def __init__(self, held_item: Item | None = None):
super().__init__(held_item=held_item)
self.object_type = ObjectType.STATION_EXAMPLE

def take_action(self, avatar: Avatar) -> Item | None:
avatar.pick_up(self.held_item)
10 changes: 5 additions & 5 deletions game/common/stations/station.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def held_item(self, held_item: Item) -> None:
raise ValueError(f'{self.__class__.__name__}.held_item must be an Item or None, not {held_item}.')
self.__item = held_item

# take action method
# base of take action method, defined in classes that extend Station (StationExample demonstrates this)
def take_action(self, avatar: Avatar) -> Item | None:
return self.held_item
pass

# json methods
def to_json(self) -> dict:
Expand All @@ -47,9 +47,9 @@ def from_json(self, data: dict) -> Self:
return self

# framework match case for from json, can add more object types that can be item
match held_item['object_type']:
match ObjectType(held_item['object_type']):
case ObjectType.ITEM:
self.held_item = Item().from_json(data['held_item'])
self.held_item = Item().from_json(held_item)
case _:
raise Exception(f'Could not parse held_item: {self.held_item}')
raise Exception(f'Could not parse held_item: {held_item}')
return self
13 changes: 13 additions & 0 deletions game/common/stations/station_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from game.common.avatar import Avatar
from game.common.enums import ObjectType
from game.common.items.item import Item
from game.common.stations.station import Station

# create example of station that gives item to avatar
class StationExample(Station):
def __init__(self, held_item: Item | None = None):
super().__init__(held_item=held_item)
self.object_type = ObjectType.STATION_EXAMPLE

def take_action(self, avatar: Avatar) -> Item | None:
avatar.pick_up(self.held_item)
15 changes: 15 additions & 0 deletions game/common/stations/station_receiver_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from game.common.avatar import Avatar
from game.common.enums import ObjectType
from game.common.items.item import Item
from game.common.stations.station import Station

# create example of station that takes held_item from avatar at inventory slot 0
class StationReceiverExample(Station):
def __init__(self, held_item: Item | None = None):
super().__init__(held_item=held_item)
self.object_type = ObjectType.STATION_RECEIVER_EXAMPLE

def take_action(self, avatar: Avatar) -> None:
self.held_item = avatar.held_item
# assuming held_item is stored at index 0 in inventory
avatar.inventory.pop(0)
19 changes: 15 additions & 4 deletions game/controllers/master_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from game.controllers.interact_controller import InteractController
from game.common.map.game_board import GameBoard
from game.config import MAX_NUMBER_OF_ACTIONS_PER_TURN
from game.utils.vector import Vector


class MasterController(Controller):
Expand Down Expand Up @@ -65,8 +66,12 @@ def __init__(self):
# Receives all clients for the purpose of giving them the objects they will control
def give_clients_objects(self, clients: list[Player], world: dict):
# starting_positions = [[3, 3], [3, 9]] # would be done in generate game
for index, client in enumerate(clients):
client.avatar = Avatar(position=world[index])
gb: GameBoard = world['game_board']
avatars: list[Avatar] = gb.get_objects(ObjectType.AVATAR)
for avatar, client in zip(avatars,clients):
avatar.position = Vector(1,1)
client.avatar = avatar


# Generator function. Given a key:value pair where the key is the identifier for the current world and the value is
# the state of the world, returns the key that will give the appropriate world information
Expand Down Expand Up @@ -108,8 +113,14 @@ def client_turn_arguments(self, client: Player, turn):
def turn_logic(self, clients: list[Player], turn):
for client in clients:
for i in range(MAX_NUMBER_OF_ACTIONS_PER_TURN):
self.movement_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
self.interact_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
try:
self.movement_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
except IndexError:
pass
try:
self.interact_controller.handle_actions(client.actions[i], client, self.current_world_data["game_board"])
except IndexError:
pass
# checks event logic at the end of round
# self.handle_events(clients)

Expand Down
9 changes: 2 additions & 7 deletions game/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,11 @@ def pre_tick(self):
# Increment the tick
self.tick_number += 1

# Retrieve current world info
if self.current_world_key not in self.world:
raise KeyError('Given generated world key does not exist inside the world.')
current_world = self.world[self.current_world_key]

# Send current world information to master controller for purposes
if SET_NUMBER_OF_CLIENTS_START == 1:
self.master_controller.interpret_current_turn_data(self.clients[0], current_world, self.tick_number)
self.master_controller.interpret_current_turn_data(self.clients[0], self.world, self.tick_number)
else:
self.master_controller.interpret_current_turn_data(self.clients, current_world, self.tick_number)
self.master_controller.interpret_current_turn_data(self.clients, self.world, self.tick_number)

# Does actions like lets the player take their turn and asks master controller to perform game logic
def tick(self):
Expand Down
Loading

0 comments on commit 37a3662

Please sign in to comment.