mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-22 05:51:16 +00:00
Add solution for Day 17
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
41352399f9
commit
84e8a9afa0
3 changed files with 435 additions and 0 deletions
413
day17.go
Normal file
413
day17.go
Normal file
|
@ -0,0 +1,413 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type day17TileType rune
|
||||
|
||||
const (
|
||||
day17TileTypeNewline day17TileType = 10
|
||||
day17TileTypeRobotDown day17TileType = 'v'
|
||||
day17TileTypeRobotLeft day17TileType = '<'
|
||||
day17TileTypeRobotLost day17TileType = 'X'
|
||||
day17TileTypeRobotRight day17TileType = '>'
|
||||
day17TileTypeRobotUp day17TileType = '^'
|
||||
day17TileTypeScaffold day17TileType = 35 // #
|
||||
day17TileTypeSpace day17TileType = 46 // .
|
||||
)
|
||||
|
||||
type day17Tile struct {
|
||||
X, Y int64
|
||||
Type day17TileType
|
||||
}
|
||||
|
||||
type day17Grid map[string]*day17Tile
|
||||
|
||||
func (d day17Grid) bounds() (minX, minY, maxX, maxY int64) {
|
||||
minX, minY = math.MaxInt64, math.MaxInt64
|
||||
|
||||
for _, t := range d {
|
||||
if t.X < minX {
|
||||
minX = t.X
|
||||
}
|
||||
if t.Y < minY {
|
||||
minY = t.Y
|
||||
}
|
||||
if t.X > maxX {
|
||||
maxX = t.X
|
||||
}
|
||||
if t.Y > maxY {
|
||||
maxY = t.Y
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d day17Grid) findCurrentPosition() (int64, int64, day17TileType) {
|
||||
for _, t := range d {
|
||||
if t.Type == day17TileTypeRobotDown || t.Type == day17TileTypeRobotLeft || t.Type == day17TileTypeRobotRight || t.Type == day17TileTypeRobotUp {
|
||||
return t.X, t.Y, t.Type
|
||||
}
|
||||
}
|
||||
return -1, -1, day17TileTypeRobotLost
|
||||
}
|
||||
|
||||
func (d day17Grid) findPath() []int64 {
|
||||
var (
|
||||
count int64
|
||||
directives []int64
|
||||
x, y, direction = d.findCurrentPosition()
|
||||
)
|
||||
|
||||
move := func() {
|
||||
count++
|
||||
|
||||
switch direction {
|
||||
case day17TileTypeRobotDown:
|
||||
y += 1
|
||||
case day17TileTypeRobotLeft:
|
||||
x -= 1
|
||||
case day17TileTypeRobotRight:
|
||||
x += 1
|
||||
case day17TileTypeRobotUp:
|
||||
y -= 1
|
||||
}
|
||||
}
|
||||
|
||||
opportunities := func() (forward, left, right bool) {
|
||||
var tileFwd, tileL, tileR *day17Tile
|
||||
|
||||
switch direction {
|
||||
case day17TileTypeRobotDown:
|
||||
tileFwd, tileL, tileR = d[d.mapKey(x, y+1)], d[d.mapKey(x+1, y)], d[d.mapKey(x-1, y)]
|
||||
case day17TileTypeRobotLeft:
|
||||
tileFwd, tileL, tileR = d[d.mapKey(x-1, y)], d[d.mapKey(x, y+1)], d[d.mapKey(x, y-1)]
|
||||
case day17TileTypeRobotRight:
|
||||
tileFwd, tileL, tileR = d[d.mapKey(x+1, y)], d[d.mapKey(x, y-1)], d[d.mapKey(x, y+1)]
|
||||
case day17TileTypeRobotUp:
|
||||
tileFwd, tileL, tileR = d[d.mapKey(x, y-1)], d[d.mapKey(x-1, y)], d[d.mapKey(x+1, y)]
|
||||
}
|
||||
|
||||
forward = tileFwd != nil && tileFwd.Type == day17TileTypeScaffold
|
||||
left = tileL != nil && tileL.Type == day17TileTypeScaffold
|
||||
right = tileR != nil && tileR.Type == day17TileTypeScaffold
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
turn := func(left bool) {
|
||||
var (
|
||||
dl = []day17TileType{day17TileTypeRobotUp, day17TileTypeRobotLeft, day17TileTypeRobotDown, day17TileTypeRobotRight}
|
||||
dr = []day17TileType{day17TileTypeRobotUp, day17TileTypeRobotRight, day17TileTypeRobotDown, day17TileTypeRobotLeft}
|
||||
nd []day17TileType
|
||||
)
|
||||
|
||||
if count > 0 {
|
||||
directives = append(directives, count)
|
||||
count = 0
|
||||
}
|
||||
|
||||
if left {
|
||||
nd = dl
|
||||
directives = append(directives, int64('L'))
|
||||
} else {
|
||||
nd = dr
|
||||
directives = append(directives, int64('R'))
|
||||
}
|
||||
|
||||
var cd int
|
||||
for i, t := range nd {
|
||||
if t == direction {
|
||||
cd = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if cd == len(nd)-1 {
|
||||
cd = -1
|
||||
}
|
||||
|
||||
direction = nd[cd+1]
|
||||
}
|
||||
|
||||
// Do the movement
|
||||
for {
|
||||
fw, l, r := opportunities()
|
||||
|
||||
if fw {
|
||||
move()
|
||||
continue
|
||||
}
|
||||
|
||||
if l {
|
||||
turn(true)
|
||||
continue
|
||||
}
|
||||
|
||||
if r {
|
||||
turn(false)
|
||||
continue
|
||||
}
|
||||
|
||||
// Nothing possible, must be the end
|
||||
directives = append(directives, count)
|
||||
break
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func (d day17Grid) isScaffoldIntersection(x, y int64) bool {
|
||||
t := d[d.mapKey(x, y)]
|
||||
|
||||
if t.Type == day17TileTypeSpace {
|
||||
// Space cannot be a scaffold intersection
|
||||
return false
|
||||
}
|
||||
|
||||
var count int64
|
||||
for _, at := range []*day17Tile{
|
||||
d[d.mapKey(x-1, y)],
|
||||
d[d.mapKey(x+1, y)],
|
||||
d[d.mapKey(x, y-1)],
|
||||
d[d.mapKey(x, y+1)],
|
||||
} {
|
||||
if at != nil && at.Type != day17TileTypeSpace {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
// One adjacent scaffold is a line
|
||||
// Two adjacent scaffolds is a curve or a straight line
|
||||
// Three or four adjacent scaffolds cannot be a line or curve, must be intersection
|
||||
return count >= 3
|
||||
}
|
||||
|
||||
func (d day17Grid) mapKey(x, y int64) string { return fmt.Sprintf("%d:%d", x, y) }
|
||||
|
||||
func (d day17Grid) print() {
|
||||
var minX, minY, maxX, maxY = d.bounds()
|
||||
|
||||
for y := minY; y <= maxY; y++ {
|
||||
for x := minX; x <= maxX; x++ {
|
||||
fmt.Printf("%s", string(d[d.mapKey(x, y)].Type))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func day17ReadGrid(code []int64) (day17Grid, error) {
|
||||
var (
|
||||
grid = make(day17Grid)
|
||||
out = make(chan int64)
|
||||
x, y int64
|
||||
)
|
||||
|
||||
go executeIntcode(code, nil, out)
|
||||
|
||||
for o := range out {
|
||||
switch day17TileType(o) {
|
||||
|
||||
case day17TileTypeNewline:
|
||||
y += 1
|
||||
x = 0
|
||||
|
||||
case day17TileTypeRobotDown, day17TileTypeRobotLeft, day17TileTypeRobotRight, day17TileTypeRobotUp:
|
||||
fallthrough // Not yet used
|
||||
|
||||
case day17TileTypeRobotLost:
|
||||
fallthrough // Not yet used
|
||||
|
||||
case day17TileTypeScaffold, day17TileTypeSpace:
|
||||
grid[grid.mapKey(x, y)] = &day17Tile{X: x, Y: y, Type: day17TileType(o)}
|
||||
x += 1
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("Invalid character %d", o)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return grid, nil
|
||||
}
|
||||
|
||||
func solveDay17Part1(inFile string) (int64, error) {
|
||||
rawCode, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read intcode")
|
||||
}
|
||||
|
||||
code, err := parseIntcode(strings.TrimSpace(string(rawCode)))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse intcode")
|
||||
}
|
||||
|
||||
grid, err := day17ReadGrid(code)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read grid")
|
||||
}
|
||||
|
||||
var (
|
||||
apSum int64
|
||||
minX, minY, maxX, maxY = grid.bounds()
|
||||
)
|
||||
|
||||
for y := minY; y <= maxY; y++ {
|
||||
for x := minX; x <= maxX; x++ {
|
||||
|
||||
if grid.isScaffoldIntersection(x, y) {
|
||||
apSum += x * y
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return apSum, nil
|
||||
}
|
||||
|
||||
func solveDay17Part2(inFile string) (int64, error) {
|
||||
rawCode, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read intcode")
|
||||
}
|
||||
|
||||
code, err := parseIntcode(strings.TrimSpace(string(rawCode)))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse intcode")
|
||||
}
|
||||
|
||||
grid, err := day17ReadGrid(code)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read grid")
|
||||
}
|
||||
|
||||
// Get the instructions to follow the whole path
|
||||
path := grid.findPath()
|
||||
|
||||
isMovementFunction := func(n int64) bool {
|
||||
return n == int64('A') || n == int64('B') || n == int64('C')
|
||||
}
|
||||
|
||||
feedSlice := func(in chan int64, s []int64) {
|
||||
for i, n := range s {
|
||||
if isMovementFunction(n) || n == int64('L') || n == int64('R') || n == int64('n') {
|
||||
in <- n
|
||||
} else {
|
||||
for _, c := range strconv.FormatInt(n, 10) {
|
||||
in <- int64(c)
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(s)-1 {
|
||||
in <- 10 // Newline to terminate
|
||||
} else {
|
||||
in <- 44 // Comma to delimit chars
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findSubmatches := func(haystack, needle []int64) []int64 {
|
||||
var matches []int64
|
||||
|
||||
for i := 0; i < len(haystack)-len(needle)+1; i++ {
|
||||
if reflect.DeepEqual(haystack[i:i+len(needle)], needle) {
|
||||
matches = append(matches, int64(i))
|
||||
i += len(needle) - 1
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
var (
|
||||
pattern [][]int64
|
||||
main = make([]int64, len(path))
|
||||
)
|
||||
copy(main, path)
|
||||
|
||||
for len(findSubmatches(main, []int64{int64('L')}))+len(findSubmatches(main, []int64{int64('R')})) > 0 {
|
||||
var start, length int64 = 0, 2
|
||||
|
||||
for isMovementFunction(main[start]) {
|
||||
start++
|
||||
}
|
||||
|
||||
for len(findSubmatches(main, main[start:start+length])) > 1 && !isMovementFunction(main[start+length-1]) {
|
||||
length += 2
|
||||
}
|
||||
|
||||
length -= 2 // Revert last addition as it caused trouble
|
||||
|
||||
var patternID = int64('A') + int64(len(pattern))
|
||||
pattern = append(pattern, main[start:start+length])
|
||||
|
||||
var (
|
||||
pos int64
|
||||
submatchStarts = findSubmatches(main, pattern[len(pattern)-1])
|
||||
tmpMain []int64
|
||||
)
|
||||
|
||||
for _, smStart := range submatchStarts {
|
||||
tmpMain = append(tmpMain, main[pos:smStart]...)
|
||||
tmpMain = append(tmpMain, patternID)
|
||||
pos = smStart + length
|
||||
}
|
||||
tmpMain = append(tmpMain, main[pos:]...)
|
||||
main = tmpMain
|
||||
}
|
||||
|
||||
if len(pattern) != 3 {
|
||||
return 0, errors.Errorf("Required more than 3 pattern: %d", len(pattern))
|
||||
}
|
||||
|
||||
// Feed program with new data
|
||||
var (
|
||||
in = make(chan int64, 1000) // I could calculate the length but I don't care
|
||||
out = make(chan int64)
|
||||
wg = new(sync.WaitGroup)
|
||||
)
|
||||
|
||||
// Feed main movement routine
|
||||
feedSlice(in, main)
|
||||
// Feed movement routines
|
||||
feedSlice(in, pattern[0]) // A
|
||||
feedSlice(in, pattern[1]) // B
|
||||
feedSlice(in, pattern[2]) // C
|
||||
// Answer "continuous video feed" question
|
||||
feedSlice(in, []int64{int64('n')})
|
||||
|
||||
code, err = parseIntcode(strings.TrimSpace(string(rawCode)))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse intcode")
|
||||
}
|
||||
|
||||
// Force the vacuum robot to wake up by changing the value in your
|
||||
// ASCII program at address 0 from 1 to 2.
|
||||
code[0] = 2
|
||||
|
||||
// Execute the program and throw away all but last output, we know
|
||||
// how the grid looks
|
||||
wg.Add(1)
|
||||
go executeIntcode(code, in, out)
|
||||
|
||||
var result int64
|
||||
go func() {
|
||||
for o := range out {
|
||||
result = o
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return result, nil
|
||||
}
|
1
day17_input.txt
Normal file
1
day17_input.txt
Normal file
|
@ -0,0 +1 @@
|
|||
1,330,331,332,109,4278,1101,0,1182,16,1102,1485,1,24,102,1,0,570,1006,570,36,102,1,571,0,1001,570,-1,570,1001,24,1,24,1105,1,18,1008,571,0,571,1001,16,1,16,1008,16,1485,570,1006,570,14,21102,1,58,0,1105,1,786,1006,332,62,99,21101,0,333,1,21101,0,73,0,1106,0,579,1101,0,0,572,1102,1,0,573,3,574,101,1,573,573,1007,574,65,570,1005,570,151,107,67,574,570,1005,570,151,1001,574,-64,574,1002,574,-1,574,1001,572,1,572,1007,572,11,570,1006,570,165,101,1182,572,127,1002,574,1,0,3,574,101,1,573,573,1008,574,10,570,1005,570,189,1008,574,44,570,1006,570,158,1105,1,81,21102,340,1,1,1105,1,177,21102,477,1,1,1106,0,177,21102,514,1,1,21101,0,176,0,1105,1,579,99,21102,184,1,0,1105,1,579,4,574,104,10,99,1007,573,22,570,1006,570,165,1002,572,1,1182,21101,0,375,1,21102,1,211,0,1105,1,579,21101,1182,11,1,21101,222,0,0,1106,0,979,21102,1,388,1,21101,0,233,0,1105,1,579,21101,1182,22,1,21101,244,0,0,1106,0,979,21101,401,0,1,21102,1,255,0,1105,1,579,21101,1182,33,1,21101,0,266,0,1106,0,979,21101,414,0,1,21101,277,0,0,1106,0,579,3,575,1008,575,89,570,1008,575,121,575,1,575,570,575,3,574,1008,574,10,570,1006,570,291,104,10,21101,0,1182,1,21102,1,313,0,1105,1,622,1005,575,327,1101,0,1,575,21102,1,327,0,1106,0,786,4,438,99,0,1,1,6,77,97,105,110,58,10,33,10,69,120,112,101,99,116,101,100,32,102,117,110,99,116,105,111,110,32,110,97,109,101,32,98,117,116,32,103,111,116,58,32,0,12,70,117,110,99,116,105,111,110,32,65,58,10,12,70,117,110,99,116,105,111,110,32,66,58,10,12,70,117,110,99,116,105,111,110,32,67,58,10,23,67,111,110,116,105,110,117,111,117,115,32,118,105,100,101,111,32,102,101,101,100,63,10,0,37,10,69,120,112,101,99,116,101,100,32,82,44,32,76,44,32,111,114,32,100,105,115,116,97,110,99,101,32,98,117,116,32,103,111,116,58,32,36,10,69,120,112,101,99,116,101,100,32,99,111,109,109,97,32,111,114,32,110,101,119,108,105,110,101,32,98,117,116,32,103,111,116,58,32,43,10,68,101,102,105,110,105,116,105,111,110,115,32,109,97,121,32,98,101,32,97,116,32,109,111,115,116,32,50,48,32,99,104,97,114,97,99,116,101,114,115,33,10,94,62,118,60,0,1,0,-1,-1,0,1,0,0,0,0,0,0,1,24,26,0,109,4,1201,-3,0,587,20102,1,0,-1,22101,1,-3,-3,21102,1,0,-2,2208,-2,-1,570,1005,570,617,2201,-3,-2,609,4,0,21201,-2,1,-2,1105,1,597,109,-4,2105,1,0,109,5,2102,1,-4,629,21001,0,0,-2,22101,1,-4,-4,21101,0,0,-3,2208,-3,-2,570,1005,570,781,2201,-4,-3,653,20102,1,0,-1,1208,-1,-4,570,1005,570,709,1208,-1,-5,570,1005,570,734,1207,-1,0,570,1005,570,759,1206,-1,774,1001,578,562,684,1,0,576,576,1001,578,566,692,1,0,577,577,21102,1,702,0,1106,0,786,21201,-1,-1,-1,1105,1,676,1001,578,1,578,1008,578,4,570,1006,570,724,1001,578,-4,578,21102,731,1,0,1106,0,786,1105,1,774,1001,578,-1,578,1008,578,-1,570,1006,570,749,1001,578,4,578,21101,0,756,0,1106,0,786,1105,1,774,21202,-1,-11,1,22101,1182,1,1,21101,774,0,0,1106,0,622,21201,-3,1,-3,1105,1,640,109,-5,2106,0,0,109,7,1005,575,802,21002,576,1,-6,21001,577,0,-5,1106,0,814,21101,0,0,-1,21102,0,1,-5,21102,1,0,-6,20208,-6,576,-2,208,-5,577,570,22002,570,-2,-2,21202,-5,57,-3,22201,-6,-3,-3,22101,1485,-3,-3,1202,-3,1,843,1005,0,863,21202,-2,42,-4,22101,46,-4,-4,1206,-2,924,21102,1,1,-1,1105,1,924,1205,-2,873,21101,35,0,-4,1105,1,924,2101,0,-3,878,1008,0,1,570,1006,570,916,1001,374,1,374,1202,-3,1,895,1101,0,2,0,2101,0,-3,902,1001,438,0,438,2202,-6,-5,570,1,570,374,570,1,570,438,438,1001,578,558,922,20101,0,0,-4,1006,575,959,204,-4,22101,1,-6,-6,1208,-6,57,570,1006,570,814,104,10,22101,1,-5,-5,1208,-5,49,570,1006,570,810,104,10,1206,-1,974,99,1206,-1,974,1101,1,0,575,21101,973,0,0,1106,0,786,99,109,-7,2105,1,0,109,6,21102,1,0,-4,21102,0,1,-3,203,-2,22101,1,-3,-3,21208,-2,82,-1,1205,-1,1030,21208,-2,76,-1,1205,-1,1037,21207,-2,48,-1,1205,-1,1124,22107,57,-2,-1,1205,-1,1124,21201,-2,-48,-2,1105,1,1041,21102,-4,1,-2,1106,0,1041,21102,1,-5,-2,21201,-4,1,-4,21207,-4,11,-1,1206,-1,1138,2201,-5,-4,1059,1202,-2,1,0,203,-2,22101,1,-3,-3,21207,-2,48,-1,1205,-1,1107,22107,57,-2,-1,1205,-1,1107,21201,-2,-48,-2,2201,-5,-4,1090,20102,10,0,-1,22201,-2,-1,-2,2201,-5,-4,1103,1202,-2,1,0,1106,0,1060,21208,-2,10,-1,1205,-1,1162,21208,-2,44,-1,1206,-1,1131,1105,1,989,21102,1,439,1,1106,0,1150,21102,1,477,1,1105,1,1150,21102,1,514,1,21102,1,1149,0,1106,0,579,99,21101,0,1157,0,1106,0,579,204,-2,104,10,99,21207,-3,22,-1,1206,-1,1138,1202,-5,1,1176,2101,0,-4,0,109,-6,2106,0,0,14,11,46,1,9,1,46,1,9,1,46,1,9,1,46,1,9,1,46,1,9,1,46,1,9,1,46,1,9,1,44,13,44,1,1,1,54,1,1,1,54,1,1,1,54,1,1,9,46,1,9,1,46,1,9,1,46,1,9,1,46,1,9,1,11,13,22,1,9,1,11,1,11,1,22,1,9,1,11,1,9,11,14,1,9,1,11,1,9,1,1,1,7,1,14,9,1,1,11,1,9,1,1,1,7,1,22,1,1,1,11,1,9,1,1,1,7,1,22,1,1,1,11,1,9,1,1,1,7,1,22,1,1,1,11,1,9,1,1,1,7,1,22,1,1,13,9,1,1,1,7,1,22,1,23,1,1,1,7,1,14,13,19,13,12,1,7,1,25,1,7,1,1,1,12,1,7,1,25,9,1,1,12,1,7,1,35,1,12,1,7,1,35,1,12,1,7,1,35,1,8,13,35,1,8,1,3,1,43,14,43,2,7,1,47,2,7,1,37,9,1,2,7,1,37,1,7,1,1,2,7,1,35,14,7,1,35,1,1,1,7,1,2,1,7,1,35,1,1,1,7,1,2,1,7,1,35,1,1,1,7,1,2,1,7,1,35,1,1,1,7,1,2,1,7,1,35,1,1,1,7,1,2,9,35,1,1,1,7,1,46,1,1,1,7,1,46,11,48,1,44,13,10
|
21
day17_test.go
Normal file
21
day17_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package aoc2019
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCalculateDay17_Part1(t *testing.T) {
|
||||
res, err := solveDay17Part1("day17_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 17 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 17 Part 1: %d", res)
|
||||
}
|
||||
|
||||
func TestCalculateDay17_Part2(t *testing.T) {
|
||||
res, err := solveDay17Part2("day17_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 17 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 17 Part 2: %d", res)
|
||||
}
|
Loading…
Reference in a new issue