An end-to-end computer vision and OCR system that turns an iPhone screenshot of a 5×5 Russian word game into ranked word suggestions.
Watch the 25-second demo · Read the technical article · Русская версия
Word Solver CV is more than an OCR notebook. It connects a real mobile workflow to an image-processing pipeline, a custom search algorithm, and a containerized server:
- An iOS Shortcut captures the game screenshot.
- A Pythonista client sends the image to the server over a bounded TCP protocol.
- OpenCV crops, cleans, sharpens, and splits the board into 25 letter cells.
- Tesseract recognizes Russian characters.
- A recursive solver prunes impossible branches using dictionary prefixes.
- Ranked results return to the iPhone through the same request connection.
The original system was built and published in 2021. This repository preserves that case study while refreshing its runtime, networking, tests, and documentation for reproducibility.
- 5×5 board with 25 individually segmented letter cells.
- 123,509 unique words in the preserved project dictionary.
- 14-second end-to-end run documented in the original article and demo environment.
- In one preserved 25-cell test, the selected legacy Tesseract engine mode recognized 100% of cells, compared with 91% for the tested LSTM configuration. This is a project-specific comparison, not a general OCR benchmark.
- The accompanying 18-minute Habr article showed 9.3K reach when rechecked in July 2026.
Python · OpenCV · Tesseract OCR · NumPy · TCP sockets · Docker · iOS Shortcuts · Pythonista
For every board position, the solver explores the eight neighboring cells. A cell cannot be reused within one candidate word. Before following a branch, it checks whether the current letter sequence is a prefix of at least one dictionary word. That prefix pruning removes paths that cannot produce a valid result.
The server loads the dictionary once, processes each image in memory, and returns a structured JSON response containing:
- the 25 recognized letters;
- words ordered by length and then alphabetically;
- total word count;
- server-side processing time.
Requirements: Docker with Compose and Python 3.10+ for the zero-dependency demo client.
git clone https://github.com/voropaevv/words_solver_cv.git
cd words_solver_cv
docker compose up --buildIn a second terminal:
python3 client_demo.pyThe container is exposed only on 127.0.0.1:50000 by default. This is intentional: the
protocol is designed for a trusted local network and does not implement authentication or
encryption.
To let an iPhone on the same trusted LAN reach the server:
WORD_SOLVER_BIND_ADDRESS=0.0.0.0 docker compose up --buildDo not expose port 50000 directly to the public internet.
Install Tesseract and create a virtual environment:
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txtThe repository includes the exact official Russian model used by the original pipeline. Point Tesseract at it and start the server:
export TESSDATA_PREFIX="$PWD"
python main.py --host 127.0.0.1 --port 50000Then run python client_demo.py in another terminal.
- Copy
client_iphone.pyandprotocol.pyinto the same Pythonista directory. - Set
SERVER_HOSTinclient_iphone.pyto the trusted LAN address of the server. - Recreate the iOS Shortcut shown below and name it
WordsSolverCV. - Start the server with
WORD_SOLVER_BIND_ADDRESS=0.0.0.0.
The Pythonista script reads the screenshot from the clipboard, sends one framed request, copies the returned words back to the clipboard, and resumes the Shortcut.
| Environment variable | Default | Purpose |
|---|---|---|
WORD_SOLVER_HOST |
127.0.0.1 |
Socket host; Compose overrides the container to 0.0.0.0 |
WORD_SOLVER_PORT |
50000 |
TCP port |
WORD_SOLVER_TIMEOUT_SECONDS |
30 |
Per-connection timeout |
WORD_SOLVER_MAX_IMAGE_BYTES |
10485760 |
Maximum accepted image frame |
WORD_SOLVER_DICTIONARY |
dictionary/words_rus.txt |
Dictionary path |
WORD_SOLVER_BIND_ADDRESS |
127.0.0.1 |
Host interface published by Docker Compose |
Equivalent server CLI flags are available through python main.py --help.
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-dev.txt
ruff check .
pytest
docker build -t words-solver-cv:test .The automated suite covers the prefix-pruned solver, board validation, framing and JSON transport, image preprocessing and 25-cell segmentation, and a structured server response. It combines deterministic OCR doubles with a real 25-cell fixture that runs against the vendored official Russian model.
main.py TCP server and in-memory request pipeline
protocol.py bounded length-prefixed transport
cv_recognition.py OpenCV preprocessing and Tesseract OCR
words_solver.py board, dictionary, and prefix-pruned search
client_demo.py zero-dependency desktop demo client
client_iphone.py Pythonista / iOS Shortcuts client
dictionary/ preserved 123,509-word project dictionary
notebooks/ original algorithm and OCR explorations
explanatory_media/ original architecture, Shortcut, and demo assets
tests/ deterministic test suite
- The crop and cell geometry target the preserved 828×1792 screenshot layout. Supporting other games or screen sizes requires calibration or board detection.
- The current server is intentionally sequential because the original use case is one personal device on a local network.
- The server now bounds frames and timeouts, but it does not authenticate clients or encrypt traffic.
- The word list reflects the rules and sources available in 2021 and still contains entries that would benefit from linguistic cleanup.
- The repository is a documented portfolio case, not an actively operated game service.
No project-wide license is declared yet. The provenance of the aggregated 2021 word list is
incomplete, so public availability must not be interpreted as permission to reuse every
asset. The vendored rus.traineddata file is an exact copy of the official
tesseract-ocr/tessdata Russian model distributed under Apache-2.0. See
THIRD_PARTY_NOTICES.md for checksums and boundaries.
Built by Vlad Voropaev.
For the implementation story, design decisions, visual preprocessing stages, and the original measured run, read the full Habr article.

