Included gomill framework for SGF and GTP support, and sketched out SGF game-loading code.

This commit is contained in:
2012-04-21 04:27:05 -04:00
parent 700a6a2f32
commit 692dc294d6
119 changed files with 27458 additions and 3 deletions

43
gomill/docs/_static/gomill.css_t vendored Normal file
View File

@ -0,0 +1,43 @@
@import url("default.css");
p.topic-title {
color: {{ theme_headtextcolor }};
}
dl.setting dd > p:first-child,
dl.mc-setting dd > p:first-child,
dl.ce-setting dd > p:first-child,
dl.gtp dd > p:first-child {
font-style: italic;
}
tt.std-gtp span.pre {
white-space: nowrap;
}
li.current > a {color: yellow;}
div.tip {
background-color: #EEEEEE;
border: 1px solid #CCCCCC;
}
div.caution {
background-color: #EEEEEE;
border: 1px solid #A00000;
}
abbr {
border-bottom: none; cursor:help;
}
th.field-name {
background-color: #E4E4E4;
font-weight: normal;
}
div#library-overview table.docutils {
width : 100%;
margin-bottom: 3ex;
}

35
gomill/docs/_templates/genindex.html vendored Normal file
View File

@ -0,0 +1,35 @@
{% extends "!genindex.html" %}
{% block body %}
<h1 id="index">{{ _('Index') }}</h1>
<div class="genindex-jumpbox">
{% for key, dummy in genindexentries -%}
<a href="#{{ key }}"><strong>{{ key }}</strong></a> {% if not loop.last %}| {% endif %}
{%- endfor %}
</div>
{%- for key, entries in genindexentries %}
<h2 id="{{ key }}">{{ key }}</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td valign="top"><dl>
{%- for entryname, (links, subitems) in entries %}
<dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a>
{%- for link in links[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor %}
{%- else %}{{ entryname|e }}{% endif %}</dt>
{%- if subitems %}
<dd><dl>
{%- for subentryname, subentrylinks in subitems %}
<dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a>
{%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor -%}
</dt>
{%- endfor %}
</dl></dd>
{%- endif -%}
{%- endfor %}
</dl></td>
</tr></table>
{% endfor %}
{% endblock %}

2
gomill/docs/_templates/wholetoc.html vendored Normal file
View File

@ -0,0 +1,2 @@
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
{{ toctree(collapse=False) }}

120
gomill/docs/allplayalls.rst Normal file
View File

@ -0,0 +1,120 @@
.. index:: all-play-all
All-play-all tournaments
^^^^^^^^^^^^^^^^^^^^^^^^
:setting:`competition_type` string: ``"allplayall"``.
In an all-play-all tournament the control file lists a number of players (the
:dfn:`competitors`), and games are played between each possible pairing.
All games are played with no handicap and with the same komi. The players in
each pairing will swap colours in successive games.
For most purposes an all-play-all tournament is equivalent to a playoff
tournament with a matchup defined for each pair of competitors; the main
difference is that reports include a results summary grid.
The tournament runs until :aa-setting:`rounds` games have been played between
each pairing (indefinitely, if :aa-setting:`rounds` is unset).
.. contents:: Page contents
:local:
:backlinks: none
.. _sample_allplayall_control_file:
Sample control file
"""""""""""""""""""
Here is a sample control file::
competition_type = 'allplayall'
players = {
'gnugo-l1' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=1"),
'gnugo-l2' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=2"),
'gnugo-l3' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=3"),
}
board_size = 9
komi = 6
rounds = 20
competitors = ['gnugo-l1', 'gnugo-l2', 'gnugo-l3']
.. _allplayall_control_file_settings:
Control file settings
"""""""""""""""""""""
The following settings can be set at the top level of the control file:
All :ref:`common settings <common settings>`.
The following game settings: :setting:`board_size`, :setting:`komi`,
:setting:`move_limit`, :setting:`scorer`.
The following additional settings:
.. aa-setting:: competitors
List of :ref:`player codes <player codes>`.
This defines which players will take part. Reports will list the players
in the order in which they appear here. You may not list the same player
more than once.
.. aa-setting:: rounds
Integer (default ``None``)
The number of games to play for each pairing. If you leave this unset, the
tournament will continue indefinitely.
The only required settings are :setting:`competition_type`,
:setting:`players`, :aa-setting:`competitors`, :setting:`board_size`, and
:setting:`komi`.
Reporting
"""""""""
The :ref:`live display <live_display>` and :ref:`competition report
<competition report file>` summarise the tournament results in the form of a
grid, for example::
A B C
A gnugo-l1 4-5 3-5
B gnugo-l2 5-4 3-5
C gnugo-l3 5-3 5-3
Each row shows the number of wins and losses for the player named on that row
against each opponent (in the example, ``gnugo-l1`` has won 4 games and lost 5
against ``gnugo-l2``).
If any games have unknown results (because they could not be scored, or
reached the :setting:`move_limit`), they will not be shown in the grid.
The competition report also shows full details of each pairing in the same
style as playoff tournaments.
For purposes of the :doc:`tournament results API <tournament_results>`, the
matchup ids are of the form ``AvB`` (using the competitor letters shown in the
results grid).
Changing the control file between runs
""""""""""""""""""""""""""""""""""""""
You can add new players to the end of the :aa-setting:`competitors` list
between runs, but you may not remove or reorder competitors.

View File

@ -0,0 +1,52 @@
The :mod:`~gomill.ascii_boards` module
--------------------------------------
.. module:: gomill.ascii_boards
:synopsis: ASCII Go board diagrams.
The :mod:`!gomill.ascii_boards` module contains functions for producing and
interpreting ASCII diagrams of Go board positions.
.. function:: render_board(board)
:rtype: string
Returns an ASCII diagram of the position on the :class:`.Board` *board*.
The returned string does not end with a newline.
::
>>> b = boards.Board(9)
>>> b.play(2, 5, 'b')
>>> b.play(3, 6, 'w')
>>> print ascii_boards.render_board(b)
9 . . . . . . . . .
8 . . . . . . . . .
7 . . . . . . . . .
6 . . . . . . . . .
5 . . . . . . . . .
4 . . . . . . o . .
3 . . . . . # . . .
2 . . . . . . . . .
1 . . . . . . . . .
A B C D E F G H J
See also the :script:`show_sgf.py` example script.
.. function:: interpret_diagram(diagram, size[, board])
:rtype: :class:`.Board`
Returns the position given in an ASCII diagram.
*diagram* must be a string in the format returned by :func:`render_board`,
representing a position with the specified size.
Raises :exc:`ValueError` if it can't interpret the diagram.
If the optional *board* parameter is provided, it must be an empty
:class:`.Board` of the right size; the same object will be returned (this
option is provided so you can use a different Board class).

114
gomill/docs/boards.rst Normal file
View File

@ -0,0 +1,114 @@
The :mod:`~gomill.boards` module
--------------------------------
.. module:: gomill.boards
:synopsis: Go board representation.
The :mod:`!gomill.boards` module contains Gomill's Go board representation.
Everything in this module works with boards of arbitrarily large sizes.
The implementation is not designed for speed (even as Python code goes), and
is certainly not appropriate for implementing a playing engine.
The module contains a single class:
.. class:: Board(side)
A :class:`!Board` object represents a legal position on a Go board.
Instantiate with the board size, as an int >= 1. Only square boards are
supported. The board is initially empty.
Board objects do not maintain any history information.
Board objects have the following attributes (which should be treated as
read-only):
.. attribute:: side
The board size.
.. attribute:: board_points
A list of *points*, giving all points on the board.
The principal :class:`!Board` methods are :meth:`!get` and :meth:`!play`.
Their *row* and *col* parameters should be ints representing coordinates in
the :ref:`system <go_related_data_representation>` used for a *point*.
The behaviour of :class:`!Board` methods is unspecified if they are passed
out-of-range coordinates.
.. method:: Board.get(row, col)
:rtype: *colour* or ``None``
Returns the contents of the specified point.
.. method:: Board.play(row, col, colour)
:rtype: *move*
Places a stone of the specified *colour* on the specified point.
Raises :exc:`ValueError` if the point isn't empty.
Carries out any captures which follow from the placement, including
self-captures.
This method doesn't enforce any ko rule.
The return value indicates whether, immediately following this move, any
point would be forbidden by the :term:`simple ko` rule. If so, that point
is returned; otherwise the return value is ``None``.
The other :class:`!Board` methods are:
.. method:: Board.is_empty()
:rtype: bool
Returns ``True`` if all points on the board are empty.
.. method:: Board.list_occupied_points()
:rtype: list of pairs (*colour*, *point*)
Returns a list of all nonempty points, in unspecified order.
.. method:: Board.area_score()
:rtype: int
Calculates the area score of a position, assuming that all stones are
alive. The result is the number of points controlled (occupied or
surrounded) by Black minus the number of points controlled by White.
Doesn't take any :term:`komi` into account.
.. method:: Board.copy()
:rtype: :class:`!Board`
Returns an independent copy of the board.
.. method:: Board.apply_setup(black_points, white_points, empty_points)
:rtype: bool
Adds and/or removes stones on arbitrary points. This is intended to support
behaviour like |sgf| ``AB``/``AW``/``AE`` properties.
Each parameter is an iterable of *points*.
This method applies all the specified additions and removals, then removes
any groups with no liberties (so the resulting position is always legal).
If the same point is specified in more than one list, the order in which
the instructions are applied is undefined.
Returns ``True`` if the position was legal as specified.

376
gomill/docs/cem_tuner.rst Normal file
View File

@ -0,0 +1,376 @@
.. |ce| replace:: :ref:`[CE] <ce_paper>`
The cross-entropy tuner
^^^^^^^^^^^^^^^^^^^^^^^
:setting:`competition_type` string: ``"ce_tuner"``.
The cross-entropy tuner uses the :dfn:`cross-entropy method` described in
|ce|:
.. _ce_paper:
| [CE] G.M.J-B. Chaslot, M.H.M Winands, I. Szita, and H.J. van den Herik.
| Cross-entropy for Monte-Carlo Tree Search. ICGA Journal, 31(3):145-156.
| http://www.personeel.unimaas.nl/g-chaslot/papers/crossmcICGA.pdf
.. caution:: The cross-entropy tuner is experimental. It can take a very large
number of games to converge.
.. contents:: Page contents
:local:
:backlinks: none
The tuning algorithm
""""""""""""""""""""
The algorithm is not described in detail in this documentation. See |ce|
section 3 for the description. The tuner always uses a Gaussian distribution.
The improvement suggested in section 5 is not implemented.
.. _ce parameter model:
The parameter model
"""""""""""""""""""
The parameter values taken from the Gaussian distribution are floating-point
numbers known as :dfn:`optimiser parameters`.
These parameters can be transformed before being used to configure the
candidate (see 3.3 *Normalising Parameters* in |ce|). The transformed values
are known as :dfn:`engine parameters`. The transformation is implemented using
a Python :ce-setting:`transform` function defined in the control file.
Reports show engine parameters (see the :ce-setting:`format` parameter
setting), together with the mean and variance of the corresponding optimiser
parameter distribution in the form :samp:`{mean}~{variance}`.
.. _the cem tuning algorithm:
.. _sample_cem_control_file:
Sample control file
"""""""""""""""""""
Here is a sample control file, illustrating most of the available settings for
a cross-entropy tuning event::
competition_type = "ce_tuner"
description = """\
This is a sample control file.
It illustrates the available settings for the cross entropy tuner.
"""
players = {
'gnugo-l10' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=10"),
}
def fuego(max_games, additional_commands=[]):
commands = [
"go_param timelimit 999999",
"uct_max_memory 350000000",
"uct_param_search number_threads 1",
"uct_param_player reuse_subtree 0",
"uct_param_player ponder 0",
"uct_param_player max_games %d" % max_games,
]
return Player(
"fuego --quiet",
startup_gtp_commands=commands+additional_commands)
FUEGO_MAX_GAMES = 1000
def exp_10(f):
return 10.0**f
parameters = [
Parameter('rave_weight_initial',
# Mean and variance are in terms of log_10 (rave_weight_initial)
initial_mean = -1.0,
initial_variance = 1.5,
transform = exp_10,
format = "I: %4.2f"),
Parameter('rave_weight_final',
# Mean and variance are in terms of log_10 (rave_weight_final)
initial_mean = 3.5,
initial_variance = 1.5,
transform = exp_10,
format = "F: %4.2f"),
]
def make_candidate(rwi, rwf):
return fuego(
FUEGO_MAX_GAMES,
["uct_param_search rave_weight_initial %f" % rwi,
"uct_param_search rave_weight_final %f" % rwf])
board_size = 9
komi = 7.5
opponent = 'gnugo-l10'
candidate_colour = 'w'
number_of_generations = 5
samples_per_generation = 100
batch_size = 10
elite_proportion = 0.1
step_size = 0.8
.. _cem_control_file_settings:
Control file settings
"""""""""""""""""""""
The following settings can be set at the top level of the control file:
All :ref:`common settings <common settings>` (the :setting:`players`
dictionary is required, though it is used only to define the opponent).
The following game settings (only :setting:`!board_size` and :setting:`!komi`
are required):
- :setting:`board_size`
- :setting:`komi`
- :setting:`handicap`
- :setting:`handicap_style`
- :setting:`move_limit`
- :setting:`scorer`
The following additional settings (they are all required):
.. ce-setting:: candidate_colour
String: ``"b"`` or ``"w"``
The colour for the candidates to take in every game.
.. ce-setting:: opponent
Identifier
The :ref:`player code <player codes>` of the player to use as the
candidates' opponent.
.. ce-setting:: parameters
List of :ce-setting-cls:`Parameter` definitions (see :ref:`ce parameter
configuration`).
Describes the parameters that the tuner will work with. See :ref:`ce
parameter model` for more details.
The order of the :ce-setting-cls:`Parameter` definitions is used for the
arguments to :ce-setting:`make_candidate`, and whenever parameters are
described in reports or game records.
.. ce-setting:: make_candidate
Python function
Function to create a :setting-cls:`Player` from its engine parameters.
This function is passed one argument for each candidate parameter, and must
return a :setting-cls:`Player` definition. Each argument is the output of
the corresponding Parameter's :ce-setting:`transform`.
The function will typically use its arguments to construct command line
options or |gtp| commands for the player. For example::
def make_candidate(param1, param2):
return Player(["goplayer", "--param1", str(param1),
"--param2", str(param2)])
def make_candidate(param1, param2):
return Player("goplayer", startup_gtp_commands=[
["param1", str(param1)],
["param2", str(param2)],
])
.. ce-setting:: number_of_generations
Positive integer
The number of times to repeat the tuning algorithm (*number of iterations*
or *T* in the terminology of |ce|).
.. ce-setting:: samples_per_generation
Positive integer
The number of candidates to make in each generation (*population_size* or
*N* in the terminology of |ce|).
.. ce-setting:: batch_size
Positive integer
The number of games played by each candidate.
.. ce-setting:: elite_proportion
Float between 0.0 and 1.0
The proportion of candidates to select from each generation as 'elite' (the
*selection ratio* or *ρ* in the terminology of |ce|). A value between 0.01
and 0.1 is recommended.
.. ce-setting:: step_size
Float between 0.0 and 1.0
The rate at which to update the distribution parameters between generations
(*α* in the terminology of |ce|).
.. caution:: I can't find anywhere in the paper the value they used for
this, so I don't know what to recommend.
.. _ce parameter configuration:
Parameter configuration
"""""""""""""""""""""""
.. ce-setting-cls:: Parameter
A :ce-setting-cls:`!Parameter` definition has the same syntax as a Python
function call: :samp:`Parameter({arguments})`. Apart from :ce-setting:`!code`,
the arguments should be specified using keyword form (see
:ref:`sample_cem_control_file`).
The :ce-setting:`code`, :ce-setting:`initial_mean`, and
:ce-setting:`initial_variance` arguments are required.
The arguments are:
.. ce-setting:: code
Identifier
A short string used to identify the parameter. This is used in error
messages, and in the default for :ce-setting:`format`.
.. ce-setting:: initial_mean
Float
The mean value for the parameter in the first generation's distribution.
.. ce-setting:: initial_variance
Float >= 0
The variance for the parameter in the first generation's distribution.
.. ce-setting:: transform
Python function (default identity)
Function mapping an optimiser parameter to an engine parameter; see :ref:`ce
parameter model`.
Examples::
def exp_10(f):
return 10.0**f
Parameter('p1', initial_mean = …, initial_variance = …,
transform = exp_10)
If the :ce-setting:`!transform` is not specified, the optimiser parameter is
used directly as the engine parameter.
.. ce-setting:: format
String (default :samp:`"{parameter_code}: %s"`)
Format string used to display the parameter value. This should include a
short abbreviation to indicate which parameter is being displayed, and also
contain ``%s``, which will be replaced with the engine parameter value.
You can use any Python conversion specifier instead of ``%s``. For example,
``%.2f`` will format a floating point number to two decimal places. ``%s``
should be safe to use for all types of value. See `string formatting
operations`__ for details.
.. __: http://docs.python.org/release/2.7/library/stdtypes.html#string-formatting-operations
Format strings should be kept short, as screen space is limited.
Examples::
Parameter('parameter_1',
initial_mean = 0.0, initial_variance = 1.0,
format = "p1: %.2f")
Parameter('parameter_2',
initial_mean = 5000, initial_variance = 250000,
format = "p2: %d")
Reporting
"""""""""
Currently, there aren't any sophisticated reports.
The standard report shows the parameters of the current Gaussian distribution,
and the number of wins for each candidate in the current generation.
After each generation, the details of the candidates are written to the
:ref:`history file <logging>`. The candidates selected as elite are marked
with a ``*``.
Changing the control file between runs
""""""""""""""""""""""""""""""""""""""
Some settings can safely be changed between runs of the same cross-entropy
tuning event:
:ce-setting:`batch_size`
safe to increase
:ce-setting:`samples_per_generation`
not safe to change
:ce-setting:`number_of_generations`
safe to change
:ce-setting:`elite_proportion`
safe to change
:ce-setting:`step_size`
safe to change
:ce-setting:`make_candidate`
safe to change, but don't alter play-affecting options
:ce-setting:`transform`
not safe to change
:ce-setting:`format`
safe to change

93
gomill/docs/changes.rst Normal file
View File

@ -0,0 +1,93 @@
Changes
=======
Gomill 0.7.2 (2011-09-05)
-------------------------
* Added the *wrap* parameter to :meth:`.Sgf_game.serialise`.
* Added the :script:`gomill-clop` example script.
Gomill 0.7.1 (2011-08-15)
-------------------------
Bug-fix release.
* Bug fix: made board sizes 24 and 25 work (column lettering, and therefore
|gtp| support, was incorrect for these sizes in all previous versions).
* Tightened up input validation for :func:`.format_vertex` and
:func:`.colour_name`.
* Distinguished Stone, Point, and Move in the :ref:`sgf_property_types`
table in |sgf| documentation.
Gomill 0.7 (2011-08-13)
-----------------------
The ringmaster now applies handicap stone compensation when using its internal
scorer. Set :setting:`internal_scorer_handicap_compensation` to ``"no"`` to
return to the old behaviour.
* Added a full implementation of :doc:`sgf`, replacing the previous minimal
support.
* Added a :script:`split_sgf_collection.py` example script.
* The :mod:`~gomill.common`, :mod:`~gomill.boards`,
:mod:`~gomill.ascii_boards`, and :mod:`~gomill.handicap_layout` modules are
now documented as stable.
* Improved handling of long responses to the :gtp:`!version` |gtp| command.
* Added support for handicap stone compensation when scoring games.
* Gomill now checks the response to the :gtp:`!fixed_handicap` |gtp| command.
* Added the :data:`gomill.__version__` constant.
Changes to (previously) undocumented parts of the library:
* Renamed the :mod:`!gomill.gomill_common` module to :mod:`!gomill.common`.
* Renamed the :mod:`!gomill.gomill_utils` module to :mod:`!gomill.utils`.
* Renamed :attr:`!Board.board_coords` to :attr:`~.Board.board_points`.
* Replaced the :func:`!ascii_boards.play_diagram` function with
:func:`~.ascii_boards.interpret_diagram`, making the *board* parameter
optional.
* :func:`!gtp_engine.interpret_float` now rejects infinities and NaNs.
* Changes to the :mod:`!gtp_states` module: tightened error handling, removed
the komi-mangling feature, renamed :attr:`!History_move.coords` to
:attr:`!History_move.move`.
Gomill 0.6 (2011-02-13)
-----------------------
Playoff tournament :ref:`state files <competition state>` from Gomill 0.5 are
incompatible with Gomill 0.6. Tuning event state files are compatible.
* Added the :doc:`All-play-all <allplayalls>` tournament type.
* Expanded and documented the :doc:`tournament_results`. Changed return type
of
:meth:`~.Tournament_results.get_matchup_results`.
* Fixed reporting for matchups with the same player specified twice.
* Allowed arbitrary filename extensions for control files.
Gomill 0.5 (2010-10-29)
-----------------------
* First public release.

74
gomill/docs/common.rst Normal file
View File

@ -0,0 +1,74 @@
The :mod:`~gomill.common` module
--------------------------------
.. module:: gomill.common
:synopsis: Go-related utility functions.
The :mod:`!gomill.common` module provides Go-related utility functions, used
throughout Gomill.
It is designed to be safe to use as ``from common import *``.
.. function:: opponent_of(colour)
:rtype: *colour*
Returns the other colour::
>>> opponent_of('b')
'w'
.. function:: colour_name(colour)
:rtype: string
Returns the (lower-case) full name of a *colour*::
>>> colour_name('b')
'black'
.. function:: format_vertex(move)
:rtype: string
Returns a string describing a *move* in conventional notation::
>>> format_vertex((3, 0))
'A4'
>>> format_vertex(None)
'pass'
The result is suitable for use directly in |GTP| responses. Note that ``I``
is omitted from the letters used to indicate columns, so the maximum
supported column value is ``25``.
.. function:: format_vertex_list(moves)
:rtype: string
Returns a string describing a sequence of *moves*::
>>> format_vertex_list([(0, 1), (2, 3), None])
'B1,D3,pass'
>>> format_vertex_list([])
''
.. function:: move_from_vertex(vertex, board_size)
:rtype: *move*
Interprets the string *vertex* as conventional notation, assuming a square
board whose side is *board_size*::
>>> move_from_vertex("A4", 9)
(3, 0)
>>> move_from_vertex("a4", 9)
(3, 0)
>>> move_from_vertex("pass", 9)
None
Raises :exc:`ValueError` if it can't parse the string, or if the resulting
point would be off the board.
Treats *vertex* case-insensitively.

View File

@ -0,0 +1,64 @@
.. index:: competition type
.. _competition types:
Competition types
-----------------
The ringmaster supports a number of different :dfn:`competition types`. These
are divided into :dfn:`tournaments` and :dfn:`tuning events`.
.. contents:: Page contents
:local:
:backlinks: none
.. index:: tournament
.. _tournaments:
Tournaments
^^^^^^^^^^^
A :dfn:`tournament` is a form of competition in which the ringmaster plays
games between predefined players, in order to compare their strengths.
There are currently two types of tournament:
.. toctree::
:maxdepth: 3
:titlesonly:
Playoff <playoffs>
All-play-all <allplayalls>
.. index:: tuning event
.. _tuners:
Tuning events
^^^^^^^^^^^^^
A :dfn:`tuning event` is a form of competition in which the ringmaster runs an
algorithm which adjusts engine parameters to try to find the values which give
strongest play.
.. index:: opponent
At present, all tuning events work by playing games between different
:dfn:`candidate` players and a single fixed :dfn:`opponent` player. The
candidate always takes the same colour. The komi and any handicap can be
specified as usual.
There are currently two tuning algorithms:
.. toctree::
:maxdepth: 3
:titlesonly:
Monte Carlo <mcts_tuner>
Cross-entropy <cem_tuner>

View File

@ -0,0 +1,370 @@
.. _running competitions:
Running competitions
--------------------
.. contents:: Page contents
:local:
:backlinks: none
Pairings
^^^^^^^^
When a competition is run, the ringmaster will launch one or more games
between pairs of players.
For playoff tournaments, the pairings are determined by the
:pl-setting-cls:`Matchup` descriptions in the control file. If more than one
matchup is specified, the ringmaster prefers to start games from the matchup
which has played fewest games.
For all-play-all tournaments, the ringmaster will again prefer the pair of
:aa-setting:`competitors` which has played the fewest games.
For tuning events, the pairings are specified by a tuning algorithm.
.. _simultaneous games:
Simultaneous games
^^^^^^^^^^^^^^^^^^
The ringmaster can run more than one game at a time, if the
:option:`--parallel <ringmaster --parallel>` command line option is specified.
This can be useful to keep processor cores busy, or if the actual playing
programs are running on different machines to the ringmaster.
Normally it makes no difference whether the ringmaster starts games in
sequence or in parallel, but it does have an effect on the :doc:`Monte Carlo
tuner <mcts_tuner>`, as in parallel mode it will have less information each
time it chooses a candidate player.
.. tip:: Even if an engine is capable of using multiple threads, it may be
better to use a single-threaded configuration during development to get
reproducible results, or to be sure that system load does not affect play.
.. tip:: When deciding how many games to run in parallel, remember to take
into account the amount of memory needed, as well as the number of
processor cores available.
.. _live_display:
Display
^^^^^^^
While the competition runs, the ringmaster displays a summary of the
tournament results (or of the tuning algorithm status), a list of games in
progress, and a list of recent game results. For example, in a playoff
tournament with a single matchup::
2 games in progress: 0_2 0_4
(Ctrl-X to halt gracefully)
gnugo-l1 v gnugo-l2 (3/5 games)
board size: 9 komi: 7.5
wins black white avg cpu
gnugo-l1 2 66.67% 1 100.00% 1 50.00% 1.13
gnugo-l2 1 33.33% 1 50.00% 0 0.00% 1.32
2 66.67% 1 33.33%
= Results =
game 0_1: gnugo-l2 beat gnugo-l1 B+8.5
game 0_0: gnugo-l1 beat gnugo-l2 B+33.5
game 0_3: gnugo-l1 beat gnugo-l2 W+2.5
Use :ref:`quiet mode <quiet mode>` to turn this display off.
.. _stopping competitions:
Stopping competitions
^^^^^^^^^^^^^^^^^^^^^
Unless interrupted, a run will continue until either the competition completes
or the per-run limit specified by the :option:`--max-games
<ringmaster --max-games>` command line option is reached.
Type :kbd:`Ctrl-X` to stop a run. The ringmaster will wait for all games in
progress to complete, and then exit (the stop request won't be acknowledged on
screen until the next game result comes in).
It's also reasonable to stop a run with :kbd:`Ctrl-C`; games in progress will
be terminated immediately (assuming the engine processes are well-behaved).
The partial games will be forgotten; the ringmaster will replay them as
necessary if the competition is resumed later.
You can also stop a competition by running the command line :action:`stop`
action from a shell; like :kbd:`Ctrl-X`, this will be acknowledged when the
next game result comes in, and the ringmaster will wait for games in progress
to complete.
Running players
^^^^^^^^^^^^^^^
The ringmaster requires the player engines to be standalone executables which
speak :term:`GTP` version 2 on their standard input and output streams.
It launches the executables itself, with command line arguments and other
environment as detailed by the :ref:`player settings <player configuration>`
in the control file.
It launches a new engine subprocess for each game and closes it when the game
is terminated.
.. tip:: To run a player on a different computer to the ringmaster, specify a
suitable :program:`ssh` command line in the :setting-cls:`Player`
definition.
See :ref:`engine errors` and :ref:`engine exit behaviour` for details of what
happens if engines misbehave.
.. index:: rules, ko, superko
.. _playing games:
Playing games
^^^^^^^^^^^^^
The :setting:`board_size`, :setting:`komi`, :setting:`handicap`, and
:setting:`handicap_style` game settings control the details of the game. The
ringmaster doesn't know or care what rule variant the players are using; it's
up to you to make sure they agree with each other.
Any :setting:`startup_gtp_commands` configured for a player will be sent
before the :gtp:`!boardsize` and :gtp:`!clear_board` commands. Failure responses
from these commands are ignored.
Each game normally continues until both players pass in succession, or one
player resigns.
The ringmaster rejects moves to occupied points, and moves forbidden by
:term:`simple ko`, as illegal. It doesn't reject self-capture moves, and it
doesn't enforce any kind of :term:`superko` rule. If the ringmaster rejects a
move, the player that tried to make it loses the game by forfeit.
If one of the players rejects a move as illegal (ie, with the |gtp| failure
response ``illegal move``), the ringmaster assumes its opponent really has
played an illegal move and so should forfeit the game (this is convenient if
you're testing an experimental engine against an established one).
If one of the players returns any other |gtp| failure response (either to
:gtp:`!genmove` or to :gtp:`!play`), or an uninterpretable response to
:gtp:`!genmove`, it forfeits the game.
If the game lasts longer than the configured :setting:`move_limit`, it is
stopped at that point, and recorded as having an unknown result (with |sgf|
result ``Void``).
See also :ref:`claiming wins`.
.. note:: The ringmaster does not provide a game clock, and it does not
use any of the |gtp| time handling commands. Players should normally be
configured to use a fixed amount of computing power, independent of
wall-clock time.
.. index:: handicap compensation
.. _scoring:
Scoring
^^^^^^^
The ringmaster has two scoring methods: ``players`` (which is the default),
and ``internal``. The :setting:`scorer` game setting determines which is used.
When the ``players`` method is used, the players are asked to score the game
using the |gtp| :gtp:`!final_score` command. See also the
:setting:`is_reliable_scorer` player setting.
When the ``internal`` method is used, the ringmaster scores the game itself,
area-fashion. It assumes that all stones remaining on the board at the end of
the game are alive. It applies :setting:`komi`.
In handicap games, the internal scorer can also apply handicap stone
compensation, controlled by the
:setting:`internal_scorer_handicap_compensation` game setting: ``"full"`` (the
default) means that White is given an additional point for each handicap
stone, ``"short"`` means White is given an additional point for each handicap
stone except the first, and ``"no"`` means that no handicap stone compensation
is given.
.. _claiming wins:
Claiming wins
^^^^^^^^^^^^^
The ringmaster supports a protocol to allow players to declare that they have
won the game. This can save time if you're testing against opponents which
don't resign.
To support this, the player has to implement :gtp:`gomill-genmove_ex` and
recognise the ``claim`` keyword.
You must also set :setting:`allow_claim` ``True`` in the :setting-cls:`Player`
definition for this mechanism to be used.
The |sgf| result of a claimed game will simply be ``B+`` or ``W+``.
.. _startup checks:
Startup checks
^^^^^^^^^^^^^^
Whenever the ringmaster starts a run, before starting any games, it launches
an instance of each engine that will be required for the run and checks that
it operates reasonably.
If any engine fails the checks, the run is cancelled. The standard error
stream from the engines is suppressed for these automatic startup checks.
The :action:`check` command line action runs the same checks, but it leaves
the engines' standard error going to the console (any
:setting:`discard_stderr` player settings are ignored).
For playoff tournaments, only players listed in matchups are checked (and
matchups with :pl-setting:`number_of_games` set to ``0`` are ignored). If a
player appears in more than one matchup, the board size and komi from its
first matchup are used.
For all-play-all tournaments, all players listed as :aa-setting:`competitors`
are checked.
For tuning events, the opponent and one sample candidate are checked.
The checks are as follows:
- the engine subprocess starts, and replies to |gtp| commands
- the engine reports |gtp| protocol version 2 (if it supports
:gtp:`!protocol_version` at all)
- the engine accepts any :setting:`startup_gtp_commands`
- the engine accepts the required board size and komi
- the engine accepts the :gtp:`!clear_board` |gtp| command
.. _quiet mode:
.. index:: quiet mode
Quiet mode
^^^^^^^^^^
The :option:`--quiet <ringmaster --quiet>` command line option makes the
ringmaster run in :dfn:`quiet mode`. In this mode, it prints nothing to
standard output, and only errors and warnings to standard error.
This mode is suitable for running in the background.
:kbd:`Ctrl-X` still works in quiet mode to stop a run gracefully, if the
ringmaster process is in the foreground.
.. _output files:
.. _competition directory:
Output files
^^^^^^^^^^^^
.. index:: competition directory
The ringmaster writes a number of files, which it places in the directory
which contains the control file (the :dfn:`competition directory`). The
filename stem (the part before the filename extension) of each file is the
same as in the control file (:file:`{code}` in the table below).
The full set of files that may be present in the competition directory is:
======================= =======================================================
:file:`{code}.ctl` the :doc:`control file <settings>`
:file:`{code}.status` the :ref:`competition state <competition state>` file
:file:`{code}.log` the :ref:`event log <logging>`
:file:`{code}.hist` the :ref:`history file <logging>`
:file:`{code}.report` the :ref:`report file <competition report file>`
:file:`{code}.cmd` the :ref:`remote control file <remote control file>`
:file:`{code}.games/` |sgf| :ref:`game records <game records>`
:file:`{code}.void/` |sgf| game records for :ref:`void games <void games>`
:file:`{code}.gtplogs/` |gtp| logs
(from :option:`--log-gtp <ringmaster --log-gtp>`)
======================= =======================================================
The recommended filename extension for the control file is :file:`.ctl`, but
other extensions are allowed (except those listed in the table above).
.. _competition state:
Competition state
^^^^^^^^^^^^^^^^^
.. index:: state file
The competition :dfn:`state file` (:file:`{code}.state`) contains a
machine-readable description of the competition's results; this allows
resuming the competition, and also programmatically :ref:`querying the results
<querying the results>`. It is rewritten after each game result is received,
so that little information will be lost if the ringmaster stops ungracefully
for any reason.
The :action:`reset` command line action deletes **all** competition output
files, including game records and the state file.
State files written by one Gomill release may not be accepted by other
releases. See :doc:`changes` for details.
.. caution:: If the ringmaster loads a state file written by a hostile party,
it can be tricked into executing arbitrary code. On a shared system, do not
make the competition directory or the state file world-writeable.
.. index:: logging, event log, history file
.. _logging:
Logging
^^^^^^^
The ringmaster writes two log files: the :dfn:`event log` (:file:`{code}.log`)
and the :dfn:`history file` (:file:`{code}.hist`).
The event log has entries for competition runs starting and finishing and for
games starting and finishing, including details of errors from games which
fail. It may also include output from the players' :ref:`standard error
streams <standard error>`, depending on the :setting:`stderr_to_log` setting.
The history file has entries for game results, and in tuning events it
may have periodic descriptions of the tuner status.
Also, if the :option:`--log-gtp <ringmaster --log-gtp>` command line option is
passed, the ringmaster logs all |gtp| commands and responses. It writes a
separate log file for each game, in the :file:`{code}.gtplogs` directory.
.. _standard error:
Players' standard error
^^^^^^^^^^^^^^^^^^^^^^^
By default, the players' standard error streams are sent to the ringmaster's
:ref:`event log <logging>`. All players write to the same log, so there's no
direct indication of which messages came from which player (the log entries
for games starting and completing may help).
If the competition setting :setting:`stderr_to_log` is False, the players'
standard error streams are left unchanged from the ringmaster's. This is only
useful in :ref:`quiet mode <quiet mode>`, or if you redirect the ringmaster's
standard error.
You can send standard error for a particular player to :file:`/dev/null` using
the player setting :setting:`discard_stderr`. This can be used for players
which like to send copious diagnostics to stderr, but if possible it is better
to configure the player not to do that, so that any real error messages aren't
hidden (eg with a command line option like ``fuego --quiet``).

124
gomill/docs/conf.py Normal file
View File

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
needs_sphinx = '1.0'
extensions = ['sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.intersphinx',
'sphinx.ext.viewcode']
templates_path = ['_templates']
source_suffix = '.rst'
source_encoding = 'utf-8'
master_doc = 'index'
project = u'gomill'
copyright = u'2009-2011, Matthew Woodcraft'
version = '0.7.2'
release = '0.7.2'
unused_docs = []
exclude_dirnames = ['.git']
pygments_style = 'vs'
modindex_common_prefix = ['gomill.']
html_theme = 'default'
html_theme_options = {
'nosidebar' : False,
#'rightsidebar' : True,
'stickysidebar' : False,
'footerbgcolor' : '#3d3011',
#'footertextcolor' : ,
'sidebarbgcolor' : '#3d3011',
#'sidebartextcolor' : ,
'sidebarlinkcolor' : '#d8d898',
'relbarbgcolor' : '#523f13',
#'relbartextcolor' : ,
#'relbarlinkcolor' : ,
#'bgcolor' : ,
#'textcolor' : ,
'linkcolor' : '#7c5f35',
'visitedlinkcolor' : '#7c5f35',
#'headbgcolor' : ,
'headtextcolor' : '#5c4320',
#'headlinkcolor' : ,
#'codebgcolor' : ,
#'codetextcolor' : ,
'externalrefs' : True,
}
html_static_path = ['_static']
html_add_permalinks = False
html_copy_source = False
html_sidebars = {'**' : ['wholetoc.html', 'relations.html', 'searchbox.html']}
html_style = "gomill.css"
html_show_sourcelink = False
pngmath_use_preview = True
todo_include_todos = True
intersphinx_mapping = {'python': ('http://docs.python.org/2.7',
'python-inv.txt')}
rst_epilog = """
.. |gtp| replace:: :abbr:`GTP (Go Text Protocol)`
.. |sgf| replace:: :abbr:`SGF (Smart Game Format)`
"""
def setup(app):
app.add_object_type('action', 'action',
indextemplate='pair: %s; ringmaster action',
objname="Ringmaster action")
app.add_object_type('gtp', 'gtp',
indextemplate='pair: %s; GTP command',
objname="GTP command")
app.add_object_type('script', 'script',
indextemplate='pair: %s; example script',
objname="Example script")
app.add_object_type('setting', 'setting',
indextemplate='pair: %s; control file setting',
objname="Control file setting")
app.add_object_type('pl-setting', 'pl-setting',
indextemplate='pair: %s; Playoff tournament setting',
objname="Playoff tournament setting")
app.add_object_type('aa-setting', 'aa-setting',
indextemplate='pair: %s; All-play-all tournament setting',
objname="All-play-all tournament setting")
app.add_object_type('mc-setting', 'mc-setting',
indextemplate='pair: %s; Monte Carlo tuner setting',
objname="Monte Carlo tuner setting")
app.add_object_type('ce-setting', 'ce-setting',
indextemplate='pair: %s; cross-entropy tuner setting',
objname="Cross-entropy tuner setting")
app.add_crossref_type('setting-cls', 'setting-cls',
indextemplate='single: %s',
objname="Control file object")
app.add_crossref_type('pl-setting-cls', 'pl-setting-cls',
indextemplate='single: %s',
objname="Control file object")
app.add_crossref_type('mc-setting-cls', 'mc-setting-cls',
indextemplate='single: %s',
objname="Control file object")
app.add_crossref_type('ce-setting-cls', 'ce-setting-cls',
indextemplate='single: %s',
objname="Control file object")
# Undo undesirable sphinx code that auto-adds 'xref' class to literals 'True',
# 'False', and 'None'.
from sphinx.writers import html as html_mod
def visit_literal(self, node):
self.body.append(self.starttag(node, 'tt', '',
CLASS='docutils literal'))
self.protect_literal_text += 1
html_mod.HTMLTranslator.visit_literal = visit_literal

13
gomill/docs/contact.rst Normal file
View File

@ -0,0 +1,13 @@
Contact
=======
Gomill's home page is http://mjw.woodcraft.me.uk/gomill/.
Updated versions will be made available for download from that site.
I'm happy to receive any bug reports, suggestions, patches, questions and so
on at <matthew@woodcraft.me.uk>.
I'm particularly interested in hearing about any |gtp| engines (even buggy
ones) which don't work with the ringmaster.

187
gomill/docs/errors.rst Normal file
View File

@ -0,0 +1,187 @@
Error handling and exceptional situations
-----------------------------------------
This page contains some tedious details of the implementation; it might be of
interest if you're wondering whether the behaviour you see is intentional or a
bug.
.. contents:: Page contents
:local:
:backlinks: none
.. _game id:
Game identification
^^^^^^^^^^^^^^^^^^^
Each game played in a competition is identified using a short string (the
:dfn:`game_id`). This is used in the |sgf| :ref:`game record <game records>`
filename and game name (``GN``), the :ref:`log files <logging>`, the live
display, and so on.
For playoff tournaments, game ids are made up from the :pl-setting:`matchup id
<id>` and the number of the game within the matchup; for example, the first
game played might be ``0_0`` or ``0_000`` (depending on the value of
:pl-setting:`number_of_games`).
Similarly for all-play-all tournaments, game ids are like ``AvB_0``, using the
competitor letters shown in the results grid, with the length depending on the
:aa-setting:`rounds` setting.
.. _details of scoring:
Details of scoring
^^^^^^^^^^^^^^^^^^
If :setting:`scorer` is ``"players"`` but neither engine is able to score
(whether because :gtp:`!final_score` isn't implemented, or it fails, or
:setting:`is_reliable_scorer` is ``False``), the game result is reported as
unknown (|sgf| result ``?``).
If both engines are able to score but they disagree about the winner, the game
result is reported as unknown. The engines' responses to :gtp:`!final_score`
are recorded in |sgf| file comments.
If the engines agree about the winner but disagree about the winning margin,
the |sgf| result is simply ``B+`` or ``W+``, and the engines' responses are
recorded in |sgf| file comments.
.. _engine errors:
Engine errors
^^^^^^^^^^^^^
If an engine returns a |gtp| failure response to any of the commands which set
up the game (eg :gtp:`!boardsize` or :gtp:`!fixed_handicap`), the game is
treated as :ref:`void <void games>`.
If an engine fails to start, exits unexpectedly, or produces a |gtp| response
which is ill-formed at the protocol level, the game is treated as :ref:`void
<void games>`.
As an exception, if such an error happens after the game's result has been
established (in particular, if one player has already forfeited the game), the
game is not treated as void.
.. _engine exit behaviour:
Engine exit behaviour
^^^^^^^^^^^^^^^^^^^^^
Before reporting the game result, the ringmaster sends :gtp:`!quit` to both
engines, closes their input and output pipes, and waits for the subprocesses
to exit.
If an engine hangs (during the game or at exit), the ringmaster will just hang
too (or, if in parallel mode, one worker process will).
The exit status of engine subprocesses is ignored.
.. index:: void games
.. _void games:
Void games
^^^^^^^^^^
Void games are games which were not completed due to a software failure, and
which don't count as a forfeit by either engine.
Void games don't appear in the competition results. They're recorded in the
:ref:`event log <logging>`, and a warning is displayed on screen when they
occur.
If :setting:`record_games` is enabled, a game record will be written for each
void game that had at least one move played. These are placed in the
:file:`{code}.void/` subdirectory of the competition directory.
A void game will normally be replayed, with the same game id (the details
depend on the competition type; see below).
(Note that void games aren't the same thing as games whose |sgf| result is
``Void``; the ringmaster uses that result for games which exceed the
:setting:`move_limit`.)
Halting competitions due to errors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A single error which causes a void game will not normally cause a competition
to be prematurely halted, but multiple errors may.
The details depend on the competition type:
For playoff and all-play-all tournaments, a run is halted early if the first
game in any matchup is void, or if two games in a row for the same matchup are
void.
For tuning events, a run is halted immediately if the first game to finish is
void.
Otherwise, in Monte Carlo tuning events a void game will be ignored: a new
game will be scheduled from the current state of the MCTS tree (and the
original game number will be skipped). If two game results in a row are void,
the run will be halted.
In cross-entropy tuning events a void game will be replayed; if it fails
again, the run will be halted.
In parallel mode, outstanding games will be allowed to complete.
Preventing simultaneous runs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If :c:func:`!flock()` is available, the ringmaster will detect attempts to run
a competition which is already running (but this probably won't work if the
control file is on a network filesystem).
It's fine to use :action:`show` and :action:`report`, or the :doc:`tournament
results API <tournament_results>`, while a competition is running.
Signals and controlling terminal
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The check for :kbd:`Ctrl-X` uses the ringmaster's controlling terminal,
independently of stdin and stdout. If there's no controlling terminal, or
:mod:`termios` isn't available, this check is disabled.
The engine subprocesses are left attached to the ringmaster's controlling
terminal, so they will receive signals from :kbd:`Ctrl-C`; unless they detach
from their controlling terminal or ignore the signal, they should exit
cleanly in response.
Running the ringmaster in the background (including using :kbd:`Ctrl-Z`)
should work properly (you probably want :ref:`quiet mode <quiet mode>`).
.. _remote control file:
The remote control file
^^^^^^^^^^^^^^^^^^^^^^^
The :action:`stop` action is implemented by writing a :file:`{code}.cmd` file
to the competition directory.
Character encoding
^^^^^^^^^^^^^^^^^^
Gomill is designed for a UTF-8 environment; it is intended to work correctly
if non-ASCII characters provided as input are encoded in UTF-8, and to produce
terminal and report output in UTF-8.
Non-ASCII characters in the control file must be encoded in UTF-8.
|gtp| engines may return UTF-8 characters in in response to :gtp:`!name`,
:gtp:`!version`, :gtp:`gomill-describe_engine`, or
:gtp:`gomill-explain_last_move`.
SGF files written by Gomill always explicitly specify UTF-8 encoding.

View File

@ -0,0 +1,104 @@
.. _example scripts:
The example scripts
===================
The following example scripts are available in the :file:`gomill_examples/`
directory of the Gomill source distribution.
Some of them may be independently useful, as well as illustrating the library
API.
See the top of each script for further information.
See :ref:`running the example scripts <running the example scripts>` for notes
on making the :mod:`!gomill` package available for use with the example
scripts.
.. script:: show_sgf.py
Prints an ASCII diagram of the position from an |sgf| file.
This demonstrates the :mod:`~gomill.sgf`, :mod:`~gomill.sgf_moves`, and
:mod:`~gomill.ascii_boards` modules.
.. script:: split_sgf_collection.py
Splits a file containing an |sgf| game collection into multiple files.
This demonstrates the parsing functions from the :mod:`!sgf_grammar` module.
.. script:: twogtp
A 'traditional' twogtp implementation.
This demonstrates the :mod:`!gtp_games` module.
.. script:: find_forfeits.py
Finds the forfeited games from a playoff or all-play-all tournament.
This demonstrates the :doc:`tournament results API <tournament_results>`.
.. script:: gtp_test_player
A |gtp| engine intended for testing |gtp| controllers.
This demonstrates the low-level engine-side |gtp| code (the
:mod:`!gtp_engine` module).
.. script:: gtp_stateful_player
A |gtp| engine which maintains the board position.
This demonstrates the :mod:`!gtp_states` module, which can be used to make a
|gtp| engine from a stateless move-generating program, or to add commands
like :gtp:`!undo` and :gtp:`!loadsgf` to an engine which doesn't natively
support them.
.. script:: kgs_proxy.py
A |gtp| engine proxy intended for use with `kgsGtp`_. This produces game
records including the engine's commentary, if the engine supports
:gtp:`gomill-savesgf`.
.. _`kgsGtp`: http://senseis.xmp.net/?KgsGtp
This demonstrates the :mod:`!gtp_proxy` module, and may be independently
useful.
.. script:: mogo_wrapper.py
A |gtp| engine proxy intended for use with `Mogo`_. This can be used to run
Mogo with a |gtp| controller (eg `Quarry`_) which doesn't get on with Mogo's
|gtp| implementation.
.. _`Mogo`: http://www.lri.fr/~gelly/MoGo_Download.htm
.. _`Quarry`: http://home.gna.org/quarry/
This demonstrates the :mod:`!gtp_proxy` module, and may be independently
useful.
.. script:: gomill-clop
An experimental script for using Gomill as a back end for Rémi Coulom's CLOP
optimisation system. It has been tested with ``CLOP-0.0.8``, which can be
downloaded from http://remi.coulom.free.fr/CLOP/ .
To use it, write a control file based on :file:`clop_example.ctl` in the
:file:`gomill_examples/` directory, and run ::
$ gomill-clop <control file> setup
That will create a :samp:`.clop` file in the same directory as the control
file, which you can then run using :samp:`clop-gui`.

116
gomill/docs/glossary.rst Normal file
View File

@ -0,0 +1,116 @@
Glossary
========
.. glossary::
GTP
The Go Text Protocol
A communication protocol used to control Go-playing programs. Gomill
uses only GTP version 2, which is specified at
http://www.lysator.liu.se/~gunnar/gtp/gtp2-spec-draft2/gtp2-spec.html.
(As of August 2011, the specification describes itself as a draft, but it
has remained stable for several years and is widely implemented.)
SGF
The Smart Game Format
A text-based file format used for storing Go game records.
Gomill uses version FF[4], which is specified at
http://www.red-bean.com/sgf/index.html.
jigo
A tied game (after komi is taken into account).
komi
Additional points awarded to White in final scoring.
simple ko
A Go rule prohibiting repetition of the immediately-preceding position.
superko
A Go rule prohibiting repetition of preceding positions.
There are several possible variants of the superko rule. Gomill does not
enforce any of them.
pondering
A feature implemented by some Go programs: thinking while it is their
opponent's turn to move.
controller
A program implementing the 'referee' side of the |gtp| protocol.
The |gtp| protocol can be seen as a client-server protocol, with the
controller as the client.
engine
A program implementing the 'playing' side of the |gtp| protocol.
The |gtp| protocol can be seen as a client-server protocol, with the
engine as the server.
player
A |gtp| engine, together with a particular configuration.
competition
An 'event' consisting of multiple games managed by the Gomill ringmaster
(either a tournament or a tuning event).
tournament
A competition in which the ringmaster plays games between predefined
players, to compare their strengths.
playoff
A tournament comprising many games played between fixed pairings of
players.
all-play-all
A tournament in which games are played between all pairings from a list of
players.
matchup
A pairing of players in a tournament, together with its settings (board
size, komi, handicap, and so on)
tuning event
A competition in which the ringmaster runs an algorithm which adjusts
player parameters to try to find the values which give strongest play.
Bandit problem
A problem in which an agent has to repeatedly choose between actions whose
value is initially unknown, trading off time spent on the action with the
best estimated value against time spent evaluating other actions.
See http://en.wikipedia.org/wiki/Multi-armed_bandit
UCB
Upper Confidence Bound algorithms
A family of algorithms for addressing bandit problems.
UCT
Upper Confidence bounds applied to Trees.
A variant of UCB for bandit problems in which the actions are arranged in
the form of a tree.
See http://senseis.xmp.net/?UCT.

View File

@ -0,0 +1,79 @@
The :mod:`gomill` package
-------------------------
All Gomill code is contained in modules under the :mod:`!gomill` package.
The package includes both the 'toolkit' (Go board, |sgf|, and |gtp|) code, and
the code implementing the ringmaster.
.. contents:: Page contents
:local:
:backlinks: none
Package module contents
^^^^^^^^^^^^^^^^^^^^^^^
The package module itself defines only a single constant:
.. module:: gomill
:synopsis: Tools for testing and tuning Go-playing programs.
.. data:: __version__
The library version, as a string (like ``"0.7"``).
.. versionadded:: 0.7
Generic data representation
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Unless otherwise stated, string values are 8-bit UTF-8 strings.
.. _go_related_data_representation:
Go-related data representation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Gomill represents Go colours and moves internally as follows:
======== ===========================================
Name Possible values
======== ===========================================
*colour* single-character string: ``'b'`` or ``'w'``
*point* pair (*int*, *int*) of coordinates
*move* *point* or ``None`` (for a pass)
======== ===========================================
The terms *colour*, *point*, and *move* are used as above throughout this
library documentation (in particular, when describing parameters and return
types).
*colour* values are used to represent players, as well as stones on the board.
(When a way to represent an empty point is needed, ``None`` is used.)
*point* values are treated as (row, column). The bottom left is ``(0, 0)``
(the same orientation as |gtp|, but not |sgf|). So the coordinates for a 9x9
board are as follows::
9 (8,0) . . . . . (8,8)
8 . . . . . . . . .
7 . . . . . . . . .
6 . . . . . . . . .
5 . . . . . . . . .
4 . . . . . . . . .
3 . . . . . . . . .
2 . . . . . . . . .
1 (0,0) . . . . . (0,8)
A B C D E F G H J
There are functions in the :mod:`~gomill.common` module to convert between
these coordinates and the conventional (``T19``\ -style) notation.
Gomill is designed to work with square boards, up to 25x25 (which is the upper
limit of the conventional notation, and the upper limit for |gtp|). Some parts
of the library can work with larger board sizes; these cases are documented
explicitly.

View File

@ -0,0 +1,133 @@
GTP extensions
==============
Gomill supports a number of |gtp| extension commands. These are all named with
a ``gomill-`` prefix.
The extensions used by the ringmaster are as follows:
.. gtp:: gomill-explain_last_move
Arguments: none
Return a string containing the engine's comments about the last move it
generated.
This might include the engine's estimate of its winning chances, a principal
variation, or any other diagnostic information.
The intention is that |gtp| controllers which produce game records should
use this command to write a comment associated with the move.
Any non-ASCII characters in the response should be encoded as UTF-8.
If no information is available, return an empty string.
The behaviour of this command is unspecified if a command changing the board
state (eg :gtp:`!play` or :gtp:`!undo`) has occurred since the engine last
generated a move.
.. gtp:: gomill-describe_engine
Arguments: none
Return a string with a description of the engine's configuration. This
should repeat the information from the :gtp:`!name` and :gtp:`!version`
commands. Controllers should expect the response to take multiple lines.
The intention is that |gtp| controllers which produce game records should
use the output of this command as part of a comment for the game as a whole.
If possible, the response should include a description of all engine
parameters which affect gameplay. If the engine plays reproducibly given the
seed of a random number generator, the response should include that seed.
Any non-ASCII characters in the response should be encoded as UTF-8.
.. gtp:: gomill-cpu_time
Arguments: none
Return a float (represented in decimal) giving the amount of CPU time the
engine has used to generate all moves made so far (in seconds).
For engines which use multiple threads or processes, this should be the
total time used on all CPUs.
It may not be possible to meaningfully respond to this command (for example,
if an engine runs on multiple processors which run at different speeds); in
complex cases, the engine should document how the CPU time is calculated.
.. gtp:: gomill-genmove_ex
Arguments: colour, list of keywords
This is a variant of the standard :gtp:`!genmove` command. Each keyword
indicates a permitted (or desired) variation of behaviour. For example::
gomill-genmove_ex b claim
If :gtp:`!gomill-genmove_ex` is sent without any arguments (ie, no colour is
specified), the engine should return a list of the keywords it supports (one
per line, like :gtp:`!list_commands`).
Engines must ignore keywords they do not support. :gtp:`!gomill-genmove_ex`
with no keywords is exactly equivalent to :gtp:`!genmove`.
The following keywords are currently defined:
``claim``
In addition to the usual responses to :gtp:`!genmove`, the engine may also
return ``claim``, which indicates that the engine believes it is certain
to win the game (the engine must not assume that the controller will act
on this claim).
There is also an extension which is not used by the ringmaster:
.. gtp:: gomill-savesgf
Arguments: filename, list of |sgf| properties
Write an |sgf| game record of the current game.
See the :term:`GTP` specification's description of :gtp:`!loadsgf` for the
interpretation of the ``filename`` argument.
The |sgf| properties should be specified in the form
:samp:`{PropIdent}={PropValue}`, eg ``RE=W+3.5``. Escape spaces in values
with ``\_``, backslashes with ``\\``. Encode non-ASCII characters in UTF-8.
These |sgf| properties should be added to the root node. The engine should
fill in any properties it can (at least ``AP``, ``SZ``, ``KM``, ``HA``, and
``DT``). Explicitly-specified properties should override the engine's
defaults.
The intention is that engines which have 'comments' about their moves (as
for :gtp:`gomill-explain_last_move`) should include them in the game record.
Example::
gomill-savesgf xxx.sgf PB=testplayer PW=GNU\_Go:3.8 RE=W+3.5
.. note::
|gtp| engines aren't typically well placed to write game records, as they
don't have enough information to write the game metadata properly (this is
why :gtp:`!gomill-savesgf` has to take the |sgf| properties explicitly).
It's usually better for the controller to do it. See the
:script:`kgs_proxy.py` example script for an example of when this command
might be useful.
The :gtp:`gomill-explain_last_move`, :gtp:`gomill-genmove_ex`, and
:gtp:`gomill-savesgf` commands are supported by the Gomill :mod:`!gtp_states`
module.
.. The other extension is gomill-passthrough (used by proxies), but I don't
think it makes sense to document it as a generic extension

View File

@ -0,0 +1,36 @@
The :mod:`~gomill.handicap_layout` module
-----------------------------------------
.. module:: gomill.handicap_layout
:synopsis: Standard layout of fixed handicap stones.
The :mod:`!gomill.handicap_layout` module describes the standard layout used
for fixed handicap stones. It follows the rules from the :term:`GTP`
specification.
.. function:: handicap_points(number_of_stones, board_size)
:rtype: list of *points*
Returns the handicap points for a given number of stones and board size.
Raises :exc:`ValueError` if there isn't a standard placement pattern for
the specified number of handicap stones and board size.
The result's length is always exactly *number_of_stones*.
.. function:: max_fixed_handicap_for_board_size(board_size)
:rtype: int
Returns the maximum number of stones permitted for the |gtp|
:gtp:`!fixed_handicap` command, given the specified board size.
.. function:: max_free_handicap_for_board_size(board_size)
:rtype: int
Returns the maximum number of stones permitted for the |gtp|
:gtp:`!place_free_handicap` command, given the specified board size.

24
gomill/docs/index.rst Normal file
View File

@ -0,0 +1,24 @@
******
Gomill
******
.. toctree::
:maxdepth: 3
:titlesonly:
intro
ringmaster
gtp_extensions
library
example_scripts
install
contact
changes
licence
glossary
* :ref:`genindex`
* :ref:`search`
.. todolist::

152
gomill/docs/install.rst Normal file
View File

@ -0,0 +1,152 @@
Installation
============
.. contents:: Page contents
:local:
:backlinks: none
Requirements
------------
Gomill requires Python 2.5, 2.6, or 2.7.
For Python 2.5 only, the :option:`--parallel <ringmaster --parallel>` feature
requires the external `multiprocessing`__ package.
.. __: http://pypi.python.org/pypi/multiprocessing
Gomill is intended to run on any modern Unix-like system.
Obtaining Gomill
----------------
Gomill is distributed as a pure-Python source archive,
:file:`gomill-{version}.tar.gz`. The most recent version can be obtained from
http://mjw.woodcraft.me.uk/gomill/.
This documentation is distributed separately as
:file:`gomill-doc-{version}.tar.gz`.
Once you have downloaded the source archive, extract it using a command like
:samp:`tar -xzf gomill-{version}.tar.gz`. This will create a directory named
:file:`gomill-{version}`, referred to below as the :dfn:`distribution
directory`.
Alternatively, you can access releases using Git::
git clone http://mjw.woodcraft.me.uk/gomill/git/ gomill
which would create :file:`gomill` as the distribution directory.
Running the ringmaster
----------------------
The ringmaster executable in the distribution directory can be run directly
without any further installation; it will use the copy of the :mod:`!gomill`
package in the distribution directory.
A symbolic link to the ringmaster executable will also work, but if you move
the executable elsewhere it will not be able to find the :mod:`!gomill`
package unless the package is installed.
Installing
----------
Installing Gomill puts the :mod:`!gomill` package onto the Python module
search path, and the ringmaster executable onto the executable
:envvar:`!PATH`.
To install, first change to the distribution directory, then:
- to install for the system as a whole, run (as a sufficiently privileged
user) ::
python setup.py install
- to install for the current user only (Python 2.6 or 2.7), run ::
python setup.py install --user
(in this case the ringmaster executable will be placed in
:file:`~/.local/bin`.)
Pass :option:`!--dry-run` to see what these will do. See
http://docs.python.org/2.7/install/ for more information.
Uninstalling
------------
To remove an installed version of Gomill, run ::
python setup.py uninstall
(This uses the Python module search path and the executable :envvar:`!PATH` to
find the files to remove; pass :option:`!--dry-run` to see what it will do.)
Running the test suite
----------------------
To run the testsuite against the distributed :mod:`!gomill` package, change to
the distribution directory and run ::
python -m gomill_tests.run_gomill_testsuite
To run the testsuite against an installed :mod:`!gomill` package, change to
the distribution directory and run ::
python test_installed_gomill.py
With Python versions earlier than 2.7, the unittest2__ library is required
to run the testsuite.
.. __: http://pypi.python.org/pypi/unittest2/
.. _running the example scripts:
Running the example scripts
---------------------------
To run the example scripts, it is simplest to install the :mod:`!gomill`
package first.
If you do not wish to do so, you can run ::
export PYTHONPATH=<path to the distribution directory>
so that the example scripts will be able to find the :mod:`!gomill` package.
Building the documentation
--------------------------
The sources for this HTML documentation are included in the Gomill source
archive. To rebuild the documentation, change to the distribution directory
and run ::
python setup.py build_sphinx
The documentation will be generated in :file:`build/sphinx/html`.
Requirements:
- Sphinx__ version 1.0 or later (at least 1.0.4 recommended)
- LaTeX__
- dvipng__
.. __: http://sphinx.pocoo.org/
.. __: http://www.latex-project.org/
.. __: http://www.nongnu.org/dvipng/

97
gomill/docs/intro.rst Normal file
View File

@ -0,0 +1,97 @@
Introduction
============
Gomill is a suite of tools, and a Python library, for use in developing and
testing Go-playing programs. It is based around the Go Text Protocol
(:term:`GTP`) and the Smart Game Format (:term:`SGF`).
The principal tool is the :dfn:`ringmaster`, which plays programs against each
other and keeps track of the results.
Ringmaster features include:
- testing multiple pairings in one run
- playing multiple games in parallel
- displaying live results
- engine configuration by command line options or |gtp| commands
- a protocol for including per-move engine diagnostics in |sgf| output
- automatically tuning engine parameters based on game results
(**experimental**)
.. contents:: Page contents
:local:
:backlinks: none
Ringmaster example
------------------
Create a file called :file:`demo.ctl`, with the following contents::
competition_type = 'playoff'
board_size = 9
komi = 7.5
players = {
'gnugo-l1' : Player('gnugo --mode=gtp --level=1'),
'gnugo-l2' : Player('gnugo --mode=gtp --level=2'),
}
matchups = [
Matchup('gnugo-l1', 'gnugo-l2',
alternating=True,
number_of_games=5),
]
(If you don't have :program:`gnugo` installed, change the
:setting-cls:`Player` definitions to use a command line for whatever |gtp|
engine you have available.)
Then run ::
$ ringmaster demo.ctl
The ringmaster will run five games between the two players, showing a summary
of the results on screen, and then exit.
(If the ringmaster is not already installed, see :doc:`install` for
instructions.)
The final display should be something like this::
gnugo-l1 v gnugo-l2 (5/5 games)
board size: 9 komi: 7.5
wins black white avg cpu
gnugo-l1 2 40.00% 1 33.33% 1 50.00% 1.05
gnugo-l2 3 60.00% 1 50.00% 2 66.67% 1.12
2 40.00% 3 60.00%
= Results =
game 0_0: gnugo-l2 beat gnugo-l1 W+21.5
game 0_1: gnugo-l2 beat gnugo-l1 B+9.5
game 0_2: gnugo-l2 beat gnugo-l1 W+14.5
game 0_3: gnugo-l1 beat gnugo-l2 W+7.5
game 0_4: gnugo-l1 beat gnugo-l2 B+2.5
The ringmaster will create several files named like :file:`demo.{xxx}` in the
same directory as :file:`demo.ctl`, including a :file:`demo.sgf` directory
containing game records.
The Python library
------------------
Gomill also provides a Python library for working with |gtp| and |sgf|, though
as of Gomill |version| only part of the API is stable. See :doc:`library` for
details.
The example scripts
-------------------
Some :doc:`example scripts <example_scripts>` are also included in the Gomill
distribution, as illustrations of the library interface and in some cases as
tools useful in themselves.

32
gomill/docs/library.rst Normal file
View File

@ -0,0 +1,32 @@
The Gomill library
==================
Gomill is intended to be useful as a Python library for developing |gtp|- and
|sgf|-based tools.
As of Gomill |version|, not all of the library API is formally documented.
Only the parts which are described in this documentation should be considered
stable public interfaces.
Nonetheless, the source files for the remaining modules contain fairly
detailed documentation, and the :doc:`example scripts <example_scripts>`
illustrate how some of them can be used.
.. contents:: Page contents
:local:
:backlinks: none
.. toctree::
:maxdepth: 3
:titlesonly:
library_overview
gomill_package
common
boards
ascii_boards
handicap_layout
sgf
tournament_results

View File

@ -0,0 +1,74 @@
Library overview
----------------
The :mod:`gomill` package includes the following modules:
.. the descriptions here should normally match the module :synopsis:, and
therefore the module index.
========================================= ========================================================================
Generic support code
========================================= ========================================================================
:mod:`~!gomill.utils`
:mod:`~!gomill.compact_tracebacks`
:mod:`~!gomill.ascii_tables`
:mod:`~!gomill.job_manager`
:mod:`~!gomill.settings`
========================================= ========================================================================
========================================= ========================================================================
Go-related support code
========================================= ========================================================================
:mod:`~gomill.common` Go-related utility functions.
:mod:`~gomill.boards` Go board representation.
:mod:`~gomill.ascii_boards` ASCII Go board diagrams.
:mod:`~gomill.handicap_layout` Standard layout of fixed handicap stones.
========================================= ========================================================================
========================================= ========================================================================
|sgf| support
========================================= ========================================================================
:mod:`~!gomill.sgf_grammar`
:mod:`~!gomill.sgf_properties`
:mod:`~gomill.sgf` High level |sgf| interface.
:mod:`~gomill.sgf_moves` Higher-level processing of moves and positions from |sgf| games
========================================= ========================================================================
========================================= ========================================================================
|gtp| controller side
========================================= ========================================================================
:mod:`~!gomill.gtp_controller`
:mod:`~!gomill.gtp_games`
========================================= ========================================================================
========================================= ========================================================================
|gtp| engine side
========================================= ========================================================================
:mod:`~!gomill.gtp_engine`
:mod:`~!gomill.gtp_states`
:mod:`~!gomill.gtp_proxy`
========================================= ========================================================================
========================================= ========================================================================
Competitions
========================================= ========================================================================
:mod:`~!gomill.competition_schedulers`
:mod:`~!gomill.competitions`
:mod:`~gomill.tournament_results` Retrieving and reporting on tournament results.
:mod:`~!gomill.tournaments`
:mod:`~!gomill.playoffs`
:mod:`~!gomill.allplayalls`
:mod:`~!gomill.cem_tuners`
:mod:`~!gomill.mcts_tuners`
========================================= ========================================================================
========================================= ========================================================================
The ringmaster
========================================= ========================================================================
:mod:`~!gomill.game_jobs`
:mod:`~!gomill.terminal_input`
:mod:`~!gomill.ringmaster_presenters`
:mod:`~!gomill.ringmasters`
:mod:`~!gomill.ringmaster_command_line`
========================================= ========================================================================

25
gomill/docs/licence.rst Normal file
View File

@ -0,0 +1,25 @@
Licence
=======
Gomill is copyright 2009-2011 Matthew Woodcraft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
.. Note:: This is the licence commonly known as the 'MIT' Licence.

646
gomill/docs/mcts_tuner.rst Normal file
View File

@ -0,0 +1,646 @@
.. index:: monte carlo tuner
The Monte Carlo tuner
^^^^^^^^^^^^^^^^^^^^^
:setting:`competition_type` string: ``"mc_tuner"``.
The Monte Carlo tuner treats the tuning event as a :term:`bandit problem`.
That is, it attempts to find the candidate which has the highest probability
of beating the opponent, and arranges to 'spend' more games on the candidates
which have the highest winning percentages so far.
It does this using a form of the :term:`UCB` algorithm (or, optionally,
:term:`UCT`) which is familiar to Go programmers.
.. caution:: As of Gomill |version|, the Monte Carlo tuner is still
experimental. The control file settings may change in future. The reports
aren't very good.
.. contents:: Page contents
:local:
:backlinks: none
.. _mc parameter model:
The parameter model
"""""""""""""""""""
The Monte Carlo tuner expects to work with one or more independent player
parameters.
Internally, it models each parameter value as a floating point number in the
range 0.0 to 1.0. It uses parameter values taken uniformly from this range to
make the candidate players. Values from this range are known as
:dfn:`optimiser parameters`.
In practice, engine parameters might not be floating point numbers, their
range is unlikely to be 0.0 to 1.0, and you may wish to use a non-uniform (eg,
logarithmic) scale for the candidates.
To support this, each parameter has an associated :mc-setting:`scale`. This is
a function which maps an optimiser parameter to an :dfn:`engine parameter`
(which can be of an arbitrary Python type). A number of :ref:`predefined
scales <predefined scales>` are provided.
The candidate players are configured using these engine parameters.
Reports, and the live display, are also based on engine parameters; see the
:mc-setting:`format` parameter setting.
Candidates
""""""""""
Each parameter also has a :mc-setting:`split` setting (a smallish integer).
This determines how many 'samples' of the parameter range are used to make
candidate players.
When there are multiple parameters, one candidate is made for each combination
of these samples. So if there is only one parameter, the total number of
candidates is just :mc-setting:`split`, and if there are multiple parameters,
the total number of candidates is the product of all the :mc-setting:`split`
settings. For example, the sample control file below creates 64 candidates.
.. caution:: While the Monte Carlo tuner does not impose any limit on the
number of parameters you use, unless the games are unusually rapid it may
be unreasonable to try to tune more than two or three parameters at once.
Each candidate's engine parameters are passed to the
:mc-setting:`make_candidate` function, which returns a :setting-cls:`Player`
definition.
The samples are taken by dividing the optimiser parameter range into
:mc-setting:`split` divisions, and taking the centre of each division as the
sample (so the end points of the range are not used). For example, if a
parameter has a linear scale from 0.0 to 8.0, and :mc-setting:`split` is 3,
the samples (after translation to engine parameters) will be 1.0, 4.0, and
7.0.
.. _the mcts tuning algorithm:
The tuning algorithm
""""""""""""""""""""
Each time the tuner starts a new game, it chooses the candidate which gives
the highest value to the following formula:
.. math:: w_c/g_c + E \sqrt(log(g_p) / g_c)
where
- :math:`E` is the :mc-setting:`exploration_coefficient`
- :math:`g_c` is the number of games the candidate has played
- :math:`w_c` is the number of games the candidate has won
- :math:`g_p` is the total number of games played in the tuning event
At the start of the tuning event, each candidate's :math:`g_c` is set to
:mc-setting:`initial_visits`, and :math:`w_c` is set to
:mc-setting:`initial_wins`.
(:math:`w_c/g_c` is just the candidate's current win rate. :math:`E
\sqrt(log(g_p) / g_c)` is known as the :dfn:`exploration term`; as more games
are played, its value increases most rapidly for the least used candidates, so
that unpromising candidates will eventually be reconsidered.)
When more than one candidate has the highest value (for example, at the start
of the event), one is chosen at random.
The tuning event runs until :mc-setting:`number_of_games` games have been
played (indefinitely, if :mc-setting:`number_of_games` is unset).
The tuner can be stopped at any time; after each game result, it reports the
parameters of the current 'best' candidate. This is the candidate with the
most *wins* (note that this may not be the one with the best win rate; it is
usually the same as the candidate which has played the most games).
.. _sample_mcts_control_file:
Sample control file
"""""""""""""""""""
Here is a sample control file, illustrating most of the available settings for
a Monte Carlo tuning event::
competition_type = "mc_tuner"
description = """\
This is a sample control file.
It illustrates the available settings for the Monte Carlo tuner.
"""
players = {
'gnugo-l10' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=10"),
}
def fuego(max_games, additional_commands=[]):
commands = [
"go_param timelimit 999999",
"uct_max_memory 350000000",
"uct_param_search number_threads 1",
"uct_param_player reuse_subtree 0",
"uct_param_player ponder 0",
"uct_param_player max_games %d" % max_games,
]
return Player(
"fuego --quiet",
startup_gtp_commands=commands+additional_commands)
FUEGO_MAX_GAMES = 5000
parameters = [
Parameter('rave_weight_initial',
scale = LOG(0.01, 5.0),
split = 8,
format = "I: %4.2f"),
Parameter('rave_weight_final',
scale = LOG(1e2, 1e5),
split = 8,
format = "F: %4.2f"),
]
def make_candidate(rwi, rwf):
return fuego(
FUEGO_MAX_GAMES,
["uct_param_search rave_weight_initial %f" % rwi,
"uct_param_search rave_weight_final %f" % rwf])
board_size = 19
komi = 7.5
opponent = 'gnugo-l10'
candidate_colour = 'w'
number_of_games = 10000
exploration_coefficient = 0.45
initial_visits = 10
initial_wins = 5
summary_spec = [40]
log_tree_to_history_period = 200
.. _mcts_control_file_settings:
Control file settings
"""""""""""""""""""""
The following settings can be set at the top level of the control file:
All :ref:`common settings <common settings>` (the :setting:`players`
dictionary is required, though it is used only to define the opponent).
The following game settings (only :setting:`!board_size` and :setting:`!komi`
are required):
- :setting:`board_size`
- :setting:`komi`
- :setting:`handicap`
- :setting:`handicap_style`
- :setting:`move_limit`
- :setting:`scorer`
:setting:`!komi` must be fractional, as the tuning algorithm doesn't currently
support :term:`jigos <jigo>`.
The following additional settings (all those without a listed default are
required):
.. mc-setting:: number_of_games
Integer (default ``None``)
The total number of games to play in the event. If you leave this unset,
there will be no limit.
.. mc-setting:: candidate_colour
String: ``"b"`` or ``"w"``
The colour for the candidates to take in every game.
.. mc-setting:: opponent
Identifier
The :ref:`player code <player codes>` of the player to use as the
candidates' opponent.
.. mc-setting:: parameters
List of :mc-setting-cls:`Parameter` definitions (see :ref:`mc parameter
configuration`).
Describes the parameter space that the tuner will work in. See :ref:`The
parameter model <mc parameter model>` for more details.
The order of the :mc-setting-cls:`Parameter` definitions is used for the
arguments to :mc-setting:`make_candidate`, and whenever parameters are
described in reports or game records.
.. mc-setting:: make_candidate
Python function
Function to create a :setting-cls:`Player` from its engine parameters.
This function is passed one argument for each candidate parameter, and must
return a :setting-cls:`Player` definition. Each argument is the output of
the corresponding :mc-setting-cls:`Parameter`'s :mc-setting:`scale`.
The function will typically use its arguments to construct command line
options or |gtp| commands for the player. For example::
def make_candidate(param1, param2):
return Player(["goplayer", "--param1", str(param1),
"--param2", str(param2)])
def make_candidate(param1, param2):
return Player("goplayer", startup_gtp_commands=[
["param1", str(param1)],
["param2", str(param2)],
])
.. mc-setting:: exploration_coefficient
Float
The coefficient of the exploration term in the :term:`UCB` algorithm (eg
``0.45``). See :ref:`The tuning algorithm <the mcts tuning algorithm>`.
.. mc-setting:: initial_visits
Positive integer
The number of games to initialise each candidate with. At the start of the
event, the tuner will behave as if each candidate has already played this
many games. See :ref:`The tuning algorithm <the mcts tuning algorithm>`.
.. mc-setting:: initial_wins
Positive integer
The number of wins to initialise each candidate with. At the start of the
event, the tuner will behave as if each candidate has already won this many
games. See :ref:`The tuning algorithm <the mcts tuning algorithm>`.
.. tip:: It's best to set :mc-setting:`initial_wins` so that
:mc-setting:`initial_wins` / :mc-setting:`initial_visits` is close to the
typical candidate's expected win rate.
.. mc-setting:: max_depth
Positive integer (default 1)
See :ref:`tree search` below.
The remaining settings only affect reporting and logging; they have no effect
on the tuning algorithm.
.. mc-setting:: summary_spec
List of integers (default [30])
Number of candidates to describe in the runtime display and reports (the
candidates with most visits are described).
(This list should have :mc-setting:`max_depth` elements; if
:mc-setting:`max_depth` is greater than 1, it specifies how many candidates
to show from each level of the tree, starting with the highest.)
.. mc-setting:: log_tree_to_history_period
Positive integer (default None)
If this is set, a detailed description of the :term:`UCT` tree is written to
the :ref:`history file <logging>` periodically (after every
:mc-setting:`!log_tree_to_history_period` games).
.. mc-setting:: number_of_running_simulations_to_show
Positive integer (default 12)
The maximum number of games in progress to describe on the runtime display.
.. _mc parameter configuration:
Parameter configuration
"""""""""""""""""""""""
.. mc-setting-cls:: Parameter
A :mc-setting-cls:`!Parameter` definition has the same syntax as a Python
function call: :samp:`Parameter({arguments})`. Apart from :mc-setting:`!code`,
the arguments should be specified using keyword form (see
:ref:`sample_mcts_control_file`).
All arguments other than :mc-setting:`format` are required.
The arguments are:
.. mc-setting:: code
Identifier
A short string used to identify the parameter. This is used in error
messages, and in the default for :mc-setting:`format`.
.. mc-setting:: scale
Python function
Function mapping an optimiser parameter to an engine parameter; see :ref:`mc
parameter model`.
Although this can be defined explicitly, in most cases you should be able
to use one of the :ref:`predefined scales <predefined scales>`.
Examples::
Parameter('p1', split = 8,
scale = LINEAR(-1.0, 1.0))
Parameter('p2', split = 8,
scale = LOG(10, 10000, integer=True))
Parameter('p3', split = 3,
scale = EXPLICIT(['low', 'medium', 'high']))
def scale_p3(f):
return int(1000 * math.sqrt(f))
Parameter('p3', split = 20, scale = scale_p3)
.. mc-setting:: split
Positive integer
The number of samples from this parameter to use to make candidates. See
:ref:`The tuning algorithm <the mcts tuning algorithm>`.
.. mc-setting:: format
String (default :samp:`"{parameter_code}: %s"`)
Format string used to display the parameter value. This should include a
short abbreviation to indicate which parameter is being displayed, and also
contain ``%s``, which will be replaced with the engine parameter value.
You can use any Python conversion specifier instead of ``%s``. For example,
``%.2f`` will format a floating point number to two decimal places. ``%s``
should be safe to use for all types of value. See `string formatting
operations`__ for details.
.. __: http://docs.python.org/release/2.7/library/stdtypes.html#string-formatting-operations
Format strings should be kept short, as screen space is limited.
Examples::
Parameter('parameter_1', split = 8,
scale = LINEAR(-1.0, 1.0),
format = "p1: %.2f")
Parameter('parameter_2', split = 8,
scale = LOG(10, 10000, integer=True),
format = "p2: %d")
Parameter('parameter_3', split = 3,
scale = EXPLICIT(['low', 'medium', 'high']),
format = "p3: %s")
.. index:: predefined scale
.. index:: scale; predefined
.. _predefined scales:
Predefined scales
"""""""""""""""""
There are three kinds of predefined scale which you can use in a
:mc-setting:`scale` definition:
.. index:: LINEAR
.. object:: LINEAR
A linear scale between specified bounds. This takes two arguments:
``lower_bound`` and ``upper_bound``.
Optionally, you can also pass ``integer=True``, in which case the result is
rounded to the nearest integer.
Examples::
LINEAR(0, 100)
LINEAR(-64.0, 256.0, integer=True)
.. tip:: To make candidates which take each value from a simple integer range
from (say) 0 to 10 inclusive, use::
Parameter('p1', split = 11,
scale = LINEAR(-0.5, 10.5, integer=True))
(or use EXPLICIT)
.. index:: LOG
.. object:: LOG
A 'logarithmic scale' (ie, an exponential function) between specified
bounds. This takes two arguments: ``lower_bound`` and ``upper_bound``.
Optionally, you can also pass ``integer=True``, in which case the result is
rounded to the nearest integer.
Example::
LOG(0.01, 1000)
LOG(1e2, 1e9, integer=True)
.. index:: EXPLICIT
.. object:: EXPLICIT
This scale makes the engine parameters take values from an explicitly
specified list. You should normally use this with :mc-setting:`split` equal
to the length of the list.
Examples::
EXPLICIT([0, 1, 2, 4, 6, 8, 10, 15, 20])
EXPLICIT(['low', 'medium', 'high'])
.. note:: if :mc-setting:`max_depth` is greater than 1, :mc-setting:`split`
^ :mc-setting:`max_depth` should equal the length of the list.
Writing scale functions
"""""""""""""""""""""""
The following built-in Python functions might be useful: :func:`abs`,
:func:`min`, :func:`max`, :func:`round`.
More functions are available from the :mod:`math` module. Put a line like ::
from math import log, exp, sqrt
in the control file to use them.
Dividing two integers with ``/`` gives a floating point number (that is,
'Future division' is in effect).
You can use scientific notation like ``1.3e-2`` to specify floating point
numbers.
Here are scale functions equivalent to ``LINEAR(3, 3000)`` and
``LOG(3, 3000)``::
def scale_linear(f):
return 2997 * f + 3
def scale_log(f):
return exp(log(1000) * f) * 3
Reporting
"""""""""
Currently, there aren't any sophisticated reports.
The standard report shows the candidates which have played most games; the
:mc-setting:`summary_spec` setting defines how many to show.
In a line like::
(0,1) I: 0.01; F: 365.17 0.537 70
The ``(0,1)`` are the 'coordinates' of the candidate, ``I: 0.01; F: 365.17``
are the engine parameters (identified using the :mc-setting:`format` setting),
``0.537`` is the win rate (including the :mc-setting:`initial_wins` and
:mc-setting:`initial_visits`), and ``70`` is the number of games (excluding
the :mc-setting:`initial_visits`).
Also, after every :mc-setting:`log_tree_to_history_period` games, the status
of all candidates is written to the :ref:`history file <logging>` (if
:mc-setting:`max_depth` > 1, the first two generations of candidates are
written).
.. _tree search:
Tree search
"""""""""""
As a further (and even more experimental) refinement, it's possible to arrange
the candidates in the form of a tree and use the :term:`UCT` algorithm instead
of plain :term:`UCB`. To do this, set the :mc-setting:`max_depth` setting to a
value greater than 1.
Initially, this behaves as described in :ref:`The tuning algorithm <the mcts
tuning algorithm>`. But whenever a candidate is chosen for the second time, it
is :dfn:`expanded`: a new generation of candidates is created and placed as
that candidate's children in a tree structure.
The new candidates are created by sampling their parent's 'division' of
optimiser parameter space in the same way as the full space was sampled to
make the first-generation candidates (so the number of children is again the
product of the :mc-setting:`split` settings). Their :math:`g_c` and :math:`w_c`
values are initialised to :mc-setting:`initial_visits` and
:mc-setting:`initial_wins` as usual.
Then one of these child candidates is selected using the usual formula, where
- :math:`g_c` is now the number of games the child has played
- :math:`w_c` is now the number of games the child has won
- :math:`g_p` is now the number of games the parent has played
If :mc-setting:`max_depth` is greater than 2, then when a second-generation
candidate is chosen for the second time, it is expanded itself, and so on
until :mc-setting:`max_depth` is reached.
Each time the tuner starts a new game, it walks down the tree using this
formula to choose a child node at each level, until it reaches a 'leaf' node.
Once a candidate has been expanded, it does not play any further games; only
candidates which are 'leaf' nodes of the tree are used as players. The
:math:`g_c` and :math:`w_c` values for non-leaf candidates count the games and
wins played by the candidate's descendants, as well as by the candidate
itself.
The 'best' candidate is determined by walking down the tree and choosing the
child with the most wins at each step (which may not end up with the leaf
candidate with the most wins in the entire tree).
.. note:: It isn't clear that using UCT for a continuous parameter space like
this is a wise (or valid) thing to do. I suspect it needs some form of RAVE
to perform well.
.. caution:: If you use a high :option:`--parallel <ringmaster --parallel>`
value, note that the Monte Carlo tuner doesn't currently take any action to
prevent the same unpromising branch of the tree being explored by multiple
processes simultaneously, which might lead to odd results (particularly if
you stop the competition and restart it).
Changing the control file between runs
""""""""""""""""""""""""""""""""""""""
In general, you shouldn't change the :mc-setting-cls:`Parameter` definitions
or the settings which control the tuning algorithm between runs. The
ringmaster will normally notice and refuse to start, but it's possible to fool
it and so get meaningless results.
Changing the :mc-setting:`exploration_coefficient` is ok. Increasing
:mc-setting:`max_depth` is ok (decreasing it is ok too, but it won't stop the
tuner exploring parts of the tree that it has already expanded).
Changing :mc-setting:`make_candidate` is ok, though if this affects player
behaviour it will probably be unhelpful.
Changing :mc-setting:`initial_wins` or :mc-setting:`initial_visits` will have
no effect if :mc-setting:`max_depth` is 1; otherwise it will affect only
candidates created in future.
Changing the settings which control reporting, including :mc-setting:`format`,
is ok.
Changing :mc-setting:`number_of_games` is ok.

214
gomill/docs/playoffs.rst Normal file
View File

@ -0,0 +1,214 @@
.. index:: playoffs
.. _playoff tournament:
Playoff tournaments
^^^^^^^^^^^^^^^^^^^
:setting:`competition_type` string: ``"playoff"``.
In a playoff tournament the control file explicitly describes one or more
pairings of players (:dfn:`matchups`).
Each matchup is treated independently: different matchups can use different
board sizes, handicap arrangements, and so on.
The tournament runs until :pl-setting:`number_of_games` have been played for
each matchup (indefinitely, if :pl-setting:`number_of_games` is unset).
.. contents:: Page contents
:local:
:backlinks: none
.. _sample_playoff_control_file:
Sample control file
"""""""""""""""""""
Here is a sample control file, illustrating how matchups are specified::
competition_type = 'playoff'
players = {
'gnugo-l1' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=1"),
'gnugo-l2' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=2"),
}
board_size = 9
komi = 6
matchups = [
Matchup('gnugo-l1', 'gnugo-l2', board_size=13,
handicap=2, handicap_style='free', komi=0,
scorer='players', number_of_games=5),
Matchup('gnugo-l1', 'gnugo-l2', alternating=True,
scorer='players', move_limit=200),
Matchup('gnugo-l1', 'gnugo-l2',
komi=0.5,
scorer='internal'),
]
.. _playoff_control_file_settings:
Control file settings
"""""""""""""""""""""
The following settings can be set at the top level of the control file:
All :ref:`common settings <common settings>`.
All :ref:`game settings <game settings>`, and the matchup settings
:pl-setting:`alternating` and :pl-setting:`number_of_games` described below;
these will be used for any matchups which don't explicitly override them.
.. pl-setting:: matchups
List of :pl-setting-cls:`Matchup` definitions (see :ref:`matchup
configuration`).
This defines which players will compete against each other, and the game
settings they will use.
The only required settings are :setting:`competition_type`,
:setting:`players`, and :pl-setting:`matchups`.
.. _matchup configuration:
Matchup configuration
"""""""""""""""""""""
.. pl-setting-cls:: Matchup
A :pl-setting-cls:`!Matchup` definition has the same syntax as a Python
function call: :samp:`Matchup({arguments})`.
The first two arguments should be the :ref:`player codes <player codes>` for
the two players involved in the matchup. The remaining arguments should be
specified in keyword form. For example::
Matchup('gnugo-l1', 'fuego-5k', board_size=13, komi=6)
Defaults for matchup arguments (other than :pl-setting:`id` and
:pl-setting:`name`) can be specified at the top level of the control file.
The :setting:`board_size` and :setting:`komi` arguments must be given for all
matchups (either explicitly or as defaults); the rest are all optional.
.. caution:: a default :setting:`komi` or :pl-setting:`alternating` setting
will be applied even to handicap games.
All :ref:`game settings <game settings>` can be used as matchup arguments, and
also the following:
.. _matchup id:
.. pl-setting:: id
Identifier
A short string (usually one to three characters) which is used to identify
the matchup. Matchup ids appear in the :ref:`game ids <game id>` (and so in
the |sgf| filenames), and are used in the :doc:`tournament results API
<tournament_results>`.
If this is left unspecified, the matchup id will be the index of the matchup
in the :pl-setting:`matchups` list (formatted as a decimal string, starting
from ``"0"``).
.. pl-setting:: name
String
A string used to describe the matchup in reports. By default, this has the
form :samp:`{player code} vs {player code}`; you may wish to change it if you
have more than one matchup between the same pair of players (perhaps with
different komi or handicap).
.. pl-setting:: alternating
Boolean (default ``False``)
If this is ``True``, the players will swap colours in successive games.
Otherwise, the player given as the first argument always takes Black.
.. pl-setting:: number_of_games
Integer (default ``None``)
The total number of games to play in the matchup. If you leave this unset,
there will be no limit.
Changing :pl-setting:`!number_of_games` to ``0`` provides a way to effectively
disable a matchup in future runs, without forgetting its results.
Reporting
"""""""""
The :ref:`live display <live_display>` and :ref:`competition report
<competition report file>` show each matchup's results in the following form::
gnugo-l1 v gnugo-l2 (5/5 games)
board size: 9 komi: 7.5
wins black white avg cpu
gnugo-l1 2 40.00% 1 33.33% 1 50.00% 1.23
gnugo-l2 3 60.00% 1 50.00% 2 66.67% 1.39
2 40.00% 3 60.00%
or, if the players have not alternated colours::
gnugo-l1 v gnugo-l2 (5/5 games)
board size: 9 komi: 7.5
wins avg cpu
gnugo-l1 0 0.00% (black) 0.49
gnugo-l2 5 100.00% (white) 0.48
Any :term:`jigos <jigo>` are counted as half a win for each player. If any
games have been lost by forfeit, a count will be shown for each player. If any
games have unknown results (because they could not be scored, or reached the
:setting:`move_limit`), a count will be shown for each matchup. :ref:`void
games` are not shown in these reports.
If there is more than one matchup between the same pair of players, use the
matchup :pl-setting:`name` setting to distinguish them.
Changing the control file between runs
""""""""""""""""""""""""""""""""""""""
If you change a :pl-setting-cls:`Matchup` definition, the new definition will
be used when describing the matchup in reports; there'll be no record of the
earlier definition, or which games were played under it.
If you change a :pl-setting-cls:`Matchup` definition to have different players
(ie, player codes), the ringmaster will refuse to run the competition.
If you delete a :pl-setting-cls:`Matchup` definition, results from that
matchup won't be displayed during future runs, but will be included (with some
missing information) in the :action:`report` and :action:`show` output.
If you add a :pl-setting-cls:`Matchup` definition, put it at the end of the
list (or else explicitly specify the matchup ids).
It's safe to increase or decrease a matchup's :pl-setting:`number_of_games`.
If more games have been played than the new limit, they will not be forgotten.
In practice, you shouldn't delete :pl-setting-cls:`Matchup` definitions (if
you don't want any more games to be played, set :pl-setting:`number_of_games`
to ``0``).

View File

@ -0,0 +1,12 @@
# Sphinx inventory version 1
# Project: Python
# Version: 2.7
os.path.expanduser function library/os.path.html
shlex.split function library/shlex.html
termios mod library/termios.html
math mod library/math.html
abs function library/functions.html
max function library/functions.html
min function library/functions.html
round function library/functions.html
datetime.date class library/datetime.html

106
gomill/docs/results.rst Normal file
View File

@ -0,0 +1,106 @@
Viewing results
---------------
.. contents:: Page contents
:local:
:backlinks: none
.. _competition report file:
.. index:: report file
Reports
^^^^^^^
The competition :dfn:`report file` (:file:`{code}.report`) file is a plain
text description of the competition results. This is similar to the live
report that is displayed while the competition is running. It includes the
contents of the competition's :setting:`description` setting.
For tournaments, it also shows descriptions of the players. These are obtained
using the |gtp| :gtp:`!name` and :gtp:`!version` commands, or using
:gtp:`gomill-describe_engine` if the engine provides it.
For example, in a playoff tournament with a single matchup::
playoff: example
Testing GNU Go level 1 vs level 2, 2010-10-14
gnugo-l1 v gnugo-l2 (5/5 games)
board size: 9 komi: 7.5
wins black white avg cpu
gnugo-l1 2 40.00% 1 33.33% 1 50.00% 1.23
gnugo-l2 3 60.00% 1 50.00% 2 66.67% 1.39
2 40.00% 3 60.00%
player gnugo-l1: GNU Go:3.8
player gnugo-l2: GNU Go:3.8
The report file is written automatically at the end of each run. The
:action:`report` command line action forces it to be rewritten; this can be
useful if you have changed descriptive text in the control file, or if a run
stopped ungracefully.
The :action:`show` command line action prints the same report to standard
output.
It's safe to run :action:`show` or :action:`report` on a competition which is
currently being run.
.. _game records:
Game records
^^^^^^^^^^^^
The ringmaster writes an |sgf| record of each game it plays to the
:file:`{code}.games/` directory (which it will create if necessary). This can
be disabled with the :setting:`record_games` setting. The filename is based on
the game's :ref:`game_id <game id>`.
(You might also see game records in a :file:`{code}.void/` directory; these
are games which were abandoned due to software failure; see :ref:`void
games`.)
The ringmaster supports a protocol for engines to provide text to be placed in
the comment section for individual moves: see :gtp:`gomill-explain_last_move`.
The game record includes a description of the players in the root node comment
[#]_. If an engine implements :gtp:`gomill-describe_engine`, its output is
included.
.. [#] The root node comment is used rather than the game comment because (in
my experience) |sgf| viewers tend to make it easier to see information
there.
.. index:: CPU time
.. index:: time; CPU
.. _cpu time:
CPU time
^^^^^^^^
The reports and game records show the CPU time taken by the players, when
available.
If an engine implements the :gtp:`gomill-cpu_time` command, its output is
used. Otherwise, the ringmaster uses the CPU time of the engine process that
it created, as returned by the :c:func:`!wait4()` system call (user plus system
time); unfortunately, this may not be meaningful, if the engine's work isn't
all done directly in that process.
.. _querying the results:
Querying the results
^^^^^^^^^^^^^^^^^^^^
Gomill provides a Python library interface for processing the game results
stored in a tournament's :ref:`state file <competition state>`.
This is documented in :doc:`tournament_results`. See also the
:script:`find_forfeits.py` example script.

View File

@ -0,0 +1,71 @@
The ringmaster
==============
The ringmaster is a command line program which arranges games between |gtp|
engines and keeps track of the results. See :doc:`ringmaster_cmdline` below
for details of the command line options.
.. contents:: Page contents
:local:
:backlinks: none
Competitions and runs
---------------------
.. index:: competition
The ringmaster takes its instructions from a single configuration file known
as the :doc:`control file <settings>`. Each control file defines a
:term:`competition`; the :ref:`output files <output files>` for that
competition are written to the directory containing the control file.
.. index:: run
A competition can take place over multiple invocations of the ringmaster
(:dfn:`runs`). For example, a run can be halted from the console, in which
case starting the ringmaster again will make it continue from where it left
off.
Competition types
-----------------
The ringmaster supports a number of different :dfn:`competition types`.
These are divided into :dfn:`tournaments` and :dfn:`tuning events`.
In a tournament, the ringmaster plays games between predefined players, in
order to compare their strengths. The types of tournament are:
Playoff tournaments
In a playoff tournament the control file explicitly describes one or more
pairings of players (:dfn:`matchups`). Each matchup can have separate
settings.
All-play-all tournaments
In an all-play-all tournament the control file lists a number of players, and
games are played with the same settings between each possible pairing.
In a tuning event, the ringmaster runs an algorithm for adjusting player
parameters to try to find the values which give strongest play.
See :ref:`competition types` for full details of the types of tournament and
tuning event.
Using the ringmaster
--------------------
.. toctree::
:maxdepth: 3
:titlesonly:
competitions
results
ringmaster_cmdline
settings
competition_types
Error handling… <errors>

View File

@ -0,0 +1,80 @@
Command line
^^^^^^^^^^^^
.. program:: ringmaster
.. index:: action; ringmaster
The ringmaster expects two command line arguments: the pathname of the control
file and an :dfn:`action`::
ringmaster [options] <code>.ctl run
ringmaster [options] <code>.ctl show
ringmaster [options] <code>.ctl reset
ringmaster [options] <code>.ctl check
ringmaster [options] <code>.ctl report
ringmaster [options] <code>.ctl stop
The default action is :action:`!run`, so running a competition is normally a
simple line like::
$ ringmaster competitions/test.ctl
See :ref:`Stopping competitions <stopping competitions>` for the various ways
to stop the ringmaster.
The following actions are available:
.. action:: run
Starts the competition running. If the competition has been run previously,
it continues from where it left off.
.. action:: show
Prints a :ref:`report <competition report file>` of the competition's
current status. This can be used for both running and stopped competitions.
.. action:: reset
Cleans up the competition completely. This deletes all output files,
including the competition's :ref:`state file <competition state>`.
.. action:: check
Runs a test invocation of the competition's players. This is the same as the
:ref:`startup checks`, except that any output the players send to their
standard error stream will be printed.
.. action:: report
Rewrites the :ref:`competition report file <competition report file>` based
on the current status. This can be used for both running and stopped
competitions.
.. action:: stop
Tells a running ringmaster for the competition to stop as soon as the
current games have completed.
The following options are available:
.. option:: --parallel <N>, -j <N>
Play N :ref:`simultaneous games <simultaneous games>`.
.. option:: --quiet, -q
Disable the on-screen reporting; see :ref:`Quiet mode <quiet mode>`.
.. option:: --max-games <N>, -g <N>
Maximum number of games to play in the run; see :ref:`running
competitions`.
.. option:: --log-gtp
Log all |gtp| traffic; see :ref:`logging`.

534
gomill/docs/settings.rst Normal file
View File

@ -0,0 +1,534 @@
.. _control file:
The control file
----------------
.. contents:: Page contents
:local:
:backlinks: none
.. _sample control file:
Sample control file
^^^^^^^^^^^^^^^^^^^
Here is a sample control file for a playoff tournament::
competition_type = 'playoff'
description = """
This is a sample control file.
It illustrates player definitions, common settings, and game settings.
"""
record_games = True
stderr_to_log = False
players = {
# GNU Go level 1
'gnugo-l1' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=1"),
# GNU Go level 2
'gnugo-l2' : Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=2"),
# Fuego at 5000 playouts per move
'fuego-5k' : Player("fuego --quiet",
startup_gtp_commands=[
"go_param timelimit 999999",
"uct_max_memory 350000000",
"uct_param_search number_threads 1",
"uct_param_player reuse_subtree 0",
"uct_param_player ponder 0",
"uct_param_player max_games 5000",
]),
}
board_size = 9
komi = 6
matchups = [
Matchup('gnugo-l1', 'fuego-5k', board_size=13,
handicap=2, handicap_style='free', komi=0,
scorer='players', number_of_games=5),
Matchup('gnugo-l2', 'fuego-5k', alternating=True,
scorer='players', move_limit=200),
Matchup('gnugo-l1', 'gnugo-l2',
komi=0.5,
scorer='internal'),
]
File format
^^^^^^^^^^^
The control file is a plain text configuration file.
It is interpreted in the same way as a Python source file. See the
:ref:`sample control file` above for an example of the syntax.
.. __: http://docs.python.org/release/2.7/reference/index.html
The control file is made up of a series of top-level :dfn:`settings`, in the
form of assignment statements: :samp:`{setting_name} = {value}`.
Each top-level setting should begin on a new line, in the leftmost column of
the file. Settings which use brackets of any kind can be split over multiple
lines between elements (for example, lists can be split at the commas).
Comments are introduced by the ``#`` character, and continue until the end of
the line.
In general, the order of settings in the control file isn't significant
(except for list members). But note that :setting:`competition_type` must come
first.
See :ref:`data types` below for the representation of values. See the `Python
language reference`__ for a formal specification.
The settings which are common to all competition types are listed below.
Further settings are given on the page documenting each competition type.
.. caution:: while the ringmaster will give error messages for unacceptable
setting values, it will ignore attempts to set a nonexistent setting (this
is because you're allowed to define variables of your own in the control
file and use them in later setting definitions).
If you wish, you can use arbitrary Python expressions in the control file; see
:ref:`control file techniques` below.
.. caution:: all Python code in the control file will be executed; a hostile
party with write access to a control file can cause the ringmaster to
execute arbitrary code. On a shared system, do not make the competition
directory or the control file world-writeable.
The recommended filename extension for the control file is :file:`.ctl`.
.. _data types:
Data types
^^^^^^^^^^
The following data types are used for values of settings:
String
A literal string of characters in single or double quotes, eg ``'gnugo-l1'``
or ``"free"``.
Strings containing non-ASCII characters should be encoded as UTF-8 (Python
unicode objects are also accepted).
Strings can be broken over multiple lines by writing adjacent literals
separated only by whitespace; see the :setting-cls:`Player` definitions in
the example above.
Backslash escapes can be used in strings, such as ``\n`` for a newline.
Alternatively, three (single or double) quotes can be used for a multi-line
string; see ``description`` in the example above.
Identifier
A (short) string made up of any combination of ASCII letters, numerals, and
the punctuation characters ``- ! $ % & * + - . : ; < = > ? ^ _ ~``.
Boolean
A truth value, written as ``True`` or ``False``.
Integer
A whole number, written as a decimal literal, eg ``19`` or ``-1``.
Float
A floating-point number, written as a decimal literal, eg ``6`` or ``6.0``
or ``6.5``.
List
A sequence of values of uniform type, written with square brackets separated
by commas, eg ``["max_playouts 3000", "initial_wins 5"]``. An extra comma
after the last item is harmless.
Dictionary
An explicit map of keys of uniform type to values of uniform type, written
with curly brackets, colons, and commas, eg ``{'p1' : True, 'p2' : False}``.
An extra comma after the last item is harmless.
.. _file and directory names:
File and directory names
^^^^^^^^^^^^^^^^^^^^^^^^
When values in the control file are file or directory names, non-absolute
names are interpreted relative to the :ref:`competition directory <competition
directory>`.
If a file or directory name begins with ``~``, home directory expansion is
applied (see :func:`os.path.expanduser`).
.. _common settings:
Common settings
^^^^^^^^^^^^^^^
The following settings can appear at the top level of the control file for all
competition types.
.. setting:: competition_type
String: ``"playoff"``, ``"allplayall"``, ``"mc_tuner"``, or ``"ce_tuner"``
Determines the type of tournament or tuning event. This must be set on the
first line in the control file (not counting blank lines and comments).
.. setting:: description
String (default ``None``)
A text description of the competition. This will be included in the
:ref:`competition report file <competition report file>`. Leading and
trailing whitespace is ignored.
.. setting:: record_games
Boolean (default ``True``)
Write |sgf| :ref:`game records <game records>`.
.. setting:: stderr_to_log
Boolean (default ``True``)
Redirect all players' standard error streams to the :ref:`event log
<logging>`. See :ref:`standard error`.
.. _player codes:
.. index:: player code
.. setting:: players
Dictionary mapping identifiers to :setting-cls:`Player` definitions (see
:ref:`player configuration`).
Describes the |gtp| engines that can be used in the competition. If you wish
to use the same program with different settings, each combination of
settings must be given its own :setting-cls:`Player` definition. See
:ref:`control file techniques` below for a compact way to define several
similar Players.
The dictionary keys are the :dfn:`player codes`; they are used to identify
the players in reports and the |sgf| game records, and elsewhere in the
control file to specify how players take part in the competition.
See the pages for specific competition types for the way in which players
are selected from the :setting:`!players` dictionary.
It's fine to have player definitions here which aren't used in the
competition. These definitions will be ignored, and no corresponding engines
will be run.
.. _player configuration:
Player configuration
^^^^^^^^^^^^^^^^^^^^
.. setting-cls:: Player
A :setting-cls:`!Player` definition has the same syntax as a Python function
call: :samp:`Player({arguments})`. Apart from :setting:`command`, the
arguments should be specified using keyword form (see the examples for
particular arguments below).
All arguments other than :setting:`command` are optional.
.. tip:: For results to be meaningful, you should normally configure players
to use a fixed amount of computing power, paying no attention to the amount
of real time that passes, and make sure :term:`pondering` is not turned on.
The arguments are:
.. setting:: command
String or list of strings
This is the only required :setting-cls:`Player` argument. It can be
specified either as the first argument, or using a keyword
:samp:`command="{...}"`. It specifies the executable which will provide the
player, and its command line arguments.
The player subprocess is executed directly, not run via a shell.
The :setting:`!command` can be either a string or a list of strings. If it
is a string, it is split using rules similar to a Unix shell's (see
:func:`shlex.split`).
In either case, the first element is taken as the executable name and the
remainder as its arguments.
If the executable name does not contain a ``/``, it is searched for on the
the :envvar:`!PATH`. Otherwise it is handled as described in :ref:`file and
directory names <file and directory names>`.
Example::
Player("~/src/fuego-svn/fuegomain/fuego --quiet")
.. setting:: cwd
String (default ``None``)
The working directory for the player.
If this is left unset, the player's working directory will be the working
directory from when the ringmaster was launched (which may not be the
competition directory). Use ``cwd="."`` to specify the competition
directory.
.. tip::
If an engine writes debugging information to its working directory, use
:setting:`cwd` to get it out of the way::
Player('mogo', cwd='~/tmp')
.. setting:: environ
Dictionary mapping strings to strings (default ``None``)
This specifies environment variables to be set in the player process, in
addition to (or overriding) those inherited from its parent.
Note that there is no special handling in this case for values which happen
to be file or directory names.
Example::
Player('goplayer', environ={'GOPLAYER-DEBUG' : 'true'})
.. setting:: discard_stderr
Boolean (default ``False``)
Redirect the player's standard error stream to :file:`/dev/null`. See
:ref:`standard error`.
Example::
Player('mogo', discard_stderr=True)
.. setting:: startup_gtp_commands
List of strings, or list of lists of strings (default ``None``)
|gtp| commands to send at the beginning of each game. See :ref:`playing
games`.
Each command can be specified either as a single string or as a list of
strings (with each |gtp| argument in a single string). For example, the
following are equivalent::
Player('fuego', startup_gtp_commands=[
"uct_param_player ponder 0",
"uct_param_player max_games 5000"])
Player('fuego', startup_gtp_commands=[
["uct_param_player", "ponder", "0"],
["uct_param_player", "max_games", "5000"]])
.. setting:: gtp_aliases
Dictionary mapping strings to strings (default ``None``)
This is a map of |gtp| command names to command names, eg::
Player('fuego', gtp_aliases={'gomill-cpu_time' : 'cputime'})
When the ringmaster would normally send :gtp:`gomill-cpu_time`, it will send
:gtp:`!cputime` instead.
The command names are case-sensitive. There is no mechanism for altering
arguments.
.. setting:: is_reliable_scorer
Boolean (default ``True``)
If the :setting:`scorer` setting is ``players``, the ringmaster normally
asks each player that implements the :gtp:`!final_score` |gtp| command to
report the game result. Setting :setting:`!is_reliable_scorer` to ``False``
for a player causes that player never to be asked.
.. setting:: allow_claim
Boolean (default ``False``)
Permits the player to claim a win (using the |gtp| extension
:gtp:`gomill-genmove_ex`). See :ref:`claiming wins`.
.. _game settings:
Game settings
^^^^^^^^^^^^^
The following settings describe how a particular game is to be played.
They are not all used in every competition type, and may be specified in some
other way than a top level control file setting; see the page documenting a
particular competition type for details.
.. setting:: board_size
Integer
The size of Go board to use for the game (eg ``19`` for a 19x19 game). The
ringmaster is willing to use board sizes from 2 to 25.
.. setting:: komi
Float
The :term:`komi` to use for the game. You can specify any floating-point
value, and it will be passed on to the |gtp| engines unchanged, but normally
only integer or half-integer values will be useful. Negative values are
allowed.
.. setting:: handicap
Integer (default ``None``)
The number of handicap stones to give Black at the start of the game. See
also :setting:`handicap_style`.
See the `GTP specification`_ for the rules about what handicap values
are permitted for different board sizes (in particular, values less than 2
are never allowed).
.. setting:: handicap_style
String: ``"fixed"`` or ``"free"`` (default ``"fixed"``)
Determines whether the handicap stones are placed on prespecified points, or
chosen by the Black player. See the `GTP specification`_ for more details.
This is ignored if :setting:`handicap` is unset.
.. _GTP specification: http://www.lysator.liu.se/~gunnar/gtp/gtp2-spec-draft2/gtp2-spec.html#SECTION00051000000000000000
.. setting:: move_limit
Integer (default ``1000``)
The maximum number of moves to allow in a game. If this limit is reached,
the game is stopped; see :ref:`playing games`.
.. setting:: scorer
String: ``"players"`` or ``"internal"`` (default ``"players"``)
Determines whether the game result is determined by the engines, or by the
ringmaster. See :ref:`Scoring <scoring>` and :setting:`is_reliable_scorer`.
.. setting:: internal_scorer_handicap_compensation
String: ``"no"``, ``"full"`` or ``"short"`` (default ``"full"``)
Specifies whether White is given extra points to compensate for Black's
handicap stones; see :ref:`Scoring <scoring>` for details. This setting has
no effect for games which are played without handicap, and it has no effect
when :setting:`scorer` is set to ``"players"``.
Changing the control file between runs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changing the control file between runs of the same competition (or after the
final run) is allowed. For example, in a playoff tournament it's fine to
increase a completed matchup's :pl-setting:`number_of_games` and set the
competition off again.
The intention is that nothing surprising should happen if you change the
control file; of course if you change settings which affect player behaviour
then result summaries might not be meaningful.
In particular, if you change a :setting-cls:`Player` definition, the new
definition will be used when describing the player in reports; there'll be no
record of the earlier definition, or which games were played under it.
If you change descriptive text, you can use the :action:`report` command line
action to remake the report file.
The page documenting each competition type has more detail on what it is safe
to change.
.. _control file techniques:
Control file techniques
^^^^^^^^^^^^^^^^^^^^^^^
As the control file is just Python code, it's possible to use less direct
methods to specify the values of settings.
One convenient way to define a number of similar players is to define a
function which returns a :setting-cls:`Player` object. For example, the player
definitions in the sample control file could be rewritten as follows::
def gnugo(level):
return Player("gnugo --mode=gtp --chinese-rules "
"--capture-all-dead --level=%d" % level)
def fuego(playouts_per_move, additional_commands=[]):
commands = [
"go_param timelimit 999999",
"uct_max_memory 350000000",
"uct_param_search number_threads 1",
"uct_param_player reuse_subtree 0",
"uct_param_player ponder 0",
"uct_param_player max_games %d" % playouts_per_move,
]
return Player(
"fuego --quiet",
startup_gtp_commands=commands+additional_commands)
players = {
'gnugo-l1' : gnugo(level=1),
'gnugo-l2' : gnugo(level=2),
'fuego-5k' : fuego(playouts_per_move=5000)
}
If you assign to a setting more than once, the final value is the one that
counts. Settings specified above as having default ``None`` can be assigned
the value ``None``, which will be equivalent to leaving them unset.
Importing parts of the Python standard library (or other Python libraries that
you have installed) is allowed.

914
gomill/docs/sgf.rst Normal file
View File

@ -0,0 +1,914 @@
SGF support
-----------
.. module:: gomill.sgf
:synopsis: High level SGF interface.
.. versionadded:: 0.7
Gomill's |sgf| support is intended for use with version FF[4], which is
specified at http://www.red-bean.com/sgf/index.html. It has support for the
game-specific properties for Go, but not those of other games. Point, Move and
Stone values are interpreted as Go points.
The :mod:`gomill.sgf` module provides the main support. This module is
independent of the rest of Gomill.
The :mod:`gomill.sgf_moves` module contains some higher-level functions for
processing moves and positions, and provides a link to the
:mod:`.boards` module.
The :mod:`!gomill.sgf_grammar` and :mod:`!gomill.sgf_properties` modules are
used to implement the :mod:`!sgf` module, and are not currently documented.
.. contents:: Page contents
:local:
:backlinks: none
Examples
^^^^^^^^
Reading and writing::
>>> from gomill import sgf
>>> g = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9];B[ee];W[ge])")
>>> g.get_size()
9
>>> root_node = g.get_root()
>>> root_node.get("SZ")
9
>>> root_node.get_raw("SZ")
'9'
>>> root_node.set("RE", "B+R")
>>> new_node = g.extend_main_sequence()
>>> new_node.set_move("b", (2, 3))
>>> [node.get_move() for node in g.get_main_sequence()]
[(None, None), ('b', (4, 4)), ('w', (4, 6)), ('b', (2, 3))]
>>> g.serialise()
'(;FF[4]GM[1]RE[B+R]SZ[9];B[ee];W[ge];B[dg])\n'
Recording a game::
g = sgf.Sgf_game(size=13)
for move_info in ...:
node = g.extend_main_sequence()
node.set_move(move_info.colour, move_info.move)
if move_info.comment is not None:
node.set("C", move_info.comment)
with open(pathname, "w") as f:
f.write(g.serialise())
See also the :script:`show_sgf.py` and :script:`split_sgf_collection.py`
example scripts.
Sgf_game objects
^^^^^^^^^^^^^^^^
|sgf| data is represented using :class:`!Sgf_game` objects. Each object
represents the data for a single |sgf| file (corresponding to a ``GameTree``
in the |sgf| spec). This is typically used to represent a single game,
possibly with variations (but it could be something else, such as a problem
set).
An :class:`!Sgf_game` can either be created from scratch or loaded from a
string.
To create one from scratch, instantiate an :class:`!Sgf_game` object directly:
.. class:: Sgf_game(size, encoding="UTF-8"])
*size* is an integer from 1 to 26, indicating the board size.
The optional *encoding* parameter specifies the :ref:`raw property encoding
<raw_property_encoding>` to use for the game.
When a game is created this way, the following root properties are initially
set: :samp:`FF[4]`, :samp:`GM[1]`, :samp:`SZ[{size}]`, and
:samp:`CA[{encoding}]`.
To create a game from existing |sgf| data, use the
:func:`!Sgf_game.from_string` classmethod:
.. classmethod:: Sgf_game.from_string(s[, override_encoding=None])
:rtype: :class:`!Sgf_game`
Creates an :class:`!Sgf_game` from the |sgf| data in *s*, which must be an
8-bit string.
The board size and :ref:`raw property encoding <raw_property_encoding>` are
taken from the ``SZ`` and ``CA`` properties in the root node (defaulting to
``19`` and ``"ISO-8859-1"``, respectively). Board sizes greater than ``26``
are rejected.
If *override_encoding* is present, the source data is assumed to be in the
encoding it specifies (no matter what the ``CA`` property says), and the
``CA`` property and raw property encoding are changed to match.
Raises :exc:`ValueError` if it can't parse the string, or if the ``SZ`` or
``CA`` properties are unacceptable. No error is reported for other
malformed property values. See also :ref:`parsing_details` below.
Example::
g = sgf.Sgf_game.from_string(
"(;FF[4]GM[1]SZ[9]CA[UTF-8];B[ee];W[ge])",
override_encoding="iso8859-1")
To retrieve the |sgf| data as a string, use the :meth:`!serialise` method:
.. method:: Sgf_game.serialise([wrap])
:rtype: string
Produces the |sgf| representation of the data in the :class:`!Sgf_game`.
Returns an 8-bit string, in the encoding specified by the ``CA`` root
property (defaulting to ``"ISO-8859-1"``).
See :ref:`transcoding <transcoding>` below for details of the behaviour if
the ``CA`` property is changed from its initial value.
This makes some effort to keep the output line length to no more than 79
bytes. Pass ``None`` in the *wrap* parameter to disable this behaviour, or
pass an integer to specify a different limit.
The complete game tree is represented using :class:`Tree_node` objects, which
are used to access the |sgf| properties. An :class:`!Sgf_game` always has at
least one node, the :dfn:`root node`.
.. method:: Sgf_game.get_root()
:rtype: :class:`Tree_node`
Returns the root node of the game tree.
The root node contains global properties for the game tree, and typically also
contains *game-info* properties. It sometimes also contains *setup* properties
(for example, if the game does not begin with an empty board).
Changing the ``FF`` and ``GM`` properties is permitted, but Gomill will carry
on using the FF[4] and GM[1] (Go) rules. Changing ``SZ`` is not permitted (but
if the size is 19 you may remove the property). Changing ``CA`` is permitted
(this controls the encoding used by :meth:`~Sgf_game.serialise`).
.. rubric:: Convenience methods for tree access
The complete game tree can be accessed through the root node, but the
following convenience methods are also provided. They return the same
:class:`Tree_node` objects that would be reached via the root node.
Some of the convenience methods are for accessing the :dfn:`leftmost`
variation of the game tree. This is the variation which appears first in the
|sgf| ``GameTree``, often shown in graphical editors as the topmost horizontal
line of nodes. In a game tree without variations, the leftmost variation is
just the whole game.
.. method:: Sgf_game.get_last_node()
:rtype: :class:`Tree_node`
Returns the last (leaf) node in the leftmost variation.
.. method:: Sgf_game.get_main_sequence()
:rtype: list of :class:`Tree_node` objects
Returns the complete leftmost variation. The first element is the root
node, and the last is a leaf.
.. method:: Sgf_game.get_main_sequence_below(node)
:rtype: list of :class:`Tree_node` objects
Returns the leftmost variation beneath the :class:`Tree_node` *node*. The
first element is the first child of *node*, and the last is a leaf.
Note that this isn't necessarily part of the leftmost variation of the
game as a whole.
.. method:: Sgf_game.get_main_sequence_above(node)
:rtype: list of :class:`Tree_node` objects
Returns the partial variation leading to the :class:`Tree_node` *node*. The
first element is the root node, and the last is the parent of *node*.
.. method:: Sgf_game.extend_main_sequence()
:rtype: :class:`Tree_node`
Creates a new :class:`Tree_node`, adds it to the leftmost variation, and
returns it.
This is equivalent to
:meth:`get_last_node`\ .\ :meth:`~Tree_node.new_child`
.. rubric:: Convenience methods for root properties
The following methods provide convenient access to some of the root node's
|sgf| properties. The main difference between using these methods and using
:meth:`~Tree_node.get` on the root node is that these methods return the
appropriate default value if the property is not present.
.. method:: Sgf_game.get_size()
:rtype: integer
Returns the board size (``19`` if the ``SZ`` root property isn't present).
.. method:: Sgf_game.get_charset()
:rtype: string
Returns the effective value of the ``CA`` root property (``ISO-8859-1`` if
the ``CA`` root property isn't present).
The returned value is a codec name in normalised form, which may not be
identical to the string returned by ``get_root().get("CA")``. Raises
:exc:`ValueError` if the property value doesn't identify a Python codec.
This gives the encoding that would be used by :meth:`serialise`. It is not
necessarily the same as the :ref:`raw property encoding
<raw_property_encoding>` (use :meth:`~Tree_node.get_encoding` on the root
node to retrieve that).
.. method:: Sgf_game.get_komi()
:rtype: float
Returns the :term:`komi` (``0.0`` if the ``KM`` root property isn't
present).
Raises :exc:`ValueError` if the ``KM`` root property is present but
malformed.
.. method:: Sgf_game.get_handicap()
:rtype: integer or ``None``
Returns the number of handicap stones.
Returns ``None`` if the ``HA`` root property isn't present, or if it has
value zero (which isn't strictly permitted).
Raises :exc:`ValueError` if the ``HA`` property is otherwise malformed.
.. method:: Sgf_game.get_player_name(colour)
:rtype: string or ``None``
Returns the name of the specified player, or ``None`` if the required
``PB`` or ``PW`` root property isn't present.
.. method:: Sgf_game.get_winner()
:rtype: *colour*
Returns the colour of the winning player.
Returns ``None`` if the ``RE`` root property isn't present, or if neither
player won.
.. method:: Sgf_game.set_date([date])
Sets the ``DT`` root property, to a single date.
If *date* is specified, it should be a :class:`datetime.date`. Otherwise
the current date is used.
(|sgf| allows ``DT`` to be rather more complicated than a single date, so
there's no corresponding get_date() method.)
Tree_node objects
^^^^^^^^^^^^^^^^^
.. class:: Tree_node
A Tree_node object represents a single node from an |sgf| file.
Don't instantiate Tree_node objects directly; retrieve them from
:class:`Sgf_game` objects.
Tree_node objects have the following attributes (which should be treated as
read-only):
.. attribute:: owner
The :class:`Sgf_game` that the node belongs to.
.. attribute:: parent
The node's parent :class:`!Tree_node` (``None`` for the root node).
.. rubric:: Tree navigation
A :class:`!Tree_node` acts as a list-like container of its children: it can be
indexed, sliced, and iterated over like a list, and it supports the `index`__
method. A :class:`!Tree_node` with no children is treated as having truth
value false. For example, to find all leaf nodes::
def print_leaf_comments(node):
if node:
for child in node:
print_leaf_comments(child)
else:
if node.has_property("C"):
print node.get("C")
else:
print "--"
.. __: http://docs.python.org/release/2.7/library/stdtypes.html#mutable-sequence-types
.. rubric:: Property access
Each node holds a number of :dfn:`properties`. Each property is identified by
a short string called the :dfn:`PropIdent`, eg ``"SZ"`` or ``"B"``. See
:ref:`sgf_property_list` below for a list of the standard properties. See the
:term:`SGF` specification for full details. See :ref:`parsing_details` below
for restrictions on well-formed *PropIdents*.
Gomill doesn't enforce |sgf|'s restrictions on where properties can appear
(eg, the distinction between *setup* and *move* properties).
The principal methods for accessing the node's properties are:
.. method:: Tree_node.get(identifier)
Returns a native Python representation of the value of the property whose
*PropIdent* is *identifier*.
Raises :exc:`KeyError` if the property isn't present.
Raises :exc:`ValueError` if it detects that the property value is
malformed.
See :ref:`sgf_property_types` below for details of how property values are
represented in Python.
See :ref:`sgf_property_list` below for a list of the known properties.
Setting nonstandard properties is permitted; they are treated as having
type Text.
.. method:: Tree_node.set(identifier, value)
Sets the value of the property whose *PropIdent* is *identifier*.
*value* should be a native Python representation of the required property
value (as returned by :meth:`get`).
Raises :exc:`ValueError` if the property value isn't acceptable.
See :ref:`sgf_property_types` below for details of how property values
should be represented in Python.
See :ref:`sgf_property_list` below for a list of the known properties. Any
other property is treated as having type Text.
.. method:: Tree_node.unset(identifier)
Removes the property whose *PropIdent* is *identifier* from the node.
Raises :exc:`KeyError` if the property isn't currently present.
.. method:: Tree_node.has_property(identifier)
:rtype: bool
Checks whether the property whose *PropIdent* is *identifier* is present.
.. method:: Tree_node.properties()
:rtype: list of strings
Lists the properties which are present in the node.
Returns a list of *PropIdents*, in unspecified order.
.. method:: Tree_node.find_property(identifier)
Returns the value of the property whose *PropIdent* is *identifier*,
looking in the node's ancestors if necessary.
This is intended for use with properties of type *game-info*, and with
properties which have the *inherit* attribute.
It looks first in the node itself, then in its parent, and so on up to the
root, returning the first value it finds. Otherwise the behaviour is the
same as :meth:`get`.
Raises :exc:`KeyError` if no node defining the property is found.
.. method:: Tree_node.find(identifier)
:rtype: :class:`!Tree_node` or ``None``
Returns the nearest node defining the property whose *PropIdent* is
*identifier*.
Searches in the same way as :meth:`find_property`, but returns the node
rather than the property value. Returns ``None`` if no node defining the
property is found.
.. rubric:: Convenience methods for properties
The following convenience methods are also provided, for more flexible access
to a few of the most important properties:
.. method:: Tree_node.get_move()
:rtype: tuple (*colour*, *move*)
Indicates which of the the ``B`` or ``W`` properties is present, and
returns its value.
Returns (``None``, ``None``) if neither property is present.
.. method:: Tree_node.set_move(colour, move)
Sets the ``B`` or ``W`` property. If the other property is currently
present, it is removed.
Gomill doesn't attempt to ensure that moves are legal.
.. method:: Tree_node.get_setup_stones()
:rtype: tuple (set of *points*, set of *points*, set of *points*)
Returns the settings of the ``AB``, ``AW``, and ``AE`` properties.
The tuple elements represent black, white, and empty points respectively.
If a property is missing, the corresponding set is empty.
.. method:: Tree_node.set_setup_stones(black, white[, empty])
Sets the ``AB``, ``AW``, and ``AE`` properties.
Each parameter should be a sequence or set of *points*. If a parameter
value is empty (or, in the case of *empty*, if the parameter is
omitted) the corresponding property will be unset.
.. method:: Tree_node.has_setup_stones()
:rtype: bool
Returns ``True`` if the ``AB``, ``AW``, or ``AE`` property is present.
.. method:: Tree_node.add_comment_text(text)
If the ``C`` property isn't already present, adds it with the value given
by the string *text*.
Otherwise, appends *text* to the existing ``C`` property value, preceded by
two newlines.
.. rubric:: Board size and raw property encoding
Each :class:`!Tree_node` knows its game's board size, and its :ref:`raw
property encoding <raw_property_encoding>` (because these are needed to
interpret property values). They can be retrieved using the following methods:
.. method:: Tree_node.get_size()
:rtype: int
.. method:: Tree_node.get_encoding()
:rtype: string
This returns the name of the raw property encoding (in a normalised form,
which may not be the same as the string originally used to specify the
encoding).
An attempt to change the value of the ``SZ`` property so that it doesn't match
the board size will raise :exc:`ValueError` (even if the node isn't the root).
.. rubric:: Access to raw property values
Raw property values are 8-bit strings, containing the exact bytes that go
between the ``[`` and ``]`` in the |sgf| file. They should be treated as being
encoded in the node's :ref:`raw property encoding <raw_property_encoding>`
(but there is no guarantee that they hold properly encoded data).
The following methods are provided for access to raw property values. They can
be used to access malformed values, or to avoid the standard escape processing
and whitespace conversion for Text and SimpleText values.
When setting raw property values, any string that is a well formed |sgf|
*PropValue* is accepted: that is, any string that that doesn't contain an
unescaped ``]`` or end with an unescaped ``\``. There is no check that the
string is properly encoded in the raw property encoding.
.. method:: Tree_node.get_raw_list(identifier)
:rtype: nonempty list of 8-bit strings
Returns the raw values of the property whose *PropIdent* is *identifier*.
Raises :exc:`KeyError` if the property isn't currently present.
If the property value is an empty elist, returns a list containing a single
empty string.
.. method:: Tree_node.get_raw(identifier)
:rtype: 8-bit string
Returns the raw value of the property whose *PropIdent* is *identifier*.
Raises :exc:`KeyError` if the property isn't currently present.
If the property has multiple `PropValue`\ s, returns the first. If the
property value is an empty elist, returns an empty string.
.. method:: Tree_node.get_raw_property_map(identifier)
:rtype: dict: string → list of 8-bit strings
Returns a dict mapping *PropIdents* to lists of raw values.
Returns the same dict object each time it's called.
Treat the returned dict object as read-only.
.. method:: Tree_node.set_raw_list(identifier, values)
Sets the raw values of the property whose *PropIdent* is *identifier*.
*values* must be a nonempty list of 8-bit strings. To specify an empty
elist, pass a list containing a single empty string.
Raises :exc:`ValueError` if the identifier isn't a well-formed *PropIdent*,
or if any value isn't a well-formed *PropValue*.
.. method:: Tree_node.set_raw(identifier, value)
Sets the raw value of the property whose *PropIdent* is *identifier*.
Raises :exc:`ValueError` if the identifier isn't a well-formed *PropIdent*,
or if the value isn't a well-formed *PropValue*.
.. rubric:: Tree manipulation
The following methods are provided for manipulating the tree:
.. method:: Tree_node.new_child([index])
:rtype: :class:`!Tree_node`
Creates a new :class:`!Tree_node` and adds it to the tree as this node's
last child.
If the optional integer *index* parameter is present, the new node is
inserted in the list of children at the specified index instead (with the
same behaviour as :meth:`!list.insert`).
Returns the new node.
.. method:: Tree_node.delete()
Removes the node from the tree (along with all its descendents).
Raises :exc:`ValueError` if called on the root node.
You should not continue to use a node which has been removed from its tree.
.. method:: Tree_node.reparent(new_parent[, index])
Moves the node from one part of the tree to another (along with all its
descendents).
*new_parent* must be a node belonging to the same game.
Raises :exc:`ValueError` if the operation would create a loop in the tree
(ie, if *new_parent* is the node being moved or one of its descendents).
If the optional integer *index* parameter is present, the new node is
inserted in the new parent's list of children at the specified index;
otherwise it is placed at the end.
This method can be used to reorder variations. For example, to make a node
the leftmost variation of its parent::
node.reparent(node.parent, 0)
.. _sgf_property_types:
Property types
^^^^^^^^^^^^^^
The :meth:`~Tree_node.get` and :meth:`~Tree_node.set` node methods convert
between raw |sgf| property values and suitable native Python types.
The following table shows how |sgf| property types are represented as Python
values:
=========== ========================
|sgf| type Python representation
=========== ========================
None ``True``
Number int
Real float
Double ``1`` or ``2`` (int)
Colour *colour*
SimpleText 8-bit UTF-8 string
Text 8-bit UTF-8 string
Stone *point*
Point *point*
Move *move*
=========== ========================
Gomill doesn't distinguish the Point and Stone |sgf| property types. It
rejects representations of 'pass' for the Point and Stone types, but accepts
them for Move (this is not what is described in the |sgf| specification, but
it does correspond to the properties in which 'pass' makes sense).
Values of list or elist types are represented as Python lists. An empty elist
is represented as an empty Python list (in contrast, the raw value is a list
containing a single empty string).
Values of compose types are represented as Python pairs (tuples of length
two). ``FG`` values are either a pair (int, string) or ``None``.
For Text and SimpleText values, :meth:`~Tree_node.get` and
:meth:`~Tree_node.set` take care of escaping. You can store arbitrary strings
in a Text value and retrieve them unchanged, with the following exceptions:
* all linebreaks are are normalised to ``\n``
* whitespace other than line breaks is converted to a single space
:meth:`~Tree_node.get` accepts compressed point lists, but
:meth:`~Tree_node.set` never produces them (some |sgf| viewers still don't
support them).
In some cases, :meth:`~Tree_node.get` will accept values which are not
strictly permitted in |sgf|, if there's a sensible way to interpret them. In
particular, empty lists are accepted for all list types (not only elists).
In some cases, :meth:`~Tree_node.set` will accept values which are not exactly
in the Python representation listed, if there's a natural way to convert them
to the |sgf| representation.
Both :meth:`~Tree_node.get` and :meth:`~Tree_node.set` check that Point values
are in range for the board size. Neither :meth:`~Tree_node.get` nor
:meth:`~Tree_node.set` pays attention to range restrictions for values of type
Number.
Examples::
>>> node.set('KO', True)
>>> node.get_raw('KO')
''
>>> node.set('HA', 3)
>>> node.set('KM', 5.5)
>>> node.set('GB', 2)
>>> node.set('PL', 'w')
>>> node.set('RE', 'W+R')
>>> node.set('GC', 'Example game\n[for documentation]')
>>> node.get_raw('GC')
'Example game\n[for documentation\\]'
>>> node.set('B', (2, 3))
>>> node.get_raw('B')
'dg'
>>> node.set('LB', [((6, 0), "label 1"), ((6, 1), "label 2")])
>>> node.get_raw_list('LB')
['ac:label 1', 'bc:label 2']
.. _sgf_property_list:
Property list
^^^^^^^^^^^^^
Gomill knows the types of all general and Go-specific |sgf| properties defined
in FF[4]:
====== ========================== ===================
Id |sgf| type Meaning
====== ========================== ===================
``AB`` list of Stone Add Black
``AE`` list of Point Add Empty
``AN`` SimpleText Annotation
``AP`` SimpleText:SimpleText Application
``AR`` list of Point:Point Arrow
``AW`` list of Stone Add White
``B`` Move Black move
``BL`` Real Black time left
``BM`` Double Bad move
``BR`` SimpleText Black rank
``BT`` SimpleText Black team
``C`` Text Comment
``CA`` SimpleText Charset
``CP`` SimpleText Copyright
``CR`` list of Point Circle
``DD`` elist of Point Dim Points
``DM`` Double Even position
``DO`` None Doubtful
``DT`` SimpleText Date
``EV`` SimpleText Event
``FF`` Number File format
``FG`` None | Number:SimpleText Figure
``GB`` Double Good for Black
``GC`` Text Game comment
``GM`` Number Game
``GN`` SimpleText Game name
``GW`` Double Good for White
``HA`` Number Handicap
``HO`` Double Hotspot
``IT`` None Interesting
``KM`` Real Komi
``KO`` None Ko
``LB`` list of Point:SimpleText Label
``LN`` list of Point:Point Line
``MA`` list of Point Mark
``MN`` Number Set move number
``N`` SimpleText Node name
``OB`` Number Overtime stones left for Black
``ON`` SimpleText Opening
``OT`` SimpleText Overtime description
``OW`` Number Overtime stones left for White
``PB`` SimpleText Black player name
``PC`` SimpleText Place
``PL`` Colour Player to play
``PM`` Number Print move mode
``PW`` SimpleText White player name
``RE`` SimpleText Result
``RO`` SimpleText Round
``RU`` SimpleText Rules
``SL`` list of Point Selected
``SO`` SimpleText Source
``SQ`` list of Point Square
``ST`` Number Style
``SZ`` Number Size
``TB`` elist of Point Black territory
``TE`` Double Tesuji
``TM`` Real Time limit
``TR`` list of Point Triangle
``TW`` elist of Point White territory
``UC`` Double Unclear position
``US`` SimpleText User
``V`` Real Value
``VW`` elist of Point View
``W`` Move White move
``WL`` Real White time left
``WR`` SimpleText White rank
``WT`` SimpleText White team
====== ========================== ===================
.. _raw_property_encoding:
Character encoding handling
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The |sgf| format is defined as containing ASCII-encoded data, possibly with
non-ASCII characters in Text and SimpleText property values. The Gomill
functions for loading and serialising |sgf| data work with 8-bit Python
strings.
The encoding used for Text and SimpleText property values is given by the
``CA`` root property (if that isn't present, the encoding is ``ISO-8859-1``).
In order for an encoding to be used in Gomill, it must exist as a Python
built-in codec, and it must be compatible with ASCII (at least whitespace,
``\``, ``]``, and ``:`` must be in the usual places). Behaviour is unspecified
if a non-ASCII-compatible encoding is requested.
When encodings are passed as parameters (or returned from functions), they are
represented using the names or aliases of Python built-in codecs (eg
``"UTF-8"`` or ``"ISO-8859-1"``). See `standard encodings`__ for a list.
Values of the ``CA`` property are interpreted in the same way.
.. __: http://docs.python.org/release/2.7/library/codecs.html#standard-encodings
Each :class:`.Sgf_game` and :class:`.Tree_node` has a fixed :dfn:`raw property
encoding`, which is the encoding used internally to store the property values.
The :meth:`Tree_node.get_raw` and :meth:`Tree_node.set_raw` methods use the
raw property encoding.
When an |sgf| game is loaded from a file, the raw property encoding is the
original file encoding (unless overridden). Improperly encoded property values
will not be detected until they are accessed (:meth:`~Tree_node.get` will
raise :exc:`ValueError`; use :meth:`~Tree_node.get_raw` to retrieve the actual
bytes).
.. _transcoding:
.. rubric:: Transcoding
When an |sgf| game is serialised to a string, the encoding represented by the
``CA`` root property is used. This :dfn:`target encoding` will be the same as
the raw property encoding unless ``CA`` has been changed since the
:class:`.Sgf_game` was created.
When the raw property encoding and the target encoding match, the raw property
values are included unchanged in the output (even if they are improperly
encoded.)
Otherwise, if any raw property value is improperly encoded,
:exc:`UnicodeDecodeError` is raised, and if any property value can't be
represented in the target encoding, :exc:`UnicodeEncodeError` is raised.
If the target encoding doesn't identify a Python codec, :exc:`ValueError` is
raised. The behaviour of :meth:`~Sgf_game.serialise` is unspecified if the
target encoding isn't ASCII-compatible (eg, UTF-16).
.. _parsing_details:
Parsing
^^^^^^^
The parser permits non-|sgf| content to appear before the beginning and after
the end of the game. It identifies the start of |sgf| content by looking for
``(;`` (with possible whitespace between the two characters).
The parser accepts at most 8 letters in *PropIdents* (there is no formal limit
in the specification, but no standard property has more than 2).
The parser doesn't perform any checks on property values. In particular, it
allows multiple values to be present for any property.
The parser doesn't, in general, attempt to 'fix' ill-formed |sgf| content. As
an exception, if a *PropIdent* appears more than once in a node it is
converted to a single property with multiple values.
The parser doesn't permit lower-case letters in *PropIdents* (these are
allowed in some ancient |sgf| variants).
The :mod:`!sgf_moves` module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. module:: gomill.sgf_moves
:synopsis: Higher-level processing of moves and positions from SGF games.
The :mod:`!gomill.sgf_moves` module contains some higher-level functions for
processing moves and positions, and provides a link to the :mod:`.boards`
module.
.. function:: get_setup_and_moves(sgf_game[, board])
:rtype: tuple (:class:`.Board`, list of tuples (*colour*, *move*))
Returns the initial setup and the following moves from an
:class:`.Sgf_game`.
The board represents the position described by ``AB`` and/or ``AW``
properties in the |sgf| game's root node. :exc:`ValueError` is raised if
this position isn't legal.
The moves are from the game's leftmost variation. Doesn't check that the
moves are legal.
Raises :exc:`ValueError` if the game has structure it doesn't support.
Currently doesn't support ``AB``/``AW``/``AE`` properties after the root
node.
If the optional *board* parameter is provided, it must be an empty
:class:`.Board` of the right size; the same object will be returned (this
option is provided so you can use a different Board class).
See also the :script:`show_sgf.py` example script.
.. function:: set_initial_position(sgf_game, board)
Adds ``AB``/``AW``/``AE`` properties to an :class:`.Sgf_game`'s root node,
to reflect the position from a :class:`.Board`.
Replaces any existing ``AB``/``AW``/``AE`` properties in the root node.
.. function:: indicate_first_player(sgf_game)
Adds a ``PL`` property to an :class:`.Sgf_game`'s root node if appropriate,
to indicate which colour is first to play.
Looks at the first child of the root to see who the first player is, and
sets ``PL`` it isn't the expected player (Black normally, but White if
there is a handicap), or if there are non-handicap setup stones.

View File

@ -0,0 +1,401 @@
Tournament results API
----------------------
.. module:: gomill.tournament_results
:synopsis: Retrieving and reporting on tournament results.
This is a Python interface for processing the game results stored in a
tournament's :ref:`state file <competition state>`. It can be used to write
custom reports, or to find games with particular results.
Note that it can be used only for :ref:`tournaments <tournaments>` (not for
:ref:`tuning events <tuners>`).
.. contents:: Page contents
:local:
:backlinks: none
General
^^^^^^^
In this interface, players are identified using their player codes (that is,
their keys in the control file's :setting:`players` dictionary).
.. note:: In a :doc:`playoff tournament <playoffs>`, it is possible
to define a matchup in which the same player takes both colours. In this
case, the player code used for the second player will be the player code
from the control file with ``'#2'`` appended.
The classes described here are implemented in the
:mod:`!gomill.tournament_results` and :mod:`!gomill.gtp_games` modules, but
you should not normally import these directly. See
:ref:`using_the_api_in_scripts`.
Tournament_results objects
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. class:: Tournament_results
A Tournament_results object provides access to the game results and
statistics for a single tournament.
The tournament results are catalogued in terms of :dfn:`matchups`, with
each matchup corresponding to a series of games which had the same players
and settings. Each matchup has an id, which is a short string.
Tournament_results objects are normally retrieved from :class:`!Competition`
or :class:`!Ringmaster` objects; see :ref:`using_the_api_in_scripts`.
Tournament_results objects support the following methods:
.. method:: get_matchup_ids()
:rtype: list of strings
Return the tournament's matchup ids.
.. method:: get_matchup(matchup_id)
:rtype: :class:`Matchup_description`
Describe the matchup with the specified id.
.. method:: get_matchups()
:rtype: map *matchup id* → :class:`Matchup_description`
Describe all matchups.
.. method:: get_matchup_stats(matchup_id)
:rtype: :class:`Matchup_stats` object
Return statistics for the matchup with the specified id.
.. method:: get_matchup_results(matchup_id)
:rtype: list of :class:`~.Game_result` objects
Return the individual game results for the matchup with the specified id.
The list is in unspecified order (in particular, the colours don't
necessarily alternate, even if :attr:`~Matchup_description.alternating`
is ``True`` for the matchup).
:ref:`void games` do not appear in these results.
Matchup_description objects
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. class:: Matchup_description
A Matchup_description describes a series of games which had the same
players and settings. The information comes from the current contents of
the tournament's control file.
Matchup_descriptions are normally retrieved from
:class:`Tournament_results` objects.
Matchup_descriptions have the following attributes (which should be treated
as read-only):
.. attribute:: id
The :ref:`matchup id <matchup id>` (a string, usually 1 to 3 characters).
.. attribute:: player_1
player_2
The :ref:`player codes <player codes>` of the two players. These are
never equal.
.. attribute:: name
String describing the matchup (eg ``'xxx v yyy'``).
.. attribute:: board_size
Integer (eg ``19``).
.. attribute:: komi
Float (eg ``7.0``).
.. attribute:: alternating
Bool. If this is ``False``, :attr:`player_1` played black and
:attr:`player_2` played white; otherwise they alternated.
.. attribute:: handicap
Integer or ``None``.
.. attribute:: handicap_style
String: ``'fixed'`` or ``'free'``.
.. attribute:: move_limit
Integer or ``None``. See :ref:`playing games`.
.. attribute:: scorer
String: ``'internal'`` or ``'players'``. See :ref:`scoring`.
.. attribute:: number_of_games
Integer or ``None``. This is the number of games requested in the
control file; it may not match the number of game results that are
available.
Matchup_descriptions support the following method:
.. method:: describe_details()
:rtype: string
Return a text description of the matchup's game settings.
This covers the most important game settings which can't be observed in
the results table (board size, handicap, and komi).
Matchup_stats objects
^^^^^^^^^^^^^^^^^^^^^
.. class:: Matchup_stats
A Matchup_stats object provides basic summary information for a matchup.
The information comes from the tournament's :ref:`state file <competition
state>`.
Matchup_stats objects are normally retrieved from
:class:`Tournament_results` objects.
Matchup_stats objects have the following attributes (which should be
treated as read-only):
.. attribute:: player_1
player_2
The :ref:`player codes <player codes>` of the two players. These are
never equal.
.. attribute:: total
Integer. The number of games played in the matchup.
.. attribute:: wins_1
wins_2
Integer. The number of games won by each player.
.. attribute:: forfeits_1
forfeits_2
Integer. The number of games in which each player lost by forfeit.
.. attribute:: unknown
Integer. The number of games whose result is unknown.
.. attribute:: average_time_1
average_time_2
float or ``None``. The average CPU time taken by each player.
If CPU times are available for only some games, the average is taken
over the games for which they are available. If they aren't available
for any games, the average is given as ``None``. See :ref:`cpu time`
for notes on how CPU times are obtained.
.. attribute:: played_1b
played_2b
Integer. The number of games in which each player took Black.
.. attribute:: played_1w
played_2w
Integer. The number of games in which each player took White.
.. attribute:: alternating
Bool. This is true if each player played at least one game as Black and
at least one game as White.
This doesn't always equal the :attr:`~Matchup_description.alternating`
attribute from the corresponding :class:`Matchup_description` object (in
particular, if only one game was played in the matchup, it will always
be ``False``).
If :attr:`alternating` is ``True``, the following attributes are also
available:
.. attribute:: wins_b
Integer. The number of games in which Black won.
.. attribute:: wins_w
Integer. The number of games in which White won.
.. attribute:: wins_1b
wins_2b
Integer. The number of games in which each player won with Black.
.. attribute:: wins_1w
wins_2w
Integer. The number of games in which each player won with White.
If :attr:`alternating` is ``False``, the following attributes are also
available:
.. attribute:: colour_1
colour_2
The *colour* taken by each player.
.. currentmodule:: gomill.gtp_games
Game_result objects
^^^^^^^^^^^^^^^^^^^
.. class:: Game_result
A Game_result contains the information recorded for an individual game. The
information comes from the tournament's :ref:`state file <competition
state>`.
.. note:: If an |sgf| :ref:`game record <game records>` has been written
for the game, you can retrieve its location in the filesystem from a
:class:`!Ringmaster` object using
:samp:`ringmaster.get_sgf_pathname({game_id})`.
The :ref:`player codes <player codes>` used here are the same as the ones
in the corresponding :class:`.Matchup_description`'s
:attr:`~.Matchup_description.player_1` and
:attr:`~.Matchup_description.player_2` attributes.
See :ref:`playing games` and :ref:`details of scoring` for an explanation
of the possible game results. Games with unknown result can be
distinguished as having :attr:`winning_player` ``None`` but :attr:`is_jigo`
``False``.
Game_results can be retrieved from
:class:`.Tournament_results` objects.
Game_results have the following attributes (which should be treated as
read-only):
.. attribute:: game_id
Short string uniquely identifying the game within the tournament. See
:ref:`game id`.
.. Game_results returned via Tournament_results always have game_id set,
so documenting it that way here.
.. attribute:: players
Map *colour* → :ref:`player code <player codes>`.
.. attribute:: player_b
:ref:`player code <player codes>` of the Black player.
.. attribute:: player_w
:ref:`player code <player codes>` of the White player.
.. attribute:: winning_player
:ref:`player code <player codes>` or ``None``.
.. attribute:: losing_player
:ref:`player code <player codes>` or ``None``.
.. attribute:: winning_colour
*colour* or ``None``.
.. attribute:: losing_colour
*colour* or ``None``.
.. attribute:: is_jigo
Bool: ``True`` if the game was a :term:`jigo`.
.. attribute:: is_forfeit
Bool: ``True`` if one of the players lost the game by forfeit; see
:ref:`playing games`.
.. attribute:: sgf_result
String describing the game's result. This is in the format used for the
:term:`SGF` ``RE`` property (eg ``'B+1.5'``).
.. attribute:: detail
Additional information about the game result (string or ``None``).
This is present (not ``None``) for those game results which are not wins
on points, jigos, or wins by resignation.
.. (leaving cpu_times undocumented, as I don't want to say it's stable)
.. attribute:: cpu_times
Map :ref:`player code <player codes>` → *time*.
The time is a float representing a number of seconds, or ``None`` if
time is not available, or ``'?'`` if :gtp:`gomill-cpu_time` is
implemented but returned a failure response.
See :ref:`cpu time` for more details.
Game_results support the following method:
.. method:: describe()
:rtype: string
Return a short human-readable description of the result.
For example, ``'xxx beat yyy (W+2.5)'``.
.. currentmodule:: tournament_results
.. _using_the_api_in_scripts:
Using the API in scripts
^^^^^^^^^^^^^^^^^^^^^^^^
To write a standalone script using the tournaments results API, obtain a
:class:`.Tournament_results` object from a :class:`!Ringmaster` object as
follows::
from gomill import ringmasters
ringmaster = ringmasters.Ringmaster(control_file_pathname)
ringmaster.load_status()
tournament_results = ringmaster.tournament_results()
All of these calls report problems by raising the :exc:`!RingmasterError`
exception defined in the :mod:`!ringmasters` module.
See the :script:`find_forfeits.py` example script for a more fleshed-out
example.