Source code for yawning_titan.game_modes.components.game_rules

from __future__ import annotations

from typing import Optional

from yawning_titan.config.core import ConfigGroup, ConfigGroupValidation
from yawning_titan.config.groups.core import RestrictRangeGroup, UseValueGroup
from yawning_titan.config.groups.validation import AnyUsedGroup
from yawning_titan.config.item_types.bool_item import BoolItem, BoolProperties
from yawning_titan.config.item_types.int_item import IntItem, IntProperties
from yawning_titan.exceptions import ConfigGroupValidationError

# --- Tier 0 groups


[docs]class NetworkCompatibilityGroup(ConfigGroup): """A set of optional restrictions that collectively constrain the types of network a game mode can be used upon."""
[docs] def __init__( self, doc: Optional[str] = None, node_count: Optional[RestrictRangeGroup] = None, entry_node_count: Optional[RestrictRangeGroup] = None, high_value_node_count: Optional[RestrictRangeGroup] = None, ): self.node_count = ( node_count if node_count else RestrictRangeGroup( doc="Restrict the game mode to only work with network works within a range of node counts" ) ) self.entry_node_count = ( entry_node_count if entry_node_count else RestrictRangeGroup( doc="Restrict the game mode to only work with network works within a range of entry_node_count counts" ) ) self.high_value_node_count = ( high_value_node_count if high_value_node_count else RestrictRangeGroup( doc="Restrict the game mode to only work with network works within a range of high_value_node_count counts" ) ) self.node_count.min.alias = "min_number_of_network_nodes" super().__init__(doc)
[docs]class BlueLossConditionGroup(AnyUsedGroup): """The state of the network that must be reached for the red agent to win the game."""
[docs] def __init__( self, doc: Optional[str] = None, all_nodes_lost: Optional[bool] = False, high_value_node_lost: Optional[bool] = False, target_node_lost: Optional[bool] = False, n_percent_nodes_lost: Optional[UseValueGroup] = None, ): self.all_nodes_lost: BoolItem = BoolItem( value=all_nodes_lost, doc="The blue agent loses if all the nodes become compromised", properties=BoolProperties(allow_null=True, default=False), alias="lose_when_all_nodes_lost", ) self.high_value_node_lost: BoolItem = BoolItem( value=high_value_node_lost, doc="Blue loses if a special node designated as 'high value' is lost", properties=BoolProperties(allow_null=True, default=False), alias="lose_when_high_value_node_lost", ) self.target_node_lost: BoolItem = BoolItem( value=target_node_lost, doc="Blue loses if a target node it lost", properties=BoolProperties(allow_null=True, default=False), alias="lose_when_target_node_lost", ) self.n_percent_nodes_lost: UseValueGroup = ( n_percent_nodes_lost if n_percent_nodes_lost else UseValueGroup( doc="The percentage of nodes that need to be lost for blue to lose", ) ) self.n_percent_nodes_lost.value.alias = ( "percentage_of_nodes_compromised_equals_loss" ) self.n_percent_nodes_lost.use.alias = "lose_when_n_percent_of_nodes_lost" super().__init__(doc)
# --- Tier 1 groups ---
[docs]class GameRules(ConfigGroup): """The overall rules of the game mode."""
[docs] def __init__( self, grace_period_length: Optional[int] = 0, max_steps: Optional[int] = 0, blue_loss_condition: Optional[BlueLossConditionGroup] = None, network_compatibility: Optional[NetworkCompatibilityGroup] = None, ): doc = "The rules of the overall game mode" self.grace_period_length = IntItem( value=grace_period_length, doc=( "The length of a grace period at the start of the game. During this time the red agent cannot act. " "This gives the blue agent a chance to 'prepare' (A length of 0 means that there is no grace period)" ), properties=IntProperties( allow_null=False, default=0, min_val=0, max_val=100, inclusive_min=True, inclusive_max=True, ), alias="grace_period_length", ) self.max_steps = IntItem( value=max_steps, doc="The max steps that a game can go on for. If the blue agent reaches this they win", properties=IntProperties( allow_null=False, default=1, min_val=1, max_val=10_000_000, inclusive_min=True, inclusive_max=True, ), alias="max_steps", ) self.blue_loss_condition: BlueLossConditionGroup = ( blue_loss_condition if blue_loss_condition else BlueLossConditionGroup( doc="The state of the network that must be reached for the red agent to win the game.", ) ) self.network_compatibility: NetworkCompatibilityGroup = ( network_compatibility if network_compatibility else NetworkCompatibilityGroup( doc="The range of networks the game mode can be played upon" ) ) super().__init__(doc)
[docs] def validate(self) -> ConfigGroupValidation: """Extend the parent validation with additional rules specific to this :class: `~yawning_titan.config.core.ConfigGroup`.""" super().validate() try: if self.grace_period_length.value > self.max_steps.value: msg = "The grace period cannot be the entire length of the game" raise ConfigGroupValidationError(msg) except ConfigGroupValidationError as e: self.validation.add_validation(msg, e) return self.validation