From 5cdc74af5b7a423445307b1eb5a49d0e0e4dc186 Mon Sep 17 00:00:00 2001 From: shima004 Date: Thu, 12 Dec 2024 19:22:04 +0900 Subject: [PATCH 1/4] feat: Add CommandExecutor, CommandPicker, and DefaultCommandPickerPolice classes for centralized command handling --- .../component/centralized/command_executor.py | 93 ++++++++++++++++++ .../component/centralized/command_picker.py | 95 +++++++++++++++++++ .../default_command_picker_ambulance.py | 88 +++++++++++++++++ .../default_command_picker_fire.py | 88 +++++++++++++++++ .../default_command_picker_police.py | 80 ++++++++++++++++ 5 files changed, 444 insertions(+) create mode 100644 adf_core_python/core/component/centralized/command_executor.py create mode 100644 adf_core_python/core/component/centralized/command_picker.py create mode 100644 adf_core_python/implement/centralized/default_command_picker_ambulance.py create mode 100644 adf_core_python/implement/centralized/default_command_picker_fire.py create mode 100644 adf_core_python/implement/centralized/default_command_picker_police.py diff --git a/adf_core_python/core/component/centralized/command_executor.py b/adf_core_python/core/component/centralized/command_executor.py new file mode 100644 index 00000000..80f70c50 --- /dev/null +++ b/adf_core_python/core/component/centralized/command_executor.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, Optional, TypeVar + +from adf_core_python.core.agent.action.action import Action +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 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.component.communication.communication_message import ( + CommunicationMessage, +) + +T = TypeVar("T", bound=CommunicationMessage) + + +class CommandExecutor(ABC, Generic[T]): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_manager = module_manager + self._develop_data = develop_data + + self._result: Optional[Action] = None + + self._count_precompute: int = 0 + self._count_prepare: int = 0 + self._count_resume: int = 0 + self._count_update_info: int = 0 + self._count_update_info_current_time: int = 0 + + @abstractmethod + def set_command(self, command: T) -> CommandExecutor: + pass + + @abstractmethod + def calculate(self) -> CommandExecutor: + pass + + def get_action(self) -> Optional[Action]: + return self._result + + def precompute(self) -> CommandExecutor: + self._count_precompute += 1 + return self + + def prepare(self) -> CommandExecutor: + self._count_prepare += 1 + return self + + def resume(self) -> CommandExecutor: + self._count_resume += 1 + return self + + def update_info(self) -> CommandExecutor: + if self._count_update_info_current_time != self._agent_info.get_time(): + self._count_update_info_current_time = self._agent_info.get_time() + self._count_update_info += 1 + return self + + def get_count_precompute(self) -> int: + return self._count_precompute + + def get_count_prepare(self) -> int: + return self._count_prepare + + def get_count_resume(self) -> int: + return self._count_resume + + def get_count_update_info(self) -> int: + return self._count_update_info + + def reset_count_precompute(self) -> None: + self._count_precompute = 0 + + def reset_count_prepare(self) -> None: + self._count_prepare = 0 + + def reset_count_resume(self) -> None: + self._count_resume = 0 + + def reset_count_update_info(self) -> None: + self._count_update_info = 0 diff --git a/adf_core_python/core/component/centralized/command_picker.py b/adf_core_python/core/component/centralized/command_picker.py new file mode 100644 index 00000000..6e84e52a --- /dev/null +++ b/adf_core_python/core/component/centralized/command_picker.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + +from rcrs_core.worldmodel.entityID import EntityID + +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 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.component.communication.communication_message import ( + CommunicationMessage, +) + + +class CommandPicker(ABC): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + self._agent_info = agent_info + self._world_info = world_info + self._scenario_info = scenario_info + self._module_manager = module_manager + self._develop_data = develop_data + self._count_precompute: int = 0 + self._count_prepare: int = 0 + self._count_resume: int = 0 + self._count_update_info: int = 0 + self._count_update_info_current_time: int = 0 + + @abstractmethod + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + pass + + @abstractmethod + def calculate(self) -> CommandPicker: + pass + + @abstractmethod + def get_result(self) -> list[CommunicationMessage]: + pass + + def precompute(self) -> CommandPicker: + self._count_precompute += 1 + return self + + def prepare(self) -> CommandPicker: + self._count_prepare += 1 + return self + + def resume(self) -> CommandPicker: + self._count_resume += 1 + return self + + def update_info(self) -> CommandPicker: + if self._count_update_info_current_time != self._agent_info.get_time(): + self._count_update_info_current_time = self._agent_info.get_time() + self._count_update_info = 0 + self._count_update_info += 1 + return self + + def get_count_precompute(self) -> int: + return self._count_precompute + + def get_count_prepare(self) -> int: + return self._count_prepare + + def get_count_resume(self) -> int: + return self._count_resume + + def get_count_update_info(self) -> int: + return self._count_update_info + + def get_count_update_info_current_time(self) -> int: + return self._count_update_info_current_time + + def reset_count_precompute(self) -> None: + self._count_precompute = 0 + + def reset_count_prepare(self) -> None: + self._count_prepare = 0 + + def reset_count_resume(self) -> None: + self._count_resume = 0 + + def reset_count_update_info(self) -> None: + self._count_update_info = 0 diff --git a/adf_core_python/implement/centralized/default_command_picker_ambulance.py b/adf_core_python/implement/centralized/default_command_picker_ambulance.py new file mode 100644 index 00000000..4092b8ce --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_picker_ambulance.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from rcrs_core.entities.ambulanceTeam import AmbulanceTeam +from rcrs_core.entities.area import Area +from rcrs_core.entities.human import Human +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerAmbulance(CommandPicker): + 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.messages: list[CommunicationMessage] = [] + self.scout_distance: int = 40000 + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, AmbulanceTeam): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + if isinstance(target, Human): + command = CommandAmbulance( + True, + ambulance_id, + agent.get_id(), + CommandAmbulance.ACTION_RESCUE, + StandardMessagePriority.NORMAL, + target.get_id(), + ) + self.messages.append(command) + + if isinstance(target, Area): + command = CommandScout( + True, + ambulance_id, + agent.get_id(), + self.scout_distance, + StandardMessagePriority.NORMAL, + target.get_id(), + ) + self.messages.append(command) + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages diff --git a/adf_core_python/implement/centralized/default_command_picker_fire.py b/adf_core_python/implement/centralized/default_command_picker_fire.py new file mode 100644 index 00000000..99f5b720 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_picker_fire.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from rcrs_core.entities.area import Area +from rcrs_core.entities.fireBrigade import FireBrigade +from rcrs_core.entities.human import Human +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerFire(CommandPicker): + 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.messages: list[CommunicationMessage] = [] + self.scout_distance: int = 40000 + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, FireBrigade): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + if isinstance(target, Human): + command = CommandAmbulance( + True, + ambulance_id, + agent.get_id(), + CommandAmbulance.ACTION_RESCUE, + StandardMessagePriority.NORMAL, + target.get_id(), + ) + self.messages.append(command) + + if isinstance(target, Area): + command = CommandScout( + True, + ambulance_id, + agent.get_id(), + self.scout_distance, + StandardMessagePriority.NORMAL, + target.get_id(), + ) + self.messages.append(command) + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages diff --git a/adf_core_python/implement/centralized/default_command_picker_police.py b/adf_core_python/implement/centralized/default_command_picker_police.py new file mode 100644 index 00000000..9f2d0440 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_picker_police.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +from rcrs_core.entities.area import Area +from rcrs_core.entities.human import Human +from rcrs_core.entities.policeForce import PoliceForce +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.component.centralized.command_picker import CommandPicker +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class DefaultCommandPickerPolice(CommandPicker): + 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.messages: list[CommunicationMessage] = [] + self.allocation_data: dict[EntityID, EntityID] = {} + + def set_allocator_result( + self, allocation_data: dict[EntityID, EntityID] + ) -> CommandPicker: + self.allocation_data = allocation_data + return self + + def calculate(self) -> CommandPicker: + self.messages.clear() + if not self.allocation_data: + return self + + for ambulance_id in self.allocation_data.keys(): + agent = self._world_info.get_entity(ambulance_id) + if agent is None or not isinstance(agent, PoliceForce): + continue + + target = self._world_info.get_entity(self.allocation_data[ambulance_id]) + if target is None: + continue + + if isinstance(target, Area): + # TODO: Implement the command + # command = CommandPolice( + # True, + # agent.get_id(), + + # target.get_id(), + # StandardMessagePriority.NORMAL, + # ) + # self.messages.append(command) + pass + return self + + def get_result(self) -> list[CommunicationMessage]: + return self.messages From 10f26d652a80cddab7c846f705b66447c1c7e9cc Mon Sep 17 00:00:00 2001 From: shima004 Date: Mon, 16 Dec 2024 01:09:40 +0900 Subject: [PATCH 2/4] fix: Update agent ID retrieval in command picker classes for consistency --- .../default_command_picker_ambulance.py | 4 ++-- .../default_command_picker_fire.py | 4 ++-- .../default_command_picker_police.py | 19 +++++++++---------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/adf_core_python/implement/centralized/default_command_picker_ambulance.py b/adf_core_python/implement/centralized/default_command_picker_ambulance.py index 4092b8ce..9af95233 100644 --- a/adf_core_python/implement/centralized/default_command_picker_ambulance.py +++ b/adf_core_python/implement/centralized/default_command_picker_ambulance.py @@ -65,7 +65,7 @@ def calculate(self) -> CommandPicker: command = CommandAmbulance( True, ambulance_id, - agent.get_id(), + self._agent_info.get_entity_id(), CommandAmbulance.ACTION_RESCUE, StandardMessagePriority.NORMAL, target.get_id(), @@ -76,7 +76,7 @@ def calculate(self) -> CommandPicker: command = CommandScout( True, ambulance_id, - agent.get_id(), + self._agent_info.get_entity_id(), self.scout_distance, StandardMessagePriority.NORMAL, target.get_id(), diff --git a/adf_core_python/implement/centralized/default_command_picker_fire.py b/adf_core_python/implement/centralized/default_command_picker_fire.py index 99f5b720..435d9316 100644 --- a/adf_core_python/implement/centralized/default_command_picker_fire.py +++ b/adf_core_python/implement/centralized/default_command_picker_fire.py @@ -65,7 +65,7 @@ def calculate(self) -> CommandPicker: command = CommandAmbulance( True, ambulance_id, - agent.get_id(), + self._agent_info.get_entity_id(), CommandAmbulance.ACTION_RESCUE, StandardMessagePriority.NORMAL, target.get_id(), @@ -76,7 +76,7 @@ def calculate(self) -> CommandPicker: command = CommandScout( True, ambulance_id, - agent.get_id(), + self._agent_info.get_entity_id(), self.scout_distance, StandardMessagePriority.NORMAL, target.get_id(), diff --git a/adf_core_python/implement/centralized/default_command_picker_police.py b/adf_core_python/implement/centralized/default_command_picker_police.py index 9f2d0440..75b7ef7b 100644 --- a/adf_core_python/implement/centralized/default_command_picker_police.py +++ b/adf_core_python/implement/centralized/default_command_picker_police.py @@ -64,16 +64,15 @@ def calculate(self) -> CommandPicker: continue if isinstance(target, Area): - # TODO: Implement the command - # command = CommandPolice( - # True, - # agent.get_id(), - - # target.get_id(), - # StandardMessagePriority.NORMAL, - # ) - # self.messages.append(command) - pass + command = CommandPolice( + True, + agent.get_id(), + self._agent_info.get_entity_id(), + CommandPolice.ACTION_AUTONOMY, + StandardMessagePriority.NORMAL, + target.get_id(), + ) + self.messages.append(command) return self def get_result(self) -> list[CommunicationMessage]: From 524b2a9b0c3dd76d79a5fc42d4d14d382089a5ec Mon Sep 17 00:00:00 2001 From: shima004 Date: Wed, 18 Dec 2024 01:50:45 +0900 Subject: [PATCH 3/4] feat: Integrate CommandPicker into TacticsCenter and related classes for improved command handling --- .../core/agent/module/module_manager.py | 54 ++++ .../component/centralized/command_executor.py | 22 +- .../component/centralized/command_picker.py | 21 +- .../core/component/tactics/tactics_center.py | 4 +- .../default_command_executor_ambulance.py | 302 ++++++++++++++++++ .../default_command_executor_fire.py | 243 ++++++++++++++ .../default_command_executor_police.py | 261 +++++++++++++++ .../default_command_executor_scout.py | 158 +++++++++ .../default_command_executor_scout_police.py | 170 ++++++++++ .../default_tactics_ambulance_center.py | 21 +- .../tactics/default_tactics_ambulance_team.py | 51 ++- .../tactics/default_tactics_fire_brigade.py | 51 ++- .../tactics/default_tactics_fire_station.py | 21 +- .../tactics/default_tactics_police_force.py | 54 +++- .../tactics/default_tactics_police_office.py | 21 +- config/module.yaml | 53 ++- 16 files changed, 1453 insertions(+), 54 deletions(-) create mode 100644 adf_core_python/implement/centralized/default_command_executor_ambulance.py create mode 100644 adf_core_python/implement/centralized/default_command_executor_fire.py create mode 100644 adf_core_python/implement/centralized/default_command_executor_police.py create mode 100644 adf_core_python/implement/centralized/default_command_executor_scout.py create mode 100644 adf_core_python/implement/centralized/default_command_executor_scout_police.py diff --git a/adf_core_python/core/agent/module/module_manager.py b/adf_core_python/core/agent/module/module_manager.py index 18346aa6..0d5695cb 100644 --- a/adf_core_python/core/agent/module/module_manager.py +++ b/adf_core_python/core/agent/module/module_manager.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Any, Optional from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.centralized.command_picker import CommandPicker from adf_core_python.core.component.communication.channel_subscriber import ( ChannelSubscriber, ) @@ -189,6 +191,58 @@ def get_message_coordinator( f"Message coordinator {class_name} is not a subclass of MessageCoordinator" ) + def get_command_executor( + self, command_executor_name: str, default_command_executor_name: str + ) -> CommandExecutor: + class_name = self._module_config.get_value_or_default( + command_executor_name, default_command_executor_name + ) + + command_executor_class: type = self._load_module(class_name) + + instance = self._executors.get(command_executor_name) + if instance is not None: + return instance + + if issubclass(command_executor_class, CommandExecutor): + instance = command_executor_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._executors[command_executor_name] = instance + return instance + + raise RuntimeError(f"Command executor {class_name} is not a subclass of object") + + def get_command_picker( + self, command_picker_name: str, default_command_picker_name: str + ) -> CommandPicker: + class_name = self._module_config.get_value_or_default( + command_picker_name, default_command_picker_name + ) + + command_picker_class: type = self._load_module(class_name) + + instance = self._pickers.get(command_picker_name) + if instance is not None: + return instance + + if issubclass(command_picker_class, CommandPicker): + instance = command_picker_class( + self._agent_info, + self._world_info, + self._scenario_info, + self, + self._develop_data, + ) + self._pickers[command_picker_name] = instance + return instance + + raise RuntimeError(f"Command picker {class_name} is not a subclass of object") + def _load_module(self, class_name: str) -> type: module_name, module_class_name = class_name.rsplit(".", 1) module = importlib.import_module(module_name) diff --git a/adf_core_python/core/component/centralized/command_executor.py b/adf_core_python/core/component/centralized/command_executor.py index 80f70c50..ebd501fb 100644 --- a/adf_core_python/core/component/centralized/command_executor.py +++ b/adf_core_python/core/component/centralized/command_executor.py @@ -1,14 +1,18 @@ from __future__ import annotations from abc import ABC, abstractmethod -from typing import Generic, Optional, TypeVar +from typing import TYPE_CHECKING, Generic, Optional, TypeVar + +if TYPE_CHECKING: + 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 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.agent.action.action import Action -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 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.component.communication.communication_message import ( CommunicationMessage, ) @@ -50,7 +54,7 @@ def calculate(self) -> CommandExecutor: def get_action(self) -> Optional[Action]: return self._result - def precompute(self) -> CommandExecutor: + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: self._count_precompute += 1 return self @@ -58,11 +62,11 @@ def prepare(self) -> CommandExecutor: self._count_prepare += 1 return self - def resume(self) -> CommandExecutor: + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: self._count_resume += 1 return self - def update_info(self) -> CommandExecutor: + def update_info(self, message_manager: MessageManager) -> CommandExecutor: if self._count_update_info_current_time != self._agent_info.get_time(): self._count_update_info_current_time = self._agent_info.get_time() self._count_update_info += 1 diff --git a/adf_core_python/core/component/centralized/command_picker.py b/adf_core_python/core/component/centralized/command_picker.py index 6e84e52a..19c869a4 100644 --- a/adf_core_python/core/component/centralized/command_picker.py +++ b/adf_core_python/core/component/centralized/command_picker.py @@ -1,14 +1,19 @@ from __future__ import annotations from abc import ABC, abstractmethod +from typing import TYPE_CHECKING from rcrs_core.worldmodel.entityID import EntityID -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 ScenarioInfo -from adf_core_python.core.agent.info.world_info import WorldInfo -from adf_core_python.core.agent.module.module_manager import ModuleManager +if TYPE_CHECKING: + 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 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.communication.message_manager import MessageManager +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData from adf_core_python.core.component.communication.communication_message import ( CommunicationMessage, ) @@ -48,7 +53,7 @@ def calculate(self) -> CommandPicker: def get_result(self) -> list[CommunicationMessage]: pass - def precompute(self) -> CommandPicker: + def precompute(self, precompute_data: PrecomputeData) -> CommandPicker: self._count_precompute += 1 return self @@ -56,11 +61,11 @@ def prepare(self) -> CommandPicker: self._count_prepare += 1 return self - def resume(self) -> CommandPicker: + def resume(self, precompute_data: PrecomputeData) -> CommandPicker: self._count_resume += 1 return self - def update_info(self) -> CommandPicker: + def update_info(self, message_manager: MessageManager) -> CommandPicker: if self._count_update_info_current_time != self._agent_info.get_time(): self._count_update_info_current_time = self._agent_info.get_time() self._count_update_info = 0 diff --git a/adf_core_python/core/component/tactics/tactics_center.py b/adf_core_python/core/component/tactics/tactics_center.py index 2469cc6e..0e76a797 100644 --- a/adf_core_python/core/component/tactics/tactics_center.py +++ b/adf_core_python/core/component/tactics/tactics_center.py @@ -3,6 +3,8 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Optional +from adf_core_python.core.component.centralized.command_picker import CommandPicker + if TYPE_CHECKING: from adf_core_python.core.agent.communication.message_manager import MessageManager from adf_core_python.core.agent.develop.develop_data import DevelopData @@ -18,7 +20,7 @@ class TacticsCenter(ABC): def __init__(self, parent: Optional[TacticsCenter] = None) -> None: self._parent = parent self._modules: list[AbstractModule] = [] - self._command_pickers: list[Any] = [] + self._command_pickers: list[CommandPicker] = [] @abstractmethod def initialize( diff --git a/adf_core_python/implement/centralized/default_command_executor_ambulance.py b/adf_core_python/implement/centralized/default_command_executor_ambulance.py new file mode 100644 index 00000000..f2c00d61 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_executor_ambulance.py @@ -0,0 +1,302 @@ +from typing import Optional, cast + +from rcrs_core.entities.ambulanceTeam import AmbulanceTeam +from rcrs_core.entities.area import Area +from rcrs_core.entities.civilian import Civilian +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.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.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorAmbulance(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandAmbulance.ACTION_REST + ACTION_MOVE = CommandAmbulance.ACTION_MOVE + ACTION_RESCUE = CommandAmbulance.ACTION_RESCUE + ACTION_LOAD = CommandAmbulance.ACTION_LOAD + ACTION_UNLOAD = CommandAmbulance.ACTION_UNLOAD + ACTION_AUTONOMY = CommandAmbulance.ACTION_AUTONOMY + + 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._command_type = self.ACTION_UNKNOWN + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorAmbulance.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_transport = module_manager.get_extend_action( + "DefaultCommandExecutorAmbulance.ExtActionTransport", + "adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorAmbulance.ExtActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandAmbulance) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_RESCUE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_LOAD: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_UNLOAD: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_transport.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_transport.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_transport.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + + if self._command_type == self.ACTION_LOAD: + self._command_type = self.ACTION_UNLOAD + self._target = None + else: + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_transport.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_transport.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_transport.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + return agent.get_damage() == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + return agent.get_damage() == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_RESCUE: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + return human.get_buriedness() == 0 or human.get_hp() == 0 + case self.ACTION_LOAD: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + if human.get_hp() == 0: + return True + if isinstance(human, Civilian): + self._command_type = self.ACTION_RESCUE + return self._is_command_completed() + if human.get_position() is not None: + position = human.get_position() + if position in self._world_info.get_entity_ids_of_types( + [AmbulanceTeam] + ): + return True + elif isinstance(self._world_info.get_entity(position), Refuge): + return True + return False + + case self.ACTION_UNLOAD: + if self._target is not None: + entity = self._world_info.get_entity(self._target) + if entity is not None and isinstance(entity, Refuge): + if self._target == self._agent_info.get_position_entity_id(): + return False + return self._agent_info.some_one_on_board() is None + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + self._command_type = ( + self._agent_info.some_one_on_board() is None + and self.ACTION_MOVE + or self.ACTION_UNLOAD + ) + return self._is_command_completed() + elif isinstance(target_entity, Human): + human = target_entity + if human.get_hp() == 0: + return True + self._command_type = ( + isinstance(human, Civilian) + and self.ACTION_LOAD + or self.ACTION_RESCUE + ) + return self._is_command_completed() + return True + case _: + return True diff --git a/adf_core_python/implement/centralized/default_command_executor_fire.py b/adf_core_python/implement/centralized/default_command_executor_fire.py new file mode 100644 index 00000000..abd2ebe8 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_executor_fire.py @@ -0,0 +1,243 @@ +from typing import Optional, cast + +from rcrs_core.entities.area import Area +from rcrs_core.entities.civilian import Civilian +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.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.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorFire(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandAmbulance.ACTION_REST + ACTION_MOVE = CommandAmbulance.ACTION_MOVE + ACTION_RESCUE = CommandAmbulance.ACTION_RESCUE + ACTION_AUTONOMY = CommandAmbulance.ACTION_AUTONOMY + + 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._command_type = self.ACTION_UNKNOWN + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorFire.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_fire_rescue = module_manager.get_extend_action( + "DefaultCommandExecutorFire.ExtActionFireRescue", + "adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorFire.ExtActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandAmbulance) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_RESCUE: + if self._target: + self._result = ( + self._action_fire_rescue.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_fire_rescue.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_fire_rescue.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_fire_rescue.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_fire_rescue.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_fire_rescue.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + return agent.get_damage() == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + return agent.get_damage() == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_RESCUE: + if self._target is None: + return True + human = self._world_info.get_entity(self._target) + if not isinstance(human, Human): + return True + return human.get_buriedness() == 0 or human.get_hp() == 0 + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + self._command_type = self.ACTION_MOVE + return self._is_command_completed() + elif isinstance(target_entity, Human): + human = target_entity + if human.get_hp() == 0: + return True + if isinstance(human, Civilian): + self._command_type = self.ACTION_RESCUE + return self._is_command_completed() + return True + case _: + return True diff --git a/adf_core_python/implement/centralized/default_command_executor_police.py b/adf_core_python/implement/centralized/default_command_executor_police.py new file mode 100644 index 00000000..aecd5744 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_executor_police.py @@ -0,0 +1,261 @@ +from typing import Optional, cast + +from rcrs_core.entities.area import Area +from rcrs_core.entities.blockade import Blockade +from rcrs_core.entities.human import Human +from rcrs_core.entities.refuge import Refuge +from rcrs_core.entities.road import Road +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.action.common.action_rest import ActionRest +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorPolice(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_REST = CommandPolice.ACTION_REST + ACTION_MOVE = CommandPolice.ACTION_MOVE + ACTION_CLEAR = CommandPolice.ACTION_CLEAR + ACTION_AUTONOMY = CommandPolice.ACTION_AUTONOMY + + 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._command_type = self.ACTION_UNKNOWN + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorPolice.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_clear = module_manager.get_extend_action( + "DefaultCommandExecutorPolice.ExtActionClear", + "adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear", + ) + self._action_move = module_manager.get_extend_action( + "DefaultCommandExecutorPolice.ExtActionMove", + "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._target: Optional[EntityID] = None + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandPolice) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + self._command_type = command.get_execute_action() or self.ACTION_UNKNOWN + self._target = command.get_command_target_entity_id() + self._commander = command.get_sender_entity_id() + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_REST: + position = self._agent_info.get_entity_id() + if self._target is None: + refuges = self._world_info.get_entity_ids_of_types([Refuge]) + if position in refuges: + self._result = ActionRest() + else: + path = self._path_planning.get_path(position, refuges[0]) + if path: + self._result = ActionMove(path) + else: + self._result = ActionRest() + return self + if position != self._target: + path = self._path_planning.get_path(position, self._target) + if path: + self._result = ActionMove(path) + return self + self._result = ActionRest() + return self + case self.ACTION_MOVE: + if self._target: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_CLEAR: + if self._target: + self._result = ( + self._action_clear.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + case self.ACTION_AUTONOMY: + if self._target is None: + return self + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Area): + if self._agent_info.some_one_on_board() is None: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + else: + self._result = ( + self._action_move.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + elif isinstance(target_entity, Human): + self._result = ( + self._action_clear.set_target_entity_id(self._target) + .calculate() + .get_action() + ) + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + self._action_clear.update_info(message_manager) + self._action_move.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + self._action_clear.precompute(precompute_data) + self._action_move.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + self._action_clear.resume(precompute_data) + self._action_move.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + self._action_clear.prepare() + self._action_move.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_REST: + if self._target is None: + return agent.get_damage() == 0 + if (target_entity := self._world_info.get_entity(self._target)) is None: + return False + if isinstance(target_entity, Refuge): + return agent.get_damage() == 0 + return False + case self.ACTION_MOVE: + return ( + self._target is None + or self._agent_info.get_position_entity_id() == self._target + ) + case self.ACTION_CLEAR: + if self._target is None: + return True + entity = self._world_info.get_entity(self._target) + if isinstance(entity, Road): + if entity.get_blockades is not None: + return len(entity.get_blockades()) == 0 + return self._agent_info.get_position_entity_id() == self._target + return True + case self.ACTION_AUTONOMY: + if self._target is not None: + target_entity = self._world_info.get_entity(self._target) + if isinstance(target_entity, Refuge): + self._command_type = ( + self.ACTION_REST + if agent.get_damage() > 0 + else self.ACTION_CLEAR + ) + return self._is_command_completed() + elif isinstance(target_entity, Area): + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + elif isinstance(target_entity, Human): + if target_entity.get_hp() == 0: + return True + if target_entity.get_position() is not None and isinstance( + self._world_info.get_entity(target_entity.get_position()), + Area, + ): + self._target = target_entity.get_position() + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + elif isinstance(target_entity, Blockade): + if target_entity.get_position() is not None: + self._target = target_entity.get_position() + self._command_type = self.ACTION_CLEAR + return self._is_command_completed() + return True + case _: + return True diff --git a/adf_core_python/implement/centralized/default_command_executor_scout.py b/adf_core_python/implement/centralized/default_command_executor_scout.py new file mode 100644 index 00000000..11861554 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_executor_scout.py @@ -0,0 +1,158 @@ +from typing import Optional, cast + +from rcrs_core.entities.area import Area +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.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorScout(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_SCOUT: int = 1 + + 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._command_type = self.ACTION_UNKNOWN + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorScout.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._targets: list[EntityID] = [] + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandScout) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + target = command.get_command_target_entity_id() + if target is None: + target = self._agent_info.get_position_entity_id() + + self._command_type = self.ACTION_SCOUT + self._commander = command.get_sender_entity_id() + self._targets = [] + if (scout_distance := command.get_scout_range()) is None: + return self + + for entity in self._world_info.get_entities_of_types([Area]): + if isinstance(entity, Refuge): + continue + if self._world_info.get_distance(target, entity.get_id()) <= scout_distance: + self._targets.append(entity.get_id()) + + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) == 0: + return self + path = self._path_planning.get_path( + self._agent_info.get_position_entity_id(), self._targets[0] + ) + if path is None: + return self + self._result = ActionMove(path) + case _: + return self + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) != 0: + for entity in self._world_info.get_entities_of_types([Area]): + self._targets.remove(entity.get_id()) + return len(self._targets) == 0 + case _: + return True diff --git a/adf_core_python/implement/centralized/default_command_executor_scout_police.py b/adf_core_python/implement/centralized/default_command_executor_scout_police.py new file mode 100644 index 00000000..01f44c90 --- /dev/null +++ b/adf_core_python/implement/centralized/default_command_executor_scout_police.py @@ -0,0 +1,170 @@ +from typing import Optional, cast + +from rcrs_core.entities.area import Area +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.common.action_move import ActionMove +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +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 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.action.extend_action import ExtendAction +from adf_core_python.core.component.centralized.command_executor import CommandExecutor +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning + + +class DefaultCommandExecutorScoutPolice(CommandExecutor): + ACTION_UNKNOWN: int = -1 + ACTION_SCOUT: int = 1 + + 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._command_type = self.ACTION_UNKNOWN + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultCommandExecutorScoutPolice.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + self._action_clear: ExtendAction = module_manager.get_extend_action( + "DefaultCommandExecutorScoutPolice.ExtActionClear", + "adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear", + ) + + self._command_type: int = self.ACTION_UNKNOWN + self._targets: list[EntityID] = [] + self._commander: Optional[EntityID] = None + + def set_command(self, command: CommandScout) -> CommandExecutor: + agent_id: EntityID = self._agent_info.get_entity_id() + if command.get_command_executor_agent_entity_id() != agent_id: + return self + + target = command.get_command_target_entity_id() + if target is None: + target = self._agent_info.get_position_entity_id() + + self._command_type = self.ACTION_SCOUT + self._commander = command.get_sender_entity_id() + self._targets = [] + if (scout_distance := command.get_scout_range()) is None: + return self + + for entity in self._world_info.get_entities_of_types([Area]): + if isinstance(entity, Refuge): + continue + if self._world_info.get_distance(target, entity.get_id()) <= scout_distance: + self._targets.append(entity.get_id()) + + return self + + def calculate(self) -> CommandExecutor: + self._result = None + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) == 0: + return self + path = self._path_planning.get_path( + self._agent_info.get_position_entity_id(), self._targets[0] + ) + if path is None: + return self + action = ( + self._action_clear.set_target_entity_id(self._targets[0]) + .calculate() + .get_action() + ) + if action is None: + action = ActionMove(path) + self._result = action + case _: + return self + return self + + def update_info(self, message_manager: MessageManager) -> CommandExecutor: + super().update_info(message_manager) + if self.get_count_update_info() >= 2: + return self + + self._path_planning.update_info(message_manager) + + if self._is_command_completed(): + if self._command_type == self.ACTION_UNKNOWN: + return self + if self._commander is None: + return self + + message_manager.add_message( + MessageReport( + True, + True, + False, + self._commander, + StandardMessagePriority.NORMAL, + ) + ) + self._command_type = self.ACTION_UNKNOWN + self._target = None + self._commander = None + + return self + + def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().precompute(precompute_data) + if self.get_count_precompute() >= 2: + return self + self._path_planning.precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> CommandExecutor: + super().resume(precompute_data) + if self.get_count_resume() >= 2: + return self + self._path_planning.resume(precompute_data) + return self + + def prepare(self) -> CommandExecutor: + super().prepare() + if self.get_count_prepare() >= 2: + return self + self._path_planning.prepare() + return self + + def _is_command_completed(self) -> bool: + agent = self._agent_info.get_myself() + if not isinstance(agent, Human): + return False + + match self._command_type: + case self.ACTION_SCOUT: + if len(self._targets) != 0: + for entity in self._world_info.get_entities_of_types([Area]): + self._targets.remove(entity.get_id()) + return len(self._targets) == 0 + case _: + return True diff --git a/adf_core_python/implement/tactics/default_tactics_ambulance_center.py b/adf_core_python/implement/tactics/default_tactics_ambulance_center.py index d1dd2485..23a9d85e 100644 --- a/adf_core_python/implement/tactics/default_tactics_ambulance_center.py +++ b/adf_core_python/implement/tactics/default_tactics_ambulance_center.py @@ -1,5 +1,7 @@ from typing import cast +from rcrs_core.worldmodel.entityID import EntityID + 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 @@ -7,6 +9,7 @@ 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.centralized.command_picker import CommandPicker from adf_core_python.core.component.module.complex.target_allocator import ( TargetAllocator, ) @@ -33,7 +36,12 @@ def initialize( "adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator", ), ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsAmbulanceCenter.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_ambulance.DefaultCommandPickerAmbulance", + ) self.register_module(self._allocator) + self.register_command_picker(self._picker) def precompute( self, @@ -80,5 +88,14 @@ def think( message_manager: MessageManager, develop_data: DevelopData, ) -> None: - # TODO: implement - pass + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result) + .calculate() + .get_result() + ): + message_manager.add_message(message) diff --git a/adf_core_python/implement/tactics/default_tactics_ambulance_team.py b/adf_core_python/implement/tactics/default_tactics_ambulance_team.py index 2ae71587..8e7df9cd 100644 --- a/adf_core_python/implement/tactics/default_tactics_ambulance_team.py +++ b/adf_core_python/implement/tactics/default_tactics_ambulance_team.py @@ -1,10 +1,19 @@ -from typing import cast +from typing import Optional, cast from rcrs_core.entities.ambulanceTeam import AmbulanceTeam from adf_core_python.core.agent.action.action import Action 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.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) 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 ScenarioInfo @@ -62,11 +71,23 @@ def initialize( "DefaultTacticsAmbulanceTeam.ExtendActionMove", "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", ) + self._command_executor_ambulance = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorAmbulance", + "adf_core_python.implement.centralized.default_command_executor_ambulance.DefaultCommandExecutorAmbulance", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout", + ) self.register_module(self._search) self.register_module(self._human_detector) self.register_action(self._action_transport) self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_ambulance) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None def precompute( self, @@ -124,6 +145,34 @@ def think( message_manager=message_manager, ) + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_scout.set_command(message) + if isinstance(message, CommandAmbulance): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_ambulance.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandAmbulance): + action = self._command_executor_ambulance.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + target_entity_id = self._human_detector.calculate().get_target_entity_id() self._logger.debug( f"human detector target_entity_id: {target_entity_id}", diff --git a/adf_core_python/implement/tactics/default_tactics_fire_brigade.py b/adf_core_python/implement/tactics/default_tactics_fire_brigade.py index e4e09ec5..0d9740ed 100644 --- a/adf_core_python/implement/tactics/default_tactics_fire_brigade.py +++ b/adf_core_python/implement/tactics/default_tactics_fire_brigade.py @@ -1,10 +1,19 @@ -from typing import cast +from typing import Optional, cast from rcrs_core.entities.fireBrigade import FireBrigade from adf_core_python.core.agent.action.action import Action 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.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) 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 ScenarioInfo @@ -62,11 +71,23 @@ def initialize( "DefaultTacticsAmbulanceTeam.ExtendActionMove", "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", ) + self._command_executor_fire = module_manager.get_command_executor( + "DefaultTacticsFireBrigade.CommandExecutorFire", + "adf_core_python.implement.centralized.default_command_executor_fire.DefaultCommandExecutorFire", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsAmbulanceTeam.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout", + ) self.register_module(self._search) self.register_module(self._human_detector) self.register_action(self._action_rescue) self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_fire) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None def precompute( self, @@ -119,6 +140,34 @@ def think( agent: FireBrigade = cast(FireBrigade, agent_info.get_myself()) # noqa: F841 entity_id = agent_info.get_entity_id() # noqa: F841 + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_scout.set_command(command=message) + if isinstance(message, CommandFire): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_fire.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandFire): + action = self._command_executor_fire.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + target_entity_id = self._human_detector.calculate().get_target_entity_id() self._logger.debug( f"human detector target_entity_id: {target_entity_id}", diff --git a/adf_core_python/implement/tactics/default_tactics_fire_station.py b/adf_core_python/implement/tactics/default_tactics_fire_station.py index 6b3dcb2a..0b15a3e7 100644 --- a/adf_core_python/implement/tactics/default_tactics_fire_station.py +++ b/adf_core_python/implement/tactics/default_tactics_fire_station.py @@ -1,5 +1,7 @@ from typing import cast +from rcrs_core.worldmodel.entityID import EntityID + 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 @@ -7,6 +9,7 @@ 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.centralized.command_picker import CommandPicker from adf_core_python.core.component.module.complex.target_allocator import ( TargetAllocator, ) @@ -33,7 +36,12 @@ def initialize( "adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator", ), ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsFireStation.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_fire.DefaultCommandPickerFire", + ) self.register_module(self._allocator) + self.register_command_picker(self._picker) def resume( self, @@ -80,5 +88,14 @@ def think( message_manager: MessageManager, develop_data: DevelopData, ) -> None: - # TODO: implement - pass + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result) + .calculate() + .get_result() + ): + message_manager.add_message(message) diff --git a/adf_core_python/implement/tactics/default_tactics_police_force.py b/adf_core_python/implement/tactics/default_tactics_police_force.py index bec3434a..6a1f2f60 100644 --- a/adf_core_python/implement/tactics/default_tactics_police_force.py +++ b/adf_core_python/implement/tactics/default_tactics_police_force.py @@ -1,10 +1,22 @@ -from typing import cast +from typing import Optional, cast from rcrs_core.entities.policeForce import PoliceForce from adf_core_python.core.agent.action.action import Action 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.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) 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 ScenarioInfo @@ -65,11 +77,23 @@ def initialize( "DefaultTacticsPoliceForce.ExtendActionMove", "adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove", ) + self._command_executor_police = module_manager.get_command_executor( + "DefaultTacticsPoliceForce.CommandExecutorPolice", + "adf_core_python.implement.centralized.default_command_executor_police.DefaultCommandExecutorPolice", + ) + self._command_executor_scout = module_manager.get_command_executor( + "DefaultTacticsPoliceForce.CommandExecutorScout", + "adf_core_python.implement.centralized.default_command_executor_scout_police.DefaultCommandExecutorScoutPolice", + ) self.register_module(self._search) self.register_module(self._road_detector) self.register_action(self._action_ext_clear) self.register_action(self._action_ext_move) + self.register_command_executor(self._command_executor_police) + self.register_command_executor(self._command_executor_scout) + + self._recent_command: Optional[StandardMessage] = None def precompute( self, @@ -122,6 +146,34 @@ def think( agent: PoliceForce = cast(PoliceForce, agent_info.get_myself()) # noqa: F841 entity_id = agent_info.get_entity_id() # noqa: F841 + for message in message_manager.get_received_message_list(): + if isinstance(message, CommandScout): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_scout.set_command(command=message) + if isinstance(message, CommandPolice): + if ( + message.get_command_executor_agent_entity_id() + == agent_info.get_entity_id() + ): + self._recent_command = message + self._command_executor_police.set_command(message) + + if self._recent_command is not None: + action: Optional[Action] = None + if isinstance(self._recent_command, CommandScout): + action = self._command_executor_scout.calculate().get_action() + elif isinstance(self._recent_command, CommandFire): + action = self._command_executor_police.calculate().get_action() + if action is not None: + self._logger.debug( + f"action decided by command: {action}", time=agent_info.get_time() + ) + return action + target_entity_id = self._road_detector.calculate().get_target_entity_id() self._logger.debug( f"road detector target_entity_id: {target_entity_id}", diff --git a/adf_core_python/implement/tactics/default_tactics_police_office.py b/adf_core_python/implement/tactics/default_tactics_police_office.py index 554fe53e..df095ccd 100644 --- a/adf_core_python/implement/tactics/default_tactics_police_office.py +++ b/adf_core_python/implement/tactics/default_tactics_police_office.py @@ -1,5 +1,7 @@ from typing import cast +from rcrs_core.worldmodel.entityID import EntityID + 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 @@ -7,6 +9,7 @@ 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.centralized.command_picker import CommandPicker from adf_core_python.core.component.module.complex.target_allocator import ( TargetAllocator, ) @@ -33,7 +36,12 @@ def initialize( "adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator", ), ) + self._picker: CommandPicker = module_manager.get_command_picker( + "DefaultTacticsPoliceOffice.CommandPicker", + "adf_core_python.implement.centralized.default_command_picker_police.DefaultCommandPickerPolice", + ) self.register_module(self._allocator) + self.register_command_picker(self._picker) def resume( self, @@ -80,5 +88,14 @@ def think( message_manager: MessageManager, develop_data: DevelopData, ) -> None: - # TODO: implement - pass + self.module_update_info(message_manager) + + allocation_result: dict[EntityID, EntityID] = ( + self._allocator.calculate().get_result() + ) + for message in ( + self._picker.set_allocator_result(allocation_result) + .calculate() + .get_result() + ): + message_manager.add_message(message) diff --git a/config/module.yaml b/config/module.yaml index 75c15a0b..706c82ec 100644 --- a/config/module.yaml +++ b/config/module.yaml @@ -3,36 +3,36 @@ DefaultTacticsAmbulanceTeam: Search: adf_core_python.implement.module.complex.default_search.DefaultSearch ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove - CommandExecutorAmbulance: adf_core_python.implement.centralized.DefaultCommandExecutorAmbulance - CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + CommandExecutorAmbulance: adf_core_python.implement.centralized.default_command_executor_ambulance.DefaultCommandExecutorAmbulance + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout DefaultTacticsFireBrigade: HumanDetector: adf_core_python.implement.module.complex.default_human_detector.DefaultHumanDetector Search: adf_core_python.implement.module.complex.default_search.DefaultSearch ExtendActionRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove - CommandExecutorFire: adf_core_python.implement.centralized.DefaultCommandExecutorFire - CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScout + CommandExecutorFire: adf_core_python.implement.centralized.default_command_executor_fire.DefaultCommandExecutorFire + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout DefaultTacticsPoliceForce: RoadDetector: adf_core_python.implement.module.complex.default_road_detector.DefaultRoadDetector Search: adf_core_python.implement.module.complex.default_search.DefaultSearch ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove - CommandExecutorPolice: adf_core_python.implement.centralized.DefaultCommandExecutorPolice - CommandExecutorScout: adf_core_python.implement.centralized.DefaultCommandExecutorScoutPolice + CommandExecutorPolice: adf_core_python.implement.centralized.default_command_executor_police.DefaultCommandExecutorPolice + CommandExecutorScout: adf_core_python.implement.centralized.default_command_executor_scout.DefaultCommandExecutorScout DefaultTacticsAmbulanceCenter: TargetAllocator: adf_core_python.implement.module.complex.default_ambulance_target_allocator.DefaultAmbulanceTargetAllocator - # CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerAmbulance + CommandPicker: adf_core_python.implement.centralized.default_command_picker_ambulance.DefaultCommandPickerAmbulance DefaultTacticsFireStation: TargetAllocator: adf_core_python.implement.module.complex.default_fire_target_allocator.DefaultFireTargetAllocator - # CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerFire + CommandPicker: adf_core_python.implement.centralized.default_command_picker_fire.DefaultCommandPickerFire DefaultTacticsPoliceOffice: TargetAllocator: adf_core_python.implement.module.complex.default_police_target_allocator.DefaultPoliceTargetAllocator - # CommandPicker: adf_core_python.implement.centralized.DefaultCommandPickerPolice + CommandPicker: adf_core_python.implement.centralized.default_command_picker_police.DefaultCommandPickerPolice DefaultSearch: PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning @@ -56,28 +56,27 @@ DefaultExtendActionMove: DefaultExtendActionTransport: PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# DefaultCommandExecutorAmbulance: -# PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# ExtendActionTransport: adf_core_python.implement.action.DefaultExtendActionTransport -# ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove +DefaultCommandExecutorAmbulance: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionTransport: adf_core_python.implement.action.default_extend_action_transport.DefaultExtendActionTransport + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove -# DefaultCommandExecutorFire: -# PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# EtxActionFireRescue: adf_core_python.implement.action.DefaultExtendActionRescue -# EtxActionFireFighting: adf_core_python.implement.action.DefaultExtendActionFireFighting -# ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove +DefaultCommandExecutorFire: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + EtxActionFireRescue: adf_core_python.implement.action.default_extend_action_rescue.DefaultExtendActionRescue + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove -# DefaultCommandExecutorPolice: -# PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear -# ExtendActionMove: adf_core_python.implement.action.DefaultExtendActionMove +DefaultCommandExecutorPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear + ExtendActionMove: adf_core_python.implement.action.default_extend_action_move.DefaultExtendActionMove -# DefaultCommandExecutorScout: -# PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning +DefaultCommandExecutorScout: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# DefaultCommandExecutorScoutPolice: -# PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning -# ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear +DefaultCommandExecutorScoutPolice: + PathPlanning: adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning + ExtendActionClear: adf_core_python.implement.action.default_extend_action_clear.DefaultExtendActionClear MessageManager: PlatoonChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber From 4c933de821cb197032a367260b05025a28794179 Mon Sep 17 00:00:00 2001 From: shima004 Date: Wed, 18 Dec 2024 02:08:44 +0900 Subject: [PATCH 4/4] refactor: Remove unused command_type initialization in command executor classes --- .../centralized/default_command_executor_ambulance.py | 1 - .../implement/centralized/default_command_executor_fire.py | 1 - .../centralized/default_command_executor_police.py | 1 - .../centralized/default_command_executor_scout.py | 1 - .../centralized/default_command_executor_scout_police.py | 1 - .../centralized/default_command_picker_ambulance.py | 1 + .../implement/centralized/default_command_picker_fire.py | 1 + .../implement/centralized/default_command_picker_police.py | 7 ------- 8 files changed, 2 insertions(+), 12 deletions(-) diff --git a/adf_core_python/implement/centralized/default_command_executor_ambulance.py b/adf_core_python/implement/centralized/default_command_executor_ambulance.py index f2c00d61..ac296806 100644 --- a/adf_core_python/implement/centralized/default_command_executor_ambulance.py +++ b/adf_core_python/implement/centralized/default_command_executor_ambulance.py @@ -49,7 +49,6 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._command_type = self.ACTION_UNKNOWN self._path_planning: PathPlanning = cast( PathPlanning, diff --git a/adf_core_python/implement/centralized/default_command_executor_fire.py b/adf_core_python/implement/centralized/default_command_executor_fire.py index abd2ebe8..680320be 100644 --- a/adf_core_python/implement/centralized/default_command_executor_fire.py +++ b/adf_core_python/implement/centralized/default_command_executor_fire.py @@ -46,7 +46,6 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._command_type = self.ACTION_UNKNOWN self._path_planning: PathPlanning = cast( PathPlanning, diff --git a/adf_core_python/implement/centralized/default_command_executor_police.py b/adf_core_python/implement/centralized/default_command_executor_police.py index aecd5744..69ad6228 100644 --- a/adf_core_python/implement/centralized/default_command_executor_police.py +++ b/adf_core_python/implement/centralized/default_command_executor_police.py @@ -47,7 +47,6 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._command_type = self.ACTION_UNKNOWN self._path_planning: PathPlanning = cast( PathPlanning, diff --git a/adf_core_python/implement/centralized/default_command_executor_scout.py b/adf_core_python/implement/centralized/default_command_executor_scout.py index 11861554..28fdd782 100644 --- a/adf_core_python/implement/centralized/default_command_executor_scout.py +++ b/adf_core_python/implement/centralized/default_command_executor_scout.py @@ -41,7 +41,6 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._command_type = self.ACTION_UNKNOWN self._path_planning: PathPlanning = cast( PathPlanning, diff --git a/adf_core_python/implement/centralized/default_command_executor_scout_police.py b/adf_core_python/implement/centralized/default_command_executor_scout_police.py index 01f44c90..ad17d678 100644 --- a/adf_core_python/implement/centralized/default_command_executor_scout_police.py +++ b/adf_core_python/implement/centralized/default_command_executor_scout_police.py @@ -42,7 +42,6 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._command_type = self.ACTION_UNKNOWN self._path_planning: PathPlanning = cast( PathPlanning, diff --git a/adf_core_python/implement/centralized/default_command_picker_ambulance.py b/adf_core_python/implement/centralized/default_command_picker_ambulance.py index 9af95233..b2a507db 100644 --- a/adf_core_python/implement/centralized/default_command_picker_ambulance.py +++ b/adf_core_python/implement/centralized/default_command_picker_ambulance.py @@ -61,6 +61,7 @@ def calculate(self) -> CommandPicker: if target is None: continue + command: CommunicationMessage if isinstance(target, Human): command = CommandAmbulance( True, diff --git a/adf_core_python/implement/centralized/default_command_picker_fire.py b/adf_core_python/implement/centralized/default_command_picker_fire.py index 435d9316..bacb9968 100644 --- a/adf_core_python/implement/centralized/default_command_picker_fire.py +++ b/adf_core_python/implement/centralized/default_command_picker_fire.py @@ -61,6 +61,7 @@ def calculate(self) -> CommandPicker: if target is None: continue + command: CommunicationMessage if isinstance(target, Human): command = CommandAmbulance( True, diff --git a/adf_core_python/implement/centralized/default_command_picker_police.py b/adf_core_python/implement/centralized/default_command_picker_police.py index 75b7ef7b..bbda78e7 100644 --- a/adf_core_python/implement/centralized/default_command_picker_police.py +++ b/adf_core_python/implement/centralized/default_command_picker_police.py @@ -1,19 +1,12 @@ from __future__ import annotations from rcrs_core.entities.area import Area -from rcrs_core.entities.human import Human from rcrs_core.entities.policeForce import PoliceForce from rcrs_core.worldmodel.entityID import EntityID -from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( - CommandAmbulance, -) from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( CommandPolice, ) -from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( - CommandScout, -) from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( StandardMessagePriority, )