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.
TTour
class represents a tournament. It contains:
TStage
objects)
TTeam
objects where keys are
global team IDs)
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:
TGroup
objects)
TTeam
objects stored in
the TTour
instance)
TGame
objects) with optionally set results
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).
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
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:
TTeam
objects, and
game schedule usually contains games
|---------------| 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.
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.
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:
NONE
SUMMARY
stage definition field
(see section Teams comparison procedure)
ANY
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
'(("field" . symbol) ("action" . symbol) ("values" . list))The supported argument types are defined in `t_constants.hh' header and include
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):
docs
property of the filter procedure(s)
NONE
SUMMARY
stage definition field
(see section Summary definition) if supplied, or the base group's comparison
procedure (see section Teams comparison 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.
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.
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-list: ("stage-id" <game-result-list> <sort-conflict-list>) game-result-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) sort-conflict-list: (<group-sc> ...) group-sc: ("group-id" <sc> ...) sc: (("stage-local-team1-id" . <resolution>) ("stage-local-team2-id" . <resolution>) ...) 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.