주사위 4개로 미식축구를 재현하다 — 보드게임 Playbook Football 이야기
요즘 사이드 프로젝트로 미식축구 보드게임을 Flutter 앱으로 옮기는 작업을 하고 있다. 코드를 짜다 보니 문득 이런 생각이 들었다. 앱 이야기를 하기 전에, 원작 보드게임 자체가 얼마나 잘 설계됐는지를 먼저 이야기해야겠다고. 주사위 4개와 카드 몇 장으로 미식축구 한 경기를 통째로 재현하는 시스템인데, 뜯어볼수록 감탄하게 되는 구조라서다.
이 글에서는 원작 보드게임 Playbook Football을 소개하고, 게임의 심장인 주사위 4개 판정 시스템이 어떻게 돌아가는지 정리한다. 미식축구를 몰라도 읽을 수 있게 썼다. 오히려 "규칙 기반 시뮬레이션을 데이터 테이블로 설계하는 법"이라는 관점에서 개발자에게 더 재미있을 수도 있다.
Playbook Football이라는 게임
Playbook Football은 Kevin Barrett이 디자인하고 Bucephalus Games에서 나온 2인용 보드게임이다. 표지에 적힌 소개 문구가 "A hard-hitting game for two players". 미식축구의 정규 리그(NFL) 표준 규칙을 따르는 것을 전제로 하고, 룰북에서 다루지 못한 상황은 실제 미식축구 규칙을 참고해서 해결하라고 안내할 정도로 원 스포츠의 재현에 진심인 게임이다.
내가 참고한 자료는 한국어 팬 번역판 룰북이다. 번역자가 서문에 "한국에서는 다소 생소한 스포츠이자 보드게임이지만, 이번 기회에 미식축구에 대해 알게 되어 새로운 스포츠에 대한 매력을 느끼게 되었다"라고 적어놨는데, 나도 정확히 같은 경로를 밟았다. 게임을 구현하려고 룰북을 읽다가 미식축구에 입문하게 된 케이스다.
게임의 구성물은 단출하다:
| 구성물 | 역할 |
|---|---|
| 필드 보드 | 미식축구 경기장. 자석으로 붙는 두 판 |
| 볼 마커 | 공의 현재 위치 표시 |
| 야드 표시기 | "여기까지 10야드 전진하면 퍼스트 다운" 목표선 |
| Down/Play/Quarter 카운터 | 다운(1 |
| 공격 카드 9장 | 패스 4종 + 러닝 5종. 뒷면에 결과 차트 |
| 수비 카드 9장 | 수비 대형. 공격 플레이별 보정치 목록 |
| 스페셜팀/공용 카드 | 킥오프, 펀트, 필드골, 턴오버, 펌블 등 |
| 주사위 | 팀당 10면체 1개 + 12면체 1개 (총 4개) |
미식축구를 처음 접하는 분을 위해 핵심 규칙만 요약하면: 공격팀은 4번의 시도(다운) 안에 10야드를 전진해야 한다. 성공하면 다시 4번의 기회를 얻고, 실패하면 공격권이 상대에게 넘어간다. 이걸 반복하며 상대 진영 끝(엔드존)까지 공을 옮기면 터치다운(6점)이다.
게임의 한 턴이 흘러가는 방식
한 번의 플레이는 이렇게 진행된다. 룰북의 진행 순서를 그대로 옮겼다.
- 공격자와 수비자가 각자 카드 한 장을 뒷면이 보이게 내려놓는다
- 양쪽이 각자 주사위 세트(10면체 1개 + 12면체 1개)를 굴린다
- 플레이 마커를 한 칸 진행한다 (쿼터당 16플레이)
- 패널티 체크: 양팀 10면체의 합이 6 또는 16이면 반칙 발생
- 카드를 공개하고, 공격자의 10면체 값에 수비 카드의 보정치를 적용해 차트의 행(Row) 을 확인한다
- 양팀 12면체의 합으로 차트의 열(Column) 을 확인한다
- 행과 열의 교차점에 있는 숫자만큼 공을 이동한다
- 다운 카운터를 한 칸 진행한다
포인트는 1번이다. 카드를 서로 몰래 내기 때문에 이 게임은 절반이 심리전이다. "상대가 롱패스를 준비하는 것 같으니 패스 수비 대형을 깔자" 같은 수 읽기가 주사위 운만큼 중요하다.
주사위 4개, 각자 하는 일이 다르다
이 게임을 처음 하면 "주사위를 왜 4개나 굴리지?"라는 의문이 든다. 나도 앱에 주사위 애니메이션을 넣고 나서 지인에게 "이 숫자 4개가 뭐냐"는 질문을 받았다. 정리하면 이렇다.
공격 D10 (빨강) — 차트의 행을 결정
공격자가 굴리는 10면체. 이 값이 공격 카드 뒷면 차트의 행(세로 위치) 을 정한다. 차트의 행은 5개 구간(9-10 / 7-8 / 5-6 / 3-4 / 1-2)으로 나뉘어 있고, 높은 행일수록 좋은 결과가 몰려 있다.
중요한 건 이 주사위가 수비 카드의 보정을 받는다는 점이다. 각 수비 카드에는 공격 플레이 9종 각각에 대한 보정치(-3 ~ +3)가 적혀 있다. 예를 들어 GOAL LINE 수비는 러닝 계열에 -2, -3을 걸지만 롱패스에는 오히려 +1을 준다. 몸을 낮추고 라인을 지키는 수비라 단거리 돌파에는 강한데 머리 위로 넘어가는 공에는 약하다는 걸 숫자로 표현한 것이다.
예시: 공격이 PITCH OUT, 수비가 GOAL LINE을 낸 경우
공격 D10 = 9
GOAL LINE의 PITCH OUT 보정 = -2
조정값 = 9 - 2 = 7 → 차트의 "7-8" 행
그러니까 수비를 잘 고른다는 것은 상대의 주사위를 깎아서 차트의 나쁜 행으로 밀어 넣는 것이다. 이게 이 게임 전략의 핵심이다.
수비 D10 (파랑) — 반칙 판정 전용
수비자도 10면체를 굴리는데, 이 값은 차트 판정에는 전혀 쓰이지 않는다. 오직 반칙(패널티) 체크용이다.
- 양팀 D10의 합이 6 또는 16이면 반칙 발생
- 단, 두 주사위의 눈이 같으면(3-3, 8-8) 반칙이 상쇄되어 플레이가 그대로 진행된다
- 반칙이 나면 그 플레이에 낸 카드는 무효가 되고, 패널티 카드 차트로 벌칙 야드를 정한 뒤 같은 다운을 다시 진행한다
- 낮은 주사위를 굴린 쪽이 반칙을 범한 팀이 된다
처음엔 "주사위 하나를 반칙에만 쓰는 건 낭비 아닌가" 싶었는데, 확률을 계산해 보면 꽤 정교하다. D10 두 개의 합이 6 또는 16이 나올 확률은 100가지 조합 중 10가지, 딱 10%다. 여기서 눈이 같은 경우(3-3, 8-8)를 빼면 8%. 실제 NFL 경기에서 플레이당 반칙 빈도와 비슷한 수준이다. 주사위 하나를 통째로 배정해서 얻는 건 "반칙은 공수 양쪽 누구에게나, 언제든 터질 수 있다"는 긴장감이다.
D12 두 개 (보라) — 차트의 열을 결정
양팀이 하나씩 굴린 12면체의 합(2~24) 이 차트의 열(가로 위치) 을 정한다. 열은 10개 구간이다:
2-3 | 4-5 | 6-7 | 8-9 | 10-12 | 13-15 | 16-18 | 19-20 | 21-22 | 23-24
구간 폭이 균일하지 않은 게 보이는가? 주사위 두 개의 합은 중앙값(13) 근처가 가장 자주 나오는 종 모양 분포를 그린다. 그래서 자주 나오는 가운데 구간(10-12, 13-15)은 폭을 3으로 넓게, 드물게 나오는 양 끝(2-3, 23-24)은 폭을 2로 좁게 잡았다. 그리고 카드 차트를 보면 화끈한 결과(대박 전진, 인터셉트)는 양 끝 열에, 평범한 결과는 가운데 열에 배치되어 있다. 확률 분포를 차트 설계에 그대로 녹인 것이다.
한 장의 표로 정리
| 주사위 | 누가 굴리나 | 역할 | 특이사항 |
|---|---|---|---|
| 공격 D10 (빨강) | 공격팀 | 차트 행 결정 | 수비 카드 보정(-3~+3) 적용. 높을수록 유리 |
| 수비 D10 (파랑) | 수비팀 | 반칙 판정 전용 | 양팀 합 6/16이면 반칙, 같은 눈이면 상쇄 |
| D12 (보라) ×2 | 양팀 하나씩 | 합으로 차트 열 결정 | 합의 분포가 종 모양 → 극단 결과는 희귀 |
실제 판정 한 번을 따라가 보자
앱을 테스트하다 나온 실제 상황이다.
상황: AI가 PITCH OUT(러닝) 공격, 나는 GOAL LINE 수비 선택
주사위: 공격 D10 = 9, 수비 D10 = 10, D12 = 4와 10
1) 반칙 체크: 9 + 10 = 19 → 6도 16도 아님, 통과
2) 행 결정: 9 + (GOAL LINE의 PITCH OUT 보정 -2) = 7 → "7-8" 행
3) 열 결정: 4 + 10 = 14 → "13-15" 열
4) PITCH OUT 차트의 (7-8행, 13-15열) = 6 → 6야드 전진!
수비 보정 -2가 없었다면 "9-10" 행이었고, 그 행의 같은 열 값은 8이다. 수비 카드 한 장이 2야드를 막아낸 셈이다. 이런 식으로 모든 플레이에서 카드 선택이 결과에 실질적으로 개입한다.
차트라는 이름의 확률 테이블
각 공격 카드 뒷면에는 5행 × 10열 = 50칸짜리 차트가 있다. 셀에는 전진/후퇴 야드 숫자 또는 특수 결과(I = 인터셉트, F = 펌블)가 들어 있다. 이 차트가 사실상 각 플레이의 확률 분포를 하드코딩한 룩업 테이블이다.
카드 두 장을 비교해 보면 설계 의도가 보인다:
- DIVE/PLUNGE (중앙 돌파 러닝): 평균 2.7야드. 차트 대부분이 1~6야드의 작은 숫자. 대신 실패해도 손실이 적고 성공률(effectiveness)이 74.1%로 가장 높다. "짧고 확실한" 플레이
- LONG BOMB (장거리 패스): 평균 14.7야드에 차트에 96, 71 같은 대박 숫자가 있지만, 인터셉트(I) 칸이 2개나 있고 0야드 칸이 수두룩하다. 성공률 44.5%. "모 아니면 도"
개발자 관점에서 보면 이 게임의 밸런싱은 코드 로직이 아니라 데이터에 들어 있다. 실제로 앱으로 옮길 때도 판정 엔진 코드는 "행 계산, 열 계산, 테이블 참조" 세 줄 수준이고, 게임의 재미는 전부 차트 데이터가 만든다. 룰(코드)과 밸런스(데이터)를 분리하는 설계를 1970~2000년대 스포츠 보드게임들이 이미 하고 있었던 것이다.
// 앱 구현에서 판정의 뼈대. 이게 거의 전부다.
final mod = defenseCard.modifierFor(offenseCard.id); // 수비 보정
final row = rowIndex((offD10 + mod).clamp(1, 10)); // 행
final col = columnIndex(offD12 + defD12); // 열
final cell = offenseCard.chart[row][col]; // 결과
차트에 없는 것들: 스페셜 상황
일반 공수 플레이 외의 상황은 전용 카드가 처리한다. 이 카드들은 수비 카드 없이 단독으로 판정한다는 점이 다르다.
- 킥오프 / 킥오프 리턴: 전·후반 시작과 득점 후. 공을 차고, 받은 쪽이 리턴한다. 엔드존에 들어간 공은 리턴을 포기하고 20야드에서 시작(터치백)할 수 있다
- 펀트: 4번째 다운에 10야드 전진이 어려울 때, 공격권을 넘겨주는 대신 공을 멀리 차서 상대의 시작 위치를 나쁘게 만든다. Long/Short 두 종류
- 필드골: 골대까지의 거리(1-19 / 20-29 / 30-39 / 40-49 / 50-59야드)별로 카드가 따로 있고, 멀수록 실패(X) 칸이 늘어난다. 성공 시 3점
- 턴오버 / 펌블: 인터셉트나 펌블이 나면 공격권이 넘어가는데, 수비팀이 공을 잡아 얼마나 되돌려 달렸는지를 턴오버 카드로 판정한다. 턴오버 리턴 중 또 펌블이 나면 펌블 카드로 공의 주인을 다시 가린다. 참고로 펌블 카드의 회수 확률은 48.3% — 동전 던지기에 아주 살짝 못 미치게 설계되어 있다
터치다운 후에는 추가 득점을 선택한다. 안전한 추가 킥(+1점, 성공률 98.7%) 또는 도박인 2점 컨버전(+2점, 2야드 라인에서 공수 카드 플레이 한 번).
정리
Playbook Football의 판정 시스템을 한 문장으로 요약하면 이렇다:
공격 D10(수비 보정 적용)이 행을, D12 합이 열을 정하고, 그 교차점이 결과다. 수비 D10은 반칙 감시자다.
시스템 전체를 조망하면:
- 카드를 몰래 내는 심리전 — 수 읽기에 성공하면 상대 주사위를 -3까지 깎는다
- D10이 정하는 행 — 플레이의 성패
- D12 합이 정하는 열 — 종 모양 분포로 극단 결과를 희귀하게
- 차트 = 확률 분포를 담은 데이터 테이블 — 룰과 밸런스의 분리
- 반칙 전용 주사위 — 약 8% 확률의 무작위 변수
주사위 4개와 종이 카드로 이 정도의 시뮬레이션 깊이를 만들 수 있다는 게 이 게임의 매력이다. 다음 글에서는 이 시스템을 Flutter로 옮기면서 겪은 것들 — 차트 데이터 모델링, AI 상대의 카드 선택 로직, 주사위 굴리는 애니메이션 — 을 다뤄볼 생각이다.
backtodev
40대 PM, 다시 개발자로 돌아갑니다. 실패하고 배우며 성장하는 기록.