/goban_irl

A package to enable playing online from a physical go board.

Primary LanguagePythonGNU General Public License v3.0GPL-3.0

About

The purpose of this package is to be able to play go online from a physical go board at home.

./physical_virtual_demo.gif

Unlike the many other repositories which can read go board images, this one is really dumb and really fast. See the developer instructions below for how to modify the code to suit your needs.

This software can also be used to automatically interface between multiple virtual boards: ./ui_example.gif

Features

  • A `Board()` object in board.py which takes in an image and corners of the board on that image and stores what stones are on the board and the positions of the intersections on the board subimage.
  • A ui.py script which helps you load in two boards and plays stones with pyautogui on first as they change on the second.

Maintenance warning

Unfortunately I’m not in a place where I can take suggestions for features or review pull requests. However, the code here should be pretty readable, even for a novice Python programmer. If you have some functionality you’d like to see implemented or are fighting with a bug here, definitely feel free to fork this code. Also, if you message me directly with questions I’ll try to get back to you.

Developer instructions

Install

I enjoy using miniforge for managing Python environments. You can install it from that link, and then create a virtual environment as follows:

conda create -n goban_irl --file requirements.txt

After you conda activate goban_irl, you can install the package locally in editable mode with

python -m pip install -e .

Code Map

The key functionality of this code can be found in the Board class in board.py. The Board class expects two arguments:

  1. an image which can either be an OpenCV image or a path
  2. corners, which should be a list of either two or four (x,y) pixel locations in that image.

The corners are used to create a board_subimage, a rectangle whose corners are exactly the playable corners of the go board. From there, the rest is easy

  • The intersections are just 19x19 evenly spaced points in that board_subimage
  • The stone_subimage_boundary is the rectangle around each possible stone on that board
  • The detection_function takes in a stone subimage and returns a number. If the number is low we’ll call the stone black, high we’ll call it white, and anything in between should be the empty board. The cutoffs what we call black and white stones are named cutoffs

So, to use this code, you just need to repeatedly load a Board with your image as it changes. A simple user interface which compares a physical and virtual board would be as follows:

from goban_irl.board import Board
from goban_irl.helpers import get_snapshot
while True:
    board_1 = Board(image=get_snapshot('virtual'), corners=[(0, 0), (800, 800)])
    board_2 = Board(image=get_snapshot('physical'), corners=[(1437, 679), (2364, 679), (1437, 1617), (2364, 1617)])
    print(board_1.compare_to(board_2))

A more complex example of this can be found in ui.py which basically does the same thing but with some interactive options.

Stuff you might want to modify

While I run the code through python ui.py, I tolerate its many deficiencies because I wrote it. Instead of using that script directly, I would recommend modifying it to suit your needs. Here are what I expect your pain points to be:

  • The user flow as written expects the user to provide the corners of the board. Detecting those corners automatically should not be hard, particularly for virtual boards.
  • The stone detection function is very lazy and doesn’t take into account ambient light. This means when I play at different times of day, I usually need to re-calibrate the physical board. Different detection functions can be passed as input into the Board class, so it should be easy to write and insert your own better function.

Some useful things

The main important design decision here is to not care about the rules of Go beyond where stones should be placed.

I’m also very happy that this only looks at screenshots of computer Go boards. While it would have been possible to interface with the API for various different projects, it’s much easier to just look at a picture of the board and know what the color cutoffs for the stones should be. As a result, this code works with Sabaki, OGS, KGS, and probably any other virtual go resource.

I’m not thrilled with the decision here to use OpenCV. It’s much heavier as an import than what is needed here, and it’s not pleasant to use as a Python package. I couldn’t find an easy way to not use it, but I’d be happy to take any recommendations for a way to replace it. Notably, version 4.5.3 was entirely broken for me hence the pinned version at 4.5.2.