mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-21 21:41:16 +00:00
Add solution for Day 19
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
84e8a9afa0
commit
3c60b28489
3 changed files with 253 additions and 0 deletions
231
day19.go
Normal file
231
day19.go
Normal file
|
@ -0,0 +1,231 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type day19Fact uint8
|
||||
|
||||
const (
|
||||
day19FactNoMoveDownPossible day19Fact = 1 << iota
|
||||
day19FactNoMoveLeftPossible
|
||||
day19FactNoMoveRightPossible
|
||||
day19FactNoMoveTopPossible
|
||||
day19FactOriginNotInBeam
|
||||
day19FactX100NotInBeam
|
||||
day19FactY100NotInBeam
|
||||
)
|
||||
|
||||
func (f day19Fact) has(t day19Fact) bool { return f&t != 0 }
|
||||
func (f day19Fact) String() string {
|
||||
return map[day19Fact]string{
|
||||
day19FactNoMoveDownPossible: "NoMoveDownPossible",
|
||||
day19FactNoMoveLeftPossible: "NoMoveLeftPossible",
|
||||
day19FactNoMoveRightPossible: "NoMoveRightPossible",
|
||||
day19FactNoMoveTopPossible: "NoMoveTopPossible",
|
||||
day19FactOriginNotInBeam: "OriginNotInBeam",
|
||||
day19FactX100NotInBeam: "X100NotInBeam",
|
||||
day19FactY100NotInBeam: "Y100NotInBeam",
|
||||
}[f]
|
||||
}
|
||||
|
||||
func day19CountFieldsInTractorBeam(code []int64, maxX, maxY int64) int64 {
|
||||
var count int64
|
||||
|
||||
for y := int64(0); y <= maxY; y++ {
|
||||
for x := int64(0); x <= maxX; x++ {
|
||||
|
||||
var (
|
||||
in = make(chan int64)
|
||||
out = make(chan int64)
|
||||
)
|
||||
go executeIntcode(code, in, out)
|
||||
|
||||
// Submit coordinates
|
||||
in <- x
|
||||
in <- y
|
||||
|
||||
// Count fields with tractor beam
|
||||
o := <-out
|
||||
count += o
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func day19Find100x100ShipPlace(code []int64) int64 {
|
||||
var (
|
||||
x, y int64
|
||||
bvL, bvR int64
|
||||
)
|
||||
|
||||
getBeamVectors := func() {
|
||||
for x := int64(0); x <= math.MaxInt64; x++ {
|
||||
|
||||
var (
|
||||
in = make(chan int64)
|
||||
out = make(chan int64)
|
||||
)
|
||||
go executeIntcode(code, in, out)
|
||||
|
||||
// Submit coordinates
|
||||
in <- x
|
||||
in <- 100
|
||||
|
||||
// Count fields with tractor beam
|
||||
o := <-out
|
||||
switch {
|
||||
case o == 0 && bvR == 0:
|
||||
// Did not yet find the beam
|
||||
case o == 1 && bvL == 0:
|
||||
// Left beam end has not been set
|
||||
bvL, bvR = x, x
|
||||
case o == 1 && x > bvR:
|
||||
// New right "edge" found
|
||||
bvR = x
|
||||
case 0 == 0 && bvR > 0:
|
||||
// End of right edge found, end
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
checkCoordinate := func(x, y int64) day19Fact {
|
||||
var facts day19Fact
|
||||
|
||||
for flag, c := range map[day19Fact][2]int64{
|
||||
day19FactNoMoveDownPossible: {x, y + 100},
|
||||
day19FactNoMoveLeftPossible: {x - 1, y + 99},
|
||||
day19FactNoMoveRightPossible: {x + 100, y},
|
||||
day19FactNoMoveTopPossible: {x + 99, y - 1},
|
||||
day19FactOriginNotInBeam: {x, y},
|
||||
day19FactX100NotInBeam: {x + 99, y},
|
||||
day19FactY100NotInBeam: {x, y + 99},
|
||||
} {
|
||||
if c[0] < 0 || c[1] < 0 {
|
||||
// Never check negative coordinates
|
||||
facts |= flag
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
in = make(chan int64)
|
||||
out = make(chan int64)
|
||||
)
|
||||
defer close(in)
|
||||
|
||||
go executeIntcode(code, in, out)
|
||||
|
||||
// Submit coordinates
|
||||
in <- c[0]
|
||||
in <- c[1]
|
||||
|
||||
o := <-out
|
||||
if o == 0 {
|
||||
facts |= flag
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return facts
|
||||
}
|
||||
|
||||
// Initially get vectors for beam edges
|
||||
getBeamVectors()
|
||||
|
||||
// Try to get to the best position through movement
|
||||
var success bool
|
||||
for !success {
|
||||
var f = checkCoordinate(x, y)
|
||||
|
||||
switch {
|
||||
case f.has(day19FactOriginNotInBeam):
|
||||
panic("Origin placed outside beam")
|
||||
|
||||
case f.has(day19FactX100NotInBeam) || f.has(day19FactY100NotInBeam):
|
||||
// Ship does not fit, move further away
|
||||
y += 100
|
||||
x = ((bvL * (y / 100)) + (bvR * (y / 100))) / 2
|
||||
|
||||
case !f.has(day19FactNoMoveTopPossible):
|
||||
// Ship can be moved up in beam
|
||||
y -= 1
|
||||
|
||||
case !f.has(day19FactNoMoveLeftPossible):
|
||||
// Ship can be moved left in beam
|
||||
x -= 1
|
||||
|
||||
case f.has(day19FactNoMoveTopPossible) && f.has(day19FactNoMoveLeftPossible):
|
||||
// No movement towards ship is possible and ship fits in: Perfect
|
||||
success = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Result before force-move: x=%d y=%d res=%d", x, y, x*10000+y)
|
||||
|
||||
// This MIGHT not be the perfect position, force further movement
|
||||
// by ignoring some factors set above in order to compensate inaccurate
|
||||
// vectors due to working with integers only
|
||||
var fX, fY = x, y
|
||||
for success {
|
||||
var f = checkCoordinate(fX, fY)
|
||||
|
||||
if !f.has(day19FactX100NotInBeam) && !f.has(day19FactY100NotInBeam) {
|
||||
// Found a better position through forcing
|
||||
x, y = fX, fY
|
||||
}
|
||||
|
||||
switch {
|
||||
case !f.has(day19FactNoMoveTopPossible):
|
||||
// Ship can be moved up in beam
|
||||
fY -= 1
|
||||
|
||||
case f.has(day19FactOriginNotInBeam):
|
||||
// No further tests, there will be no better position
|
||||
success = false
|
||||
|
||||
default:
|
||||
fX -= 1
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Result after force-move: x=%d y=%d res=%d", x, y, x*10000+y)
|
||||
|
||||
return x*10000 + y
|
||||
}
|
||||
|
||||
func solveDay19Part1(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")
|
||||
}
|
||||
|
||||
return day19CountFieldsInTractorBeam(code, 49, 49), nil
|
||||
}
|
||||
|
||||
func solveDay19Part2(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")
|
||||
}
|
||||
|
||||
return day19Find100x100ShipPlace(code), nil
|
||||
}
|
1
day19_input.txt
Normal file
1
day19_input.txt
Normal file
|
@ -0,0 +1 @@
|
|||
109,424,203,1,21101,11,0,0,1105,1,282,21102,1,18,0,1105,1,259,2102,1,1,221,203,1,21102,1,31,0,1105,1,282,21102,38,1,0,1105,1,259,20101,0,23,2,22102,1,1,3,21101,1,0,1,21101,57,0,0,1105,1,303,2101,0,1,222,21002,221,1,3,21002,221,1,2,21102,1,259,1,21101,0,80,0,1105,1,225,21102,83,1,2,21102,1,91,0,1106,0,303,2101,0,1,223,20102,1,222,4,21101,0,259,3,21101,0,225,2,21101,225,0,1,21101,118,0,0,1106,0,225,20101,0,222,3,21101,34,0,2,21101,133,0,0,1105,1,303,21202,1,-1,1,22001,223,1,1,21102,1,148,0,1106,0,259,1201,1,0,223,20102,1,221,4,20101,0,222,3,21101,12,0,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,20101,0,23,1,21102,1,-1,3,21102,214,1,0,1105,1,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,1202,-4,1,249,22101,0,-3,1,22101,0,-2,2,21201,-1,0,3,21101,0,250,0,1105,1,225,21201,1,0,-4,109,-5,2106,0,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,22101,0,-2,-2,109,-3,2106,0,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,21201,-2,0,3,21101,343,0,0,1105,1,303,1105,1,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,1106,0,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,2106,0,0
|
21
day19_test.go
Normal file
21
day19_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package aoc2019
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCalculateDay19_Part1(t *testing.T) {
|
||||
res, err := solveDay19Part1("day19_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 19 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 19 Part 1: %d", res)
|
||||
}
|
||||
|
||||
func TestCalculateDay19_Part2(t *testing.T) {
|
||||
res, err := solveDay19Part2("day19_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 19 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 19 Part 2: %d", res)
|
||||
}
|
Loading…
Reference in a new issue