From 0b4cd9e634a811f5e17a1765b61e4596559ca131 Mon Sep 17 00:00:00 2001 From: Parnic Date: Tue, 21 Jun 2022 12:18:31 -0500 Subject: [PATCH] Day 10 solution This one was an absolute beating for me. I am so bad at these sorts of problems. Ultimately I settled on a probably-not-ideal solution that crawls the graph with offsets of each variant of (+/-x,+/-y), marking nodes visited as we come across them so that we end up with a list of asteroids that we can see. Given that this is day 10, and knowing how bad I am at math, I'm assuming this is very far from the intended solution, but it works reasonably quickly and I managed to come up with it myself, so I'm not going to stress too much about it. For asteroid destruction, the best method I could come up with for finding the correct order was to implement an entire Vector class and sort by angle, which worked, but again, I can't decide if it was the intended solution or not. I should start reusing past years' codebases so I don't have to keep building a utility library from scratch. --- days/10.go | 172 +++++++++++++++++++++++++++++++++++++++ inputs/10p.txt | 33 ++++++++ inputs/10s1.txt | 5 ++ inputs/10s2.txt | 10 +++ inputs/10s3.txt | 10 +++ inputs/10s4.txt | 10 +++ inputs/10s5.txt | 20 +++++ inputs/10s6.txt | 5 ++ main.go | 1 + utilities/constraints.go | 29 +++++++ utilities/map.go | 17 ++++ utilities/vector.go | 39 +++++++++ 12 files changed, 351 insertions(+) create mode 100644 days/10.go create mode 100644 inputs/10p.txt create mode 100644 inputs/10s1.txt create mode 100644 inputs/10s2.txt create mode 100644 inputs/10s3.txt create mode 100644 inputs/10s4.txt create mode 100644 inputs/10s5.txt create mode 100644 inputs/10s6.txt create mode 100644 utilities/constraints.go create mode 100644 utilities/map.go create mode 100644 utilities/vector.go diff --git a/days/10.go b/days/10.go new file mode 100644 index 0000000..00f471d --- /dev/null +++ b/days/10.go @@ -0,0 +1,172 @@ +package days + +import ( + "fmt" + "sort" + + u "parnic.com/aoc2019/utilities" +) + +type Day10 struct { + asteroids [][]bool + idealLocation u.Vec2[int] +} + +func (d *Day10) Parse() { + lines := u.GetStringLines("10p") + d.asteroids = make([][]bool, len(lines)) + for i, line := range lines { + d.asteroids[i] = make([]bool, len(line)) + for j, ch := range line { + d.asteroids[i][j] = ch == '#' + } + } +} + +func (d Day10) Num() int { + return 10 +} + +// func (d Day10) draw() { +// for i := range d.asteroids { +// for j := range d.asteroids[i] { +// if !d.asteroids[i][j].First { +// fmt.Print(".") +// } else { +// num := d.asteroids[i][j].Second +// ch := rune('0') + rune(num) +// if num >= 10 { +// ch = '+' +// } +// fmt.Printf("%c", ch) +// } +// } +// fmt.Println() +// } +// } + +func (d Day10) getVisibleAsteroids(i1, j1 int) map[u.Vec2[int]]bool { + visited := make(map[u.Vec2[int]]bool, 0) + foundAsteroids := make(map[u.Vec2[int]]bool, 0) + + findNext := func(startX, startY, incX, incY int) *u.Vec2[int] { + var found *u.Vec2[int] + if incX == 0 && incY == 0 { + return found + } + + x := startX + incX + y := startY + incY + for x < len(d.asteroids) && x >= 0 && y < len(d.asteroids[x]) && y >= 0 { + currPair := u.Vec2[int]{X: x, Y: y} + if _, exists := visited[currPair]; !exists { + visited[currPair] = true + + if d.asteroids[x][y] { + if found == nil { + found = &currPair + } + } + } + + x += incX + y += incY + } + + return found + } + + for incX := 0; ; { + plusXValid := i1+incX < len(d.asteroids) + minusXValid := i1-incX >= 0 + if !plusXValid && !minusXValid { + break + } + + for incY := 0; ; { + plusYValid := j1+incY < len(d.asteroids[0]) + minusYValid := j1-incY >= 0 + if !plusYValid && !minusYValid { + break + } + + if found := findNext(i1, j1, incX, incY); found != nil { + foundAsteroids[*found] = true + } + if found := findNext(i1, j1, incX, -incY); found != nil { + foundAsteroids[*found] = true + } + if found := findNext(i1, j1, -incX, incY); found != nil { + foundAsteroids[*found] = true + } + if found := findNext(i1, j1, -incX, -incY); found != nil { + foundAsteroids[*found] = true + } + + incY++ + } + + incX++ + } + + return foundAsteroids +} + +func (d Day10) numVisibleAsteroids(i1, j1 int) int { + return len(d.getVisibleAsteroids(i1, j1)) +} + +func (d *Day10) removeAsteroids(locs map[u.Vec2[int]]bool) { + for loc := range locs { + if !d.asteroids[loc.X][loc.Y] { + panic("tried to remove non-asteroid") + } + + d.asteroids[loc.X][loc.Y] = false + } +} + +func (d *Day10) Part1() string { + mostAsteroids := 0 + for i := range d.asteroids { + for j := range d.asteroids[i] { + if d.asteroids[i][j] { + numVisible := d.numVisibleAsteroids(i, j) + if numVisible > mostAsteroids { + mostAsteroids = numVisible + d.idealLocation = u.Vec2[int]{X: i, Y: j} + } + } + } + } + + return fmt.Sprintf("Most visible asteroids: %s%d%s at (%d,%d)", u.TextBold, mostAsteroids, u.TextReset, d.idealLocation.Y, d.idealLocation.X) +} + +func (d *Day10) Part2() string { + findNumVaporized := 200 + var targetLocation u.Vec2[int] + + vaporized := 0 + for vaporized < findNumVaporized { + visibleAsteroids := d.getVisibleAsteroids(d.idealLocation.X, d.idealLocation.Y) + if len(visibleAsteroids) == 0 { + panic("no more asteroids to vaporize") + } + + if vaporized+len(visibleAsteroids) < findNumVaporized { + vaporized += len(visibleAsteroids) + d.removeAsteroids(visibleAsteroids) + continue + } + + vecs := u.MapKeys(visibleAsteroids) + sort.Slice(vecs, func(i, j int) bool { + return d.idealLocation.AngleBetween(vecs[i]) > d.idealLocation.AngleBetween(vecs[j]) + }) + targetLocation = vecs[findNumVaporized-1-vaporized] + break + } + + return fmt.Sprintf("#%d asteroid to be vaporized is at (%d,%d), transformed: %s%d%s", findNumVaporized, targetLocation.Y, targetLocation.X, u.TextBold, (targetLocation.Y*100)+targetLocation.X, u.TextReset) +} diff --git a/inputs/10p.txt b/inputs/10p.txt new file mode 100644 index 0000000..4b6e8a3 --- /dev/null +++ b/inputs/10p.txt @@ -0,0 +1,33 @@ +.#......##.#..#.......#####...#.. +...#.....##......###....#.##..... +..#...#....#....#............###. +.....#......#.##......#.#..###.#. +#.#..........##.#.#...#.##.#.#.#. +..#.##.#...#.......#..##.......## +..#....#.....#..##.#..####.#..... +#.............#..#.........#.#... +........#.##..#..#..#.#.....#.#.. +.........#...#..##......###.....# +##.#.###..#..#.#.....#.........#. +.#.###.##..##......#####..#..##.. +.........#.......#.#......#...... +..#...#...#...#.#....###.#....... +#..#.#....#...#.......#..#.#.##.. +#.....##...#.###..#..#......#..## +...........#...#......#..#....#.. +#.#.#......#....#..#.....##....## +..###...#.#.##..#...#.....#...#.# +.......#..##.#..#.............##. +..###........##.#................ +###.#..#...#......###.#........#. +.......#....#.#.#..#..#....#..#.. +.#...#..#...#......#....#.#..#... +#.#.........#.....#....#.#.#..... +.#....#......##.##....#........#. +....#..#..#...#..##.#.#......#.#. +..###.##.#.....#....#.#......#... +#.##...#............#..#.....#..# +.#....##....##...#......#........ +...#...##...#.......#....##.#.... +.#....#.#...#.#...##....#..##.#.# +.#.#....##.......#.....##.##.#.## \ No newline at end of file diff --git a/inputs/10s1.txt b/inputs/10s1.txt new file mode 100644 index 0000000..6a9f2d1 --- /dev/null +++ b/inputs/10s1.txt @@ -0,0 +1,5 @@ +.#..# +..... +##### +....# +...## \ No newline at end of file diff --git a/inputs/10s2.txt b/inputs/10s2.txt new file mode 100644 index 0000000..8650346 --- /dev/null +++ b/inputs/10s2.txt @@ -0,0 +1,10 @@ +......#.#. +#..#.#.... +..#######. +.#.#.###.. +.#..#..... +..#....#.# +#..#....#. +.##.#..### +##...#..#. +.#....#### \ No newline at end of file diff --git a/inputs/10s3.txt b/inputs/10s3.txt new file mode 100644 index 0000000..dc708ab --- /dev/null +++ b/inputs/10s3.txt @@ -0,0 +1,10 @@ +#.#...#.#. +.###....#. +.#....#... +##.#.#.#.# +....#.#.#. +.##..###.# +..#...##.. +..##....## +......#... +.####.###. \ No newline at end of file diff --git a/inputs/10s4.txt b/inputs/10s4.txt new file mode 100644 index 0000000..cb09bb7 --- /dev/null +++ b/inputs/10s4.txt @@ -0,0 +1,10 @@ +.#..#..### +####.###.# +....###.#. +..###.##.# +##.##.#.#. +....###..# +..#.#..#.# +#..#.#.### +.##...##.# +.....#.#.. \ No newline at end of file diff --git a/inputs/10s5.txt b/inputs/10s5.txt new file mode 100644 index 0000000..8380323 --- /dev/null +++ b/inputs/10s5.txt @@ -0,0 +1,20 @@ +.#..##.###...####### +##.############..##. +.#.######.########.# +.###.#######.####.#. +#####.##.#.##.###.## +..#####..#.######### +#################### +#.####....###.#.#.## +##.################# +#####.##.###..####.. +..######..##.####### +####.##.####...##..# +.#####..#.######.### +##...#.##########... +#.##########.####### +.####.#.###.###.#.## +....##.##.###..##### +.#.#.###########.### +#.#.#.#####.####.### +###.##.####.##.#..## \ No newline at end of file diff --git a/inputs/10s6.txt b/inputs/10s6.txt new file mode 100644 index 0000000..18ae03f --- /dev/null +++ b/inputs/10s6.txt @@ -0,0 +1,5 @@ +.#....#####...#.. +##...##.#####..## +##...#...#.#####. +..#.....#...###.. +..#.#.....#....## \ No newline at end of file diff --git a/main.go b/main.go index 8938fe1..fb600a7 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ var dayMap = []day{ &days.Day07{}, &days.Day08{}, &days.Day09{}, + &days.Day10{}, } func main() { diff --git a/utilities/constraints.go b/utilities/constraints.go new file mode 100644 index 0000000..2ea90f6 --- /dev/null +++ b/utilities/constraints.go @@ -0,0 +1,29 @@ +package utilities + +type Ordered interface { + Integer | Float | ~string +} + +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Integer interface { + Signed | Unsigned +} + +type Float interface { + ~float32 | ~float64 +} + +type Complex interface { + ~complex64 | ~complex128 +} + +type Number interface { + Integer | Float +} diff --git a/utilities/map.go b/utilities/map.go new file mode 100644 index 0000000..58e970f --- /dev/null +++ b/utilities/map.go @@ -0,0 +1,17 @@ +package utilities + +func MapKeys[T comparable, U any](m map[T]U) []T { + r := make([]T, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} + +func MapValues[T comparable, U any](m map[T]U) []U { + r := make([]U, 0, len(m)) + for _, v := range m { + r = append(r, v) + } + return r +} diff --git a/utilities/vector.go b/utilities/vector.go new file mode 100644 index 0000000..fbfc345 --- /dev/null +++ b/utilities/vector.go @@ -0,0 +1,39 @@ +package utilities + +import "math" + +type Vec2[T Number] struct { + X T + Y T +} + +func (v Vec2[T]) Dot(other Vec2[T]) T { + return (v.X * other.X) + (v.Y * other.Y) +} + +func (v Vec2[T]) Len() T { + return T(math.Sqrt(float64(v.LenSquared()))) +} + +func (v Vec2[T]) LenSquared() T { + return (v.X * v.X) + (v.Y * v.Y) +} + +func (v Vec2[T]) To(other Vec2[T]) Vec2[T] { + return Vec2[T]{ + X: v.X - other.X, + Y: v.Y - other.Y, + } +} + +func (v Vec2[T]) AngleBetween(other Vec2[T]) float64 { + rad := math.Atan2(float64(other.Y-v.Y), float64(other.X-v.X)) + return rad * 180 / math.Pi +} + +func VecBetween[T Number](a, b Vec2[T]) Vec2[T] { + return Vec2[T]{ + X: a.X - b.X, + Y: a.Y - b.Y, + } +}