Untitled
unknown
plain_text
a month ago
10 kB
2
Indexable
package main
/*
Домашнее задание: Инвентарь персонажа
После первой лекции по ООП нужно доделать учебную систему инвентаря.
Что нужно сделать:
1. Самостоятельно добавить новый тип предмета Potion с нуля.
2. Для Potion нужно продумать поля, реализовать методы Use, GetName, GetWeight.
3. Сделать так, чтобы Potion тоже можно было сохранять и загружать.
4. Реализовать generic-функции Filter и Map.
5. Дописать метод Load у Inventory, чтобы он умел загружать все типы предметов.
6. В main показать работу нового типа предмета вместе с остальными.
Важно:
- обработку ошибок пока можно пропустить;
- тесты писать не нужно;
- цель домашки — потренировать интерфейсы, методы, структуры и generics.
*/
import (
"bufio"
"bytes"
"fmt"
"io"
"slices"
"strconv"
"strings"
)
type Item interface {
Use() string
GetName() string
GetWeight() float64
}
type Storable interface {
Serialize(w io.Writer)
Deserialize(r io.Reader)
}
type Weapon struct {
Name string
Damage int
Durability int
}
func (w *Weapon) Use() string {
if w.Durability <= 0 {
return w.Name + " сломано!"
}
w.Durability--
return fmt.Sprintf("Атаковали %s (%d урона)", w.Name, w.Damage)
}
func (w *Weapon) GetName() string {
return w.Name
}
func (w *Weapon) GetWeight() float64 {
return 2.5
}
func (w *Weapon) Serialize(wr io.Writer) {
_, _ = fmt.Fprintf(wr, "Weapon|%s|%d|%d", w.Name, w.Damage, w.Durability)
}
func (w *Weapon) Deserialize(r io.Reader) {
data, _ := io.ReadAll(r)
parts := strings.Split(strings.TrimSpace(string(data)), "|")
w.Name = parts[1]
w.Damage, _ = strconv.Atoi(parts[2])
w.Durability, _ = strconv.Atoi(parts[3])
}
type Armor struct {
Name string
Defense int
Weight float64
}
func (a *Armor) Use() string {
return fmt.Sprintf("Надели %s (+%d защиты)", a.Name, a.Defense)
}
func (a *Armor) GetName() string {
return a.Name
}
func (a *Armor) GetWeight() float64 {
return a.Weight
}
func (a *Armor) Serialize(wr io.Writer) {
_, _ = fmt.Fprintf(wr, "Armor|%s|%d|%f", a.Name, a.Defense, a.Weight)
}
func (a *Armor) Deserialize(r io.Reader) {
data, _ := io.ReadAll(r)
parts := strings.Split(strings.TrimSpace(string(data)), "|")
a.Name = parts[1]
a.Defense, _ = strconv.Atoi(parts[2])
a.Weight, _ = strconv.ParseFloat(parts[3], 64)
}
// TODO:
// 1. Добавить здесь структуру Potion.
// 2. Реализовать для неё все нужные методы.
// 3. Сделать так, чтобы Potion удовлетворял интерфейсам Item и Storable.
const glassSize = float64(0.1)
type WineColor int
const (
WineColorRed WineColor = iota
WineColorWhite
WineColorRose
WineColorOrange
)
type WineSweetness int
const (
WineSweetnessDry WineSweetness = iota
WineSweetnessSemiDry
WineSweetnessSemiSweet
WineSweetnessSweet
)
type WineRank string
const (
WineRankAOC WineRank = "AOC"
WineRankIGP WineRank = "IGP"
WineRankVDF WineRank = "VdF"
)
type Wine struct {
Name string
Color WineColor
Sweetness WineSweetness
ABV float64
Region string
Rank WineRank
Year uint
Volume float64
}
func (c WineColor) String() string {
names := [...]string{"Красное", "Белое", "Розовое", "Оранжевое"}
if c < WineColorRed || c > WineColorOrange {
return "Неизвестный цвет"
}
return names[c]
}
func (s WineSweetness) String() string {
names := [...]string{"Сухое", "Полусухое", "Полусладкое", "Сладкое"}
if s < WineSweetnessDry || s > WineSweetnessSweet {
return "Неопределенная сладость"
}
return names[s]
}
var rankNames = map[WineRank]string{
WineRankAOC: "Высшая категория (AOC)",
WineRankIGP: "Местное вино (IGP)",
WineRankVDF: "Столовое вино (VdF)",
}
func (r WineRank) String() string {
if val, ok := rankNames[r]; ok {
return val
}
return string(r)
}
type Potion struct {
Wine
}
func (p *Potion) Serialize(wr io.Writer) {
_, _ = fmt.Fprintf(wr, "Potion|%s|%d|%d|%.1f|%s|%s|%d|%.1f", p.Name, p.Color, p.Sweetness, p.ABV, p.Region, p.Rank, p.Year, p.Volume)
}
func (p *Potion) Deserialize(r io.Reader) {
data, _ := io.ReadAll(r)
parts := strings.Split(strings.TrimSpace(string(data)), "|")
// Пропускаем parts[0], так как там строка "Potion"
p.Name = parts[1]
colorVal, _ := strconv.Atoi(parts[2])
p.Color = WineColor(colorVal)
sweetVal, _ := strconv.Atoi(parts[3])
p.Sweetness = WineSweetness(sweetVal)
p.ABV, _ = strconv.ParseFloat(parts[4], 64)
p.Region = parts[5]
p.Rank = WineRank(parts[6])
yearVal, _ := strconv.Atoi(parts[7])
p.Year = uint(yearVal)
p.Volume, _ = strconv.ParseFloat(parts[8], 64)
}
func (p *Potion) Use() string {
if p.Volume < glassSize {
return p.Name + " кончилось!"
}
p.Volume -= glassSize
return fmt.Sprintf("Вы приняли %.1f %s (%s, %s). Эффект: %d%%", glassSize, p.Name, p.Color, p.Sweetness, int(p.ABV))
}
func (p *Potion) GetName() string {
return p.Name
}
func (p *Potion) GetWeight() float64 {
return p.Volume
}
func DescribeItem(i Item) string {
if i == nil {
return "Предмет отсутствует"
}
return fmt.Sprintf("%s (вес: %.1f)", i.GetName(), i.GetWeight())
}
func Filter[T any](items []T, predicate func(T) bool) []T {
result := make([]T, 0, len(items))
for item := range slices.Values(items) {
if predicate(item) {
result = append(result, item)
}
}
return result
}
func Map[T any, R any](items []T, transform func(T) R) []R {
result := make([]R, 0, len(items))
for item := range slices.Values(items) {
result = append(result, transform(item))
}
return result
}
func Find[T any](items []T, condition func(T) bool) (T, bool) {
for _, item := range items {
if condition(item) {
return item, true
}
}
var zero T
return zero, false
}
type Inventory struct {
Items []Item
}
func (inv *Inventory) AddItem(item Item) {
if item == nil {
return
}
inv.Items = append(inv.Items, item)
}
func (inv *Inventory) GetWeapons() []*Weapon {
weapons := Filter(inv.Items, func(item Item) bool {
_, ok := item.(*Weapon)
return ok
})
return Map(weapons, func(item Item) *Weapon {
return item.(*Weapon)
})
}
func (inv *Inventory) GetBrokenItems() []Item {
return Filter(inv.Items, func(item Item) bool {
switch v := item.(type) {
case *Weapon:
return v.Durability <= 0
case *Potion:
return v.Volume < glassSize
default:
return false
}
})
}
func (inv *Inventory) GetItemNames() []string {
return Map(inv.Items, func(item Item) string {
return item.GetName()
})
}
func (inv *Inventory) FindItemByName(name string) (Item, bool) {
return Find(inv.Items, func(item Item) bool {
return item.GetName() == name
})
}
func (inv *Inventory) Save(w io.Writer) {
for _, item := range inv.Items {
if storable, ok := item.(Storable); ok {
storable.Serialize(w)
_, _ = fmt.Fprintln(w)
}
}
}
func (inv *Inventory) Load(r io.Reader) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
if strings.TrimSpace(line) == "" {
continue
}
// TODO: дописать загрузку всех типов предметов
if strings.HasPrefix(line, "Weapon|") {
var w Weapon
w.Deserialize(strings.NewReader(line))
inv.AddItem(&w)
} else if strings.HasPrefix(line, "Armor|") {
var a Armor
a.Deserialize(strings.NewReader(line))
inv.AddItem(&a)
} else if strings.HasPrefix(line, "Potion|") {
var p Potion
p.Deserialize(strings.NewReader(line))
inv.AddItem(&p)
}
}
}
func main() {
inv := Inventory{}
sword := &Weapon{Name: "Меч", Damage: 10, Durability: 2}
armor := &Armor{Name: "Кираса", Defense: 7, Weight: 6.5}
abrau := &Potion{
Wine: Wine{
Name: "Абрау-Дюрсо Виктор Дравиньи",
Color: WineColorWhite,
Sweetness: WineSweetnessDry, // Брют
ABV: 12.0,
Region: "Краснодарский край (Абрау-Дюрсо)",
Rank: WineRankAOC,
Year: 2020,
Volume: 0.7,
},
}
inv.AddItem(sword)
inv.AddItem(armor)
inv.AddItem(abrau)
// TODO:
// 1. Создать Potion (или что то свое) и добавить его в инвентарь.
// 2. Показать его использование.
// 3. Проверить, что он сохраняется и загружается.
fmt.Println("=== Все предметы ===")
for _, item := range inv.Items {
fmt.Println("-", DescribeItem(item))
}
fmt.Println("\n=== Использование предметов ===")
fmt.Println(sword.Use())
fmt.Println(sword.Use())
fmt.Println(sword.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println(abrau.Use())
fmt.Println("\n=== Сохранение инвентаря ===")
var buf bytes.Buffer
inv.Save(&buf)
fmt.Println(buf.String())
fmt.Println("=== Загрузка инвентаря ===")
loaded := Inventory{}
loaded.Load(strings.NewReader(buf.String()))
fmt.Println("\n=== Имена предметов ===")
fmt.Println(loaded.GetItemNames())
fmt.Println("\n=== Сломанные предметы ===")
for _, item := range loaded.GetBrokenItems() {
fmt.Println("-", item.GetName())
}
}
Editor is loading...
Leave a Comment