29 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
a0867490c9 Add utility for array AddUnique 2022-06-18 23:07:38 -05:00
3d3ea6e266 Add support for cpu and memory profiling
My day 18 was slow and I wasn't completely certain where it was all coming from, so I dove into Go's profiling stuff. It was super helpful in identifying the problem and giving me easy wins that took runtime from 4.6s/14.4s (part 1/part 2) to 180ms/50ms just from swapping some arrays for maps, which the profile pointed out very clearly.
2022-06-18 23:07:38 -05:00
4b5bd0d4e0 Display part1 output as soon as it's ready
When we start having problems that take a while to run, it sucks to have to wait for everything to finish before we see any output.
2022-06-18 22:49:28 -05:00
5bc089c83d Day 17 solution
My initial idea for a solution ended up working out great, but I had a hard time getting it all down on "paper," so to speak, so this is probably 200 lines of code too many, but it definitely does exactly what I envisioned (and quickly) even if it's not the most succinct.
2022-06-14 14:37:06 -05:00
c788813cd2 Day 16 solution
Following the formula for part 1 was straightforward enough, but finding the pattern for part 2 and deducing the shortcut formula took me a while. That first 0 1 propagate enough that by the time we get halfway through applying the formula, it's all 0s and 1s, so sort of like an addition with a mask on what numbers we're adding.
2022-06-13 15:30:17 -05:00
788239e531 Day 15 solution
I wanted to use something like a right-hand wall solver, but the fact that you don't know the maze ahead of time and you can't see what something is without trying to move into it made that difficult. This semi-brute-force approach works well enough. I originally stopped as soon as I found the oxygen system and figured out the shortest path, but once I submitted that answer and saw that part 2 wanted the full map explored, I figured I might as well just fill the map all at once.

I think I would have been stuck on part 1 longer if my input set didn't happen to find the goal system fairly easily (or maybe my debug drawing helped me work through it with that input set specifically, I'm not sure) since a different input set required some tweaking to the max-visited threshold in order to find things that my first input set found with a lower setting.

Regardless, I'm pretty excited that I came to Trémaux's algorithm, more or less, on my own. I went to Wikipedia to see if I was on the right track and lo and behold, I had come to a version of it myself.

Part 2 turned out easier than I originally thought. I suspected this solution would work, but wasn't completely confident. It can only work for the type of maze used by this problem (where there are no loops of open areas). I'm just glad I didn't need A* or anything.

Oh, and this `stringer` command that allows debug printing of enums can be installed with `go install golang.org/x/tools/cmd/stringer@latest`
2022-06-13 15:30:17 -05:00
37928d7138 Day 14 solution
This one's part 1 destroyed me. I had a very difficult time, trying 3 separate approaches, each one of which worked for most cases but eventually fell apart on the 5th sample or my actual puzzle input. I ended up reading a bunch of hints from the subreddit which eventually led me to a blog post describing this solution, which wasn't far off from what I had, but I was overcomplicating things.

Part 2 surprised me in that I expected a simple "ore available divided by ore needed for 1 fuel" would solve it, but of course the excess chemicals produced in any given reaction meant that it wasn't that simple. So this approach uses that estimate as a lower bound, since it always underestimates, and then bisects its way to the solution (starting at the lower bound and adding 1 each time took too long). I'm sure a smarter upper bound choice could lower the runtime of this by a bit, but runtime isn't bad enough right now for me to try any additional optimizations.
2022-06-13 15:30:17 -05:00
da5823aa17 Day 13 solution
This was incredibly cool and I had a really fun time with it. Uncomment everything to see the game play itself! Note that I'm not seeking around in the terminal window to make the drawing smooth, I'm just outputting each new frame as it happens, so there's some jitter, but it still looks great!

I messed around a bit with control codes to move the cursor around instead of the "draw the buffer over and over again" approach, and they work, mostly, but I'm sticking with this for now.
2022-06-13 15:30:17 -05:00
8282e09a42 Day 12 solution
Okay, I had to seek help on this one. The orbital period + least-common-multiple solution was not coming to me.
2022-06-13 15:30:17 -05:00
343007481a Day 11 solution 2022-06-13 15:30:17 -05:00
44eeb5a8a6 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.
2022-06-13 15:30:17 -05:00
f8d2758c90 Move Pair to a more reusable location
I originally used this in my day 10 solution, but ended up removing it. Either way, it's a general utility so it belongs here.
2022-06-13 15:30:17 -05:00
d9e0d9b649 Day 9 solution
This day showed me that when the input instruction was introduced and said "write addresses will never be in immediate mode", that didn't mean "so don't bother handling modes for input addresses", it meant "handle the mode, but assert if it's immediate mode". It was super helpful that this program contained a bootstrap sequence to validate each instruction.

Memory expansion came with a few caveats: obviously reads and writes needed to handle expanding the memory space, but a Reset also can no longer get away with simply copying the program into memory again because we need to ensure that any additional memory is cut off (or at least zeroed), so the quickest way to handle that in Go is to simply allocate a new buffer; I'd rather manipulate the existing buffer, but I'm having a hard time finding the best way to do that.

And finally, make sure you reset your relativeBase when resetting the program...that one was ugly to track down.
2022-06-13 15:30:17 -05:00
94d83695bf Day 8 solution
I had fun with this one. I liked how straightforward it was, and it's always satisfying to see the code print a message visually when you're done.
2022-06-13 15:30:17 -05:00
365edf82b1 Day 7 solution
I will probably end up regretting this since I assume the "wait to be given an input from some other process before continuing execution" paradigm is going to come up again, but this part 2 goroutine+channel solution felt good (taking advantage of Go features) and made me happy, so I rolled with it.
2022-06-13 15:30:16 -05:00
a099a86511 Day 6 solution
I'm reasonably happy with this. I started with a bi-directional linked list, but realized that a flat list of all nodes came in handy for one use case while the linked list came in handy for another, so I settled on that.
2022-06-13 15:30:10 -05:00
acef5fdc12 Day 5 solution
This required an overhaul of the intcode machine to actually be its own type that could operate on its own memory and stuff. So I had to touch day 2 to make it adhere to the new API.

Feeling good about this foundation now. Until I get gobsmacked at some point later, which I expect to happen.
2022-06-13 15:29:18 -05:00
bdd007bb4d Reduce changes needed to the template
I always end up making these changes each time I copy from the template.
2022-06-12 13:37:24 -05:00
84 changed files with 4702 additions and 104 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
__debug_bin
aoc2019
debug.test
*.*prof

View File

@ -18,19 +18,20 @@ func (d Day02) Num() int {
return 2
}
func (d *Day02) getProgramWithParams(param1, param2 int64) utilities.IntcodeProgram {
program := make(utilities.IntcodeProgram, len(d.program))
copy(program, d.program)
program[1] = param1
program[2] = param2
return program
func (d *Day02) setParams(param1, param2 int64) {
d.program.Reset()
d.program.SetMemory(1, param1)
d.program.SetMemory(2, param2)
}
func (d *Day02) Part1() string {
program := d.getProgramWithParams(12, 2)
program.Run()
d.setParams(12, 2)
d.program.Run()
return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, program[0], utilities.TextReset)
if d.program.GetMemory(0) != 4138658 {
panic("")
}
return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, d.program.GetMemory(0), utilities.TextReset)
}
func (d *Day02) Part2() string {
@ -41,10 +42,10 @@ func (d *Day02) Part2() string {
found := false
for noun = 0; noun <= 99; noun++ {
for verb = 0; verb <= 99; verb++ {
program := d.getProgramWithParams(noun, verb)
program.Run()
d.setParams(noun, verb)
d.program.Run()
if program[0] == sentinel {
if d.program.GetMemory(0) == sentinel {
found = true
break
}
@ -58,6 +59,9 @@ func (d *Day02) Part2() string {
if !found {
panic("!found")
}
if noun != 72 || verb != 64 {
panic("")
}
return fmt.Sprintf("%d created by noun=%d, verb=%d. 100 * noun + verb = %s%d%s",
sentinel,

View File

@ -6,29 +6,24 @@ import (
"strconv"
"strings"
"parnic.com/aoc2019/utilities"
u "parnic.com/aoc2019/utilities"
)
type Pair[T, U any] struct {
a T
b U
}
type Day03 struct {
line1 []Pair[byte, int]
line2 []Pair[byte, int]
visited map[Pair[int, int]]int
overlaps []Pair[Pair[int, int], int]
line1 []u.Pair[byte, int]
line2 []u.Pair[byte, int]
visited map[u.Pair[int, int]]int
overlaps []u.Pair[u.Pair[int, int], int]
}
func (d *Day03) Parse() {
lines := utilities.GetStringLines("03p")
lines := u.GetStringLines("03p")
line1data := strings.Split(lines[0], ",")
line2data := strings.Split(lines[1], ",")
d.line1 = make([]Pair[byte, int], len(line1data))
d.line2 = make([]Pair[byte, int], len(line2data))
d.line1 = make([]u.Pair[byte, int], len(line1data))
d.line2 = make([]u.Pair[byte, int], len(line2data))
for idx, instr := range line1data {
dir := instr[0]
@ -38,7 +33,7 @@ func (d *Day03) Parse() {
panic(err)
}
d.line1[idx] = Pair[byte, int]{a: dir, b: iAmt}
d.line1[idx] = u.Pair[byte, int]{First: dir, Second: iAmt}
}
for idx, instr := range line2data {
@ -49,7 +44,7 @@ func (d *Day03) Parse() {
panic(err)
}
d.line2[idx] = Pair[byte, int]{a: dir, b: iAmt}
d.line2[idx] = u.Pair[byte, int]{First: dir, Second: iAmt}
}
}
@ -58,96 +53,96 @@ func (d Day03) Num() int {
}
func (d *Day03) Part1() string {
d.visited = make(map[Pair[int, int]]int)
d.visited = make(map[u.Pair[int, int]]int)
var x int
var y int
var steps int
for _, inst := range d.line1 {
switch inst.a {
switch inst.First {
case 'R':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
d.visited[Pair[int, int]{a: x + i, b: y}] = steps
d.visited[u.Pair[int, int]{First: x + i, Second: y}] = steps
}
x += inst.b
x += inst.Second
case 'U':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
d.visited[Pair[int, int]{a: x, b: y + i}] = steps
d.visited[u.Pair[int, int]{First: x, Second: y + i}] = steps
}
y += inst.b
y += inst.Second
case 'L':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
d.visited[Pair[int, int]{a: x - i, b: y}] = steps
d.visited[u.Pair[int, int]{First: x - i, Second: y}] = steps
}
x -= inst.b
x -= inst.Second
case 'D':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
d.visited[Pair[int, int]{a: x, b: y - i}] = steps
d.visited[u.Pair[int, int]{First: x, Second: y - i}] = steps
}
y -= inst.b
y -= inst.Second
}
}
x = 0
y = 0
steps = 0
d.overlaps = make([]Pair[Pair[int, int], int], 0)
d.overlaps = make([]u.Pair[u.Pair[int, int], int], 0)
for _, inst := range d.line2 {
switch inst.a {
switch inst.First {
case 'R':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
if _, exists := d.visited[Pair[int, int]{x + i, y}]; exists {
d.overlaps = append(d.overlaps, Pair[Pair[int, int], int]{a: Pair[int, int]{x + i, y}, b: steps})
if _, exists := d.visited[u.Pair[int, int]{First: x + i, Second: y}]; exists {
d.overlaps = append(d.overlaps, u.Pair[u.Pair[int, int], int]{First: u.Pair[int, int]{First: x + i, Second: y}, Second: steps})
}
}
x += inst.b
x += inst.Second
case 'U':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
if _, exists := d.visited[Pair[int, int]{x, y + i}]; exists {
d.overlaps = append(d.overlaps, Pair[Pair[int, int], int]{a: Pair[int, int]{x, y + i}, b: steps})
if _, exists := d.visited[u.Pair[int, int]{First: x, Second: y + i}]; exists {
d.overlaps = append(d.overlaps, u.Pair[u.Pair[int, int], int]{First: u.Pair[int, int]{First: x, Second: y + i}, Second: steps})
}
}
y += inst.b
y += inst.Second
case 'L':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
if _, exists := d.visited[Pair[int, int]{x - i, y}]; exists {
d.overlaps = append(d.overlaps, Pair[Pair[int, int], int]{a: Pair[int, int]{x - i, y}, b: steps})
if _, exists := d.visited[u.Pair[int, int]{First: x - i, Second: y}]; exists {
d.overlaps = append(d.overlaps, u.Pair[u.Pair[int, int], int]{First: u.Pair[int, int]{First: x - i, Second: y}, Second: steps})
}
}
x -= inst.b
x -= inst.Second
case 'D':
for i := 1; i <= inst.b; i++ {
for i := 1; i <= inst.Second; i++ {
steps++
if _, exists := d.visited[Pair[int, int]{x, y - i}]; exists {
d.overlaps = append(d.overlaps, Pair[Pair[int, int], int]{a: Pair[int, int]{x, y - i}, b: steps})
if _, exists := d.visited[u.Pair[int, int]{First: x, Second: y - i}]; exists {
d.overlaps = append(d.overlaps, u.Pair[u.Pair[int, int], int]{First: u.Pair[int, int]{First: x, Second: y - i}, Second: steps})
}
}
y -= inst.b
y -= inst.Second
}
}
minDist := math.MaxInt
for _, overlap := range d.overlaps {
dist := int(math.Abs(float64(overlap.a.a))) + int(math.Abs(float64(overlap.a.b)))
dist := int(math.Abs(float64(overlap.First.First))) + int(math.Abs(float64(overlap.First.Second)))
if dist < minDist {
minDist = dist
}
}
return fmt.Sprintf("Closest overlap manhattan distance = %s%d%s", utilities.TextBold, minDist, utilities.TextReset)
return fmt.Sprintf("Closest overlap manhattan distance = %s%d%s", u.TextBold, minDist, u.TextReset)
}
func (d *Day03) Part2() string {
minOverlap := math.MaxInt
for _, overlap := range d.overlaps {
line1Steps := d.visited[overlap.a]
line2Steps := overlap.b
line1Steps := d.visited[overlap.First]
line2Steps := overlap.Second
totalSteps := line1Steps + line2Steps
if totalSteps < minOverlap {
@ -155,5 +150,5 @@ func (d *Day03) Part2() string {
}
}
return fmt.Sprintf("Minimum steps to overlap = %s%d%s", utilities.TextBold, minOverlap, utilities.TextReset)
return fmt.Sprintf("Minimum steps to overlap = %s%d%s", u.TextBold, minOverlap, u.TextReset)
}

188
days/05.go Normal file
View File

@ -0,0 +1,188 @@
package days
import (
"fmt"
"parnic.com/aoc2019/utilities"
)
type Day05 struct {
program utilities.IntcodeProgram
}
func (d *Day05) Parse() {
d.program = utilities.LoadIntcodeProgram("05p")
d.test()
}
func (d Day05) Num() int {
return 5
}
func (d Day05) test() {
// Using position mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not).
program := utilities.ParseIntcodeProgram("3,9,8,9,10,9,4,9,99,-1,8")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// Using position mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,9,7,9,10,9,4,9,99,-1,8")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
// Using immediate mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,3,1108,-1,8,3,4,3,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// Using immediate mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,3,1107,-1,8,3,4,3,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
// jump tests that take an input, then output 0 if the input was zero or 1 if the input was non-zero
// position mode
program = utilities.ParseIntcodeProgram("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// jump tests that take an input, then output 0 if the input was zero or 1 if the input was non-zero
// immediate mode
program = utilities.ParseIntcodeProgram("3,3,1105,-1,9,1101,0,0,12,4,12,99,1")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// uses an input instruction to ask for a single number. The program will then output 999 if the input value is below 8, output 1000 if the input value is equal to 8, or output 1001 if the input value is greater than 8.
program = utilities.ParseIntcodeProgram("3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 999 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1000 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 9
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1001 {
panic("")
}
})
}
func (d *Day05) Part1() string {
diagCode := int64(-1)
d.program.RunIn(func(int) int64 {
return 1
}, func(val int64, state utilities.IntcodeProgramState) {
if state.IsHalting() {
diagCode = val
} else if val != 0 {
panic("test failed")
}
})
return fmt.Sprintf("Diagnostic code: %s%d%s", utilities.TextBold, diagCode, utilities.TextReset)
}
func (d *Day05) Part2() string {
d.program.Reset()
diagCode := int64(-1)
d.program.RunIn(func(int) int64 {
return 5
}, func(val int64, state utilities.IntcodeProgramState) {
if !state.IsHalting() {
panic("unexpected output received")
}
diagCode = val
})
return fmt.Sprintf("Diagnostic code: %s%d%s", utilities.TextBold, diagCode, utilities.TextReset)
}

114
days/06.go Normal file
View File

@ -0,0 +1,114 @@
package days
import (
"fmt"
"strings"
"parnic.com/aoc2019/utilities"
)
type body struct {
orbits *body
obj string
}
type Day06 struct {
allBodies []*body
}
func (d *Day06) Parse() {
d.allBodies = make([]*body, 0)
getOrAddBody := func(obj string) *body {
target := d.findBody(obj)
if target == nil {
target = &body{
obj: obj,
}
d.allBodies = append(d.allBodies, target)
}
return target
}
lines := utilities.GetStringLines("06p")
for _, line := range lines {
bodies := strings.Split(line, ")")
newBody := getOrAddBody(bodies[1])
target := getOrAddBody(bodies[0])
newBody.orbits = target
}
}
func (d *Day06) findBody(obj string) *body {
for _, checkBody := range d.allBodies {
if checkBody.obj == obj {
return checkBody
}
}
return nil
}
func (d Day06) Num() int {
return 6
}
func (d *Day06) Part1() string {
orbits := 0
for _, obj := range d.allBodies {
next := obj.orbits
for next != nil {
next = next.orbits
orbits++
}
}
return fmt.Sprintf("Total orbits: %s%d%s", utilities.TextBold, orbits, utilities.TextReset)
}
func (d *Day06) Part2() string {
you := d.findBody("YOU")
san := d.findBody("SAN")
youChildren := make([]*body, 0)
next := you.orbits
for next != nil {
youChildren = append(youChildren, next)
next = next.orbits
}
var linkingNode *body
next = san.orbits
for next != nil {
if utilities.ArrayContains(youChildren, next) {
linkingNode = next
break
}
next = next.orbits
}
if linkingNode == nil {
panic("")
}
getDistToLinking := func(start *body) int {
dist := 0
next = start.orbits
for next != nil {
if next == linkingNode {
break
}
dist++
next = next.orbits
}
return dist
}
distYouToLinking := getDistToLinking(you)
distSanToLinking := getDistToLinking(san)
return fmt.Sprintf("Transfers to get to Santa: %s%d%s", utilities.TextBold, distYouToLinking+distSanToLinking, utilities.TextReset)
}

116
days/07.go Normal file
View File

@ -0,0 +1,116 @@
package days
import (
"fmt"
"sync"
"parnic.com/aoc2019/utilities"
)
type Day07 struct {
program utilities.IntcodeProgram
amps []utilities.IntcodeProgram
}
func (d *Day07) Parse() {
d.program = utilities.LoadIntcodeProgram("07p")
d.amps = make([]utilities.IntcodeProgram, 5)
for i := range d.amps {
d.amps[i] = d.program.Copy()
}
}
func (d Day07) Num() int {
return 7
}
func (d *Day07) Part1() string {
var highestVal int64
var highestSequence []int64
allSequences := utilities.GetPermutations([]int64{0, 1, 2, 3, 4}...)
for _, sequence := range allSequences {
if len(sequence) != len(d.amps) {
panic("input sequence does not match up to number of amplifiers")
}
input := int64(0)
var output int64
for i, amp := range d.amps {
amp.RunIn(func(step int) int64 {
if step == 1 {
return sequence[i]
} else if step == 2 {
return input
}
panic("hit more input instructions than expected")
}, func(val int64, state utilities.IntcodeProgramState) {
output = val
})
input = output
}
if output > highestVal {
highestVal = output
if highestSequence == nil {
highestSequence = make([]int64, len(sequence))
}
copy(highestSequence, sequence)
}
}
return fmt.Sprintf("Max thruster signal: %s%d%s (produced by %v)", utilities.TextBold, highestVal, utilities.TextReset, highestSequence)
}
func (d *Day07) Part2() string {
var highestVal int64
var highestSequence []int64
allSequences := utilities.GetPermutations([]int64{5, 6, 7, 8, 9}...)
for _, sequence := range allSequences {
if len(sequence) != len(d.amps) {
panic("input sequence does not match up to number of amplifiers")
}
inputs := make([]chan int64, len(d.amps))
for i := range d.amps {
d.amps[i].Reset()
inputs[i] = make(chan int64, 1)
inputs[i] <- sequence[i]
}
var finalOutput int64
var wg sync.WaitGroup
for i := range d.amps {
wg.Add(1)
go func(idx int) {
d.amps[idx].RunIn(func(step int) int64 {
input := <-inputs[idx]
return input
}, func(val int64, state utilities.IntcodeProgramState) {
finalOutput = val
inputIdx := idx + 1
if inputIdx == len(inputs) {
inputIdx = 0
}
inputs[inputIdx] <- val
})
wg.Done()
}(i)
}
inputs[0] <- 0
wg.Wait()
if finalOutput > highestVal {
highestVal = finalOutput
if highestSequence == nil {
highestSequence = make([]int64, len(sequence))
}
copy(highestSequence, sequence)
}
}
return fmt.Sprintf("Max thruster signal: %s%d%s (produced by %v)", utilities.TextBold, highestVal, utilities.TextReset, highestSequence)
}

102
days/08.go Normal file
View File

@ -0,0 +1,102 @@
package days
import (
"fmt"
"math"
"strings"
"parnic.com/aoc2019/utilities"
)
type Day08 struct {
img [][]int
}
const (
imgWidth = 25
imgHeight = 6
)
func (d *Day08) Parse() {
contents := utilities.GetStringContents("08p")
imgSize := imgWidth * imgHeight
layers := len(contents) / imgSize
d.img = make([][]int, layers)
for layer := 0; layer < layers; layer++ {
d.img[layer] = make([]int, imgSize)
for i := 0; i < imgSize; i++ {
d.img[layer][i] = int(contents[(layer*imgSize)+i] - '0')
}
}
}
func (d Day08) Num() int {
return 8
}
func (d *Day08) Part1() string {
fewestZeroes := math.MaxInt
var layerFewestZeroes int
for layer := range d.img {
zeroes := 0
for i := range d.img[layer] {
if d.img[layer][i] == 0 {
zeroes++
}
}
if zeroes < fewestZeroes {
fewestZeroes = zeroes
layerFewestZeroes = layer
}
}
numOne := 0
numTwo := 0
for i := range d.img[layerFewestZeroes] {
if d.img[layerFewestZeroes][i] == 1 {
numOne++
} else if d.img[layerFewestZeroes][i] == 2 {
numTwo++
}
}
return fmt.Sprintf("Fewest zeroes on layer %d, #1s * #2s = %d * %d = %s%d%s",
layerFewestZeroes,
numOne,
numTwo,
utilities.TextBold,
numOne*numTwo,
utilities.TextReset,
)
}
func (d *Day08) Part2() string {
imgSize := imgWidth * imgHeight
finalImg := make([]int, imgSize)
for i := 0; i < imgSize; i++ {
for layer := 0; layer < len(d.img); layer++ {
if d.img[layer][i] != 2 {
finalImg[i] = d.img[layer][i]
break
}
}
}
outStr := strings.Builder{}
outStr.WriteString("Message received:\n")
outStr.WriteString(utilities.TextBold)
for y := 0; y < imgHeight; y++ {
for x := 0; x < imgWidth; x++ {
if finalImg[(y*imgWidth)+x] == 0 {
outStr.WriteRune(' ')
} else {
outStr.WriteRune('█')
}
}
outStr.WriteRune('\n')
}
outStr.WriteString(utilities.TextReset)
return outStr.String()
}

42
days/09.go Normal file
View File

@ -0,0 +1,42 @@
package days
import (
"fmt"
"parnic.com/aoc2019/utilities"
)
type Day09 struct {
program utilities.IntcodeProgram
}
func (d *Day09) Parse() {
d.program = utilities.LoadIntcodeProgram("09p")
}
func (d Day09) Num() int {
return 9
}
func (d *Day09) Part1() string {
var code int64
d.program.RunIn(func(inputStep int) int64 {
return 1
}, func(val int64, state utilities.IntcodeProgramState) {
code = val
})
return fmt.Sprintf("BOOST keycode: %s%d%s", utilities.TextBold, code, utilities.TextReset)
}
func (d *Day09) Part2() string {
var coordinates int64
d.program.Reset()
d.program.RunIn(func(inputStep int) int64 {
return 2
}, func(val int64, state utilities.IntcodeProgramState) {
coordinates = val
})
return fmt.Sprintf("Coordinates: %s%d%s", utilities.TextBold, coordinates, utilities.TextReset)
}

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

118
days/11.go Normal file
View File

@ -0,0 +1,118 @@
package days
import (
"fmt"
"strings"
u "parnic.com/aoc2019/utilities"
)
type Day11 struct {
program u.IntcodeProgram
painted map[u.Pair[int, int]]int
}
func (d *Day11) Parse() {
d.program = u.LoadIntcodeProgram("11p")
}
func (d Day11) Num() int {
return 11
}
func (d *Day11) paintHull() (int, u.Pair[int, int], u.Pair[int, int]) {
pos := u.Pair[int, int]{First: 0, Second: 0}
facing := 0
min := pos
max := pos
outputState := 0
numPainted := 1
d.program.RunIn(func(inputStep int) int64 {
return int64(d.painted[pos])
}, func(val int64, state u.IntcodeProgramState) {
if outputState == 0 {
outputState++
color := int(val)
if _, exists := d.painted[pos]; !exists {
numPainted++
}
d.painted[pos] = color
} else {
outputState = 0
direction := val
if direction == 0 {
facing--
if facing == -1 {
facing = 3
}
} else {
facing++
if facing == 4 {
facing = 0
}
}
switch facing {
case 0:
pos.First--
if pos.First < min.First {
min.First = pos.First
}
case 1:
pos.Second++
if pos.Second > max.Second {
max.Second = pos.Second
}
case 2:
pos.First++
if pos.First > max.First {
max.First = pos.First
}
case 3:
pos.Second--
if pos.Second < min.Second {
min.Second = pos.Second
}
}
}
})
return numPainted, min, max
}
func (d *Day11) Part1() string {
d.painted = map[u.Pair[int, int]]int{
{First: 0, Second: 0}: 0,
}
numPainted, _, _ := d.paintHull()
return fmt.Sprintf("Unique panels painted: %s%d%s", u.TextBold, numPainted, u.TextReset)
}
func (d *Day11) Part2() string {
d.painted = map[u.Pair[int, int]]int{
{First: 0, Second: 0}: 1,
}
_, min, max := d.paintHull()
outStr := strings.Builder{}
outStr.WriteString("Registration identifier:\n")
outStr.WriteString(u.TextBold)
for x := min.First; x <= max.First; x++ {
for y := min.Second; y <= max.Second; y++ {
val, exists := d.painted[u.Pair[int, int]{First: x, Second: y}]
if exists && val == 1 {
outStr.WriteRune('█')
} else {
outStr.WriteRune(' ')
}
}
outStr.WriteRune('\n')
}
outStr.WriteString(u.TextReset)
return outStr.String()
}

156
days/12.go Normal file
View File

@ -0,0 +1,156 @@
package days
import (
"fmt"
"math"
"strconv"
"strings"
u "parnic.com/aoc2019/utilities"
)
type moonData struct {
pos u.Vec3[int]
vel u.Vec3[int]
}
func (m *moonData) applyGravity(other *moonData) {
applyGravityAxis := func(pos1, pos2, vel1, vel2 *int) {
if *pos1 < *pos2 {
*vel1++
*vel2--
} else if *pos1 > *pos2 {
*vel1--
*vel2++
}
}
applyGravityAxis(&m.pos.X, &other.pos.X, &m.vel.X, &other.vel.X)
applyGravityAxis(&m.pos.Y, &other.pos.Y, &m.vel.Y, &other.vel.Y)
applyGravityAxis(&m.pos.Z, &other.pos.Z, &m.vel.Z, &other.vel.Z)
}
func (m *moonData) applyVelocity() {
m.pos.Add(m.vel)
}
func (m moonData) getPotentialEnergy() int {
return int(math.Abs(float64(m.pos.X))) +
int(math.Abs(float64(m.pos.Y))) +
int(math.Abs(float64(m.pos.Z)))
}
func (m moonData) getKineticEnergy() int {
return int(math.Abs(float64(m.vel.X))) +
int(math.Abs(float64(m.vel.Y))) +
int(math.Abs(float64(m.vel.Z)))
}
func (m moonData) getTotalEnergy() int {
return m.getPotentialEnergy() * m.getKineticEnergy()
}
type Day12 struct {
moons []*moonData
}
func (d *Day12) Parse() {
lines := u.GetStringLines("12p")
d.moons = make([]*moonData, len(lines))
for i, line := range lines {
trimmed := line[1 : len(line)-1]
vals := strings.Split(trimmed, ", ")
x, _ := strconv.Atoi(vals[0][2:])
y, _ := strconv.Atoi(vals[1][2:])
z, _ := strconv.Atoi(vals[2][2:])
d.moons[i] = &moonData{
pos: u.Vec3[int]{X: x, Y: y, Z: z},
}
}
}
func (d Day12) Num() int {
return 12
}
func (d Day12) copyMoons() []*moonData {
moons := make([]*moonData, len(d.moons))
for i, moon := range d.moons {
moonCopy := *moon
moons[i] = &moonCopy
}
return moons
}
func getAllEnergy(moons ...*moonData) int {
energy := 0
for _, moon := range moons {
energy += moon.getTotalEnergy()
}
return energy
}
func (d *Day12) Part1() string {
moons := d.copyMoons()
numSteps := 1000
for i := 0; i < numSteps; i++ {
for i, moon1 := range moons {
for _, moon2 := range moons[i+1:] {
moon1.applyGravity(moon2)
}
moon1.applyVelocity()
}
}
return fmt.Sprintf("Total energy after %d steps: %s%d%s", numSteps, u.TextBold, getAllEnergy(moons...), u.TextReset)
}
func (d *Day12) Part2() string {
moons := d.copyMoons()
orig := make([]u.Vec3[int], len(moons))
for i, moon := range moons {
orig[i] = moon.pos
}
period := u.Vec3[int]{}
for loops := 0; period.X == 0 || period.Y == 0 || period.Z == 0; loops++ {
for i, moon1 := range moons {
for _, moon2 := range moons[i+1:] {
moon1.applyGravity(moon2)
}
moon1.applyVelocity()
}
foundX := true
foundY := true
foundZ := true
for i, moon := range moons {
if moon.pos.X != orig[i].X || moon.vel.X != 0 {
foundX = false
}
if moon.pos.Y != orig[i].Y || moon.vel.Y != 0 {
foundY = false
}
if moon.pos.Z != orig[i].Z || moon.vel.Z != 0 {
foundZ = false
}
}
if foundX && period.X == 0 {
period.X = loops + 1
}
if foundY && period.Y == 0 {
period.Y = loops + 1
}
if foundZ && period.Z == 0 {
period.Z = loops + 1
}
}
stepsRequired := u.LCM(period.X, period.Y, period.Z)
return fmt.Sprintf("Iterations to reach a previous state: %s%d%s", u.TextBold, stepsRequired, u.TextReset)
}

147
days/13.go Normal file
View File

@ -0,0 +1,147 @@
package days
import (
"fmt"
u "parnic.com/aoc2019/utilities"
)
const (
tileEmpty = 0
tileWall = 1
tileBlock = 2
tileHPaddle = 3
tileBall = 4
)
type tile struct {
pos u.Vec2[int]
id int
}
type Day13 struct {
program u.IntcodeProgram
tiles []tile
gameBoard [24][45]int
}
func (d *Day13) Parse() {
d.program = u.LoadIntcodeProgram("13p")
d.tiles = make([]tile, 0, 1080)
}
func (d Day13) Num() int {
return 13
}
func (d Day13) getNumBlocks() int {
blockTiles := 0
for _, tile := range d.tiles {
if tile.id == tileBlock {
blockTiles++
}
}
return blockTiles
}
// func (d Day13) drawGameBoard() {
// s := strings.Builder{}
// for x := range d.gameBoard {
// for y := range d.gameBoard[x] {
// block := d.gameBoard[x][y]
// if block == tileBlock {
// s.WriteString(u.ColorBlue)
// s.WriteRune('█')
// s.WriteString(u.TextReset)
// } else if block == tileBall {
// s.WriteString(u.ColorGreen)
// s.WriteRune('█')
// s.WriteString(u.TextReset)
// } else if block == tileWall {
// s.WriteString(u.ColorWhite)
// s.WriteRune('█')
// s.WriteString(u.TextReset)
// } else if block == tileHPaddle {
// s.WriteString(u.ColorRed)
// s.WriteRune('█')
// s.WriteString(u.TextReset)
// } else if block == tileEmpty {
// s.WriteRune(' ')
// }
// }
// s.WriteRune('\n')
// }
// fmt.Print(s.String())
// }
func (d *Day13) Part1() string {
outputStep := 0
var newTilePos u.Vec2[int]
d.program.RunIn(func(inputStep int) int64 {
return 0
}, func(val int64, state u.IntcodeProgramState) {
if outputStep == 0 {
newTilePos.X = int(val)
outputStep++
} else if outputStep == 1 {
newTilePos.Y = int(val)
outputStep++
} else {
d.tiles = append(d.tiles, tile{
pos: newTilePos,
id: int(val),
})
outputStep = 0
}
})
return fmt.Sprintf("# block tiles: %s%d%s (%d total tiles)", u.TextBold, d.getNumBlocks(), u.TextReset, len(d.tiles))
}
func (d *Day13) Part2() string {
d.program.Reset()
d.program.SetMemory(0, 2)
outputStep := 0
newTilePos := u.Vec2[int]{}
var ball u.Vec2[int]
var paddle u.Vec2[int]
var score int64
d.program.RunIn(func(inputStep int) int64 {
if ball.X < paddle.X {
return -1
} else if ball.X > paddle.X {
return 1
}
return 0
}, func(val int64, state u.IntcodeProgramState) {
if outputStep == 0 {
newTilePos.X = int(val)
outputStep++
} else if outputStep == 1 {
newTilePos.Y = int(val)
outputStep++
} else {
if newTilePos.Equals(u.Vec2[int]{X: -1, Y: 0}) {
score = val
} else {
d.gameBoard[newTilePos.Y][newTilePos.X] = int(val)
if val == tileBall {
ball = newTilePos
} else if val == tileHPaddle {
paddle = newTilePos
// d.drawGameBoard()
// time.Sleep(time.Millisecond * 33)
}
}
outputStep = 0
}
})
return fmt.Sprintf("Game over! Score: %s%d%s", u.TextBold, score, u.TextReset)
}

113
days/14.go Normal file
View File

@ -0,0 +1,113 @@
package days
import (
"fmt"
"math"
"strconv"
"strings"
u "parnic.com/aoc2019/utilities"
)
type reaction struct {
inputs map[string]int64
output u.Pair[string, int]
}
type Day14 struct {
reactions []reaction
}
func (d *Day14) Parse() {
lines := u.GetStringLines("14p")
d.reactions = make([]reaction, len(lines))
for i, line := range lines {
sides := strings.Split(line, " => ")
inputs := strings.Split(sides[0], ", ")
output := sides[1]
outPair := strings.Split(output, " ")
outAmt, _ := strconv.Atoi(outPair[0])
d.reactions[i].output = u.Pair[string, int]{First: outPair[1], Second: outAmt}
d.reactions[i].inputs = make(map[string]int64)
for _, input := range inputs {
pair := strings.Split(input, " ")
d.reactions[i].inputs[pair[1]], _ = strconv.ParseInt(pair[0], 10, 64)
}
}
}
func (d Day14) getReactionProducing(chem string) *reaction {
for _, reaction := range d.reactions {
if reaction.output.First == chem {
return &reaction
}
}
return nil
}
func (d Day14) Num() int {
return 14
}
func (d *Day14) getOreRequiredForFuel(qty int64) int64 {
oreRequired := int64(0)
needs := map[string]int64{
"FUEL": qty,
}
excess := make(map[string]int64)
getFromExcess := func(qty int64, chemical string) int64 {
available := u.Min(excess[chemical], qty)
excess[chemical] -= available
return available
}
for len(needs) > 0 {
keys := u.MapKeys(needs)
producing := keys[0]
qtyRequired := needs[producing]
delete(needs, producing)
fromExcess := getFromExcess(qtyRequired, producing)
if fromExcess == qtyRequired {
continue
}
qtyRequired -= fromExcess
reaction := d.getReactionProducing(producing)
qtyProduced := int64(reaction.output.Second)
reactionsNeeded := int64(math.Ceil(float64(qtyRequired) / float64(qtyProduced)))
excess[producing] = (qtyProduced * reactionsNeeded) - qtyRequired
for reagent, inputQty := range reaction.inputs {
qtyNeeded := inputQty * reactionsNeeded
if reagent == "ORE" {
oreRequired += qtyNeeded
} else {
needs[reagent] += qtyNeeded
}
}
}
return oreRequired
}
func (d *Day14) Part1() string {
neededOre := d.getOreRequiredForFuel(1)
return fmt.Sprintf("Minimum ore to produce 1 FUEL: %s%d%s", u.TextBold, neededOre, u.TextReset)
}
func (d *Day14) Part2() string {
oreAvailable := int64(1000000000000)
estimate := oreAvailable / d.getOreRequiredForFuel(1)
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)
}

332
days/15.go Normal file
View File

@ -0,0 +1,332 @@
package days
//go:generate stringer -type=cellStatus,responseType,dirType -output=15_types_string.go
import (
"fmt"
"math"
"sort"
u "parnic.com/aoc2019/utilities"
)
type point u.Pair[int, int]
type cellStatus int
type responseType int
type dirType int
const (
cellStatusUnknown cellStatus = iota
cellStatusWall
cellStatusOpen
cellStatusGoal
)
const (
responseWall responseType = iota
responseSuccess
responseFoundGoal
)
const maxVisited = 3
const (
dirNorth dirType = iota + 1
dirSouth
dirWest
dirEast
dirFirst = dirNorth
dirLast = dirEast
)
var dirOrders = [][]dirType{
{dirNorth, dirSouth, dirWest, dirEast},
{dirSouth, dirWest, dirEast, dirNorth},
{dirWest, dirEast, dirNorth, dirSouth},
{dirEast, dirNorth, dirSouth, dirWest},
}
// turned out to be unnecessary on multiple datasets i tried. increases the iterations 6x
// var dirOrders = u.GetPermutations(dirNorth, dirSouth, dirWest, dirEast)
type visitedStatus struct {
timesVisited int
distanceFromStart int
}
type Day15 struct {
program u.IntcodeProgram
grid map[point]cellStatus
visited map[point]*visitedStatus
shortestPath []point
pos point
goalPos point
}
func (d *Day15) Parse() {
d.program = u.LoadIntcodeProgram("15p")
d.grid = map[point]cellStatus{
{First: 0, Second: 0}: cellStatusOpen,
}
d.visited = map[point]*visitedStatus{
{First: 0, Second: 0}: {timesVisited: 1},
}
d.shortestPath = []point{{}}
}
func (d Day15) Num() int {
return 15
}
func (d Day15) getPointInDirection(pos point, dir dirType) point {
target := pos
switch dir {
case dirNorth:
target.First--
case dirSouth:
target.First++
case dirWest:
target.Second--
case dirEast:
target.Second++
}
return target
}
func (d Day15) getCellTypeInDirection(pos point, dir dirType) (cellStatus, point) {
target := d.getPointInDirection(pos, dir)
return d.grid[target], target
}
func (d Day15) getAdjacentCellsOfType(pos point, cellType cellStatus) []point {
points := make([]point, 0, 4)
for i := dirFirst; i <= dirLast; i++ {
adjacentCell := d.getPointInDirection(pos, i)
if d.grid[adjacentCell] == cellType {
points = append(points, adjacentCell)
}
}
return points
}
func (d Day15) getDirToNextCellType(pos point, t cellStatus, maxNumVisited int, dirs []dirType) (dirType, point, error) {
for _, dir := range dirs {
cellInDirection, targetCell := d.getCellTypeInDirection(pos, dir)
if cellInDirection == t {
_, visitedTargetExists := d.visited[targetCell]
foundUnknown := t == cellStatusUnknown && !visitedTargetExists
foundOther := t != cellStatusUnknown && visitedTargetExists && d.visited[targetCell].timesVisited <= maxNumVisited
if foundUnknown || foundOther {
return dir, targetCell, nil
}
}
}
return dirFirst, point{}, fmt.Errorf("no %v tiles around %v", t, pos)
}
func (d *Day15) Draw() {
min := point{First: math.MaxInt, Second: math.MaxInt}
max := point{First: math.MinInt, Second: math.MinInt}
for p := range d.grid {
if p.First < min.First {
min.First = p.First
}
if p.First > max.First {
max.First = p.First
}
if p.Second < min.Second {
min.Second = p.Second
}
if p.Second > max.Second {
max.Second = p.Second
}
}
for x := min.First; x <= max.First; x++ {
for y := min.Second; y <= max.Second; y++ {
p := point{First: x, Second: y}
switch d.grid[p] {
case cellStatusGoal:
fmt.Printf("%s@%s", u.ColorBrightGreen, u.TextReset)
case cellStatusOpen:
if p == d.pos {
fmt.Print(u.BackgroundBrightRed)
} else if x == 0 && y == 0 {
fmt.Print(u.BackgroundYellow)
} else if u.ArrayContains(d.shortestPath, p) {
fmt.Print(u.BackgroundGreen)
} else if d.visited[p] != nil && d.visited[p].timesVisited > maxVisited {
fmt.Print(u.ColorYellow)
fmt.Print(u.BackgroundBlack)
} else if d.visited[p] != nil && d.visited[p].timesVisited > 1 {
fmt.Print(u.BackgroundMagenta)
} else {
fmt.Print(u.BackgroundBlue)
}
fmt.Printf(".%s", u.TextReset)
case cellStatusWall:
fmt.Print("█")
case cellStatusUnknown:
fmt.Print(" ")
}
}
fmt.Println()
}
}
func (d *Day15) markShortestPath() {
pos := d.goalPos
checkOffsets := []point{
{First: -1, Second: 0},
{First: 1, Second: 0},
{First: 0, Second: -1},
{First: 0, Second: 1},
}
checkPt := func(pt point) (bool, int) {
if v, exists := d.visited[pt]; exists && d.grid[pt] == cellStatusOpen {
return true, v.distanceFromStart
}
return false, math.MaxInt
}
d.shortestPath = []point{d.goalPos}
for pos.First != 0 || pos.Second != 0 {
lowestDist := math.MaxInt
lowestPoint := point{}
for _, pt := range checkOffsets {
newPt := point{First: pos.First + pt.First, Second: pos.Second + pt.Second}
if found, dist := checkPt(newPt); found && dist < lowestDist {
lowestDist = dist
lowestPoint = newPt
}
}
d.shortestPath = append(d.shortestPath, lowestPoint)
pos = lowestPoint
}
}
func (d *Day15) exploreFullMap() map[point]*visitedStatus {
grids := make([]map[point]cellStatus, 0, len(dirOrders))
goalVisited := d.visited
for _, dirOrder := range dirOrders {
d.program.Reset()
targetPos := point{}
nextDir := dirFirst
distFromStart := 0
d.pos = point{}
d.visited = map[point]*visitedStatus{
{First: 0, Second: 0}: {timesVisited: 1},
}
d.grid = map[point]cellStatus{
{First: 0, Second: 0}: cellStatusOpen,
}
d.program.RunIn(func(inputStep int) int64 {
var err error
nextDir, targetPos, err = d.getDirToNextCellType(d.pos, cellStatusUnknown, 0, dirOrder)
if err != nil {
// ensure we never try to go back into the trapped spot
d.visited[d.pos].timesVisited = maxVisited + 1
for x := 1; x <= maxVisited && err != nil; x++ {
nextDir, targetPos, err = d.getDirToNextCellType(d.pos, cellStatusOpen, x, dirOrder)
}
}
if err != nil {
// d.Draw()
// panic(err)
d.program.Stop()
}
return int64(nextDir)
}, func(val int64, state u.IntcodeProgramState) {
rVal := responseType(val)
p := d.getPointInDirection(d.pos, nextDir)
shouldMove := true
switch rVal {
case responseWall:
d.grid[p] = cellStatusWall
shouldMove = false
case responseSuccess:
d.grid[p] = cellStatusOpen
case responseFoundGoal:
d.grid[p] = cellStatusGoal
}
if shouldMove {
d.pos = targetPos
if d.visited[d.pos] == nil {
d.visited[d.pos] = &visitedStatus{}
distFromStart++
} else {
distFromStart--
}
d.visited[d.pos].timesVisited++
d.visited[d.pos].distanceFromStart = distFromStart
}
if rVal == responseFoundGoal {
// d.Draw()
d.goalPos = targetPos
goalVisited = d.visited
}
})
grids = append(grids, d.grid)
}
d.grid = map[point]cellStatus{
{First: 0, Second: 0}: cellStatusOpen,
}
for _, grid := range grids {
keys := u.MapKeys(grid)
for _, key := range keys {
d.grid[key] = grid[key]
}
}
return goalVisited
}
func (d *Day15) tagDistanceRecursive(pos, last point, dist int, distances map[point]int) {
distances[pos] = dist
for _, cell := range d.getAdjacentCellsOfType(pos, cellStatusOpen) {
if cell == last {
continue
}
d.tagDistanceRecursive(cell, pos, dist+1, distances)
}
}
func (d *Day15) Part1() string {
d.visited = d.exploreFullMap()
d.markShortestPath()
for _, visited := range d.visited {
visited.timesVisited = 1
}
d.pos = point{}
d.Draw()
return fmt.Sprintf("Moves required to reach target: %s%d%s", u.TextBold, d.visited[d.goalPos].distanceFromStart, u.TextReset)
}
func (d *Day15) Part2() string {
startLoc := d.goalPos
distanceMap := map[point]int{startLoc: 0}
d.tagDistanceRecursive(startLoc, point{}, 0, distanceMap)
cellDistances := u.MapValues(distanceMap)
sort.Slice(cellDistances, func(i, j int) bool { return cellDistances[i] > cellDistances[j] })
return fmt.Sprintf("Time to fill the area with oxygen: %s%d%s minutes", u.TextBold, cellDistances[0], u.TextReset)
}

66
days/15_types_string.go Normal file
View File

@ -0,0 +1,66 @@
// Code generated by "stringer -type=cellStatus,responseType,dirType -output=15_types_string.go"; DO NOT EDIT.
package days
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[cellStatusUnknown-0]
_ = x[cellStatusWall-1]
_ = x[cellStatusOpen-2]
_ = x[cellStatusGoal-3]
}
const _cellStatus_name = "cellStatusUnknowncellStatusWallcellStatusOpencellStatusGoal"
var _cellStatus_index = [...]uint8{0, 17, 31, 45, 59}
func (i cellStatus) String() string {
if i < 0 || i >= cellStatus(len(_cellStatus_index)-1) {
return "cellStatus(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _cellStatus_name[_cellStatus_index[i]:_cellStatus_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[responseWall-0]
_ = x[responseSuccess-1]
_ = x[responseFoundGoal-2]
}
const _responseType_name = "responseWallresponseSuccessresponseFoundGoal"
var _responseType_index = [...]uint8{0, 12, 27, 44}
func (i responseType) String() string {
if i < 0 || i >= responseType(len(_responseType_index)-1) {
return "responseType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _responseType_name[_responseType_index[i]:_responseType_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[dirNorth-1]
_ = x[dirSouth-2]
_ = x[dirWest-3]
_ = x[dirEast-4]
}
const _dirType_name = "dirNorthdirSouthdirWestdirEast"
var _dirType_index = [...]uint8{0, 8, 16, 23, 30}
func (i dirType) String() string {
i -= 1
if i < 0 || i >= dirType(len(_dirType_index)-1) {
return "dirType(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _dirType_name[_dirType_index[i]:_dirType_index[i+1]]
}

104
days/16.go Normal file
View File

@ -0,0 +1,104 @@
package days
import (
"fmt"
"math"
u "parnic.com/aoc2019/utilities"
)
type Day16 struct {
numberSet []int8
}
func (d *Day16) Parse() {
numberSequence := u.GetStringContents("16p")
d.numberSet = make([]int8, len(numberSequence))
for i, numRune := range numberSequence {
d.numberSet[i] = int8(numRune - '0')
}
}
func (d Day16) Num() int {
return 16
}
func (d *Day16) Part1() string {
transformed := make([]int8, len(d.numberSet))
copy(transformed, d.numberSet)
transformPattern := []int8{0, 1, 0, -1}
phases := 100
workingSet := make([]int8, len(transformed))
for i := 0; i < phases; i++ {
copy(workingSet, transformed)
// fmt.Printf("Phase %d. Input signal: %v\n", (i + 1), transformed)
for destIdx := range transformed {
repeated := 0
patternIdx := 0
workingVal := int64(0)
for idx := range transformed {
if repeated >= destIdx {
repeated = 0
patternIdx++
if patternIdx == len(transformPattern) {
patternIdx = 0
}
} else {
repeated++
}
// fmt.Printf("%d*%d", transformed[idx], transformPattern[patternIdx])
// if idx < len(transformed)-1 {
// fmt.Print(" + ")
// }
workingVal += int64(transformed[idx] * transformPattern[patternIdx])
}
workingSet[destIdx] = int8(int64(math.Abs(float64(workingVal))) % 10)
// fmt.Printf(" = %d\n", workingSet[destIdx])
}
copy(transformed, workingSet)
}
finalVal := 0
for i := range transformed[0:8] {
finalVal += int(transformed[i]) * int(math.Pow10(8-1-i))
}
return fmt.Sprintf("First 8 digits of the final output list: %s%d%s", u.TextBold, finalVal, u.TextReset)
}
func (d *Day16) Part2() string {
transformed := make([]int8, len(d.numberSet)*10000)
for i := 0; i < 10000; i++ {
copy(transformed[i*len(d.numberSet):(i*len(d.numberSet))+len(d.numberSet)], d.numberSet)
}
finalMsgOffset := 0
for i := 0; i < 7; i++ {
finalMsgOffset += int(d.numberSet[i]) * int(math.Pow10(7-1-i))
}
if finalMsgOffset < len(transformed)/2 {
panic("offset must be in the back half of the message for this solution to work")
}
phases := 100
for p := 0; p < phases; p++ {
rollingTotal := int8(0)
for i := len(transformed) - 1; i >= finalMsgOffset; i-- {
rollingTotal += transformed[i]
rollingTotal = rollingTotal % 10
transformed[i] = rollingTotal
}
}
finalVal := 0
for i := range transformed[finalMsgOffset : finalMsgOffset+8] {
finalVal += int(transformed[finalMsgOffset+i]) * int(math.Pow10(8-1-i))
}
return fmt.Sprintf("Embedded message in the final output list: %s%d%s", u.TextBold, finalVal, u.TextReset)
}

422
days/17.go Normal file
View File

@ -0,0 +1,422 @@
package days
import (
"fmt"
"strings"
u "parnic.com/aoc2019/utilities"
)
type camViewCellType int
type botFacing int
type day17Grid [][]camViewCellType
const (
cellTypeScaffold camViewCellType = iota
cellTypeOpen
cellTypeInvalid
)
const (
botFacingUp botFacing = iota
botFacingLeft
botFacingDown
botFacingRight
botFacingFirst = botFacingUp
botFacingLast = botFacingRight
)
const (
dirLeft = 1
dirRight = -1
maxInstructionSetLength = 20
)
var (
day17AdjacentOffsets = []u.Vec2i{
{X: -1, Y: 0},
{X: 1, Y: 0},
{X: 0, Y: -1},
{X: 0, Y: 1},
}
)
type Day17 struct {
program u.IntcodeProgram
}
func (d *Day17) Parse() {
d.program = u.LoadIntcodeProgram("17p")
}
func (d Day17) Num() int {
return 17
}
func (currentDir botFacing) getNewFacingDir(turnDir int) botFacing {
currentDir += botFacing(turnDir)
if currentDir < botFacingFirst {
currentDir = botFacingLast
} else if currentDir > botFacingLast {
currentDir = botFacingFirst
}
return currentDir
}
func (grid day17Grid) Draw(botLocation u.Vec2i, botFacingDir botFacing, endLocation u.Vec2i) {
for y := range grid {
for x := range grid[y] {
switch grid[y][x] {
case cellTypeOpen:
fmt.Print(" ")
case cellTypeScaffold:
char := "█"
color := u.ColorBlack
if botLocation.X == x && botLocation.Y == y {
switch botFacingDir {
case botFacingUp:
char = "^"
case botFacingLeft:
char = "<"
case botFacingDown:
char = "v"
case botFacingRight:
char = ">"
}
} else if endLocation.X == x && endLocation.Y == y {
char = "@"
} else {
color = u.ColorWhite
}
fmt.Printf("%s%s%s%s", u.BackgroundWhite, color, char, u.TextReset)
}
}
fmt.Println()
}
}
func (grid day17Grid) getAdjacentScaffolds(y, x int) []u.Vec2i {
retval := make([]u.Vec2i, 0)
for _, offset := range day17AdjacentOffsets {
offY := y + offset.Y
offX := x + offset.X
if offY < 0 || offY >= len(grid) ||
offX < 0 || offX >= len(grid[0]) {
continue
}
if grid[offY][offX] == cellTypeScaffold {
retval = append(retval, u.Vec2i{X: offX, Y: offY})
}
}
return retval
}
func (grid day17Grid) forEachCellOfType(t camViewCellType, f func(y, x int)) {
for y := range grid {
for x := range grid[y] {
if grid[y][x] == t {
f(y, x)
}
}
}
}
func (grid *day17Grid) processGridUpdate(y int, rVal rune, currBotLocation u.Vec2i, currBotFacing botFacing) (int, u.Vec2i, botFacing) {
grid.appendValue(rVal, y)
switch rVal {
case '\n':
y++
case '^', '<', 'v', '>':
currBotLocation = u.Vec2i{X: len((*grid)[y]) - 1, Y: y}
switch rVal {
case '^':
currBotFacing = botFacingUp
case '<':
currBotFacing = botFacingLeft
case 'v':
currBotFacing = botFacingDown
case '>':
currBotFacing = botFacingRight
}
}
return y, currBotLocation, currBotFacing
}
func (grid day17Grid) getCellTypeInDirection(y, x int, facingDir botFacing) (camViewCellType, int, int) {
newX := x
newY := y
switch facingDir {
case botFacingUp:
newY--
case botFacingLeft:
newX--
case botFacingDown:
newY++
case botFacingRight:
newX++
}
if newY < 0 || newY >= len(grid) || newX < 0 || newX >= len(grid[0]) {
return cellTypeInvalid, newY, newX
}
return grid[newY][newX], newY, newX
}
func (grid *day17Grid) appendValue(rVal rune, row int) {
ensureCapacity := func(y int) {
for len(*grid) <= y {
*grid = append(*grid, make([]camViewCellType, 0))
}
}
switch rVal {
case '#':
ensureCapacity(row)
(*grid)[row] = append((*grid)[row], cellTypeScaffold)
case '.':
ensureCapacity(row)
(*grid)[row] = append((*grid)[row], cellTypeOpen)
case '^', '<', 'v', '>':
ensureCapacity(row)
(*grid)[row] = append((*grid)[row], cellTypeScaffold)
}
}
func (grid day17Grid) findEndLocation(botLocation u.Vec2i) u.Vec2i {
var endLocation u.Vec2i
grid.forEachCellOfType(cellTypeScaffold, func(y, x int) {
if numSurrounding := len(grid.getAdjacentScaffolds(y, x)); numSurrounding == 1 {
if botLocation.X != x || botLocation.Y != y {
endLocation = u.Vec2i{X: x, Y: y}
}
}
})
return endLocation
}
func (grid day17Grid) getTurnDirectionFromCorner(pos u.Vec2i, botFacingDir botFacing) (int, string) {
adj := grid.getAdjacentScaffolds(pos.Y, pos.X)
turnDirection := 0
// this is so awful. i'm sure there's a better way, but i'm tired.
if botFacingDir == botFacingUp || botFacingDir == botFacingDown {
if u.ArrayContains(adj, u.Vec2i{X: pos.X - 1, Y: pos.Y}) {
if botFacingDir == botFacingUp {
turnDirection = dirLeft
} else if botFacingDir == botFacingDown {
turnDirection = dirRight
}
} else if u.ArrayContains(adj, u.Vec2i{X: pos.X + 1, Y: pos.Y}) {
if botFacingDir == botFacingUp {
turnDirection = dirRight
} else if botFacingDir == botFacingDown {
turnDirection = dirLeft
}
}
} else {
if u.ArrayContains(adj, u.Vec2i{X: pos.X, Y: pos.Y - 1}) {
if botFacingDir == botFacingLeft {
turnDirection = dirRight
} else if botFacingDir == botFacingRight {
turnDirection = dirLeft
}
} else if u.ArrayContains(adj, u.Vec2i{X: pos.X, Y: pos.Y + 1}) {
if botFacingDir == botFacingLeft {
turnDirection = dirLeft
} else if botFacingDir == botFacingRight {
turnDirection = dirRight
}
}
}
dirAscii := "L"
if turnDirection == dirRight {
dirAscii = "R"
}
return turnDirection, dirAscii
}
func buildInstructionString(instructions []string) string {
workingInstructions := make([]string, len(instructions))
copy(workingInstructions, instructions)
minimumRecurrence := 3
initialInstructionSubsetLen := 4
instructionStr := strings.Join(workingInstructions, ",")
progs := make([][]string, 3)
for i := range progs {
numFound := minimumRecurrence
subLen := initialInstructionSubsetLen
for numFound >= minimumRecurrence {
numFound = 1
instructionSubset := strings.Join(workingInstructions[0:subLen], ",")
if len(instructionSubset) > maxInstructionSetLength {
break
}
for x := len(instructionSubset); x <= len(instructionStr)-len(instructionSubset); x++ {
if instructionStr[x:x+len(instructionSubset)] == instructionSubset {
numFound++
x += len(instructionSubset)
}
}
if numFound >= minimumRecurrence {
subLen += 2
}
}
if numFound < minimumRecurrence {
subLen -= 2
}
progs[i] = make([]string, subLen)
copy(progs[i], workingInstructions[0:subLen])
instructionStr = strings.ReplaceAll(instructionStr, strings.Join(progs[i], ","), "")
instructionStr = strings.TrimPrefix(strings.ReplaceAll(instructionStr, ",,", ","), ",")
if len(instructionStr) == 0 {
workingInstructions = nil
} else {
workingInstructions = strings.Split(instructionStr, ",")
}
}
if workingInstructions != nil {
panic("failed to use up all instructions")
}
programStr := strings.Join(instructions, ",")
for i := range progs {
programStr = strings.ReplaceAll(programStr, strings.Join(progs[i], ","), fmt.Sprintf("%c", 'A'+i))
}
sb := strings.Builder{}
sb.WriteString(programStr)
sb.WriteRune('\n')
for i := range progs {
sb.WriteString(strings.Join(progs[i], ","))
sb.WriteRune('\n')
}
runDebug := 'n'
sb.WriteRune(runDebug)
sb.WriteRune('\n')
return sb.String()
}
func (grid day17Grid) solvePath(botLocation u.Vec2i, botFacingDir botFacing) string {
instructions := make([]string, 0)
pos := botLocation
endLocation := grid.findEndLocation(botLocation)
for {
if pos == endLocation {
break
}
turnDirection, dirAscii := grid.getTurnDirectionFromCorner(pos, botFacingDir)
if turnDirection == 0 {
panic("at an invalid location somehow")
}
instructions = append(instructions, dirAscii)
botFacingDir = botFacingDir.getNewFacingDir(turnDirection)
numMoved := 0
for {
cell, newY, newX := grid.getCellTypeInDirection(pos.Y, pos.X, botFacingDir)
if cell != cellTypeScaffold {
break
}
pos.X = newX
pos.Y = newY
numMoved++
}
instructions = append(instructions, fmt.Sprintf("%d", numMoved))
}
return buildInstructionString(instructions)
}
func (d *Day17) Part1() string {
grid := day17Grid{}
y := 0
var botLocation u.Vec2i
var botFacingDir botFacing
d.program.RunIn(func(inputStep int) int64 {
return 0
}, func(val int64, state u.IntcodeProgramState) {
rVal := rune(val)
y, botLocation, botFacingDir = grid.processGridUpdate(y, rVal, botLocation, botFacingDir)
})
alignmentParameterTotal := 0
grid.forEachCellOfType(cellTypeScaffold, func(y, x int) {
if numSurrounding := len(grid.getAdjacentScaffolds(y, x)); numSurrounding == 4 {
alignmentParameterTotal += y * x
}
})
// endLocation := grid.findEndLocation(botLocation)
// grid.Draw(botLocation, botFacingDir, endLocation)
return fmt.Sprintf("Alignment parameter sum: %s%d%s", u.TextBold, alignmentParameterTotal, u.TextReset)
}
func (d *Day17) Part2() string {
beforeGrid := day17Grid{}
var beforeBotLocation u.Vec2i
var beforeBotFacing botFacing
afterGrid := day17Grid{}
var afterBotLocation u.Vec2i
var afterBotFacing botFacing
d.program.Reset()
d.program.SetMemory(0, 2)
row := 0
var outputState int
var lastOutput int64
var instructionStr string
d.program.RunIn(func(inputStep int) int64 {
return int64(instructionStr[inputStep-1])
}, func(val int64, state u.IntcodeProgramState) {
rVal := rune(val)
if outputState == 0 {
row, beforeBotLocation, beforeBotFacing = beforeGrid.processGridUpdate(row, rVal, beforeBotLocation, beforeBotFacing)
} else if outputState == 2 {
row, afterBotLocation, afterBotFacing = afterGrid.processGridUpdate(row, rVal, afterBotLocation, afterBotFacing)
}
if rVal == '\n' && lastOutput == '\n' {
if outputState == 0 {
instructionStr = beforeGrid.solvePath(beforeBotLocation, beforeBotFacing)
}
outputState++
row = 0
}
lastOutput = val
})
// fmt.Println("initial grid:")
// beforeEndLocation := beforeGrid.findEndLocation(beforeBotLocation)
// beforeGrid.Draw(beforeBotLocation, beforeBotFacing, beforeEndLocation)
// fmt.Println("completed grid:")
// afterEndLocation := afterGrid.findEndLocation(afterBotLocation)
// afterGrid.Draw(afterBotLocation, afterBotFacing, afterEndLocation)
return fmt.Sprintf("Dust collected after traveling all paths: %s%d%s", u.TextBold, lastOutput, 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)
}

View File

@ -1,14 +1,16 @@
package days
import (
"parnic.com/aoc2019/utilities"
"fmt"
u "parnic.com/aoc2019/utilities"
)
type DayTemplate struct {
}
func (d *DayTemplate) Parse() {
utilities.GetIntLines("Templatep")
u.GetIntLines("Templatep")
}
func (d DayTemplate) Num() int {
@ -16,9 +18,9 @@ func (d DayTemplate) Num() int {
}
func (d *DayTemplate) Part1() string {
return ""
return fmt.Sprintf("%s%d%s", u.TextBold, 0, u.TextReset)
}
func (d *DayTemplate) Part2() string {
return ""
return fmt.Sprintf("%s%d%s", u.TextBold, 0, 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=

1
inputs/05p.txt Normal file
View File

@ -0,0 +1 @@
3,225,1,225,6,6,1100,1,238,225,104,0,1102,78,40,225,1102,52,43,224,1001,224,-2236,224,4,224,102,8,223,223,101,4,224,224,1,224,223,223,1,191,61,224,1001,224,-131,224,4,224,102,8,223,223,101,4,224,224,1,223,224,223,1101,86,74,225,1102,14,76,225,1101,73,83,224,101,-156,224,224,4,224,102,8,223,223,101,6,224,224,1,224,223,223,1102,43,82,225,2,196,13,224,101,-6162,224,224,4,224,102,8,223,223,101,5,224,224,1,223,224,223,1001,161,51,224,101,-70,224,224,4,224,102,8,223,223,1001,224,1,224,1,224,223,223,102,52,187,224,1001,224,-832,224,4,224,102,8,223,223,101,1,224,224,1,224,223,223,1102,19,79,225,101,65,92,224,1001,224,-147,224,4,224,1002,223,8,223,101,4,224,224,1,223,224,223,1102,16,90,225,1102,45,44,225,1102,92,79,225,1002,65,34,224,101,-476,224,224,4,224,102,8,223,223,1001,224,5,224,1,224,223,223,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,107,226,226,224,1002,223,2,223,1005,224,329,1001,223,1,223,1007,226,226,224,102,2,223,223,1005,224,344,101,1,223,223,1008,226,226,224,102,2,223,223,1005,224,359,1001,223,1,223,8,226,677,224,102,2,223,223,1006,224,374,101,1,223,223,1107,226,677,224,1002,223,2,223,1006,224,389,101,1,223,223,1108,226,677,224,102,2,223,223,1005,224,404,101,1,223,223,107,677,677,224,102,2,223,223,1006,224,419,1001,223,1,223,7,677,226,224,102,2,223,223,1005,224,434,101,1,223,223,1007,677,677,224,102,2,223,223,1005,224,449,1001,223,1,223,108,226,677,224,102,2,223,223,1005,224,464,1001,223,1,223,108,226,226,224,102,2,223,223,1006,224,479,101,1,223,223,107,226,677,224,102,2,223,223,1006,224,494,1001,223,1,223,7,226,226,224,1002,223,2,223,1006,224,509,101,1,223,223,1108,677,226,224,102,2,223,223,1005,224,524,101,1,223,223,1107,677,226,224,102,2,223,223,1005,224,539,101,1,223,223,1008,677,226,224,102,2,223,223,1005,224,554,101,1,223,223,1008,677,677,224,1002,223,2,223,1006,224,569,101,1,223,223,1107,677,677,224,102,2,223,223,1006,224,584,1001,223,1,223,1108,226,226,224,1002,223,2,223,1006,224,599,101,1,223,223,7,226,677,224,102,2,223,223,1006,224,614,101,1,223,223,108,677,677,224,1002,223,2,223,1006,224,629,101,1,223,223,1007,677,226,224,102,2,223,223,1006,224,644,101,1,223,223,8,677,677,224,1002,223,2,223,1006,224,659,101,1,223,223,8,677,226,224,102,2,223,223,1005,224,674,101,1,223,223,4,223,99,226

1013
inputs/06p.txt Normal file

File diff suppressed because it is too large Load Diff

11
inputs/06s1.txt Normal file
View File

@ -0,0 +1,11 @@
COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L

13
inputs/06s2.txt Normal file
View File

@ -0,0 +1,13 @@
COM)B
B)C
C)D
D)E
E)F
B)G
G)H
D)I
E)J
J)K
K)L
K)YOU
I)SAN

1
inputs/07p.txt Normal file
View File

@ -0,0 +1 @@
3,8,1001,8,10,8,105,1,0,0,21,38,47,64,85,106,187,268,349,430,99999,3,9,1002,9,4,9,1001,9,4,9,1002,9,4,9,4,9,99,3,9,1002,9,4,9,4,9,99,3,9,1001,9,3,9,102,5,9,9,1001,9,5,9,4,9,99,3,9,101,3,9,9,102,5,9,9,1001,9,4,9,102,4,9,9,4,9,99,3,9,1002,9,3,9,101,2,9,9,102,4,9,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,3,9,101,1,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,99

1
inputs/07s1.txt Normal file
View File

@ -0,0 +1 @@
3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0

1
inputs/07s2.txt Normal file
View File

@ -0,0 +1 @@
3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0

1
inputs/07s3.txt Normal file
View File

@ -0,0 +1 @@
3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0

1
inputs/07s4.txt Normal file
View File

@ -0,0 +1 @@
3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5

1
inputs/07s5.txt Normal file
View File

@ -0,0 +1 @@
3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10

1
inputs/08p.txt Normal file

File diff suppressed because one or more lines are too long

1
inputs/08s1.txt Normal file
View File

@ -0,0 +1 @@
123456789012

1
inputs/09p.txt Normal file
View File

