Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

94 improve exception strings #129

Merged
merged 10 commits into from
Jan 10, 2024
Merged
23 changes: 16 additions & 7 deletions game/common/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,26 +181,32 @@ def held_item(self, item: Item | None) -> None:

# If it's not an item, and it's not None, raise the error
if item is not None and not isinstance(item, Item):
raise ValueError(f'{self.__class__.__name__}.held_item must be an Item or None.')
raise ValueError(
f'{self.__class__.__name__}.held_item must be an Item or None. It is a(n) '
f'{item.__class__.__name__} and has the value of {item}')

# If the item is not contained in the inventory, the error will be raised.
if not self.inventory.__contains__(item):
raise ValueError(f'{self.__class__.__name__}.held_item must be set to an item that already exists'
f' in the inventory.')
f' in the inventory. It has the value of {item}')

# If the item is contained in the inventory, set the held_index to that item's index
self.__held_index = self.inventory.index(item)

@score.setter
def score(self, score: int) -> None:
if score is None or not isinstance(score, int):
raise ValueError(f'{self.__class__.__name__}.score must be an int.')
raise ValueError(
f'{self.__class__.__name__}.score must be an int. It is a(n) {score.__class__.__name__} and has the value of '
f'{score}')
self.__score: int = score

@position.setter
def position(self, position: Vector | None) -> None:
if position is not None and not isinstance(position, Vector):
raise ValueError(f'{self.__class__.__name__}.position must be a Vector or None.')
raise ValueError(
f'{self.__class__.__name__}.position must be a Vector or None. It is a(n) '
f'{position.__class__.__name__} and has the value of {position}')
self.__position: Vector | None = position

@inventory.setter
Expand All @@ -209,16 +215,19 @@ def inventory(self, inventory: list[Item | None]) -> None:
if inventory is None or not isinstance(inventory, list) \
or (len(inventory) > 0 and any(map(lambda item: item is not None and not
isinstance(item, Item), inventory))):
raise ValueError(f'{self.__class__.__name__}.inventory must be a list of Items.')
raise ValueError(
f'{self.__class__.__name__}.inventory must be a list of Items. It is a(n) {inventory.__class__.__name__} '
f'and has the value of {inventory}')
if len(inventory) > self.max_inventory_size:
raise ValueError(f'{self.__class__.__name__}.inventory size must be less than or equal to '
f'max_inventory_size')
f'max_inventory_size. It has the value of {len(inventory)}')
self.__inventory: list[Item] = inventory

@max_inventory_size.setter
def max_inventory_size(self, size: int) -> None:
if size is None or not isinstance(size, int):
raise ValueError(f'{self.__class__.__name__}.max_inventory_size must be an int.')
raise ValueError(f'{self.__class__.__name__}.max_inventory_size must be an int. It is a(n) {size.__class__.__name__} '
f'and has the value of {size}')
self.__max_inventory_size: int = size

# Private helper method that cleans the inventory of items that have a quantity of 0. This is a safety check
Expand Down
39 changes: 28 additions & 11 deletions game/common/items/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,46 +93,61 @@ def stack_size(self) -> int:
@durability.setter
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.')
raise ValueError(
f'{self.__class__.__name__}.durability must be an int. It is a(n) {durability.__class__.__name__} with the value of {durability}.')
if durability is not None and self.stack_size != 1:
raise ValueError(
f'{self.__class__.__name__}.durability must be set to None if stack_size is not equal to 1.')
f'{self.__class__.__name__}.durability must be set to None if stack_size is not equal to 1.'
f' {self.__class__.__name__}.durability has the value of {durability}.')
self.__durability = durability

@value.setter
def value(self, value: int) -> None:
if value is None or not isinstance(value, int):
raise ValueError(f'{self.__class__.__name__}.value must be an int.')
raise ValueError(
f'{self.__class__.__name__}.value must be an int.'
f' It is a(n) {value.__class__.__name__} with the value of {value}.')
self.__value: int = value

@quantity.setter
def quantity(self, quantity: int) -> None:
if quantity is None or not isinstance(quantity, int):
raise ValueError(f'{self.__class__.__name__}.quantity must be an int.')
raise ValueError(
f'{self.__class__.__name__}.quantity must be an int.'
f' It is a(n) {quantity.__class__.__name__} with the value of {quantity}.')
if quantity < 0:
raise ValueError(f'{self.__class__.__name__}.quantity must be greater than or equal to 0.')
raise ValueError(
f'{self.__class__.__name__}.quantity must be greater than or equal to 0.'
f' {self.__class__.__name__}.quantity has the value of {quantity}.')

# The self.quantity is set to the lower value between stack_size and the given quantity
# The remaining given quantity is returned if it's larger than self.quantity
if quantity > self.stack_size:
raise ValueError(f'{self.__class__.__name__}.quantity cannot be greater than '
f'{self.__class__.__name__}.stack_size')
f'{self.__class__.__name__}.stack_size.'
f' {self.__class__.__name__}.quantity has the value of {quantity}.')
self.__quantity: int = quantity

@stack_size.setter
def stack_size(self, stack_size: int) -> None:
if stack_size is None or not isinstance(stack_size, int):
raise ValueError(f'{self.__class__.__name__}.stack_size must be an int.')
raise ValueError(
f'{self.__class__.__name__}.stack_size must be an int.'
f' It is a(n) {stack_size.__class__.__name__} with the value of {stack_size}.')
if self.durability is not None and stack_size != 1:
raise ValueError(f'{self.__class__.__name__}.stack_size must be 1 if {self.__class__.__name__}.durability '
f'is not None.')
f'is not None. {self.__class__.__name__}.stack_size has the value of {stack_size}.')
if self.__quantity is not None and stack_size < self.__quantity:
raise ValueError(f'{self.__class__.__name__}.stack_size must be greater than or equal to the quantity.')
raise ValueError(
f'{self.__class__.__name__}.stack_size must be greater than or equal to the quantity.'
f' {self.__class__.__name__}.stack_size has the value of {stack_size}.')
self.__stack_size: int = stack_size

def take(self, item: Self) -> Self | None:
if item is not None and not isinstance(item, Item):
raise ValueError(f'{item.__class__.__name__} is not of type Item.')
raise ValueError(
f'{item.__class__.__name__}.item must be an item.'
f' It is a(n) {item.__class__.__name__} with the value of {item}.')

# If the item is None, just return None
if item is None:
Expand All @@ -156,7 +171,9 @@ def take(self, item: Self) -> Self | None:

def pick_up(self, item: Self) -> Self | None:
if item is not None and not isinstance(item, Item):
raise ValueError(f'{item.__class__.__name__} is not of type Item.')
raise ValueError(
f'{item.__class__.__name__}.item must be an item.'
f' It is a(n) {item.__class__.__name__} with the value of {item}.')

# If the item is None, just return None
if item is None:
Expand Down
43 changes: 27 additions & 16 deletions game/common/map/game_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ def seed(self, seed: int | None) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if seed is not None and not isinstance(seed, int):
raise ValueError(f'{self.__class__.__name__}.seed must be an integer or None.')
raise ValueError(
f'{self.__class__.__name__}.seed must be an int. '
f'It is a(n) {seed.__class__.__name__} with the value of {seed}.')
self.__seed = seed

@property
Expand All @@ -149,7 +151,9 @@ def game_map(self, game_map: list[list[Tile]]) -> None:
any(map(lambda l: not isinstance(l, list), game_map)) or
any([any(map(lambda g: not isinstance(g, Tile), tile_list))
for tile_list in game_map])):
raise ValueError(f'{self.__class__.__name__}.game_map must be a list[list[Tile]].')
raise ValueError(
f'{self.__class__.__name__}.game_map must be a list[list[Tile]]. '
f'It is a(n) {game_map.__class__.__name__} with the value of {game_map}.')
self.__game_map = game_map

@property
Expand All @@ -161,7 +165,9 @@ def map_size(self, map_size: Vector) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if map_size is None or not isinstance(map_size, Vector):
raise ValueError(f'{self.__class__.__name__}.map_size must be a Vector.')
raise ValueError(
f'{self.__class__.__name__}.map_size must be a Vector. '
f'It is a(n) {map_size.__class__.__name__} with the value of {map_size}.')
self.__map_size = map_size

