2018 Day 19 part 1 complete

This commit is contained in:
2018-12-19 10:24:50 -06:00
parent 5cbc4fdad0
commit 9e2be89e23
4 changed files with 332 additions and 102 deletions

View File

@@ -20,8 +20,8 @@ var charSlice []*complex64
var turnCount int
const (
ClearScreen = "\033[H\033[2J"
MaxInt = int(^uint(0) >> 1)
CLEAR_SCREEN = "\033[H\033[2J"
MAX_INT = int(^uint(0) >> 1)
DIR_N = -1i
DIR_E = 1
@@ -38,6 +38,7 @@ func main() {
func part1() {
for {
// Sort the players on the field
charSlice = charSlice[0:0]
for i := 0; i < len(input); i++ {
pos := getPosFromInt(i)
@@ -47,8 +48,8 @@ func part1() {
}
}
sort.Sort(ByPos(charSlice))
fmt.Printf("= Turn %d =\n", turnCount)
printBattlefield()
// Tick every player
for _, v := range charSlice {
if char, ok := characters[*v]; ok {
if !char.tick() {
@@ -56,11 +57,11 @@ func part1() {
}
}
}
if false {
if true {
time.Sleep(time.Millisecond * 250)
printBattlefield()
fmt.Println()
}
printBattlefield()
if checkBattleOver() {
break
}
@@ -110,95 +111,63 @@ func (c *Character) hasEnemies() bool {
}
func (c *Character) tick() bool {
// If we're already in range of a target, don't look for a new one
if tPos, err := c.easiestAdjacentTarget(); err == nil {
c.attack(tPos)
return c.hasEnemies()
// Check if this character has any enemies on the field
if !c.hasEnemies() {
fmt.Println(c.string(), "is unopposed")
return false
}
// If we have no open sides, we can't more
if !c.hasOpenFlank() {
fmt.Println(c.string(), "blocked in")
return true
}
// Otherwise, find a target
var opportunities []complex64
for _, oppPos := range charSlice {
if v, ok := characters[*oppPos]; ok {
if v.tp != c.tp {
opportunities = append(opportunities, *oppPos)
}
// Now move/attack
if _, err := c.easiestAdjacentTarget(); err != nil {
// Ok, figure out a move
nxt, tgt := c.findMove()
if nxt != nil {
fmt.Println(c.string(), "is moving to", *nxt, "(", *tgt, ")")
c.moveTo(*nxt)
}
}
/*
var opportunities []complex64
for _, oppPos := range charSlice {
if v, ok := characters[*oppPos]; ok {
if v.tp != c.tp {
fmt.Println(c.string(), "checking:", v.string())
opportunities = append(opportunities, v.getOpenSides()...)
}
}
}
closestDistance := MaxInt
var closestPos complex64
for _, v := range opportunities {
if distance(c.pos, v) < closestDistance {
closestDistance = distance(c.pos, v)
closestPos = v
}
}
*/
// If there are no opportunities, we're done
if len(opportunities) == 0 {
fmt.Println(c.string(), "sees no opportunities")
return true
}
var bestOpportunities []*complex64
moves := make(map[complex64]complex64)
bestDist := MaxInt
for _, opp := range opportunities {
paths := append([]pathPoint{}, pathPoint{
pos: opp,
count: 0,
})
move, length := findMove(c.pos, paths)
if length < bestDist {
bestOpportunities = append([]*complex64{}, &opp)
for k := range moves {
delete(moves, k)
}
moves[opp] = move
bestDist = length
} else if length == bestDist {
bestOpportunities = append(bestOpportunities, &opp)
moves[opp] = move
}
}
sort.Sort(ByPos(bestOpportunities))
//paths := append([]pathPoint{}, pathPoint{
// pos: closestPos,
// count: 0,
//})
//move := findMove(c.pos, paths)
if len(bestOpportunities) > 0 {
goWith := bestOpportunities[0]
fmt.Println(c.string(),
"moving to",
getCoordString(moves[*goWith]),
"(->",
getCoordString(*goWith),
bestDist, ")")
c.moveTo(*goWith)
} else {
fmt.Println(c.string(), "can't find a path")
}
// After moving, check for an attack
if tPos, err := c.easiestAdjacentTarget(); err == nil {
c.attack(tPos)
}
return c.hasEnemies()
}
// findMove returns the position this character should move to and the position
// that is it's ultimate target
func (c *Character) findMove() (*complex64, *complex64) {
var opps []*complex64
closestTargetDistance := MAX_INT
dist, path := findAllPaths(&c.pos)
for _, v := range characters {
if v.health <= 0 {
continue
}
if v.tp != c.tp {
for _, t := range v.getOpenSides() {
if d, ok := dist[t]; ok && d <= closestTargetDistance {
if d < closestTargetDistance {
closestTargetDistance = d
opps = []*complex64{}
}
opps = append(opps, &t)
}
}
}
sort.Sort(ByPos(opps))
if len(opps) > 0 {
t := opps[0]
curr := *t
for {
if pv == c.pos {
return &curr, t
}
curr = pv
}
}
}
return nil, nil
}
func (c *Character) string() string {
return fmt.Sprintf("[%s%s:%d]", string(c.tp), getCoordString(c.pos), c.health)
}
@@ -263,18 +232,6 @@ func (c *Character) hasOpenFlank() bool {
return false
}
func (c *Character) move(dir complex64) bool {
if getByte(c.pos+dir) != '.' {
return false
}
delete(characters, c.pos)
setByte(c.pos, '.')
c.pos += dir
characters[c.pos] = c
setByte(c.pos, c.tp)
return true
}
func (c *Character) moveTo(pos complex64) bool {
if getByte(pos) != '.' {
return false
@@ -287,8 +244,39 @@ func (c *Character) moveTo(pos complex64) bool {
return true
}
func getOpenSides(c complex64) []complex64 {
var ret []complex64
for _, d := range []complex64{DIR_N, DIR_E, DIR_S, DIR_W} {
if getByte(c+d) == '.' {
ret = append(ret, c+d)
}
}
return ret
}
// findAllPaths returns a map of all distances and a map of all paths
func findAllPaths(start *complex64) (map[complex64]int, map[complex64]complex64) {
all := []complex64{*start}
dist := map[complex64]int{*start: 0}
prev := map[complex64]complex64{*start: 0}
for len(all) > 0 {
c := all[0]
all = all[1:]
for _, n := range getOpenSides(c) {
if _, ok := dist[n]; !ok {
all = append(all, n)
dist[n] = dist[c] + 1
prev[n] = c
}
}
}
return dist, prev
}
func printBattlefield() {
//fmt.Print(ClearScreen)
//fmt.Print(CLEAR_SCREEN)
for i := 0; i < len(input)/width; i++ {
fmt.Println(string(input[i*width : (i+1)*width]))
}
@@ -357,7 +345,7 @@ func findMove(p1 complex64, paths []pathPoint) (complex64, int) {
// First check if p1 has _any_ possible moves
lowest := pathPoint{
pos: 0 - 1i,
count: MaxInt,
count: MAX_INT,
}
for _, v := range paths {
for _, d := range []complex64{DIR_N, DIR_E, DIR_S, DIR_W} {
@@ -390,7 +378,7 @@ func findMove(p1 complex64, paths []pathPoint) (complex64, int) {
paths = append(paths, wrkPt)
}
}
if len(paths) != pathCount && lowest.count == MaxInt {
if len(paths) != pathCount && lowest.count == MAX_INT {
return findMove(p1, paths)
}
// We hit the end, return the lowest part