11 Commits

Author SHA1 Message Date
788d22f33c Merge branch 'develop' into dev/19 2022-06-21 01:18:12 -05:00
34ece5dd77 Revert "Merge branch 'develop' into dev/19"
This reverts commit 5247ae9d72, reversing
changes made to 9947a9b559.
2022-06-21 01:18:05 -05:00
e153d022a7 Intcode Reset optimization
Initially I noticed that I was copying twice unnecessarily (once in init() after nulling out memory, and again after returning from init()). After cleaning that up, I realized that we don't need to create a new buffer at all if the program never malloc-ed, so sometimes we can skip the re-init and we can always avoid the double-copy. I don't know if this is actually measurable anywhere, but I spot-checked some results and I still seem to be getting the same answers, so I'm gonna roll with it.
2022-06-21 01:17:33 -05:00
b97ec2eb17 Add bisect utility
This is too common of an optimization to not have this readily accessible. And I kinda like how this worked out, too. Go is fun.
2022-06-21 01:17:31 -05:00
de07c7c987 First optimizations
This gets runtime down to about a quarter of what it was. I need to see if I can apply bisect to the X value, but I'm not sure I understand how/if that can work.
2022-06-21 01:16:42 -05:00
5247ae9d72 Merge branch 'develop' into dev/19 2022-06-21 00:22:40 -05:00
dfc2eb6e4f Intcode Reset optimization
Initially I noticed that I was copying twice unnecessarily (once in init() after nulling out memory, and again after returning from init()). After cleaning that up, I realized that we don't need to create a new buffer at all if the program never malloc-ed, so sometimes we can skip the re-init and we can always avoid the double-copy. I don't know if this is actually measurable anywhere, but I spot-checked some results and I still seem to be getting the same answers, so I'm gonna roll with it.
2022-06-21 00:21:58 -05:00
94471df109 Add bisect utility
This is too common of an optimization to not have this readily accessible. And I kinda like how this worked out, too. Go is fun.
2022-06-21 00:12:06 -05:00
9947a9b559 Day 19 functional
We're at about 1s runtime on my macbook, but we do get the right answer now.
2022-06-21 00:11:22 -05:00
cbaab27054 Day 19 almost
Part 1 works, part 2 is giving me trouble. I need to find a better way to validate whether 100x100 can fit, and then something faster to figure out the minimum position. In other words, the entire problem.
2022-06-20 08:58:58 -05:00
23bf2e9d84 Day 18 solution
This one was a doozy and took far more time than I'd like to admit. I hope it's the hardest problem of the year. My original solution gave correct answers, but took on the order of 3+ minutes to run, even with memoization (it wasn't finishing any time soon without it). I then tried dropping in some A* and Dijkstra libraries, but wasn't really happy with how things were progressing with them. Some research pointed me toward double-ended queues and priority queues as better solutions, which I should have come up with my own since they've been used along with memoization in other AoC's, and dropping those in took the runtimes down to 4-15 seconds on my m1 macbook air. Once I swapped out the memoized data structures from arrays to maps, the runtime finally dropped to a much more palatable 50-180 millisecond range.

I'm always suspicious when my solution is hundreds of lines of code, though, since you tend to see much more terse solutions from others in the AoC subreddit, but I attribute at least some of the bloat to "Go things" like how maps and arrays usually need to be "make"d first, how there's no easy "list comprehension" or "linq" style data structure queries, etc.

Finally: note that I've seen _dramatically_ different runtimes based on the input. One set of input I checked against ran in 30ms/10ms while another ran in 180ms/54ms. I guess it's never been promised that all inputs are created equally...
2022-06-18 23:13:19 -05:00
21 changed files with 672 additions and 26 deletions

View File

@ -104,26 +104,10 @@ func (d *Day14) Part1() string {
func (d *Day14) Part2() string {
oreAvailable := int64(1000000000000)
estimate := oreAvailable / d.getOreRequiredForFuel(1)
high := estimate * 2
low := estimate
lastSuccess := low
lastFailure := high
fuelProduced := low
for math.Abs(float64(lastFailure-lastSuccess)) > 1 {
oreConsumed := d.getOreRequiredForFuel(fuelProduced)
adjustment := (lastFailure - lastSuccess) / 2
if oreConsumed < oreAvailable {
lastSuccess = fuelProduced
} else {
lastFailure = fuelProduced
adjustment = -adjustment
}
fuelProduced += adjustment
}
lastSuccess := u.Bisect(estimate, estimate*2, 1, func(val int64) bool {
oreConsumed := d.getOreRequiredForFuel(val)
return oreConsumed < oreAvailable
})
return fmt.Sprintf("Maximum fuel we can make from 1 trillion ore: %s%d%s", u.TextBold, lastSuccess, u.TextReset)
}

345
days/18.go Normal file
View File

@ -0,0 +1,345 @@
package days
import (
"container/heap"
"fmt"
"math"
"strings"
"github.com/edwingeng/deque/v2"
u "parnic.com/aoc2019/utilities"
)
type day18Cell int
type day18Vec u.Vec2[int]
type day18Graph map[rune][]u.Pair[rune, int]
const (
day18CellWall day18Cell = iota
day18CellOpen
)
var (
day18AdjacentOffsets = []day18Vec{
{X: -1, Y: 0},
{X: 1, Y: 0},
{X: 0, Y: -1},
{X: 0, Y: 1},
}
)
type reachableKeysMemo struct {
pos rune
keysFound int
}
type minStepsMemo struct {
pos string
keysToFind int
keysFound int
}
type Day18 struct {
entrance day18Vec
grid [][]day18Cell
doors map[day18Vec]int
keys map[day18Vec]int
knownReachableKeys map[reachableKeysMemo][]u.Pair[rune, int]
knownMinimumSteps map[minStepsMemo]int
}
func (d *Day18) Parse() {
d.doors = make(map[day18Vec]int)
d.keys = make(map[day18Vec]int)
d.knownReachableKeys = make(map[reachableKeysMemo][]u.Pair[rune, int])
d.knownMinimumSteps = make(map[minStepsMemo]int, 0)
lines := u.GetStringLines("18p")
d.grid = make([][]day18Cell, len(lines))
for i, line := range lines {
d.grid[i] = make([]day18Cell, len(line))
for j, char := range line {
if char == '#' {
d.grid[i][j] = day18CellWall
} else if char == '.' {
d.grid[i][j] = day18CellOpen
} else if char == '@' {
d.grid[i][j] = day18CellOpen
d.entrance = day18Vec{X: j, Y: i}
} else if char >= 'A' && char <= 'Z' {
d.grid[i][j] = day18CellOpen
d.doors[day18Vec{X: j, Y: i}] = int(char - 'A')
} else if char >= 'a' && char <= 'z' {
d.grid[i][j] = day18CellOpen
d.keys[day18Vec{X: j, Y: i}] = int(char - 'a')
}
}
}
}
func (d Day18) Num() int {
return 18
}
func (d Day18) Draw(grid [][]day18Cell, keys, doors map[day18Vec]int, entrances ...day18Vec) {
for y := range grid {
for x := range grid[y] {
switch grid[y][x] {
case day18CellWall:
fmt.Print("█")
case day18CellOpen:
posVec := day18Vec{X: x, Y: y}
if _, exists := doors[posVec]; exists {
fmt.Printf("%c", rune(doors[posVec]+'A'))
} else if _, exists := keys[posVec]; exists {
fmt.Printf("%c", rune(keys[posVec]+'a'))
} else if u.ArrayContains(entrances, posVec) {
fmt.Print("@")
} else {
fmt.Print(".")
}
}
}
fmt.Println()
}
}
func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, grid [][]day18Cell) []u.Pair[rune, int] {
found := make([]u.Pair[rune, int], 0)
getAdjacent := func(pos day18Vec) []day18Vec {
retAdjacent := make([]day18Vec, 0, len(day18AdjacentOffsets))
for _, off := range day18AdjacentOffsets {
offVec := day18Vec{X: pos.X + off.X, Y: pos.Y + off.Y}
if grid[offVec.Y][offVec.X] == day18CellWall {
continue
}
retAdjacent = append(retAdjacent, offVec)
}
return retAdjacent
}
queue := deque.NewDeque[u.Pair[int, day18Vec]]()
visited := make(map[day18Vec]bool)
for _, adjacent := range getAdjacent(inPos) {
queue.PushBack(u.Pair[int, day18Vec]{First: 1, Second: adjacent})
}
for !queue.IsEmpty() {
next := queue.PopFront()
if _, exists := visited[next.Second]; !exists {
visited[next.Second] = true
key, adjacentIsKey := keys[next.Second]
door, adjacentIsDoor := doors[next.Second]
if adjacentIsKey || adjacentIsDoor {
var rVal rune
if adjacentIsKey {
rVal = rune('a' + key)
} else if adjacentIsDoor {
rVal = rune('A' + door)
}
alreadyFound := false
for _, p := range found {
if p.First == rVal {
alreadyFound = true
break
}
}
if !alreadyFound {
found = append(found, u.Pair[rune, int]{First: rVal, Second: next.First})
continue
}
}
for _, neighbor := range getAdjacent(next.Second) {
if _, exists := visited[neighbor]; !exists {
queue.PushBack(u.Pair[int, day18Vec]{First: next.First + 1, Second: neighbor})
}
}
}
}
return found
}
type day18PriorityQueue struct {
distance int
neighbor rune
}
type day18PriorityQueueHeap []day18PriorityQueue
func (h day18PriorityQueueHeap) Len() int { return len(h) }
func (h day18PriorityQueueHeap) Less(i, j int) bool { return h[i].distance < h[j].distance }
func (h day18PriorityQueueHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *day18PriorityQueueHeap) Push(x any) {
*h = append(*h, x.(day18PriorityQueue))
}
func (h *day18PriorityQueueHeap) Pop() any {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func (d Day18) reachableKeys(inPos rune, keysFound int, graph day18Graph) []u.Pair[rune, int] {
memo := reachableKeysMemo{
pos: inPos,
keysFound: keysFound,
}
if v, exists := d.knownReachableKeys[memo]; exists {
return v
}
ret := make([]u.Pair[rune, int], 0)
distance := make(map[rune]int)
ih := make(day18PriorityQueueHeap, 0)
for _, p := range graph[inPos] {
ih = append(ih, day18PriorityQueue{
distance: p.Second,
neighbor: p.First,
})
}
heap.Init(&ih)
for ih.Len() > 0 {
node := heap.Pop(&ih).(day18PriorityQueue)
// it's a key and we haven't picked it up yet...
if node.neighbor >= 'a' && node.neighbor <= 'z' && (1<<int(node.neighbor-'a')&keysFound) == 0 {
ret = append(ret, u.Pair[rune, int]{First: node.neighbor, Second: node.distance})
continue
}
// it's a door but we don't have the key yet...
if node.neighbor >= 'A' && node.neighbor <= 'Z' && ((1<<int(node.neighbor-'A'))&keysFound) == 0 {
continue
}
for _, p := range graph[node.neighbor] {
newDistance := node.distance + p.Second
if dist, exists := distance[p.First]; !exists || newDistance < dist {
distance[p.First] = newDistance
heap.Push(&ih, day18PriorityQueue{
distance: newDistance,
neighbor: p.First,
})
}
}
}
d.knownReachableKeys[memo] = ret
return ret
}
func (d Day18) minimumSteps(inPos string, keysToFind int, keysFound int, graph day18Graph) int {
memo := minStepsMemo{
pos: inPos,
keysToFind: keysToFind,
keysFound: keysFound,
}
if v, exists := d.knownMinimumSteps[memo]; exists {
return v
}
if keysToFind == 0 {
return 0
}
best := math.Inf(1)
for _, item := range inPos {
for _, p := range d.reachableKeys(item, keysFound, graph) {
sb := strings.Builder{}
oldIdx := strings.IndexRune(inPos, item)
for i := range inPos {
if i == oldIdx {
sb.WriteRune(p.First)
} else {
sb.WriteByte(inPos[i])
}
}
newKeys := keysFound + (1 << (p.First - 'a'))
dist := p.Second
dist += d.minimumSteps(sb.String(), keysToFind-1, newKeys, graph)
if float64(dist) < best {
best = float64(dist)
}
}
}
d.knownMinimumSteps[memo] = int(best)
return int(best)
}
func (d Day18) buildGraph(pos []day18Vec, keys map[day18Vec]int, doors map[day18Vec]int, grid [][]day18Cell) day18Graph {
graph := make(day18Graph)
for i, p := range pos {
adjacent := d.findAdjacentCells(p, keys, doors, grid)
graph[rune('1'+i)] = adjacent
}
for keyPos, keyType := range keys {
graph[rune('a'+keyType)] = d.findAdjacentCells(keyPos, keys, doors, grid)
}
for doorPos, doorType := range doors {
graph[rune('A'+doorType)] = d.findAdjacentCells(doorPos, keys, doors, grid)
}
return graph
}
func (d Day18) part2PatchMap(grid [][]day18Cell, entrance day18Vec) []day18Vec {
grid[entrance.Y-1][entrance.X] = day18CellWall
grid[entrance.Y][entrance.X-1] = day18CellWall
grid[entrance.Y][entrance.X] = day18CellWall
grid[entrance.Y][entrance.X+1] = day18CellWall
grid[entrance.Y+1][entrance.X] = day18CellWall
return []day18Vec{
{X: entrance.X - 1, Y: entrance.Y - 1},
{X: entrance.X + 1, Y: entrance.Y - 1},
{X: entrance.X - 1, Y: entrance.Y + 1},
{X: entrance.X + 1, Y: entrance.Y + 1},
}
}
func (d *Day18) Part1() string {
// fmt.Println("initial state:")
// d.Draw(d.grid, d.keys, d.doors, d.entrance)
graph := d.buildGraph([]day18Vec{d.entrance}, d.keys, d.doors, d.grid)
minSteps := d.minimumSteps("1", len(d.keys), 0, graph)
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, minSteps, u.TextReset)
}
func (d *Day18) Part2() string {
// fmt.Println("initial state:")
grid := make([][]day18Cell, len(d.grid))
for i := range d.grid {
grid[i] = make([]day18Cell, len(d.grid[i]))
copy(grid[i], d.grid[i])
}
entrances := d.part2PatchMap(grid, d.entrance)
// d.Draw(grid, d.keys, d.doors, entrances...)
// clear memoized maps that (might have) came from part1
d.knownMinimumSteps = make(map[minStepsMemo]int)
d.knownReachableKeys = make(map[reachableKeysMemo][]u.Pair[rune, int])
graph := d.buildGraph(entrances, d.keys, d.doors, grid)
minSteps := d.minimumSteps("1234", len(d.keys), 0, graph)
return fmt.Sprintf("Total distance traveled: %s%d%s", u.TextBold, minSteps, u.TextReset)
}

122
days/19.go Normal file
View File

@ -0,0 +1,122 @@
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
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
y := u.Bisect(startY+100, 9999, threshold, func(y int) bool {
foundX := false
for x := startX; ; x++ {
if !f(x, y) {
if !foundX {
continue
} else {
return true
}
}
if !foundX {
foundX = true
}
if !f(x+99, y) {
return true
}
if !f(x, y+99) {
continue
}
lastGoodX = x
return false
}
})
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)
}

