PokerBrain: как запустить Python-проект на MacBook

Есть такой тип задач, которые выглядят простыми ровно до момента, когда садишься их делать. «Протестируй на макбуке» — казалось бы, что сложного? Скачай, поставь зависимости, запусти. Но когда проект называется PokerBrain, использует Tesseract OCR, Claude Vision API, PyQt6-оверлей и нативные хоткеи macOS — всё становится интереснее.
Вот что реально произошло, когда мы попытались запустить PokerBrain на MacBook Air с M-чипом — и как в итоге всё заработало.
Что такое PokerBrain
PokerBrain — это десктопное AI-приложение для помощи в принятии решений за покерным столом. Оно работает поверх любого покерного клиента (в нашем случае WPT Global) в виде прозрачного оверлея, анализирует экран через OCR и Claude Vision, считает pot odds, equity через Monte Carlo (10 000 симуляций), учитывает ICM-давление в турнирах и профилирует оппонентов.
Стек проекта:
- Python 3.11+ — основной язык
- PyQt6 — GUI и оверлей
- Tesseract OCR — распознавание карт и чисел на экране
- Anthropic Claude API — Vision-анализ и LLM-советы
- pynput — глобальные горячие клавиши
- pytest — 207 тестов покрывают стратегический движок
Проект приватный, репозиторий на GitHub. Это сразу добавляет один уровень сложности при настройке на новой машине.
Проблема: с чего начать на чистом макбуке
Первый вопрос, который возник: как вообще получить актуальную версию кода, если нет настроенного git и доступа к приватному репо?
Классический путь — git clone — сразу отвалился. Репо приватное, на рабочем MacBook не настроен GitHub-аккаунт, и вообще непонятно, есть ли git.
Решение оказалось простым: собирать ZIP-архив прямо на сервере и раздавать его через HTTP. Это не самый элегантный CI/CD, но для быстрого тестирования работает отлично.
На сервере:
# Собираем чистый архив через git archive (без venv и мусора)
git archive --format=zip --output=/srv/pokerbrain-latest.zip HEAD
# Открываем порт файрвола и запускаем HTTP-сервер
python3 -m http.server 9090 --directory /srv/На MacBook:
cd ~/Downloads
curl -O http://[SERVER_IP]:9090/pokerbrain-latest.zip
mkdir pokerbrain && cd pokerbrain
unzip ../pokerbrain-latest.zipВажный нюанс: первая версия архива была собрана неправильно — файлы оказались вложены в папку внутри ZIP, и при распаковке структура ломалась. Пришлось переделать через git archive, который гарантирует правильную плоскую структуру без лишних вложений.
Первый барьер: версия Python и pip
Маковский Python из CommandLineTools — это 3.9, а проект требует 3.11+. Попытка поставить зависимости через системный pip3 дала ошибку:
ERROR: File "setup.py" or "setup.cfg" not found.
Directory cannot be installed in editable mode: /Users/.../pokerbrain
(A "pyproject.toml" file was found, but editable mode currently requires
a setuptools-based build.)
Две причины одновременно: старый pip не поддерживает editable install через pyproject.toml, и Python слишком старый для части зависимостей.
Решение — установить Python 3.11 через официальный .pkg-установщик с python.org, без Homebrew. Это важно: Homebrew требует выполнения дополнительных команд в .zprofile и может быть заблокирован корпоративными политиками на рабочих машинах.
# После установки Python 3.11.pkg
python3.11 --version # Python 3.11.9
# Устанавливаем зависимости правильной версией
python3.11 -m pip install -e ".[dev,gui,eval]"
# Запускаем тесты
python3.11 -m pytest tests/ -vТесты прошли: 204 из 207. Три «фейла» — это не баги, а различие окружений:
test_capture_returns_none_without_display— написан для headless Linux, на MacBook дисплей есть, capture работает (это хорошо)- Два теста на UI ожидали русский язык, а в настройках по умолчанию стоит английский
Стратегическая логика — pot odds, equity, preflop charts, ICM — всё зелёное.
Второй барьер: краш pynput на Apple Silicon
После установки запускаем приложение:
python3.11 -m pokerbrainОверлей открылся, настройки выставлены — и через несколько секунд краш. В crash report:
Process: Python [9345]
Path: /opt/homebrew/.../Python.framework/.../Python
Code Type: ARM-64 (Native)
Причина: pynput на macOS ARM вызывает TSMGetInputSourceProperty из фонового потока. Это нарушает thread safety macOS Text Services Manager и вызывает SIGTRAP на уровне C — обычный try/except это не ловит.
Первая попытка исправления — заменить pynput на QShortcut из PyQt6:
# Было (pynput - крашится на macOS ARM)
from pynput import keyboard
def on_activate_analyze():
self._trigger_analysis()
hotkey = keyboard.GlobalHotKeys({
'<cmd>+<shift>+p': on_activate_analyze
})
hotkey.start()
# Стало (QShortcut - работает в Qt event loop)
from PyQt6.QtGui import QKeySequence, QShortcut
shortcut = QShortcut(QKeySequence('Ctrl+Shift+P'), self)
shortcut.activated.connect(self._trigger_analysis)Это решило краш, но создало новую проблему: QShortcut срабатывает только когда окно оверлея в фокусе. Для покерного ассистента это бесполезно — нужны именно глобальные хоткеи, которые работают пока ты играешь в покер в другом окне.
Финальное решение — macOS native NSEvent через PyObjC:
import AppKit
def setup_global_hotkeys(self):
mask = AppKit.NSKeyDownMask
AppKit.NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(
mask,
self._handle_global_key
)
def _handle_global_key(self, event):
flags = event.modifierFlags()
key = event.charactersIgnoringModifiers()
ctrl = flags & AppKit.NSControlKeyMask
shift = flags & AppKit.NSShiftKeyMask
if ctrl and shift and key == 'p':
self._trigger_analysis()
elif ctrl and shift and key == 'h':
self._toggle_visibility()
elif ctrl and shift and key == 'q':
self._quit()NSEvent.addGlobalMonitorForEventsMatchingMask_handler_ — это официальный macOS API для глобального мониторинга клавиатуры. Он работает в main thread через RunLoop, не конфликтует с Qt event loop, и не требует никаких хаков. После этого хоткеи заработали стабильно.
Третий барьер: «FOLD — card not detected»
После решения проблем с хоткеями приложение стало запускаться без крашей. Но на любое нажатие горячей клавиши оверлей отвечал одним и тем же: FOLD — card not detected.
Это ожидаемо: в коде стоят дефолтные координаты для захвата экрана, которые не совпадают с расположением стола WPT Global на конкретном мониторе. OCR просто смотрит не туда.
Дополнительная сложность: WPT Global создаёт каждый стол в новом окне, которое может быть в любом месте экрана. Жёсткая калибровка координат не работает.
Решение — интерактивный выбор области при каждом запуске:
class RegionSelector(QWidget):
"""Полноэкранный виджет для выбора области покерного стола"""
region_selected = pyqtSignal(QRect)
def __init__(self):
super().__init__()
self.setWindowFlags(
Qt.WindowType.FramelessWindowHint |
Qt.WindowType.WindowStaysOnTopHint |
Qt.WindowType.Tool
)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setWindowOpacity(0.4)
self.showFullScreen()
self._start = None
self._end = None
def mousePressEvent(self, event):
self._start = event.pos()
def mouseMoveEvent(self, event):
self._end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self._end = event.pos()
rect = QRect(self._start, self._end).normalized()
self.region_selected.emit(rect)
self.close()
def paintEvent(self, event):
if self._start and self._end:
painter = QPainter(self)
painter.setPen(QPen(QColor(0, 255, 0), 2))
painter.drawRect(QRect(self._start, self._end).normalized())После выбора области — Claude Vision анализирует скриншот и автоматически определяет координаты hole cards, board, pot size и stack size внутри выбранного региона. Это убирает необходимость в ручной калибровке и делает приложение переносимым между разными столами и мониторами.
Как выглядит процесс тестирования теперь
После всех исправлений флоу на MacBook выглядит так:
- Скачиваем свежий ZIP с сервера:
cd ~/Downloads
rm -rf pokerbrain pokerbrain-latest.zip
curl -O http://[SERVER_IP]:9090/pokerbrain-latest.zip
mkdir pokerbrain && cd pokerbrain
unzip ../pokerbrain-latest.zip- Если зависимости ещё не стоят (первый раз):
python3.11 -m pip install -e ".[dev,gui,eval]"- Прогоняем тесты:
python3.11 -m pytest tests/ -v
# Результат: 204 passed, 3 skipped (env differences)- Запускаем приложение:
export ANTHROPIC_API_KEY="[ваш ключ]"
python3.11 -m pokerbrain- При запуске выбираем область покерного стола мышью → Claude Vision калибрует регионы → оверлей готов к работе.
При обновлении кода на сервере — просто повторяем шаг 1 (скачиваем новый ZIP). Зависимости переустанавливать не нужно, если не менялся pyproject.toml.
Pot odds и стратегический движок
Отдельно стоит сказать о том, что именно тестируется. PokerBrain — не просто OCR-обёртка над подсказками. Внутри полноценный стратегический движок.
Расчёт pot odds (strategy/postflop.py):
pot_odds = (bet_to_call / (pot + bet_to_call)) * 100Дерево решений на основе equity против pot odds (strategy/engine.py):
if equity > pot_odds * 1.5:
return Action.RAISE # Сильное математическое преимущество
elif equity > pot_odds:
return Action.CALL # Положительное ожидаемое значение (+EV)
else:
return Action.FOLD # Отрицательное ожидаемое значение (-EV)Equity считается через Monte Carlo с 10 000 симуляций — достаточно для точности, достаточно быстро для реального времени. Для турниров добавляется ICM-давление, которое корректирует рекомендации исходя из позиции в турнирной сетке.
Все эти компоненты покрыты тестами, и именно поэтому 204 passed — хороший сигнал. Можно доверять рекомендациям движка.
Выводы и уроки
Эта история про «не получается протестировать на макбуке» оказалась учебником по типичным проблемам при переносе Python-проектов между окружениями.
Первый урок: версия Python — это не детали. Разница между 3.9 и 3.11 — это не просто синтаксический сахар. Это editable installs через pyproject.toml, это match statements, это улучшенные type hints. Если проект написан под 3.11, не надо надеяться что заработает на системном Python. Всегда указывайте минимальную версию в pyproject.toml и проверяйте её первой.
Второй урок: pynput — не вариант для macOS ARM в фоновом потоке. Это известная проблема, которая не решится патчингом — только архитектурно. Либо NSEvent через PyObjC для глобальных хоткеев, либо QShortcut для локальных. Никакого pynput.GlobalHotKeys в production на macOS.
Третий урок: git archive против ручной упаковки. Когда собираешь ZIP для дистрибуции, используй git archive — он гарантирует чистый архив без venv, кэшей, временных файлов. Ручная упаковка через zip -r почти всегда захватывает лишнее и ломает структуру. Одна команда — один правильный результат.
Четвёртый урок: hardcoded координаты — антипаттерн для screen capture приложений. Любой покерный клиент открывает столы в разных местах, на разных мониторах, с разными разрешениями. Интерактивный выбор области + AI-калибровка внутри неё — это единственный подход, который работает в реальных условиях. Дополнительный бонус: пользователю не нужно читать документацию по настройке координат.
В итоге то, что казалось «просто запусти и протестируй», потребовало решения четырёх нетривиальных проблем: дистрибуция без git, совместимость версий Python, краши pynput на ARM, и динамическая калибровка OCR. Каждая из них — отдельный класс задач, с которым рано или поздно сталкивается любой, кто делает десктопные Python-приложения для macOS.
Полезные ссылки

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.