Game Character System
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 107 of 107.
Challenge
EasyLet's build a Game Character System that brings together struct embedding, interfaces, and the Strategy pattern to create flexible combat mechanics! You'll design different character types that can attack and defend in unique ways, with abilities that can be swapped at runtime.
You'll organize your code across five files:
character.go: Define a baseCharacterstruct with common fields:Name(string),Health(int), andMaxHealth(int). Include aNewCharacterconstructor and methods:IsAlive() bool— returns true if health is greater than 0Status() string— returns the format:[Name]: [Health]/[MaxHealth] HP
interfaces.go: Define the combat interfaces that enable polymorphism:Attackerinterface withAttack() int— returns damage dealtDefenderinterface withTakeDamage(damage int) int— applies damage reduction and returns actual damage takenAbilityinterface withUse() string— returns a description of the ability effect
characters.go: Create three character types that embedCharacterand implement bothAttackerandDefender:Warrior— has aStrength(int) andArmor(int) field. Attack returns Strength as damage. TakeDamage reduces incoming damage by Armor (minimum 1 damage taken).Mage— hasIntelligence(int) andMagicResist(int) fields. Attack returns Intelligence * 2 as damage. TakeDamage reduces incoming damage by MagicResist / 2 (minimum 1 damage taken).Archer— hasAgility(int) andEvasion(int) fields. Attack returns Agility + 5 as damage. TakeDamage: if Evasion > 10, reduce damage by 25% (integer division), otherwise take full damage (minimum 1 damage taken).Include constructors for each:
NewWarrior,NewMage,NewArcherthat take name, health, and their specific stat values.abilities.go: Implement the Strategy pattern with swappable abilities:FireSpellstruct withPower(int). ItsUse()returns:Casts fireball for [Power] damageIceSpellstruct withPower(int). ItsUse()returns:Casts ice shard for [Power] damagePowerStrikestruct withMultiplier(int). ItsUse()returns:Performs power strike at [Multiplier]x damageAdd an
abilityfield (of typeAbility) to the Mage struct, withSetAbility(a Ability)andUseAbility() stringmethods. UseAbility returnsNo ability setif ability is nil.main.go: Create characters and demonstrate combat mechanics.Read the character type (
warrior,mage, orarcher), then read the character's name, health, and type-specific stats. Create the appropriate character.Then read the number of actions. Each action is one of:
status— print the character's statusattack— print[Name] attacks for [damage] damagedefend [amount]— apply damage and print[Name] takes [actual] damagesetability [type] [power]— for mages only, set ability (type isfire,ice, orstrike)useability— for mages only, print the ability's Use() result
The following inputs will be provided:
- Character type, name, health, and type-specific stats
- Number of actions, then each action on a separate line
For example, given:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100Your output should be:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageAnd given:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Your output should be:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damageAnd given:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusYour output should be:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPNotice how struct embedding lets each character type share the base Character fields and methods, while interfaces enable polymorphic combat where any Attacker can deal damage and any Defender can receive it. The Strategy pattern shines with the Mage's swappable abilities—the same character can use different spells without changing its core implementation!
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Read character type
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// Read character name
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// Read health
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Read first type-specific stat
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Read second type-specific stat
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: Create the appropriate character based on charType
// Use variables: charType, name, health, stat1, stat2
// For warrior: stat1 = Strength, stat2 = Armor
// For mage: stat1 = Intelligence, stat2 = MagicResist
// For archer: stat1 = Agility, stat2 = Evasion
// Read number of actions
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Process each action
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: Handle each action type:
// - "status": print character's Status()
// - "attack": print "[Name] attacks for [damage] damage"
// - "defend [amount]": apply damage and print "[Name] takes [actual] damage"
// - "setability [type] [power]": for mages, set ability (fire, ice, or strike)
// - "useability": for mages, print UseAbility() result
_ = action // Remove this line when implementing
_ = fmt.Println // Remove this line when implementing
}
}
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool