Menu
Coddy logo textTech

Sistema de personajes de juego

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 107 de 107.

challenge icon

Desafío

Fácil

¡Vamos a construir un Sistema de Personajes de Juego que reúna la composición de structs (struct embedding), interfaces y el patrón Strategy para crear mecánicas de combate flexibles! Diseñarás diferentes tipos de personajes que pueden atacar y defender de formas únicas, con habilidades que se pueden intercambiar en tiempo de ejecución.

Organizarás tu código en cinco archivos:

  • character.go: Define un struct base Character con campos comunes: Name (string), Health (int) y MaxHealth (int). Incluye un constructor NewCharacter y los métodos:
    • IsAlive() bool — devuelve true si la salud es mayor que 0
    • Status() string — devuelve el formato: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: Define las interfaces de combate que permiten el polimorfismo:
    • Interfaz Attacker con Attack() int — devuelve el daño infligido
    • Interfaz Defender con TakeDamage(damage int) int — aplica la reducción de daño y devuelve el daño real recibido
    • Interfaz Ability con Use() string — devuelve una descripción del efecto de la habilidad
  • characters.go: Crea tres tipos de personajes que embeban Character e implementen tanto Attacker como Defender:

    Warrior — tiene un campo Strength (int) y Armor (int). Attack devuelve Strength como daño. TakeDamage reduce el daño entrante por Armor (mínimo 1 de daño recibido).

    Mage — tiene campos Intelligence (int) y MagicResist (int). Attack devuelve Intelligence * 2 como daño. TakeDamage reduce el daño entrante por MagicResist / 2 (mínimo 1 de daño recibido).

    Archer — tiene campos Agility (int) y Evasion (int). Attack devuelve Agility + 5 como daño. TakeDamage: si Evasion > 10, reduce el daño en un 25% (división entera), de lo contrario recibe el daño completo (mínimo 1 de daño recibido).

    Incluye constructores para cada uno: NewWarrior, NewMage, NewArcher que reciban el nombre, la salud y sus valores de estadísticas específicos.

  • abilities.go: Implementa el patrón Strategy con habilidades intercambiables:

    Struct FireSpell con Power (int). Su Use() devuelve: Casts fireball for [Power] damage

    Struct IceSpell con Power (int). Su Use() devuelve: Casts ice shard for [Power] damage

    Struct PowerStrike con Multiplier (int). Su Use() devuelve: Performs power strike at [Multiplier]x damage

    Agrega un campo ability (de tipo Ability) al struct Mage, con los métodos SetAbility(a Ability) y UseAbility() string. UseAbility devuelve No ability set si la habilidad es nil.

  • main.go: Crea personajes y demuestra las mecánicas de combate.

    Lee el tipo de personaje (warrior, mage o archer), luego lee el nombre del personaje, la salud y las estadísticas específicas del tipo. Crea el personaje apropiado.

    Luego lee el número de acciones. Cada acción es una de las siguientes:

    • status — imprime el estado del personaje
    • attack — imprime [Name] attacks for [damage] damage
    • defend [amount] — aplica el daño e imprime [Name] takes [actual] damage
    • setability [type] [power] — solo para magos, establece la habilidad (el tipo es fire, ice o strike)
    • useability — solo para magos, imprime el resultado de Use() de la habilidad

Se proporcionarán las siguientes entradas:

  • Tipo de personaje, nombre, salud y estadísticas específicas del tipo
  • Número de acciones, luego cada acción en una línea separada

Por ejemplo, dado:

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

Tu salida debería ser:

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

Y dado:

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

Tu salida debería ser:

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

Y dado:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Tu salida debería ser:

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

Observa cómo la composición de structs permite que cada tipo de personaje comparta los campos y métodos base de Character, mientras que las interfaces permiten un combate polimórfico donde cualquier Attacker puede infligir daño y cualquier Defender puede recibirlo. El patrón Strategy brilla con las habilidades intercambiables del Mago: ¡el mismo personaje puede usar diferentes hechizos sin cambiar su implementación principal!

Pruébalo tú mismo

package main

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

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

	// Leer el tipo de personaje
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// Leer el nombre del personaje
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// Leer la salud
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Leer la primera estadística específica del tipo
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Leer la segunda estadística específica del tipo
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: Crear el personaje apropiado basado en charType
	// Usar variables: charType, name, health, stat1, stat2
	// Para warrior: stat1 = Strength, stat2 = Armor
	// Para mage: stat1 = Intelligence, stat2 = MagicResist
	// Para archer: stat1 = Agility, stat2 = Evasion

	// Leer el número de acciones
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Procesar cada acción
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: Manejar cada tipo de acción:
		// - "status": imprimir el Status() del personaje
		// - "attack": imprimir "[Name] attacks for [damage] damage"
		// - "defend [amount]": aplicar daño e imprimir "[Name] takes [actual] damage"
		// - "setability [type] [power]": para magos, establecer habilidad (fire, ice, o strike)
		// - "useability": para magos, imprimir el resultado de UseAbility()

		_ = action // Eliminar esta línea al implementar
		_ = fmt.Println // Eliminar esta línea al implementar
	}
}

Todas las lecciones de Programación Orientada a Objetos