The purpose of this package is to be able to play go online from a physical go board at home.
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:
- 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.
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.
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 .
The key functionality of this code can be found in the Board
class in board.py. The Board
class expects two arguments:
- an
image
which can either be an OpenCV image or a path 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 thatboard_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 namedcutoffs
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.
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.
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
.