Skip to content

Commit 75310aa

Browse files
chore: core 공유 타입 정의·계약 테스트·CI 구축
1 parent 97d56b6 commit 75310aa

27 files changed

Lines changed: 1524 additions & 14 deletions

.github/pull_request_template.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## 제목 형식
2+
`[영역] 설명`
3+
> 영역 예: 요트-비전, 요트-FSM, 늑대-비전, 늑대-FSM, 코어, 브릿지, 문서
4+
5+
---
6+
7+
## 작업 요약
8+
<!-- 3~5줄로 무엇을 왜 했는지 설명 -->
9+
10+
---
11+
12+
## 변경 영역
13+
- [ ] core/
14+
- [ ] bridge/
15+
- [ ] vision/
16+
- [ ] games/
17+
- [ ] audio/
18+
- [ ] backend/
19+
- [ ] frontend/
20+
- [ ] tests/
21+
- [ ] docs/
22+
- [ ] CI / 설정
23+
24+
---
25+
26+
## 테스트
27+
- [ ] 로컬에서 직접 실행해 동작 확인
28+
- [ ] 관련 단위 테스트 추가 또는 업데이트
29+
30+
---
31+
32+
## 체크리스트
33+
- [ ] `ruff check` 통과
34+
- [ ] `black --check` 통과
35+
- [ ] 기존 계약 테스트(`pytest tests/test_contracts.py`) 통과
36+
- [ ] `core/` 수정 시 계약 테스트도 함께 업데이트

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Python 3.11
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.11"
20+
21+
- name: Install dependencies
22+
working-directory: boardgame-ai
23+
run: pip install -e ".[dev]"
24+
25+
- name: Lint (ruff)
26+
working-directory: boardgame-ai
27+
run: ruff check core/ bridge/ tests/
28+
29+
- name: Format check (black)
30+
working-directory: boardgame-ai
31+
run: black --check core/ bridge/ tests/
32+
33+
- name: Type check (mypy)
34+
working-directory: boardgame-ai
35+
run: mypy core/ bridge/
36+
37+
- name: Contract tests
38+
working-directory: boardgame-ai
39+
run: pytest tests/test_contracts.py -v

README.md

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
## 📁 디렉토리 구조
44

