Day 19 solution
I had some trouble with the logic of this puzzle in my head, and the visuals were too large to realistically render for me to spot-check my solution, so I kept getting part 2 wrong. I eventually realized what needed to be done to get the right answer, which was partially the realization that I needed to check +99 instead of +100, and partially that I probably didn't need to check the entire space, just the bounding points, at least for the initial filters. And then I optimized it with a bisect instead of a linear search. Always a good idea when you need to narrow a large range to a single point.
This commit is contained in:
146
days/19.go
Normal file
146
days/19.go
Normal file
@ -0,0 +1,146 @@
|
||||
package days
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
u "parnic.com/aoc2019/utilities"
|
||||
)
|
||||
|
||||
type Day19 struct {
|
||||
program u.IntcodeProgram
|
||||
}
|
||||
|
||||
func (d *Day19) Parse() {
|
||||
d.program = u.LoadIntcodeProgram("19p")
|
||||
}
|
||||
|
||||
func (d Day19) Num() int {
|
||||
return 19
|
||||
}
|
||||
|
||||
func (d *Day19) Part1() string {
|
||||
grid := make([][]bool, 50)
|
||||
for y := 0; y < len(grid); y++ {
|
||||
grid[y] = make([]bool, 50)
|
||||
}
|
||||
|
||||
count := int64(0)
|
||||
|
||||
for y := 0; y < 50; y++ {
|
||||
for x := 0; x < 50; x++ {
|
||||
d.program.Reset()
|
||||
d.program.RunIn(func(inputStep int) int64 {
|
||||
if inputStep == 1 {
|
||||
return int64(x)
|
||||
}
|
||||
|
||||
return int64(y)
|
||||
}, func(val int64, state u.IntcodeProgramState) {
|
||||
res := val == 1
|
||||
grid[y][x] = res
|
||||
if res {
|
||||
count++
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.Println("50x50 tractor view:")
|
||||
// for y := 0; y < len(grid); y++ {
|
||||
// for x := 0; x < len(grid[y]); x++ {
|
||||
// if grid[y][x] {
|
||||
// fmt.Print("█")
|
||||
// } else {
|
||||
// fmt.Print(" ")
|
||||
// }
|
||||
// }
|
||||
// fmt.Println()
|
||||
// }
|
||||
|
||||
return fmt.Sprintf("Points affected in 50x50 area: %s%d%s", u.TextBold, count, u.TextReset)
|
||||
}
|
||||
|
||||
func (d *Day19) Part2() string {
|
||||
f := func(x, y int) bool {
|
||||
ret := false
|
||||
d.program.Reset()
|
||||
d.program.RunIn(func(inputStep int) int64 {
|
||||
if inputStep == 1 {
|
||||
return int64(x)
|
||||
}
|
||||
return int64(y)
|
||||
}, func(val int64, state u.IntcodeProgramState) {
|
||||
ret = val == 1
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// find lower bound
|
||||
// this may not be necessary, but helps seed the bisect with a known-good lower bound
|
||||
startY := 0
|
||||
startX := 0
|
||||
for y := 1; startY == 0; y++ {
|
||||
for x := 0; x < 10*y; x++ {
|
||||
if f(x, y) {
|
||||
startY = y
|
||||
startX = x
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastGoodX := 0
|
||||
threshold := 1
|
||||
// add 100 to start y since we know it has to be a 100x100 square.
|
||||
// since we multiply x by 10,000 for the final result, that tells us y cannot be 10k+
|
||||
y := u.Bisect(startY+100, 9999, threshold, func(y int) bool {
|
||||
foundX := false
|
||||
for x := startX; ; x++ {
|
||||
// check top left
|
||||
if !f(x, y) {
|
||||
if !foundX {
|
||||
continue
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !foundX {
|
||||
foundX = true
|
||||
}
|
||||
|
||||
// check top right
|
||||
if !f(x+99, y) {
|
||||
return true
|
||||
}
|
||||
// check bottom left
|
||||
if !f(x, y+99) {
|
||||
continue
|
||||
}
|
||||
|
||||
// we believe the corners work, so run final validations on the full borders.
|
||||
// this may not be necessary, but i've seen some rows end up shorter than a
|
||||
// previous row because of the angle of the beam and our integer fidelity.
|
||||
// plus it's really not that much more expensive to do to be certain we're correct.
|
||||
for y2 := y; y2 < y+100; y2++ {
|
||||
// right wall
|
||||
if !f(x+99, y2) {
|
||||
return true
|
||||
}
|
||||
// left wall
|
||||
if !f(x, y2) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
lastGoodX = x
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
// since we invert our bisect success returns, we need to increment y to
|
||||
// tip back over into the "success" range
|
||||
y += threshold
|
||||
result := (lastGoodX * 10000) + y
|
||||
return fmt.Sprintf("Closest 100x100 square for the ship starts at %d,%d = %s%d%s", lastGoodX, y, u.TextBold, result, u.TextReset)
|
||||
}
|
1
inputs/19p.txt
Normal file
1
inputs/19p.txt
Normal file
@ -0,0 +1 @@
|
||||
109,424,203,1,21102,11,1,0,1105,1,282,21101,0,18,0,1105,1,259,2101,0,1,221,203,1,21102,1,31,0,1105,1,282,21102,1,38,0,1105,1,259,20102,1,23,2,21201,1,0,3,21102,1,1,1,21102,57,1,0,1106,0,303,2102,1,1,222,21002,221,1,3,20101,0,221,2,21101,0,259,1,21101,0,80,0,1105,1,225,21101,44,0,2,21102,91,1,0,1105,1,303,1202,1,1,223,21002,222,1,4,21102,259,1,3,21102,1,225,2,21102,225,1,1,21101,118,0,0,1106,0,225,21002,222,1,3,21101,163,0,2,21101,0,133,0,1106,0,303,21202,1,-1,1,22001,223,1,1,21102,148,1,0,1106,0,259,1202,1,1,223,20101,0,221,4,21001,222,0,3,21102,1,24,2,1001,132,-2,224,1002,224,2,224,1001,224,3,224,1002,132,-1,132,1,224,132,224,21001,224,1,1,21101,195,0,0,105,1,108,20207,1,223,2,21002,23,1,1,21102,-1,1,3,21102,1,214,0,1106,0,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,2101,0,-4,249,22102,1,-3,1,22101,0,-2,2,22101,0,-1,3,21102,250,1,0,1106,0,225,21202,1,1,-4,109,-5,2105,1,0,109,3,22107,0,-2,-1,21202,-1,2,-1,21201,-1,-1,-1,22202,-1,-2,-2,109,-3,2106,0,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,22102,1,-2,-2,109,-3,2105,1,0,109,5,22207,-3,-4,-1,1206,-1,346,22201,-4,-3,-4,21202,-3,-1,-1,22201,-4,-1,2,21202,2,-1,-1,22201,-4,-1,1,21202,-2,1,3,21101,0,343,0,1106,0,303,1106,0,415,22207,-2,-3,-1,1206,-1,387,22201,-3,-2,-3,21202,-2,-1,-1,22201,-3,-1,3,21202,3,-1,-1,22201,-3,-1,2,21201,-4,0,1,21101,384,0,0,1105,1,303,1105,1,415,21202,-4,-1,-4,22201,-4,-3,-4,22202,-3,-2,-2,22202,-2,-4,-4,22202,-3,-2,-3,21202,-4,-1,-2,22201,-3,-2,1,21202,1,1,-4,109,-5,2105,1,0
|
Reference in New Issue
Block a user