Previous Thread adventofcode.com/ >Advent of Code is a series of small programming puzzles for a variety of skill levels. They are self-contained and are just as appropriate for an expert who wants to stay sharp as they are for a beginner who is just learning to code. Each puzzle calls upon different skills and has two parts that build on a theme. You need to register with an account but you can appear as anonymous.
Private leaderboard join code:
368748-e33dcae3
People who are inactive for 5 days will be kicked, but other leaderboards can be made if desired.
Alternative leaderboard for those waiting for inactives to be kicked from the first:
I don't understand why the stack implemntation works. If you have a regex like eg. "^N(E|W)N$", this should visit rooms [(0,1), (1,1), (-1,1), (1,2), (-1,2)]
but with the stack implementation where you reset the location on a ")", it would only visit [(0,1), (1,0), (-1, 0), (0, 2)]
Am I misinterpreting how the paths are supposed to work or did eric just not give any inputs where the longest shortest path follows a branching group?
Lucas Wright
both of those are wrong there's no (-1,2) and the second one appears to be doing the branch from (0,0) when it should be doing it from (0,1)
Lincoln Taylor
the correct sequence [(0,1), (1,1), (-1,1), (0,2)]
Camden Perez
Oops yeah sorry the part where it goes from (0,0) is my fault
Jackson Parker
last thread got deleted
this ship is running on fumes
Ian Reed
"^N(E|W)N$ contains a branch: after going north, you must choose to go either east or west before finishing your route by going north again." From this it sounds like the possible paths are N -> E -> N N -> W -> N But the stack implementation produces the paths N -> E N -> W N -> N ???
Luke Torres
Last thread was over 100 posts past the bump limit.
Luke Bell
>^N(E|W)N$ this shit got me too, i spent 2 hours trying to solve a way harder problem because of it.
so yea, it seems like there are no branches like this in the actual problems. just consider them deadends that dont continue after.
got pushed off the board because of a spammer
Josiah Hughes
Why does eric not make clear problem descriptions with inputs to match
Logan Brooks
>Incredibly tired >Understand the problem but fail to implement it for hours because as soon as my head wraps around what I'm trying to solve I forget the context, or get hungry and go eat something >Guided by the stupidest test case written above, somehow end up with this solution >Thinking it won't work but check out some test cases >It actually works I don't even know. I feel like I'm doing a ton of pointless shit here
do you need to create some kind of graph for part 1? im just making sure im not overthinking this.
Logan Cook
it would help you to debug it if you could draw it on the screen but no you don't need to
Mason Fisher
can you describe your solution to me?
Ryder Watson
You don't 'have to' do that, you can use dictionaries of points.
Michael Allen
>keep a stack of junctions so you can go backwards and find the last junction when required >read input from left to right >anything in "NSEW" means step in that direction >"(" means we're at a junction, so save this location (add to junction stack) >"|" means we're done with this branch, go back to the last junction >")" means we're done with this entire junction, go back to the last junction and pop it off our stack of junctions
I found it simpler to build the graph using this method, and then traverse it afterwards to find the furthest distance away. It's not the most efficient, but this method also let me solve part 2 with only two extra lines of code.
Benjamin Perry
I also finished the hard-branching problem. If we do not actually have to keep track of positions after branches, why does he explicitly state the opposite? Regardless of which option is taken, the route continues from the position it is left at after taking those steps
just why? if you want to make the problem hard, I have no issues with that, but at least make your testcases reflect that... welp, I whipped out the big guns and wrote a parse grammar. pastebin.com/ZXNsg6Fw
Nathaniel Torres
you still have to describe the connections between those points?
Ian Clark
so you don't need to create a graph but you did it anyway?
Sebastian Rogers
I'm not that user.
Asher Sanchez
No
Gavin Jones
You only need to keep track of a given point's shortest path length, so no you don't need connections
I ended up writing the naive stack solution like many other. In retrospect like you say it shouldn't work but the inputs seem to only have relevant branches be detours back to their starting point.
Classic eric.
Eli Morgan
If there's one thing I've learned about AoC, it's that if it sounds like a hard problem, the data is probably set up to make it an easier problem.
Benjamin Jenkins
If there's one thing I've learned it's that Eric Wastl is terrible at writing descriptions and coming up with good examples for his puzzles
Hunter Murphy
If there's one thing I learned about AoC it's input parsing AABB collision heat maps stacks queued processes recursion linked lists visualization summed area tables 1d cellular automata finite state machines autism bfs pathfinding assembly pattern skip fluid simulation reverse engineering
Colton Davis
hm
Lucas Walker
Does anyone have the "Chad decompiler vs virgin optimising compiler" calendar image from last year?
Adam Miller
Learn networkx instead of shitting around writing code that doesn't work.
Parker Gutierrez
Wait you could actually not do all possible branches? No wonder I didn't understand how people were solving this shit
It's a shame that he did so poorly early on and he's not higher on the private leaderboard. Hope to see him again next year.
Colton Clark
I wasted some time trying to parse the string into a more structured regex type first, in the end I just built an adjacency map from the string directly
>input contains poison tracts to discourage tree length calculations like (WNSE|) fucking cunts
Evan Harris
One of the first paths go 800 steps S and 800 steps W so part 1 should be 1600
Colton Price
> $ time python advent20.py 801 0 python advent20.py 0.23s user 0.01s system 76% cpu 0.324 total
seems right. it's 800 units not steps
Nathan Ross
How would you reach a room 1600 steps away passing only 801 doors?
Levi Flores
in the examples ascii drawings the rooms and doors are both 1 unit, so probably did the same thing as me and set the step size to 2. now there are 400 steps in each direction totalling 800 steps and 1600 units.
Blake Hughes
There are 800 S and 800 W, do you honestly believe that SW would only be 1 away?
Kevin Ortiz
okay that is what I get for not looking at my input. you are right there are 800 S and 800 W so it should be at leaast 1600 steps. I just assumed there were 400 each
Brandon Bell
>make retarded class in python to solve expecting to have to wrestle it into usable state for hours >get amusingly high result and put it in for kicks >"That's the right answer! You are one gold star closer to fixing the time stream." >second part is 5 minutes off with a couple of addendums pastebin.com/NM1Z8fzk what in the bloody fuck? why does this even work? why would it ever even work like this?
Matthew Cook
yeah I fixed my program to handle branches correctly, apparently the puzzle input doesn't require it. Now I run out of memory when I try to run lol
Ayden Perry
801 doors is the maximum distance from the farthest room, 0 rooms reached with at least 1000 doors traversed, 603 with at least 500 doors, 1403 with at least 100 doors
Jayden White
nice. since the official input never required it, I didn't even find the silly mistake I made. time python advent20.py 2400 621500 python advent20.py 3.57s user 0.07s system 99% cpu 3.663 total
Joshua Perry
are you now satisfied with the result? and pls give bigger boy
Robert Cruz
and what about now? 1600 391602 python advent20.py 17.35s user 0.50s system 99% cpu 17.993 total
Does the number of paths > 1000 fit in a 32-bit integer?
Hudson Brooks
what do you say? I say that networkx sucks at memory consumption part2 15420052 part1 9983 python advent20.py 72.42s user 2.63s system 99% cpu 1:15.42 total
Aaron Taylor
I get 9982 for part 1 and same for part 2. Part 1 should be 995*2+999*8 unless I messed up somewhere. What square does your version think is furthest away?
Zachary Jackson
day20 code
`stepfrom` is the part that builds the map. It makes a single pass over the regex, but the "current position" is actually a set of points instead of a single point, representing all the places we could be given the branches traversed so far. So on `N(E|W)S`, after the `N(E|W)` part, `poss` would be `{(1, -1), (-1, -1)}`, since we could have gone either `NE` or `NW`. After `N(E|W)S` it's `{(1, 0), (-1, 0)}` for `NES` or `NWS`. This was inspired by the NFA->DFA conversion where each DFA state corresponds to a set of NFA states that you could be in at this point in the input.
`flood` is just a regular bfs that records the distance of every node, to get the actual answers once the map is built
malformed input one because there's fucking spaces in it second because the first branch has an empty group that cannot be skipped Sometimes, a list of options can have an empty option, like (NEWS|WNSE|). This means that routes at this point could effectively skip the options in parentheses and move on immediately. This regex has one main route which, at three locations, can optionally include additional detours and be valid: (NEWS|), (WNSE|), and (SWEN|). Regardless of which option is taken, the route continues from the position it is left at after taking those steps. third because (N|E)(N|E) isn't a valid regex for the problem
Jace Roberts
9982 is right.it was a one-off error in my BFS. 1990 -1998 -1990 1998
are the coordinates of the furthest points where North is (-1,0) and East is (0,1)
Joseph Peterson
There are no spaces in the input, there is no problem taking the empty path of the first branch and nothing in the description disallow (N|E)(N|E).
Joseph Rodriguez
operator overloading is fun. for (auto c: input) switch(c) { case '^': case '$': break; case '(': stack > current; visited |= current; break; default: { current.go(c); visited |= current; bounds.update(current); } break; }
PASSED: Tested regex, got 3 expected 3 PASSED: Tested regex, got 10 expected 10 PASSED: Tested regex, got 18 expected 18 PASSED: Tested regex, got 23 expected 23 PASSED: Tested regex, got 31 expected 31 PASSED: Tested regex, got 3958 expected 3958 PASSED: Tested regex, got 1600 expected 1600 FAILED: Tested regex, got 9990 expected 9982 FAILED: Tested regex, got 4246 expected 4247 how the fug In order, those are the five test inputs, this user's from last thread , the smallboi , the bigboi , and my own input. I ended up guessing my answer because my first try was off by +3 and my next one was off by -1
Shows me for trying to be clever I guess, time to build up a dict of points and bfs it instead of trying to do some clever recursive shit while parsing the input, which clearly only sort of works on part 1 and doesn't work at all on part 2.
Thomas Clark
Maybe you're not accounting for cases in which the path revisits itself or some other corner case
Gavin Watson
oh fug, didnt notice that. ix.io/1wsk made a visualision.
Your visualisation made me realize how retarded that whole "no need for a map, i'll just give you a regex dude" thing is retarded I hate the elfs
Carter Collins
don't worry in the future they will be raped by goblins.
Josiah Jenkins
I'm pretty sure it's because the approach I attempted doesn't account for shortcuts opening up, and because it's AoC they didn't actually bother requiring that all of the inputs handle that properly (for part 1, anyway):
Before I scrap half of this and rewrite it properly, here's some code from a certified brainlet in case anyone wants to gawk at it: inp = open("d20.txt").read()
def rparse(s): o = [] toparse = [] pct = 0 orify = 0 for c in s: if c in {"N","S","E","W"}: if pct == 0: o.append(c) else: toparse.append(c) elif c == "(": if pct == 0: None else: toparse.append(c) pct += 1 elif c == ")": pct -= 1 if pct == 0: o.append(rparse("".join(toparse))) toparse = [] else: toparse.append(c) else: # | if pct == 0: orify = True o.append(c) else: toparse.append(c) if orify: o2 = [[]] for c in o: if c == "|": o2.append([]) else: o2[-1].append(c) return(o2) else: return(o)
def traverse(inp, depth): depth += 1 longest = 0 stack = [] for move in inp: if isinstance(move,list): longest = max(longest, traverse(move,depth)) else: stack.append(move) if len(stack) > 1 and {stack[-1], stack[-2]} in reverse: stack = stack[0:-2] #print(" "*depth, "longest", length) return(len(stack) + longest)
I'm writing day 10 now. How the fuck am I supposed to deal with a 100.000 pixel-wide image?
Ian Martin
you need to close files after opening them.
Jackson Stewart
Hint 1: You're not.
Adam Fisher
The starting positions are on the range of (-50.000,-50.000) and (50.000,50.000). I shall throw a frame on the middle and hope to get the message?
Jack Turner
the vast majority of the image is empty, just keep track of the pixels that aren't empty
Lucas Ramirez
>I shall throw a frame on the middle and hope Why not just draw it around what's there if it gets smaller?
Jonathan Price
Wait... so all branches are distinct? I thought they could intersect. The description doesn't explicitly forbid regexes such as ^N(EEENWWW|NNES)$.
Sebastian Evans
the description allows more than what was actually required to solve the puzzles
Logan Torres
There are no rogue points fucking around going on random directions?
Christian Sullivan
I finally worked our what the fuck is going on in day19 part2. One and a half days analysis fucking outputs for a one liner in python. Fuck this shit.
Chase Sanchez
Why not test it and see for yourself?
Nathan Cook
if you're using python, why not use a debugger, drop a breakpoint on execution and watch the registers? It took me like 10 minutes to understand what was going on
Brody Lopez
because I don't know how to use a debugger nor breakpoints.
Luis Carter
if you're completely new and don't mind installing some proprietary crap, get vscode and the python extension. It's what I've been using thus far
Gavin Collins
>because I don't know how to use a debugger nor breakpoints excellent learning opportunity for you then if you learn how to do these things you will instantly be a 10x better programmer if you take 1 thing away from advent of code, this should be your priority
Carter Myers
can anybody beat 150MHz interpreting the VM in day 19?
I can do 1 billion instructions in 6.7 seconds, which also comes out to about 147 MHz, on a i5-4210M CPU @ 2.60GHz did you parallelize it?
Angel Cook
I'm not sure how to parallelize something as serial as a VM interpreter..
I wrote a simple python interpreter and got ~12MHz with pypy, then converted it to use direct threading in C. I forgot to shutdown the pypy version in the background when I took that screenshot and it's actually averaging ~164MHz.
I've got a i5-3210M at 2.5GHz
Juan Perez
>direct threading Oh I assumed you were talking about threading as in a parallelized program, what do you mean by threading here?
Tyler Hill
oh, sorry, it's a term that comes from interpreter/vm literature.
threaded code is where your code that implements a given opcode contains the code to jump into the next opcode handler, no dispatch loop necessary.
in gnu C it's realized like this (address of labels is a nonstandard C extension)
typedef struct { void* opcode_handler; int a, b, c; } inst;
inst program[36] = { {&&addi, 5, 16, 5}, // &&xxyy means take the address of the label 'xxyy' {&&seti, 1, 9, 1}, {&&seti, 1, 5, 4}, {&&mulr, 1, 4, 3}, ...
and below that you have some labels like this
addi: registers[c] = registers[a] + b;
// usually this chunk is put in a macro call NEXT or something pc = pc+1; // point to next instruction a = program[pc].a; // load operands b = program[pc].b; c = program[pc].c; goto *program[pc].opcode_handler; // load pointer and jump to address
addr: registers[c] = registers[a] + registers[b]; NEXT(); // decode operands and jump to instruction
...
a dispatch loop has two jumps one of them being a hard-to-predict jump to an opcode handler, and the second being a predictable jump back to the top of the loop
direct threaded code has just one jump per instruction, and since it's split up, it's easier to predict the jump targets if certain instructions tend to come before other instructions
Owen Lewis
ah ok that's basically the same I did, expect I used an index into a address table in the struct instead of putting the void* in there directly. I didn't know that's what it's called. When I read your post I tried putting the void* in the struct, but it actually made my program slower. I also noticed a slowdown when using ints for a,b and c instead of uint8_t, did you try that out?
Samuel Cruz
Solved it the naïve way like everyone else
import qualified Data.Map.Strict as Map import Data.Map.Strict (Map)
main :: IO () main = do input Int solve1 = maximum solve2 :: Map (Int, Int) Int -> Int solve2 = length . Map.filter (>= 1000)
buildPaths :: String -> Map (Int, Int) Int buildPaths input = buildPaths' Map.empty [] (0, 0) 0 input where buildPaths' paths stack loc steps ('(':ms) = buildPaths' paths ((loc, steps):stack) loc steps ms buildPaths' paths stack@((loc, steps):_) _ _ ('|':ms) = buildPaths' paths stack loc steps ms buildPaths' paths ((loc, steps):stack) _ _ (')':ms) = buildPaths' paths stack loc steps ms buildPaths' paths stack (x, y) steps (m:ms) = let loc' = case m of 'N' -> (x , y+1) 'E' -> (x+1, y ) 'S' -> (x , y-1) 'W' -> (x-1, y ) steps' = steps + 1 paths' = Map.insertWithKey (\_ x x' -> min x x') loc' steps' paths in buildPaths' paths' stack loc' steps' ms buildPaths' paths _ _ _ "" = paths
Luis Price
Neither uint8_t's or separating operands and addresses into separate arrays helped. uint8_t's actually made it slower
Prefetching the next instruction after the current one got me up to 170MHz though, and converting modifications to the PC register to branch instructions that know to prefetch the branched-to instruction hits 180MHz occasionally.
Aiden Howard
>finally get it working after 7 hours of bug-fixing and rethinking the approach >it's so slow I pretty clearly did it wrong