55
```text
6-
boardmaster-ai/
6+
boardgame-ai/
77
├── app.py # 전체 모듈을 조립하고 실행하는 진입점
88
├── requirements.txt # Python 의존성 목록
9+
├── pyproject.toml # 패키지 설정 및 개발 도구 설정
910
├── .gitignore # Git 추적 제외 파일 목록
1011
11-
├── 📁 core/ # 게임 공통 모델과 이벤트, 식별 로직
12-
│ ├── models.py # Player, Session 등 공통 데이터 모델
13-
│ ├── events.py # GameEvent, FusionContext 등 공통 이벤트 스키마
14-
│ ├── player_identifier.py# zone + handedness 기반 플레이어 식별
15-
│ ├── seat_registration.py# 좌석 등록 및 제스처 확인 로직
16-
│ └── pointing_resolver.py# 검지 방향과 앵커를 매칭하는 로직
12+
├── 📁 core/ # 공유 타입·상수 (순수 Python, 외부 라이브러리 금지)
13+
│ ├── constants.py # MsgType, CommonPhase, CommonEventType, DEFAULT_PARAMS 등
14+
│ ├── models.py # Player, SeatZone 공통 데이터 모델
15+
│ ├── events.py # GameEvent, FusionContext 이벤트 스키마
16+
│ ├── envelope.py # WSMessage 공통 메시지 봉투
17+
│ ├── audio.py # TTSRequest, AudioType, AudioPriority
18+
│ └── player_manager.py # 플레이어 CRUD + 좌석 등록 공통 로직
1719
1820
├── 📁 vision/ # 객체·제스처 인식을 수행하는 비전 파이프라인
1921
│ ├── pipeline.py # 전체 비전 파이프라인 조립
@@ -27,10 +29,10 @@ boardmaster-ai/
2729
│ ├── yacht/ # 요트다이스 전용 FSM/상태/점수 로직
2830
│ └── werewolf/ # 늑대인간 전용 FSM/상태/판정 로직
2931
30-
├── 📁 bridge/ # 비전과 게임 로직을 연결하는 인터페이스 계층
31-
│ ├── interface.py # 공통 브리지 인터페이스 정의
32-
│ ├── local_bridge.py # 로컬 실행용 직접 호출 브리지
33-
│ └── websocket_bridge.py # 분리 배포용 WebSocket 브리지
32+
├── 📁 bridge/ # 비전 ↔ FSM 통신 인터페이스 계층
33+
│ ├── interface.py # 추상 Bridge 인터페이스 정의
34+
│ ├── local_bridge.py # 인프로세스 직접 연결 (개발/테스트용)
35+
│ └── websocket_bridge.py # WebSocket 브리지 (Phase 1 후반 구현 예정)
3436
3537
├── 📁 audio/ # TTS, 효과음, BGM 등 오디오 관리
3638
│ ├── manager.py # 오디오 재생 큐와 인터럽트 관리
@@ -49,9 +51,8 @@ boardmaster-ai/
4951
│ ├── package.json # 프론트엔드 의존성 및 스크립트 설정
5052
│ └── vite.config.js # Vite 빌드 설정
5153
52-
├── 📁 weights/ # 학습된 모델 가중치 파일 저장
53-
│ ├── yacht_best.pt # 요트다이스 객체 인식 가중치
54-
│ └── werewolf_best.pt # 늑대인간 객체 인식 가중치
54+
├── 📁 weights/ # 학습된 모델 가중치 (Git 제외, Google Drive 공유)
55+
│ └── README.md # 명명 규칙 및 관리 방법
5556
5657
├── 📁 training/ # 객체 인식 모델 학습 코드와 설정 파일
5758
│ ├── yacht/ # 요트다이스 학습 데이터 설정
@@ -64,13 +65,17 @@ boardmaster-ai/
6465
│ └── dice_prototype_v2.py# 주사위 인식 프로토타입 코드
6566
6667
├── 📁 tests/ # 게임 로직과 규칙 검증용 테스트 코드
68+
│ ├── test_contracts.py # core/ 타입 직렬화·구조 계약 테스트 (CI 필수)
6769
│ ├── test_yacht_fsm.py # 요트다이스 FSM 테스트
6870
│ ├── test_werewolf_fsm.py# 늑대인간 FSM 테스트
6971
│ ├── test_scoring.py # 점수 계산 테스트
7072
│ └── test_fusion_rules.py# 퓨전 규칙 테스트
7173
7274
└── 📁 docs/ # 설계 문서와 통신/데이터 흐름 명세
75+
├── phase1_spec.md # 비전↔FSM 인터페이스 계약서 (Phase 1)
76+
├── team_workflow.md # 브랜치·PR·CI·가중치 관리 규칙
7377
├── FSM_설계.md # FSM 설계 문서
7478
├── WebSocket_명세.md # WebSocket 메시지 명세
7579
├── 데이터흐름_명세.md # 시스템 데이터 흐름 문서
7680
└── diagrams/ # 아키텍처 및 설계 다이어그램
81+
```

boardgame-ai/.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,19 @@ audio/assets/tts_cache/*.wav
5050
training/*/images/
5151
training/*/labels/
5252
training/runs/
53+
54+
# 개발 도구 캐시
55+
.pytest_cache/
56+
.mypy_cache/
57+
.ruff_cache/
58+
htmlcov/
59+
.coverage
60+
61+
# 모델 가중치
62+
weights/*.onnx
63+
weights/*.engine
64+
65+
# 학습 데이터
66+
training/**/datasets/
67+
training/**/runs/
68+
training/**/*.zip

