{"id":15525470,"url":"https://github.com/robertbasic/aoc2017","last_synced_at":"2025-02-22T19:41:02.574Z","repository":{"id":138843423,"uuid":"112737472","full_name":"robertbasic/aoc2017","owner":"robertbasic","description":"Bruteforcing my way through Advent of Code 2017 edition with golang.","archived":false,"fork":false,"pushed_at":"2017-12-25T10:01:49.000Z","size":254,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-18T10:12:59.334Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertbasic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-01T12:28:07.000Z","updated_at":"2017-12-11T10:21:03.000Z","dependencies_parsed_at":"2023-03-17T23:15:53.875Z","dependency_job_id":null,"html_url":"https://github.com/robertbasic/aoc2017","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertbasic%2Faoc2017","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertbasic%2Faoc2017/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertbasic%2Faoc2017/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertbasic%2Faoc2017/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertbasic","download_url":"https://codeload.github.com/robertbasic/aoc2017/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240229882,"owners_count":19768584,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T10:57:50.898Z","updated_at":"2025-02-22T19:41:02.558Z","avatar_url":"https://github.com/robertbasic.png","language":"Go","readme":"# AoC2017 in go\n\nBruteforcing my way through Advent of Code 2017 edition with golang.\n\nI have no idea what am I doing.\n\n## Notes\n\nI'm stealing this idea of notes from [Luka](https://github.com/lmuzinic).\n\nAfter day 13 I went back to day 1 and started optimizing the code.\n\n## Day 1\n\nMy first try at day 1 was a [bit of a mess](https://github.com/robertbasic/aoc2017/blob/54a103f5910eaa1e4d5558439914011ba5c24d49/day1.go).\n\nOn day 2 I rewrote day 1 to be a bit more nicer to read and added tests for it.\n\nI also moved to reading the day inputs from files, using [`bufio.NewScanner`](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day1.go#L16).\n\nComing from PHP, juggling types around is [hard yo](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day1.go#L47-L50)! That's what I get for not understanding Go's type system!\n\n### Optimizations\n\n - using `ioutil.ReadFile` to read in the entire file instead of openning it, scanning with a scanner, creating an input var, passing that around.\n - moved away from strings and converting to integers to do the sum calculations to bytes. Subtracting 48 from the byte to get the actual integer value.\n - benchmarking reports no difference between using `int`s and `int64`, so I left those as `int64`\n - I also see no difference between looping over the `input` bytes with `range` or with a \"regular\" `for` loop.\n\n```\nBenchmarkDay1-4      \t   10000\t    149501 ns/op\t   22660 B/op\t    4086 allocs/op\nBenchmarkDay1Opt-4   \t  200000\t      9665 ns/op\t    3104 B/op\t      10 allocs/op\n```\n\n## Day 2\n\nI don't understand why do I need to re-read a file [after scanning it once](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day2.go#L13-L24)? Or is there maybe a way to \"reset\" a scanner?\n\nThere's a difference between a [`bufio.Scanner`](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day2.go#L27) and a `scanner.Scanner`. I first type-hinted the `scanner` against `scanner.Scanner`, took me a while to figure out why all of a sudden `scanner.Scan()` wanted to return 3 values instead of a `bool`.\n\nGood thing about this [`bufio.NewScanner`](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day2_test.go#L35) is that I can pass in a different `io.Reader` - a string reader in tests, a file in actual code.\n\n[`strconv.Atoi`](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day2.go#L58) seems like a nicer way to convert strings to integers.\n\n[`strings.Fields`](https://github.com/robertbasic/aoc2017/blob/f6c810294e331c688c5401e0d15e3b4c7004c1d0/day2.go#L75) is nice, splits a string by whitespace.\n\nSo far the smallest thing I constantly trip up on is `=` vs `:=`. I'll learn it, eventually.\n\n## Day 3\n\nPuzzle 1 was tough as balls.\n\nI cheated with puzzle 2 and solved it with [OEIS](https://oeis.org/A141481).\n\n## Day 4\n\nNothing special about puzzle 1 for today. It's just wierd that Go has no \"unique\" function.\n\nFor puzzle 2, I first named the `resort` function as `sort`. That tripped up Go and couldn't\nimport the `sort` package.\n\n## Day 5\n\nWith puzzle 1, I first tried to use a `map` for `instructions`, realised that a map is unordered, so I had to\nswitch to a slice.\n\nThen I kept comparing `pos \u003e l` and wondering why I keep getting an index out of range error.\n\nFor puzzle 2, what caught me off guard is that the slices are \"just pointers\" to the underlying array. See this\n[goplay](https://play.golang.org/p/AIzHYPB07F)\n\n## Day 6\n\nFor puzzle 1, I made a mistake in generating the `k` for the `blocks` map. I used only the value from the slice,\nwhich caused incorrect result in the end, as it breaks when, for example, we have 11 \u0026 1, and 1 \u0026 11.\n\nFor puzzle 2, I just dumped all the `k`s from the `blocks` map into a file, found the line number on which\nthe last line first appears/repeats, substracted it from the last line number, voila.\n\n## Day 7\n\nSolved the first puzzle with an \"array diff\". Built two slices, one with all the programs that are holding\nother programs, and another slice with all the programs that are being held by other programs. The difference\nbetween those two slices is the program that holds all the other programs.\n\nThe second puzzle was harder. I didn't even solve it properly.\n\nKnowing the \"root\" program from the first puzzle, I built a tree of towers from the bottom up. Every tower\nhas the name, the weight, the carried weight and the total weight.\n\nThen the idea was to go through this tree and find the out of balance tower. Ended up just dumping the whole\ntower tree into a file and then visually look for the branches that are out of balance until I spotted the\none that has the wrong weight.\n\nA day later, I want back to the 2nd puzzle and managed to solve it completely by code. The solution is not 100%\ncorrect, as it is a bit too \"eager\", as in it fixes the weight by the required weight difference on **all** of\nthe towers on the unbalanced branch, but in the end it finds the final unbalanced tower and correctly adjusts\nit's weight to give the correct answer, so I can live with that.\n\n## Day 8\n\nI was so happy when I read the first puzzle of the day, immediately had a solution in my head using golang\n`struct`s, had the solution typed out soon, the test case was passing, ran the program over the puzzle input,\ngot the answer, aaaand. It's wrong. WTH?!\n\nWell, I misunderstood the task in the first puzzle. I understood it as to find the largest value ever in the\nregistries, but the actual task is to find the largest value at the end of the instructions.\n\nMy original solution to puzzle 1 was the solution to puzzle 2, so... Yeah. Read and re-read, and re-read some\nmore, folks!\n\n## Day 9\n\nGoing step by step for puzzle 1 was the right way to go:\n\n - cancel out stuff\n - remove garbage\n - count score\n\nMost problem had with cancelling out stuff, because I kept looking back in the input string, instead of in the\noutput string.\n\nTurns out going step by step proved to be really helpful for the 2nd puzzle, because I already had the function\nfor cancelling out stuff, which doesn't count towards the total garbage removed. The rest was counting the number\nof characters between the opening `\u003c` and closing `\u003e`.\n\n## Day 10\n\nUgh, this was a tough one. Again. I guess I'm not good with numbers.\n\nFor the first puzzle, my first and biggest mistake is that I kept trying to build a new list out of the existing\nlist. This ended up in weird bugs as I mixed golang's `append` with index access, as the new list kept growing\nbeyond the size of the original list. Eventually I deleted all the code I wrote on Sunday and left it for Monday.\n\nOn Monday I started from scratch and instead went with replacing items in the existing list. That quickly fell\ninto place once I fixed all the out of index errors:\n\n - instead of `sublist := list[position : position+length]` I had `sublist := list[position : length]` which was\n   good for the test input, but was broken on the real input,\n - instead fo `for i := position; i \u003c length; i++ {` I had `for i := position; i \u003c position+length; i++ {` which\n   made `i` go out of bounds pretty quick. Again, was OK for test input, not so OK for real input.\n\nOK, even though the resulting checksum in puzzle 1 was correct, the resulting list was bad. Pure luck I guess that\nthe first two elements were what they should have been.\n\nNeed to modify a copy of the original list. On Sunday I was making a completely new list, but the real thing is to\nwork on the copy of the original list. I should probably look into the real reason why, but I'm so tired from this\npuzzle, to be honest.\n\nSecond puzzle was bad because I HAD THE WRONG VARIABLE AS AN ARGUMENT TO `ElvenHashChecksum`!!! After converting the\nlengths to their ASCII counterpart, I ended up still having the original `lengths` variable as an argument. FFS.\n\n**NOTE**: On what is technically day 15 I was traveling to Zagreb, so in that time I rewrote day 10 to be more\nmanageable, easier to reuse for day 14.\n\n## Day 11\n\nI tried first to figure out movement on my own... Didn't quite get it. I still have another idea I want to try, but\nuntil then I solved it using information from [this AMAZING article on hex grids](https://www.redblobgames.com/grids/hexagons/).\nh/t [@lmuzinic](https://github.com/lmuzinic) for finding that article.\n\n## Day 12\n\nTook me a little while to figure out how to recurse here... Biggest problem was that I was setting `seen[to]` in `BuildPointers`\noutside/after the main loop, which meant the `seen` was pretty much useless. Once I fixed that, the rest fell into place.\n\n## Day 13\n\nBiggest challenge today was to come up with a solution for `move` that does not include `for` loops\nand handles the delay correctly. I don't even know how to explain the math behind it:\n\n``` golang\nfunc (s *Scanner) move(cp int) {\n\tr := s.rang - 1\n\td := cp - r\n\tdiv := int(math.Trunc(float64(d / r)))\n\tm := int(math.Mod(float64(d), float64(r)))\n\n\tif math.Mod(float64(div), 2) == 0 {\n\t\ts.position = r - m\n\t} else {\n\t\ts.position = m\n\t}\n}\n```\n\nBoils down to how many rounds does a scanner do moving around in it's range for the given `cp` \nnumber of steps. It probably can be improved further, but it gave me the correct answer in under\na minute, so I'm good with that.\n\nOh, and notice that the range for depth 1 is always 2. That helps in speeding up things.\n\n@lmuzinic had a good optimization idea - when looking for the delay, no need to everytime a scanner catches us, it's enough\nto be caught only for the first time!\n\nWith all this in place the execution is pretty fast:\n\n```\ntime ./aoc2017\nSeverity is:  1476\nDelay for:  3937334\n\nreal    0m3.146s\nuser    0m3.139s\nsys     0m0.003s\n```\n\n## Day 14\n\nHoly shit is the description of today's puzzle confusing. I need to go back and rewrite day 10 to make day 14 nicer.\n\n3 days later... (OK, I was traveling on days 15 and 16). After rewriting the day 10 puzzles, the first puzzle for day 14\nwas easy. Apparently day 12 can be used for day 14 puzzle 2, but I ended up writing a new solution for the 2nd puzzle\nanyway.\n\nFor puzzle 2 my first attempt consisted of just simply going one step back and one step up from the current position. That\ndidn't work, gave a number to high. Then I realised I was missing an edge case of one step forward one step up. But then\nthat made me realise I was missing an even larger edge case. I ended up drawing a 8x8 grid of ones and zeros that covers\nall the possible cases (it's not that hard really), and using that grid solving that puzzle became a lot easier:\n\n``` golang\nbinrows := []string{\n\t\"01001011\",\n\t\"11011011\",\n\t\"01010001\",\n\t\"01110011\",\n\t\"00000110\",\n\t\"11100111\",\n\t\"00100000\",\n\t\"01111111\",\n}\n```\n\n## Day 15\n\nPretty much bruteforced myself out of today's puzzles. Also, solved on day 17.\n\n## Day 16\n\nOh, this was fun! The programs rearrange themselves to a..p after 60 iterations, so I only\nhad to do 1billion mod 60 = 40 iterations to find the correct answer for the 2nd puzzle.\n\n## Day 17\n\nPuzzle 1 taught me this weird slice trick, which I think I understand\n\n``` golang\nfunc insertAt(s []int, p int, v int) []int {\n\ts = append(s, 0)\n\tcopy(s[p+1:], s[p:])\n\ts[p] = v\n\treturn s\n}\n```\n\nIt appends a single zero to the end, copies from the position `p` to the end to the position `p+1` to the end, and\nsets the value at the position `p` to be `v`. All this while leaving anything below position `p` intact.\n\nPuzzle 2 boils down to track the number that would be added after the zero number, which is always at zero position.\nTook me a while to figure that out, but here we are!\n\n## Day 18\n\nLagging behind with days 16 and 17, so jumping straight to day 18.\n\nEven though I'd like to say the first puzzle was easy, I won't. First, I don't like to say \"easy\" or \"simple\" about\nany programming task... Second, I made some silly mistakes.\n\nBiggest mistake was with `jgz` is that I tested against the `reg`, and then jumped by it too.\n\nThen I learned that to `break` multiple levels in Go I need to use labels. `break` breaks only one level, but to break\n2 or more levels, we need to set labels and then `break foo` to get to the foo label.\n\nFor puzzle 2 on this day I tried for far too long to find the solution by using goroutines and channels. I failed\nmiserably. Finally I went the good ol' run-the-programs-one-at-a-time way, using an empty queue as a sign to \"interrupt\"\nand jump to the other program for execution. Once both programs have empty queues and the last instruction is for the\ncurrent program is a `rcv` instruction, break out as we're done.\n\n## Day 19\n\nOh man do I miss `isset` in go. Other than that pretty much smooth sailing for day 19 puzzles.\n\n## Day 20\n\nGoroutines! I finally managed to use them!\n\nPuzzle 1 has all the particles flying indefinitely, so given enough time eventually one particle will be the closest one.\nI have all the particles fly concurrently for 10000 iterations and then send them to a buffered channel from which the\nparticles will be read out to find the closest one.\n\nThe one thing I don't understand yet is why I had to make the `pch` channel a buffered one? There has to be a way to send\na particle to the channel once it's done and immediately read it back in a different goroutine and compare there to the\nbase particle... A thing to figure out some other day.\n\nP.S.: With some divide and conquer approach, the minimum number of iterations to find the actual closest one is 285.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertbasic%2Faoc2017","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertbasic%2Faoc2017","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertbasic%2Faoc2017/lists"}