Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion consai_game/consai_game/core/tactic/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
from consai_msgs.msg import MotionCommand

from consai_game.core.tactic.role import Role, RoleConst
from consai_game.core.tactic.tactic_base import TacticState
from consai_game.core.tactic.tactic_state import TacticState
from consai_game.core.tactic.robot_tactic_status import RobotTacticStatus
from consai_game.world_model.world_model import WorldModel


Expand Down Expand Up @@ -88,3 +89,21 @@ def reset_tactic(self) -> None:

self.present_tactic = self.role.tactics[self.present_tactic_index]
self.present_tactic.reset(self.role.robot_id)

def get_robot_tactic_status(self) -> Optional[RobotTacticStatus]:
"""ロボットが実行しているTacticの状態を取得する関数.

TacticかロボットIDが無効な場合はNoneを返す.
"""

if self.present_tactic is None:
return None

if self.role.robot_id == RoleConst.INVALID_ROLE_ID:
return None

return RobotTacticStatus(
robot_id=self.role.robot_id,
tactic_name=self.present_tactic.name,
tactic_state="none", # TODO: tacticが状態遷移器を持っていたら、状態をセットする
)
11 changes: 11 additions & 0 deletions consai_game/consai_game/core/tactic/agent_scheduler_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from consai_game.core.tactic.agent import Agent
from consai_game.core.tactic.role import Role
from consai_game.core.tactic.robot_tactic_status import RobotTacticStatus
from consai_game.utils.process_info import process_info
from consai_game.world_model.world_model import WorldModel

Expand Down Expand Up @@ -78,3 +79,13 @@ def set_roles(self, roles: list[Role]):
with self.lock:
for role, agent in zip(roles, self.agents):
agent.set_role(role)

def get_robot_tactic_status_list(self) -> list[RobotTacticStatus]:
"""各エージェントのロボットID, 戦術名, 戦術状態を取得する関数."""
status_list = []

for agent in self.agents:
if status := agent.get_robot_tactic_status():
status_list.append(status)

return status_list
24 changes: 24 additions & 0 deletions consai_game/consai_game/core/tactic/robot_tactic_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2025 Roots
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from dataclasses import dataclass


@dataclass
class RobotTacticStatus:
"""ロボットが実行しているTacticと状態をまとめたクラス"""

robot_id: int
tactic_name: str
tactic_state: str
21 changes: 12 additions & 9 deletions consai_game/consai_game/core/tactic/tactic_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,21 @@
"""

from abc import ABC, abstractmethod
from enum import Enum, auto

from consai_game.core.tactic.tactic_state import TacticState
from consai_game.world_model.world_model import WorldModel

from consai_msgs.msg import MotionCommand


class TacticState(Enum):
"""戦術の状態を表す列挙型."""

BEFORE_INIT = 0
RUNNING = auto()
FINISHED = auto()


class TacticBase(ABC):
"""戦術の基本クラス."""

def __init__(self):
"""TacticBaseの初期化を行う関数."""
self._robot_id = -1
self._state = TacticState.BEFORE_INIT
self._name = self.__class__.__name__

@abstractmethod
def run(self, world_model: WorldModel) -> MotionCommand:
Expand Down Expand Up @@ -75,3 +68,13 @@ def state(self) -> TacticState:
def state(self, value: TacticState) -> None:
"""戦術の状態を設定する関数."""
self._state = value

@property
def name(self) -> str:
"""戦術の名前を取得する関数."""
return self._name

@name.setter
def name(self, value: str) -> None:
"""戦術の名前を設定する関数."""
self._name = value
24 changes: 24 additions & 0 deletions consai_game/consai_game/core/tactic/tactic_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2025 Roots
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from enum import Enum, auto


class TacticState(Enum):
"""戦術の状態を表す列挙型."""

BEFORE_INIT = 0
RUNNING = auto()
FINISHED = auto()
38 changes: 38 additions & 0 deletions consai_game/consai_game/core/tactic/wrapper_tactic_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2025 Roots
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from consai_game.core.tactic.tactic_base import TacticBase


class WrapperTacticBase(TacticBase):
"""WrapperTacticの基底クラス.

