Menu
Coddy logo textTech

게임 캐릭터 시스템

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 107번째.

challenge icon

챌린지

쉬움

구조체 임베딩, 인터페이스, Strategy 패턴을 결합하여 유연한 전투 메커니즘을 만드는 게임 캐릭터 시스템을 구축해 봅시다! 런타임에 교체할 수 있는 능력과 함께, 고유한 방식으로 공격하고 방어할 수 있는 다양한 캐릭터 유형을 설계하게 됩니다.

코드를 다섯 개의 파일로 구성합니다:

  • character.go: 공통 Fields인 Name (string), Health (int), MaxHealth (int)를 포함하는 기본 Character 구조체를 Define합니다. NewCharacter 생성자와 다음 메서드를 포함합니다:
    • IsAlive() bool: Health가 0보다 크면 true를 반환합니다
    • Status() string: 다음 형식으로 반환합니다: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: 다형성을 가능하게 하는 전투 인터페이스를 Define합니다:
    • Attacker 인터페이스와 Attack() int: dealt damage를 반환합니다
    • Defender 인터페이스와 TakeDamage(damage int) int: damage reduction을 적용하고 actual damage taken을 반환합니다
    • Ability 인터페이스와 Use() string: ability effect에 대한 description을 반환합니다
  • characters.go: Character를 임베드하고 AttackerDefender를 모두 구현하는 세 가지 character 유형을 Create합니다:

    Warrior: Strength (int)와 Armor (int) Fields를 가집니다. Attack은 Strength를 damage로 반환합니다. TakeDamage는 들어오는 damage를 Armor만큼 줄입니다 (최소 1 damage taken).

    Mage: Intelligence (int)와 MagicResist (int) Fields를 가집니다. Attack은 Intelligence * 2를 damage로 반환합니다. TakeDamage는 들어오는 damage를 MagicResist / 2만큼 줄입니다 (최소 1 damage taken).

    Archer: Agility (int)와 Evasion (int) Fields를 가집니다. Attack은 Agility + 5를 damage로 반환합니다. TakeDamage: Evasion > 10이면 damage를 25% 줄이고 (정수 나눗셈), 그렇지 않으면 전체 damage를 받습니다 (최소 1 damage taken).

    각 유형에 대한 생성자도 포함합니다: name, health 및 각 유형의 특정 stat 값을 받는 NewWarrior, NewMage, NewArcher입니다.

  • abilities.go: 교체 가능한 ability를 사용하여 Strategy 패턴을 구현합니다:

    FireSpell 구조체에 Power (int)를 포함합니다. Use()는 다음을 반환합니다: Casts fireball for [Power] damage

    IceSpell 구조체에 Power (int)를 포함합니다. Use()는 다음을 반환합니다: Casts ice shard for [Power] damage

    PowerStrike 구조체에 Multiplier (int)를 포함합니다. Use()는 다음을 반환합니다: Performs power strike at [Multiplier]x damage

    Mage 구조체에 Ability 타입의 ability Field를 추가하고, SetAbility(a Ability)UseAbility() string 메서드를 추가합니다. UseAbility는 ability가 nil이면 No ability set을 반환합니다.

  • main.go: character를 Create하고 전투 메커니즘을 시연합니다.

    character 유형(warrior, mage 또는 archer)을 읽은 다음 character의 name, health 및 유형별 stats를 읽습니다. 적절한 character를 Create합니다.

    그런 다음 action의 수를 읽습니다. 각 action은 다음 중 하나입니다:

    • status: character의 status를 출력합니다
    • attack: [Name] attacks for [damage] damage를 출력합니다
    • defend [amount]: damage를 적용하고 [Name] takes [actual] damage를 출력합니다
    • setability [type] [power]: mage만 사용할 수 있으며, ability를 설정합니다 (type은 fire, ice 또는 strike입니다)
    • useability: mage만 사용할 수 있으며, ability의 Use() 결과를 출력합니다

다음 입력이 제공됩니다:

  • Character type, name, health 및 type-specific stats
  • Number of actions, 그다음 각 action이 별도의 줄에 제공됩니다

예를 들어 다음이 주어졌을 때:

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 유형이 기본 Character의 Fields와 메서드를 공유할 수 있고, 인터페이스를 사용하면 어떤 Attacker든 damage를 가하고 어떤 Defender든 damage를 받을 수 있는 다형적 전투가 가능해진다는 점에 주목하세요. Strategy 패턴은 Mage의 교체 가능한 ability에서 특히 빛을 발합니다. 동일한 character가 핵심 구현을 변경하지 않고도 서로 다른 주문을 사용할 수 있습니다!

직접 해보기

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())

	// 체력 읽기
	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 // 구현할 때 이 줄을 제거하세요
	}
}

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Go 컴파일러