package main import ( "fmt" "sort" "strings" ) type Cave struct { Units SortableUnits Map Map } const ( KindSpace = 1 << iota KindElf KindGoblin KindWall KindHighlight ) var KindRunes = map[int]rune{ KindSpace: '.', KindElf: 'E', KindGoblin: 'G', KindWall: '#', KindHighlight: '@', } var RuneKinds = map[rune]int{ '.': KindSpace, 'E': KindElf, 'G': KindGoblin, '#': KindWall, } func IsUnit(bit int) bool { return (KindElf|KindGoblin)&bit != 0 } func NewCave(input []string, elfPower int) *Cave { c := &Cave{} c.ParseMap(input, elfPower) return c } func (c *Cave) ParseMap(input []string, elfPower int) { m := make(Map) for y, row := range input { for x, col := range row { kind, ok := RuneKinds[col] if !ok { kind = KindWall } tile := &Tile{Kind: kind} if IsUnit(kind) { c.Units = append(c.Units, NewUnit(tile, kind, elfPower)) } m.SetTile(tile, x, y) } } c.Map = m } func (c Cave) PrintMap(highlight *Tile) { for y := 0; y < len(c.Map); y++ { var units []string for x := 0; x < len(c.Map[y]); x++ { t := c.Map.Tile(x, y) if t == highlight { fmt.Print(string(KindRunes[KindHighlight])) } else { fmt.Print(string(KindRunes[t.Kind])) } if t.Unit != nil { units = append(units, fmt.Sprintf("%c(%d)", KindRunes[t.Unit.Kind], t.Unit.Hitpoints)) } } if len(units) > 0 { fmt.Print(" ", strings.Join(units, ", ")) } fmt.Println() } } func (c Cave) PrintDistance(t *Tile) { distances, _ := c.Map.FindWalkableTiles(t) for y := 0; y < len(c.Map); y++ { for x := 0; x < len(c.Map[y]); x++ { curT := c.Map.Tile(x, y) if d, ok := distances[curT]; ok && curT != t { fmt.Print(d) } else { fmt.Print(string(KindRunes[curT.Kind])) } } fmt.Println() } } func (c Cave) Status() (int, bool) { var elves, goblins bool var hp int for _, u := range c.Units { if u.Hitpoints <= 0 { continue } if u.Kind == KindElf { elves = true } else { goblins = true } hp = hp + u.Hitpoints } return hp, elves && goblins } func (c *Cave) RemoveTheDead() { var newUnits SortableUnits for _, unit := range c.Units { if unit.Hitpoints > 0 { newUnits = append(newUnits, unit) } } c.Units = newUnits } func (c *Cave) RemoveUnit(u *Unit) { u.Tile.Kind = KindSpace u.Tile.Unit = nil u.Tile = nil } // Tick returns false if combat ended during the round, and whether or not an elf has died this round func (c *Cave) Tick(stopOnElfDeath bool) (bool, bool) { c.RemoveTheDead() sort.Sort(c.Units) for _, unit := range c.Units { if unit.Hitpoints <= 0 { continue } if !unit.Targets(c) { return false, false } unit.Move(c) if unit.Attack(c) && stopOnElfDeath { return false, true } } return true, false }