Files
joke-app/docs/games/ninety-nine.md
EJClaw f4d63e1544 docs: 온라인 보드게임 사이트 계획서 (공통 14편 + 게임 26종)
- 개요, 조사(BGA·한국 인기 보드게임·UI/UX), 아키텍처, 실시간 프로토콜,
  안정성·보안, 계정, 로비·방, UI/UX, 데이터 모델, 게임 엔진, 테스트,
  배포·운영, 로드맵(M0~M9), 사용자 결정 항목
- 게임별 규칙·엔진 설계·UI·테스트 체크리스트 26종

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 04:18:29 +09:00

268 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 구십구 (`ninety-nine`)
> 마일스톤: M7 · 인원: 최소 2 ~ 최대 10명 · 예상 시간: 약 5~15분 · 난이도: 쉬움
## 1. 개요
- 차례대로 카드를 1장씩 내며 숫자를 더해 간다. 합계를 99보다 크게 만들 수밖에 없는 사람이 탈락(또는 목숨 1개를 잃음)하는 서바이벌 카드 게임이다.
- 이 문서는 두 가지 규칙을 담는다.
- `kr-product` 모드(기본): 코리아보드게임즈의 카드게임 "구십구"(2~10명, 만 8세 이상, 약 5분) 방식. 전용 카드(1~8, 10, ±9, ±10, 0, 조커)를 쓴다.
- `classic` 모드: 트럼프 52장으로 하는 전통 게임 "99(Ninety-Nine)". 영미권 놀이 게임이고, 나무위키 "99(플레잉 카드)"에도 같은 계열 규칙이 있다.
- 인원 근거
- 코리아보드게임즈 공식 상품 페이지에 2~10명으로 나온다(일부 쇼핑몰은 2~6명으로 표기).
- 나무위키 "99(플레잉 카드)"도 2~10명이다.
- 그래서 2~10명으로 한다.
### 확인된 것과 확인되지 않은 것 (오너 확인 필요)
- 확인됨(코리아보드게임즈 매거진 기사, 공식 상품 페이지)
- 1~8 카드 각 7장, 10 카드 21장.
- 특수 카드 ±9, ±10, 0, 조커(정확히 2장).
- 자기 차례에 1장을 내고 이전 수에 더한 새 수를 말한다.
- 99를 넘게 만드는 카드는 낼 수 없고, 낼 카드가 1장도 없으면 탈락한다. 혼자 남으면 승리한다.
- ±9·±10은 더하거나 뺄 수 있다. 0은 수 변화 없이 진행 방향을 바꾼다. 조커는 앞 수와 상관없이 60~99 중 원하는 수를 부른다.
- 확인되지 않음(공식 규칙서 원문을 구하지 못함. 아래는 이 문서의 기본값이고 모두 옵션)
- ±9, ±10, 0 카드의 장수. 기본값은 각 4장(추정)이다.
- 시작 손패 장수. 기본값은 3장(전통 99와 같음)이다.
- 카드를 낸 뒤 1장 보충 여부. 기본값은 보충한다(전통 99와 같음).
- 빼기로 0 미만이 될 때 처리. 기본값은 0 미만이 되는 빼기는 금지다.
- 탈락자가 생긴 뒤 합계를 이어 갈지 초기화할지. 기본값은 이어 간다.
- 칩(목숨) 변형이 공식 규칙서에 있는지. 일부 소개 글에 "칩 3개, 99를 넘기면 1개 잃음" 변형이 언급되지만 원문으로 확인하지 못했다. 전통 99의 토큰 3개 규칙과 같은 구조라 `lives` 옵션으로 둔다.
## 2. 모드와 옵션
| 옵션 키 | 설명 | 선택지 | 기본값 |
|---|---|---|---|
| `mode` | 규칙 | `kr-product` / `classic` | `kr-product` |
| `handSize` | 손패 장수 | 3 / 4 / 5 | 3 |
| `refill` | 카드를 낸 뒤 1장 보충 | on / off | on |
| `lives` | 목숨(토큰) 수. 0이면 목숨 없이 바로 탈락 | 0 / 1 / 2 / 3 / 5 | `kr-product` 0, `classic` 3 |
| `onTheBoard` | 목숨을 다 잃은 뒤에도 한 번 더 질 때까지 남음(전통 변형) | on / off | off |
| `afterElimination` | (`lives=0`) 탈락 뒤 합계 처리 | `continue`(그대로 이어 감) / `reset`(0으로, 다시 나눔) | `continue` |
| `floorAtZero` | 빼기 결과가 0 미만일 때 | `forbid`(그런 빼기는 못 냄) / `clamp`(0으로 맞춤) | `forbid` |
| `specialCounts` | (`kr-product`) ±9, ±10, 0 각 장수 | 2 / 3 / 4 / 5 / 6 | 4 |
| `jokerRange` | (`kr-product`) 조커로 부를 수 있는 범위 | `60-99`(원작) / `0-99` | `60-99` |
| `aceHigh` | (`classic`) A의 큰 값 | 10(나무위키) / 11(Bicycle) | 11 |
| `deckCount` | (`classic`) 덱 수 | `auto`(7인 이상 2벌) / 1 / 2 | `auto` |
| `turnSeconds` | 턴 제한 시간 | 10 / 15 / 20 / 30 | 15 |
## 3. 구성물
`kr-product` (기본값 기준 91장)
| 카드 | 효과 | 장수 |
|---|---|---|
| 1~8 | 그 숫자만큼 더함 | 각 7장(56장) |
| 10 | 10 더함 | 21장 |
| ±9 | 9를 더하거나 뺌(낸 사람이 선택) | `specialCounts`(기본 4, 추정) |
| ±10 | 10을 더하거나 뺌 | `specialCounts`(기본 4, 추정) |
| 0 | 수 변화 없음, 진행 방향 반대 | `specialCounts`(기본 4, 추정) |
| 조커 | 앞 수와 상관없이 60~99 중 원하는 수로 만듦 | 2장 |
- 9 숫자 카드는 없다(±9만 있음).
- 목숨을 쓰면(`lives > 0`) 플레이어마다 목숨 토큰을 그만큼 준다. 방 안에서만 쓰는 표시용이다.
`classic` (트럼프 52장, Bicycle 규칙 기준)
| 카드 | 효과 |
|---|---|
| A | 1 또는 `aceHigh`(11) 더함(낸 사람이 선택) |
| 2, 3, 5, 6, 7, 8 | 그 숫자만큼 더함 |
| 4 | 0(수 변화 없음), 진행 방향 반대 |
| 9 | 0(수 변화 없음), 그냥 넘김 |
| 10 | 10을 뺌 |
| J, Q | 10 더함 |
| K | 합계를 바로 99로 만듦(이미 99면 그대로 99) |
- 조커는 쓰지 않는다. `deckCount = 2`이면 104장이다.
## 4. 준비(셋업)
1. 모드에 맞는 덱을 만들고 시드 RNG로 섞는다.
2. 선 플레이어를 RNG로 정한다. 진행은 시계 방향이다.
3. 각자 `handSize`장을 받는다. 남은 카드는 뽑을 더미가 된다.
4. 합계는 0에서 시작한다.
5. 목숨을 쓰면 각자 `lives`개를 받는다.
## 5. 진행 규칙
### 5.1 턴
1. **탈락 판정(턴 시작 때 서버 자동)**: 현재 플레이어가 낼 수 있는 카드가 하나도 없으면 바로 "패배 처리"(5.3)를 한다. 패배 처리 결과 탈락하지 않은 경우(목숨이 남음)에는 새 라운드가 시작된다.
2. **카드 내기**: 낼 수 있는 카드 1장을 낸다. ±카드는 더하기/빼기를, A는 1/11을, 조커는 숫자를 함께 고른다. 새 합계가 공개된다.
3. **보충**(`refill = on`): 뽑을 더미에서 1장을 가져온다. 더미가 비었으면 버린 카드를 모두 섞어 새 더미를 만든다(합계는 그대로). 그래도 없으면 보충하지 않는다.
4. 다음 차례로 넘긴다. 0(또는 `classic`의 4)이면 방향을 반대로 바꾼 뒤 넘긴다.
- 패스는 없다. 낼 수 있는 카드가 있으면 반드시 1장을 내야 한다.
- 2인일 때 방향 반대 카드는 방향만 바뀌고 다음 차례는 여전히 상대다.
### 5.2 낼 수 있는 카드(합법 조건)
합계를 S, 카드를 낸 뒤 합계를 S'라 할 때 S' ≤ 99여야 한다.
`kr-product`
- 1~8, 10: S' = S + 값.
- ±9, ±10
- 더하기면 S' = S + 값.
- 빼기면 S' = S - 값. `floorAtZero = forbid`이면 S' ≥ 0이어야 하고, `clamp`이면 S' = max(0, S - 값).
- 두 방향 중 하나라도 합법이면 낼 수 있다.
- 0: S' = S. 항상 낼 수 있다.
- 조커: S'를 `jokerRange`(60~99) 안의 아무 정수로 고른다. 항상 낼 수 있다. 현재 합계와 같은 수를 골라도 된다.
`classic`
- A: 1 또는 11 중 S' ≤ 99인 값.
- 2, 3, 5~8, J, Q: S' = S + 값.
- 4, 9: S' = S. 항상 낼 수 있다.
- 10: S' = S - 10. `forbid`이면 S ≥ 10일 때만, `clamp`이면 항상 가능.
- K: S' = 99. 항상 낼 수 있다.
### 5.3 패배 처리
- `lives = 0`(`kr-product` 기본)
- 그 플레이어는 탈락한다. 손패는 버린 더미로 간다.
- `afterElimination = continue`면 같은 합계로 다음 사람 차례가 된다(다음 사람도 낼 카드가 없으면 연달아 탈락할 수 있다).
- `reset`이면 합계를 0으로 하고 남은 사람 손패를 모두 거둬 다시 섞어 나눈다. 탈락자의 다음 사람이 시작한다.
- `lives > 0`
- 그 플레이어가 목숨 1개를 잃는다. 이번 라운드는 끝난다.
- 목숨이 0이 되면 탈락한다. `onTheBoard = on`이면 목숨 0인 상태로 계속 하고, 그 상태에서 한 번 더 지면 탈락한다.
- 새 라운드: 모든 카드를 거둬 섞고, 남은 사람에게 다시 나누고, 합계 0에서 시작한다. 진 사람이 탈락하지 않았으면 진 사람이 먼저 하고, 탈락했으면 그 다음 사람이 먼저 한다.
- 남은 사람이 1명이면 게임이 끝난다.
### 5.4 예시 (`kr-product`, 3인)
- 합계 85에서 A가 10을 내 95가 된다. A가 보충한다.
- B의 손패는 [8, 7, ±9]이다. 8과 7은 99를 넘는다. ±9를 빼기로 내서 86이 된다.
- C의 손패는 [10, 10, 0]이다. 0을 내서 86 그대로이고 방향이 반대가 되어 B 차례가 된다.
- B의 손패는 [8, 7, 6](보충한 6)이다. 8 → 94, 7 → 93, 6 → 92 모두 가능하다. B가 7을 내서 93.
- 방향이 바뀌었으므로 다음은 A다. A의 손패가 [8, 10, 7]이면 101, 103, 100이라 낼 카드가 없다. A는 턴 시작 때 탈락하고 C 차례가 된다.
## 6. 승패와 점수 계산
- 마지막까지 남은 1명이 승자다.
- 순위: 늦게 탈락한 사람이 앞선다. 같은 시점 동시 탈락은 없다(턴 순서대로 판정).
- 목숨을 쓰는 경우에도 탈락 순서로 순위를 정한다. 결과 화면에 각자 잃은 목숨 수를 보여 준다.
- 점수, 칩, 돈은 없다. 목숨 토큰은 이 판에서만 쓰는 표시이고 방 밖으로 이어지지 않는다.
## 7. 지역 룰 / 하우스 룰 (옵션으로 켜고 끄기)
- 목숨(칩) 변형(`lives`, `onTheBoard`): 전통 99의 토큰 3개 규칙이다. 구십구 소개 글 일부에서도 "칩 3개" 변형이 언급된다(원문 미확인).
- 손패 장수(`handSize`), 보충 여부(`refill`).
- 탈락 뒤 합계 처리(`afterElimination`).
- 0 미만 빼기 처리(`floorAtZero`).
- 조커 범위 확장(`jokerRange`, 어린이용 0~99).
- `classic`의 A 큰 값(`aceHigh` 10/11).
- 지원하지 않는 변형(문서화만)
- Wikipedia "표준" 변형: 3이 다음 사람 건너뛰기, 9가 99로 만들기, 10이 ±10, K가 0.
- 정확히 99를 만들면 보너스, 같은 숫자 연속 내기 금지 같은 지역 규칙.
- 필요하면 `classic` 카드 효과표를 옵션 프리셋으로 늘린다.
## 8. 엔진 설계
### 8.1 상태(State)
```ts
type NNCard =
| { id: string; kind: 'add'; v: number } // kr: 1..8, 10 / classic: 2,3,5,6,7,8, J/Q(10)
| { id: string; kind: 'plusMinus'; v: 9 | 10 } // kr ±9, ±10
| { id: string; kind: 'reverse' } // kr 0 / classic 4
| { id: string; kind: 'hold' } // classic 9
| { id: string; kind: 'minus'; v: 10 } // classic 10
| { id: string; kind: 'ace' } // classic A (1 또는 aceHigh)
| { id: string; kind: 'to99' } // classic K
| { id: string; kind: 'joker' }; // kr 조커 (jokerRange)
// 화면 표시용으로 원래 표기(rank/suit 또는 kr 라벨)를 별도 필드 face로 둔다.
interface NinetyNineState {
mode: 'kr-product' | 'classic';
options: NNOptions;
rng: RngState;
seats: PlayerId[];
hands: Record<PlayerId, NNCard[]>; // 비공개
drawPile: NNCard[]; // 비공개
discard: NNCard[]; // 공개(최근 몇 장만 view)
total: number; // 공개 합계
direction: 1 | -1;
current: PlayerId;
alive: Record<PlayerId, boolean>;
lives: Record<PlayerId, number>; // lives=0 모드에서는 사용 안 함
onBoard: Record<PlayerId, boolean>; // onTheBoard 상태
eliminationOrder: PlayerId[];
round: number;
phase: 'playing' | 'finished';
turnStartedAt: number;
seq: number;
}
```
### 8.2 액션
| type | payload | 누가 / 언제 | 검증 조건 |
|---|---|---|---|
| `play` | `{ cardId: string; sign?: '+' \| '-'; aceValue?: 1 \| 10 \| 11; jokerValue?: number }` | `current`, playing | 카드가 손에 있음. ±카드는 `sign` 필수, A는 `aceValue` 필수(1 또는 `aceHigh`), 조커는 `jokerValue` 필수이고 `jokerRange` 안의 정수. 5.2 조건으로 계산한 S' ≤ 99(그리고 `forbid`면 S' ≥ 0) |
- 플레이어가 보내는 액션은 `play` 하나뿐이다. 탈락, 목숨 감소, 라운드 재시작, 보충은 `apply` 안에서 자동으로 처리한다.
- `play`를 적용한 직후 다음 플레이어를 정한다. 그 플레이어에게 합법 수가 없으면 같은 `apply` 안에서 패배 처리를 하고, 다시 다음 사람을 검사한다(반복). 이 과정을 이벤트로 순서대로 남긴다.
- 이렇게 하면 "낼 카드가 없는 사람이 타이머를 기다리는" 상황이 없다.
- 이벤트 예: `cardPlayed{player, face, total}`, `directionChanged`, `cardDrawn{player}`, `reshuffled`, `lifeLost{player, left}`, `eliminated{player}`, `roundStarted{round, starter}`.
- `reason` 예: "99를 넘어서 낼 수 없어요", "0보다 작아져서 뺄 수 없어요", "더하기와 빼기 중 하나를 골라 주세요", "조커는 60부터 99 사이 숫자만 고를 수 있어요".
### 8.3 공개/비공개 정보 (view)
- 모두(관전자 포함): 현재 합계, 방향, 현재 차례, 각자 손패 장수, 목숨, 생존 여부, 탈락 순서, 뽑을 더미 장수, 최근에 낸 카드 몇 장과 그때의 합계 기록.
- 본인만
- 자기 손패.
- 각 카드의 합법 여부와 결과 미리보기(예: "±9 → 더하면 104 불가 / 빼면 86").
- 탈락 때 손패는 공개하지 않고 장수만 버린 더미로 옮긴다(실물에서는 보일 수 있지만 다른 사람 추리에 영향이 없도록 비공개로 둔다).
- 절대 보내지 않는 것: 남의 손패, 더미 순서, RNG 상태. 보충 이벤트는 본인에게만 카드 내용을 준다.
### 8.4 랜덤 요소 (시드 RNG 사용 지점)
- `setup`: 덱 섞기, 선 플레이어.
- 더미 소진 때 버린 카드 다시 섞기.
- 라운드 재시작(목숨 모드, `reset`) 때 다시 섞기.
### 8.5 타이머·시간 초과·연결 끊김 시 자동 행동
- `deadline = turnStartedAt + turnSeconds*1000`.
- `onTimeout(state, p)`: 합법인 (카드, 선택값) 조합 중 결과 합계 S'가 가장 작은 것을 고른다.
- 같으면 우선순위: 0/4/9(변화 없음) → 일반 숫자 카드 → ±카드 → K/조커. 그래도 같으면 카드 ID 순.
- 조커는 `jokerRange` 최소값(60)으로 낸다.
- 이 규칙은 본인 손패와 공개 합계만 쓰므로 정보 유출이 없고 결정적이다.
- 합법 수가 없는 경우는 타이머까지 가지 않고 `apply`에서 이미 처리된다(8.2).
- 연결이 끊겨도 위 자동 행동으로 계속 진행한다. 연속 3턴 타임아웃이면 서버가 마감을 3초로 줄인다.
### 8.6 종료 조건과 결과(GameResult)
- 생존자가 1명이 되면 `finished`.
```ts
interface GameResult {
ranking: { player: PlayerId; rank: number; livesLost: number; eliminatedAtRound: number | null }[];
winner: PlayerId[];
summary: string; // "끝까지 살아남은 사람은 지호 님! 마지막 합계 97"
}
```
## 9. UI/UX
- 모바일 세로 화면
- 가운데: 현재 합계를 아주 크게(예: 72pt) 보여 준다. 99에 가까울수록 색이 초록 → 노랑 → 빨강으로 바뀌는 게이지 링을 두른다.
- 위: 상대 아바타(손패 장수, 목숨 하트, 차례 표시), 진행 방향 화살표.
- 아래: 내 손패 3장을 큰 카드로 보여 준다(최소 72×100px).
- 조작
- 카드를 탭하면 "내면 → 81"처럼 결과 합계 미리보기가 카드 위에 뜬다. 한 번 더 탭하면 낸다.
- ±카드를 탭하면 [+9 → 95] [−9 → 77] 두 버튼이 뜬다. 불가능한 쪽은 비활성이다.
- 조커를 탭하면 60~99 숫자 다이얼(+/− 버튼과 빠른 선택 60/90/99)이 뜬다.
- 낼 수 없는 카드는 흐리게 하고 "99 초과" 배지를 붙인다.
- 애니메이션: 합계 숫자가 올라가는 카운트 애니메이션, 방향 전환 화살표 회전, 탈락 때 아바타 흑백 처리, 목숨 하트가 깨지는 연출.
- 초보 도움말: "99를 넘기면 안 돼요!" 한 줄 규칙과 특수 카드 표. 어린이 방 추천 설정(조커 0~99, 손패 4장).
- 실시간 감각: 턴이 빨라서 타이머 링을 크게 보여 주고, 내 차례가 되면 진동(모바일 지원 시)으로 알린다.
## 10. 테스트 체크리스트
- [ ] 덱 구성: `kr-product` 기본값 91장(1~8 각 7, 10이 21, ±9/±10/0 각 4, 조커 2). `specialCounts=2`면 85장. `classic` 52장, 7인 이상 `auto`면 104장.
- [ ] 같은 시드면 같은 분배, 같은 선이 나온다.
- [ ] 합계 95에서 손패 [8, 5, 4] (`kr-product`)이면 4만 합법이다. 4를 내서 99가 된다. 다음 사람이 [1, 2, 3]만 있으면 턴 시작 때 바로 탈락한다.
- [ ] ±10: 합계 5에서 빼기는 `forbid`면 거부된다. `clamp`면 0이 된다. 더하기는 15로 허용된다.
- [ ] 조커: 합계 20에서 조커로 99를 고르면 99가 된다. 59는 거부되고(`60-99`), 100도 거부된다.
- [ ] 0 카드: 3인에서 A → B 차례에 B가 0을 내면 방향이 반대가 되어 다음이 A다. 2인에서는 상대 차례 그대로다.
- [ ] `classic`: 합계 90에서 K → 99, 그다음 사람은 4/9/10/K만 낼 수 있다. A는 1이어도 100이라 거부된다. 합계 88에서 A를 11로 내면 99로 허용된다.
- [ ] `lives=3`: 진 사람이 목숨 1개를 잃고, 합계 0으로 새 라운드가 시작되며 진 사람이 먼저 한다. 목숨 0이면 탈락하고 다음 사람이 시작한다. `onTheBoard`면 0개에서 한 번 더 져야 탈락한다.
- [ ] 연쇄 탈락(`lives=0`, `continue`): 합계 98에서 다음 두 사람 모두 낼 카드가 없으면 한 번의 `apply` 안에서 두 명이 순서대로 탈락하고, 세 번째 사람 차례가 된다. 이벤트 순서가 맞다.
- [ ] 더미 소진: 보충할 때 더미가 비면 버린 카드가 섞여 보충되고 합계는 그대로다.
- [ ] 시간 초과: 손패 [10, 3, 0], 합계 50이면 자동 행동은 0(결과 50, 가장 작음)을 낸다. 손패 [±9, 3, 0], 합계 50이면 −9(결과 41)를 낸다.
- [ ] 연결 끊김: 끊긴 플레이어 차례가 자동으로 진행되고 게임이 멈추지 않는다.
- [ ] 정보 유출: 관전자와 상대 view에 남의 손패, 보충 카드 내용, 더미 순서가 없다. 탈락자 손패도 공개되지 않는다.
- [ ] 차례가 아닌 `play`, `sign` 없는 ±카드, 범위 밖 `jokerValue`, 중복 패킷이 거부되고 상태가 바뀌지 않는다.
## 11. 참고 자료
- 코리아보드게임즈 매거진 "숫자를 더해나가며 한계에 도전하라!": https://www.koreaboardgames.com/magazine/menuDetail?boardCd=contents&postNo=947 (1~8 각 7장, 10이 21장, ±9·±10·0·조커 효과, 조커 2장 60~99, 99 초과 금지, 낼 카드 없으면 탈락)
- 코리아보드게임즈 상품 페이지 "구십구": https://www.koreaboardgames.com/product/detail?prdCd=PD2024002211B1JR (2~10명, 만 8세+, 5분)
- 나무위키 "99(플레잉 카드)": https://namu.wiki/w/99(플레잉%20카드) (트럼프판 2~10명, 손패 3장, 내고 1장 보충, A 1/10, 4 반대, 9는 0, 10은 −10, J·Q 10, K는 99)
- Bicycle Cards "99 (Ninety Nine)": https://bicyclecards.com/how-to-play/99-ninety-nine (토큰 3개, 손패 3장, A 1/11, 4 반대, 9 패스, 10 −10, K 99, 넘기면 토큰 1개 잃고 라운드 종료)
- Wikipedia "Ninety-nine (addition card game)": https://en.wikipedia.org/wiki/Ninety-nine_(addition_card_game) (토큰 3개, 지역 변형 목록)
## 12. 메모 (상표·법적 주의 등)
- "구십구"는 코리아보드게임즈가 파는 상품명이다. 숫자 이름이라 상표 효력이 약할 수 있지만 안전하게 표시 이름을 바꾸는 것을 권한다. 후보: "99 넘기지 마!", "아흔아홉", "숫자 더하기 99"(사용자 선택).
- 전통 게임 "99(Ninety-Nine)" 규칙 자체는 공공 영역이다. `kr-product` 모드의 전용 카드 구성(±9, ±10, 0, 조커 60~99)은 상품의 고유 설계를 따른 것이다. 규칙 자체는 저작권 보호 대상이 아니지만, 카드 그림과 규칙서 문장은 쓰지 않고 직접 만든다.
- 목숨(칩) 토큰은 방 안에서만 쓰는 게임 내 표시다. 돈, 포인트, 교환과 연결하지 않는다. 화면 용어는 도박 느낌을 피하려고 "칩" 대신 "목숨"(하트)을 쓴다.