Go to the first, previous, next, last section, table of contents.

The engine

A number of C++ classes mimic the components of a sporting tournament described in See section Structure of a tournament.

This chapter describes main classes of the libtour implementation and their collaboration. It is meant to provide an implementation overview but also should serve as a high level explanation of how libtour works. For details please refer to the C++ source in `libtour-N.N/libtour/' directory.

Main classes

TTour class represents a tournament. It contains:

TTour provides an interface for looking up teams and stages as well as encoding its state in a Scheme data structure, and restoring itself from the saved state (see section Saving and restoring state).

TStage class is a representation of a tournament stage. It contains:

Some other stage parameters are configurable from the Scheme definition:

TStage objects keep record of their state. See section TStage state transitions, for detailed description of TStage state transitions.

TGroup class corresponds to a group of teams in a tournament that usually play games between one another and have their results summarized in a table of standings. A group (TGroup) only exists within a stage (TStage).

TGroup knows its teams and keeps track of their performance. The Scheme tournament definitions must provide each group with the following information:

TFormula class hierarchy gives the groups capability to determine if all their games have been played (if the groups are "complete"). See section Game results and formula, for more.

TGroup object also stores information about sorting conflicts if any (see section Sorting teams).

TTeam objects represent participants of a competition. They are created and stored in the TTour instance. Teams are known by their global ID across the tournament, and by stage-local ID to the stages and groups.

Each TGroup object contains TTeamRes instances for each team that belongs to the group. TTeamRes accumulates interpreted game results for a team and can be thought of as a representation of a row in the table of standings.

TGame class represents a game between two teams in a tournament. Each of them contains a pointer to TGameRes which is zero until the game result is known. The game is read-only (i.e. its result cannot be changed) if its result was specified in the Scheme schedule definition (see section Game schedule definition).

TQuery class provides the means for the user code to interact with the libtour engine. It only keeps a reference to the TTour instance and uses its public interface to get hold of TStage, TGroup, and other objects as required.

Through a TQuery instance it is possible to query or change the tournament state. In each case, a T_QuerySpec object must be built and passed to TQuery for processing. For a read-only request (a query), a TQueryResults instance is filled with the reply. The user is provided with T_xxx envelope classes for TQuery and TQueryResults that are described in See section libtour API.

When an error occurs an instance of T_Exception class hierarchy is thrown. In case of libtour internal problem, the exception is T_InternalException. Invalid requests, or errors in tournament definitions (Scheme code) will lead to T_RuntimeException. T_StageReadyException (a descendant of T_RuntimeException) will be thrown if a stage completion request could not be performed because of the following stage not being able to become ready (see section TStage state transitions).

Scheme/C++ interaction

During a tournament interpretation libtour will read data structures defined in Scheme and call procedures embedded in them. These Scheme procedures can use C++ static functions exported back into Scheme.

TUsrProcedures singleton provides C++ functions for the Guile interpreter. To be able to locate the proper tournament when receiving a call from Scheme, TUsrProcedures requires each TTour object to register before the exported functions can be used. Naturally, each exported C++ function requires a tournament name as its argument so that the corresponding TTour object can be located.

The C++ functions available from Scheme are exported into the (libtour builtin) Guile module. See section builtin module, for detailed description of `(libtour builtin)'.

