class Game(statemachine.StateMachine): actions_done = statemachine.State('ActionsDone', value=9, initial=True) instants_triggered = statemachine.State('InstantsTriggered', value=1) cycle = actions_done.to(instants_triggered) | instants_triggered.to( actions_done) def __init__(self, arena_name: str, to_spawn: list[controller.Controller]) -> None: self.arena: arenas.Arena = arenas.Arena.load(arena_name) self.arena.spawn_menhir() self._prepare_controllers(to_spawn) self.champions: list[characters.Champion] = self._spawn_champions( to_spawn) self.action_queue: list[characters.Champion] = [] self.episode: int = 0 self.deaths: list[ChampionDeath] = [] self.finished = False super(statemachine.StateMachine, self).__init__() def on_enter_actions_done(self) -> None: if not self.action_queue: self._environment_action() else: self._champion_action() def on_enter_instants_triggered(self): self.arena.trigger_instants() def score(self) -> dict[str, int]: if not self.finished: raise RuntimeError("Attempted to score an unfinished game!") return { death.champion.controller.name: score for death, score in zip(self.deaths, self._fibonacci()) } def _prepare_controllers(self, to_spawn: list[controller.Controller]): random.shuffle(to_spawn) for controller_to_spawn in to_spawn: controller_to_spawn.reset(self.arena.description()) def _spawn_champions( self, to_spawn: list[controller.Controller] ) -> list[characters.Champion]: champions = [] champion_positions = random.sample(self.arena.empty_coords(), len(to_spawn)) for controller_to_spawn, coords in zip(to_spawn, champion_positions): champion = characters.Champion(coords, self.arena) self.arena.terrain[coords].character = champion champion.controller = controller_to_spawn champions.append(champion) logging.debug( f"Champion for {controller_to_spawn.name} spawned at {coords} facing {champion.facing}." ) return champions def _environment_action(self) -> None: self._clean_dead_champions() self.action_queue = self.champions.copy() self.episode += 1 logging.debug(f"Starting episode {self.episode}.") if self.episode % MIST_TTH == 0: self.arena.increase_mist() def _clean_dead_champions(self): alive = [] for champion in self.champions: if champion.alive: alive.append(champion) else: death = ChampionDeath(champion, self.episode) self.deaths.append(death) self.champions = alive if len(self.champions) == 1: logging.debug( f"Champion {self.champions[0].controller.name} was the last one standing." ) champion = self.champions.pop() death = ChampionDeath(champion, self.episode) self.deaths.append(death) if not self.champions: self.finished = True def _champion_action(self) -> None: champion = self.action_queue.pop() champion.act() @staticmethod def _fibonacci() -> Iterator[int]: a = 1 b = 2 while True: yield a a, b = b, a + b
def setUp(self): shared_data = {} self.state_history = [] self.root_state = statemachine.State() self.root_state.event_loop = self self.root_state.switch_to_substate(TestSubState1())
class Game(statemachine.StateMachine): actions_done = statemachine.State('ActionsDone', value=9, initial=True) instants_triggered = statemachine.State('InstantsTriggered', value=1) cycle = actions_done.to(instants_triggered) | instants_triggered.to( actions_done) def __init__( self, arena_name: str, to_spawn: list[controller.Controller], menhir_position: Optional[coordinates.Coords] = None, initial_champion_positions: Optional[list[coordinates.Coords]] = None, ) -> None: self.arena: arenas.Arena = arenas.Arena.load(arena_name) self.arena.spawn_menhir(menhir_position) self._prepare_controllers(to_spawn) self.initial_champion_positions: Optional[list[ coordinates.Coords]] = initial_champion_positions self.champions: list[characters.Champion] = self._spawn_champions( to_spawn) self.action_queue: list[characters.Champion] = [] self.episode: int = 0 self.deaths: list[ChampionDeath] = [] self.finished = False super(statemachine.StateMachine, self).__init__() def on_enter_actions_done(self) -> None: if not self.action_queue: self._environment_action() else: self._champion_action() def on_enter_instants_triggered(self): self.arena.trigger_instants() def score(self) -> dict[str, int]: if not self.finished: raise RuntimeError("Attempted to score an unfinished game!") return { death.champion.controller.name: score for death, score in zip(self.deaths, self._fibonacci()) } def _prepare_controllers(self, to_spawn: list[controller.Controller]): for controller_to_spawn in to_spawn: controller_to_spawn.reset(self.arena.description()) def _spawn_champions( self, to_spawn: list[controller.Controller], ) -> list[characters.Champion]: champions = [] if self.initial_champion_positions is None: self.initial_champion_positions = random.sample( self.arena.empty_coords(), len(to_spawn)) if len(to_spawn) != len(self.initial_champion_positions): raise RuntimeError( "Unable to spawn champions: not enough positions!" ) # TODO: remove if works for controller_to_spawn, coords in zip( to_spawn, self.initial_champion_positions): champion = characters.Champion(coords, self.arena) self.arena.terrain[coords].character = champion champion.assign_controller(controller_to_spawn) champions.append(champion) verbose_logger.debug( f"{champion.tabard.value} champion for {controller_to_spawn.name}" f" spawned at {coords} facing {champion.facing}.") ChampionSpawnedReport(controller_to_spawn.name, coords, champion.facing.value).log(logging.DEBUG) return champions def _environment_action(self) -> None: self._clean_dead_champions() self.action_queue = self.champions.copy() self.episode += 1 verbose_logger.debug(f"Starting episode {self.episode}.") EpisodeStartReport(self.episode).log(logging.DEBUG) if self.episode % MIST_TTH == 0: self.arena.increase_mist() def _clean_dead_champions(self): alive = [] for champion in self.champions: if champion.alive: alive.append(champion) else: death = ChampionDeath(champion, self.episode) self.deaths.append(death) self.champions = alive if len(self.champions) == 1: verbose_logger.debug( f"Champion {self.champions[0].controller.name} was the last one standing." ) LastManStandingReport(self.champions[0].controller.name).log( logging.DEBUG) champion = self.champions.pop() death = ChampionDeath(champion, self.episode) self.deaths.append(death) win_callable = getattr(champion.controller, "win", None) if win_callable: win_callable() if not self.champions: self.finished = True def _champion_action(self) -> None: champion = self.action_queue.pop() champion.act() @staticmethod def _fibonacci() -> Iterator[int]: a = 1 b = 2 while True: yield a a, b = b, a + b
class ServiceState(statemachine.StateMachine): """State machine describing the state and lifecycle of a :class:`Service` instance.""" # states pending = statemachine.State("pending", initial=True) """The service instance has not yet been assigned to a provider.""" starting = statemachine.State("starting") """The service instance is starting on a provider. The activity within which the service is running has been created on a provider node and now the service instance's :meth:`~yapapi.services.Service.start` handler is active and has not yet finished. """ running = statemachine.State("running") """The service instance is running on a provider. The instance's :meth:`~yapapi.services.Service.start` handler has finished and the :meth:`~yapapi.services.Service.run` handler is active. """ stopping = statemachine.State("stopping") """The service instance is stopping on a provider. The instance's :meth:`~yapapi.services.Service.run` handler has finished and the :meth:`~yapapi.services.Service.shutdown` handler is active. """ terminated = statemachine.State("terminated") """The service instance has been terminated and is no longer bound to an activity. This means that either the service has been explicitly stopped by the requestor, or the activity that the service had been attached-to has been terminated - e.g. by a failure on the provider's end or as a result of termination of the agreement between the requestor and the provider. """ unresponsive = statemachine.State("unresponsive") # transitions start: statemachine.Transition = pending.to(starting) ready: statemachine.Transition = starting.to(running) stop: statemachine.Transition = running.to(stopping) terminate: statemachine.Transition = terminated.from_( starting, running, stopping, terminated) mark_unresponsive: statemachine.Transition = unresponsive.from_( starting, running, stopping, terminated) restart: statemachine.Transition = pending.from_(pending, starting, running, stopping, terminated, unresponsive) lifecycle = start | ready | stop | terminate """Transition performed when handler for the current state terminates normally. That is, not due to an error or `ControlSignal.stop` """ error_or_stop = stop | terminate """transition performed on error or `ControlSignal.stop`""" AVAILABLE = (starting, running, stopping) """A helper set of states in which the service instance is bound to an activity and can be interacted with.""" instance: "ServiceInstance" """The ServiceRunner's service instance.""" def on_enter_state(self, state: statemachine.State): """Register `state` in the instance's list of visited states.""" self.instance.visited_states.append(state)