WrapperTacticは、他のTacticをラップして、特定の機能を追加する
"""

def __init__(self, tactic=TacticBase):
"""内部tacticを初期化する関数."""
super().__init__()
self.inner_tactic = tactic
self.name += f"({self.inner_tactic.name})"

def reset(self, robot_id: int) -> None:
"""inner_tacticをリセットする関数."""
super().reset(robot_id)
self.inner_tactic.reset(robot_id)

def exit(self):
"""inner_tacticをexitする関数."""
super().exit()
self.inner_tactic.exit()
4 changes: 3 additions & 1 deletion consai_game/consai_game/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from consai_game.core.play.play_node import PlayNode
from consai_game.core.tactic.agent_scheduler_node import AgentSchedulerNode
from consai_game.world_model.visualize_msg_publisher_node import VisualizeMsgPublisherNode
from consai_game.visualization.visualize_msg_publisher_node import VisualizeMsgPublisherNode
from consai_game.world_model.world_model_provider_node import WorldModelProviderNode


Expand All @@ -40,6 +40,8 @@ def main():
world_model = world_model_provider_node.world_model
play_node.set_world_model(world_model)
agent_scheduler_node.set_world_model(world_model)

vis_msg_publisher_node.set_robot_tactic_status_list(agent_scheduler_node.get_robot_tactic_status_list())
vis_msg_publisher_node.publish(world_model)

time.sleep(1 / UPDATE_HZ)
Expand Down
4 changes: 1 addition & 3 deletions consai_game/consai_game/tactic/back_dribble.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"""

from consai_game.core.tactic.tactic_base import TacticBase
from consai_game.core.tactic.tactic_base import TacticState
from consai_game.world_model.world_model import WorldModel
from consai_game.utils.generate_dummy_ball_position import generate_dummy_ball_position

Expand Down Expand Up @@ -132,8 +131,7 @@ def __init__(self, x=0.0, y=0.0):

def reset(self, robot_id: int) -> None:
"""Reset the tactic state for the specified robot."""
self.robot_id = robot_id
self.state = TacticState.RUNNING
super().reset(robot_id)
self.machine.reset()

def run(self, world_model: WorldModel) -> MotionCommand:
Expand Down
4 changes: 1 addition & 3 deletions consai_game/consai_game/tactic/dribble.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"""

from consai_game.core.tactic.tactic_base import TacticBase
from consai_game.core.tactic.tactic_base import TacticState
from consai_game.world_model.world_model import WorldModel
from consai_game.utils.generate_dummy_ball_position import generate_dummy_ball_position

Expand Down Expand Up @@ -126,8 +125,7 @@ def __init__(self, x=0.0, y=0.0):

def reset(self, robot_id: int) -> None:
"""Reset the tactic state for the specified robot."""
self.robot_id = robot_id
self.state = TacticState.RUNNING
super().reset(robot_id)
self.machine.reset()

def run(self, world_model: WorldModel) -> MotionCommand:
Expand Down
5 changes: 2 additions & 3 deletions consai_game/consai_game/tactic/kick.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from consai_tools.geometry import geometry_tools as tool

from consai_game.world_model.world_model import WorldModel
from consai_game.core.tactic.tactic_base import TacticBase, TacticState
from consai_game.core.tactic.tactic_base import TacticBase
from consai_game.utils.generate_dummy_ball_position import generate_dummy_ball_position

from transitions.extensions import GraphMachine
Expand Down Expand Up @@ -113,8 +113,7 @@ def __init__(self, x=0.0, y=0.0, is_pass=False, is_tapping=False, is_setplay=Fal

def reset(self, robot_id: int) -> None:
"""ロボットIDを設定し, Tacticの状態をRUNNINGにリセットする関数."""
self.robot_id = robot_id
self.state = TacticState.RUNNING
super().reset(robot_id)
self.machine.reset()

def run(self, world_model: WorldModel) -> MotionCommand:
Expand Down
6 changes: 1 addition & 5 deletions consai_game/consai_game/tactic/man_mark.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from consai_game.core.tactic.tactic_base import TacticBase, TacticState
from consai_game.core.tactic.tactic_base import TacticBase
from consai_msgs.msg import MotionCommand
from consai_msgs.msg import State2D
from consai_game.world_model.world_model import WorldModel
Expand All @@ -21,10 +21,6 @@ def __init__(self, target_robot_id: int, mark_distance: float = 0.5):
# ペナルティエリア際に移動するためのマージン
self.area_margin = 0.15

def reset(self, robot_id: int):
self.robot_id = robot_id
self.state = TacticState.RUNNING

def run(self, world_model: WorldModel) -> MotionCommand:
command = MotionCommand()
command.robot_id = self.robot_id
Expand Down
6 changes: 0 additions & 6 deletions consai_game/consai_game/tactic/receive.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"""