2
go.mod
View File

@ -1,3 +1,5 @@
module parnic.com/aoc2019
go 1.18
require github.com/edwingeng/deque/v2 v2.0.1

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/edwingeng/deque/v2 v2.0.1 h1:yNEsA9tUImO0vyw2hmVGiK4nnkoxBQ8stMYpdVq2ZmQ=
github.com/edwingeng/deque/v2 v2.0.1/go.mod h1:HukI8CQe9KDmZCcURPZRYVYjH79Zy2tIjTF9sN3Bgb0=

81
inputs/18p.txt Normal file
View File

@ -0,0 +1,81 @@
#################################################################################
#...............#.......#...............#.........#.......#.......#.....#.......#
#.#########.#.###.#####.#.#.#######.#####.#######.#.#######.#.###.###.#.#.#####.#
#.#.#.....#.#.#...#...#..f#...#.....#...#...#.....#.........#...#.....#...#m....#
#.#.#.###.###.#.#####.#######.#######.#.#.#.#.#######.#########.###########.#####
#...#...#.#...#.......#.....#.#.......#.#.#.#...#...#...#.......#..h......#.....#
###.###.#.#.#########.#.###.#T#.#######.###.###.#B#.#.###.#######.#######.#####.#
#...#...#...#.....#...#...#.#...#.....#.#...#.#...#.#.#...#.........#...#.......#
#####.#######.###.#.###.#.#######.###.#.#.###.#####.###.#.###########.#.#######.#
#...#...#.....#.#...#.#.#.....#...#.....#.#.....#...#...#.#...........#.#.....#.#
#.#.###.#.#####.#####.#.#####.#.###.#####.#.###.#.###.#####.###########.#.###.#.#
#.#.....#.#.....#.........#...#...#.#...#.#...#..x#...W.....#.....#.....#.#...#.#
#.#######.###.#.#########.#.#####.###.#.#.###.#######.#######.###.#.#####.#.#####
#.........#...#.......#.#.#.#...#.....#.#.#.#.#.....#...#.....#...#.#.....#.....#
#.#########O#########.#.#.#.#.#.#.#####.#.#.#.###.#.#.###.#####.###.#.#########.#
#.#.........#.........#...#.#.#.#...#.#.#.#.......#.#.#...#...#.#.......#.....#.#
#.#####.#########.#########.#.#.###.#.#.#.#########.###.###.###.#####.###.###.#.#
#.....#...#.....#.#.........#.#.#.....#.#.....#.....#...#...........#.#...#e..#.#
#####.###.#K###.#.#####.#####.#.#######.#####.#####.#.#.###########.###.###.###.#
#...#.....#.#...#.......#.....#.#.......#.....#...#...#.#.........#.....#.#.#...#
#.#####.###.#.#########.#.#####.#.#####.#.#####.#.#######.#####.#.#######.#.#.#.#
#.#j..#.#...#a..#.....#.#.#.......#.....#.......#...........#.#.#.......#.#...#.#
#.#.#.#.#.#####.#.#####.#.#############.#.###################.#########.#.#####.#
#...#.#.#.#...#...#.....#.#..z..#.....#.#.....#.......#.....#...#...#...#...#...#
#.###.#.#.###.#####.#####.#.###.#.###.#######.#.#.#####.###.###.#.#.#.###.###.###
#.#...#.#.....#.#...#...#.#...#.#.#.#...#.....#.#.....#...#.....#.#...#.#...#...#
###.###.#####.#.#.###.#.#.###.#.#.#.###.#.###########.###.#######.#####.#.#.###.#
#...#...#...#.#.......#.#.....#.#.....#.#.#.............#...#...#...#.....#.#...#
#.#.#.#####.#.#############.###.###.###.#.#.#########.#####.###.#.#.#####.#.#.###
#.#.#.#.....#.............#...#.....#...#.#.....#...#.#.....#...#.#.#...#.#.#.#.#
#.###.#.#.###############.###.#####.#.#.#.#####.#.#.#.#.#####.###.#.#.#.#.#.#.#.#
#.#...#.#.......#.........#...#...#.#.#.#.#.....#.#...#.#.........#.#.#.#.#...#.#
#.#.#######.###.#.#########.###.#.###.#.#.#.###########.#.#########.#.#.#####.#.#
#.#.......#...#.#.#.......#...#.#.#...#.#.#...#.......#.#.#.......#...#.#...#c..#
#.#######.#.#.###.###.###.#####.#.#.###.#.###.#.#####.#.###.#####.#.###.#.#.###.#
#.L..q..#.#.#...#...#.#.#.....#.#...#...#...#.....#...#.....#.....#...#...#.#...#
#.#######.#####.###.#.#.#####.#.#####.#####.#######S#########.#############.#.###
#...#...#.#.......#...#.#.....#.#...#...#...#.....#.....#...#.#.....#.....#k#...#
#.#.#.#.#.#.#####.#####.#.#####.#.#.###.#.###.###.###.#.###.#.#.###.#.###.#.###.#
#.#...#.....#...........#.......#.#...........#.......#.....#...#.....#.....#...#
#######################################.@.#######################################
#.#...#...........#u....#...#.......#...............#.....#...................#.#
#.#.#.#.#####.###.#.#.#.#.#.#.#.#####.#.#####.#####.#.###.#.###.#############.#.#
#.#.#...#...#...#.#.#.#.#.#...#.#s....#.#.....#...#.#.#.#..p#.#.#.....#.....#...#
#.#.#######.###.###.#.###.#####.#.#####.#.#######.#.#.#.#####.#.#.###.#.###.###.#
#...#.........#.#...#.#...#...#...#...#.#...#.....#...#.......#.#.#.#.....#.#.#.#
#.###.#.#####.#.#.###.#.###.#######.#.#.#.#.#.###.#####.#######.#.#.#######.#.#.#
#...#.#.#.....#...#...#.#...#.....#.#...#.#.#.#...#.....#.......#.#...#.V...#...#
###.###.#.#########.###.#.#.#.###.#.###.###.#.#####.#.###.#######.###.#.#####.###
#.#.....#.#.....#...#.....#.#...#...#...#...#.#.....#.......#...#.#...#.#.....#.#
#.#######.#N#.#.#.#.#.#########.#.#######.###.#.#############.#.#.#.#.#.#.#####.#
#.......#.#.#.#.#.#.#.#...#.....#.#.....#.#.........#.........#.#...#.#.#.....U.#
#.#######.###.#.#.###.#.#P#.#######.###.#.###.#######.#########.#####.#.#######.#
#.#.....#...#.#.#.....#.#...#.......#...#...#...#.....#.......#.......#.#...#...#
#.#.###.###.#.#########.###.#.#######.#####.###.#.#####.###.###########.#.#.#.###
#...#...#.#.#.#.....#...#...#.#.....#...#...#...#.......#...#.........#.#.#.#.#.#
#.###.#.#.#.#.#.#.###.#######.#.###.###.#.#####.#####.#####.#.#######.#.#.#Q#.#.#
#.#...#...#.#...#...#.....#...#.#...#...#.....#.....#.#.Yi#.#.#...#...#.#.#.#...#
#.#.#######.###.###.#####.#.###.#.#.#.###.###.#######.#.#.#.#.###.#.###.#.#.###.#
#.#.......#...#.#.....#...#.#...#.#.#...#.#...#.......#.#.#.#...#.....#.#.#.#...#
#.#######.###.###.#.###.###.#####.#####.#.#.#.#.#######.#.#.###.#####.#.#.#.#####
#.#.....#.....#...#.#.#.........#.....#.#.#.#.#.#...#...#.#.#.#.#...#...#.#.....#
#.#.###.#######.#####.#########.#.#.###.#.#.#.#.#.###.###.#.#.#.#.#.#####.#####.#
#.#.#.....#.........#.#.....#.#.#.#b#...#.#.#.#.#.#..y#.#.#.#.#.#.#...#...#.....#
#.###.#####.#######.#.#.###.#.#.#.#.#.#####.###.#.#.###.#.###.#.#.###.#.###.###.#
#.E.#....d..#.......#...#.#.#.#.#.#.#...#...#...#.#...#.#...I.#...#..l#.#...#...#
###.#.#######.###########F#.#.#.###.###.#.#.#.###.###.#.#########.#.###.#.###J###
#...#...#...#...........#...#.......#...#.#.#.#.....#.G.#.......#.#.....#.#.#.#.#
#.#####.#.#########.#.#.#.#########.#.#.#.###.#####.###.#.###.#.#####.###.#.#.#.#
#..v..#...#.......#.#.#.#.#...#.....#.#.#...#.......#.#.#...#.#.....#.#...#.#...#
#####.#####.#####.###.###.#.#.#.#####.#.#Z#.#######X#.#.#####.#####.#.#.###.###.#
#...#.....#...#.....#...#...#.#.#...#.#.#.#.......#...#.....#.....#.#.#.#...#...#
#.#######.#.#.#####.###.#####.#.#.###.#.###.#.#####.#######.#.###.#.###.#.###.###
#.........#.#.#...#...#...#...#...#...#.#...#.#...#.#.R....g#.#...#.....#.#...#.#
#.###########.#.#.###.###.#.###.###.###.#.#####.#.#.#.#########.#########.#.###.#
#...#.#.....#...#.#.#.#...#..t#.#...#...#.#.....#...#...#.....#.#..n....#.M.#...#
###.#.#.#.#.#####.#.#.#.#####.###.#####.#.#.###########.#.###.#.#.#######.###.#.#
#...#...#.#.#..w#.#.#.#.....#...#.....#.#.#.......#.....#.#...#.#.#.D...#.....#.#
#C#######.#.#.#.#.#.#.###.#.###.#####.#.#.#######.#.#####.#.###.#.#.###.#######H#
#......o..#...#...#.......#...#.A.....#.#.........#r......#.....#.....#.........#
#################################################################################

3
inputs/18s1.txt Normal file
View File

@ -0,0 +1,3 @@
#########
#b.A.@.a#
#########

5
inputs/18s2.txt Normal file
View File

@ -0,0 +1,5 @@
########################
#f.D.E.e.C.b.A.@.a.B.c.#
######################.#
#d.....................#
########################

5
inputs/18s3.txt Normal file
View File

@ -0,0 +1,5 @@
########################
#...............b.C.D.f#
#.######################
#.....@.a.B.c.d.A.e.F.g#
########################

9
inputs/18s4.txt Normal file
View File

@ -0,0 +1,9 @@
#################
#i.G..c...e..H.p#
########.########
#j.A..b...f..D.o#
########@########
#k.E..a...g..B.n#
########.########
#l.F..d...h..C.m#
#################

6
inputs/18s5.txt Normal file
View File

@ -0,0 +1,6 @@
########################
#@..............ac.GI.b#
###d#e#f################
###A#B#C################
###g#h#i################
########################

7
inputs/18s6.txt Normal file
View File

@ -0,0 +1,7 @@
#######
#a.#Cd#
##...##
##.@.##
##...##
#cB#Ab#
#######

7
inputs/18s7.txt Normal file
View File

@ -0,0 +1,7 @@
###############
#d.ABC.#.....a#
######...######
######.@.######
######...######
#b.....#.....c#
###############