C++-defined procedures are guaranteed to throw proper Scheme errors when a C++ exception is thrown by the C++ code. This "C++ exception -> Guile error" conversion is implemented as shown below:

    SCM ret_scm = scm_listify( SCM_UNDEFINED );
    try {
        //do processing, set ret_scm

    } catch ( T_Exception& e ) {
        scm_throw( scm_str2symbol("t_exception"),
                   scm_makfrom0str(("<C++ function name>: " +
                                    e.why()).c_str()) );
    return ret_scm;

Likewise, Guile errors, possibly caused by syntatically invalid scheme code or thrown by the Scheme procedures called from C++, are handled with scm_internal_catch and converted into C++ exceptions. See TCallWithCCExcp class implementation for details.

TStage state transitions

TStage class delays determining it's teams (and complete game schedule) until the previous stage is complete. There are three possible states of a TStage object:

|---------------|   1   |---------------|   2   |---------------|
|               |------>|               |------>|               |
| uninitialized |       |     ready     |       |    complete   |
|               |<------|               |<------|               |
|---------------|   4   |---------------|   3   |---------------|
       ^                                                |
       |________________________________________________| 5

On the "uninitialized -> ready" transition (1), teams (and sometimes game schedule) are determined by calling the user-supplied Scheme procedures (thunks). TStage that is trying to become "ready" (1) runs an SCM thunk for each local team ID to locate the corresponding TTeam object, and asks its TSchedule member for a list of the games (this will run more Scheme code if schedule is not static). If the Scheme procedure fails to return an unambiguous result (because the previous stage is not complete, or a team sorting conflict prevents exact team positioning, etc) a T_Exception will be thrown.

To become "complete" (transition 2) a stage relies on its groups (TGtoup) when deciding if all games have been played; in other words, a stage is complete if all its groups are complete. Completeness of a group is decided according to its playing "formula" (see section Game results and formula). Another condition must be satisfied for a stage to become "complete": if this stage is not the last in the tournament, the following stage should be able to become "ready" (transition 1). If this fails (usually due to a sorting conflict in the previous stage), libtour will throw a T_StageReadyException (a descendant of T_RuntimeException.

A stage in the "complete" state cannot be changed, except for adding and deleting filtered groups (see section Filtered groups).

It is possible for a stage to undergo opposite transitions: a "complete" stage can become "ready" by executing uncompete-stage modifying query (see section Modifying queries).

Whenever a stage ceases to be "complete", all following stages in "complete" or "ready" state automatically become "uninitialized" (transitions 4 and 5).

Note that during loading saved state (see section Saving and restoring state) stages become "complete" automatically. In all other cases issuing the compete-stage modifying query (see section Modifying queries) is necessary.

Game results and formula

A TQuery object passes a game result to the corresponding stage for processing.

The stage will find the TGame object, create its result (TTGameRes) and ensure that the game belongs to at least one group and no group reject it for some reason (decided by the groups' TFormula members). If this check passes, the game result will be communicated to the groups and set in the TGame object. This two-pass process is necessary to keep the groups in consistent state.

Game result updates and deletions are processed similarly.

There are a number of predefined types of the group playing formulas implemented as TFormula class hierarchy:

"none" formula has no way to determine the group's completeness independently and relies on the groups within the stage which have a real (non-"none") formula.

In the "best-of N series" (playoff) formula the schedule may contain excessive games; indeed, if the teams play until four wins, the last three of the seven scheduled games may not be necessary. A game result will be rejected for an excessive game. Some restrictions apply on game result update and deletion.

TGroup objects can also report a list of IDs of their excessive games which also works for groups with "none" formula.

Summary group

If the SUMMARY entry of a stage definition (see section Summary definition) is a procedure, libtour will create a summary group for that stage. The summary group will be defined as follows:

"All teams"
all teams that belong to the stage
the procedure given in SUMMARY stage definition field (see section Teams comparison procedure)

Filtered groups

Groups of teams within a stage are defined in the tournament's rules (see section Groups definition). It is also possible to create a group on the fly using

The idea behind filtered groups is as follows. A filtered group is based on a pre-defined group (one in the rules definition). By using filters the user can filter out teams and/or games that belong to the base group at the time of creation of the filtered group.

For each filter a filter-making procedures should be defined. These procedures are written in Scheme. A number of them are provided by (libtour filters) module, but also can be defined directly inside tournament's rules.

A team/game filter-making procedure takes at least three arguments:

Following arguments if any are used to configure the filter. They should be provided by the user. libtour will supply the first three arguments when calling the filter-making procedure.

A team filter-making procedure will retun a Scheme closure (so that all the parameters are remembered) that takes a team associative list as its only argument. The list has keys as in STANDINGS-FIELDS (see section Standings and Game result fields definitions).

A game filter-making procedure returns a closure which is like a game-belongs-to-group procedure in a group definition (see section game-belongs-to-group procedure). It expects a single argument of a game associative list with keys as in SCHEDULE-FORMAT (see section Game schedule definition) and returns a pair of boolean values, one for each team.

Documentation of every filter-making procedure as well as the resulting closure must be provided as they are used by libtour

`filter-making procedure documentation'
documentation string (the first string expression in the definition)
`filter-making procedure's 'args property'
associative list of argument names and their types, for instance
'(("field" . symbol)
  ("action" . symbol)
  ("values" . list))
The supported argument types are defined in `t_constants.hh' header and include
`filter procedure's 'docs property'
string desribing what is being filterd out

If a team filter is provided, libtour will call it for every team of the base group to determine which should also belong to the filtered group. Once the group is created, libtour will loop through the games of the base group and call the game filter (if any) to determine which of the games should also belong to the newly created group.

Based on the filter information, libtour will call the TGroup constructor with the following associative list of the group definition (see section Groups definition):

provided by user
provided by user, or "<base group id> (U)"
docs property of the filter procedure(s)
base group's team IDs after applying the team filter if any
the procedure given in SUMMARY stage definition field (see section Summary definition) if supplied, or the base group's comparison procedure (see section Teams comparison procedure)
game filter if supplied, or the base group's procedure (see section game-belongs-to-group procedure)

It is important to note that a filtered group formula is always NONE (see section Group formula definition).

When a stage containing filtered groups becomes uninitialized (see section TStage state transitions), all filtered groups are deleted. When saving results (see section Saving and restoring state) no sorting conflicts are recorded for filtered groups.

Sorting teams

The Scheme tournament definition provides a procedure for sorting teams in each group. Proper sorting of teams within a group is essential for (1) constructing of a table of standings, and (2) determining the teams that advance to the following stage and the winners of the tournament.

It is possible that exact positions of two teams can't be established (e.g. they both have equal scores according to the sorting procedure). In such case TGroup will record a "sorting conflict".

Sorting conflict are represented by the TSortConflict class and managed by the TSConflicts class. Each TGroup uses a TSConflicts instance to handle sorting conflicts.

Position of a team within a group is represented by a pair of unsigned integers. They are equal for a team without a sorting conflict. If, on the other hand, a team has a sorting conflict, the integers will differ. In the case it is essential to know the team at certain position (like in this scenario: "the team at first place in group G advances to the next stage"), the sorting conflict that prevents exact positioning will have to be resolved.

Resolution of sorting conflicts falls outside of the tournament rules defined in Scheme. libtour provides an interface for resolving sorting conflicts (see section Modifying queries).

For comparing personal results between two teams (as it is often required by the tournament's rules), TGroup can create and delete a helper TGroup within itself. See section common module, *make-tmp-group* for an example how temporary groups can be utilized.

Saving and restoring state

A user application can ask the TTour instance for a Scheme data structure that encapsulates its current state. This data structure can be used later to restore the state of the tournament.

The following information is stored for each stage:

Here is the structure of the state list:

(<stage-list> ...)

("stage-id" <game-result-list> <sort-conflict-list>)

(("game-id" . result-list) ...)

result-list: list of numbers as long as the game field list
(see section Standings and Game result fields definitions)

(<group-sc> ...)

("group-id" <sc> ...)

(("stage-local-team1-id" . <resolution>)
 ("stage-local-team2-id" . <resolution>) ...)

-1 if the conflict is not resolved, 0 or greater to
notify relative position of the team within the conflict

When feeding the state data structure at the tournament loading time, libtour will pass the game results (see section Game results and formula) as well as sorting conflict information to the stages so that the correct positioning of the teams within the underlying groups will be restored.

Go to the first, previous, next, last section, table of contents.