Sign in

    moonbaduk

    Go (Weiqi/Baduk) rules engine library for MoonBit: captures, ko & positional superko, area scoring, SGF (FF[4]) parse/write/replay and an embeddable GTP engine / 围棋规则引擎:提子、劫与全局同形禁止、数子法计分、SGF 棋谱与 GTP 引擎

    go
    baduk
    weiqi
    sgf
    gtp
    rules-engine
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    49

    No dependencies

    #moonbaduk

    CI

    Reusable Go (weiqi/baduk) infrastructure for the MoonBit ecosystem: a rules engine, an SGF game-record library and a GTP protocol engine — the building blocks you need when you are building a Go GUI client, a game record service, a teaching platform, an analysis tool or an engine wrapper, not the finished product itself.

    Licensed under Apache-2.0.

    #Components

    PackageWhat it providesTypical consumer
    moonbaduk (root)Board data structure, Game rules engine (Chinese rules)any tool that must judge legality, captures, ko/superko or score
    moonbaduk/sgfSGF FF[4] parser/writer, game-tree model, replay against the rules enginerecord databases, editors, converters, server-side validation
    moonbaduk/gtpText-based GTP v2 engine over the rules engine (I/O free)GUI frontends, bot harnesses, engine test suites

    #Usage

    Add the dependency to your moon.mod:

    wbgxiaosu/moonbaduk

    #Rules engine

    let g = @moonbaduk.Game::new(19, 7.5) // 19x19, Chinese rules, komi 7.5
    let r = g.play_stone(@moonbaduk.Black, 3, 3) // Ok(captured) or Err(PlayError)
    let legal = g.legal_points(@moonbaduk.White) // ko / superko / suicide excluded
    let _ = g.play_pass(@moonbaduk.White)
    let score = g.final_score() // "B+2.5" / "W+0.5" / "0" / "void"

    Key types: Board (immutable-friendly grid with flood-fill group_at), Stone, Game (turn handling, captures, simple ko, positional superko, undo, area scoring), PlayError (precise rejection reasons).

    #SGF records

    let tree = @sgf.parse_sgf_one("(;GM[1]FF[4]SZ[19]KM[7.5];B[pd];W[dp])")
    let root_size = tree.root().get("SZ") // Some("19")
    let rep = tree.replay() // replayed on a fresh Game
    // rep.moves, rep.warnings, rep.game
    let out = tree.to_sgf() // writer, roundtrip-safe

    Handles property trees with variations, escaped values (\], soft line breaks incl. CR LF), lowercase ids, AB/AW setup stones, tt passes and out-of-spec data (collected as warnings instead of aborting).

    #GTP engine

    let e = @gtp.GtpEngine::new("my-client", "1.0")
    let resp = e.handle("play black Q16") // "= \n\n" or "? reason\n\n"
    let resp2 = e.handle("genmove white") // "= D4\n\n" / "= pass\n\n"
    // resp.quit tells the host whether to shut down

    Implements protocol_version, name, version, known_command, list_commands, boardsize, clear_board, komi, play, genmove, undo, showboard, final_score, quit, with GTP-compliant =/? responses, optional numeric ids and tolerance for malformed input. The engine performs no I/O, so it can sit behind a socket, a REPL or a test harness unchanged. genmove is a deterministic reference mover — real engines replace it and talk GTP to this or any other controller.

    #Testing

    moon test — 33 blackbox tests covering rules (captures, suicide, ko/superko, scoring), SGF (parsing, escaping, roundtrips, replay) and GTP (protocol behaviour, vertex conversion, session scripts). CI runs the suite on all three backends (wasm/wasm-gc/native) via GitHub Actions.

    #Scope

    This project deliberately implements reusable infrastructure and stops at the protocol boundary. It does not ship a GUI, an AI or a server — those are applications built on top of these components.

    Board

    pub struct Board {
    size : Int
    // private fields
    }

    A square Go board. Rows and columns are 0-based with row 0 at the top and column 0 on the left (SGF convention). Board sizes 2..=26 are allowed; out-of-range sizes are clamped so the type is always safe to use.

    Board::col_of

    fn Board::col_of(self : Board, i : Int) -> Int

    Board::copy

    fn Board::copy(self : Board) -> Board

    Deep copy of the board.

    Board::get

    fn Board::get(self : Board, row : Int, col : Int) -> Stone

    Board::get_at

    fn Board::get_at(self : Board, i : Int) -> Stone

    Board::group_at

    fn Board::group_at(self : Board, i : Int) -> GroupInfo

    Flood-fills the group containing point i. Returns a group with empty arrays when the point is Empty.

    Board::idx

    fn Board::idx(self : Board, row : Int, col : Int) -> Int

    Board::in_bounds

    fn Board::in_bounds(self : Board, row : Int, col : Int) -> Bool

    Board::neighbors

    fn Board::neighbors(self : Board, i : Int) -> Array[Int]

    Orthogonal neighbors of a point (2 on a corner, 3 on a side, 4 inside).

    Board::new

    fn Board::new(size : Int) -> Board

    Board::position_key

    fn Board::position_key(self : Board) -> String

    A compact character key describing the full board position, used for positional-superko comparison. Row-major, one char per point.

    Board::render

    fn Board::render(self : Board) -> String

    ASCII rendering of the board, row 0 (top) first.

    Board::row_of

    fn Board::row_of(self : Board, i : Int) -> Int

    Board::set

    fn Board::set(self : Board, row : Int, col : Int, stone : Stone) -> Unit

    Board::set_at

    fn Board::set_at(self : Board, i : Int, stone : Stone) -> Unit

    Game

    pub struct Game {
    // private fields
    }

    A Go game under Chinese rules (area scoring, positional superko). Black moves first on an empty board; handicap games place stones with [Game::setup_stone] and start with White to play.

    Game::board

    fn Game::board(self : Game) -> Board

    A defensive copy of the current board.

    Game::captures

    fn Game::captures(self : Game) -> (Int, Int)

    Stones captured by (black, white).

    Game::consecutive_passes

    fn Game::consecutive_passes(self : Game) -> Int

    Game::final_score

    fn Game::final_score(self : Game) -> String

    Result string in GTP/SGF style: B+2.5, W+0.5 or 0 (jigo). Returns void when the game has not ended with two passes.

    Game::is_over

    fn Game::is_over(self : Game) -> Bool

    Game::ko_point

    fn Game::ko_point(self : Game) -> (Int, Int)?

    The forbidden recapture point after a ko-shaped capture, if any.

    Game::komi

    fn Game::komi(self : Game) -> Double

    Game::legal_points

    fn Game::legal_points(self : Game, player : Stone) -> Array[(Int, Int)]

    All points where player may legally play right now (ko, superko and suicide excluded). Used by GTP genmove and by test tooling.

    Game::new

    fn Game::new(size : Int, komi : Double) -> Game

    Creates a new empty game. komi is added to White's score (7.5 is the usual Chinese-rules value).

    Game::play

    fn Game::play(self : Game, row : Int, col : Int) -> Result[Int, PlayError]

    Plays a stone for the side to move.

    Game::play_pass

    fn Game::play_pass(self : Game, player : Stone) -> Unit

    The given color passes. Two consecutive passes end the game.

    Game::play_stone

    fn Game::play_stone(self : Game, player : Stone, row : Int, col : Int) -> Result[Int, PlayError]

    Plays a stone of color player at (row, col). Out-of-turn coordinates are accepted (the mover defines the color), matching GTP semantics. Returns the number of captured stones.

    Game::score_area

    fn Game::score_area(self : Game) -> (Double, Double)

    Area score (Chinese rules): stones on the board plus empty regions enclosed by a single color. Neutral points (dame) count for nobody. Returns (black, white) where white already includes komi.

    Game::set_history

    fn Game::set_history(self : Game, keys : Array[String]) -> Unit

    Replaces the superko history with the given position keys (sorted automatically). Used when loading a position from SGF/GTP where the earlier positions of the game are unknown.

    Game::set_komi

    fn Game::set_komi(self : Game, komi : Double) -> Unit

    Game::set_over

    fn Game::set_over(self : Game, over : Bool) -> Unit

    Declares (or revokes) the game-over state. Two consecutive passes end a game automatically; this is provided for adjudication tools.

    Game::setup_stone

    fn Game::setup_stone(self : Game, row : Int, col : Int, stone : Stone) -> Unit

    Places (or clears) a stone without turn handling — used for handicap stones and SGF setup properties. Resets the superko history to the resulting position. Illegal points are silently ignored.

    Game::size

    fn Game::size(self : Game) -> Int

    Game::turn

    fn Game::turn(self : Game) -> Stone

    The color to move next.

    Game::undo

    fn Game::undo(self : Game) -> Bool

    Undoes the last move or pass. Returns false when nothing to undo.

    GroupInfo

    pub struct GroupInfo {
    stones : Array[Int]
    liberties : Array[Int]
    }

    Result of a flood-fill over one connected group of same-colored stones.

    PlayError

    pub(all) enum PlayError {
    OutOfBounds
    Occupied
    Suicide
    KoViolation
    SuperkoViolation
    GameOver
    } derive(Eq,
    Debug
    )

    Why a move was rejected.

    PlayError::equal

    fn PlayError::equal(PlayError, PlayError) -> Bool

    PlayError::message

    fn PlayError::message(self : PlayError) -> String

    PlayError::not_equal

    fn PlayError::not_equal(x : PlayError, y : PlayError) -> Bool

    Snapshot

    type Snapshot

    Stone

    pub(all) enum Stone {
    Empty
    Black
    White
    } derive(Eq,
    Debug
    )

    A point on the board: empty, black stone or white stone.

    Stone::equal

    fn Stone::equal(Stone, Stone) -> Bool

    Stone::is_stone

    fn Stone::is_stone(self : Stone) -> Bool

    Stone::marker

    fn Stone::marker(self : Stone) -> String

    Short marker used in board rendering and position keys.

    Stone::not_equal

    fn Stone::not_equal(x : Stone, y : Stone) -> Bool

    Stone::opponent

    fn Stone::opponent(self : Stone) -> Stone

    The opposing color. Empty maps to Empty.

    Stone::to_repr

    Source Files