@ -0,0 +1 @@
1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,3,0,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1102,1,31,1018,1102,352,1,1023,1101,0,1,1021,1101,0,33,1003,1102,1,36,1007,1102,21,1,1005,1101,359,0,1022,1101,0,787,1024,1102,1,24,1011,1101,30,0,1014,1101,22,0,1016,1101,0,0,1020,1102,1,29,1000,1101,778,0,1025,1102,23,1,1017,1102,1,28,1002,1101,38,0,1019,1102,1,27,1013,1102,1,32,1012,1101,0,37,1006,1101,444,0,1027,1102,1,20,1009,1101,0,447,1026,1101,0,39,1008,1101,35,0,1010,1102,559,1,1028,1102,26,1,1004,1102,1,25,1015,1102,1,34,1001,1101,0,554,1029,109,-3,2101,0,9,63,1008,63,34,63,1005,63,205,1001,64,1,64,1105,1,207,4,187,1002,64,2,64,109,23,21107,40,39,-7,1005,1013,227,1001,64,1,64,1106,0,229,4,213,1002,64,2,64,109,-17,1202,-2,1,63,1008,63,36,63,1005,63,249,1106,0,255,4,235,1001,64,1,64,1002,64,2,64,109,-6,1202,10,1,63,1008,63,36,63,1005,63,277,4,261,1106,0,281,1001,64,1,64,1002,64,2,64,109,-2,1208,9,26,63,1005,63,303,4,287,1001,64,1,64,1106,0,303,1002,64,2,64,109,32,1206,-7,321,4,309,1001,64,1,64,1106,0,321,1002,64,2,64,109,-29,1207,7,20,63,1005,63,337,1105,1,343,4,327,1001,64,1,64,1002,64,2,64,109,27,2105,1,-2,1001,64,1,64,1106,0,361,4,349,1002,64,2,64,109,-25,2108,39,7,63,1005,63,377,1106,0,383,4,367,1001,64,1,64,1002,64,2,64,109,1,1201,6,0,63,1008,63,36,63,1005,63,409,4,389,1001,64,1,64,1105,1,409,1002,64,2,64,109,1,2102,1,1,63,1008,63,33,63,1005,63,435,4,415,1001,64,1,64,1105,1,435,1002,64,2,64,109,28,2106,0,-3,1106,0,453,4,441,1001,64,1,64,1002,64,2,64,109,-13,21101,41,0,1,1008,1018,44,63,1005,63,477,1001,64,1,64,1106,0,479,4,459,1002,64,2,64,109,4,21108,42,42,-2,1005,1019,501,4,485,1001,64,1,64,1106,0,501,1002,64,2,64,109,-21,2101,0,2,63,1008,63,28,63,1005,63,523,4,507,1105,1,527,1001,64,1,64,1002,64,2,64,109,26,1205,-5,545,4,533,1001,64,1,64,1105,1,545,1002,64,2,64,109,3,2106,0,-1,4,551,1106,0,563,1001,64,1,64,1002,64,2,64,109,-33,1201,4,0,63,1008,63,28,63,1005,63,583,1105,1,589,4,569,1001,64,1,64,1002,64,2,64,109,11,2107,27,-3,63,1005,63,609,1001,64,1,64,1106,0,611,4,595,1002,64,2,64,109,8,21102,43,1,3,1008,1018,43,63,1005,63,637,4,617,1001,64,1,64,1105,1,637,1002,64,2,64,109,-5,21108,44,41,0,1005,1010,653,1105,1,659,4,643,1001,64,1,64,1002,64,2,64,109,-13,2108,21,8,63,1005,63,681,4,665,1001,64,1,64,1106,0,681,1002,64,2,64,109,6,1207,0,34,63,1005,63,703,4,687,1001,64,1,64,1105,1,703,1002,64,2,64,109,7,1208,-7,35,63,1005,63,723,1001,64,1,64,1106,0,725,4,709,1002,64,2,64,109,-13,2102,1,7,63,1008,63,23,63,1005,63,745,1105,1,751,4,731,1001,64,1,64,1002,64,2,64,109,13,1205,10,767,1001,64,1,64,1105,1,769,4,757,1002,64,2,64,109,14,2105,1,0,4,775,1001,64,1,64,1106,0,787,1002,64,2,64,109,-20,21107,45,46,7,1005,1011,809,4,793,1001,64,1,64,1105,1,809,1002,64,2,64,109,-3,2107,25,3,63,1005,63,827,4,815,1106,0,831,1001,64,1,64,1002,64,2,64,109,13,1206,7,847,1001,64,1,64,1106,0,849,4,837,1002,64,2,64,109,-11,21101,46,0,7,1008,1010,46,63,1005,63,871,4,855,1106,0,875,1001,64,1,64,1002,64,2,64,109,15,21102,47,1,-4,1008,1014,48,63,1005,63,895,1106,0,901,4,881,1001,64,1,64,4,64,99,21102,27,1,1,21101,0,915,0,1106,0,922,21201,1,63208,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,1,942,0,1106,0,922,21202,1,1,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1106,0,968,21201,-2,0,-2,109,-3,2106,0,0

