pygo/gomill/docs/sgf.rst

915 lines
31 KiB
ReStructuredText
Raw Normal View History

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.