Skip to content

rooklift/sgf

Repository files navigation

Golang library for manipulation of SGF trees (i.e. Go / Weiqi / Baduk kifu). Some auto-generated documentation is available for the various methods, etc, at GoDoc.

Technical notes

  • A tree is just a bunch of nodes connected together.
  • Nodes have 0 or more keys. Keys have 1 or more values. Keys and values are always strings.
  • Nodes also have a parent node (possibly nil), and 0 or more child nodes.
  • Boards are generated only as needed, and cached.
  • Nodes are generally created by playing a move at an existing node.
  • Functions that want a point expect it to be an SGF-string e.g. "dd" is the top-left hoshi.
  • Such strings can be produced with sgf.Point(3,3) - the numbers are zeroth based.
  • Escaping of ] and \ characters is handled invisibly to the user at file load/save.

Note to future self

  • If a board cache becomes invalid, internally we must call clear_board_cache_recursive().
  • For weird encodings (e.g. not utf-8), some potential problems if a character contains a ] or \ byte.

Projects using the library

  • gtp - a helper for GTP bots
  • twogtp - a match runner for GTP bots

Example

package main

import (
    "fmt"
    "github.com/rooklift/sgf"
)

func main() {

    // Start a new game tree and get the root node...

    root := sgf.NewTree(19)
    node := root

    // Here we create the ancient Chinese pattern...

    node.AddValue("AB", sgf.Point(3, 3))
    node.AddValue("AB", sgf.Point(15, 15))
    node.AddValue("AW", sgf.Point(15, 3))
    node.AddValue("AW", sgf.Point(3, 15))

    // The normal way to create new nodes is by playing moves.
    // If successful, Play() returns the new node.

    node, err := node.Play("cf")            // "cf" is SGF-speak
    fmt.Printf("%v\n", err)                 // Prints nil (no error)

    // We can get an SGF coordinate (e.g. "cf") by calling Point().
    // Note that the coordinate system is zeroth-based, from the top left.

    node, err = node.Play(sgf.Point(2, 5))
    fmt.Printf("%v\n", err)                 // Already filled

    // Illegal moves (including suicide and basic ko) will return an error.
    // As a convenience, Play() returns the original node in this case.
    // You may still wish to check for errors...

    node, err = node.Play(sgf.Point(19, 19))
    fmt.Printf("%v\n", err)                 // Off-board

    // We can create variations from any node.

    node = node.Parent()
    node.Play(sgf.Point(13, 2))             // Create variation 1
    node.Play(sgf.Point(16, 5))             // Create variation 2

    // Colours are determined intelligently, but we can always force a colour.

    node.PlayColour(sgf.Point(2, 5), sgf.WHITE)     // Create variation 3

    // We can iterate through a node's children.

    for i, child := range node.Children() {
        child.SetValue("C", fmt.Sprintf("Comment %d", i))
    }

    // And we can go down those variations if we wish.
    // (Errors ignored here for simplicity.)

    node, _ = node.Play(sgf.Point(5, 16))   // Create variation 4 and go down it
    node, _ = node.Play(sgf.Point(2, 12))   // ...continue going down it
    node, _ = node.Play(sgf.Point(3, 17))   // ...continue going down it

    // Passes are a thing.
    // Doing the same action on the same node many times just returns the first-created child each time.

    foo := node.Pass()
    bar := node.Pass()                      // Does not create a new node
    node = node.Pass()                      // Does not create a new node

    fmt.Printf("%v, %v\n", foo == bar, bar == node)     // true, true

    // We can directly manipulate SGF properties...
    // We can also examine the board.

    node.SetValue("C", "White passed. Lets highlight all white stones for some reason...")

    board := node.Board()                   // Note that this is a deep copy

    for x := 0; x < board.Size; x++ {
        for y := 0; y < board.Size; y++ {
            if board.State[x][y] == sgf.WHITE {
                node.AddValue("TR", sgf.Point(x, y))
            }
        }
    }

    // It is also possible to directly manage node creation and properties,
    // though this has no legality checks...

    node = sgf.NewNode(node)                // Specify the parent
    node.AddValue("B", "dj")

    // It is possible to edit board-altering properties even if a node has
    // children. All cached boards in descendent nodes will be cleared, and
    // remade as needed.

    root.AddValue("AB", "jj")               // Editing the root.
    node, err = node.Play("jj")             // Trying to play at the current node.
    fmt.Printf("%v\n", err)                 // Prints the err: jj is not empty.

    // We can adjust the tree so this branch we are on is the main line...

    node.MakeMainLine()

    // Calling SGF() will return the SGF of the tree as a string.

    fmt.Println(node.SGF())

    // Calling Save() will save the entire tree, regardless of node position.

    node.Save("foo.sgf")

    // We can also load files.

    node, err = sgf.Load("foo.sgf")
}

About

Library for SGF manipulation in Golang

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy