게임 캐릭터 시스템
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 107번째.
챌린지
쉬움구조체 임베딩(struct embedding), 인터페이스, 그리고 전략 패턴(Strategy pattern)을 결합하여 유연한 전투 메커니즘을 만드는 게임 캐릭터 시스템을 구축해 봅시다! 여러분은 런타임에 교체 가능한 능력을 갖추고, 고유한 방식으로 공격과 방어를 수행하는 다양한 캐릭터 유형을 설계하게 됩니다.
코드는 다음 다섯 개의 파일로 구성됩니다:
character.go: 공통 필드인Name(string),Health(int),MaxHealth(int)를 가진 기본Character구조체를 정의합니다.NewCharacter생성자와 다음 메서드들을 포함하세요:IsAlive() bool— 체력이 0보다 크면 true를 반환합니다.Status() string—[Name]: [Health]/[MaxHealth] HP형식의 문자열을 반환합니다.
interfaces.go: 다형성을 가능하게 하는 전투 인터페이스를 정의합니다:Attack() int메서드를 가진Attacker인터페이스 — 가한 데미지를 반환합니다.TakeDamage(damage int) int메서드를 가진Defender인터페이스 — 데미지 감소를 적용하고 실제 입은 데미지를 반환합니다.Use() string메서드를 가진Ability인터페이스 — 능력 효과에 대한 설명을 반환합니다.
characters.go:Character를 임베딩하고Attacker와Defender를 모두 구현하는 세 가지 캐릭터 유형을 만듭니다:Warrior—Strength(int)와Armor(int) 필드를 가집니다. Attack은 Strength를 데미지로 반환합니다. TakeDamage는 들어오는 데미지에서 Armor만큼을 차감합니다 (최소 1의 데미지를 입음).Mage—Intelligence(int)와MagicResist(int) 필드를 가집니다. Attack은 Intelligence * 2를 데미지로 반환합니다. TakeDamage는 들어오는 데미지에서 MagicResist / 2만큼을 차감합니다 (최소 1의 데미지를 입음).Archer—Agility(int)와Evasion(int) 필드를 가집니다. Attack은 Agility + 5를 데미지로 반환합니다. TakeDamage: 만약 Evasion > 10이면 데미지를 25% 감소시키고(정수 나눗셈), 그렇지 않으면 전체 데미지를 입습니다 (최소 1의 데미지를 입음).각 캐릭터에 대해 이름, 체력, 그리고 특정 스탯 값을 인자로 받는 생성자
NewWarrior,NewMage,NewArcher를 포함하세요.abilities.go: 교체 가능한 능력을 사용하여 전략 패턴을 구현합니다:Power(int)를 가진FireSpell구조체.Use()는Casts fireball for [Power] damage를 반환합니다.Power(int)를 가진IceSpell구조체.Use()는Casts ice shard for [Power] damage를 반환합니다.Multiplier(int)를 가진PowerStrike구조체.Use()는Performs power strike at [Multiplier]x damage를 반환합니다.Mage 구조체에
ability필드(Ability타입)를 추가하고,SetAbility(a Ability)와UseAbility() string메서드를 구현하세요. UseAbility는 ability가 nil인 경우No ability set을 반환합니다.main.go: 캐릭터를 생성하고 전투 메커니즘을 시연합니다.캐릭터 유형(
warrior,mage, 또는archer)을 읽은 다음, 캐릭터의 이름, 체력, 유형별 스탯을 읽습니다. 적절한 캐릭터를 생성하세요.그 다음 액션의 개수를 읽습니다. 각 액션은 다음 중 하나입니다:
status— 캐릭터의 상태를 출력합니다.attack—[Name] attacks for [damage] damage를 출력합니다.defend [amount]— 데미지를 적용하고[Name] takes [actual] damage를 출력합니다.setability [type] [power]— 마법사 전용으로, 능력을 설정합니다 (type은fire,ice, 또는strike).useability— 마법사 전용으로, 능력의 Use() 결과를 출력합니다.
다음과 같은 입력이 제공됩니다:
- 캐릭터 유형, 이름, 체력, 그리고 유형별 스탯
- 액션의 개수, 그리고 각 줄에 하나씩의 액션
예를 들어, 다음과 같이 입력되면:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100출력은 다음과 같아야 합니다:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage그리고 다음과 같이 입력되면:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40출력은 다음과 같아야 합니다:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage그리고 다음과 같이 입력되면:
archer
Legolas
90
18
15
4
status
attack
defend 24
status출력은 다음과 같아야 합니다:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP구조체 임베딩을 통해 각 캐릭터 유형이 기본 Character 필드와 메서드를 어떻게 공유하는지, 그리고 인터페이스가 어떻게 모든 Attacker가 데미지를 입히고 모든 Defender가 데미지를 받을 수 있는 다형성 전투를 가능하게 하는지 주목하세요. 전략 패턴은 Mage의 교체 가능한 능력에서 빛을 발합니다. 동일한 캐릭터가 핵심 구현을 변경하지 않고도 서로 다른 주문을 사용할 수 있습니다!
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 캐릭터 타입 읽기
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// 캐릭터 이름 읽기
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// 체력(health) 읽기
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 첫 번째 타입별 스탯 읽기
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 두 번째 타입별 스탯 읽기
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: charType에 따라 적절한 캐릭터 생성하기
// 변수 사용: charType, name, health, stat1, stat2
// 전사(warrior)의 경우: stat1 = Strength, stat2 = Armor
// 마법사(mage)의 경우: stat1 = Intelligence, stat2 = MagicResist
// 궁수(archer)의 경우: stat1 = Agility, stat2 = Evasion
// 액션 횟수 읽기
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// 각 액션 처리하기
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: 각 액션 타입 처리하기:
// - "status": 캐릭터의 Status() 출력
// - "attack": "[Name] attacks for [damage] damage" 출력
// - "defend [amount]": 데미지를 적용하고 "[Name] takes [actual] damage" 출력
// - "setability [type] [power]": 마법사의 경우, 능력(fire, ice, 또는 strike) 설정
// - "useability": 마법사의 경우, UseAbility() 결과 출력
_ = action // 구현 시 이 라인을 제거하세요
_ = fmt.Println // 구현 시 이 라인을 제거하세요
}
}
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서