7
inputs/18s8.txt Normal file
View File

@ -0,0 +1,7 @@
#############
#DcBa.#.GhKl#
#.###...#I###
#e#d#.@.#j#k#
###C#...###J#
#fEbA.#.FgHi#
#############

9
inputs/18s9.txt Normal file
View File

@ -0,0 +1,9 @@
#############
#g#f.D#..h#l#
#F###e#E###.#
#dCba...BcIJ#
#####.@.#####
#nK.L...G...#
#M###N#H###.#
#o#m..#i#jk.#
#############

1
inputs/19p.txt Normal file
View File

@ -0,0 +1 @@
109,424,203,1,21102,11,1,0,1105,1,282,21102,18,1,0,1105,1,259,1201,1,0,221,203,1,21101,31,0,0,1105,1,282,21101,38,0,0,1106,0,259,21002,23,1,2,22101,0,1,3,21102,1,1,1,21101,0,57,0,1105,1,303,1201,1,0,222,20102,1,221,3,21001,221,0,2,21101,259,0,1,21101,0,80,0,1105,1,225,21102,1,76,2,21101,91,0,0,1106,0,303,1201,1,0,223,21001,222,0,4,21102,1,259,3,21101,0,225,2,21102,1,225,1,21102,1,118,0,1106,0,225,20101,0,222,3,21101,100,0,2,21102,1,133,0,1105,1,303,21202,1,-1,1,22001,223,1,1,21101,148,0,0,1105,1,259,2102,1,1,223,20102,1,221,4,21001,222,0,3,21101,0,17,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,0,195,0,106,0,109,20207,1,223,2,21002,23,1,1,21102,1,-1,3,21101,214,0,0,1105,1,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,1201,-4,0,249,22101,0,-3,1,21201,-2,0,2,22102,1,-1,3,21101,0,250,0,1106,0,225,22101,0,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,2105,1,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,22101,0,-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,22101,0,-2,3,21102,1,343,0,1105,1,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,21102,1,384,0,1106,0,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,21201,1,0,-4,109,-5,2106,0,0

