diff --git a/2018/day15/day15.go b/2018/day15/day15.go index d7aae8d..eec6510 100644 --- a/2018/day15/day15.go +++ b/2018/day15/day15.go @@ -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 diff --git a/2018/day19/day19.go b/2018/day19/day19.go new file mode 100644 index 0000000..75f5009 --- /dev/null +++ b/2018/day19/day19.go @@ -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 +} diff --git a/2018/day19/input b/2018/day19/input new file mode 100644 index 0000000..df1e0ad --- /dev/null +++ b/2018/day19/input @@ -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 diff --git a/2018/day19/testinput b/2018/day19/testinput new file mode 100644 index 0000000..d7d2a21 --- /dev/null +++ b/2018/day19/testinput @@ -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