Welcome to Catanatron’s documentation!#

Catanatron is a Python implementation of the Settlers of Catan core game logic.

The most basic usage of the package looks like:

from catanatron.game import Game
from catanatron.models.player import RandomPlayer, Color

players = [
   RandomPlayer(Color.RED),
   RandomPlayer(Color.BLUE),
   RandomPlayer(Color.WHITE),
   RandomPlayer(Color.ORANGE),
]
game = Game(players)
game.play()  # returns winning color

The example above executes a game of 4 RandomPlayers (players that decide completly at random). The catanatron.players has more Player implementations.

Although the package exposes an OOP API, internally we’ve been moving to a Functional implementation for performance. Particularly, when making copies of the state (a common operation for tree-searching algorithms), it’s much faster to copy a dictionary of primitives than to copy.deepcopy an entire class.

Internally the Game.play() method is mainly a while-no-one-has-won loop that asks players to decide on a possible action. This architecture naturally makes the framework fit the Game Trees concept, and makes it easy to implement tree-searching players.

Thus, Players must implement the following API:

from catanatron.game import Game
from catanatron.models.actions import Action
from catanatron.models.player import Player

class MyPlayer(Player):
   def decide(self, game: Game, playable_actions: Iterable[Action]):
      """Should return one of the playable_actions.

      Args:
            game (Game): complete game state. read-only.
            playable_actions (Iterable[Action]): options to choose from
      Return:
            action (Action): Chosen element of playable_actions
      """
      raise NotImplementedError

The first parameter, game is mainly so that the player can access game.state to take its decisions. This game.state is currently represented by a simple data container class; you can see the documentation here: https://catanatron.readthedocs.io/en/latest/catanatron.html#catanatron.state.State. For now, players should not mutate this state (should treat it read-only). If one would like to make modifications to consider actions one should copy the state with the State.copy function.

The second parameter is the list of playable Actions. An Action is a tuple of enums and primitives like:

  • (ActionType.PLAY_MONOPOLY, WHEAT) (i.e. play monopoly card and select wheat)

  • (ActionType.BUILD_SETTLEMENT, 3) (i.e. build settlement on node 3)

  • (ActionType.MOVE_ROBBER, (1,0,1), Color.BLUE) (i.e. move robber to tile on coordinate (1,0,1) and steal from blue)

  • (ActionType.END_TURN, None) (i.e. do nothing else and end turn)

After a player takes a decision, the Game follows a Redux/Router pattern in which calls generic apply_action(state, action) method that will route to the appropriate state-mutating function in the state_functions module.

A great way to further undersand the internals is to place a breakpoint in MyPlayer or RandomPlayer run the provided sample.py in the repo and inspect the game and playable_actions objects.

This package is published in PyPi and is pip-installable like so:

pip install catanatron

Indices and tables#