View File

@ -51,6 +51,8 @@ var dayMap = []day{
&days.Day15{},
&days.Day16{},
&days.Day17{},
&days.Day18{},
&days.Day19{},
}
func main() {

28
utilities/bisect.go Normal file
View File

@ -0,0 +1,28 @@
package utilities
import (
"math"
)
// Bisect takes a known-good low and known-bad high value as the bounds
// to bisect, and a function to test each value for success or failure.
// If the function succeeds, the value is adjusted toward the maximum,
// and if the function fails, the value is adjusted toward the minimum.
// The final value is returned when the difference between the success
// and the failure is less than or equal to the acceptance threshold
// (usually 1, for integers).
func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T {
currVal := low
for T(math.Abs(float64(high-low))) > threshold {
currVal = low + ((high - low) / 2)
success := tryFunc(currVal)
if success {
low = currVal
} else {
high = currVal
}
}
return currVal
}

View File

@ -128,9 +128,15 @@ func (p *IntcodeProgram) ensureMemoryCapacity(address int) {
}
func (p *IntcodeProgram) Reset() {
p.memory = nil
wiped := false
if len(p.memory) != len(p.program) {
wiped = true
p.memory = nil
}
p.init()
copy(p.memory, p.program)
if !wiped {
copy(p.memory, p.program)
}
p.relativeBase = 0
}

View File

@ -15,3 +15,13 @@ func MapValues[T comparable, U any](m map[T]U) []U {
}
return r
}
// CopyMap returns a copy of the passed-in map. Note: currently only works if [U]
// is not a map or slice.
func CopyMap[T comparable, U any](m map[T]U) map[T]U {
r := make(map[T]U)
for k, v := range m {
r[k] = v
}
return r
}

View File

@ -44,11 +44,16 @@ func (v Vec2[T]) Equals(other Vec2[T]) bool {
v.Y == other.Y
}
func (v Vec2[T]) ManhattanDistance(other Vec2[T]) T {
return T(math.Abs(float64(v.X-other.X)) + math.Abs(float64(v.Y-other.Y)))
}
func VecBetween[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{
X: a.X - b.X,
Y: a.Y - b.Y,
}
return a.To(b)
}
func ManhattanDistance[T Number](a, b Vec2[T]) T {
return a.ManhattanDistance(b)
}
func (v Vec3[T]) Dot(other Vec3[T]) T {