@property
Expand All @@ -173,13 +179,10 @@ def locations(self, locations: dict[tuple[Vector]:list[GameObject]] | None) -> N
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if locations is not None and not isinstance(locations, dict):
raise ValueError("Locations must be a dict. The key must be a tuple of Vector Objects, and the "
"value a list of GameObject.")
# if locations is not None:
# for k, v in locations.items():
# if len(k) != len(v):
# raise ValueError("Cannot set the locations for the game_board. A key has a different "
# "length than its key.")
raise ValueError(
f'Locations must be a dict. The key must be a tuple of Vector Objects, '
f'and the value a list of GameObject. '
f'It is a(n) {locations.__class__.__name__} with the value of {locations}.')

self.__locations = locations

Expand All @@ -192,7 +195,9 @@ def walled(self, walled: bool) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if walled is None or not isinstance(walled, bool):
raise ValueError(f'{self.__class__.__name__}.walled must be a bool.')
raise ValueError(
f'{self.__class__.__name__}.walled must be a bool. '
f'It is a(n) {walled.__class__.__name__} with the value of {walled}.')

self.__walled = walled

Expand All @@ -213,7 +218,9 @@ def generate_map(self) -> None:
def __populate_map(self) -> None:
for k, v in self.locations.items():
if len(k) == 0 or len(v) == 0: # Key-Value lengths must be > 0 and equal
raise ValueError("A key-value pair from game_board.locations has a length of 0. ")
raise ValueError(
f'A key-value pair from game_board.locations has a length of 0. '
f'The length of the keys is {len(k)} and the length of the values is {len(v)}.')

# random.sample returns a randomized list which is used in __help_populate()
j = random.sample(k, k=len(k))
Expand Down Expand Up @@ -253,7 +260,9 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game
temp_tile = temp_tile.occupied_by

if temp_tile.occupied_by is not None:
raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.")
raise ValueError(
f'Last item on the given tile doesn\'t have the \'occupied_by\' attribute. '
f'It is a(n) {temp_tile.occupied_by.__class__.__name__} with the value of {temp_tile.occupied_by}.')

temp_tile.occupied_by = game_object

Expand All @@ -268,7 +277,9 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game

for game_object in remaining_objects:
if not hasattr(temp_tile, 'occupied_by') or temp_tile.occupied_by is not None:
raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.")
raise ValueError(
f'Last item on the given tile doesn\'t have the \'occupied_by\' attribute.'
f' It is a(n) {temp_tile.occupied_by.__class__.__name__} with the value of {temp_tile.occupied_by}.')
temp_tile.occupied_by = game_object
temp_tile = temp_tile.occupied_by

Expand Down Expand Up @@ -327,8 +338,8 @@ def __from_json_helper(self, data: dict) -> GameObject:
return Avatar().from_json(data)
# If adding more ObjectTypes that can be placed on the game_board, specify here
case _:
raise ValueError(f'The location (dict) must have a valid key (tuple of vectors) and a valid value ('
f'list of GameObjects).')
raise ValueError(
f'The object type of the object is not handled properly. The object type passed in is {temp}.')

def from_json(self, data: dict) -> Self:
super().from_json(data)
Expand Down
6 changes: 4 additions & 2 deletions game/common/map/occupiable.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ def occupied_by(self) -> GameObject | None:
@occupied_by.setter
def occupied_by(self, occupied_by: GameObject | None) -> None:
if occupied_by is not None and isinstance(occupied_by, Item):
raise ValueError(f'{self.__class__.__name__}.occupied_by cannot be an Item.')
raise ValueError(
f'{self.__class__.__name__}.occupied_by must be a GameObject. It is a(n) {occupied_by.__class__.__name__} with the value of {occupied_by}.')
if occupied_by is not None and not isinstance(occupied_by, GameObject):
raise ValueError(f'{self.__class__.__name__}.occupied_by must be None or an instance of GameObject.')
raise ValueError(
f'{self.__class__.__name__}.occupied_by must be None or an instance of GameObject. It is a(n) {occupied_by.__class__.__name__} with the value of {occupied_by}.')
self.__occupied_by = occupied_by

