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.
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 baseCharactercon campos comunes:Name(string),Health(int) yMaxHealth(int). Incluye un constructorNewCharactery los métodos:IsAlive() bool— devuelve true si la salud es mayor que 0Status() string— devuelve el formato:[Name]: [Health]/[MaxHealth] HP
interfaces.go: Define las interfaces de combate que permiten el polimorfismo:- Interfaz
AttackerconAttack() int— devuelve el daño infligido - Interfaz
DefenderconTakeDamage(damage int) int— aplica la reducción de daño y devuelve el daño real recibido - Interfaz
AbilityconUse() string— devuelve una descripción del efecto de la habilidad
- Interfaz
characters.go: Crea tres tipos de personajes que embebanCharactere implementen tantoAttackercomoDefender:Warrior— tiene un campoStrength(int) yArmor(int). Attack devuelve Strength como daño. TakeDamage reduce el daño entrante por Armor (mínimo 1 de daño recibido).Mage— tiene camposIntelligence(int) yMagicResist(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 camposAgility(int) yEvasion(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,NewArcherque reciban el nombre, la salud y sus valores de estadísticas específicos.abilities.go: Implementa el patrón Strategy con habilidades intercambiables:Struct
FireSpellconPower(int). SuUse()devuelve:Casts fireball for [Power] damageStruct
IceSpellconPower(int). SuUse()devuelve:Casts ice shard for [Power] damageStruct
PowerStrikeconMultiplier(int). SuUse()devuelve:Performs power strike at [Multiplier]x damageAgrega un campo
ability(de tipoAbility) al struct Mage, con los métodosSetAbility(a Ability)yUseAbility() string. UseAbility devuelveNo ability setsi la habilidad es nil.main.go: Crea personajes y demuestra las mecánicas de combate.Lee el tipo de personaje (
warrior,mageoarcher), 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 personajeattack— imprime[Name] attacks for [damage] damagedefend [amount]— aplica el daño e imprime[Name] takes [actual] damagesetability [type] [power]— solo para magos, establece la habilidad (el tipo esfire,iceostrike)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 100Tu salida debería ser:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageY dado:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Tu 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 damageY dado:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusTu salida debería ser:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPObserva 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
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool