26 Commits
dev/19 ... main

Author SHA1 Message Date
2b554d1b3d Day 25 solution
Sort of...I mean, you have to play it yourself, but it works. This is a text-based adventure game. The goal is to go around picking up items (except for the 5 items that will kill you/end or hang the game) then find the right combination of items that lets you get through the pressure-sensitive room which terminates the game and gives you the code to enter on the website.

My understanding is that every item has a power-of-2 weight to it, and you have to reach the right set of items to have your weight match the target. The main problem I had was that the output for the pressure room's "heavier" and "lighter" words mean that _other_ droids are heavier/lighter than _you_. Which means if it says "lighter", you need to shed weight/drop stuff, but if it says "heavier", you need to add weight/pick up stuff.

This could be automated in several different ways, but my thought is you'd want to explore the maze, picking up everything that's not the blocked 5 items, find the pressure-sensitive room, and try every combination of items until the process exits, at which point you'd display the number after "by typing" in the final output string. I've stubbed out support for reading the program's output so that you could inspect it and automate if you really wanted to. Right now it just parses the answer from the final output line and prints that by itself.

Items that **should not** be picked up are:

    infinite loop (does what it says to your program)
    molten lava (kills you)
    escape pod (ends the game)
    photons (turns out the lights and causes you to get eaten by a grue)
    giant electromagnet (gets you stuck - no other input works and it cannot be dropped)

This commit's program lets you through if you're carrying:

- ornament
- astrolabe
- weather machine
- food ration

which results in a code of 4206594
2022-07-22 16:56:47 -05:00
5a53ccc865 Allow day 2 to run on any input
I had this in as a sanity check.
2022-07-22 08:59:42 -05:00
a723fc113a Day 24 solution
This one was pretty straightforward and completes the "Game of Life" AoC bingo square. Part 2 felt a little tedious insofar as there wasn't much problem solving to do, just push the part 1 solution into a map/dictionary and implement the new adjacency logic per the spec.
2022-07-21 09:37:39 -05:00
7e4b44a3b6 Day 23 solution
This one takes around a second to arrive at the first answer and around 15 seconds to arrive at the second answer. I don't know why, since I'm just running the given program as fast as possible (is my interpreter slow?). Go channels should be about as fast as we can get here. Maybe it would actually run faster if everyone stopped and processed their inputs at the same time or something? I dunno.

Part 2 was pretty simple, though I feel like my idle detection could be improved. Seems to work, though.
2022-07-20 08:35:34 -05:00
4175778d52 Day 22 solution
Part 1 was very straightforward. I believe there are ways to calculate this answer without the complicated maths from part 2 and without actually applying each of the instructions, but I'm not concerned with finding it.

Part 2 was a big "nope" from me. I went out and found this answer and I don't understand it. I'm okay with that.
2022-07-19 13:29:19 -05:00
e5dd6a6ca3 Day 21 solution
Hopefully my logic is explained well enough in the comments for this one. We're just layering more programming languages on top of other programming languages. This is how you anger the computer gods and bring about the AI singularity.

I also made some general tweaks to the Intcode machine to make ASCII intcode machines dead simple to deal with. Is it worth the extra branches for each input and output instruction in the interpreter? Probably not...but I was never going to win any speed competitions anyway.
2022-07-18 09:20:29 -05:00
effd94c09b Day 20 solution
Part 1 was pretty much just day 18 again, but without doors and with an input data parsing skill check. I simplified day 18 and went with a basic Dijkstra algorithm after the DFS to crawl the maze which solved this just fine.

Part 2 messed me up more than I was expecting. The key feature for part 2 is changing up what the Dijkstra solver views as an "adjacent" node and injecting the appropriate depth as we traverse inner/outer portals. The main thing that tripped me up here from part 1 was that I needed to be carrying more data along with portals than just their locations, and I could no longer get away with storing the portals as pairs of their inner/outer locations, so a small refactor was needed. Once I made those corrections, it was mostly a matter of ironing out the "get neighbors" function to adhere to part 2's rules, which took me a lot of debugging to get just right.

Part 2 still took me longer than I'd care to admit. I had problems wrapping my head around the problem.
2022-07-14 14:54:02 -05:00
f9f31820ac Day 19 solution
I had some trouble with the logic of this puzzle in my head, and the visuals were too large to realistically render for me to spot-check my solution, so I kept getting part 2 wrong. I eventually realized what needed to be done to get the right answer, which was partially the realization that I needed to check +99 instead of +100, and partially that I probably didn't need to check the entire space, just the bounding points, at least for the initial filters.

And then I optimized it with a bisect instead of a linear search. Always a good idea when you need to narrow a large range to a single point.
2022-07-13 08:40:48 -05:00
4db2d11401 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-07-12 08:45:47 -05:00
3f1c66813c 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-07-12 08:44:46 -05:00
1f27585231 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-07-11 09:31:26 -05:00
ec27a5ec6c 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.

Also, fun fact: changing the numberSet from an array of ints (my initial implementation) to an array of int8's (what's here now) reduced the memory footprint of the application substantially (from something like 50mb to closer to 8).
2022-06-30 08:03:18 -05:00
f0be3f9f98 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-29 08:11:33 -05:00
4cde56eb84 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 tweaking and rewriting until I landed on this which wasn't far off from what I was trying to do previously, but I had been 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-28 08:32:59 -05:00
dd5ea5ea86 Day 13 solution
This was incredibly cool and I had a really fun time with it. Uncomment the draw and sleep in Part2 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-27 00:01:19 -05:00
2178a2618d Day 12 solution
Okay, I had to seek some advice on this one. The orbital period + least-common-multiple solution was not coming to me naturally.
2022-06-23 10:08:33 -05:00
4cdd9c645b Day 11 solution 2022-06-22 08:17:45 -05:00
14625d191e Add utility for array AddUnique 2022-06-21 12:23:12 -05:00
1cdc94bebe 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-21 12:22:30 -05:00
ff10dfd1ba 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-21 12:21:03 -05:00
ca6a16cedd 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 12:20:37 -05:00
0b4cd9e634 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-21 12:18:31 -05:00
d7836f4e59 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-20 08:54:52 -05:00
b321fb87ec 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-16 08:24:56 -05:00
d129e52c70 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-15 08:58:51 -05:00
c8878ffbce 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-14 09:21:10 -05:00
37 changed files with 1633 additions and 82 deletions

View File

@ -28,9 +28,6 @@ func (d *Day02) Part1() string {
d.setParams(12, 2) d.setParams(12, 2)
d.program.Run() d.program.Run()
if d.program.GetMemory(0) != 4138658 {
panic("")
}
return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, d.program.GetMemory(0), utilities.TextReset) return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, d.program.GetMemory(0), utilities.TextReset)
} }
@ -59,9 +56,6 @@ func (d *Day02) Part2() string {
if !found { if !found {
panic("!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", return fmt.Sprintf("%d created by noun=%d, verb=%d. 100 * noun + verb = %s%d%s",
sentinel, sentinel,

View File

@ -45,9 +45,9 @@ func (d Day10) Num() int {
// } // }
// } // }
func (d Day10) getVisibleAsteroids(i1, j1 int) []u.Vec2[int] { func (d Day10) getVisibleAsteroids(i1, j1 int) map[u.Vec2[int]]bool {
visited := make([]u.Vec2[int], 0) visited := make(map[u.Vec2[int]]bool, 0)
foundAsteroids := make([]u.Vec2[int], 0) foundAsteroids := make(map[u.Vec2[int]]bool, 0)
findNext := func(startX, startY, incX, incY int) *u.Vec2[int] { findNext := func(startX, startY, incX, incY int) *u.Vec2[int] {
var found *u.Vec2[int] var found *u.Vec2[int]
@ -59,8 +59,8 @@ func (d Day10) getVisibleAsteroids(i1, j1 int) []u.Vec2[int] {
y := startY + incY y := startY + incY
for x < len(d.asteroids) && x >= 0 && y < len(d.asteroids[x]) && y >= 0 { for x < len(d.asteroids) && x >= 0 && y < len(d.asteroids[x]) && y >= 0 {
currPair := u.Vec2[int]{X: x, Y: y} currPair := u.Vec2[int]{X: x, Y: y}
if !u.ArrayContains(visited, currPair) { if _, exists := visited[currPair]; !exists {
visited = append(visited, currPair) visited[currPair] = true
if d.asteroids[x][y] { if d.asteroids[x][y] {
if found == nil { if found == nil {
@ -91,16 +91,16 @@ func (d Day10) getVisibleAsteroids(i1, j1 int) []u.Vec2[int] {
} }
if found := findNext(i1, j1, incX, incY); found != nil { if found := findNext(i1, j1, incX, incY); found != nil {
foundAsteroids = append(foundAsteroids, *found) foundAsteroids[*found] = true
} }
if found := findNext(i1, j1, incX, -incY); found != nil { if found := findNext(i1, j1, incX, -incY); found != nil {
foundAsteroids = append(foundAsteroids, *found) foundAsteroids[*found] = true
} }
if found := findNext(i1, j1, -incX, incY); found != nil { if found := findNext(i1, j1, -incX, incY); found != nil {
foundAsteroids = append(foundAsteroids, *found) foundAsteroids[*found] = true
} }
if found := findNext(i1, j1, -incX, -incY); found != nil { if found := findNext(i1, j1, -incX, -incY); found != nil {
foundAsteroids = append(foundAsteroids, *found) foundAsteroids[*found] = true
} }
incY++ incY++
@ -116,8 +116,8 @@ func (d Day10) numVisibleAsteroids(i1, j1 int) int {
return len(d.getVisibleAsteroids(i1, j1)) return len(d.getVisibleAsteroids(i1, j1))
} }
func (d *Day10) removeAsteroids(locs ...u.Vec2[int]) { func (d *Day10) removeAsteroids(locs map[u.Vec2[int]]bool) {
for _, loc := range locs { for loc := range locs {
if !d.asteroids[loc.X][loc.Y] { if !d.asteroids[loc.X][loc.Y] {
panic("tried to remove non-asteroid") panic("tried to remove non-asteroid")
} }
@ -156,14 +156,15 @@ func (d *Day10) Part2() string {
if vaporized+len(visibleAsteroids) < findNumVaporized { if vaporized+len(visibleAsteroids) < findNumVaporized {
vaporized += len(visibleAsteroids) vaporized += len(visibleAsteroids)
d.removeAsteroids(visibleAsteroids...) d.removeAsteroids(visibleAsteroids)
continue continue
} }
sort.Slice(visibleAsteroids, func(i, j int) bool { vecs := u.MapKeys(visibleAsteroids)
return d.idealLocation.AngleBetween(visibleAsteroids[i]) > d.idealLocation.AngleBetween(visibleAsteroids[j]) sort.Slice(vecs, func(i, j int) bool {
return d.idealLocation.AngleBetween(vecs[i]) > d.idealLocation.AngleBetween(vecs[j])
}) })
targetLocation = visibleAsteroids[findNumVaporized-1-vaporized] targetLocation = vecs[findNumVaporized-1-vaporized]
break break
} }

View File

@ -2,6 +2,7 @@ package days
import ( import (
"fmt" "fmt"
"strings"
u "parnic.com/aoc2019/utilities" u "parnic.com/aoc2019/utilities"
) )
@ -45,36 +46,36 @@ func (d Day13) getNumBlocks() int {
return blockTiles return blockTiles
} }
// func (d Day13) drawGameBoard() { func (d Day13) Draw() {
// s := strings.Builder{} s := strings.Builder{}
// for x := range d.gameBoard { for x := range d.gameBoard {
// for y := range d.gameBoard[x] { for y := range d.gameBoard[x] {
// block := d.gameBoard[x][y] block := d.gameBoard[x][y]
// if block == tileBlock { if block == tileBlock {
// s.WriteString(u.ColorBlue) s.WriteString(u.ColorBlue)
// s.WriteRune('█') s.WriteRune('█')
// s.WriteString(u.TextReset) s.WriteString(u.TextReset)
// } else if block == tileBall { } else if block == tileBall {
// s.WriteString(u.ColorGreen) s.WriteString(u.ColorGreen)
// s.WriteRune('█') s.WriteRune('█')
// s.WriteString(u.TextReset) s.WriteString(u.TextReset)
// } else if block == tileWall { } else if block == tileWall {
// s.WriteString(u.ColorWhite) s.WriteString(u.ColorWhite)
// s.WriteRune('█') s.WriteRune('█')
// s.WriteString(u.TextReset) s.WriteString(u.TextReset)
// } else if block == tileHPaddle { } else if block == tileHPaddle {
// s.WriteString(u.ColorRed) s.WriteString(u.ColorRed)
// s.WriteRune('█') s.WriteRune('█')
// s.WriteString(u.TextReset) s.WriteString(u.TextReset)
// } else if block == tileEmpty { } else if block == tileEmpty {
// s.WriteRune(' ') s.WriteRune(' ')
// } }
// } }
// s.WriteRune('\n') s.WriteRune('\n')
// } }
// fmt.Print(s.String()) fmt.Print(s.String())
// } }
func (d *Day13) Part1() string { func (d *Day13) Part1() string {
outputStep := 0 outputStep := 0
@ -97,7 +98,7 @@ func (d *Day13) Part1() string {
} }
}) })
return fmt.Sprintf("# block tiles: %s%d%s (%d total tiles)", u.TextBold, d.getNumBlocks(), u.TextReset, len(d.tiles)) return fmt.Sprintf("%d total tiles, # block tiles: %s%d%s", len(d.tiles), u.TextBold, d.getNumBlocks(), u.TextReset)
} }
func (d *Day13) Part2() string { func (d *Day13) Part2() string {
@ -134,7 +135,7 @@ func (d *Day13) Part2() string {
ball = newTilePos ball = newTilePos
} else if val == tileHPaddle { } else if val == tileHPaddle {
paddle = newTilePos paddle = newTilePos
// d.drawGameBoard() // d.Draw()
// time.Sleep(time.Millisecond * 33) // time.Sleep(time.Millisecond * 33)
} }
} }

View File

@ -314,7 +314,7 @@ func (d *Day15) Part1() string {
visited.timesVisited = 1 visited.timesVisited = 1
} }
d.pos = point{} d.pos = point{}
d.Draw() // d.Draw()
return fmt.Sprintf("Moves required to reach target: %s%d%s", u.TextBold, d.visited[d.goalPos].distanceFromStart, u.TextReset) return fmt.Sprintf("Moves required to reach target: %s%d%s", u.TextBold, d.visited[d.goalPos].distanceFromStart, u.TextReset)
} }

View File

@ -48,6 +48,7 @@ type Day17 struct {
func (d *Day17) Parse() { func (d *Day17) Parse() {
d.program = u.LoadIntcodeProgram("17p") d.program = u.LoadIntcodeProgram("17p")
// d.program.SetDebugASCIIPrint(true)
} }
func (d Day17) Num() int { func (d Day17) Num() int {
@ -388,9 +389,8 @@ func (d *Day17) Part2() string {
row := 0 row := 0
var outputState int var outputState int
var lastOutput int64 var lastOutput int64
var instructionStr string
d.program.RunIn(func(inputStep int) int64 { d.program.RunIn(func(inputStep int) int64 {
return int64(instructionStr[inputStep-1]) panic("unexpected read")
}, func(val int64, state u.IntcodeProgramState) { }, func(val int64, state u.IntcodeProgramState) {
rVal := rune(val) rVal := rune(val)
if outputState == 0 { if outputState == 0 {
@ -401,7 +401,7 @@ func (d *Day17) Part2() string {
if rVal == '\n' && lastOutput == '\n' { if rVal == '\n' && lastOutput == '\n' {
if outputState == 0 { if outputState == 0 {
instructionStr = beforeGrid.solvePath(beforeBotLocation, beforeBotFacing) d.program.FeedInputString(beforeGrid.solvePath(beforeBotLocation, beforeBotFacing))
} }
outputState++ outputState++
row = 0 row = 0

View File

@ -6,7 +6,6 @@ import (
"math" "math"
"strings" "strings"
"github.com/edwingeng/deque/v2"
u "parnic.com/aoc2019/utilities" u "parnic.com/aoc2019/utilities"
) )
@ -120,14 +119,15 @@ func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, g
return retAdjacent return retAdjacent
} }
queue := deque.NewDeque[u.Pair[int, day18Vec]]() queue := make([]u.Pair[int, day18Vec], 0)
visited := make(map[day18Vec]bool) visited := make(map[day18Vec]bool)
for _, adjacent := range getAdjacent(inPos) { for _, adjacent := range getAdjacent(inPos) {
queue.PushBack(u.Pair[int, day18Vec]{First: 1, Second: adjacent}) queue = append(queue, u.Pair[int, day18Vec]{First: 1, Second: adjacent})
} }
for !queue.IsEmpty() { for len(queue) > 0 {
next := queue.PopFront() next := queue[0]
queue = queue[1:]
if _, exists := visited[next.Second]; !exists { if _, exists := visited[next.Second]; !exists {
visited[next.Second] = true visited[next.Second] = true
@ -157,7 +157,7 @@ func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, g
for _, neighbor := range getAdjacent(next.Second) { for _, neighbor := range getAdjacent(next.Second) {
if _, exists := visited[neighbor]; !exists { if _, exists := visited[neighbor]; !exists {
queue.PushBack(u.Pair[int, day18Vec]{First: next.First + 1, Second: neighbor}) queue = append(queue, u.Pair[int, day18Vec]{First: next.First + 1, Second: neighbor})
} }
} }
} }

View File

@ -77,6 +77,7 @@ func (d *Day19) Part2() string {
} }
// find lower bound // find lower bound
// this may not be necessary, but helps seed the bisect with a known-good lower bound
startY := 0 startY := 0
startX := 0 startX := 0
for y := 1; startY == 0; y++ { for y := 1; startY == 0; y++ {
@ -91,9 +92,12 @@ func (d *Day19) Part2() string {
lastGoodX := 0 lastGoodX := 0
threshold := 1 threshold := 1
// add 100 to start y since we know it has to be a 100x100 square.
// since we multiply x by 10,000 for the final result, that tells us y cannot be 10k+
y := u.Bisect(startY+100, 9999, threshold, func(y int) bool { y := u.Bisect(startY+100, 9999, threshold, func(y int) bool {
foundX := false foundX := false
for x := startX; ; x++ { for x := startX; ; x++ {
// check top left
if !f(x, y) { if !f(x, y) {
if !foundX { if !foundX {
continue continue
@ -105,18 +109,38 @@ func (d *Day19) Part2() string {
foundX = true foundX = true
} }
// check top right
if !f(x+99, y) { if !f(x+99, y) {
return true return true
} }
// check bottom left
if !f(x, y+99) { if !f(x, y+99) {
continue continue
} }
// we believe the corners work, so run final validations on the full borders.
// this may not be necessary, but i've seen some rows end up shorter than a
// previous row because of the angle of the beam and our integer fidelity.
// plus it's really not that much more expensive to do to be certain we're correct.
for y2 := y; y2 < y+100; y2++ {
// right wall
if !f(x+99, y2) {
return true
}
// left wall
if !f(x, y2) {
return true
}
}
lastGoodX = x lastGoodX = x
return false return false
} }
}) })
// since we invert our bisect success returns, we need to increment y to
// tip back over into the "success" range
y += threshold
result := (lastGoodX * 10000) + y 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) return fmt.Sprintf("Closest 100x100 square for the ship starts at %d,%d = %s%d%s", lastGoodX, y, u.TextBold, result, u.TextReset)
} }

398
days/20.go Normal file
View File

@ -0,0 +1,398 @@
package days
import (
"container/heap"
"fmt"
"math"
"strings"
u "parnic.com/aoc2019/utilities"
)
type day20Cell int8
type day20Graph map[day20Portal][]u.Pair[day20Portal, int]
const (
day20CellWall day20Cell = iota
day20CellPath
day20CellDonutHole
)
var (
day20AdjacentOffsets = []u.Vec2i{
{X: -1, Y: 0},
{X: 1, Y: 0},
{X: 0, Y: -1},
{X: 0, Y: 1},
}
entrancePortal = day20Portal{name: "AA"}
exitPortal = day20Portal{name: "ZZ"}
)
type day20Portal struct {
name string
inner bool
depth int
}
type Day20 struct {
grid [][]day20Cell
entrance u.Vec2i
exit u.Vec2i
portals map[day20Portal]u.Vec2i
portalNames []string
}
func (d *Day20) Parse() {
d.portals = make(map[day20Portal]u.Vec2i)
d.portalNames = make([]string, 0)
lines := u.GetStringLines("20p")
d.grid = make([][]day20Cell, len(lines)-4)
currPortal := strings.Builder{}
for row, line := range lines {
y := row - 2
isGridRow := row >= 2 && row < len(lines)-2
if isGridRow {
d.grid[y] = make([]day20Cell, len(line)-4)
}
for col, ch := range lines[row] {
x := col - 2
isGridCol := col >= 2 && col < len(line)-2
if isGridRow && isGridCol {
if ch == '#' {
d.grid[y][x] = day20CellWall
} else if ch == '.' {
d.grid[y][x] = day20CellPath
} else {
d.grid[y][x] = day20CellDonutHole
}
}
if ch >= 'A' && ch <= 'Z' {
portalX := 0
portalY := 0
if len(line) > col+1 && line[col+1] >= 'A' && line[col+1] <= 'Z' {
currPortal.WriteRune(ch)
currPortal.WriteRune(rune(line[col+1]))
if len(line) > col+2 && line[col+2] == '.' {
portalY = y
portalX = x + 2
} else if col-1 >= 0 && line[col-1] == '.' {
portalY = y
portalX = x - 1
} else {
panic("!")
}
} else if len(lines) > row+1 && lines[row+1][col] >= 'A' && lines[row+1][col] <= 'Z' {
currPortal.WriteRune(ch)
currPortal.WriteRune(rune(lines[row+1][col]))
if len(lines) > row+2 && lines[row+2][col] == '.' {
portalY = y + 2
portalX = x
} else if row-1 >= 0 && lines[row-1][col] == '.' {
portalY = y - 1
portalX = x
} else {
panic("!")
}
}
if currPortal.Len() == 2 {
portalName := currPortal.String()
portalVec := u.Vec2i{X: portalX, Y: portalY}
if portalName == entrancePortal.name {
d.entrance = portalVec
} else if portalName == exitPortal.name {
d.exit = portalVec
} else {
portal := day20Portal{
name: portalName,
inner: !d.isOuterPortal(portalVec),
}
d.portals[portal] = portalVec
u.AddToArray(&d.portalNames, portalName)
}
currPortal.Reset()
}
}
}
}
}
func (d Day20) Num() int {
return 20
}
func (d Day20) isPortal(vec u.Vec2i) (bool, int) {
if d.grid[vec.Y][vec.X] != day20CellPath {
return false, 0
}
for i, name := range d.portalNames {
p, exists := d.portals[day20Portal{name: name, inner: !d.isOuterPortal(vec)}]
if exists && vec == p {
return true, i
}
}
return false, 0
}
func (d Day20) Draw() {
for y := range d.grid {
for x := range d.grid[y] {
switch d.grid[y][x] {
case day20CellWall:
fmt.Print("█")
case day20CellDonutHole:
fmt.Print(" ")
case day20CellPath:
posVec := u.Vec2i{X: x, Y: y}
if posVec == d.entrance {
fmt.Print("@")
} else if posVec == d.exit {
fmt.Print("!")
} else {
isPortal, portalIdx := d.isPortal(posVec)
if isPortal {
ch := 'a' + portalIdx
if ch > 'z' {
ch = 'A' + (portalIdx - 26)
}
fmt.Printf("%c", ch)
} else {
fmt.Print(" ")
}
}
}
}
fmt.Println()
}
}
func (d Day20) isOuterPortal(pos u.Vec2i) bool {
return pos.X == 0 || pos.Y == 0 || pos.X == len(d.grid[0])-1 || pos.Y == len(d.grid)-1
}
func (d Day20) findAdjacentCells(inPos u.Vec2i) []u.Pair[day20Portal, int] {
found := make([]u.Pair[day20Portal, int], 0)
getAdjacent := func(pos u.Vec2i) []u.Vec2i {
retAdjacent := make([]u.Vec2i, 0, len(day20AdjacentOffsets))
for _, off := range day20AdjacentOffsets {
offVec := u.Vec2i{X: pos.X + off.X, Y: pos.Y + off.Y}
if offVec.Y < 0 || offVec.Y >= len(d.grid) || offVec.X < 0 || offVec.X >= len(d.grid[0]) {
continue
}
if d.grid[offVec.Y][offVec.X] != day20CellPath {
continue
}
retAdjacent = append(retAdjacent, offVec)
}
return retAdjacent
}
queue := make([]u.Pair[int, u.Vec2i], 0)
visited := map[u.Vec2i]bool{
inPos: true,
}
for _, adjacent := range getAdjacent(inPos) {
queue = append(queue, u.Pair[int, u.Vec2i]{First: 1, Second: adjacent})
}
for len(queue) > 0 {
next := queue[0]
queue = queue[1:]
if _, exists := visited[next.Second]; !exists {
visited[next.Second] = true
adjacentIsPortal, portalIdx := d.isPortal(next.Second)
if adjacentIsPortal || next.Second == d.entrance || next.Second == d.exit {
var portalName string
if next.Second == d.entrance {
portalName = entrancePortal.name
} else if next.Second == d.exit {
portalName = exitPortal.name
} else {
portalName = d.portalNames[portalIdx]
}
alreadyFound := false
for _, p := range found {
if p.First.name == portalName {
alreadyFound = true
break
}
}
if !alreadyFound {
found = append(found, u.Pair[day20Portal, int]{First: day20Portal{
name: portalName,
inner: !d.isOuterPortal(next.Second),
}, Second: next.First})
continue
}
}
for _, neighbor := range getAdjacent(next.Second) {
if _, exists := visited[neighbor]; !exists {
queue = append(queue, u.Pair[int, u.Vec2i]{First: next.First + 1, Second: neighbor})
}
}
}
}
return found
}
type day20PriorityQueue struct {
distance int
neighbor day20Portal
}
type day20PriorityQueueHeap []day20PriorityQueue
func (h day20PriorityQueueHeap) Len() int { return len(h) }
func (h day20PriorityQueueHeap) Less(i, j int) bool { return h[i].distance < h[j].distance }
func (h day20PriorityQueueHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *day20PriorityQueueHeap) Push(x any) {
*h = append(*h, x.(day20PriorityQueue))
}
func (h *day20PriorityQueueHeap) Pop() any {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func (d Day20) dijkstra(graph day20Graph, start, end day20Portal, neighborFunc func(inPortal day20Portal) []u.Pair[day20Portal, int]) int {
distance := make(map[day20Portal]int)
ih := day20PriorityQueueHeap{
day20PriorityQueue{
distance: 0,
neighbor: start,
},
}
heap.Init(&ih)
visited := make(map[day20Portal]bool)
for ih.Len() > 0 {
node := heap.Pop(&ih).(day20PriorityQueue)
if node.neighbor == end {
return node.distance
}
if _, exists := visited[node.neighbor]; exists {
continue
}
visited[node.neighbor] = true
for _, p := range neighborFunc(node.neighbor) {
if _, exists := visited[p.First]; exists {
continue
}
newDistance := node.distance + p.Second
if dist, exists := distance[p.First]; !exists || newDistance < dist {
distance[p.First] = newDistance
heap.Push(&ih, day20PriorityQueue{
distance: newDistance,
neighbor: p.First,
})
}
}
}
return math.MaxInt
}
func (d Day20) buildGraph() day20Graph {
graph := make(day20Graph, len(d.portals)+1)
adjacent := d.findAdjacentCells(d.entrance)
graph[entrancePortal] = adjacent
for portal, portalVec := range d.portals {
adjacent = d.findAdjacentCells(portalVec)
graph[portal] = adjacent
}
return graph
}
func (d Day20) getDepthNeighbors(graph day20Graph, portal day20Portal) []u.Pair[day20Portal, int] {
basePortal := portal
basePortal.depth = 0
baseNeighbors := graph[basePortal]
neighbors := make([]u.Pair[day20Portal, int], 0)
if portal.inner {
n := day20Portal{name: portal.name, inner: false, depth: portal.depth + 1}
neighbors = append(neighbors, u.Pair[day20Portal, int]{First: n, Second: 1})
}
if portal.depth == 0 {
for _, i := range baseNeighbors {
if i.First.inner || i.First.name == entrancePortal.name || i.First.name == exitPortal.name {
neighbors = append(neighbors, u.Pair[day20Portal, int]{First: i.First, Second: i.Second})
}
}
} else {
if !portal.inner {
n := day20Portal{name: portal.name, inner: true, depth: portal.depth - 1}
neighbors = append(neighbors, u.Pair[day20Portal, int]{First: n, Second: 1})
}
for _, i := range baseNeighbors {
if i.First.name != entrancePortal.name && i.First.name != exitPortal.name {
n := day20Portal{name: i.First.name, inner: i.First.inner, depth: portal.depth}
neighbors = append(neighbors, u.Pair[day20Portal, int]{First: n, Second: i.Second})
}
}
}
return neighbors
}
func (d *Day20) Part1() string {
// d.Draw()
graph := d.buildGraph()
for portal, adjacent := range graph {
if portal.name == entrancePortal.name {
continue
}
graph[portal] = append(adjacent, u.Pair[day20Portal, int]{First: day20Portal{name: portal.name, inner: !portal.inner}, Second: 1})
}
distance := d.dijkstra(graph, entrancePortal, exitPortal, func(inPortal day20Portal) []u.Pair[day20Portal, int] { return graph[inPortal] })
return fmt.Sprintf("Steps to traverse maze: %s%d%s", u.TextBold, distance, u.TextReset)
}
func (d *Day20) Part2() string {
graph := d.buildGraph()
distance := d.dijkstra(graph, entrancePortal, exitPortal, func(inPortal day20Portal) []u.Pair[day20Portal, int] { return d.getDepthNeighbors(graph, inPortal) })
return fmt.Sprintf("Steps to traverse recursive maze: %s%d%s", u.TextBold, distance, u.TextReset)
}

86
days/21.go Normal file
View File

@ -0,0 +1,86 @@
package days
import (
"fmt"
"strings"
u "parnic.com/aoc2019/utilities"
)
type Day21 struct {
program u.IntcodeProgram
}
func (d *Day21) Parse() {
d.program = u.LoadIntcodeProgram("21p")
// d.program.SetDebugASCIIPrint(true)
}
func (d Day21) Num() int {
return 21
}
func (d *Day21) Part1() string {
// if there's any hole up to 3 ahead of us but there's ground where we'd land if we jumped
// (a jump takes 4 spaces), go ahead and jump
cmds := []string{
// check if a hole at 1 or 2 ahead
"NOT A T",
"NOT B J",
// store that result in J
"OR T J",
// check if a hole at 3 ahead
"NOT C T",
// store hole in 1, 2, or 3 in T
"OR J T",
// set J true if hole in 1, 2, or 3
"OR T J",
// set J true if also no hole at 4 ahead
"AND D J",
"WALK",
}
instructionStr := strings.Join(cmds, "\n") + "\n"
d.program.FeedInputString(instructionStr)
res := d.program.Run()
return fmt.Sprintf("Hull damage value when walking: %s%d%s", u.TextBold, res, u.TextReset)
}
func (d *Day21) Part2() string {
d.program.Reset()
// @
// #####.#.##.##.###
// ABCDEFGHI
// using the first program, this kills us. if we jump, we land at D and H becomes our new D, so it won't jump again.
// but if we waited to jump until we got one more ahead, we'd be ok.
// so now we want to know essentially the same thing as part 1, but also if our multiple (immediate second jump) would be successful.
// in problem terms, that's: if there's a hole at 1 or 2 ahead, and there's a hole at C with ground at H, and there's ground at D.
// so now for the above example we'd wait to jump until here:
// @
// #####.#.##.##.###
// ABCDEFGHI
// and all will be well.
cmds := []string{
// check if a hole at 1 or 2 ahead
"NOT A J",
"NOT B T",
// store that result in J
"OR T J",
// check if a hole at 3 ahead...
"NOT C T",
// and ground at 8 ahead (so we can immediately jump again if needed)...
"AND H T",
// combine those into J
"OR T J",
// and ensure we also still have a place to land if we jumped right away
"AND D J",
"RUN",
}
instructionStr := strings.Join(cmds, "\n") + "\n"
d.program.FeedInputString(instructionStr)
res := d.program.Run()
return fmt.Sprintf("Hull damage value when running: %s%d%s", u.TextBold, res, u.TextReset)
}

155
days/22.go Normal file
View File

@ -0,0 +1,155 @@
package days
import (
"fmt"
"math"
"math/big"
"strconv"
"strings"
u "parnic.com/aoc2019/utilities"
)
type day22Instruction int
const (
day22InstructionNewStack day22Instruction = iota
day22InstructionCut
day22InstructionDealIncrement
)
type day22Shuffle struct {
instruction day22Instruction
arg int
}
type Day22 struct {
shuffles []day22Shuffle
}
func (d *Day22) Parse() {
lines := u.GetStringLines("22p")
d.shuffles = make([]day22Shuffle, len(lines))
for idx, line := range lines {
split := strings.Split(line, " ")
if split[0] == "deal" {
if split[1] == "into" {
d.shuffles[idx] = day22Shuffle{instruction: day22InstructionNewStack}
} else if split[1] == "with" {
arg, err := strconv.Atoi(split[3])
if err != nil {
panic(err)
}
d.shuffles[idx] = day22Shuffle{
instruction: day22InstructionDealIncrement,
arg: arg,
}
}
} else if split[0] == "cut" {
arg, err := strconv.Atoi(split[1])
if err != nil {
panic(err)
}
d.shuffles[idx] = day22Shuffle{
instruction: day22InstructionCut,
arg: arg,
}
}
}
}
func (d Day22) Num() int {
return 22
}
func (d Day22) applyShuffle(s day22Shuffle, stack, scratch []int) {
switch s.instruction {
case day22InstructionNewStack:
for i := 0; i < len(stack)/2; i++ {
stack[i], stack[len(stack)-1-i] = stack[len(stack)-1-i], stack[i]
}
// there's probably a way to do these two in place...
case day22InstructionCut:
absArg := int(math.Abs(float64(s.arg)))
for i, v := range stack {
if s.arg > 0 {
if i < absArg {
scratch[len(scratch)-absArg+i] = v
} else {
scratch[i-absArg] = v
}
} else {
if i < absArg {
scratch[i] = stack[len(stack)-absArg+i]
} else {
scratch[i] = stack[i-absArg]
}
}
}
copy(stack, scratch)
case day22InstructionDealIncrement:
for i, v := range stack {
scratch[(i*s.arg)%len(stack)] = v
}
copy(stack, scratch)
}
}
func (d *Day22) Part1() string {
deckSize := 10007
// deckSize := 10
stack := make([]int, deckSize)
for i := range stack {
stack[i] = i
}
scratch := make([]int, len(stack))
for _, s := range d.shuffles {
d.applyShuffle(s, stack, scratch)
}
pos := -1
for i, v := range stack {
if v == 2019 {
pos = i
break
}
}
return fmt.Sprintf("Card 2019 is at position %s%d%s", u.TextBold, pos, u.TextReset)
}
func (d *Day22) Part2() string {
n, iter := big.NewInt(119315717514047), big.NewInt(101741582076661)
offset, increment := big.NewInt(0), big.NewInt(1)
for _, s := range d.shuffles {
switch s.instruction {
case day22InstructionNewStack:
increment.Mul(increment, big.NewInt(-1))
offset.Add(offset, increment)
case day22InstructionCut:
offset.Add(offset, big.NewInt(0).Mul(big.NewInt(int64(s.arg)), increment))
case day22InstructionDealIncrement:
increment.Mul(increment, big.NewInt(0).Exp(big.NewInt(int64(s.arg)), big.NewInt(0).Sub(n, big.NewInt(2)), n))
}
}
finalIncr := big.NewInt(0).Exp(increment, iter, n)
finalOffs := big.NewInt(0).Exp(increment, iter, n)
finalOffs.Sub(big.NewInt(1), finalOffs)
invmod := big.NewInt(0).Exp(big.NewInt(0).Sub(big.NewInt(1), increment), big.NewInt(0).Sub(n, big.NewInt(2)), n)
finalOffs.Mul(finalOffs, invmod)
finalOffs.Mul(finalOffs, offset)
answer := big.NewInt(0).Mul(big.NewInt(2020), finalIncr)
answer.Add(answer, finalOffs)
answer.Mod(answer, n)
return fmt.Sprintf("Card at position 2020: %s%d%s", u.TextBold, answer, u.TextReset)
}

170
days/23.go Normal file
View File

@ -0,0 +1,170 @@
package days
import (
"fmt"
"sync"
u "parnic.com/aoc2019/utilities"
)
type day23Computer struct {
program u.IntcodeProgram
id int
packetQueue chan u.Vec2[int64]
outputStep int
nextPacketDest int
sendingPacket u.Vec2[int64]
hasQueuedPacket bool
lastReceivedPacket u.Vec2[int64]
idle bool
}
func (d Day23) makeComputer(id int) *day23Computer {
c := &day23Computer{
program: d.program.Copy(),
id: id,
packetQueue: make(chan u.Vec2[int64]),
idle: true,
}
return c
}
type Day23 struct {
program u.IntcodeProgram
}
func (d *Day23) Parse() {
d.program = u.LoadIntcodeProgram("23p")
}
func (d Day23) Num() int {
return 23
}
func (d Day23) initComputers() []*day23Computer {
computers := make([]*day23Computer, 50)
for i := range computers {
computers[i] = d.makeComputer(i)
}
return computers
}
func (d Day23) execComputers(computers []*day23Computer, nat chan u.Vec2[int64]) *sync.WaitGroup {
wg := &sync.WaitGroup{}
wg.Add(len(computers))
for _, c := range computers {
go func(c *day23Computer) {
bootedUp := false
c.program.RunIn(func(inputStep int) int64 {
if !bootedUp {
bootedUp = true
return int64(c.id)
}
if c.hasQueuedPacket {
// fmt.Printf(" %d finished processing packet %v\n", c.id, c.lastReceivedPacket)
c.hasQueuedPacket = false
return c.lastReceivedPacket.Y
}
select {
case c.lastReceivedPacket = <-c.packetQueue:
// fmt.Printf("computer %d received packet %v\n", c.id, packet)
c.hasQueuedPacket = true
return c.lastReceivedPacket.X
default:
c.idle = true
return -1
}
}, func(val int64, state u.IntcodeProgramState) {
c.idle = false
switch c.outputStep {
case 0:
c.nextPacketDest = int(val)
case 1:
c.sendingPacket.X = val
case 2:
c.sendingPacket.Y = val
if c.nextPacketDest == 255 {
// fmt.Printf("computer %d sending %v to 255\n", c.id, c.sendingPacket)
nat <- c.sendingPacket
} else {
// fmt.Printf("computer %d sending %v to computer %d\n", c.id, c.sendingPacket, c.nextPacketDest)
computers[c.nextPacketDest].packetQueue <- c.sendingPacket
}
}
c.outputStep = (c.outputStep + 1) % 3
})
wg.Done()
}(c)
}
return wg
}
func (d *Day23) Part1() string {
computers := d.initComputers()
natChan := make(chan u.Vec2[int64])
wg := d.execComputers(computers, natChan)
answer := <-natChan
for _, c := range computers {
c.program.Stop()
}
// not really necessary, but let's make sure they all shut down in case
// we're running all days at once
wg.Wait()
return fmt.Sprintf("First packet sent to 255 Y value: %s%d%s", u.TextBold, answer.Y, u.TextReset)
}
func (d *Day23) Part2() string {
computers := d.initComputers()
natChan := make(chan u.Vec2[int64])
wg := d.execComputers(computers, natChan)
answerChan := make(chan int64)
go func() {
var currVal u.Vec2[int64]
var lastVal u.Vec2[int64]
hasReceived := false
for {
select {
case currVal = <-natChan:
hasReceived = true
default:
}
allIdle := true
for _, c := range computers {
if !c.idle {
allIdle = false
break
}
}
if allIdle && hasReceived {
// fmt.Printf("all idle, sending %v to computer 0\n", currVal)
if lastVal.Y == currVal.Y {
// fmt.Printf("found answer? %d\n", currVal.Y)
answerChan <- currVal.Y
}
computers[0].packetQueue <- currVal
lastVal = currVal
}
}
}()
answer := <-answerChan
for _, c := range computers {
c.program.Stop()
}
wg.Wait()
return fmt.Sprintf("First Y value sent to the NAT twice in a row: %s%d%s", u.TextBold, answer, u.TextReset)
}

289
days/24.go Normal file
View File

@ -0,0 +1,289 @@
package days
import (
"fmt"
"math"
u "parnic.com/aoc2019/utilities"
)
var (
day24AdjacentOffsets = []u.Vec2i{
{X: -1, Y: 0},
{X: 1, Y: 0},
{X: 0, Y: -1},
{X: 0, Y: 1},
}
)
type Day24 struct {
grid [][]bool
}
func (d *Day24) Parse() {
lines := u.GetStringLines("24p")
d.grid = make([][]bool, len(lines))
for i, line := range lines {
d.grid[i] = make([]bool, len(line))
for j, ch := range line {
d.grid[i][j] = ch == '#'
}
}
}
func (d Day24) Num() int {
return 24
}
func (d Day24) calcActivatedNeighbors(grid [][]bool, i, j int) int {
activatedNeighbors := 0
for _, o := range day24AdjacentOffsets {
newI := i + o.X
newJ := j + o.Y
if newI < 0 || newI >= len(grid) || newJ < 0 || newJ >= len(grid[i]) {
continue
}
if grid[newI][newJ] {
activatedNeighbors++
}
}
return activatedNeighbors
}
func (d Day24) recursiveCalcActivatedNeighbors(gridMap map[int][][]bool, mapIdx, i, j int) int {
activatedNeighbors := 0
numNeighbors := 0
thisGrid := gridMap[mapIdx]
for _, o := range day24AdjacentOffsets {
newI := i + o.X
newJ := j + o.Y
if newI < 0 || newI >= len(thisGrid) || newJ < 0 || newJ >= len(thisGrid[i]) {
continue
}
if newI == 2 && newJ == 2 {
continue
}
numNeighbors++
if thisGrid[newI][newJ] {
activatedNeighbors++
}
}
checkLower := (i == 1 && j == 2) ||
(i == 2 && (j == 1 || j == 3)) ||
(i == 3 && j == 2)
if checkLower {
if lowerGrid, exists := gridMap[mapIdx+1]; exists {
if i == 1 {
for _, b := range lowerGrid[0] {
numNeighbors++
if b {
activatedNeighbors++
}
}
} else if i == 2 {
if j == 1 {
for _, r := range lowerGrid {
numNeighbors++
if r[0] {
activatedNeighbors++
}
}
} else if j == 3 {
for _, r := range lowerGrid {
numNeighbors++
if r[len(lowerGrid[0])-1] {
activatedNeighbors++
}
}
}
} else if i == 3 {
for _, b := range lowerGrid[len(lowerGrid)-1] {
numNeighbors++
if b {
activatedNeighbors++
}
}
}
}
}
checkUpper := (i == 0) || (i == len(thisGrid)-1) ||
((i != 0 && i != len(thisGrid)) && (j == 0 || j == len(thisGrid[0])-1))
if checkUpper {
if upperGrid, exists := gridMap[mapIdx-1]; exists {
if i == 0 {
numNeighbors++
if upperGrid[1][2] {
activatedNeighbors++
}
} else if i == len(thisGrid)-1 {
numNeighbors++
if upperGrid[3][2] {
activatedNeighbors++
}
}
if j == 0 {
numNeighbors++
if upperGrid[2][1] {
activatedNeighbors++
}
} else if j == len(thisGrid[0])-1 {
numNeighbors++
if upperGrid[2][3] {
activatedNeighbors++
}
}
}
}
return activatedNeighbors
}
func (d Day24) calcRating(grid [][]bool) int {
rating := 0
for i, r := range grid {
for j := range r {
pow := (i * len(r)) + j
if grid[i][j] {
result := int(math.Pow(2, float64(pow)))
rating += result
}
}
}
return rating
}
func (d Day24) getNumBugs(gridMap map[int][][]bool) int {
ret := 0
for _, v := range gridMap {
for _, r := range v {
for _, b := range r {
if b {
ret++
}
}
}
}
return ret
}
func copy2d[T comparable](dest [][]T, src [][]T) {
for i, r := range src {
copy(dest[i], r)
}
}
func (d Day24) Draw(grid [][]bool) {
for _, r := range grid {
for _, c := range r {
if c {
fmt.Print("#")
} else {
fmt.Print(".")
}
}
fmt.Println()
}
fmt.Println()
}
func (d *Day24) Part1() string {
grid := make([][]bool, len(d.grid))
scratch := make([][]bool, len(grid))
for i, g := range d.grid {
grid[i] = make([]bool, len(g))
scratch[i] = make([]bool, len(g))
copy(grid[i], d.grid[i])
}
found := false
answer := 0
seenRatings := make([]int, 0)
for i := 1; !found; i++ {
// d.Draw(grid)
for i, r := range grid {
for j := range r {
numActivated := d.calcActivatedNeighbors(grid, i, j)
if grid[i][j] {
scratch[i][j] = numActivated == 1
} else {
scratch[i][j] = numActivated == 1 || numActivated == 2
}
}
}
rating := d.calcRating(scratch)
if u.ArrayContains(seenRatings, rating) {
found = true
// d.Draw(scratch)
answer = rating
}
seenRatings = append(seenRatings, rating)
copy2d(grid, scratch)
}
return fmt.Sprintf("First repeated biodiversity rating is %s%d%s", u.TextBold, answer, u.TextReset)
}
func (d *Day24) Part2() string {
makeGrid := func(initialGrid [][]bool) ([][]bool, [][]bool) {
grid := make([][]bool, len(d.grid))
scratch := make([][]bool, len(grid))
for i, g := range d.grid {
grid[i] = make([]bool, len(g))
scratch[i] = make([]bool, len(g))
if initialGrid != nil {
copy(grid[i], initialGrid[i])
}
}
return grid, scratch
}
gridMap := make(map[int][][]bool)
scratchMap := make(map[int][][]bool)
gridMap[0], scratchMap[0] = makeGrid(d.grid)
min := 0
max := 0
for i := 0; i < 200; i++ {
gridMap[min-1], scratchMap[min-1] = makeGrid(nil)
gridMap[max+1], scratchMap[max+1] = makeGrid(nil)
min, max = min-1, max+1
// if i == 10 {
// keys := u.MapKeys(gridMap)
// sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
// for _, k := range keys {
// fmt.Println("Depth", k)
// d.Draw(gridMap[k])
// }
// fmt.Println("# bugs:", d.numBugs(gridMap))
// }
for depth, grid := range gridMap {
for i, r := range grid {
for j := range r {
if i == 2 && j == 2 {
continue
}
numActivated := d.recursiveCalcActivatedNeighbors(gridMap, depth, i, j)
if grid[i][j] {
scratchMap[depth][i][j] = numActivated == 1
} else {
scratchMap[depth][i][j] = numActivated == 1 || numActivated == 2
}
}
}
}
for d := range gridMap {
copy2d(gridMap[d], scratchMap[d])
}
}
return fmt.Sprintf("Bugs present after 200 minutes: %s%d%s", u.TextBold, d.getNumBugs(gridMap), u.TextReset)
}

61
days/25.go Normal file
View File

@ -0,0 +1,61 @@
package days
import (
"bufio"
"fmt"
"os"
"strings"
u "parnic.com/aoc2019/utilities"
)
type Day25 struct {
program u.IntcodeProgram
}
func (d *Day25) Parse() {
d.program = u.LoadIntcodeProgram("25p")
}
func (d Day25) Num() int {
return 25
}
func (d *Day25) Part1() string {
lastCmdStrings := make([]string, 0)
sb := strings.Builder{}
inReader := bufio.NewReader(os.Stdin)
d.program.SetDebugASCIIPrint(true)
d.program.RunIn(func(inputStep int) int64 {
lastCmdStrings = lastCmdStrings[:0]
text, _ := inReader.ReadString('\n')
d.program.FeedInputString(text[1:])
return int64(text[0])
}, func(val int64, state u.IntcodeProgramState) {
if val == '\n' {
str := sb.String()
if len(str) > 0 {
lastCmdStrings = append(lastCmdStrings, sb.String())
sb.Reset()
}
} else {
sb.WriteRune(rune(val))
}
})
lastString := lastCmdStrings[len(lastCmdStrings)-1]
var answer string
if idx := strings.Index(lastString, " by typing "); idx >= 0 {
startIdx := idx + len(" by typing ")
endIdx := startIdx + strings.Index(lastString[startIdx:], " ")
answer = lastString[startIdx:endIdx]
}
return fmt.Sprintf("Door passcode: %s%s%s", u.TextBold, answer, u.TextReset)
}
func (d *Day25) Part2() string {
return "There is no part 2"
}

2
go.mod
View File

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

2
go.sum
View File

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

View File

@ -1 +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 1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,0,3,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,252,1,1023,1102,36,1,1008,1102,24,1,1017,1101,25,0,1013,1102,479,1,1026,1101,0,259,1022,1102,1,38,1001,1102,1,713,1024,1101,0,708,1025,1102,1,22,1006,1101,0,32,1010,1101,476,0,1027,1102,1,516,1029,1102,1,34,1009,1101,0,23,1016,1102,1,37,1011,1102,525,1,1028,1101,0,35,1004,1102,31,1,1002,1102,39,1,1019,1102,28,1,1015,1102,1,1,1021,1101,0,30,1007,1101,0,27,1014,1101,21,0,1018,1101,0,29,1005,1102,26,1,1000,1102,1,0,1020,1101,0,20,1012,1101,33,0,1003,109,13,21108,40,40,6,1005,1019,199,4,187,1106,0,203,1001,64,1,64,1002,64,2,64,109,15,1205,-7,221,4,209,1001,64,1,64,1105,1,221,1002,64,2,64,109,-25,1208,-3,26,63,1005,63,243,4,227,1001,64,1,64,1106,0,243,1002,64,2,64,109,25,2105,1,-5,1001,64,1,64,1106,0,261,4,249,1002,64,2,64,109,-4,21108,41,42,-8,1005,1016,281,1001,64,1,64,1106,0,283,4,267,1002,64,2,64,109,-6,1206,2,301,4,289,1001,64,1,64,1105,1,301,1002,64,2,64,109,-4,21102,42,1,2,1008,1016,42,63,1005,63,323,4,307,1106,0,327,1001,64,1,64,1002,64,2,64,109,-7,2108,35,1,63,1005,63,343,1105,1,349,4,333,1001,64,1,64,1002,64,2,64,109,-13,1208,7,35,63,1005,63,369,1001,64,1,64,1106,0,371,4,355,1002,64,2,64,109,24,21102,43,1,-1,1008,1017,42,63,1005,63,391,1105,1,397,4,377,1001,64,1,64,1002,64,2,64,109,-13,2101,0,-4,63,1008,63,38,63,1005,63,419,4,403,1105,1,423,1001,64,1,64,1002,64,2,64,109,21,1206,-5,435,1106,0,441,4,429,1001,64,1,64,1002,64,2,64,109,-22,21101,44,0,10,1008,1014,44,63,1005,63,463,4,447,1105,1,467,1001,64,1,64,1002,64,2,64,109,25,2106,0,-2,1106,0,485,4,473,1001,64,1,64,1002,64,2,64,109,-19,2107,37,-2,63,1005,63,501,1106,0,507,4,491,1001,64,1,64,1002,64,2,64,109,8,2106,0,10,4,513,1001,64,1,64,1105,1,525,1002,64,2,64,109,-6,21107,45,46,0,1005,1012,547,4,531,1001,64,1,64,1105,1,547,1002,64,2,64,109,-5,1202,-1,1,63,1008,63,21,63,1005,63,567,1105,1,573,4,553,1001,64,1,64,1002,64,2,64,109,2,1207,-3,21,63,1005,63,589,1105,1,595,4,579,1001,64,1,64,1002,64,2,64,109,1,1201,-8,0,63,1008,63,34,63,1005,63,619,1001,64,1,64,1106,0,621,4,601,1002,64,2,64,109,-6,2102,1,-1,63,1008,63,33,63,1005,63,643,4,627,1105,1,647,1001,64,1,64,1002,64,2,64,109,10,21101,46,0,3,1008,1017,43,63,1005,63,667,1106,0,673,4,653,1001,64,1,64,1002,64,2,64,109,-13,2102,1,8,63,1008,63,35,63,1005,63,697,1001,64,1,64,1106,0,699,4,679,1002,64,2,64,109,23,2105,1,0,4,705,1105,1,717,1001,64,1,64,1002,64,2,64,109,-1,1205,-3,729,1106,0,735,4,723,1001,64,1,64,1002,64,2,64,109,-15,2101,0,0,63,1008,63,38,63,1005,63,755,1106,0,761,4,741,1001,64,1,64,1002,64,2,64,109,-2,2107,28,-1,63,1005,63,779,4,767,1106,0,783,1001,64,1,64,1002,64,2,64,109,-2,2108,35,0,63,1005,63,801,4,789,1105,1,805,1001,64,1,64,1002,64,2,64,109,1,1201,-5,0,63,1008,63,26,63,1005,63,831,4,811,1001,64,1,64,1105,1,831,1002,64,2,64,109,-5,1207,5,30,63,1005,63,849,4,837,1106,0,853,1001,64,1,64,1002,64,2,64,109,2,1202,-2,1,63,1008,63,26,63,1005,63,879,4,859,1001,64,1,64,1105,1,879,1002,64,2,64,109,15,21107,47,46,0,1005,1017,899,1001,64,1,64,1105,1,901,4,885,4,64,99,21102,1,27,1,21101,915,0,0,1106,0,922,21201,1,66416,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,942,1,0,1105,1,922,21202,1,1,-1,21201,-2,-3,1,21102,1,957,0,1105,1,922,22201,1,-1,-2,1105,1,968,22102,1,-2,-2,109,-3,2105,1,0

View File

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

File diff suppressed because one or more lines are too long

View File

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

113
inputs/20p.txt Normal file
View File

@ -0,0 +1,113 @@
G R U E Z S S
W N C K Z G D
#################################.###########.###.#######.#########.#.#######.#####################################
#.#.......#.#.........#.#.#.....#.#.....#.#.#...#.....#.........#.....#...#.#...........#.......#.....#.....#.#...#
#.#####.###.###.###.#.#.#.###.###.#.###.#.#.###.#.###.###.#####.#.###.###.#.###.#########.###.###.#######.###.#.###
#.#.#.#...#...#.#.#.#.................#.#...#...#.#.#...#.#.....#.#.#.#.....................#.#.......#.........#.#
#.#.#.#.###.#####.#########.###########.#.#.#.###.#.#######.###.#.#.#####.###.#.###.#.###.#.###.#######.#######.#.#
#...#...#.#.#.#.........#...#...#.......#.#...#.......#.....#.#.#.....#.....#.#...#.#.#...#.#...#.#.........#.#.#.#
#.#.###.#.#.#.#####.#.#.#######.#.#######.###.#.###.#####.###.#####.#.###.#########.#.#.#######.#.#####.#####.#.#.#
#.#...#.#.#.#.#.....#.#.................#.#...#.#.#.....#...#.#.#...#...#.#.....#.#.#.#...#.#.#...........#.#.....#
###.###.#.#.#.#.#.#####.#.###.#########.#.#######.#.#####.###.#.#.#.#####.#.#.###.#.#######.#.#.###.#######.###.#.#
#...#.......#...#.#.#.#.#...#.#.........#.....#.......#.#.......#.#...#.....#.....#.#...#.........#...#.#.#.#.#.#.#
###.#######.#######.#.###########.#.#####.###.#####.###.#.#####.#.###.#.#.###.#.#######.###.#####.#####.#.#.#.#####
#.......#.#...#.......#.........#.#.#...#.#.#.#.....#.....#...#.#...#.#.#.#...#...#...#...#...#.#.#...........#.#.#
#.#.###.#.#.#########.#########.#.###.###.#.#####.#.###.#.#.#.#.#.###########.#.###.###.###.###.###.###.#.#####.#.#
#.#...#.....#.#...#.#.#.#.#.............#.....#...#...#.#.#.#...#...#.#.#.....#.........#...#.......#...#...#...#.#
###########.#.#.###.#.#.#.#######.#####.#.#####.#.#######.#####.###.#.#.#####.#.#.###.#####.###.#############.#.#.#
#.#.#.#.#.....#.#.....#.#.#.#.....#...#.#.....#.#.....#...#.#.#...#...#.#.#...#.#...#...#...#...#...#.#.......#.#.#
#.#.#.#.###.###.#####.#.#.#.#####.#.###.#.#########.#######.#.#.###.###.#.#.###.###.#####.#####.#.###.#####.#####.#
#.#...#.#...#.#.#...#...#.#.....#.#.....#.#...#.....#.......#.#.#...#.....#.#.#.#.......#.......#...#.#.#...#.#...#
#.#.#.#.###.#.#.###.#.###.#.#####.#.###.#.###.###.###.#.#.###.#.#.###.#.#.###.#.###.#####.#.#####.###.#.###.#.###.#
#.#.#.#.#...#.#...#...#.#.#...#...#...#.#.#.....#.#...#.#.#.....#.#...#.#.#.......#.......#...#.#.....#.........#.#
#.#.###.###.#.#.#####.#.#.#.###.#.#####.#.#.###.#.###.#.###.#.###.#.###.#####.#####.#####.#####.###.#########.#.#.#
#.....#...#.......#.#.#.......#.#.#...#.#...#.#.#.....#...#.#...#...#.#...#.......#...#.#...#...#...#.#.#.#.#.#.#.#
###.###.#######.###.#.#.#.#.#########.#.#####.#.#.#####.#######.#####.#.#####.#.#.###.#.#######.###.#.#.#.#.#.###.#
#.....#...#.#.#.......#.#.#.#.#.......#...#.....#.#.....#.......#.......#.#...#.#.#.#...#.....#.#.....#.#.#...#.#.#
###.###.###.#.#######.#.#####.#####.#.#.###.#########.#.###.#.#####.#####.###.#####.#######.###.###.###.#.#.###.#.#
#.....#.......#...#...#...#.#.#.#.#.#.#.#.......#.#...#.#.#.#...#.......#...#.......#...#.....#...#.#.........#...#
#.#####.#######.###.#.#.###.#.#.#.#.#.#.###.#.###.#.#####.#####.#####.#####.###.#######.###.###.###.#########.#.###
#...#...#...#.#.#...#.#.#.#.........#.....#.#.....#.......#.......#...#.............#...#...#.#.........#...#.#...#
#.###.###.###.#.#####.#.#.#.#####.#############.#######.#####.#####.#######.###########.###.#.###.#######.###.#.###
#...#...#...#...#.#...#...#.# S L B V G R #.#.#.#.....#.#.......#.#...#
#.###.#.#.#####.#.###.#.###.# D M E H X Q #.#.#.#####.#.###.#####.#.###
#.....#.#.................#.# #.........................#.#
#.###.###.#####.#.#.#.###.### #.#.#.###.#######.###.#.#.#.#
#.#.#.....#.#...#.#.#.#.....# RZ..#.#...#.....#.#...#.#.#.#..BE
#.#.#####.#.#####.#.#####.### #######.#####.#.#.#######.#.#
#.....#.#.....#.#.#.....#.#.# #.#.#.#.#.#...#.......#...#.#
###.###.###.#.#.#.#.###.#.#.# #.#.#.###.#####.#.#######.#.#
RI....#.....#.#.#...#.#.#.#....UW #.#.......#...#.#.#.#...#...#
#####.###############.###.### #.###.#####.#.#####.#.#.#####
#.....#.#.#...#.#.....#.#...# GW..........#.#.........#.#...#
#.###.#.#.#.###.###.###.##### #.###.#.#.###.#######.###.###
#...#...#.....#.#.......#....FM #.#...#.#.....#...#.........#
###.###.#.#.#.#.#.#########.# ###.#######.###.###########.#
AJ..#...#...#.#...#...#.....#.# #...#.#.#...#...#.#...#.#.#..GX
#.#.#########.#.#.#######.#.# #####.#.#######.#.###.#.#.###
#.....#.#.#.#.#.............# #...............#...#........DD
#######.#.#.###########.###.# ###.#######.###.#.#.#.#.###.#
#.....................#.#...# #.......#.....#...#.#.#.#...#
###.###.#.#.###.#####.####### #####.#########.###.#.###.###
GP..#...#.#.#.#.....#.#...#...# SG..#...#.....#...#.#.....#...#
#.###.#####.#.#.#.#.###.###.# #.#.###.#.#######.#######.###
#.#.....#.#.#.#.#.#.....#...# #.....#.#.#.#...#.#.#...#.#.#
#.#.#####.#########.#######.# #########.#.###.#.#.#.#####.#
#.....#...#..................SI DD....#........................UW
#.#######.#####.###.###.##### #.###.#####.#.###.#.###.#####
#.#.#...#...#.....#.#.#.#.#..AJ #.........#.#.#.#.#.#.....#.#
###.#.###.#.#########.###.#.# #.#####.###.#.#.#######.###.#
#.........#.#...#.....#.#.#.# #...#.....#.#...#.#.#.#.#....LM
#########.#.###.#.###.#.#.#.# #########.#.#.###.#.#.#####.#
AA......#.#.#.....#.#...#.....# #.#.#.#.#.#.#.#...#.#.#.....#
#.#####.#.#.###.#.#.###.#.### #.#.#.#.#########.#.#.#####.#
JO..........#...#...#.....#...# #.#.......#.#.............#.#
#####.###############.###.#.# #.#.#.#####.###.###.#.###.#.#
#.#.#...#.........#.#.#.#.#.# TX....#.............#.#...#...#
#.#.#######.#.#.#.#.###.###.# #.###.#.###.#####.###########
VH......#.....#.#.#.....#...#.# #.#...#.#...#...#.#.....#...#
#####.#.###########.###.##### #############.#####.###.#.#.#
#.....#.....#.#.......#.....# #.#...#...#.#.......#.#.#.#..RZ
#.#.#######.#.#######.#.##### #.###.###.#.###.#.###.#.#.###
#.#...........#.#............FW RN..#.....#...#.#.#.#.....#...#
#########.###.#.#.########### #.#.#.###.###.###.#.#####.###
#...#...#.#.....#.#.........# #...#.............#.......#.#
#.#.#.#################.#.#.# ###############.###########.#
AX..#...#.#...#.#...#...#.#.#..HR JO............#.#.#...........#
#.#.###.###.#.#.#####.###.#.# #.#.#####.#.#.###.#.#.#####.#
TX..#...#.......#...#.....#.#..EK #.#...#.#.#.......#.#...#.#..MM
###.#.###.#.#.#.#.###.#.#.### #.###.#.#######.#.###.###.#.#
#...#.....#.#...#.....#.....# #.#.#.....#.#...#...#.#.....#
#####.#.#.#####.###.#######.# R Y G U A M S #.#.#.#.###.#####.#.#.###.#.#
#.....#.#...#.....#.....#...# I Q P C X M Y #...#.#.#.....#...#.#...#.#.#
#####.#########.###.#####.###########.#####.###.###########.#####.###########.#.#######.#.###.#.#########.#.#####.#
#...#...#.#.......#.....#...#...#.....#.....#.....#.#.....#...#...........#...#...#...#.#.#.....#.#.......#.#.....#
#.#.#.###.###.#.#.#######.#####.###.###.###.#####.#.###.#.#.#########.#####.#####.#.#########.###.###.#####.###.#.#
#.#.......#...#.#.#.......#.....#.#.#.....#.#.....#...#.#.......#.#.#...#.......#.#.......#.#.#.#.....#.......#.#.#
#.#.#.#.#####.###########.#####.#.#.#.#.#.#####.###.###.#########.#.#.#######.###.#.#######.###.###.#.###.#.#.#.#.#
#.#.#.#...#.......#.......#.........#.#.#.....#.#.....#.........#.........#.#.#.....#.......#.......#.#.#.#.#.#.#.#
#######.#.#.#.#####.#.#########.#############.#.#.###.#######.#####.#.#####.#.#.#######.#####.#.#.###.#.#.#######.#
#.......#.#.#...#...#.#.........#.......#...#.#.....#.#.......#.....#...#.....#...........#.#.#.#.#.....#...#.....#
#.#.#.#####.#.#.#####.#.#######.#.###.###.###.#.###.#.#.###.#.#.#.###.#.#####.#.###.#.#.###.###.#.#.#.#####.#####.#
#.#.#.#.....#.#.#.....#...#.#.....#...#.......#.#...#.#...#.#.#.#...#.#.#.....#.#...#.#.......#.#.#.#...#.#.#.....#
#.###.###.#.###.###.#.#.###.#.#.#.#.###.###.#.#######.###.#.###.#######.#.#.#.#####.#.#.#.#############.#.#####.###
#.#.#.#.#.#...#.#...#.#.#.....#.#.#...#...#.#...#.....#...#...#...#.....#.#.#.....#.#.#.#.#.....#.#.#.......#...#.#
###.#.#.#.#.###.#####.#############.#.#.#.#.#######.###.###.###.#############.#####.#####.###.###.#.#.#####.###.#.#
#.......#.#.#.....#...#.#.......#...#.#.#.#.#.#.#.#...#...#.#.....#.#.#...#.....#.#.#...#...#.......#.....#...#...#
###.###.#.###.#########.#######.#####.#.#####.#.#.#.###.#######.###.#.#.#####.###.#.#.###.###.#####.#.#.###.###.###
#.....#.#.#.....#.....#.#.......#.#.#.#...#.......#.#.......#...#.....#.......#.........#.#.#.....#.#.#.#...#.....#
#.#####.#.###.#.#####.#.#######.#.#.#.#.#######.#.#.###.#####.#.###.#.###.#.#####.###.#####.#.#######.###.#.#.#.###
#.#.....#.#...#...#.......#.#.....#...#.#.#.....#...#.......#.#.....#.#...#...#.#...#...#.#.#.....#.#.#.#.#.#.#...#
#####.#.#.###.#######.#.###.###.#.#.###.#.#####.#######.#######.#.#####.###.###.###.#####.#.#.#####.###.#######.#.#
#.#.#.#.#.#.#.#...#...#...#.#.#.#...#.......#.#.....#.#.#...#...#.....#.#.......#.........#.#...#.#.........#.#.#.#
#.#.#######.#####.#####.###.#.#.#########.###.#####.#.#.#.#######.#########.#.###.#########.###.#.#.#########.#.#.#
#...#.#.#.#.....#.#...................#...#...#...#...#.....#...#.#...#...#.#.#.....#.#.......#.#.#.#.#.#.#...#.#.#
###.#.#.#.#.###.#.#####.#############.###.#.#.#.#.###.#.#.#####.#.#.#####.###.#.###.#.#####.###.#.#.#.#.#.###.#.###
#...........#.#.#...#...#.#.......#.#...#...#.#.#...#.#.#...#.#.......#.....#.#...#.#.#.#.#.....#.....#.#.....#...#
#####.#.#.#.#.###.#####.#.#.#.###.#.#.#######.#.###.#.#.###.#.#####.###.###.#.#.###.#.#.#.#.#####.###.#.#.#########
#.#...#.#.#.............#...#...#...#.....#...#...#...#.#.#.#...#...#...#.#...#.#...................#.............#
#.#####.#######.#.###.###.#.#.#####.#.#######.###.#####.#.###.#.#.#####.#.###.#.#.#####.###.#####.###.#.###.#.#####
#.......#.......#...#...#.#.#.#.........#.....#.....#.......#.#.......#.....#.#.#.....#...#.....#...#.#...#.#.....#
###################################.#######.###.#########.#######.#########.#######.###############################
Y S R S F F H
Q Y Q I M W R

19
inputs/20s1.txt Normal file
View File

@ -0,0 +1,19 @@
A
A
#######.#########
#######.........#
#######.#######.#
#######.#######.#
#######.#######.#
##### B ###.#
BC...## C ###.#
##.## ###.#
##...DE F ###.#
##### G ###.#
#########.#####.#
DE..#######...###.#
#.#########.###.#
FG..#########.....#
###########.#####
Z
Z

37
inputs/20s2.txt Normal file
View File

@ -0,0 +1,37 @@
A
A
#################.#############
#.#...#...................#.#.#
#.#.#.###.###.###.#########.#.#
#.#.#.......#...#.....#.#.#...#
#.#########.###.#####.#.#.###.#
#.............#.#.....#.......#
###.###########.###.#####.#.#.#
#.....# A C #.#.#.#
####### S P #####.#
#.#...# #......VT
#.#.#.# #.#####
#...#.# YN....#.#
#.###.# #####.#
DI....#.# #.....#
#####.# #.###.#
ZZ......# QG....#..AS
###.### #######
JO..#.#.# #.....#
#.#.#.# ###.#.#
#...#..DI BU....#..LF
#####.# #.#####
YN......# VT..#....QG
#.###.# #.###.#
#.#...# #.....#
###.### J L J #.#.###
#.....# O F P #.#...#
#.###.#####.#.#####.#####.###.#
#...#.#.#...#.....#.....#.#...#
#.#####.###.###.#.#.#########.#
#...#.#.....#...#.#.#.#.....#.#
#.###.#####.###.###.#.#.#######
#.#.........#...#.............#
#########.###.###.#############
B J C
U P P

37
inputs/20s3.txt Normal file
View File

@ -0,0 +1,37 @@
Z L X W C
Z P Q B K
###########.#.#.#.#######.###############
#...#.......#.#.......#.#.......#.#.#...#
###.#.#.#.#.#.#.#.###.#.#.#######.#.#.###
#.#...#.#.#...#.#.#...#...#...#.#.......#
#.###.#######.###.###.#.###.###.#.#######
#...#.......#.#...#...#.............#...#
#.#########.#######.#.#######.#######.###
#...#.# F R I Z #.#.#.#
#.###.# D E C H #.#.#.#
#.#...# #...#.#
#.###.# #.###.#
#.#....OA WB..#.#..ZH
#.###.# #.#.#.#
CJ......# #.....#
####### #######
#.#....CK #......IC
#.###.# #.###.#
#.....# #...#.#
###.### #.#.#.#
XF....#.# RF..#.#.#
#####.# #######
#......CJ NM..#...#
###.#.# #.###.#
RE....#.# #......RF
###.### X X L #.#.#.#
#.....# F Q P #.#.#.#
###.###########.###.#######.#########.###
#.....#...#.....#.......#...#.....#.#...#
#####.#.###.#######.#######.###.###.#.#.#
#.......#.......#.#.#.#.#...#...#...#.#.#
#####.###.#####.#.#.#.#.###.###.#.###.###
#.......#.....#.#...#...............#...#
#############.#.#.###.###################
A O F N
A A D M

1
inputs/21p.txt Normal file

File diff suppressed because one or more lines are too long

100
inputs/22p.txt Normal file
View File

@ -0,0 +1,100 @@
cut 578
deal with increment 25
cut -3085
deal with increment 16
cut -6620
deal with increment 17
cut -1305
deal with increment 71
cut -4578
deal with increment 44
cut 5639
deal with increment 74
deal into new stack
deal with increment 39
cut 7888
deal with increment 17
deal into new stack
cut 6512
deal with increment 46
cut -8989
deal with increment 46
cut -8518
deal with increment 75
cut -870
deal into new stack
deal with increment 53
cut 7377
deal with increment 60
cut -4733
deal with increment 25
cut -6914
deal with increment 23
cut -4379
deal into new stack
cut 582
deal with increment 35
cut 9853
deal with increment 2
cut -142
deal with increment 74
cut 328
deal into new stack
deal with increment 75
deal into new stack
cut -8439
deal into new stack
deal with increment 34
cut 2121
deal with increment 2
cut 8335
deal with increment 65
cut -1254
deal into new stack
cut -122
deal with increment 75
cut -9227
deal into new stack
deal with increment 24
cut 3976
deal into new stack
deal with increment 8
cut -3292
deal with increment 4
deal into new stack
cut -8851
deal with increment 2
deal into new stack
cut 4333
deal with increment 73
deal into new stack
deal with increment 9
cut -7880
deal with increment 49
cut 9770
deal with increment 30
cut 2701
deal with increment 59
cut 4292
deal with increment 37
deal into new stack
cut -184
deal with increment 25
cut 9907
deal with increment 46
deal into new stack
cut 902
deal with increment 46
cut 2622
deal into new stack
cut 637
deal with increment 58
cut 7354
deal with increment 69
deal into new stack
deal with increment 49
deal into new stack
deal with increment 19
cut -8342
deal with increment 68
deal into new stack

3
inputs/22s1.txt Normal file
View File

@ -0,0 +1,3 @@
deal with increment 7
deal into new stack
deal into new stack

3
inputs/22s2.txt Normal file
View File

@ -0,0 +1,3 @@
cut 6
deal with increment 7
deal into new stack

3
inputs/22s3.txt Normal file
View File

@ -0,0 +1,3 @@
deal with increment 7
deal with increment 9
cut -2

10
inputs/22s4.txt Normal file
View File

@ -0,0 +1,10 @@
deal into new stack
cut -2
deal with increment 7
cut 8
cut -4
deal with increment 7
cut 3
deal with increment 9
deal with increment 3
cut -1

1
inputs/23p.txt Normal file

File diff suppressed because one or more lines are too long

5
inputs/24p.txt Normal file
View File

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

5
inputs/24s1.txt Normal file
View File

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

5
inputs/24s2.txt Normal file
View File

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

1
inputs/25p.txt Normal file

File diff suppressed because one or more lines are too long

View File

@ -53,6 +53,12 @@ var dayMap = []day{
&days.Day17{}, &days.Day17{},
&days.Day18{}, &days.Day18{},
&days.Day19{}, &days.Day19{},
&days.Day20{},
&days.Day21{},
&days.Day22{},
&days.Day23{},
&days.Day24{},
&days.Day25{},
} }
func main() { func main() {

View File

@ -12,10 +12,8 @@ import (
// and the failure is less than or equal to the acceptance threshold // and the failure is less than or equal to the acceptance threshold
// (usually 1, for integers). // (usually 1, for integers).
func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T { func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T {
currVal := low
for T(math.Abs(float64(high-low))) > threshold { for T(math.Abs(float64(high-low))) > threshold {
currVal = low + ((high - low) / 2) currVal := low + ((high - low) / 2)
success := tryFunc(currVal) success := tryFunc(currVal)
if success { if success {
low = currVal low = currVal
@ -24,5 +22,5 @@ func Bisect[T Number](low, high, threshold T, tryFunc func(val T) bool) T {
} }
} }
return currVal return low
} }

View File

@ -28,6 +28,8 @@ type IntcodeProgram struct {
program []int64 program []int64
relativeBase int relativeBase int
haltRequested bool haltRequested bool
printASCII bool
feedInput []rune
} }
type IntcodeProgramState struct { type IntcodeProgramState struct {
@ -140,14 +142,15 @@ func (p *IntcodeProgram) Reset() {
p.relativeBase = 0 p.relativeBase = 0
} }
func (p *IntcodeProgram) Run() { func (p *IntcodeProgram) Run() int64 {
p.RunIn(func(int) int64 { return 0 }, func(int64, IntcodeProgramState) {}) return p.RunIn(func(int) int64 { return 0 }, func(int64, IntcodeProgramState) {})
} }
func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOutputFunc) { func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOutputFunc) int64 {
p.init() p.init()
inputsRequested := 0 inputsRequested := 0
lastOutput := int64(0)
for instructionPointer := 0; instructionPointer < len(p.program) && !p.haltRequested; { for instructionPointer := 0; instructionPointer < len(p.program) && !p.haltRequested; {
instruction := p.GetMemory(instructionPointer) instruction := p.GetMemory(instructionPointer)
instructionPointer++ instructionPointer++
@ -184,13 +187,28 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
case opInput: case opInput:
inputsRequested++ inputsRequested++
param1 := p.GetMemory(instructionPointer) param1 := p.GetMemory(instructionPointer)
p.setMemory(int(param1), inputFunc(inputsRequested), paramModes[0]) var inputVal int64
if len(p.feedInput) > 0 {
inputVal = int64(p.feedInput[0])
p.feedInput = p.feedInput[1:]
} else {
inputVal = inputFunc(inputsRequested)
}
if p.printASCII && inputVal <= 255 {
fmt.Printf("%c", rune(inputVal))
}
p.setMemory(int(param1), inputVal, paramModes[0])
instructionPointer += 1 instructionPointer += 1
case opOutput: case opOutput:
param1 := p.GetMemory(instructionPointer) param1 := p.GetMemory(instructionPointer)
outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer)) param1Val := p.getParamValue(int(param1), paramModes[0])
if p.printASCII && param1Val <= 255 {
fmt.Printf("%c", rune(param1Val))
}
outputFunc(param1Val, p.makeState(instructionPointer))
lastOutput = param1Val
instructionPointer += 1 instructionPointer += 1
@ -256,8 +274,19 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
} }
p.haltRequested = false p.haltRequested = false
return lastOutput
} }
func (p *IntcodeProgram) Stop() { func (p *IntcodeProgram) Stop() {
p.haltRequested = true p.haltRequested = true
} }
func (p *IntcodeProgram) SetDebugASCIIPrint(enable bool) {
p.printASCII = enable
}
func (p *IntcodeProgram) FeedInputString(str string) {
p.feedInput = make([]rune, len(str))
copy(p.feedInput, []rune(str))
}