boardgame-ai/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# BoardGame AI
2+
3+
오버헤드 카메라로 테이블을 비춰 물리 보드게임(요트다이스, 한밤의 늑대인간)을 AI가 자동 진행하는 시스템.
4+
5+
## 빠른 시작
6+
7+
```bash
8+
cd boardgame-ai
9+
pip install -e ".[dev]"
10+
python -c "from core import GameEvent, FusionContext, PlayerManager; print('OK')"
11+
pytest tests/test_contracts.py -v
12+
```
13+
14+
## 팀 구성
15+
16+
| 이름 | 담당 |
17+
|--------|----------------|
18+
| 김성민 | 요트 비전 |
19+
| 강병진 | 요트 FSM |
20+
| 양승경 | 늑대 비전 |
21+
| 유형승 | 늑대 FSM |
22+
23+
## 앱 흐름
24+
25+
`PLAYER_SETUP` → (좌석 등록) → `GAME_SELECT``게임 FSM``게임 종료`
26+
종료 후 3선택지: 플레이어 변경 / 게임 변경 / 재시작
27+
28+
## 레포 구조
29+
30+
```
31+
boardgame-ai/
32+
├── core/ 공유 타입 & 상수 (순수 Python, 외부 라이브러리 금지)
33+
├── bridge/ 비전↔FSM 통신 인터페이스
34+
├── vision/ 비전 파이프라인 (YOLO, MediaPipe, ByteTrack)
35+
├── games/ 게임별 FSM
36+
├── audio/ TTS/오디오 모듈
37+
├── backend/ FastAPI 백엔드
38+
├── frontend/ 태블릿 UI (React)
39+
├── tests/ 계약 테스트
40+
├── docs/ 설계 문서
41+
├── weights/ 모델 가중치 (Git 제외, Drive 공유)
42+
└── training/ 학습 스크립트
43+
```
44+
45+
## 핵심 원칙
46+
47+
1. `core/`는 순수 Python만 — numpy, cv2, torch 등 외부 라이브러리 import 금지
48+
2. `core/`는 게임을 몰라야 함 — 요트·늑대 전용 로직을 core에 넣지 않음
49+
3. 좌표는 정규화 float64 (0.0 ~ 1.0)
50+
4. ID는 전부 문자열
51+
5. `state_version`: FSM 전이마다 +1, 불일치 메시지 drop
52+
6. FSM은 Fusion 통과한 `GameEvent`만 수신 — raw 프레임 수신 금지
53+
54+
## 주요 문서
55+
56+
- [Phase 1 인터페이스 계약서](docs/phase1_spec.md)
57+
- [팀 워크플로우](docs/team_workflow.md)

boardgame-ai/bridge/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# bridge/
2+
3+
비전 파이프라인 ↔ FSM 통신 인터페이스.
4+
5+
- **`LocalBridge`** (현재 사용): 인프로세스 직접 연결. 개발·테스트용.
6+
- **`WebSocketBridge`** (Phase 1 후반): 태블릿 UI 연동용 WebSocket 브릿지. 미구현.
7+
8+
## 사용 예시
9+
10+
```python
11+
from bridge import LocalBridge
12+
13+
bridge = LocalBridge()
14+
bridge.on_game_event(lambda event, ver: print(event, ver))
15+
bridge.start()
16+
17+
# 비전 파이프라인에서
18+
bridge.send_game_event(event, state_version=1)
19+
20+
bridge.stop()
21+
```
22+
23+
`LocalBridge``WebSocketBridge`는 동일한 `Bridge` 인터페이스를 구현하므로
24+
FSM/비전 코드 변경 없이 교체 가능하다.

boardgame-ai/bridge/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from bridge.interface import Bridge
2+
from bridge.local_bridge import LocalBridge
3+
4+
__all__ = ["Bridge", "LocalBridge"]

