Система игровых персонажей
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 107 из 107.
Задание
ЛегкоДавайте создадим систему игровых персонажей, объединяющую встраивание структур, интерфейсы и паттерн Strategy для создания гибкой боевой механики! Вы спроектируете разные типы персонажей, которые смогут уникальным образом атаковать и защищаться, а их способности можно будет заменять во время выполнения программы.
Вы организуете код в пяти файлах:
character.go: определите базовую структуруCharacterс общими полями:Name(string),Health(int) иMaxHealth(int). Добавьте конструкторNewCharacterи методы:IsAlive() bool: возвращает true, если здоровье больше 0Status() string: возвращает формат:[Name]: [Health]/[MaxHealth] HP
interfaces.go: определите боевые интерфейсы, обеспечивающие полиморфизм:- интерфейс
Attackerс методомAttack() int: возвращает нанесённый урон - интерфейс
Defenderс методомTakeDamage(damage int) int: применяет уменьшение урона и возвращает фактически полученный урон - интерфейс
Abilityс методомUse() string: возвращает описание эффекта способности
- интерфейс
characters.go: создайте три типа персонажей, которые встраиваютCharacterи реализуют оба интерфейса —AttackerиDefender:Warrior: имеет поляStrength(int) иArmor(int). Attack возвращает Strength в качестве урона. TakeDamage уменьшает входящий урон на значение Armor (минимальный полученный урон — 1).Mage: имеет поляIntelligence(int) иMagicResist(int). Attack возвращает Intelligence * 2 в качестве урона. TakeDamage уменьшает входящий урон на MagicResist / 2 (минимальный полученный урон — 1).Archer: имеет поляAgility(int) иEvasion(int). Attack возвращает Agility + 5 в качестве урона. TakeDamage: если Evasion > 10, уменьшает урон на 25% (с целочисленным делением), иначе персонаж получает полный урон (минимальный полученный урон — 1).Добавьте конструкторы для каждого типа:
NewWarrior,NewMage,NewArcher, принимающие имя, здоровье и значения специфичных для типа характеристик.abilities.go: реализуйте паттерн Strategy со сменными способностями:структура
FireSpellс полемPower(int). Её методUse()возвращает:Casts fireball for [Power] damageструктура
IceSpellс полемPower(int). Её методUse()возвращает:Casts ice shard for [Power] damageструктура
PowerStrikeс полемMultiplier(int). Её методUse()возвращает:Performs power strike at [Multiplier]x damageДобавьте в структуру Mage поле
abilityтипаAbility, а также методыSetAbility(a Ability)иUseAbility() string. UseAbility возвращаетNo ability set, если ability равно nil.main.go: создайте персонажей и продемонстрируйте боевую механику.Сначала прочитайте тип персонажа (
warrior,mageилиarcher), затем имя персонажа, здоровье и специфичные для типа характеристики. Создайте соответствующего персонажа.Затем прочитайте количество действий. Каждое действие является одной из следующих команд:
status: выведите статус персонажаattack: выведите[Name] attacks for [damage] damagedefend [amount]: примените урон и выведите[Name] takes [actual] damagesetability [type] [power]: только для магов — установите способность (type — этоfire,iceилиstrike)useability: только для магов — выведите результат Use() способности
Будут предоставлены следующие входные данные:
- Тип персонажа, имя, здоровье и специфичные для типа характеристики
- Количество действий, затем каждое действие в отдельной строке
Например, для следующих данных:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100Ваш вывод должен быть следующим:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageА для следующих данных:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Ваш вывод должен быть следующим:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damageА для следующих данных:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusВаш вывод должен быть следующим:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPОбратите внимание, как встраивание структур позволяет каждому типу персонажа совместно использовать базовые поля и методы Character, а интерфейсы обеспечивают полиморфный бой, в котором любой Attacker может наносить урон, а любой Defender — получать его. Паттерн Strategy особенно хорошо проявляет себя в сменных способностях Mage: один и тот же персонаж может использовать разные заклинания без изменения своей основной реализации!
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Прочитать тип персонажа
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// Прочитать имя персонажа
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// Прочитать здоровье
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Прочитать первую характеристику, специфичную для типа
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Прочитать вторую характеристику, специфичную для типа
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: Создать соответствующего персонажа на основе charType
// Использовать переменные: charType, name, health, stat1, stat2
// Для warrior: stat1 = Strength, stat2 = Armor
// Для mage: stat1 = Intelligence, stat2 = MagicResist
// Для archer: stat1 = Agility, stat2 = Evasion
// Прочитать количество действий
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Обработать каждое действие
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: Обработать каждый тип действия:
// - "status": вывести Status() персонажа
// - "attack": вывести "[Name] attacks for [damage] damage"
// - "defend [amount]": применить урон и вывести "[Name] takes [actual] damage"
// - "setability [type] [power]": для магов установить способность (fire, ice или strike)
// - "useability": для магов вывести результат UseAbility()
_ = action // Удалить эту строку при реализации
_ = fmt.Println // Удалить эту строку при реализации
}
}
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker PoolПотренируйтесь самостоятельно: Онлайн-компилятор Go