diff --git a/game/common/avatar.py b/game/common/avatar.py index 47a61fd..6469ce4 100644 --- a/game/common/avatar.py +++ b/game/common/avatar.py @@ -6,7 +6,7 @@ class Avatar(GameObject): - """ + ''' Notes for the inventory: The avatar's inventory is a list of items. Each item has a quantity and a stack_size (the max amount of an @@ -89,7 +89,7 @@ class Avatar(GameObject): picked_up_item is left where it was first found. Inventory after: [inventory_item (5/5), inventory_item (5/5) inventory_item (5/5) inventory_item (5/5), inventory_item (5/5)] - """ + ''' def __init__(self, item: Item | None = None, position: Vector | None = None, inventory: list[Item] = [], max_inventory_size: int = 10): @@ -125,34 +125,34 @@ def max_inventory_size(self) -> int: 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.') self.__held_item: Item = 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.') 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.') self.__position: Vector | None = position @inventory.setter def inventory(self, inventory: list[Item]) -> None: if inventory is None or not isinstance(inventory, list) \ or (len(inventory) > 0 and any(map(lambda item: 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.') if len(inventory) > self.max_inventory_size: - raise ValueError(f"{self.__class__.__name__}.inventory size must be less than max_inventory_size") + raise ValueError(f'{self.__class__.__name__}.inventory size must be less than max_inventory_size') 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.') self.__max_inventory_size: int = size def pick_up(self, item: Item) -> Item | None: @@ -169,7 +169,7 @@ def to_json(self) -> dict: data: dict = super().to_json() data['held_item'] = self.held_item.to_json() if self.held_item is not None else None data['score'] = self.score - data['position'] = self.position + data['position'] = self.position.to_json() if self.position is not None else None data['inventory'] = self.inventory data['max_inventory_size'] = self.max_inventory_size return data @@ -177,7 +177,7 @@ def to_json(self) -> dict: def from_json(self, data: dict) -> Self: super().from_json(data) self.score: int = data['score'] - self.position: Vector | None = data['position'] + self.position: Vector | None = None if data['position'] is None else Vector().from_json(data['position']) self.inventory: list[Item] = data['inventory'] self.max_inventory_size: int = data['max_inventory_size'] held_item: Item | None = data['held_item'] @@ -189,6 +189,6 @@ def from_json(self, data: dict) -> Self: case ObjectType.ITEM: self.held_item = Item().from_json(data['held_item']) case _: - raise ValueError(f"{self.__class__.__name__}.held_item needs to be an item.") + raise ValueError(f'{self.__class__.__name__}.held_item needs to be an item.') return self diff --git a/game/common/game_board.py b/game/common/game_board.py index 57721b4..d481f24 100644 --- a/game/common/game_board.py +++ b/game/common/game_board.py @@ -1,6 +1,8 @@ import random from typing import Self from game.utils.vector import Vector +from game.common.stations.occupiable_station import Occupiable_Station +from game.common.stations.station import Station from game.common.avatar import Avatar from game.common.game_object import GameObject from game.common.map.tile import Tile @@ -102,35 +104,51 @@ def __init__(self, seed: int | None = None, map_size: Vector = Vector(), locations: dict[tuple[Vector]:list[GameObject]] | None = None, walled: bool = False): super().__init__() - self.seed = seed + # game_map is initially going to be None. Since generation is slow, call generate_map() as needed + self.game_map: list[list[GameObject]] | None = None + self.seed: int | None = seed random.seed(seed) self.object_type: ObjectType = ObjectType.GAMEBOARD - self.event_active = None + self.event_active: int | None = None self.map_size: Vector = map_size - self.locations: dict = locations + # when passing Vectors as a tuple, end the tuple of Vectors with a comma so it is recognized as a tuple + self.locations: dict | None = locations self.walled: bool = walled - - # game_map is initially going to be None. Since generation is slow, call generate_map() as needed - self.game_map: list[list[GameObject]] | None = None - + @property def seed(self) -> int: return self.__seed @seed.setter - def seed(self, seed: int | None): - if seed is not None or not isinstance(seed, int): - raise ValueError("Seed must be an integer.") + 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.') self.__seed = seed + @property + def game_map(self) -> list[list[GameObject]] | None: + return self.__game_map + + @game_map.setter + def game_map(self, game_map: list[list[GameObject]]) -> None: + if game_map is not None and (not isinstance(game_map, list) or \ + any(map(lambda l: not isinstance(l, list), game_map)) or \ + any([any(map(lambda g: not isinstance(g, GameObject), l)) for l in game_map])): + raise ValueError(f'{self.__class__.__name__}.game_map must be a list[list[GameObject]].') + self.__game_map = game_map + @property def map_size(self) -> Vector: return self.__map_size @map_size.setter - def map_size(self, map_size: Vector): + 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("Map_size must be a Vector.") + raise ValueError(f'{self.__class__.__name__}.map_size must be a Vector.') self.__map_size = map_size @property @@ -138,14 +156,17 @@ def locations(self) -> dict: return self.__locations @locations.setter - def locations(self, locations: dict[tuple[Vector]:list[GameObject]] | None): - if locations is not None or not isinstance(locations, dict): + def locations(self, locations: dict[tuple[Vector]:list[GameObject]] | 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 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.") - for k,v in locations: - if len(k) != len(v): - raise ValueError("Cannot set the locations for the game_board. A key has a different " - "length than its key.") + 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.") self.__locations = locations @@ -154,13 +175,15 @@ def walled(self) -> bool: return self.__walled @walled.setter - def walled(self, walled: bool): + 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("Walled must be a bool.") + raise ValueError(f'{self.__class__.__name__}.walled must be a bool.') self.__walled = walled - def generate_map(self): + def generate_map(self) -> None: # generate map self.game_map = [[Tile() for _ in range(self.map_size.x)] for _ in range(self.map_size.y)] @@ -174,35 +197,33 @@ def generate_map(self): self.__populate_map() - def __populate_map(self): + def __populate_map(self) -> None: for k, v in self.locations.items(): if len(k) != len(v) or (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 mismatching lengths. " "They must be the same length, regardless of size.") - # random.choices returns a randomized list which is used in __help_populate() - j = random.choices(k, k=len(k)) + # random.sample returns a randomized list which is used in __help_populate() + j = random.sample(k, k=len(k)) self.__help_populate(j, v) - def __help_populate(self, vector_list: list[Vector], v: list[GameObject]): - for i in v: - temp_vector: Vector = vector_list.pop() - + def __help_populate(self, vector_list: list[Vector], v: list[GameObject]) -> None: + for j, i in zip(vector_list,v): if isinstance(i, Avatar): # If the GameObject is an Avatar, assign it the coordinate position - i.position = temp_vector + i.position = j - temp_tile: GameObject = self.game_map[temp_vector.y][temp_vector.x] + temp_tile: GameObject = self.game_map[j.y][j.x] while hasattr(temp_tile.occupied_by, 'occupied_by'): temp_tile = temp_tile.occupied_by - if temp_tile is not None: + if temp_tile is None: raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.") temp_tile.occupied_by = i def get_objects(self, look_for: ObjectType) -> list[GameObject]: - to_return = list() + to_return: list[GameObject] = list() for row in self.game_map: for object_in_row in row: @@ -211,7 +232,7 @@ def get_objects(self, look_for: ObjectType) -> list[GameObject]: return to_return - def __get_objects_help(look_for: ObjectType, temp: GameObject | Tile, to_return: list[GameObject]): + def __get_objects_help(self, look_for: ObjectType, temp: GameObject | Tile, to_return: list[GameObject]): while hasattr(temp, 'occupied_by'): if temp.object_type is look_for: to_return.append(temp) @@ -224,25 +245,40 @@ def __get_objects_help(look_for: ObjectType, temp: GameObject | Tile, to_return: def to_json(self) -> dict: data: dict[str, str] = super().to_json() - temp: list[list[GameObject]] = list((map(lambda tile: tile.to_json(), y)) for y in self.game_map) + temp: list[list[GameObject]] = list(list(map(lambda tile: tile.to_json(), y)) for y in self.game_map) if self.game_map is not None else None data["game_map"] = temp data["seed"] = self.seed - data["map_size"] = self.map_size - data["locations"] = self.locations + data["map_size"] = self.map_size.to_json() + data["location_vectors"] = [[vec.to_json() for vec in k] for k in self.locations.keys()] if self.locations is not None else None + data["location_objects"] = [[obj.to_json() for obj in v] for v in self.locations.values()] if self.locations is not None else None data["walled"] = self.walled data['event_active'] = self.event_active return data - def generate_event(self, start, end): + def generate_event(self, start: int, end: int) -> None: self.event_active = random.randint(start, end) - def from_json(self, data) -> Self: + def __from_json_helper(self, data: dict) -> GameObject: + match data['object_type']: + case ObjectType.WALL: + return Wall().from_json(data) + case ObjectType.OCCUPIABLE_STATION: + return Occupiable_Station().from_json(data) + case ObjectType.STATION: + return Station().from_json(data) + case ObjectType.AVATAR: + 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 (list of GameObjects).') + + def from_json(self, data: dict) -> Self: super().from_json(data) temp = data["game_map"] - self.game_map: list[list[GameObject]] = list((map(lambda tile: Tile().from_json(tile), y)) for y in temp) self.seed: int | None = data["seed"] - self.map_size: Vector = data["map_size"] - self.locations: dict[tuple[Vector]:list[GameObject]] = data["locations"] + self.map_size: Vector = Vector().from_json(data["map_size"]) + self.locations: dict[tuple[Vector]:list[GameObject]] = {tuple(map(lambda vec: Vector().from_json(vec), k)) : [self.__from_json_helper(obj) for obj in v] for k, v in zip(data["location_vectors"], data["location_objects"])} if data["location_vectors"] is not None else None self.walled: bool = data["walled"] - self.event_active = data['event_active'] + self.event_active: int = data['event_active'] + self.game_map: list[list[GameObject]] = list(list(map(lambda tile: Tile().from_json(tile), y)) for y in temp) if temp is not None else None return self diff --git a/game/common/game_object.py b/game/common/game_object.py index 2ca5ef7..8c268a8 100644 --- a/game/common/game_object.py +++ b/game/common/game_object.py @@ -1,6 +1,7 @@ import uuid from game.common.enums import ObjectType +from typing import Self class GameObject: @@ -8,7 +9,7 @@ def __init__(self, **kwargs): self.id = str(uuid.uuid4()) self.object_type = ObjectType.NONE - def to_json(self): + def to_json(self) -> dict: # It is recommended call this using super() in child implementations data = dict() @@ -17,10 +18,11 @@ def to_json(self): return data - def from_json(self, 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'] + return self def obfuscate(self): pass diff --git a/game/common/items/item.py b/game/common/items/item.py index c600c21..98a292d 100644 --- a/game/common/items/item.py +++ b/game/common/items/item.py @@ -7,6 +7,7 @@ class Item(GameObject): def __init__(self, value: int = 1, durability: int | None = 100, quantity: int = 1, stack_size: int = 1): super().__init__() self.__quantity = None # This is here to prevent an error + self.__durability = None self.object_type: ObjectType = ObjectType.ITEM self.value: int = value # Value can more specified based on purpose (e.g., the sell price) self.stack_size: int = stack_size # the max quantity this item can contain @@ -31,8 +32,10 @@ def stack_size(self) -> int: @durability.setter def durability(self, durability: int | None): - if durability is not None and not isinstance(durability, int) or self.stack_size > 1: - raise ValueError(f'{self.__class__.__name__}.durability must be an int or None, and stack_size must be 1.') + 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: + raise ValueError(f'{self.__class__.__name__}.durability must be set to None if stack_size is not equal to 1.') self.__durability = durability @value.setter @@ -45,7 +48,7 @@ def value(self, value: int) -> None: 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.') - if quantity < 0: + if quantity <= 0: raise ValueError(f'{self.__class__.__name__}.quantity must be greater than 0.') # The self.quantity is set to the lower value between stack_size and the given quantity @@ -59,11 +62,17 @@ def quantity(self, quantity: int) -> None: 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.') + 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.') 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.') self.__stack_size: int = stack_size def pick_up(self, item: Self) -> Self | None: + if item is None or not isinstance(item, Item): + raise ValueError(f'{item.__class__.__name__} is not of type Item.') + # If the items don't match, return the given item without modifications if self.object_type != item.object_type: return item diff --git a/game/common/map/wall.py b/game/common/map/wall.py index dff908d..8ef252f 100644 --- a/game/common/map/wall.py +++ b/game/common/map/wall.py @@ -6,4 +6,4 @@ class Wall(GameObject): def __init__(self): super().__init__() self.object_type = ObjectType.WALL - \ No newline at end of file + \ No newline at end of file diff --git a/game/common/stations/station.py b/game/common/stations/station.py index 9f452c1..ce17d5b 100644 --- a/game/common/stations/station.py +++ b/game/common/stations/station.py @@ -19,7 +19,7 @@ def item(self) -> Item|None: @item.setter def item(self, item: Item) -> None: if item is not None and not isinstance(item, Item): - raise ValueError(f"{self.__class__.__name__}.item must be an Item or None, not {item}.") + raise ValueError(f'{self.__class__.__name__}.item must be an Item or None, not {item}.') self.__item = item # take action method diff --git a/game/config.py b/game/config.py index aa3a065..c9af04f 100644 --- a/game/config.py +++ b/game/config.py @@ -39,6 +39,6 @@ class Debug: # Keeps track of the current debug level of the game - level = DebugLevel.none + level = DebugLevel.NONE # Other Settings Here -------------------------------------------------------------------------------------------------- diff --git a/game/test_suite/tests/test_avatar.py b/game/test_suite/tests/test_avatar.py index 081a747..8bdef35 100644 --- a/game/test_suite/tests/test_avatar.py +++ b/game/test_suite/tests/test_avatar.py @@ -4,46 +4,74 @@ from game.common.items.item import Item from game.utils.vector import Vector + # class to test the avatar class and its methods class TestAvatar(unittest.TestCase): def setUp(self) -> None: - self.item: Item = Item(10, 100) - self.avatar: Avatar = Avatar(None, None) - + self.avatar: Avatar = Avatar(None, None, [], 1) + self.item: Item = Item(10, 100, 1, 1) + # test set item - def testAvatarSetItem(self): + def test_avatar_set_item(self): self.avatar.held_item = self.item self.assertEqual(self.avatar.held_item, self.item) - def testAvatarSetItemFail(self): + def test_avatar_set_item_fail(self): with self.assertRaises(ValueError) as e: self.avatar.held_item = 3 self.assertEqual(str(e.exception), 'Avatar.held_item must be an Item or None.') # test set score - def testAvatarSetScore(self): + def test_avatar_set_score(self): self.avatar.score = 10 self.assertEqual(self.avatar.score, 10) - def testAvatarSetScoreFail(self): + def test_avatar_set_score_fail(self): with self.assertRaises(ValueError) as e: self.avatar.score = 'wow' self.assertEqual(str(e.exception), 'Avatar.score must be an int.') # test set position - def testAvatarSetPosition(self): - self.avatar.position = Vector(10,10) - self.assertEqual(str(self.avatar.position), str(Vector(10,10))) + def test_avatar_set_position(self): + self.avatar.position = Vector(10, 10) + self.assertEqual(str(self.avatar.position), str(Vector(10, 10))) - def testAvatarSetPositionFail(self): + def test_avatar_set_position_fail(self): with self.assertRaises(ValueError) as e: self.avatar.position = 10 self.assertEqual(str(e.exception), 'Avatar.position must be a Vector or None.') - # test json method + # test set inventory + def test_avatar_set_inventory(self): + self.avatar.inventory = [Item(1, 1)] + self.assertEqual(self.avatar.inventory[0].value, Item(1, 1).value) + + # fails if inventory is not a list + def test_avatar_set_inventory_fail_1(self): + with self.assertRaises(ValueError) as e: + self.avatar.inventory = 'Fail' + self.assertEqual(str(e.exception), 'Avatar.inventory must be a list of Items.') + + # fails if inventory size is less than the max_inventory_size + def test_avatar_set_inventory_fail_2(self): + with self.assertRaises(ValueError) as e: + self.avatar.inventory = [Item(1, 1), Item(4, 2)] + self.assertEqual(str(e.exception), 'Avatar.inventory size must be less than max_inventory_size') + + def test_avatar_set_max_inventory_size(self): + self.avatar.max_inventory_size = 10 + self.assertEqual(str(self.avatar.max_inventory_size), str(10)) + + def test_avatar_set_max_inventory_size_fail(self): + with self.assertRaises(ValueError) as e: + self.avatar.max_inventory_size = 'Fail' + self.assertEqual(str(e.exception), 'Avatar.max_inventory_size must be an int.') + + # test json method + def testAvatarJson(self): self.avatar.held_item = self.item - self.avatar.position = Vector(10,10) + self.avatar.position = Vector(10, 10) data: dict = self.avatar.to_json() avatar: Avatar = Avatar().from_json(data) self.assertEqual(self.avatar.object_type, avatar.object_type) diff --git a/game/test_suite/tests/test_game_board.py b/game/test_suite/tests/test_game_board.py new file mode 100644 index 0000000..1034ab3 --- /dev/null +++ b/game/test_suite/tests/test_game_board.py @@ -0,0 +1,90 @@ +import unittest + +from game.common.enums import ObjectType +from game.common.avatar import Avatar +from game.common.items.item import Item +from game.common.stations.station import Station +from game.common.stations.occupiable_station import Occupiable_Station +from game.common.map.tile import Tile +from game.common.map.wall import Wall +from game.utils.vector import Vector +from game.common.game_object import GameObject +from game.common.game_board import GameBoard + +# class to test initalization of game_board +class TestGameBoard(unittest.TestCase): + def setUp(self) -> None: + self.item: Item = Item(10, None) + self.wall: Wall = Wall() + self.avatar: Avatar = Avatar(None, Vector(5, 5)) + self.locations: dict[tuple[Vector]:list[GameObject]] = { + (Vector(1, 1),):[Station(None)], + (Vector(1, 2), Vector(1, 3)):[Occupiable_Station(self.item), Station(None)], + (Vector(5, 5),):[self.avatar], + (Vector(5, 6),):[self.wall] + } + self.game_board: GameBoard = GameBoard(1, Vector(10, 10), self.locations, False) + self.game_board.generate_map() + + # test that seed cannot be set after generate_map + def test_seed_fail(self): + with self.assertRaises(RuntimeError) as e: + self.game_board.seed = 20 + self.assertEqual(str(e.exception), 'GameBoard variables cannot be changed once generate_map is run.') + + # test that map_size cannot be set after generate_map + def test_map_size_fail(self): + with self.assertRaises(RuntimeError) as e: + self.game_board.map_size = Vector(1, 1) + self.assertEqual(str(e.exception), 'GameBoard variables cannot be changed once generate_map is run.') + + # test that locations cannot be set after generate_map + def test_locations_fail(self): + with self.assertRaises(RuntimeError) as e: + self.game_board.locations = self.locations + self.assertEqual(str(e.exception), 'GameBoard variables cannot be changed once generate_map is run.') + + # test that locations raises RuntimeError even with incorrect data type + def test_locations_incorrect_fail(self): + with self.assertRaises(RuntimeError) as e: + self.game_board.locations = Vector(1, 1) + self.assertEqual(str(e.exception), 'GameBoard variables cannot be changed once generate_map is run.') + + # test that walled cannot be set after generate_map + def test_walled_fail(self): + with self.assertRaises(RuntimeError) as e: + self.game_board.walled = False + self.assertEqual(str(e.exception), 'GameBoard variables cannot be changed once generate_map is run.') + + # test that get_objects works correctly with stations + def test_get_objects_station(self): + stations: list[Station] = self.game_board.get_objects(ObjectType.STATION) + self.assertTrue(all(map(lambda station: isinstance(station, Station), stations))) + self.assertEqual(len(stations), 2) + + # test that get_objects works correctly with occupiable stations + def test_get_objects_occupiable_station(self): + occupiable_stations: list[Occupiable_Station] = self.game_board.get_objects(ObjectType.OCCUPIABLE_STATION) + self.assertTrue(all(map(lambda occupiable_station: isinstance(occupiable_station, Occupiable_Station), occupiable_stations))) + self.assertEqual(len(occupiable_stations), 1) + + # test that get_objects works correctly with avatar + def testget_objects_avatar(self): + avatars: list[Avatar] = self.game_board.get_objects(ObjectType.AVATAR) + self.assertTrue(all(map(lambda avatar: isinstance(avatar, Avatar), avatars))) + self.assertEqual(len(avatars), 1) + + # test that get_objects works correctly with walls + def test_get_objects_wall(self): + walls: list[Wall] = self.game_board.get_objects(ObjectType.WALL) + self.assertTrue(all(map(lambda wall: isinstance(wall, Wall), walls))) + self.assertEqual(len(walls), 1) + + # test json method + def test_game_board_json(self): + data: dict = self.game_board.to_json() + temp: GameBoard = GameBoard().from_json(data) + for (k, v), (x, y) in zip(self.locations.items(), temp.locations.items()): + for (i, j), (a, b) in zip(zip(k, v), zip(x, y)): + self.assertEqual(i.object_type, a.object_type) + self.assertEqual(j.object_type, b.object_type) \ No newline at end of file diff --git a/game/test_suite/tests/test_game_board_no_gen.py b/game/test_suite/tests/test_game_board_no_gen.py new file mode 100644 index 0000000..a836ba8 --- /dev/null +++ b/game/test_suite/tests/test_game_board_no_gen.py @@ -0,0 +1,100 @@ +import unittest + +from game.common.enums import ObjectType +from game.common.avatar import Avatar +from game.common.items.item import Item +from game.common.stations.station import Station +from game.common.stations.occupiable_station import Occupiable_Station +from game.common.map.tile import Tile +from game.common.map.wall import Wall +from game.utils.vector import Vector +from game.common.game_object import GameObject +from game.common.game_board import GameBoard + +# class to test initalization of game_board +class TestGameBoard(unittest.TestCase): + def setUp(self) -> None: + self.item: Item = Item(10, None) + self.wall: Wall = Wall() + self.avatar: Avatar = Avatar(None, Vector(5, 5)) + self.locations: dict[tuple[Vector]:list[GameObject]] = { + (Vector(1, 1),):[Station(None)], + (Vector(1, 2), Vector(1, 3)):[Occupiable_Station(self.item), Station(None)], + (Vector(5, 5),):[self.avatar], + (Vector(5, 6),):[self.wall] + } + self.game_board: GameBoard = GameBoard(1, Vector(10, 10), self.locations, False) + + # test seed + def testSeed(self): + self.game_board.seed = 2 + self.assertEqual(self.game_board.seed, 2) + + def testSeedFail(self): + with self.assertRaises(ValueError) as e: + self.game_board.seed = "False" + self.assertEqual(str(e.exception), 'GameBoard.seed must be an integer.') + + # test map_size + def testMap_size(self): + self.game_board.map_size = Vector(12, 12) + self.assertEqual(str(self.game_board.map_size), str(Vector(12, 12))) + + def testMap_sizeFail(self): + with self.assertRaises(ValueError) as e: + self.game_board.map_size = "wow" + self.assertEqual(str(e.exception), 'GameBoard.map_size must be a Vector.') + + # test locations + def testLocations(self): + self.locations = { + (Vector(1, 1),):[self.avatar], + (Vector(1, 2), Vector(1, 3)):[Occupiable_Station(self.item), Station(None)], + (Vector(5, 5),):[Station(None)], + (Vector(5, 6),):[self.wall] + } + self.game_board.locations = self.locations + self.assertEqual(str(self.game_board.locations), str(self.locations)) + + def testLocationsFailType(self): + with self.assertRaises(ValueError) as e: + self.game_board.locations = "wow" + self.assertEqual(str(e.exception), 'Locations must be a dict. The key must be a tuple of Vector Objects, and the value a list of GameObject.') + + def testLocationsFailLen(self): + with self.assertRaises(ValueError) as e: + self.locations = { + (Vector(1, 1),):[], + (Vector(1, 2), Vector(1, 3)):[Occupiable_Station(self.item), Station(None)], + (Vector(5, 5),):[Station(None)], + (Vector(5, 6),):[self.wall] + } + self.game_board.locations = self.locations + self.assertEqual(str(e.exception), 'Cannot set the locations for the game_board. A key has a different length than its key.') + + # test walled + def testWalled(self): + self.game_board.walled = True + self.assertEqual(self.game_board.walled, True) + + def testWalledFail(self): + with self.assertRaises(ValueError) as e: + self.game_board.walled = "wow" + self.assertEqual(str(e.exception), 'GameBoard.walled must be a bool.') + + # test json method + def test_game_board_json(self): + data: dict = self.game_board.to_json() + temp: GameBoard = GameBoard().from_json(data) + for (k, v), (x, y) in zip(self.locations.items(), temp.locations.items()): + for (i, j), (a, b) in zip(zip(k, v), zip(x, y)): + self.assertEqual(i.object_type, a.object_type) + self.assertEqual(j.object_type, b.object_type) + + def testGenerate_map(self): + self.game_board.generate_map() + self.assertEqual(self.game_board.game_map[1][1].occupied_by.object_type, ObjectType.STATION) + self.assertEqual(self.game_board.game_map[2][1].occupied_by.object_type, ObjectType.OCCUPIABLE_STATION) + self.assertEqual(self.game_board.game_map[3][1].occupied_by.object_type, ObjectType.STATION) + self.assertEqual(self.game_board.game_map[5][5].occupied_by.object_type, ObjectType.AVATAR) + self.assertEqual(self.game_board.game_map[6][5].occupied_by.object_type, ObjectType.WALL) \ No newline at end of file diff --git a/game/test_suite/tests/test_item.py b/game/test_suite/tests/test_item.py new file mode 100644 index 0000000..54cbac5 --- /dev/null +++ b/game/test_suite/tests/test_item.py @@ -0,0 +1,113 @@ +import unittest + +from game.common.avatar import Avatar +from game.common.items.item import Item +from game.common.enums import ObjectType + +# class to test the item class and its methods +class TestItem(unittest.TestCase): + def setUp(self) -> None: + self.avatar: Avatar = Avatar(None, None, [], 1) + self.item: Item = Item() + + # test set durability + def test_set_durability(self): + self.item.durability = 10 + self.assertEqual(self.item.durability, 10) + + def test_set_durability_none(self): + self.item.durability = None + self.assertEqual(self.item.durability, None) + + def test_set_durability_fail(self): + with self.assertRaises(ValueError) as e: + self.item.durability = 'fail' + self.assertEqual(str(e.exception), 'Item.durability must be an int or None.') + + def test_set_durability_stack_size_fail(self): + with self.assertRaises(ValueError) as e: + self.item = Item(10, None, 10, 10) + self.item.durability = 19 + self.assertEqual(str(e.exception), 'Item.durability must be set to None if stack_size is not equal to 1.') + + # test set value + def test_set_value(self): + self.item.value = 10 + self.assertEqual(self.item.value, 10) + + def test_set_value_fail(self): + with self.assertRaises(ValueError) as e: + self.item.value = 'fail' + self.assertEqual(str(e.exception), 'Item.value must be an int.') + + # test set quantity + def test_set_quantity(self): + self.item = Item(10, None, 10, 10) + self.item.quantity = 5 + self.assertEqual(self.item.quantity, 5) + + def test_set_quantity_fail(self): + with self.assertRaises(ValueError) as e: + self.item.quantity = 'fail' + self.assertEqual(str(e.exception), 'Item.quantity must be an int.') + + def test_set_quantity_fail_greater_than_0(self): + with self.assertRaises(ValueError) as e: + self.item.quantity = 0 + self.assertEqual(str(e.exception), 'Item.quantity must be greater than 0.') + + def test_set_quantity_fail_stack_size(self): + with self.assertRaises(ValueError) as e: + self.item.quantity = 10 + self.item.stack_size = 1 + self.assertEqual(str(e.exception), 'Item.quantity cannot be greater than Item.stack_size') + + def test_stack_size(self): + self.item = Item(10, None, 10, 10) + self.assertEqual(self.item.quantity, 10) + + def test_stack_size_fail(self): + with self.assertRaises(ValueError) as e: + self.item.stack_size = 'fail' + self.assertEqual(str(e.exception), 'Item.stack_size must be an int.') + + def test_stack_size_fail_quantity(self): + # value, durability, quantity, stack size + with self.assertRaises(ValueError) as e: + item: Item = Item(10, None, 10, 10) + item.stack_size = 5 + self.assertEqual(str(e.exception), 'Item.stack_size must be greater than or equal to the quantity.') + + def test_pick_up(self): + # value, durability, quantity, stack size + item: Item = Item(10, None, 2, 10) + self.item = Item(10, None, 1, 10) + self.item.pick_up(item) + self.assertEqual(self.item.quantity, 3) + + def test_pick_up_fail(self): + with self.assertRaises(ValueError) as e: + self.item.pick_up(self.item.pick_up(None)) + self.assertEqual(str(e.exception), 'NoneType is not of type Item.') + + def test_pick_up_wrong_object_type(self): + item: Item = Item(10, 10, 1, 1) + item.object_type = ObjectType.PLAYER + self.item = Item(10, 10, 1, 1) + self.item = self.item.pick_up(item) + self.assertEqual(self.item.object_type, item.object_type) + + def test_pick_up_surplus(self): + item: Item = Item(10, None, 10, 10) + self.item = Item(10, None, 9, 10) + surplus: Item = self.item.pick_up(item) + self.assertEqual(surplus.quantity, 9) + + def test_item_json(self): + data: dict = self.item.to_json() + item: Item = Item().from_json(data) + self.assertEqual(self.item.object_type, item.object_type) + self.assertEqual(self.item.value, item.value) + self.assertEqual(self.item.stack_size, item.stack_size) + self.assertEqual(self.item.durability, item.durability) + self.assertEqual(self.item.quantity, item.quantity) \ No newline at end of file