diff --git a/game/common/map/game_board.py b/game/common/map/game_board.py index 3e55b95..d47d8fb 100644 --- a/game/common/map/game_board.py +++ b/game/common/map/game_board.py @@ -1,5 +1,5 @@ import random -from typing import Self +from typing import Self, Callable from game.common.avatar import Avatar from game.common.enums import * @@ -175,11 +175,11 @@ def locations(self, locations: dict[tuple[Vector]:list[GameObject]] | None) -> N 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.") + # 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 @@ -212,20 +212,42 @@ def generate_map(self) -> None: 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.") + 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. ") # 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]) -> 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 = j + def __occupied_filter(self, game_object_list: list[GameObject]) -> list[GameObject]: + """ + A helper method that returns a list of game objects that have the 'occupied_by' attribute. + :param game_object_list: + :return: a list of game object + """ + return [game_object for game_object in game_object_list if hasattr(game_object, 'occupied_by')] - temp_tile: GameObject = self.game_map[j.y][j.x] + def __help_populate(self, vector_list: list[Vector], game_object_list: list[GameObject]) -> None: + """ + A helper method that helps populate the game map. + :param vector_list: + :param game_object_list: + :return: None + """ + + zipped_list: [tuple[list[Vector], list[GameObject]]] = list(zip(vector_list, game_object_list)) + last_vec: Vector = zipped_list[-1][0] + + remaining_objects: list[GameObject] | None = self.__occupied_filter(game_object_list[len(zipped_list):]) \ + if len(self.__occupied_filter(game_object_list)) > len(zipped_list) \ + else None + + # Will cap at smallest list when zipping two together + for vector, game_object in zipped_list: + if isinstance(game_object, Avatar): # If the GameObject is an Avatar, assign it the coordinate position + game_object.position = vector + + temp_tile: GameObject = self.game_map[vector.y][vector.x] while hasattr(temp_tile.occupied_by, 'occupied_by'): temp_tile = temp_tile.occupied_by @@ -233,7 +255,23 @@ def __help_populate(self, vector_list: list[Vector], v: list[GameObject]) -> Non 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 + temp_tile.occupied_by = game_object + + if remaining_objects is None: + return + + # stack remaining game_objects on last vector + temp_tile: GameObject = self.game_map[last_vec.y][last_vec.x] + + while hasattr(temp_tile.occupied_by, 'occupied_by'): + temp_tile = temp_tile.occupied_by + + for game_object in remaining_objects: + if temp_tile is None: + raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.") + + temp_tile.occupied_by = game_object + temp_tile = temp_tile.occupied_by def get_objects(self, look_for: ObjectType) -> list[tuple[Vector, list[GameObject]]]: to_return: list[tuple[Vector, list[GameObject]]] = list() diff --git a/game/test_suite/tests/test_game_board.py b/game/test_suite/tests/test_game_board.py index 28ce0f5..e496b6a 100644 --- a/game/test_suite/tests/test_game_board.py +++ b/game/test_suite/tests/test_game_board.py @@ -29,6 +29,8 @@ def setUp(self) -> None: self.locations: dict[tuple[Vector]:list[GameObject]] = { (Vector(1, 1),): [Station(None)], (Vector(1, 2), Vector(1, 3)): [OccupiableStation(self.item), Station(None)], + (Vector(2, 2), Vector(2, 3)): [OccupiableStation(self.item), OccupiableStation(self.item), OccupiableStation(self.item), OccupiableStation(self.item)], + (Vector(3, 1), Vector(3, 2), Vector(3, 3)): [OccupiableStation(self.item), Station(None)], (Vector(5, 5),): [self.avatar], (Vector(5, 6),): [self.wall] } @@ -69,14 +71,23 @@ def test_walled_fail(self): def test_get_objects_station(self): stations: list[tuple[Vector, list[Station]]] = self.game_board.get_objects(ObjectType.STATION) self.assertTrue(all(map(lambda station: isinstance(station[1][0], Station), stations))) - self.assertEqual(len(stations), 2) + self.assertEqual(len(stations), 3) # test that get_objects works correctly with occupiable stations def test_get_objects_occupiable_station(self): occupiable_stations: list[tuple[Vector, list[OccupiableStation]]] = self.game_board.get_objects(ObjectType.OCCUPIABLE_STATION) self.assertTrue( all(map(lambda occupiable_station: isinstance(occupiable_station[1][0], OccupiableStation), occupiable_stations))) - self.assertEqual(len(occupiable_stations), 1) + objects_stacked = [x[1] for x in occupiable_stations] + objects_unstacked = [x for xs in objects_stacked for x in xs] + self.assertEqual(len(objects_unstacked), 6) + + def test_get_objects_occupiable_station_2(self): + occupiable_stations: list[tuple[Vector, list[OccupiableStation]]] = self.game_board.get_objects(ObjectType.OCCUPIABLE_STATION) + self.assertTrue(any(map(lambda vec_list: len(vec_list[1]) == 3, occupiable_stations))) + objects_stacked = [x[1] for x in occupiable_stations] + objects_unstacked = [x for xs in objects_stacked for x in xs] + self.assertEqual(len(objects_unstacked), 6) # test that get_objects works correctly with avatar def test_get_objects_avatar(self): diff --git a/game/test_suite/tests/test_game_board_no_gen.py b/game/test_suite/tests/test_game_board_no_gen.py index adcdac8..754cb05 100644 --- a/game/test_suite/tests/test_game_board_no_gen.py +++ b/game/test_suite/tests/test_game_board_no_gen.py @@ -72,18 +72,6 @@ def test_locations_fail_type(self): 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 test_locations_fail_len(self): - with self.assertRaises(ValueError) as e: - self.locations = { - (Vector(1, 1),): [], - (Vector(1, 2), Vector(1, 3)): [OccupiableStation(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 test_walled(self): self.game_board.walled = True