boardgame-ai/bridge/interface.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""비전 ↔ FSM 통신 추상 인터페이스."""
2+
3+
from __future__ import annotations
4+
5+
from abc import ABC, abstractmethod
6+
from collections.abc import Callable
7+
8+
from core.events import FusionContext, GameEvent
9+
10+
11+
class Bridge(ABC):
12+
@abstractmethod
13+
def send_game_event(self, event: GameEvent, state_version: int) -> None: ...
14+
15+
@abstractmethod
16+
def send_fusion_context(self, context: FusionContext, state_version: int) -> None: ...
17+
18+
@abstractmethod
19+
def on_game_event(self, handler: Callable[[GameEvent, int], None]) -> None: ...
20+
21+
@abstractmethod
22+
def on_fusion_context(self, handler: Callable[[FusionContext, int], None]) -> None: ...
23+
24+
@abstractmethod
25+
def start(self) -> None: ...
26+
27+
@abstractmethod
28+
def stop(self) -> None: ...
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""인프로세스 브릿지 구현. 개발/테스트용."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Callable
6+
7+
from bridge.interface import Bridge
8+
from core.events import FusionContext, GameEvent
9+
10+
11+
class LocalBridge(Bridge):
12+
"""같은 프로세스 내에서 비전과 FSM을 직접 연결하는 브릿지.
13+
14+
핸들러 리스트를 유지하고 send 시 등록된 모든 핸들러를 순차 호출한다.
15+
"""
16+
17+
def __init__(self) -> None:
18+
self._game_event_handlers: list[Callable[[GameEvent, int], None]] = []
19+
self._fusion_context_handlers: list[Callable[[FusionContext, int], None]] = []
20+
self._running = False
21+
22+
def send_game_event(self, event: GameEvent, state_version: int) -> None:
23+
for handler in self._game_event_handlers:
24+
handler(event, state_version)
25+
26+
def send_fusion_context(self, context: FusionContext, state_version: int) -> None:
27+
for handler in self._fusion_context_handlers:
28+
handler(context, state_version)
29+
30+
def on_game_event(self, handler: Callable[[GameEvent, int], None]) -> None:
31+
self._game_event_handlers.append(handler)
32+
33+
def on_fusion_context(self, handler: Callable[[FusionContext, int], None]) -> None:
34+
self._fusion_context_handlers.append(handler)
35+
36+
def start(self) -> None:
37+
self._running = True
38+
39+
def stop(self) -> None:
40+
self._running = False
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""WebSocket 브릿지. Phase 1 후반 구현 예정.
2+
3+
태블릿 UI와 연동하는 WebSocket 기반 브릿지.
4+
LocalBridge와 동일한 Bridge 인터페이스를 구현하므로 FSM/비전 코드 변경 없이 교체 가능.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from collections.abc import Callable
10+
11+
from bridge.interface import Bridge
12+
from core.events import FusionContext, GameEvent
13+
14+
15+
class WebSocketBridge(Bridge):
16+
"""WebSocket 기반 비전 ↔ FSM 브릿지. Phase 1 후반 구현 예정."""
17+
18+
def __init__(self, host: str = "localhost", port: int = 8765) -> None:
19+
raise NotImplementedError(
20+
"WebSocketBridge is not yet implemented. " "Use LocalBridge for Phase 0/1 development."
21+
)
22+
23+
def send_game_event(self, event: GameEvent, state_version: int) -> None:
24+
raise NotImplementedError
25+
26+
def send_fusion_context(self, context: FusionContext, state_version: int) -> None:
27+
raise NotImplementedError
28+
29+
def on_game_event(self, handler: Callable[[GameEvent, int], None]) -> None:
30+
raise NotImplementedError
31+
32+
def on_fusion_context(self, handler: Callable[[FusionContext, int], None]) -> None:
33+
raise NotImplementedError
34+
35+
def start(self) -> None:
36+
raise NotImplementedError
37+
38+
def stop(self) -> None:
39+
raise NotImplementedError

0 commit comments

Comments
 (0)