Menu
Coddy logo textTech

Spielcharakter-System

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 107 von 107.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Spiel-Charakter-System entwickeln, das Struct-Einbettung, Interfaces und das Strategy-Pattern kombiniert, um flexible Kampfmechaniken zu erstellen! Sie werden verschiedene Charaktertypen entwerfen, die auf einzigartige Weise angreifen und verteidigen können, mit Fähigkeiten, die zur Laufzeit ausgetauscht werden können.

Sie werden Ihren Code in fünf Dateien organisieren:

  • character.go: Definieren Sie ein Basis-Struct Character mit den gemeinsamen Feldern: Name (string), Health (int) und MaxHealth (int). Fügen Sie einen NewCharacter-Konstruktor und folgende Methoden hinzu:
    • IsAlive() bool — gibt true zurück, wenn die Gesundheit größer als 0 ist
    • Status() string — gibt das Format zurück: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: Definieren Sie die Kampf-Interfaces, die Polymorphismus ermöglichen:
    • Attacker-Interface mit Attack() int — gibt den verursachten Schaden zurück
    • Defender-Interface mit TakeDamage(damage int) int — wendet Schadensreduzierung an und gibt den tatsächlich erlittenen Schaden zurück
    • Ability-Interface mit Use() string — gibt eine Beschreibung des Fähigkeitseffekts zurück
  • characters.go: Erstellen Sie drei Charaktertypen, die Character einbetten und sowohl Attacker als auch Defender implementieren:

    Warrior — hat ein Strength (int) und ein Armor (int) Feld. Attack gibt Strength als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um Armor (mindestens 1 erlittener Schaden).

    Mage — hat Intelligence (int) und MagicResist (int) Felder. Attack gibt Intelligence * 2 als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um MagicResist / 2 (mindestens 1 erlittener Schaden).

    Archer — hat Agility (int) und Evasion (int) Felder. Attack gibt Agility + 5 als Schaden zurück. TakeDamage: Wenn Evasion > 10, reduziere den Schaden um 25% (Ganzzahldivision), andernfalls nimm den vollen Schaden (mindestens 1 erlittener Schaden).

    Fügen Sie Konstruktoren für jeden Typ hinzu: NewWarrior, NewMage, NewArcher, die Name, Gesundheit und ihre spezifischen Statuswerte entgegennehmen.

  • abilities.go: Implementieren Sie das Strategy-Pattern mit austauschbaren Fähigkeiten:

    FireSpell-Struct mit Power (int). Seine Use()-Methode gibt zurück: Casts fireball for [Power] damage

    IceSpell-Struct mit Power (int). Seine Use()-Methode gibt zurück: Casts ice shard for [Power] damage

    PowerStrike-Struct mit Multiplier (int). Seine Use()-Methode gibt zurück: Performs power strike at [Multiplier]x damage

    Fügen Sie dem Mage-Struct ein ability-Feld (vom Typ Ability) hinzu, mit den Methoden SetAbility(a Ability) und UseAbility() string. UseAbility gibt No ability set zurück, wenn ability nil ist.

  • main.go: Erstellen Sie Charaktere und demonstrieren Sie die Kampfmechaniken.

    Lesen Sie den Charaktertyp (warrior, mage oder archer), lesen Sie dann den Namen des Charakters, die Gesundheit und die typspezifischen Statuswerte. Erstellen Sie den entsprechenden Charakter.

    Lesen Sie dann die Anzahl der Aktionen. Jede Aktion ist eine der folgenden:

    • status — gibt den Status des Charakters aus
    • attack — gibt [Name] attacks for [damage] damage aus
    • defend [amount] — wendet Schaden an und gibt [Name] takes [actual] damage aus
    • setability [type] [power] — nur für Magier, setzt die Fähigkeit (Typ ist fire, ice oder strike)
    • useability — nur für Magier, gibt das Ergebnis von Use() der Fähigkeit aus

Die folgenden Eingaben werden bereitgestellt:

  • Charaktertyp, Name, Gesundheit und typspezifische Statuswerte
  • Anzahl der Aktionen, dann jede Aktion in einer separaten Zeile

Zum Beispiel, gegeben:

warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100

Sollte Ihre Ausgabe wie folgt aussehen:

Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage

Und gegeben:

mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40

Sollte Ihre Ausgabe wie folgt aussehen:

Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage

Und gegeben:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Sollte Ihre Ausgabe wie folgt aussehen:

Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP

Beachten Sie, wie die Struct-Einbettung es jedem Charaktertyp ermöglicht, die Felder und Methoden des Basis-Characters zu teilen, während Interfaces einen polymorphen Kampf ermöglichen, bei dem jeder Attacker Schaden verursachen und jeder Defender Schaden empfangen kann. Das Strategy-Pattern glänzt bei den austauschbaren Fähigkeiten des Magiers – derselbe Charakter kann verschiedene Zaubersprüche verwenden, ohne seine Kernimplementierung zu ändern!

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Charaktertyp lesen
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// Charakternamen lesen
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// Lebenspunkte lesen
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Ersten typspezifischen Wert lesen
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Zweiten typspezifischen Wert lesen
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: Erstelle den entsprechenden Charakter basierend auf charType
	// Verwende die Variablen: charType, name, health, stat1, stat2
	// Für warrior: stat1 = Strength, stat2 = Armor
	// Für mage: stat1 = Intelligence, stat2 = MagicResist
	// Für archer: stat1 = Agility, stat2 = Evasion

	// Anzahl der Aktionen lesen
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Jede Aktion verarbeiten
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: Behandle jeden Aktionstyp:
		// - "status": Status() des Charakters ausgeben
		// - "attack": "[Name] attacks for [damage] damage" ausgeben
		// - "defend [amount]": Schaden anwenden und "[Name] takes [actual] damage" ausgeben
		// - "setability [type] [power]": Für Magier, Fähigkeit setzen (fire, ice oder strike)
		// - "useability": Für Magier, Ergebnis von UseAbility() ausgeben

		_ = action // Diese Zeile bei der Implementierung entfernen
		_ = fmt.Println // Diese Zeile bei der Implementierung entfernen
	}
}

Alle Lektionen in Objektorientierte Programmierung