-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Welcome to the cloaca wiki!
There are a few classes handling server operation.
- Game
- GameState
- Player
- Building
The GameState object represents the physical game state, containing
a library of Orders cards, zones for the pool, jacks, and sites and a list
of Player objects.
The Player and Building classes are containers used by GameState.
The Game class is a high-level object that runs the server, manipulating
the GameState, Player, and Building objects.
Note that there is no separate Card class.
Building is a container representing a building in GtR. There are lists
of cards for the site, foundation, added building materials, materials
added by the Stairway, and a flag that indicates completion.
Player is a container for all the zones associated with a player.
This includes the hand, camp, stockpile, vault, clientele, buildings,
and influence. Member functions are limited because the rules that apply to
players go beyond the buildings they have completed, possibly including
other player's buildings affected by the Stairway.
GameState represents the physical game state, including a list with a Player
object for each player.
Other members include the deck, Jacks, pool, sites, and flags related to the
game state, eg. whose turn it is.
The rules of the game are not enforced by the GameState
object, but physical limits (eg. trying to draw from an empty deck) are enforced.
There are some member functions designed to be make manipulation more convenient,
but they generally do not check legality.
In the current incarnation, there are member functions that print the game state
to stdout, but will likely change.
Since the GameState class contains the stack that controls game flow, saving
and loading this object is enough to fully restore a game.
The Game class should maybe be more properly called Server.
Game has a GameState object member which it manipulates while maintaining
the flow of the game.
After initializing the GameState and the players' names and turn order, Game
begins by calling take_turn(first_player). This method starts the series of
steps that make up a player's turn and eventually lead to calling
take_turn(next_player). A schematic example, showing the member functions
of Game that are called at each step.
- take_turn(p1)
- lead_role_action(p1) # asks if/what p1 is leading
- follow_role_action(p2) # asks if p2 is following
- perform_thinker_action(p2) # thinking, not leading
- perform_role_being_led(p1) # performs role (also clients)
- perform_craftsman_action(p1)
- perform_clientele_action(p1, 'Craftsman')
- perform_role_being_led(p2)
- perform_clientele_action(p2, 'Craftsman') # p2 still gets clientele
- end_turn(p1)
- kids_in_pool()
- take_turn(p2)
- ...
The control flow in the Game class is handled by a stack. See the section
below for more information.
Cards do not have their own class. Instead, they are passed around as
strings corresponding to the building name (or the material name, for sites).
The card_manager module handles conversions from the building name to
coin value, material, role, etc. This module also imports the cards from
a json dictionary of all legal cards, which only includes the I.V. edition.
There are several Dialog methods that are used to interact with the
player, using the rudimentary Python function raw_input().
There is a Dialog for every possible information request from the player.
Here's a non-exhaustive list:
- ThinkerOrLeadDialog()
- UseLatrineDialog()
- UseVomitoriumDialog()
- ThinkerTypeDialog()
- LeadRoleDialog()
- FollowRoleDialog()
- LaborerDialog()
- PatronFromPoolDialog()
- PatronFromDeckDialog()
- PatronFromHandDialog()
The atomicity of these information requests is somewhat arbitrary, but there are
subtleties. For instance, notice that there are separate PatronFromPool,
PatronFromDeck, and PatronFromHand Dialogs, reflecting the possibility
that the Bath allows any of these patron actions to change relevant information,
such as the clientele limit.
However, there is a single LaborerDialog() that
asks the player to select card from the pool and a card from his hand
if he has a Dock, all in one go. This is possible because peforming one of
the two modes doesn't affect the other.
These methods are intended as a stop-gap until a proper client can be developed.
They are member functions of the Game class, printing the requests directly
to stdout and taking input typed at the command line.
The upgrade path will be to implement the Dialogs as a self-contained
LocalTerminalClient that can register itself with Game.
Additional clients, eg. HTMLClient, or GUIClient could then be devloped
with the same interface.
The game flow is controlled with a stack. Game actions will push new frames on the stack as the last thing they do. A central control loop will process the top frame of the stack in an infinite loop.
In the Game class, this loop is contained in the run() function.
while(self.game_state.stack.stack):
self.save_game_state('tmp/log_state')
self.process_stack_frame()
The stack frames (see stack.py) have a member that's the name
of a function in gtr.py, and a list of arguments to this function.
The process_stack_frame() function pops the top frame and evaluates it.
Upon returning from this function,
the loop recycles and the next frame is called.
Because of this, the stack frame must push new frames as the last action
in its function call. Any further action would occur before the pushed
frames, ruining the last-in-first-out order.
The stack allows adjustment of the granularity of certain maintenance tasks, like saving the game or refreshing the screen. If we want to save before a particular game action, we just need to make that game action its own stack frame.
Another advantage of the game state stack is that the game can easily
be saved with finer granularity than ever turn.
Before each stack frame is evaluated, the whole GameState is pickled
and written to a text file in the current directory.
The most recent file can be loaded by calling Game.run(True).
Because there is a limited set of buildings that is unlikely to change, they are implemented by coding them directly into the game rules. An alternative would be to provide hooks in the turn sequence and player actions, and allow scripts associated with each card's function to hook into these.
These are the buildings that are not yet fully implemented:
- Forum
- Prison
- Wall
- Bridge
- Coliseum
- Palisade
The Legionary-focused buildings are not implemented because Legionary isn't fully implemented.
The Forum needs checks for victory inserted after every game action that could result in a change.
- Completion of any building
- Addition of a material with stairway
- After Prison resolves
- Client gained