1
inputs/09s1.txt Normal file
View File

@ -0,0 +1 @@
109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99

1
inputs/09s2.txt Normal file
View File

@ -0,0 +1 @@
1102,34915192,34915192,7,4,7,99,0

1
inputs/09s3.txt Normal file
View File

@ -0,0 +1 @@
104,1125899906842624,99

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 @@
.#....#####...#..
##...##.#####..##
##...#...#.#####.
..#.....#...###..
..#.#.....#....##

1
inputs/11p.txt Normal file
View File

@ -0,0 +1 @@
3,8,1005,8,330,1106,0,11,0,0,0,104,1,104,0,3,8,102,-1,8,10,101,1,10,10,4,10,1008,8,0,10,4,10,102,1,8,29,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,51,1,1103,2,10,1006,0,94,1006,0,11,1,1106,13,10,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,1001,8,0,87,3,8,102,-1,8,10,101,1,10,10,4,10,1008,8,0,10,4,10,1001,8,0,109,2,1105,5,10,2,103,16,10,1,1103,12,10,2,105,2,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,146,1006,0,49,2,1,12,10,2,1006,6,10,1,1101,4,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,1001,8,0,183,1,6,9,10,1006,0,32,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,213,2,1101,9,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,239,1006,0,47,1006,0,4,2,6,0,10,1006,0,58,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,0,10,4,10,102,1,8,274,2,1005,14,10,1006,0,17,1,104,20,10,1006,0,28,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1002,8,1,309,101,1,9,9,1007,9,928,10,1005,10,15,99,109,652,104,0,104,1,21101,0,937263411860,1,21102,347,1,0,1105,1,451,21101,932440724376,0,1,21102,1,358,0,1105,1,451,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21101,0,29015167015,1,21101,0,405,0,1106,0,451,21102,1,3422723163,1,21101,0,416,0,1106,0,451,3,10,104,0,104,0,3,10,104,0,104,0,21101,0,868389376360,1,21101,0,439,0,1105,1,451,21102,825544712960,1,1,21102,1,450,0,1106,0,451,99,109,2,21201,-1,0,1,21101,0,40,2,21102,482,1,3,21102,1,472,0,1106,0,515,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,477,478,493,4,0,1001,477,1,477,108,4,477,10,1006,10,509,1101,0,0,477,109,-2,2106,0,0,0,109,4,2101,0,-1,514,1207,-3,0,10,1006,10,532,21102,1,0,-3,22101,0,-3,1,22102,1,-2,2,21102,1,1,3,21101,551,0,0,1106,0,556,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,579,2207,-4,-2,10,1006,10,579,22102,1,-4,-4,1106,0,647,21201,-4,0,1,21201,-3,-1,2,21202,-2,2,3,21102,1,598,0,1106,0,556,22101,0,1,-4,21101,1,0,-1,2207,-4,-2,10,1006,10,617,21102,0,1,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,639,21201,-1,0,1,21102,639,1,0,105,1,514,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0

