Menu
Coddy logo textTech

Game Character System

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 107 of 107.

challenge icon

Challenge

Easy

Let'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 base Character struct with common fields: Name (string), Health (int), and MaxHealth (int). Include a NewCharacter constructor and methods:
    • IsAlive() bool — returns true if health is greater than 0
    • Status() string — returns the format: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: Define the combat interfaces that enable polymorphism:
    • Attacker interface with Attack() int — returns damage dealt
    • Defender interface with TakeDamage(damage int) int — applies damage reduction and returns actual damage taken
    • Ability interface with Use() string — returns a description of the ability effect
  • characters.go: Create three character types that embed Character and implement both Attacker and Defender:

    Warrior — has a Strength (int) and Armor (int) field. Attack returns Strength as damage. TakeDamage reduces incoming damage by Armor (minimum 1 damage taken).

    Mage — has Intelligence (int) and MagicResist (int) fields. Attack returns Intelligence * 2 as damage. TakeDamage reduces incoming damage by MagicResist / 2 (minimum 1 damage taken).

    Archer — has Agility (int) and Evasion (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, NewArcher that take name, health, and their specific stat values.

  • abilities.go: Implement the Strategy pattern with swappable abilities:

    FireSpell struct with Power (int). Its Use() returns: Casts fireball for [Power] damage

    IceSpell struct with Power (int). Its Use() returns: Casts ice shard for [Power] damage

    PowerStrike struct with Multiplier (int). Its Use() returns: Performs power strike at [Multiplier]x damage

    Add an ability field (of type Ability) to the Mage struct, with SetAbility(a Ability) and UseAbility() string methods. UseAbility returns No ability set if ability is nil.

  • main.go: Create characters and demonstrate combat mechanics.

    Read the character type (warrior, mage, or archer), 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 status
    • attack — print [Name] attacks for [damage] damage
    • defend [amount] — apply damage and print [Name] takes [actual] damage
    • setability [type] [power] — for mages only, set ability (type is fire, ice, or strike)
    • 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 100

Your output should be:

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

And given:

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

Your 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 damage

And given:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Your output should be:

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

Notice 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