From e1a8a4c44f573570a81d83d3de968db057329e33 Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Tue, 5 Sep 2023 04:32:35 +0200 Subject: [PATCH 1/7] LNX-188 Added dtop action with tests --- lynx/common/actions/common_requirements.py | 7 ++ lynx/common/actions/drop.py | 40 ++++++++ lynx/common/enitity.py | 1 + tests/drop_test.py | 114 +++++++++++++++++++++ tests/scene_test.py | 1 + tests/user_helper_functions_test.py | 2 +- 6 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 lynx/common/actions/drop.py create mode 100644 tests/drop_test.py diff --git a/lynx/common/actions/common_requirements.py b/lynx/common/actions/common_requirements.py index d20dfc0..ced8ed0 100644 --- a/lynx/common/actions/common_requirements.py +++ b/lynx/common/actions/common_requirements.py @@ -70,3 +70,10 @@ def any_object_on_square_has_all_given_tags(scene: Scene, position: Vector, give return True return False + def has_something_in_inventory(scene: 'Scene', object_id: int) -> bool: + object: Object = scene.get_object_by_id(object_id) + + if object.inventory == {}: + return False + + return True diff --git a/lynx/common/actions/drop.py b/lynx/common/actions/drop.py new file mode 100644 index 0000000..0121d5b --- /dev/null +++ b/lynx/common/actions/drop.py @@ -0,0 +1,40 @@ +from dataclasses import dataclass, field +from typing import Dict + +from lynx.common.actions.action import Action +from lynx.common.actions.common_requirements import CommonRequirements +from lynx.common.actions.create_object import CreateObject +from lynx.common.actions.remove_object import RemoveObject +from lynx.common.object import Object +from lynx.common.vector import Vector + + +@dataclass +class Drop(Action): + """ + Action used to empty inventory of agent. + If target_position is equal to drop_area_position, we should increase global points and resources. + If target_position is different than drop_area_position then we should create each object from inventory on choosen + square (target_position) + """ + object_id: int = -1 + target_position: Vector = Vector(0, 1) + drop_area_position: Vector = Vector(0, 0) + + def apply(self, scene: 'Scene') -> None: + agent: Object = scene.get_object_by_id(self.object_id) + if self.target_position != self.drop_area_position: + for objects_to_drop in agent.inventory: + for object_to_drop in range(agent.inventory[objects_to_drop]): + object_created = Object(id=scene.generate_id(), + name=objects_to_drop, + tags=['pushable', 'pickable'], + position=self.target_position) + create_action = CreateObject(object_created.serialize()) + scene.add_to_pending_actions(create_action.serialize()) + agent.inventory = {} + + def satisfies_requirements(self, scene: 'Scene') -> bool: + return CommonRequirements.is_in_range(scene, self.object_id, self.target_position, 1) \ + and CommonRequirements.is_walkable(scene, self.target_position) \ + and CommonRequirements.has_something_in_inventory(scene, self.object_id) diff --git a/lynx/common/enitity.py b/lynx/common/enitity.py index 6b74b6e..947f105 100644 --- a/lynx/common/enitity.py +++ b/lynx/common/enitity.py @@ -32,6 +32,7 @@ def deserialize(cls, json_string: str) -> Entity: from lynx.common.actions.remove_object import RemoveObject from lynx.common.actions.print import Print from lynx.common.actions.take import Take + from lynx.common.actions.drop import Drop exported_entity = cls._Exported.deserialize(json_string) entity_type = locals()[exported_entity.type] diff --git a/tests/drop_test.py b/tests/drop_test.py new file mode 100644 index 0000000..5b8b1ef --- /dev/null +++ b/tests/drop_test.py @@ -0,0 +1,114 @@ +import random +from typing import NoReturn + +from lynx.common.actions.action import Action +from lynx.common.actions.drop import Drop +from lynx.common.object import Object +from lynx.common.scene import Scene +from lynx.common.vector import Vector + + +class TestDropSerialization: + expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}, "drop_area_position": {"x": 1, "y": 0}}}' + + def test_success_serialization(self) -> NoReturn: + serialized_drop = Drop(object_id=1, target_position=Vector(1, 0), drop_area_position=Vector(1, 0)).serialize() + + assert self.expected_serialization_drop == serialized_drop + + def test_success_deserialization(self): + expected_drop = Drop(object_id=1, target_position=Vector(1, 0), drop_area_position=Vector(1, 0)) + dummy_drop = Drop.deserialize(self.expected_serialization_drop) + + assert dummy_drop == expected_drop + +class TestDropApply: + def test_success_apply(self): + random.seed(1222) + expected_scene = Scene() + expected_dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + expected_dummy_object2 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), + tags=['pushable', 'pickable']) + expected_scene.add_entity(expected_dummy_object) + expected_scene.add_entity(expected_dummy_drop) + expected_scene.add_entity(expected_dummy_object2) + + random.seed(1222) + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + scene.add_entity(dummy_object) + scene.add_entity(dummy_drop) + + dummy_drop.apply(scene) + for action in scene.pending_actions: + Action.deserialize(action).apply(scene) + scene.pending_actions.clear() + + assert scene == expected_scene + + def test_success_multiple_objects_apply(self): + random.seed(1222) + expected_scene = Scene() + expected_dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + expected_dummy_object2 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), + tags=['pushable', 'pickable']) + expected_dummy_object3 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), + tags=['pushable', 'pickable']) + expected_dummy_object4 = Object(id=expected_scene.generate_id(), name="Stone", position=Vector(5, 6), + tags=['pushable', 'pickable']) + expected_scene.add_entity(expected_dummy_object) + expected_scene.add_entity(expected_dummy_drop) + expected_scene.add_entity(expected_dummy_object2) + expected_scene.add_entity(expected_dummy_object3) + expected_scene.add_entity(expected_dummy_object4) + + random.seed(1222) + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 2, "Stone": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + scene.add_entity(dummy_object) + scene.add_entity(dummy_drop) + + dummy_drop.apply(scene) + for action in scene.pending_actions: + Action.deserialize(action).apply(scene) + scene.pending_actions.clear() + + assert scene == expected_scene + +class TestDropRequirements: + + def test_success_requirements(self): + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) + scene.add_entity(dummy_object) + assert dummy_drop.satisfies_requirements(scene) is True + + def test_fail_requirements_distance(self): + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(6, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) + scene.add_entity(dummy_object) + assert dummy_drop.satisfies_requirements(scene) is not True + + def test_fail_requirements_inventory(self): + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) + scene.add_entity(dummy_object) + assert dummy_drop.satisfies_requirements(scene) is not True + + def test_fail_requirements_walkable(self): + scene = Scene() + dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6))) + scene.add_entity(dummy_object) + assert dummy_drop.satisfies_requirements(scene) is not True diff --git a/tests/scene_test.py b/tests/scene_test.py index 69d8635..e567970 100644 --- a/tests/scene_test.py +++ b/tests/scene_test.py @@ -13,6 +13,7 @@ class TestSceneSerialization: '"tick": "", "on_death": "", "owner": "", "tags": [], "inventory": {}}}, ' \ '{"type": "Move", "attributes": {"object_id": 456, "direction": {"x": 1, "y": ' \ '0}}}], "pending_actions": []}' + def test_success(self) -> NoReturn: scene = Scene() diff --git a/tests/user_helper_functions_test.py b/tests/user_helper_functions_test.py index 081b3ec..1f6a776 100644 --- a/tests/user_helper_functions_test.py +++ b/tests/user_helper_functions_test.py @@ -52,7 +52,7 @@ def test_filter_objects_failure(self) -> NoReturn: def test_random_direction_success(self) -> NoReturn: self.scene.add_entity(Object(id=10, name="Grass", position=Vector(-1, 0), tags=["walkable"])) self.scene.add_entity(Object(id=11, name="Grass", position=Vector(0, 1), tags=["walkable"])) - assert random_direction(self.scene, 1) == Vector(-1, 0) + assert random_direction(self.scene, 1) == Vector(0, 1) def test_random_direction_failure(self) -> NoReturn: assert random_direction(self.scene, 1) != Vector(1, 0) From 01851e143dc5898ef47d715c0782d81aa1d9b76e Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Sat, 9 Sep 2023 14:15:56 +0200 Subject: [PATCH 2/7] LNX-188 Update drop action, for droping in drop area --- lynx/common/actions/drop.py | 16 +++++++++++----- lynx/common/actions/update_points.py | 20 ++++++++++++++++++++ lynx/common/scene.py | 2 ++ tests/drop_test.py | 22 +++++++++++++++++++++- 4 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 lynx/common/actions/update_points.py diff --git a/lynx/common/actions/drop.py b/lynx/common/actions/drop.py index 0121d5b..2e18f84 100644 --- a/lynx/common/actions/drop.py +++ b/lynx/common/actions/drop.py @@ -1,10 +1,10 @@ -from dataclasses import dataclass, field -from typing import Dict +from dataclasses import dataclass +from collections import defaultdict from lynx.common.actions.action import Action from lynx.common.actions.common_requirements import CommonRequirements from lynx.common.actions.create_object import CreateObject -from lynx.common.actions.remove_object import RemoveObject +from lynx.common.actions.update_points import UpdatePoints from lynx.common.object import Object from lynx.common.vector import Vector @@ -23,7 +23,12 @@ class Drop(Action): def apply(self, scene: 'Scene') -> None: agent: Object = scene.get_object_by_id(self.object_id) - if self.target_position != self.drop_area_position: + if self.target_position == self.drop_area_position: + for object_name, count in agent.inventory.items(): + scene.players_points[agent.owner][object_name] += count + updated_points_action = UpdatePoints(agent.owner, agent.inventory) + scene.add_to_pending_actions(updated_points_action.serialize()) + else: for objects_to_drop in agent.inventory: for object_to_drop in range(agent.inventory[objects_to_drop]): object_created = Object(id=scene.generate_id(), @@ -36,5 +41,6 @@ def apply(self, scene: 'Scene') -> None: def satisfies_requirements(self, scene: 'Scene') -> bool: return CommonRequirements.is_in_range(scene, self.object_id, self.target_position, 1) \ - and CommonRequirements.is_walkable(scene, self.target_position) \ + and (CommonRequirements.is_walkable(scene, self.target_position) + or self.drop_area_position == self.target_position) \ and CommonRequirements.has_something_in_inventory(scene, self.object_id) diff --git a/lynx/common/actions/update_points.py b/lynx/common/actions/update_points.py new file mode 100644 index 0000000..e87a4d3 --- /dev/null +++ b/lynx/common/actions/update_points.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass +from typing import Dict +from dataclasses import field + +from lynx.common.actions.action import Action + + +@dataclass +class UpdatePoints(Action): + """ + Simple action for updating points on fronted. + """ + user_name: str = "" + points_updated: Dict[str, int] = field(default_factory=dict) + + def satisfies_requirements(self, scene: 'Scene') -> bool: + return True + + def apply(self, scene: 'Scene') -> None: + pass diff --git a/lynx/common/scene.py b/lynx/common/scene.py index e391ba9..155a99e 100644 --- a/lynx/common/scene.py +++ b/lynx/common/scene.py @@ -11,6 +11,7 @@ @dataclass class Scene(Serializable): players: List[str] = field(default_factory=list) + players_points: Dict[str, Dict[str, int]] = field(default_factory=dict) entities: List[Entity] = field(default_factory=list) pending_actions: List[str] = field(default_factory=list) # Transformations which occur, during other transformations (e.g. chop -> Create logs) _square_position_map: Dict[Vector, Square] = field(default_factory=dict) @@ -71,6 +72,7 @@ def is_player_new(self, player: str) -> bool: def add_player(self, player: str) -> None: self.players.append(player) + self.players_points[player] = {"Wood": 0, "Stone": 0} def is_world_created(self) -> bool: return bool(self.entities) diff --git a/tests/drop_test.py b/tests/drop_test.py index 5b8b1ef..86643d2 100644 --- a/tests/drop_test.py +++ b/tests/drop_test.py @@ -6,7 +6,7 @@ from lynx.common.object import Object from lynx.common.scene import Scene from lynx.common.vector import Vector - +from lynx.common.actions.update_points import UpdatePoints class TestDropSerialization: expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}, "drop_area_position": {"x": 1, "y": 0}}}' @@ -79,6 +79,26 @@ def test_success_multiple_objects_apply(self): assert scene == expected_scene + def test_success_drop_to_drop_area_apply(self): + expected_scene = Scene() + expected_scene.players_points["test"] = {"Wood": 2, "Stone": 1} + expected_dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + expected_update_points = UpdatePoints(user_name="test", points_updated = {"Wood": 2, "Stone": 1}) + expected_scene.add_entity(expected_dummy_object) + expected_scene.add_entity(expected_dummy_drop) + expected_scene.pending_actions.append(expected_update_points.serialize()) + + scene = Scene() + scene.players_points["test"] = {"Wood": 0, "Stone": 0} + dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5), inventory={"Wood": 2, "Stone": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + scene.add_entity(dummy_object) + scene.add_entity(dummy_drop) + + dummy_drop.apply(scene) + + assert scene == expected_scene class TestDropRequirements: def test_success_requirements(self): From 1bedb8ac07afc44b623c9f3f549ec2cd36e41574 Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Sun, 10 Sep 2023 03:59:05 +0200 Subject: [PATCH 3/7] LNX-188 Added droping to drop_area, player class in scene --- lynx/common/actions/drop.py | 14 ++++--- lynx/common/enitity.py | 2 + lynx/common/player.py | 14 +++++++ lynx/common/scene.py | 31 +++++++++++--- tests/drop_test.py | 65 ++++++++++++++--------------- tests/user_helper_functions_test.py | 3 +- 6 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 lynx/common/player.py diff --git a/lynx/common/actions/drop.py b/lynx/common/actions/drop.py index 2e18f84..1035b48 100644 --- a/lynx/common/actions/drop.py +++ b/lynx/common/actions/drop.py @@ -19,14 +19,13 @@ class Drop(Action): """ object_id: int = -1 target_position: Vector = Vector(0, 1) - drop_area_position: Vector = Vector(0, 0) def apply(self, scene: 'Scene') -> None: agent: Object = scene.get_object_by_id(self.object_id) - if self.target_position == self.drop_area_position: - for object_name, count in agent.inventory.items(): - scene.players_points[agent.owner][object_name] += count - updated_points_action = UpdatePoints(agent.owner, agent.inventory) + player_name = agent.owner + if self.target_position == scene.get_drop_area_of_a_player(player_name): + scene.update_resources_of_player(player_name, agent.inventory) + updated_points_action = UpdatePoints(player_name, agent.inventory) scene.add_to_pending_actions(updated_points_action.serialize()) else: for objects_to_drop in agent.inventory: @@ -37,10 +36,13 @@ def apply(self, scene: 'Scene') -> None: position=self.target_position) create_action = CreateObject(object_created.serialize()) scene.add_to_pending_actions(create_action.serialize()) + agent.inventory = {} def satisfies_requirements(self, scene: 'Scene') -> bool: + agent: Object = scene.get_object_by_id(self.object_id) + return CommonRequirements.is_in_range(scene, self.object_id, self.target_position, 1) \ and (CommonRequirements.is_walkable(scene, self.target_position) - or self.drop_area_position == self.target_position) \ + or scene.get_drop_area_of_a_player(agent.owner) == self.target_position) \ and CommonRequirements.has_something_in_inventory(scene, self.object_id) diff --git a/lynx/common/enitity.py b/lynx/common/enitity.py index 947f105..a575b6b 100644 --- a/lynx/common/enitity.py +++ b/lynx/common/enitity.py @@ -33,6 +33,8 @@ def deserialize(cls, json_string: str) -> Entity: from lynx.common.actions.print import Print from lynx.common.actions.take import Take from lynx.common.actions.drop import Drop + from lynx.common.actions.update_points import UpdatePoints + from lynx.common.player import Player exported_entity = cls._Exported.deserialize(json_string) entity_type = locals()[exported_entity.type] diff --git a/lynx/common/player.py b/lynx/common/player.py new file mode 100644 index 0000000..df6b059 --- /dev/null +++ b/lynx/common/player.py @@ -0,0 +1,14 @@ +from typing import Dict, NoReturn, Optional + +from lynx.common.square import Square +from lynx.common.object import * +from lynx.common.serializable import Serializable +from lynx.common.vector import Vector +from lynx.common.serializable import Serializable + +@dataclass +class Player(Serializable): + player_id: str = field(default_factory=str) + player_resources: Dict[str, int] = field(default_factory=dict) + drop_area: Vector = field(default_factory=Vector) + diff --git a/lynx/common/scene.py b/lynx/common/scene.py index 155a99e..1b6769f 100644 --- a/lynx/common/scene.py +++ b/lynx/common/scene.py @@ -5,13 +5,13 @@ from lynx.common.serializable import Serializable from lynx.common.vector import Vector from lynx.common.actions.create_object import CreateObject +from lynx.common.player import Player import random @dataclass class Scene(Serializable): - players: List[str] = field(default_factory=list) - players_points: Dict[str, Dict[str, int]] = field(default_factory=dict) + players: List[Player] = field(default_factory=list) entities: List[Entity] = field(default_factory=list) pending_actions: List[str] = field(default_factory=list) # Transformations which occur, during other transformations (e.g. chop -> Create logs) _square_position_map: Dict[Vector, Square] = field(default_factory=dict) @@ -67,12 +67,12 @@ def remove_object(self, object: Object) -> NoReturn: def add_to_pending_actions(self, action: str) -> NoReturn: self.pending_actions.append(action) - def is_player_new(self, player: str) -> bool: - return player not in self.players + def is_player_new(self, player_id: str) -> bool: + players_id = [player.player_id for player in self.players] + return player_id not in players_id def add_player(self, player: str) -> None: - self.players.append(player) - self.players_points[player] = {"Wood": 0, "Stone": 0} + self.players.append(Player(player_id=player, player_resources={"Wood": 0, "Stone": 0}, drop_area=None)) def is_world_created(self) -> bool: return bool(self.entities) @@ -90,3 +90,22 @@ def generate_drop_area(self, player: str) -> None: drop_area = Object(name="DropArea", id=self.generate_id(), position=position, owner=player) create_drop_area = CreateObject(drop_area.serialize()) self.add_to_pending_actions(create_drop_area.serialize()) + player_object = self.get_player(player) + player_object.drop_area = position + + def get_player(self, player_id: str) -> Optional[Player]: + for player in self.players: + if player.player_id == player_id: + return player + return None + + def get_drop_area_of_a_player(self, player_id: str) -> Optional[Vector]: + for player in self.players: + if player.player_id == player_id: + return player.drop_area + return None + + def update_resources_of_player(self, player_id: str, inventory: Dict): + player = self.get_player(player_id) + for object_name, count in inventory.items(): + player.player_resources[object_name] += count diff --git a/tests/drop_test.py b/tests/drop_test.py index 86643d2..620f22e 100644 --- a/tests/drop_test.py +++ b/tests/drop_test.py @@ -4,20 +4,21 @@ from lynx.common.actions.action import Action from lynx.common.actions.drop import Drop from lynx.common.object import Object +from lynx.common.player import Player from lynx.common.scene import Scene from lynx.common.vector import Vector from lynx.common.actions.update_points import UpdatePoints class TestDropSerialization: - expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}, "drop_area_position": {"x": 1, "y": 0}}}' + expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}}}' def test_success_serialization(self) -> NoReturn: - serialized_drop = Drop(object_id=1, target_position=Vector(1, 0), drop_area_position=Vector(1, 0)).serialize() + serialized_drop = Drop(object_id=1, target_position=Vector(1, 0)).serialize() assert self.expected_serialization_drop == serialized_drop def test_success_deserialization(self): - expected_drop = Drop(object_id=1, target_position=Vector(1, 0), drop_area_position=Vector(1, 0)) + expected_drop = Drop(object_id=1, target_position=Vector(1, 0)) dummy_drop = Drop.deserialize(self.expected_serialization_drop) assert dummy_drop == expected_drop @@ -25,9 +26,9 @@ def test_success_deserialization(self): class TestDropApply: def test_success_apply(self): random.seed(1222) - expected_scene = Scene() - expected_dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) - expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + expected_scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + expected_dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) expected_dummy_object2 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), tags=['pushable', 'pickable']) expected_scene.add_entity(expected_dummy_object) @@ -35,9 +36,9 @@ def test_success_apply(self): expected_scene.add_entity(expected_dummy_object2) random.seed(1222) - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(dummy_object) scene.add_entity(dummy_drop) @@ -50,9 +51,9 @@ def test_success_apply(self): def test_success_multiple_objects_apply(self): random.seed(1222) - expected_scene = Scene() - expected_dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) - expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + expected_scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + expected_dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) expected_dummy_object2 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), tags=['pushable', 'pickable']) expected_dummy_object3 = Object(id=expected_scene.generate_id(), name="Wood", position=Vector(5, 6), @@ -66,9 +67,9 @@ def test_success_multiple_objects_apply(self): expected_scene.add_entity(expected_dummy_object4) random.seed(1222) - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 2, "Stone": 1}) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(0, 0)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 2, "Stone": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(dummy_object) scene.add_entity(dummy_drop) @@ -80,19 +81,17 @@ def test_success_multiple_objects_apply(self): assert scene == expected_scene def test_success_drop_to_drop_area_apply(self): - expected_scene = Scene() - expected_scene.players_points["test"] = {"Wood": 2, "Stone": 1} + expected_scene = Scene(players=[Player(player_id="test", player_resources={"Wood": 2, "Stone": 1}, drop_area=Vector(5, 6))]) expected_dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5)) - expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) expected_update_points = UpdatePoints(user_name="test", points_updated = {"Wood": 2, "Stone": 1}) expected_scene.add_entity(expected_dummy_object) expected_scene.add_entity(expected_dummy_drop) expected_scene.pending_actions.append(expected_update_points.serialize()) - scene = Scene() - scene.players_points["test"] = {"Wood": 0, "Stone": 0} + scene = Scene(players=[Player(player_id="test", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 6))]) dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5), inventory={"Wood": 2, "Stone": 1}) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(dummy_object) scene.add_entity(dummy_drop) @@ -102,33 +101,33 @@ def test_success_drop_to_drop_area_apply(self): class TestDropRequirements: def test_success_requirements(self): - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 6)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is True def test_fail_requirements_distance(self): - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) - dummy_drop = Drop(target_position=Vector(6, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(6, 6), object_id=1) scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is not True def test_fail_requirements_inventory(self): - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5)) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is not True def test_fail_requirements_walkable(self): - scene = Scene() - dummy_object = Object(id=1, name="dummy", position=Vector(5, 5), inventory={"Wood": 1}) - dummy_drop = Drop(target_position=Vector(5, 6), object_id=1, drop_area_position=Vector(5, 8)) + scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) + dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) + dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6))) scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is not True diff --git a/tests/user_helper_functions_test.py b/tests/user_helper_functions_test.py index 1f6a776..620ef94 100644 --- a/tests/user_helper_functions_test.py +++ b/tests/user_helper_functions_test.py @@ -50,9 +50,10 @@ def test_filter_objects_failure(self) -> NoReturn: assert filter_objects(self.scene, [1, 2, 3, 4], "empty") != [1] def test_random_direction_success(self) -> NoReturn: + random.seed(12) self.scene.add_entity(Object(id=10, name="Grass", position=Vector(-1, 0), tags=["walkable"])) self.scene.add_entity(Object(id=11, name="Grass", position=Vector(0, 1), tags=["walkable"])) - assert random_direction(self.scene, 1) == Vector(0, 1) + assert random_direction(self.scene, 1) == Vector(-1, 0) def test_random_direction_failure(self) -> NoReturn: assert random_direction(self.scene, 1) != Vector(1, 0) From a24da5d9cd6ea7c619f8c0deafc94ae9db11ee8f Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Sun, 10 Sep 2023 22:09:54 +0200 Subject: [PATCH 4/7] LNX-188 Rename update_points to update_resources --- lynx/common/actions/drop.py | 4 ++-- lynx/common/actions/{update_points.py => update_resources.py} | 2 +- lynx/common/enitity.py | 2 +- tests/drop_test.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename lynx/common/actions/{update_points.py => update_resources.py} (93%) diff --git a/lynx/common/actions/drop.py b/lynx/common/actions/drop.py index 1035b48..bb8c8eb 100644 --- a/lynx/common/actions/drop.py +++ b/lynx/common/actions/drop.py @@ -4,7 +4,7 @@ from lynx.common.actions.action import Action from lynx.common.actions.common_requirements import CommonRequirements from lynx.common.actions.create_object import CreateObject -from lynx.common.actions.update_points import UpdatePoints +from lynx.common.actions.update_resources import UpdateResources from lynx.common.object import Object from lynx.common.vector import Vector @@ -25,7 +25,7 @@ def apply(self, scene: 'Scene') -> None: player_name = agent.owner if self.target_position == scene.get_drop_area_of_a_player(player_name): scene.update_resources_of_player(player_name, agent.inventory) - updated_points_action = UpdatePoints(player_name, agent.inventory) + updated_points_action = UpdateResources(player_name, agent.inventory) scene.add_to_pending_actions(updated_points_action.serialize()) else: for objects_to_drop in agent.inventory: diff --git a/lynx/common/actions/update_points.py b/lynx/common/actions/update_resources.py similarity index 93% rename from lynx/common/actions/update_points.py rename to lynx/common/actions/update_resources.py index e87a4d3..3eb74dd 100644 --- a/lynx/common/actions/update_points.py +++ b/lynx/common/actions/update_resources.py @@ -6,7 +6,7 @@ @dataclass -class UpdatePoints(Action): +class UpdateResources(Action): """ Simple action for updating points on fronted. """ diff --git a/lynx/common/enitity.py b/lynx/common/enitity.py index a575b6b..da49629 100644 --- a/lynx/common/enitity.py +++ b/lynx/common/enitity.py @@ -33,7 +33,7 @@ def deserialize(cls, json_string: str) -> Entity: from lynx.common.actions.print import Print from lynx.common.actions.take import Take from lynx.common.actions.drop import Drop - from lynx.common.actions.update_points import UpdatePoints + from lynx.common.actions.update_resources import UpdateResources from lynx.common.player import Player exported_entity = cls._Exported.deserialize(json_string) diff --git a/tests/drop_test.py b/tests/drop_test.py index 620f22e..c063a1d 100644 --- a/tests/drop_test.py +++ b/tests/drop_test.py @@ -7,7 +7,7 @@ from lynx.common.player import Player from lynx.common.scene import Scene from lynx.common.vector import Vector -from lynx.common.actions.update_points import UpdatePoints +from lynx.common.actions.update_resources import UpdateResources class TestDropSerialization: expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}}}' @@ -84,7 +84,7 @@ def test_success_drop_to_drop_area_apply(self): expected_scene = Scene(players=[Player(player_id="test", player_resources={"Wood": 2, "Stone": 1}, drop_area=Vector(5, 6))]) expected_dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5)) expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) - expected_update_points = UpdatePoints(user_name="test", points_updated = {"Wood": 2, "Stone": 1}) + expected_update_points = UpdateResources(user_name="test", points_updated = {"Wood": 2, "Stone": 1}) expected_scene.add_entity(expected_dummy_object) expected_scene.add_entity(expected_dummy_drop) expected_scene.pending_actions.append(expected_update_points.serialize()) From ea74b350714fa11842edfcc85ae3e369258f9c2b Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Sun, 10 Sep 2023 22:13:37 +0200 Subject: [PATCH 5/7] LNX-188 Updated is_in_inventory method --- lynx/common/actions/common_requirements.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lynx/common/actions/common_requirements.py b/lynx/common/actions/common_requirements.py index ced8ed0..93cb906 100644 --- a/lynx/common/actions/common_requirements.py +++ b/lynx/common/actions/common_requirements.py @@ -72,8 +72,4 @@ def any_object_on_square_has_all_given_tags(scene: Scene, position: Vector, give def has_something_in_inventory(scene: 'Scene', object_id: int) -> bool: object: Object = scene.get_object_by_id(object_id) - - if object.inventory == {}: - return False - - return True + return(bool(object.inventory)) From e5a81a095efa2216e977bb617a98dca04d879efb Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Mon, 11 Sep 2023 18:55:23 +0200 Subject: [PATCH 6/7] LNX-188 Adjustments, based on PR --- lynx/common/actions/common_requirements.py | 2 +- lynx/common/actions/drop.py | 34 +++++++++++++--------- lynx/common/actions/update_resources.py | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lynx/common/actions/common_requirements.py b/lynx/common/actions/common_requirements.py index 93cb906..9e0a82b 100644 --- a/lynx/common/actions/common_requirements.py +++ b/lynx/common/actions/common_requirements.py @@ -72,4 +72,4 @@ def any_object_on_square_has_all_given_tags(scene: Scene, position: Vector, give def has_something_in_inventory(scene: 'Scene', object_id: int) -> bool: object: Object = scene.get_object_by_id(object_id) - return(bool(object.inventory)) + return bool(object.inventory) diff --git a/lynx/common/actions/drop.py b/lynx/common/actions/drop.py index bb8c8eb..f7a3bd9 100644 --- a/lynx/common/actions/drop.py +++ b/lynx/common/actions/drop.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from collections import defaultdict from lynx.common.actions.action import Action from lynx.common.actions.common_requirements import CommonRequirements @@ -12,30 +11,37 @@ @dataclass class Drop(Action): """ - Action used to empty inventory of agent. + Action used to empty inventory of Agent. If target_position is equal to drop_area_position, we should increase global points and resources. - If target_position is different than drop_area_position then we should create each object from inventory on choosen + If target_position is different than drop_area_position then we should create each object from inventory on chosen square (target_position) """ object_id: int = -1 target_position: Vector = Vector(0, 1) + def drop_in_drop_area(self, scene: 'Scene', player_name: str, inventory: dict): + scene.update_resources_of_player(player_name, inventory) + update_resources_action = UpdateResources(player_name, inventory) + scene.add_to_pending_actions(update_resources_action.serialize()) + + def drop_in_overworld(self, scene: 'Scene', inventory: dict): + for objects_to_drop in inventory: + for object_to_drop in range(inventory[objects_to_drop]): + object_created = Object(id=scene.generate_id(), + name=objects_to_drop, + tags=['pushable', 'pickable'], + position=self.target_position) + create_action = CreateObject(object_created.serialize()) + scene.add_to_pending_actions(create_action.serialize()) + + def apply(self, scene: 'Scene') -> None: agent: Object = scene.get_object_by_id(self.object_id) player_name = agent.owner if self.target_position == scene.get_drop_area_of_a_player(player_name): - scene.update_resources_of_player(player_name, agent.inventory) - updated_points_action = UpdateResources(player_name, agent.inventory) - scene.add_to_pending_actions(updated_points_action.serialize()) + self.drop_in_drop_area(scene, player_name, agent.inventory) else: - for objects_to_drop in agent.inventory: - for object_to_drop in range(agent.inventory[objects_to_drop]): - object_created = Object(id=scene.generate_id(), - name=objects_to_drop, - tags=['pushable', 'pickable'], - position=self.target_position) - create_action = CreateObject(object_created.serialize()) - scene.add_to_pending_actions(create_action.serialize()) + self.drop_in_overworld(scene, agent.inventory) agent.inventory = {} diff --git a/lynx/common/actions/update_resources.py b/lynx/common/actions/update_resources.py index 3eb74dd..e4bda01 100644 --- a/lynx/common/actions/update_resources.py +++ b/lynx/common/actions/update_resources.py @@ -8,7 +8,7 @@ @dataclass class UpdateResources(Action): """ - Simple action for updating points on fronted. + Simple action for indicating that we should update resource view in the front-end. """ user_name: str = "" points_updated: Dict[str, int] = field(default_factory=dict) From 7be9e5464785824ec74c51198976fbd1f62953e4 Mon Sep 17 00:00:00 2001 From: EasyJBL Date: Tue, 12 Sep 2023 22:36:18 +0200 Subject: [PATCH 7/7] LNX-188 Renamed tests, added type hints --- tests/drop_test.py | 23 +++++++++++++---------- tests/user_helper_functions_test.py | 1 - 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/drop_test.py b/tests/drop_test.py index c063a1d..ae9deb5 100644 --- a/tests/drop_test.py +++ b/tests/drop_test.py @@ -12,19 +12,20 @@ class TestDropSerialization: expected_serialization_drop = '{"type": "Drop", "attributes": {"object_id": 1, "target_position": {"x": 1, "y": 0}}}' - def test_success_serialization(self) -> NoReturn: + def test_success_serialization(self) -> None: serialized_drop = Drop(object_id=1, target_position=Vector(1, 0)).serialize() assert self.expected_serialization_drop == serialized_drop - def test_success_deserialization(self): + def test_success_deserialization(self) -> None: expected_drop = Drop(object_id=1, target_position=Vector(1, 0)) dummy_drop = Drop.deserialize(self.expected_serialization_drop) assert dummy_drop == expected_drop + class TestDropApply: - def test_success_apply(self): + def test_drop_single_object_in_overworld_on_tile_sucessful(self) -> None: random.seed(1222) expected_scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) expected_dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) @@ -49,7 +50,7 @@ def test_success_apply(self): assert scene == expected_scene - def test_success_multiple_objects_apply(self): + def test_drop_multiple_objects_in_overworld_on_tile_sucessful(self) -> None: random.seed(1222) expected_scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) expected_dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) @@ -80,7 +81,7 @@ def test_success_multiple_objects_apply(self): assert scene == expected_scene - def test_success_drop_to_drop_area_apply(self): + def test_success_drop_to_drop_area_apply(self) -> None: expected_scene = Scene(players=[Player(player_id="test", player_resources={"Wood": 2, "Stone": 1}, drop_area=Vector(5, 6))]) expected_dummy_object = Object(id=1, name="dummy", owner="test", position=Vector(5, 5)) expected_dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) @@ -98,9 +99,11 @@ def test_success_drop_to_drop_area_apply(self): dummy_drop.apply(scene) assert scene == expected_scene + + class TestDropRequirements: - def test_success_requirements(self): + def test_all_requirements_satisified_positive(self) -> None: scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) @@ -108,15 +111,15 @@ def test_success_requirements(self): scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is True - def test_fail_requirements_distance(self): + def test_requirements_agent_too_far_fail(self) -> None: scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) dummy_drop = Drop(target_position=Vector(6, 6), object_id=1) - scene.add_entity(Object(id=3, name="Grass", position=Vector(5, 6), tags=['walkable'])) + scene.add_entity(Object(id=3, name="Grass", position=Vector(6, 6), tags=['walkable'])) scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is not True - def test_fail_requirements_inventory(self): + def test_requirements_empty_inventory_fail(self) -> None: scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5)) dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) @@ -124,7 +127,7 @@ def test_fail_requirements_inventory(self): scene.add_entity(dummy_object) assert dummy_drop.satisfies_requirements(scene) is not True - def test_fail_requirements_walkable(self): + def test_requirements_no_walkable_tile_fail(self) -> None: scene = Scene(players=[Player(player_id="dummy", player_resources={"Wood": 0, "Stone": 0}, drop_area=Vector(5, 5))]) dummy_object = Object(id=1, name="dummy", owner="dummy", position=Vector(5, 5), inventory={"Wood": 1}) dummy_drop = Drop(target_position=Vector(5, 6), object_id=1) diff --git a/tests/user_helper_functions_test.py b/tests/user_helper_functions_test.py index 620ef94..d27b6a0 100644 --- a/tests/user_helper_functions_test.py +++ b/tests/user_helper_functions_test.py @@ -17,7 +17,6 @@ class TestUserHelperFunctions: scene.add_entity(dummy_object2) dummy_object3 = Object(id=4, name="diff_dummy", position=Vector(1, 1)) scene.add_entity(dummy_object3) - random.seed(12) def test_objects_around_success(self) -> NoReturn: assert get_objects_around(1, self.scene, 9) == [3, 4]