4
inputs/12p.txt Normal file
View File

@ -0,0 +1,4 @@
<x=-6, y=-5, z=-8>
<x=0, y=-3, z=-13>
<x=-15, y=10, z=-11>
<x=-3, y=-8, z=3>

4
inputs/12s1.txt Normal file
View File

@ -0,0 +1,4 @@
<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>

4
inputs/12s2.txt Normal file
View File

@ -0,0 +1,4 @@
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>

1
inputs/13p.txt Normal file

File diff suppressed because one or more lines are too long

56
inputs/14p.txt Normal file
View File

@ -0,0 +1,56 @@
6 WBVJ, 16 CDVNK => 2 PJBZT
135 ORE => 8 MWDXJ
27 NBRHT, 2 NSWK, 2 CMHMQ, 29 NFCB, 11 KNGJ, 12 MGCKC, 56 NHTKL, 7 WNFSV => 1 FUEL
1 SFJFX, 3 MXNK => 4 NLSBZ
2 PFKRW, 1 VXFRX, 22 QDJCL => 6 GBDG
7 TSTF, 4 ZLJN => 7 DMWS
5 KPCF, 1 DLMDJ, 1 FNWGH => 6 TSTF
8 DTWKS, 1 GBDG => 4 CGZQ
26 CNWZM, 4 KPCF => 3 DTWKS
1 JVLHM, 7 DTWKS, 7 PJBZT => 8 MRPHV
2 MWDXJ => 3 VHFPC
1 WXNW, 6 PFKRW => 7 ZVGVP
2 ZVGVP => 1 CMHMQ
8 JVLHM, 11 XRKN, 1 HCGKZ => 8 CHZLX
20 TSTF => 4 XDZMZ
3 CMHMQ, 7 ZVGVP, 10 XRKN => 9 FNWGH
12 HCGKZ, 4 NLSBZ, 15 RWRDP, 4 MRPHV, 31 KRDV, 6 PMXK, 2 NFVZ => 7 KNGJ
1 TXZCM => 9 BMPJ
2 ZFXQ => 3 NBRHT
13 JVLHM, 1 VHFPC => 3 PBJPZ
7 HCGKZ => 7 PMXK
2 RWRDP, 3 VSTQ, 12 PMXK => 7 MXNK
1 PJBZT, 3 QRSK => 1 KRDV
1 MGCKC, 6 CMHMQ => 6 PQTVS
1 TNHCS, 24 ZLJN => 4 RWRDP
5 MWDXJ, 1 WXNW => 9 QBCLF
1 ZFXQ, 1 DLMDJ => 4 DJXRM
1 ZFXQ => 2 CNWZM
1 KPCF => 6 ZXDVF
2 MRPHV => 1 GSTG
5 BMPJ, 2 ZLJN => 8 XQJZ
1 MWDXJ, 1 ZVGVP => 3 CDVNK
3 NFCB, 3 CMHMQ, 1 MWDXJ => 4 XRKN
1 WXNW, 1 TXZCM => 5 ZLJN
4 ZXDVF => 4 WBVJ
2 GBDG => 4 KPCF
4 CHZLX, 7 ZFXQ, 14 PQTVS => 9 VSTQ
3 TXZCM, 7 ZLJN, 7 ZXDVF => 9 JVLHM
1 DMWS, 3 TSTF => 5 HCGKZ
2 CGZQ => 4 NFVZ
2 PQTVS, 9 VMNJ => 9 TXZCM
3 KPCF => 4 DLMDJ
7 VMNJ, 24 XQJZ, 7 GSTG, 8 NLSBZ, 10 MGCKC, 2 SFJFX, 18 BMPJ => 1 NSWK
41 CNWZM, 5 DJXRM, 1 QRSK, 1 KPCF, 15 XDZMZ, 3 MRPHV, 1 NLSBZ, 9 KRDV => 2 WNFSV
10 PBJPZ, 29 BMPJ, 2 PMXK => 7 SFJFX
116 ORE => 4 WXNW
2 CNWZM => 2 TNHCS
10 QBCLF => 7 NFCB
1 QBCLF => 2 ZFXQ
15 ZLJN => 7 QRSK
183 ORE => 3 QDJCL
11 GBDG => 5 VMNJ
4 DMWS, 3 QRSK => 3 NHTKL
124 ORE => 6 VXFRX
1 MWDXJ => 6 MGCKC
108 ORE => 9 PFKRW

6
inputs/14s1.txt Normal file
View File

@ -0,0 +1,6 @@
10 ORE => 10 A
1 ORE => 1 B
7 A, 1 B => 1 C
7 A, 1 C => 1 D
7 A, 1 D => 1 E
7 A, 1 E => 1 FUEL

7
inputs/14s2.txt Normal file
View File

@ -0,0 +1,7 @@
9 ORE => 2 A
8 ORE => 3 B
7 ORE => 5 C
3 A, 4 B => 1 AB
5 B, 7 C => 1 BC
4 C, 1 A => 1 CA
2 AB, 3 BC, 4 CA => 1 FUEL

9
inputs/14s3.txt Normal file
View File

@ -0,0 +1,9 @@
157 ORE => 5 NZVS
165 ORE => 6 DCFZ
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
179 ORE => 7 PSHF
177 ORE => 5 HKGWZ
7 DCFZ, 7 PSHF => 2 XJWVT
165 ORE => 2 GPVTF
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT

12
inputs/14s4.txt Normal file
View File

