mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-22 05:51:16 +00:00
Add solution for Day 12
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
0602aad44e
commit
bf4d2ab80b
4 changed files with 301 additions and 0 deletions
207
day12.go
Normal file
207
day12.go
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type day12MoonSystem []*day12Moon
|
||||||
|
|
||||||
|
func day12MoonSystemFromFile(inFile string) (day12MoonSystem, error) {
|
||||||
|
f, err := os.Open(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Unable to open inFile")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return day12MoonSystemFromReader(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func day12MoonSystemFromReader(r io.Reader) (day12MoonSystem, error) {
|
||||||
|
var moons day12MoonSystem
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
m, err := day12MoonFromScan(scanner.Text())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Unable to read input file")
|
||||||
|
}
|
||||||
|
moons = append(moons, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return moons, errors.Wrap(scanner.Err(), "Unable to scan file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day12MoonSystem) move(steps int) {
|
||||||
|
for i := 0; i < steps; i++ {
|
||||||
|
d.updateVelocity()
|
||||||
|
for _, m := range d {
|
||||||
|
m.move()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day12MoonSystem) totalEnergy() int64 {
|
||||||
|
var e int64
|
||||||
|
for _, m := range d {
|
||||||
|
e += m.totalEnergy()
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day12MoonSystem) updateVelocity() {
|
||||||
|
for _, affected := range d {
|
||||||
|
for _, affector := range d {
|
||||||
|
if affector == affected {
|
||||||
|
// Moons does not affect themselves
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if affector.PX > affected.PX {
|
||||||
|
affected.VX += 1
|
||||||
|
} else if affector.PX < affected.PX {
|
||||||
|
affected.VX -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if affector.PY > affected.PY {
|
||||||
|
affected.VY += 1
|
||||||
|
} else if affector.PY < affected.PY {
|
||||||
|
affected.VY -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if affector.PZ > affected.PZ {
|
||||||
|
affected.VZ += 1
|
||||||
|
} else if affector.PZ < affected.PZ {
|
||||||
|
affected.VZ -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type day12Moon struct {
|
||||||
|
PX, PY, PZ int64
|
||||||
|
VX, VY, VZ int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func day12MoonFromScan(scan string) (*day12Moon, error) {
|
||||||
|
grps := regexp.MustCompile(`^<x=([0-9-]+), y=([0-9-]+), z=([0-9-]+)>$`).FindStringSubmatch(scan)
|
||||||
|
if len(grps) != 4 {
|
||||||
|
return nil, errors.Errorf("Invalid input %q: %#v", scan, grps)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
m = &day12Moon{}
|
||||||
|
)
|
||||||
|
|
||||||
|
if m.PX, err = strconv.ParseInt(grps[1], 10, 64); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Invalid X")
|
||||||
|
}
|
||||||
|
if m.PY, err = strconv.ParseInt(grps[2], 10, 64); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Invalid Y")
|
||||||
|
}
|
||||||
|
if m.PZ, err = strconv.ParseInt(grps[3], 10, 64); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Invalid Z")
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day12Moon) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"pos=<x=%d, y=%d, z=%d>, vel=<x=%d, y=%d, z=%d>",
|
||||||
|
d.PX, d.PY, d.PZ,
|
||||||
|
d.VX, d.VY, d.VZ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *day12Moon) clearKinetic() { d.VX, d.VY, d.VZ = 0, 0, 0 }
|
||||||
|
|
||||||
|
func (d day12Moon) eq(t *day12Moon, component int) bool {
|
||||||
|
switch component {
|
||||||
|
case 0:
|
||||||
|
return d.PX == t.PX && d.VX == t.VX
|
||||||
|
case 1:
|
||||||
|
return d.PY == t.PY && d.VY == t.VY
|
||||||
|
case 2:
|
||||||
|
return d.PZ == t.PZ && d.VZ == t.VZ
|
||||||
|
default:
|
||||||
|
panic("Invalid component compared")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *day12Moon) move() {
|
||||||
|
d.PX += d.VX
|
||||||
|
d.PY += d.VY
|
||||||
|
d.PZ += d.VZ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day12Moon) totalEnergy() int64 {
|
||||||
|
return int64((math.Abs(float64(d.PX)) + math.Abs(float64(d.PY)) + math.Abs(float64(d.PZ))) * (math.Abs(float64(d.VX)) + math.Abs(float64(d.VY)) + math.Abs(float64(d.VZ))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay12Part1(inFile string) (int64, error) {
|
||||||
|
moons, err := day12MoonSystemFromFile(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to read system scan")
|
||||||
|
}
|
||||||
|
|
||||||
|
moons.move(1000)
|
||||||
|
|
||||||
|
return moons.totalEnergy(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay12Part2(inFile string) (int64, error) {
|
||||||
|
moons, err := day12MoonSystemFromFile(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to read system scan")
|
||||||
|
}
|
||||||
|
|
||||||
|
initMoons, _ := day12MoonSystemFromFile(inFile)
|
||||||
|
var (
|
||||||
|
iterations [3]int64
|
||||||
|
completed [3]bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
moons.move(1)
|
||||||
|
|
||||||
|
for c := 0; c < len(completed); c++ {
|
||||||
|
if completed[c] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = true
|
||||||
|
for i := range moons {
|
||||||
|
if !moons[i].eq(initMoons[i], c) {
|
||||||
|
matches = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterations[c]++
|
||||||
|
if matches {
|
||||||
|
completed[c] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var allComplete = true
|
||||||
|
for _, c := range completed {
|
||||||
|
if !c {
|
||||||
|
allComplete = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allComplete {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return leastCommonMultiple(leastCommonMultiple(iterations[0], iterations[1]), iterations[2]), nil
|
||||||
|
}
|
4
day12_input.txt
Normal file
4
day12_input.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<x=-7, y=17, z=-11>
|
||||||
|
<x=9, y=12, z=5>
|
||||||
|
<x=-9, y=0, z=-4>
|
||||||
|
<x=4, y=6, z=0>
|
86
day12_test.go
Normal file
86
day12_test.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDay12MoonExample(t *testing.T) {
|
||||||
|
moons, err := day12MoonSystemFromReader(strings.NewReader("<x=-1, y=0, z=2>\n<x=2, y=-10, z=-7>\n<x=4, y=-8, z=8>\n<x=3, y=5, z=-1>"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to parse moon system: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := len(moons); l != 4 {
|
||||||
|
t.Fatalf("Unexpected moon count: exp=4 got=%d", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check initial state
|
||||||
|
for i, expStr := range []string{
|
||||||
|
"pos=<x=-1, y=0, z=2>, vel=<x=0, y=0, z=0>",
|
||||||
|
"pos=<x=2, y=-10, z=-7>, vel=<x=0, y=0, z=0>",
|
||||||
|
"pos=<x=4, y=-8, z=8>, vel=<x=0, y=0, z=0>",
|
||||||
|
"pos=<x=3, y=5, z=-1>, vel=<x=0, y=0, z=0>",
|
||||||
|
} {
|
||||||
|
if s := moons[i].String(); s != expStr {
|
||||||
|
t.Errorf("Unexpected moon %d: exp=%q got=%q", i, expStr, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move moons by one step
|
||||||
|
moons.move(1)
|
||||||
|
|
||||||
|
// Check move state
|
||||||
|
for i, expStr := range []string{
|
||||||
|
"pos=<x=2, y=-1, z=1>, vel=<x=3, y=-1, z=-1>",
|
||||||
|
"pos=<x=3, y=-7, z=-4>, vel=<x=1, y=3, z=3>",
|
||||||
|
"pos=<x=1, y=-7, z=5>, vel=<x=-3, y=1, z=-3>",
|
||||||
|
"pos=<x=2, y=2, z=0>, vel=<x=-1, y=-3, z=1>",
|
||||||
|
} {
|
||||||
|
if s := moons[i].String(); s != expStr {
|
||||||
|
t.Errorf("Unexpected moon %d: exp=%q got=%q", i, expStr, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move moons by nine more step
|
||||||
|
moons.move(9)
|
||||||
|
|
||||||
|
// Check move state
|
||||||
|
for i, expStr := range []string{
|
||||||
|
"pos=<x=2, y=1, z=-3>, vel=<x=-3, y=-2, z=1>",
|
||||||
|
"pos=<x=1, y=-8, z=0>, vel=<x=-1, y=1, z=3>",
|
||||||
|
"pos=<x=3, y=-6, z=1>, vel=<x=3, y=2, z=-3>",
|
||||||
|
"pos=<x=2, y=0, z=4>, vel=<x=1, y=-1, z=-1>",
|
||||||
|
} {
|
||||||
|
if s := moons[i].String(); s != expStr {
|
||||||
|
t.Errorf("Unexpected moon %d: exp=%q got=%q", i, expStr, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check energy after step 10
|
||||||
|
for i, expE := range []int64{
|
||||||
|
36, 45, 80, 18,
|
||||||
|
} {
|
||||||
|
if e := moons[i].totalEnergy(); e != expE {
|
||||||
|
t.Errorf("Unexpected energy on moon %d: exp=%d got=%d", i, expE, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay12_Part1(t *testing.T) {
|
||||||
|
count, err := solveDay12Part1("day12_input.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Day 12 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Solution Day 12 Part 1: %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay12_Part2(t *testing.T) {
|
||||||
|
res, err := solveDay12Part2("day12_input.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Day 12 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Solution Day 12 Part 2: %d", res)
|
||||||
|
}
|
|
@ -11,6 +11,10 @@ func greatestCommonDivisor(a, b int64) int64 {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func leastCommonMultiple(a, b int64) int64 {
|
||||||
|
return a * b / greatestCommonDivisor(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
func manhattenDistance(x1, y1, x2, y2 int) int {
|
func manhattenDistance(x1, y1, x2, y2 int) int {
|
||||||
return int(math.Abs(float64(x1-x2)) + math.Abs(float64(y1-y2)))
|
return int(math.Abs(float64(x1-x2)) + math.Abs(float64(y1-y2)))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue