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.
This commit is contained in:
2022-06-10 15:29:01 -05:00
parent dd5af1f2cc
commit e289bd9f54
11 changed files with 333 additions and 0 deletions

171
days/10.go Normal file
View File

@ -0,0 +1,171 @@
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) []u.Vec2[int] {
visited := make([]u.Vec2[int], 0)
foundAsteroids := make([]u.Vec2[int], 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 !u.ArrayContains(visited, currPair) {
visited = append(visited, currPair)
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 = append(foundAsteroids, *found)
}
if found := findNext(i1, j1, incX, -incY); found != nil {
foundAsteroids = append(foundAsteroids, *found)
}
if found := findNext(i1, j1, -incX, incY); found != nil {
foundAsteroids = append(foundAsteroids, *found)
}
if found := findNext(i1, j1, -incX, -incY); found != nil {
foundAsteroids = append(foundAsteroids, *found)
}
incY++
}
incX++
}
return foundAsteroids
}
func (d Day10) numVisibleAsteroids(i1, j1 int) int {
return len(d.getVisibleAsteroids(i1, j1))
}
func (d *Day10) removeAsteroids(locs ...u.Vec2[int]) {
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
}
sort.Slice(visibleAsteroids, func(i, j int) bool {
return d.idealLocation.AngleBetween(visibleAsteroids[i]) > d.idealLocation.AngleBetween(visibleAsteroids[j])
})
targetLocation = visibleAsteroids[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)
}

33
inputs/10p.txt Normal file
View File

@ -0,0 +1,33 @@
.#......##.#..#.......#####...#..
...#.....##......###....#.##.....
..#...#....#....#............###.
.....#......#.##......#.#..###.#.
#.#..........##.#.#...#.##.#.#.#.
..#.##.#...#.......#..##.......##
..#....#.....#..##.#..####.#.....
#.............#..#.........#.#...
........#.##..#..#..#.#.....#.#..
.........#...#..##......###.....#
##.#.###..#..#.#.....#.........#.
.#.###.##..##......#####..#..##..
.........#.......#.#......#......
..#...#...#...#.#....###.#.......
#..#.#....#...#.......#..#.#.##..
#.....##...#.###..#..#......#..##
...........#...#......#..#....#..
#.#.#......#....#..#.....##....##
..###...#.#.##..#...#.....#...#.#
.......#..##.#..#.............##.
..###........##.#................
###.#..#...#......###.#........#.
.......#....#.#.#..#..#....#..#..
.#...#..#...#......#....#.#..#...
#.#.........#.....#....#.#.#.....
.#....#......##.##....#........#.
....#..#..#...#..##.#.#......#.#.
..###.##.#.....#....#.#......#...
#.##...#............#..#.....#..#
.#....##....##...#......#........
...#...##...#.......#....##.#....
.#....#.#...#.#...##....#..##.#.#
.#.#....##.......#.....##.##.#.##

5
inputs/10s1.txt Normal file
View File

@ -0,0 +1,5 @@
.#..#
.....
#####
....#
...##

10
inputs/10s2.txt Normal file
View File

@ -0,0 +1,10 @@
......#.#.
#..#.#....
..#######.
.#.#.###..
.#..#.....
..#....#.#
#..#....#.
.##.#..###
##...#..#.
.#....####

10
inputs/10s3.txt Normal file
View File

@ -0,0 +1,10 @@
#.#...#.#.
.###....#.
.#....#...
##.#.#.#.#
....#.#.#.
.##..###.#
..#...##..
..##....##
......#...
.####.###.

10
inputs/10s4.txt Normal file
View File

@ -0,0 +1,10 @@
.#..#..###
####.###.#
....###.#.
..###.##.#
##.##.#.#.
....###..#
..#.#..#.#
#..#.#.###
.##...##.#
.....#.#..

20
inputs/10s5.txt Normal file
View File

@ -0,0 +1,20 @@
.#..##.###...#######
##.############..##.
.#.######.########.#
.###.#######.####.#.
#####.##.#.##.###.##
..#####..#.#########
####################
#.####....###.#.#.##
##.#################
#####.##.###..####..
..######..##.#######
####.##.####...##..#
.#####..#.######.###
##...#.##########...
#.##########.#######
.####.#.###.###.#.##
....##.##.###..#####
.#.#.###########.###
#.#.#.#####.####.###
###.##.####.##.#..##

5
inputs/10s6.txt Normal file
View File

@ -0,0 +1,5 @@
.#....#####...#..
##...##.#####..##
##...#...#.#####.
..#.....#...###..
..#.#.....#....##

View File

@ -40,6 +40,7 @@ var dayMap = []day{
&days.Day07{},
&days.Day08{},
&days.Day09{},
&days.Day10{},
}
func main() {

29
utilities/constraints.go Normal file
View File

@ -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
}

39
utilities/vector.go Normal file
View File

@ -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,
}
}