@ -0,0 +1,12 @@
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
17 NVRVD, 3 JNWZP => 8 VPVL
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
22 VJHF, 37 MNCFX => 5 FWMGM
139 ORE => 4 NVRVD
144 ORE => 7 JNWZP
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
145 ORE => 6 MNCFX
1 NVRVD => 8 CXFTF
1 VJHF, 6 MNCFX => 4 RFSQX
176 ORE => 6 VJHF

17
inputs/14s5.txt Normal file
View File

@ -0,0 +1,17 @@
171 ORE => 8 CNZTR
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
114 ORE => 4 BHXH
14 VRPVC => 6 BMBT
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
5 BMBT => 4 WPTQ
189 ORE => 9 KTJDG
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
12 VRPVC, 27 CNZTR => 2 XDBXC
15 KTJDG, 12 BHXH => 5 XCVML
3 BHXH, 2 VRPVC => 7 MZWV
121 ORE => 7 VRPVC
7 XCVML => 6 RJRHP
5 BHXH, 4 VRPVC => 5 LTCX

1
inputs/15p.txt Normal file
View File

@ -0,0 +1 @@
3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,102,1,1034,1039,101,0,1036,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1105,1,124,102,1,1034,1039,1001,1036,0,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1105,1,124,1001,1034,-1,1039,1008,1036,0,1041,1001,1035,0,1040,1001,1038,0,1043,1002,1037,1,1042,1105,1,124,1001,1034,1,1039,1008,1036,0,1041,102,1,1035,1040,101,0,1038,1043,1001,1037,0,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,3,1032,1006,1032,165,1008,1040,33,1032,1006,1032,165,1101,0,2,1044,1105,1,224,2,1041,1043,1032,1006,1032,179,1101,1,0,1044,1106,0,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,37,1044,1106,0,224,1102,1,0,1044,1105,1,224,1006,1044,247,101,0,1039,1034,101,0,1040,1035,1001,1041,0,1036,1002,1043,1,1038,101,0,1042,1037,4,1044,1106,0,0,42,4,15,10,25,91,86,34,69,14,50,9,24,24,54,10,18,63,17,2,88,36,31,60,20,13,20,76,94,25,41,36,78,3,39,17,94,10,25,22,16,67,72,31,47,15,25,66,8,17,54,8,89,67,29,28,92,11,54,14,4,64,78,28,80,66,6,70,36,56,13,63,17,19,83,17,27,29,34,54,4,93,24,71,6,66,22,21,92,93,39,4,31,76,72,25,74,89,18,62,18,27,57,35,83,39,14,23,95,2,79,25,97,86,13,79,1,34,90,81,29,45,31,38,67,17,92,32,31,50,1,42,81,1,2,87,7,52,74,20,85,22,32,47,16,77,96,28,14,74,22,55,15,75,44,29,19,8,73,2,54,18,26,64,95,21,98,48,25,36,11,78,77,5,16,70,18,10,76,51,51,10,25,43,56,12,13,48,8,17,68,10,64,25,93,42,3,52,24,72,99,23,54,13,44,17,15,8,68,59,15,95,61,9,50,8,51,23,8,39,13,95,64,12,28,56,90,1,62,27,12,60,6,5,18,24,13,99,12,18,92,97,7,56,22,48,91,34,87,32,98,20,89,74,16,51,84,21,46,14,23,52,17,57,12,50,17,97,23,99,11,21,68,21,61,89,13,45,64,89,18,36,40,35,90,9,1,3,81,33,32,83,99,97,34,4,46,31,21,90,62,14,93,11,22,99,51,70,88,51,2,4,29,36,35,48,17,25,30,69,34,3,39,89,31,89,33,30,88,77,18,30,67,17,40,61,19,40,85,26,23,49,22,41,30,13,79,6,34,40,33,43,49,84,19,78,43,10,74,18,61,15,22,51,86,2,78,11,33,92,24,88,27,24,44,2,97,4,4,49,72,93,24,65,79,21,60,33,46,36,22,15,87,33,78,2,49,70,7,78,78,11,14,64,41,61,41,6,1,49,35,78,47,65,14,66,10,86,76,2,32,88,3,24,14,87,9,95,32,19,4,10,67,60,15,19,53,47,24,29,65,5,95,35,1,70,16,43,53,11,64,17,34,84,74,65,30,18,58,2,35,48,38,33,46,16,87,27,12,79,11,88,35,7,5,35,67,83,38,6,17,56,82,13,45,32,30,67,25,62,7,43,63,9,36,14,58,53,25,98,12,38,78,13,63,93,33,11,54,9,66,32,79,62,47,28,6,67,31,53,71,2,30,59,12,90,59,67,2,58,52,1,30,51,49,22,89,88,27,19,41,27,13,19,76,5,82,58,12,49,51,17,15,73,35,25,74,90,29,14,96,83,69,11,18,14,10,40,93,35,31,35,36,58,36,16,48,7,66,98,31,47,34,47,33,5,28,82,88,1,30,80,95,32,87,2,19,91,74,74,19,8,25,63,65,51,30,14,41,98,99,21,90,15,91,3,31,74,27,31,77,28,74,4,27,88,82,11,54,35,52,13,88,71,93,20,82,18,36,68,33,83,1,18,5,42,46,29,62,10,78,67,9,84,48,22,33,74,36,53,58,31,5,8,55,10,24,49,34,81,1,4,86,5,25,2,75,36,49,2,24,88,72,8,64,36,38,10,23,36,93,28,51,90,4,99,57,31,10,14,94,21,27,61,34,70,41,32,14,91,20,83,30,54,26,44,30,85,96,87,35,16,61,99,16,32,53,68,87,1,89,43,9,17,4,39,50,61,8,49,27,48,13,51,34,47,30,89,68,50,18,63,99,50,32,41,33,71,1,43,57,64,24,95,9,89,8,64,18,75,23,97,74,67,24,55,1,87,97,44,0,0,21,21,1,10,1,0,0,0,0,0,0

1
inputs/16p.txt Normal file
View File

@ -0,0 +1 @@
59791875142707344554745984624833270124746225787022156176259864082972613206097260696475359886661459314067969858521185244807128606896674972341093111690401527976891268108040443281821862422244152800144859031661510297789792278726877676645835805097902853584093615895099152578276185267316851163313487136731134073054989870018294373731775466754420075119913101001966739563592696702233028356328979384389178001923889641041703308599918672055860556825287836987992883550004999016194930620165247185883506733712391462975446192414198344745434022955974228926237100271949068464343172968939069550036969073411905889066207300644632441054836725463178144030305115977951503567

1
inputs/16s1.txt Normal file
View File

@ -0,0 +1 @@
12345678

1
inputs/16s2.txt Normal file
View File

@ -0,0 +1 @@
80871224585914546619083218645595

1
inputs/16s3.txt Normal file
View File

@ -0,0 +1 @@
19617804207202209144916044189917

1
inputs/16s4.txt Normal file
View File

@ -0,0 +1 @@
69317163492948606335995924319873

1
inputs/17p.txt Normal file
View File

@ -0,0 +1 @@
1,330,331,332,109,2734,1102,1182,1,15,1102,1,1429,24,1002,0,1,570,1006,570,36,1001,571,0,0,1001,570,-1,570,1001,24,1,24,1106,0,18,1008,571,0,571,1001,15,1,15,1008,15,1429,570,1006,570,14,21102,58,1,0,1105,1,786,1006,332,62,99,21101,0,333,1,21102,73,1,0,1105,1,579,1101,0,0,572,1101,0,0,573,3,574,101,1,573,573,1007,574,65,570,1005,570,151,107,67,574,570,1005,570,151,1001,574,-64,574,1002,574,-1,574,1001,572,1,572,1007,572,11,570,1006,570,165,101,1182,572,127,1001,574,0,0,3,574,101,1,573,573,1008,574,10,570,1005,570,189,1008,574,44,570,1006,570,158,1105,1,81,21102,1,340,1,1105,1,177,21101,0,477,1,1106,0,177,21101,0,514,1,21101,0,176,0,1106,0,579,99,21102,1,184,0,1106,0,579,4,574,104,10,99,1007,573,22,570,1006,570,165,1001,572,0,1182,21102,1,375,1,21101,211,0,0,1106,0,579,21101,1182,11,1,21102,1,222,0,1105,1,979,21102,1,388,1,21102,233,1,0,1105,1,579,21101,1182,22,1,21101,0,244,0,1106,0,979,21101,401,0,1,21101,0,255,0,1105,1,579,21101,1182,33,1,21102,266,1,0,1106,0,979,21102,1,414,1,21102,1,277,0,1106,0,579,3,575,1008,575,89,570,1008,575,121,575,1,575,570,575,3,574,1008,574,10,570,1006,570,291,104,10,21102,1182,1,1,21101,313,0,0,1106,0,622,1005,575,327,1102,1,1,575,21102,327,1,0,1105,1,786,4,438,99,0,1,1,6,77,97,105,110,58,10,33,10,69,120,112,101,99,116,101,100,32,102,117,110,99,116,105,111,110,32,110,97,109,101,32,98,117,116,32,103,111,116,58,32,0,12,70,117,110,99,116,105,111,110,32,65,58,10,12,70,117,110,99,116,105,111,110,32,66,58,10,12,70,117,110,99,116,105,111,110,32,67,58,10,23,67,111,110,116,105,110,117,111,117,115,32,118,105,100,101,111,32,102,101,101,100,63,10,0,37,10,69,120,112,101,99,116,101,100,32,82,44,32,76,44,32,111,114,32,100,105,115,116,97,110,99,101,32,98,117,116,32,103,111,116,58,32,36,10,69,120,112,101,99,116,101,100,32,99,111,109,109,97,32,111,114,32,110,101,119,108,105,110,101,32,98,117,116,32,103,111,116,58,32,43,10,68,101,102,105,110,105,116,105,111,110,115,32,109,97,121,32,98,101,32,97,116,32,109,111,115,116,32,50,48,32,99,104,97,114,97,99,116,101,114,115,33,10,94,62,118,60,0,1,0,-1,-1,0,1,0,0,0,0,0,0,1,6,0,0,109,4,1202,-3,1,587,20101,0,0,-1,22101,1,-3,-3,21101,0,0,-2,2208,-2,-1,570,1005,570,617,2201,-3,-2,609,4,0,21201,-2,1,-2,1105,1,597,109,-4,2106,0,0,109,5,2102,1,-4,630,20102,1,0,-2,22101,1,-4,-4,21102,0,1,-3,2208,-3,-2,570,1005,570,781,2201,-4,-3,652,21002,0,1,-1,1208,-1,-4,570,1005,570,709,1208,-1,-5,570,1005,570,734,1207,-1,0,570,1005,570,759,1206,-1,774,1001,578,562,684,1,0,576,576,1001,578,566,692,1,0,577,577,21101,0,702,0,1105,1,786,21201,-1,-1,-1,1106,0,676,1001,578,1,578,1008,578,4,570,1006,570,724,1001,578,-4,578,21101,0,731,0,1106,0,786,1105,1,774,1001,578,-1,578,1008,578,-1,570,1006,570,749,1001,578,4,578,21101,0,756,0,1106,0,786,1106,0,774,21202,-1,-11,1,22101,1182,1,1,21102,774,1,0,1105,1,622,21201,-3,1,-3,1105,1,640,109,-5,2106,0,0,109,7,1005,575,802,21002,576,1,-6,20101,0,577,-5,1105,1,814,21102,1,0,-1,21102,1,0,-5,21102,0,1,-6,20208,-6,576,-2,208,-5,577,570,22002,570,-2,-2,21202,-5,29,-3,22201,-6,-3,-3,22101,1429,-3,-3,1202,-3,1,843,1005,0,863,21202,-2,42,-4,22101,46,-4,-4,1206,-2,924,21102,1,1,-1,1105,1,924,1205,-2,873,21102,1,35,-4,1105,1,924,1202,-3,1,878,1008,0,1,570,1006,570,916,1001,374,1,374,2101,0,-3,895,1101,2,0,0,2102,1,-3,902,1001,438,0,438,2202,-6,-5,570,1,570,374,570,1,570,438,438,1001,578,558,922,20101,0,0,-4,1006,575,959,204,-4,22101,1,-6,-6,1208,-6,29,570,1006,570,814,104,10,22101,1,-5,-5,1208,-5,45,570,1006,570,810,104,10,1206,-1,974,99,1206,-1,974,1101,1,0,575,21101,973,0,0,1105,1,786,99,109,-7,2105,1,0,109,6,21101,0,0,-4,21102,1,0,-3,203,-2,22101,1,-3,-3,21208,-2,82,-1,1205,-1,1030,21208,-2,76,-1,1205,-1,1037,21207,-2,48,-1,1205,-1,1124,22107,57,-2,-1,1205,-1,1124,21201,-2,-48,-2,1105,1,1041,21102,-4,1,-2,1106,0,1041,21101,-5,0,-2,21201,-4,1,-4,21207,-4,11,-1,1206,-1,1138,2201,-5,-4,1059,1201,-2,0,0,203,-2,22101,1,-3,-3,21207,-2,48,-1,1205,-1,1107,22107,57,-2,-1,1205,-1,1107,21201,-2,-48,-2,2201,-5,-4,1090,20102,10,0,-1,22201,-2,-1,-2,2201,-5,-4,1103,2101,0,-2,0,1106,0,1060,21208,-2,10,-1,1205,-1,1162,21208,-2,44,-1,1206,-1,1131,1105,1,989,21101,439,0,1,1105,1,1150,21102,1,477,1,1105,1,1150,21102,1,514,1,21102,1,1149,0,1105,1,579,99,21101,1157,0,0,1106,0,579,204,-2,104,10,99,21207,-3,22,-1,1206,-1,1138,2101,0,-5,1176,1202,-4,1,0,109,-6,2105,1,0,6,5,28,1,28,1,28,1,28,1,28,1,20,11,1,7,10,1,7,1,1,1,1,1,5,1,10,1,5,11,1,1,10,1,5,1,1,1,1,1,1,1,3,1,1,1,10,9,1,1,1,1,1,5,16,1,3,1,1,1,1,1,1,1,18,5,1,1,1,1,1,1,24,1,1,1,1,1,24,1,1,1,1,1,24,1,1,1,1,1,24,5,7,1,18,1,9,1,18,1,9,1,18,1,9,1,18,1,9,1,18,1,9,1,12,7,9,1,12,1,15,1,12,1,15,1,12,1,15,1,12,1,15,1,12,1,15,1,2,5,5,1,9,7,2,1,3,1,5,1,9,1,8,1,3,1,5,1,9,1,8,1,3,1,5,1,9,1,8,1,3,13,3,1,8,1,9,1,5,1,3,1,8,11,5,1,1,5,22,1,1,1,1,1,1,1,22,1,1,1,1,1,1,1,22,1,1,1,1,1,1,1,22,13,18,1,1,1,1,1,5,1,16,5,1,1,5,1,16,1,1,1,3,1,5,1,16,1,1,11,16,1,5,1,22,7,6

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

54
main.go
View File

@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"runtime/pprof"
"strconv"
"strings"
"time"
@ -26,8 +27,10 @@ const (
)
var (
flagPart1 = flag.Bool("part1", false, "whether to run part1 or not; if no flags are present, all parts are run")
flagPart2 = flag.Bool("part2", false, "whether to run part2 or not; if no flags are present, all parts are run")
flagPart1 = flag.Bool("part1", false, "whether to run part1 or not; if no flags are present, all parts are run")
flagPart2 = flag.Bool("part2", false, "whether to run part2 or not; if no flags are present, all parts are run")
flagCpuProfile = flag.String("cpuprofile", "", "write cpu profile to file")
flagMemProfile = flag.String("memprofile", "", "write memory profile to file")
)
var dayMap = []day{
@ -35,11 +38,37 @@ var dayMap = []day{
&days.Day02{},
&days.Day03{},
&days.Day04{},
&days.Day05{},
&days.Day06{},
&days.Day07{},
&days.Day08{},
&days.Day09{},
&days.Day10{},
&days.Day11{},
&days.Day12{},
&days.Day13{},
&days.Day14{},
&days.Day15{},
&days.Day16{},
&days.Day17{},
&days.Day18{},
&days.Day19{},
}
func main() {
flag.Parse()
if *flagCpuProfile != "" {
f, err := os.Create(*flagCpuProfile)
if err != nil {
log.Fatal(err)
}
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
arg := strconv.Itoa(len(dayMap))
flagArgs := flag.Args()
if len(flagArgs) > 0 && len(flagArgs[0]) > 0 {
@ -64,7 +93,14 @@ func main() {
solve(dayMap[iArg-1])
}
os.Exit(0)
if *flagMemProfile != "" {
f, err := os.Create(*flagMemProfile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
}
}
func solve(d day) {
@ -84,6 +120,11 @@ func solve(d day) {
part1Text = d.Part1()
}
part1Time := time.Since(part1Start)
if runPart1 {
fmt.Println(part1Header)
fmt.Println(">", part1Text)
fmt.Println()
}
part2Start := time.Now()
var part2Text string
@ -91,17 +132,12 @@ func solve(d day) {
part2Text = d.Part2()
}
part2Time := time.Since(part2Start)
if runPart1 {
fmt.Println(part1Header)
fmt.Println(">", part1Text)
fmt.Println()
}
if runPart2 {
fmt.Println(part2Header)
fmt.Println(">", part2Text)
fmt.Println()
}
fmt.Print(utilities.ColorBrightBlack)
fmt.Println("Parsed in", parseTime)
if runPart1 {

22
utilities/array.go Normal file
View File

@ -0,0 +1,22 @@
package utilities
// ArrayContains returns whether the specified array contains the specified value
func ArrayContains[T comparable](array []T, val T) bool {
for _, v := range array {
if v == val {
return true
}
}
return false
}
func AddToArray[V comparable, T ~[]V](arr *T, val V) bool {
for _, v := range *arr {
if v == val {
return false
}
}
*arr = append(*arr, val)
return true
}

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
}

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
}

View File

@ -1,52 +1,263 @@
package utilities
import (
"fmt"
"strconv"
"strings"
)
const (
opAdd = 1
opMul = 2
opEnd = 99
opAdd = 1
opMultiply = 2
opInput = 3
opOutput = 4
opJumpIfTrue = 5
opJumpIfFalse = 6
opLessThan = 7
opEquals = 8
opRelativeBase = 9
opHalt = 99
modePosition = 0
modeImmediate = 1
modeRelative = 2
)
type IntcodeProgram []int64
type IntcodeProgram struct {
memory []int64
program []int64
relativeBase int
haltRequested bool
}
type IntcodeProgramState struct {
program *IntcodeProgram
CurrentInstruction int
NextInstruction int
}
func (s IntcodeProgramState) IsHalting() bool {
return s.program.GetMemory(s.NextInstruction) == opHalt
}
type ProvideInputFunc func(inputStep int) int64
type ReceiveOutputFunc func(val int64, state IntcodeProgramState)
func ParseIntcodeProgram(programStr string) IntcodeProgram {
nums := strings.Split(programStr, ",")
program := make(IntcodeProgram, len(nums))
program := IntcodeProgram{
program: make([]int64, len(nums)),
}
for idx, num := range nums {
iNum, err := strconv.ParseInt(num, 10, 64)
if err != nil {
panic(err)
}
program[idx] = iNum
program.program[idx] = iNum
}
return program
}
func (program IntcodeProgram) Run() {
for instructionPointer := 0; instructionPointer < len(program); {
opcode := program[instructionPointer]
switch opcode {
case opAdd:
param1 := program[instructionPointer+1]
param2 := program[instructionPointer+2]
param3 := program[instructionPointer+3]
program[param3] = program[param1] + program[param2]
instructionPointer += 4
case opMul:
param1 := program[instructionPointer+1]
param2 := program[instructionPointer+2]
param3 := program[instructionPointer+3]
program[param3] = program[param1] * program[param2]
instructionPointer += 4
case opEnd:
instructionPointer = len(program)
}
func (p *IntcodeProgram) makeState(instructionPointer int) IntcodeProgramState {
return IntcodeProgramState{
program: p,
CurrentInstruction: instructionPointer,
NextInstruction: instructionPointer + 1,
}
}
func (p *IntcodeProgram) Copy() IntcodeProgram {
ret := IntcodeProgram{
program: make([]int64, len(p.program)),
}
copy(ret.program, p.program)
return ret
}
func (p *IntcodeProgram) init() {
if p.memory == nil {
p.memory = make([]int64, len(p.program))
copy(p.memory, p.program)
}
}
func (p *IntcodeProgram) getParamValue(param, mode int) int64 {
switch mode {
case modePosition:
return p.GetMemory(param)
case modeImmediate:
return int64(param)
case modeRelative:
return p.GetMemory(param + p.relativeBase)
}
panic("unhandled param mode")
}
func (p *IntcodeProgram) GetMemory(idx int) int64 {
p.ensureMemoryCapacity(idx)
return p.memory[idx]
}
func (p *IntcodeProgram) SetMemory(idx int, val int64) {
p.init()
p.ensureMemoryCapacity(idx)
p.memory[idx] = val
}
func (p *IntcodeProgram) setMemory(idx int, val int64, mode int) {
if mode == modeImmediate {
panic("exception executing program - write parameter must never be in immediate mode")
}
if mode == modeRelative {
idx = idx + p.relativeBase
}
p.SetMemory(idx, val)
}
func (p *IntcodeProgram) ensureMemoryCapacity(address int) {
if len(p.memory) > address {
return
}
p.memory = append(p.memory, make([]int64, address+1-len(p.memory))...)
}
func (p *IntcodeProgram) Reset() {
wiped := false
if len(p.memory) != len(p.program) {
wiped = true
p.memory = nil
}
p.init()
if !wiped {
copy(p.memory, p.program)
}
p.relativeBase = 0
}
func (p *IntcodeProgram) Run() {
p.RunIn(func(int) int64 { return 0 }, func(int64, IntcodeProgramState) {})
}
func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOutputFunc) {
p.init()
inputsRequested := 0
for instructionPointer := 0; instructionPointer < len(p.program) && !p.haltRequested; {
instruction := p.GetMemory(instructionPointer)
instructionPointer++
paramModes := [3]int{
modePosition,
modePosition,
modePosition,
}
modes := instruction / 100
for i := 0; modes > 0; i++ {
paramModes[i] = int(modes % 10)
modes = modes / 10
}
opcode := instruction % 100
switch opcode {
case opAdd:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
param3 := p.GetMemory(instructionPointer + 2)
p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])+p.getParamValue(int(param2), paramModes[1]), paramModes[2])
instructionPointer += 3
case opMultiply:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
param3 := p.GetMemory(instructionPointer + 2)
p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])*p.getParamValue(int(param2), paramModes[1]), paramModes[2])
instructionPointer += 3
case opInput:
inputsRequested++
param1 := p.GetMemory(instructionPointer)
p.setMemory(int(param1), inputFunc(inputsRequested), paramModes[0])
instructionPointer += 1
case opOutput:
param1 := p.GetMemory(instructionPointer)
outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer))
instructionPointer += 1
case opJumpIfTrue:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
if p.getParamValue(int(param1), paramModes[0]) != 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
} else {
instructionPointer += 2
}
case opJumpIfFalse:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
if p.getParamValue(int(param1), paramModes[0]) == 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
} else {
instructionPointer += 2
}
case opLessThan:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
param3 := p.GetMemory(instructionPointer + 2)
if p.getParamValue(int(param1), paramModes[0]) < p.getParamValue(int(param2), paramModes[1]) {
p.setMemory(int(param3), 1, paramModes[2])
} else {
p.setMemory(int(param3), 0, paramModes[2])
}
instructionPointer += 3
case opEquals:
param1 := p.GetMemory(instructionPointer)
param2 := p.GetMemory(instructionPointer + 1)
param3 := p.GetMemory(instructionPointer + 2)
if p.getParamValue(int(param1), paramModes[0]) == p.getParamValue(int(param2), paramModes[1]) {
p.setMemory(int(param3), 1, paramModes[2])
} else {
p.setMemory(int(param3), 0, paramModes[2])
}
instructionPointer += 3
case opRelativeBase:
param1 := p.GetMemory(instructionPointer)
p.relativeBase += int(p.getParamValue(int(param1), paramModes[0]))
instructionPointer += 1
case opHalt:
instructionPointer = len(p.program)
default:
panic(fmt.Sprintf("exception executing program - unhandled opcode %d", opcode))
}
}
p.haltRequested = false
}
func (p *IntcodeProgram) Stop() {
p.haltRequested = true
}

27
utilities/map.go Normal file
View File

@ -0,0 +1,27 @@
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
}
// 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
}

49
utilities/math.go Normal file
View File

@ -0,0 +1,49 @@
package utilities
import "math"
func GCD[T Integer](a, b T) T {
if b == 0 {
return a
}
return GCD(b, a%b)
}
func LCM[T Integer](nums ...T) uint64 {
num := len(nums)
if num == 0 {
return 0
} else if num == 1 {
return uint64(nums[0])
}
ret := lcm(nums[0], nums[1])
for i := 2; i < len(nums); i++ {
ret = lcm(uint64(nums[i]), ret)
}
return ret
}
func lcm[T Integer](a, b T) uint64 {
return uint64(a*b) / uint64(GCD(a, b))
}
func Min[T Number](nums ...T) T {
numNums := len(nums)
if numNums == 2 {
return T(math.Min(float64(nums[0]), float64(nums[1])))
}
if numNums == 0 {
return 0
}
least := nums[0]
for i := 1; i < numNums; i++ {
if nums[i] < least {
least = nums[i]
}
}
return least
}

6
utilities/pair.go Normal file
View File

@ -0,0 +1,6 @@
package utilities
type Pair[T, U any] struct {
First T
Second U
}

34
utilities/permutations.go Normal file
View File

@ -0,0 +1,34 @@
package utilities
type Permutable interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}
func GetPermutations[T Permutable](arr ...T) [][]T {
var helper func([]T, int)
res := [][]T{}
helper = func(arr []T, n int) {
if n == 1 {
tmp := make([]T, len(arr))
copy(tmp, arr)
res = append(res, tmp)
} else {
for i := 0; i < n; i++ {
helper(arr, n-1)
if n%2 == 1 {
tmp := arr[i]
arr[i] = arr[n-1]
arr[n-1] = tmp
} else {
tmp := arr[0]
arr[0] = arr[n-1]
arr[n-1] = tmp
}
}
}
}
helper(arr, len(arr))
return res
}

81
utilities/vector.go Normal file
View File

@ -0,0 +1,81 @@
package utilities
import "math"
type Vec2[T Number] struct {
X T
Y T
}
type Vec3[T Number] struct {
X T
Y T
Z T
}
type Vec2i Vec2[int]
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 (v Vec2[T]) Equals(other Vec2[T]) bool {
return v.X == other.X &&
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 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 {
return (v.X * other.X) + (v.Y * other.Y) + (v.Z * other.Z)
}
func (v Vec3[T]) Len() T {
return T(math.Sqrt(float64(v.LenSquared())))
}
func (v Vec3[T]) LenSquared() T {
return (v.X * v.X) + (v.Y * v.Y) + (v.Z * v.Z)
}
func (v *Vec3[T]) Add(other Vec3[T]) {
v.X += other.X
v.Y += other.Y
v.Z += other.Z
}
func (v Vec3[T]) Equals(other Vec3[T]) bool {
return v.X == other.X &&
v.Y == other.Y &&
v.Z == other.Z
}