from consai_game.core.tactic.tactic_base import TacticBase
from consai_game.core.tactic.tactic_base import TacticState
from consai_game.world_model.world_model import WorldModel

from consai_msgs.msg import MotionCommand
Expand All @@ -43,11 +42,6 @@ def __init__(self):
super().__init__()
self.receive_pos = State2D()

def reset(self, robot_id: int) -> None:
"""Reset the tactic state for the specified robot."""
self.robot_id = robot_id
self.state = TacticState.RUNNING

def run(self, world_model: WorldModel, diff_angle_threshold: int = 20) -> MotionCommand:
"""Run the tactic and return a MotionCommand based on the ball's position and movement."""
command = MotionCommand()
Expand Down
7 changes: 1 addition & 6 deletions consai_game/consai_game/tactic/stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from consai_msgs.msg import MotionCommand

from consai_game.world_model.world_model import WorldModel
from consai_game.core.tactic.tactic_base import TacticBase, TacticState
from consai_game.core.tactic.tactic_base import TacticBase


class Stop(TacticBase):
Expand All @@ -27,11 +27,6 @@ def __init__(self):
"""StopTacticのインスタンスを初期化する関数."""
super().__init__()

def reset(self, robot_id: int) -> None:
"""ロボットIDを設定し, Tacticの状態をRUNNINGにリセットする関数."""
self.robot_id = robot_id
self.state = TacticState.RUNNING

def run(self, world_model: WorldModel) -> MotionCommand:
"""ロボットを停止させるためのMotionCommandを生成する関数."""
command = MotionCommand()
Expand Down
5 changes: 2 additions & 3 deletions consai_game/consai_game/tactic/swab.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from consai_msgs.msg import State2D

from consai_game.world_model.world_model import WorldModel
from consai_game.core.tactic.tactic_base import TacticBase, TacticState
from consai_game.core.tactic.tactic_base import TacticBase

from transitions.extensions import GraphMachine

Expand Down Expand Up @@ -73,8 +73,7 @@ def __init__(self):

def reset(self, robot_id: int) -> None:
"""ロボットIDを設定し、Tacticの状態をRUNNINGにリセットする関数."""
self.robot_id = robot_id
self.state = TacticState.RUNNING
super().reset(robot_id)
self.machine.reset()

def run(self, world_model: WorldModel) -> MotionCommand:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,16 @@

from consai_msgs.msg import MotionCommand

from consai_game.core.tactic.wrapper_tactic_base import WrapperTacticBase
from consai_game.world_model.world_model import WorldModel
from consai_game.core.tactic.tactic_base import TacticBase


class AllowMoveInDefenseArea(TacticBase):
class AllowMoveInDefenseArea(WrapperTacticBase):
"""ディフェンスエリア内での移動を許可するWrapperTactic."

AllowMoveInDefenseArea(tactic=Position()) のように使用する
"""

def __init__(self, tactic=TacticBase):
"""inner_tacticを初期化する関数."""
super().__init__()
self.inner_tactic = tactic

def reset(self, robot_id: int) -> None:
"""inner_tacticをリセットする関数."""
super().reset(robot_id)
self.inner_tactic.reset(robot_id)

def exit(self):
"""inner_tacticをexitする関数."""
super().exit()
self.inner_tactic.exit()

def run(self, world_model: WorldModel) -> MotionCommand:
"""ディフェンスエリア内での移動と、ボールとの接触を許可する."""
command = self.inner_tactic.run(world_model)
Expand Down
Loading