2018 Day 19 part 1 complete

This commit is contained in:
Brian Buller 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 var turnCount int
const ( const (
ClearScreen = "\033[H\033[2J" CLEAR_SCREEN = "\033[H\033[2J"
MaxInt = int(^uint(0) >> 1) MAX_INT = int(^uint(0) >> 1)
DIR_N = -1i DIR_N = -1i
DIR_E = 1 DIR_E = 1
@ -38,6 +38,7 @@ func main() {
func part1() { func part1() {
for { for {
// Sort the players on the field
charSlice = charSlice[0:0] charSlice = charSlice[0:0]
for i := 0; i < len(input); i++ { for i := 0; i < len(input); i++ {
pos := getPosFromInt(i) pos := getPosFromInt(i)
@ -47,8 +48,8 @@ func part1() {
} }
} }
sort.Sort(ByPos(charSlice)) sort.Sort(ByPos(charSlice))
fmt.Printf("= Turn %d =\n", turnCount)
printBattlefield() // Tick every player
for _, v := range charSlice { for _, v := range charSlice {
if char, ok := characters[*v]; ok { if char, ok := characters[*v]; ok {
if !char.tick() { if !char.tick() {
@ -56,11 +57,11 @@ func part1() {
} }
} }
} }
if true {
if false {
time.Sleep(time.Millisecond * 250) time.Sleep(time.Millisecond * 250)
printBattlefield()
fmt.Println()
} }
printBattlefield()
if checkBattleOver() { if checkBattleOver() {
break break
} }
@ -110,95 +111,63 @@ func (c *Character) hasEnemies() bool {
} }
func (c *Character) tick() bool { func (c *Character) tick() bool {
// If we're already in range of a target, don't look for a new one // Check if this character has any enemies on the field
if tPos, err := c.easiestAdjacentTarget(); err == nil { if !c.hasEnemies() {
c.attack(tPos) fmt.Println(c.string(), "is unopposed")
return c.hasEnemies() return false
} }
// If we have no open sides, we can't more // Now move/attack
if !c.hasOpenFlank() { if _, err := c.easiestAdjacentTarget(); err != nil {
fmt.Println(c.string(), "blocked in") // Ok, figure out a move
return true nxt, tgt := c.findMove()
} if nxt != nil {
// Otherwise, find a target fmt.Println(c.string(), "is moving to", *nxt, "(", *tgt, ")")
var opportunities []complex64 c.moveTo(*nxt)
for _, oppPos := range charSlice {
if v, ok := characters[*oppPos]; ok {
if v.tp != c.tp {
opportunities = append(opportunities, *oppPos)
}
} }
} }
/*
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 { if tPos, err := c.easiestAdjacentTarget(); err == nil {
c.attack(tPos) c.attack(tPos)
} }
return c.hasEnemies() 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 { func (c *Character) string() string {
return fmt.Sprintf("[%s%s:%d]", string(c.tp), getCoordString(c.pos), c.health) 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 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 { func (c *Character) moveTo(pos complex64) bool {
if getByte(pos) != '.' { if getByte(pos) != '.' {
return false return false
@ -287,8 +244,39 @@ func (c *Character) moveTo(pos complex64) bool {
return true 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() { func printBattlefield() {
//fmt.Print(ClearScreen) //fmt.Print(CLEAR_SCREEN)
for i := 0; i < len(input)/width; i++ { for i := 0; i < len(input)/width; i++ {
fmt.Println(string(input[i*width : (i+1)*width])) 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 // First check if p1 has _any_ possible moves
lowest := pathPoint{ lowest := pathPoint{
pos: 0 - 1i, pos: 0 - 1i,
count: MaxInt, count: MAX_INT,
} }
for _, v := range paths { for _, v := range paths {
for _, d := range []complex64{DIR_N, DIR_E, DIR_S, DIR_W} { 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) paths = append(paths, wrkPt)
} }
} }
if len(paths) != pathCount && lowest.count == MaxInt { if len(paths) != pathCount && lowest.count == MAX_INT {
return findMove(p1, paths) return findMove(p1, paths)
} }
// We hit the end, return the lowest part // We hit the end, return the lowest part

197
2018/day19/day19.go Normal file
View File

@ -0,0 +1,197 @@
package main
import (
"bufio"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func main() {
input := stdinToStringSlice()
part1(input)
}
func part1(input []string) {
vpc := NewState()
vpc.reg[0] = 1
if strings.HasPrefix(input[0], "#ip") {
// Binding the ip to a register...
pts := strings.Split(input[0], " ")
vpc.ipReg = Atoi(pts[1])
input = input[1:]
}
for i := 0; i < 1000000; i++ {
if vpc.ip >= len(input) {
break
}
inp := input[vpc.ip]
if op, cmd, err := vpc.parseCommand(inp); err == nil {
if vpc.ipReg > -1 {
vpc.reg[vpc.ipReg] = vpc.ip
}
vpc.run(op, cmd)
vpc.ip = vpc.reg[vpc.ipReg]
}
vpc.ip++
}
fmt.Println(vpc.string())
}
type State struct {
ip, ipReg int
reg [6]int
OpCodes map[string]func(a, b, c int)
}
func NewState() *State {
s := &State{
ip: 0,
ipReg: -1,
}
s.OpCodes = make(map[string]func(a, b, c int))
s.OpCodes["addr"] = s.addr
s.OpCodes["addi"] = s.addi
s.OpCodes["mulr"] = s.mulr
s.OpCodes["muli"] = s.muli
s.OpCodes["banr"] = s.banr
s.OpCodes["bani"] = s.bani
s.OpCodes["borr"] = s.borr
s.OpCodes["bori"] = s.bori
s.OpCodes["setr"] = s.setr
s.OpCodes["seti"] = s.seti
s.OpCodes["gtir"] = s.gtir
s.OpCodes["gtri"] = s.gtri
s.OpCodes["gtrr"] = s.gtrr
s.OpCodes["eqir"] = s.eqir
s.OpCodes["eqri"] = s.eqri
s.OpCodes["eqrr"] = s.eqrr
return s
}
func (s *State) parseCommand(inp string) (string, []int, error) {
var retOp string
var retCmd []int
pts := strings.Split(inp, " ")
if len(pts) != 4 {
return retOp, retCmd, errors.New("Invalid Command")
}
retOp = pts[0]
for _, v := range pts[1:] {
retCmd = append(retCmd, Atoi(v))
}
return retOp, retCmd, nil
}
func (s *State) string() string {
ret := fmt.Sprintf("ip=%d ", s.ip)
for i := 0; i < len(s.reg); i++ {
if i == s.ipReg {
ret = fmt.Sprintf("%s <%d> ", ret, s.reg[i])
} else {
ret = fmt.Sprintf("%s %d ", ret, s.reg[i])
}
}
return ret
}
func (s *State) run(op string, cmd []int) error {
if fn, ok := s.OpCodes[op]; !ok {
return errors.New(fmt.Sprintf("Invalid OpCode: %d", cmd[0]))
} else {
fn(cmd[0], cmd[1], cmd[2])
}
return nil
}
func (s *State) addr(r1, r2, r3 int) {
s.reg[r3] = s.reg[r1] + s.reg[r2]
}
func (s *State) addi(r1, v2, r3 int) {
s.reg[r3] = s.reg[r1] + v2
}
func (s *State) mulr(r1, r2, r3 int) {
s.reg[r3] = s.reg[r1] * s.reg[r2]
}
func (s *State) muli(r1, v2, r3 int) {
s.reg[r3] = s.reg[r1] * v2
}
func (s *State) banr(r1, r2, r3 int) {
s.reg[r3] = s.reg[r1] & s.reg[r2]
}
func (s *State) bani(r1, v2, r3 int) {
s.reg[r3] = s.reg[r1] & v2
}
func (s *State) borr(r1, r2, r3 int) {
s.reg[r3] = s.reg[r1] | s.reg[r2]
}
func (s *State) bori(r1, v2, r3 int) {
s.reg[r3] = s.reg[r1] | v2
}
func (s *State) setr(r1, r2, r3 int) {
s.reg[r3] = s.reg[r1]
}
func (s *State) seti(v1, v2, r3 int) {
s.reg[r3] = v1
}
func (s *State) gtir(v1, r2, r3 int) {
s.reg[r3] = boolToInt(v1 > s.reg[r2])
}
func (s *State) gtri(r1, v2, r3 int) {
s.reg[r3] = boolToInt(s.reg[r1] > v2)
}
func (s *State) gtrr(r1, r2, r3 int) {
s.reg[r3] = boolToInt(s.reg[r1] > s.reg[r2])
}
func (s *State) eqir(v1, r2, r3 int) {
s.reg[r3] = boolToInt(v1 == s.reg[r2])
}
func (s *State) eqri(r1, v2, r3 int) {
s.reg[r3] = boolToInt(s.reg[r1] == v2)
}
func (s *State) eqrr(r1, r2, r3 int) {
s.reg[r3] = boolToInt(s.reg[r1] == s.reg[r2])
}
func boolToInt(v bool) int {
if v {
return 1
}
return 0
}
func stdinToStringSlice() []string {
var input []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input = append(input, scanner.Text())
}
return input
}
func Atoi(i string) int {
var ret int
var err error
if ret, err = strconv.Atoi(i); err != nil {
log.Fatal("Invalid Atoi: " + i)
}
return ret
}

37
2018/day19/input Normal file
View File

@ -0,0 +1,37 @@
#ip 4
addi 4 16 4
seti 1 4 3
seti 1 3 5
mulr 3 5 1
eqrr 1 2 1
addr 1 4 4
addi 4 1 4
addr 3 0 0
addi 5 1 5
gtrr 5 2 1
addr 4 1 4
seti 2 9 4
addi 3 1 3
gtrr 3 2 1
addr 1 4 4
seti 1 6 4
mulr 4 4 4
addi 2 2 2
mulr 2 2 2
mulr 4 2 2
muli 2 11 2
addi 1 2 1
mulr 1 4 1
addi 1 7 1
addr 2 1 2
addr 4 0 4
seti 0 8 4
setr 4 3 1
mulr 1 4 1
addr 4 1 1
mulr 4 1 1
muli 1 14 1
mulr 1 4 1
addr 2 1 2
seti 0 3 0
seti 0 6 4

8
2018/day19/testinput Normal file
View File

@ -0,0 +1,8 @@
#ip 0
seti 5 0 1
seti 6 0 2
addi 0 1 0
addr 1 2 3
setr 1 0 0
seti 8 0 4
seti 9 0 5