def to_json(self) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion game/common/map/tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ def from_json(self, data: dict) -> Self:
case ObjectType.WALL:
self.occupied_by: Wall = Wall().from_json(occupied_by)
case _:
raise Exception(f'Could not parse occupied_by: {occupied_by}')
raise Exception(f'The object type of the object is not handled properly. The object type passed in is {occupied_by}.')
return self
20 changes: 13 additions & 7 deletions game/common/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Player(GameObject):
items, etc.). For more details on the difference between the Player and Avatar classes, refer to the README
document.
"""

def __init__(self, code: object | None = None, team_name: str | None = None, actions: list[ActionType] = [],
avatar: Avatar | None = None):
super().__init__()
Expand All @@ -39,7 +40,8 @@ def actions(self, actions: list[ActionType] | list) -> None: # showing it retur
if actions is None or not isinstance(actions, list) \
or (len(actions) > 0
and any(map(lambda action_type: not isinstance(action_type, ActionType), actions))):
raise ValueError(f'{self.__class__.__name__}.action must be an empty list or a list of action types')
raise ValueError(
f'{self.__class__.__name__}.action must be an empty list or a list of action types. It is a(n) {actions.__class__.__name__} and has the value of {actions}.')
# ^if it's not either throw an error
self.__actions = actions

Expand All @@ -50,7 +52,8 @@ def functional(self) -> bool:
@functional.setter # do this for all the setters
def functional(self, functional: bool) -> None: # this enforces the type hinting
if functional is None or not isinstance(functional, bool): # if this statement is true throw an error
raise ValueError(f'{self.__class__.__name__}.functional must be a boolean')
raise ValueError(
f'{self.__class__.__name__}.functional must be a boolean. It is a(n) {functional.__class__.__name__} and has the value of {functional}.')
self.__functional = functional

@property
Expand All @@ -60,7 +63,8 @@ def team_name(self) -> str:
@team_name.setter
def team_name(self, team_name: str) -> None:
if team_name is not None and not isinstance(team_name, str):
raise ValueError(f'{self.__class__.__name__}.team_name must be a String or None')
raise ValueError(
f'{self.__class__.__name__}.team_name must be a String or None. It is a(n) {team_name.__class__.__name__} and has the value of {team_name}.')
self.__team_name = team_name

@property
Expand All @@ -70,7 +74,8 @@ def avatar(self) -> Avatar:
@avatar.setter
def avatar(self, avatar: Avatar) -> None:
if avatar is not None and not isinstance(avatar, Avatar):
raise ValueError(f'{self.__class__.__name__}.avatar must be Avatar or None')
raise ValueError(
f'{self.__class__.__name__}.avatar must be Avatar or None. It is a(n) {avatar.__class__.__name__} and has the value of {avatar}.')
self.__avatar = avatar

@property
Expand All @@ -80,7 +85,8 @@ def object_type(self) -> ObjectType:
@object_type.setter
def object_type(self, object_type: ObjectType) -> None:
if object_type is None or not isinstance(object_type, ObjectType):
raise ValueError(f'{self.__class__.__name__}.object_type must be ObjectType')
raise ValueError(
f'{self.__class__.__name__}.object_type must be ObjectType. It is a(n) {object_type.__class__.__name__} and has the value of {object_type}.')
self.__object_type = object_type

def to_json(self):
Expand Down Expand Up @@ -127,12 +133,12 @@ def from_json(self, data):
# self.action = Action().from_json(data['action']) if data['action'] is not None else None
# self.avatar = Avatar().from_json(data['avatar']) if data['avatar'] is not None else None

# to String
# to String
def __str__(self):
p = f"""ID: {self.id}
Team name: {self.team_name}
Actions:
"""
# This concatenates every action from the list of actions to the string
[p:= p + action for action in self.actions]
[p := p + action for action in self.actions]
return p
Loading