adventofcode/2018/day15/cave.go

154 lines
2.7 KiB
Go

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
}