A clean, extensible Python library for communicating with Gomoku engines via the Gomocup protocol.
Project description
PyGomo Developer Cookbook 📖
PyGomo is designed to be the backbone of your Gomoku application. Whether you are building a GUI, a tournament manager, or an analysis bot, this cookbook provides the recipes you need to get things done.
🏗️ Architecture Overview
Understanding the layers will help you know where to look:
pygomo(Top Level): Exports common objects likeEngineClient,Move,SearchInfo. Start here.pygomo.board: The "Physics Engine". Handles board state, move generation (BitBoard), and Renju rules validation.pygomo.client: The High-Level API.EngineClientlives here.pygomo.protocol: The Translator. Converts Python objects to/from Gomocup protocol strings.pygomo.transport: The Plumber. Handles subprocess creation and non-blocking I/O.
🍳 Recipe 1: The "Hello World" Connection
The simplest way to connect to an engine. Always use the with statement to ensure the subprocess is killed clean.
from pygomo import EngineClient
# Engine path can be absolute or relative
with EngineClient("./engines/piskvork") as engine:
# 1. Start a game (default 15x15)
engine.start(board_size=15)
# 2. Make a move (Human plays h8)
result = engine.turn("h8")
# 3. Print result
print(f"Engine replied: {result.move}")
🍳 Recipe 2: Configuring The Engine
Before starting the game, you often need to set limits. Note that start() resets some engine internals, so configure after start() usually (depending on engine quirk, but safely: connect -> start -> configure).
with EngineClient("./engines/rapfi") as engine:
engine.start(15)
# Setup Time Control
engine.set_time(
turn_time_ms=5000, # 5 seconds per move
match_time_ms=300000, # 5 minutes total
time_left_ms=300000 # Current remaining time
)
# Setup Rules (1 = Standard, 4 = Renju)
engine.set_rule(1)
# Advanced Config (Thread, Hash, etc.)
engine.set_threads(4)
engine.set_memory(1024 * 1024 * 1024) # 1GB
🍳 Recipe 3: Dealing with Realtime Search Info
Modern engines (like Rapfi, Yixin) provide a stream of search data while thinking. You can tap into this via the on_info callback.
from pygomo import SearchInfo
def my_info_handler(info: SearchInfo):
# This is called repeatedly while engine thinks!
if info.depth > 0:
pv_string = " ".join(str(m) for m in info.pv[:5])
print(f"\rDepth: {info.depth} | Eval: {info.eval.raw_value} | PV: {pv_string}", end="")
# Pass the handler to any thinking command (turn, begin, board, nbest)
result = engine.turn("h8", on_info=my_info_handler)
# The final 'result' also contains the LAST search info received
print(f"\nFinal Winrate: {result.search_info.winrate_percent:.2f}%")
Understanding SearchInfo fields:
depth/sel_depth: Search depth.eval:Evaluateobject. Use.raw_valuefor engine score,.score()for int,.winrate_percent()for 0-100%.pv: List ofMoveobjects (Principal Variation / Best Line).nodes/nps: Performance metrics.
🍳 Recipe 4: Managing Board State with BitBoard
Don't track 2D arrays yourself. Use pygomo.board for high-performance state management.
from pygomo.board import BitBoard, RenjuBitBoard
# Use RenjuBitBoard for complex Renju rules (double-3, double-4, overline)
board = RenjuBitBoard(size=15)
# 1. Place moves
board.place("h8") # Black
board.place("h9") # White
# 2. Check for Win/Forbidden
last_move = board.get_last_move()
win_info = board.check_win(last_move)
if win_info:
print(f"Winner: {win_info.winner}") # 1=Black, 2=White
print(f"Line: {win_info.direction}") # e.g. [(7,7), (8,8)...]
# 3. Check Forbidden (Renju Only)
if board.is_forbidden(move="j9", color=1): # Is j9 forbidden for Black?
print("Foul!")
# 4. Undo
board.undo() # Takes back h9
board.undo() # Takes back h8
🍳 Recipe 5: Synchronizing Engine with Custom Positions
If you implement Undo, Redo, or Load Game, you must sync the engine. The engine has its own internal board.
# ... user clicks undo ...
my_local_board.undo()
# SYNC RECIPE:
# 1. Convert local board to protocol objects
protocol_position = my_local_board.to_position()
# 2. Send BOARD command
# start_thinking=False means "just set the board, don't move yet"
engine.board(protocol_position, start_thinking=False)
🍳 Recipe 6: Parsing User Input Handling
Users type garbage. Move handles it.
from pygomo import Move
try:
m1 = Move("h8") # Standard algebraic
m2 = Move("H8") # Case insensitive
m3 = Move("7,7") # Numeric (0-indexed)
m4 = Move((7, 7)) # Tuple
print(m1.col, m1.row) # 7, 7
except ValueError:
print("Invalid move format")
🍳 Recipe 7: Building a Non-Blocking GUI Loop
If you use asyncio or a GUI event loop (PyQt/Tkinter), you don't want engine.turn() to freeze the UI. PyGomo is synchronous by default (for simplicity), but you can wrap it easily.
Pattern for Threading (Simplest):
import threading
def worker():
# This blocks, but it's in a thread
result = engine.turn("h8", on_info=update_gui_progress)
# Post result to GUI thread
gui.post_event(GameMoveEvent(result.move))
thread = threading.Thread(target=worker)
thread.start()
🍳 Recipe 8: Custom Commands
Need to send a command PyGomo doesn't support explicitly?
# Send raw string (fire and forget)
engine.send_raw("MY_CUSTOM_COMMAND args")
# Execute and want response (handles timeout/locking)
# This returns a CommandResult object
result = engine.execute("YXSHOWINFO")
print(result.data)
🛠️ Developer Checklist
When implementing a full game:
- Always handle specific Exceptions: Engines crash. Catch
RuntimeErroror timeouts on turn execution. - Hash Synchronization: If your board supports Zobrist hashing (
board.hash), check it against the engine occasionally (Rapfi supportschecksumin some debug modes) to detect desyncs. - Clean Shutdown: Ensure
engine.quit()is called. Orphaned engine processes consume CPU. - Protocol Logging: For debugging, you can hook into the transport or use
run_game.shwhich prints what it's doing.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygomo_lib-0.1.0.tar.gz.
File metadata
- Download URL: pygomo_lib-0.1.0.tar.gz
- Upload date:
- Size: 48.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ba23b236065f216cd0f97d1f0ebd67ffa2b61786de304dcef130e90e4aceab5
|
|
| MD5 |
728448a61ee9f0dcf63b3f0e1cac96a6
|
|
| BLAKE2b-256 |
e2b7ba10a5e30811a4880d027d9b1d4f75656f49de623c41e4fffcfa71650030
|
File details
Details for the file pygomo_lib-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pygomo_lib-0.1.0-py3-none-any.whl
- Upload date:
- Size: 51.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de986fad60d6846176e5e9b28820efd1fbc72e0bbc89c9fe81056dd9dee1c0f5
|
|
| MD5 |
7d7fcb11120e16b7a0d1730844761bd6
|
|
| BLAKE2b-256 |
672797d1d135dddb56ac963dc68ebb405d6b34ca0aa371fb0559b09d92795e97
|