The postprocessing()
Method¶
In addition to the turn()
and preprocessing()
methods, the Player
class also includes a postprocessing()
method.
This method is called after the main game loop has completed and can be used to perform any final updates or clean-up tasks.
In this tutorial, we will explore how to implement the postprocessing()
method in a custom player class.
An Example of Usage¶
In this example, we will illustrate how the postprocessing()
method can be used to analyze the moves made by an opponent player during the game.
We will not provide a complete implementation, but rather focus on the structure and purpose of the method.
Let’s consider a match in 3 games between two players, where the first player to reach 2 wins is declared the overall winner.
A game script for this will use the game.reset()
functions to allow players to store information across games.
Here is a possible class that uses the postprocessing()
method to analyze the series of game states across the games, and to determine the opponent’s strategy based on that.
Then, the turn()
method has a different behavior depending on the conclusions found in the previous game.
class MyPlayer (Player):
def __init__ ( self,
*args: object,
**kwargs: object
) -> None:
# Inherit from parent class
super().__init__(*args, **kwargs)
# Store the game states across the game
self.all_game_states = None
# Strategy to use
self.strategy = None
#######################################################################
def preprocessing ( self,
maze: Maze,
game_state: GameState,
) -> None:
# Initialize the list of game states
self.all_game_states = []
#######################################################################
def turn ( self,
maze: Maze,
game_state: GameState,
) -> Action:
# Store the current game state
self.all_game_states.append(game_state)
# Depending on the current strategy, we will choose a different action
if self.strategy == "beat_greedy_opponent":
# We determined in an earlier game that the opponent follows a greedy strategy
# Therefore, we will try to outsmart them by choosing adapted actions
elif self.strategy == "beat_density_opponent":
# We determined in an earlier game that the opponent follows a density-based strategy
# Therefore, we will try to outsmart them by choosing adapted actions
else:
# We do not have any particular information about the opponent's moves yet
# Return the action to perform
return # ...
#######################################################################
def postprocessing ( self,
maze: Maze,
game_state: GameState,
stats: dict[str, object],
) -> None:
# When the game is over, we analyze the series of game states to determine the opponent's strategy
# It can be something along these lines (probability_of_strategy is a placeholder for a function of yours)
if probability_of_strategy(self.all_game_states, "greedy") > 0.8:
self.strategy = "beat_greedy_opponent"
elif probability_of_strategy(self.all_game_states, "density") > 0.8:
self.strategy = "beat_density_opponent"
With this code, if your player determines that the opponent followed a greedy strategy during the game that just ended, it will set the self.strategy
attribute accordingly.
Thus, in the next game, the turn()
method will behave differently.
Other Possible Usages¶
In addition to the example above, here are a few extra ideas you can implement using the postprocessing()
method.
Train a model with reinforcement.
Remove some temporary files you may have created across the game.
Close a connection to an external resource.
Etc.