From 3182e341fd9fef19c74f5019851d88156af956d9 Mon Sep 17 00:00:00 2001 From: shima004 Date: Wed, 25 Sep 2024 16:25:03 +0900 Subject: [PATCH 1/2] Refactor WorldInfo class to include methods for retrieving entities and entity IDs --- adf_core_python/core/agent/info/world_info.py | 40 ++++++++++- poetry.lock | 70 +++++++++---------- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/adf_core_python/core/agent/info/world_info.py b/adf_core_python/core/agent/info/world_info.py index 076fe0e..1ca617d 100644 --- a/adf_core_python/core/agent/info/world_info.py +++ b/adf_core_python/core/agent/info/world_info.py @@ -1,5 +1,6 @@ -from typing import Any +from typing import Any, Optional +from rcrs_core.entities.entity import Entity from rcrs_core.worldmodel.changeSet import ChangeSet from rcrs_core.worldmodel.entityID import EntityID from rcrs_core.worldmodel.worldmodel import WorldModel @@ -35,3 +36,40 @@ def set_change_set(self, change_set: ChangeSet) -> None: Change set """ self._change_set = change_set + + def get_entity(self, entity_id: EntityID) -> Optional[Entity]: + """ + Get the entity + + Parameters + ---------- + entity_id : EntityID + Entity ID + + Returns + ------- + Optional[Entity] + Entity + """ + return self._world_model.get_entity(entity_id) + + def get_entity_ids_of_type(self, entity_type: type[Entity]) -> list[EntityID]: + """ + Get the entity IDs of the specified type + + Parameters + ---------- + entity_type : type[Entity] + Entity type + + Returns + ------- + list[EntityID] + Entity IDs + """ + entity_ids: list[EntityID] = [] + for entity in self._world_model.get_entities(): + if isinstance(entity, entity_type): + entity_ids.append(entity.get_id()) + + return entity_ids diff --git a/poetry.lock b/poetry.lock index 733bc53..b074efb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -35,38 +35,38 @@ files = [ [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] @@ -177,13 +177,13 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -274,7 +274,7 @@ rtree = "*" type = "git" url = "https://github.com/adf-python/rcrs-core-python" reference = "HEAD" -resolved_reference = "6771354084ce60b3dd96a77f509ad1e0314ac886" +resolved_reference = "7b0d4ef8d6c1205c00f9e197c5833a3939f482b2" [[package]] name = "rtree" @@ -362,13 +362,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240808" +version = "6.0.12.20240917" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, - {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, ] [[package]] From 5b70068175cf0efc95e618eac71ecdaef58b6ed9 Mon Sep 17 00:00:00 2001 From: shima004 Date: Wed, 25 Sep 2024 16:25:07 +0900 Subject: [PATCH 2/2] feat: Implement DefaultExtendActionMove class for extending action move functionality --- .../default_extend_action_move.py | 106 ++++++++ .../default_extend_action_transport.py | 254 ++++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 adf_core_python/implement/extend_action/default_extend_action_move.py create mode 100644 adf_core_python/implement/extend_action/default_extend_action_transport.py diff --git a/adf_core_python/implement/extend_action/default_extend_action_move.py b/adf_core_python/implement/extend_action/default_extend_action_move.py new file mode 100644 index 0000000..a8df764 --- /dev/null +++ b/adf_core_python/implement/extend_action/default_extend_action_move.py @@ -0,0 +1,106 @@ +from typing import Optional, cast + +from rcrs_core.entities.area import Area +from rcrs_core.entities.blockade import Blockade +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.human import Human +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import Mode, ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.extaction.ext_action import ExtAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultExtendActionMove(ExtAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._target_entity_id: Optional[EntityID] = None + self._threshold_to_rest: int = develop_data.get_value("threshold_to_rest", 100) + + match self.scenario_info.get_mode(): + case Mode.NON_PRECOMPUTE: + self._path_planning: PathPlanning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionMove.PathPlanning", + "adf_core_python.implement.module.astar_path_planning.AStarPathPlanning", + ), + ) + case Mode.PRECOMPUTATION: + pass + case Mode.PRECOMPUTED: + pass + + def precompute(self, precompute_data: PrecomputeData) -> ExtAction: + super().precompute(precompute_data) + if self.get_count_precompute() > 1: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtAction: + super().resume(precompute_data) + if self.get_count_resume() > 1: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> ExtAction: + super().prepare() + if self.get_count_prepare() > 1: + return self + self._path_planning.prepare() + return self + + def update_info(self, message_manager: MessageManager) -> ExtAction: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtAction: + entity: Optional[Entity] = self.world_info.get_entity(target_entity_id) + self._target_entity_id = None + + if entity is None: + return self + + if isinstance(entity, Blockade): + entity = self.world_info.get_entity(cast(Blockade, entity).get_position()) + elif isinstance(entity, Human): + entity = entity.get_position() + + if entity is None and isinstance(entity, Area): + self._target_entity_id = None + + return self + + def calc(self) -> ExtAction: + self._result = None + agent: Human = cast(Human, self.agent_info.get_myself()) + + path: list[EntityID] = self._path_planning.get_path( + agent.get_position(), self._target_entity_id + ) + + if path is not None or len(path) != 0: + self.result = ActionMove(path) + + return self diff --git a/adf_core_python/implement/extend_action/default_extend_action_transport.py b/adf_core_python/implement/extend_action/default_extend_action_transport.py new file mode 100644 index 0000000..f770b48 --- /dev/null +++ b/adf_core_python/implement/extend_action/default_extend_action_transport.py @@ -0,0 +1,254 @@ +from typing import Optional, Union, cast + +from rcrs_core.entities.ambulanceTeam import AmbulanceTeamEntity +from rcrs_core.entities.area import Area +from rcrs_core.entities.civilian import Civilian +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.human import Human +from rcrs_core.entities.refuge import Refuge +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.action.ambulance.action_load import ActionLoad +from adf_core_python.core.agent.action.ambulance.action_unload import ActionUnload +from adf_core_python.core.agent.action.common.action_move import ActionMove +from adf_core_python.core.agent.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import Mode, ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.extaction.ext_action import ExtAction +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultExtendActionTransport(ExtAction): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._target_entity_id: Optional[EntityID] = None + self._threshold_to_rest: int = develop_data.get_value("threshold_to_rest", 100) + + match self.scenario_info.get_mode(): + case Mode.NON_PRECOMPUTE: + self._path_planning: PathPlanning = cast( + PathPlanning, + self.module_manager.get_module( + "DefaultExtendActionMove.PathPlanning", + "adf_core_python.implement.module.astar_path_planning.AStarPathPlanning", + ), + ) + case Mode.PRECOMPUTATION: + pass + case Mode.PRECOMPUTED: + pass + + def precompute(self, precompute_data: PrecomputeData) -> ExtAction: + super().precompute(precompute_data) + if self.get_count_precompute() > 1: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> ExtAction: + super().resume(precompute_data) + if self.get_count_resume() > 1: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> ExtAction: + super().prepare() + if self.get_count_prepare() > 1: + return self + self._path_planning.prepare() + return self + + def update_info(self, message_manager: MessageManager) -> ExtAction: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + self._path_planning.update_info(message_manager) + return self + + def set_target_entity_id(self, target_entity_id: EntityID) -> ExtAction: + entity: Optional[Entity] = self.world_info.get_world_model().get_entity( + target_entity_id + ) + self._target_entity_id = None + + if entity is None: + return self + + if isinstance(entity, Human) or isinstance(entity, Area): + self._target_entity_id = target_entity_id + + return self + + def calc(self) -> ExtAction: + self._result = None + agent: AmbulanceTeamEntity = cast( + AmbulanceTeamEntity, self.agent_info.get_myself() + ) + transport_human: Human = self.agent_info.some_one_on_board() + if transport_human is not None: + self.result = self.calc_unload( + agent, self._path_planning, transport_human, self._target_entity_id + ) + if self.result is not None: + return self + + if self._target_entity_id is not None: + self.result = self.calc_rescue( + agent, self._path_planning, self._target_entity_id + ) + + return self + + def calc_rescue( + self, + agent: AmbulanceTeamEntity, + path_planning: PathPlanning, + target_id: EntityID, + ) -> Optional[Union[ActionMove, ActionLoad]]: + target_entity = self.world_info.get_entity(target_id) + if target_entity is None: + return None + + agent_position = agent.get_position() + if isinstance(target_entity, Human): + human = target_entity + if human.get_position() is None: + return None + if human.get_hp() is None or human.get_hp() == 0: + return None + + target_position = human.get_position() + if agent_position == target_position: + if isinstance(human, Civilian) and ( + human.get_buriedness() is not None and human.get_buriedness() > 0 + ): + return ActionLoad(human.get_id()) + else: + path = path_planning.get_path(agent_position, target_position) + if path is not None and len(path) > 0: + return ActionMove(path) + return None + + if isinstance(target_entity, Area): + path = path_planning.get_path(agent_position, target_entity.get_id()) + if path is not None and len(path) > 0: + return ActionMove(path) + + return None + + def calc_unload( + self, + agent: AmbulanceTeamEntity, + path_planning: PathPlanning, + transport_human: Optional[Human], + target_id: Optional[EntityID], + ) -> Optional[ActionMove | ActionUnload | ActionRest]: + if transport_human is None: + return None + + if not transport_human.get_hp() or transport_human.get_hp() == 0: + return ActionUnload() + + agent_position = agent.get_position() + if ( + target_id is None + or transport_human.get_id().get_value() == target_id.get_value() + ): + position = self.world_info.get_entity(agent_position) + if position is None: + return None + + if isinstance(position, Refuge): + return ActionUnload() + else: + path = path_planning.get_path( + agent_position, self.world_info.get_entity_ids_of_type(Refuge) + ) + if path is not None and len(path) > 0: + return ActionMove(path) + + if target_id is None: + return None + + target_entity = self.world_info.get_entity(target_id) + + if isinstance(target_entity, Human): + human = cast(Human, target_entity) + if human.get_position() is not None: + return self.calc_refuge_action( + agent, path_planning, [human.get_position()], True + ) + path = self.get_nearest_refuge_path(agent, path_planning) + if path is not None and len(path) > 0: + return ActionMove(path) + + return None + + def calc_refuge_action( + self, + human: Human, + path_planning: PathPlanning, + target_entity_id: EntityID, + is_unload: bool, + ) -> Optional[ActionMove | ActionUnload | ActionRest]: + position = human.get_position() + refuges = self.world_info.get_entity_ids_of_type(Refuge) + size = len(refuges) + + if position in refuges: + return ActionUnload() if is_unload else ActionRest() + + first_result = None + while len(refuges) > 0: + path = path_planning.get_path(position, refuges[0]) + + if path is not None and len(path) > 0: + if first_result is None: + first_result = path.copy() + + refuge_id = path[-1] + from_refuge_to_target = path_planning.get_path( + refuge_id, target_entity_id + ) + + if from_refuge_to_target is not None and len(from_refuge_to_target) > 0: + return ActionMove(path) + + refuges.remove(refuge_id) + if size == len(refuges): + break + size = len(refuges) + else: + break + + return ActionMove(first_result) if first_result is not None else None + + def get_nearest_refuge_path( + self, human: Human, path_planning: PathPlanning + ) -> list[EntityID]: + position = human.get_position() + refuges = self.world_info.get_entity_ids_of_type(Refuge) + nearest_path = None + + for refuge_id in refuges: + path: list[EntityID] = path_planning.get_path(position, refuge_id) + if len(path) > 0: + if nearest_path is None or len(path) < len(nearest_path): + nearest_path = path + + return nearest_path if nearest_path is not None else []