마작 타일(Mahjong Tiles) 추상화 및 인코딩 실습
마작 게임 비즈니스를 한 문장으로 설명하면 다음과 같습니다:
- 세 명 모드 <
두 방 세 명>은바라기와동그라미두 가지 패턴을 사용하며 총72장의 카드가 사용됩니다. - 네 명 모드 <
끝까지 싸우기>는바라기,동그라미,만자세 가지 패턴을 사용하며 총108장의 카드로 구성됩니다.
단계별 Mahjong Server 개발
- 단일 아키텍처를 통해 Mahjong Server 비즈니스 이해 → 나노 분산 게임 서버(Nano Distributed Game Server) + 마이크로서비스로 리팩토링.
- 데모: go-mahjong-server
타일(Tiles) 추상화
타일 구조체 정의
type Tile struct {
CardID int
Pattern int
Number int
Position int
}
CardID:108장의 카드를0~107범위의 고유 ID로 표시합니다.Pattern: 세 가지 패턴(바라기,동그라미,만자)을0~2로 구분합니다(0: 바라기, 1: 동그라미, 2: 만자).Number: 각 패턴당9종류의 숫자를1~9로 나타냅니다(예: 1바라기, 9바라기, 1동그라미, 9동그라미 등).Position: 위치를 나타내며 바라기는1~9, 동그라미는11~19, 만자는21~29범위로 지정됩니다.
표를 통한 이해
세 가지 패턴과 각 패턴의 위치(Position)를 아래 표로 확인할 수 있습니다:
| 바라기 (1 ~ 9) | 동그라미 (11 ~ 19) | 만자 (21 ~ 29) |
|---|---|---|
| 1 1바라기 | 11 1동그라미 | 21 1만자 |
| 2 2바라기 | 12 2동그라미 | 22 2만자 |
| 3 3바라기 | 13 3동그라미 | 23 3만자 |
| 4 4바라기 | 14 4동그라미 | 24 4만자 |
| 5 5바라기 | 15 5동그라미 | 25 5만자 |
| 6 6바라기 | 16 6동그라미 | 26 6만자 |
| 7 7바라기 | 17 7동그라미 | 27 7만자 |
| 8 8바라기 | 18 8동그라미 | 28 8만자 |
| 9 9바라기 | 19 9동그라미 | 29 9만자 |
총 108 장의 모든 카드 ID 번호는 다음과 같이 나뉩니다:
| 바라기 (0 ~ 35) | 동그라미 (36 ~ 71) | 만자 (72 ~ 107) |
|---|---|---|
| 0 1바라기 | 36 1동그라미 | 72 1만자 |
| 1 1바라기 | 37 1동그라미 | 73 1만자 |
| ... | ... | ... |
| 35 9바라기 | 71 9동그라미 | 107 9만자 |
ID로부터 타일 가져오기
아래 함수는 주어진 ID를 통해 해당 타일 정보를 반환합니다.
func GetTileFromID(cardID int) *Tile {
if cardID < 0 || cardID >= 108 {
panic("잘못된 타일 ID")
}
pattern := (cardID / 4) / 9
number := ((cardID / 4) % 9) + 1
position := pattern*10 + number
return &Tile{
CardID: cardID,
Pattern: pattern,
Number: number,
Position: position,
}
}
유닛 테스트 작성
GetTileFromID 함수의 정확성을 검증하기 위한 유닛 테스트 코드입니다.
func TestGetTileFromID(t *testing.T) {
tests := []struct {
name string
id int
want *Tile
}{
{"1바라기", 0, &Tile{CardID: 0, Pattern: 0, Number: 1, Position: 1}},
{"1바라기", 1, &Tile{CardID: 1, Pattern: 0, Number: 1, Position: 1}},
{"9바라기", 35, &Tile{CardID: 35, Pattern: 0, Number: 9, Position: 9}},
{"1동그라미", 36, &Tile{CardID: 36, Pattern: 1, Number: 1, Position: 11}},
{"9동그라미", 71, &Tile{CardID: 71, Pattern: 1, Number: 9, Position: 19}},
{"1만자", 72, &Tile{CardID: 72, Pattern: 2, Number: 1, Position: 21}},
{"9만자", 107, &Tile{CardID: 107, Pattern: 2, Number: 9, Position: 29}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetTileFromID(tt.id)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetTileFromID(%d) = %v, want %v", tt.id, got, tt.want)
}
})
}
}
테스트 실행 방법:
cd internal/game/mahjong
go test -v tile_test.go tile.go mahjong.go