Over the holidays I got busy with a lot of things (like getting ready to get out the Army) that I didn’t spend the time I wanted to to write about the other days that I solved with Advent of Code. So, here is day 3.
— Day 3: Crossed Wires —
The gravity assist was successful, and you’re well on your way to the Venus refuelling station. During the rush back on Earth, the fuel management system wasn’t completely installed, so that’s next on the priority list.
Opening the front panel reveals a jumble of wires. Specifically, two wires are connected to a central port and extend outward on a grid. You trace the path each wire takes as it leaves the central port, one wire per line of text (your puzzle input).
The wires twist and turn, but the two wires occasionally cross paths. To fix the circuit, you need to find the intersection point closest to the central port. Because the wires are on a grid, use the Manhattan distance for this measurement. While the wires do technically cross right at the central port where they both start, this point does not count, nor does a wire count as crossing with itself.
For example, if the first wire’s path is R8,U5,L5,D3, then starting from the central port (o), it goes right 8, up 5, left 5, and finally down 3:
........... ........... ........... ....+----+. ....|....|. ....|....|. ....|....|. .........|. .o-------+. ...........Then, if the second wire’s path is U7,R6,D4,L4, it goes up 7, right 6, down 4, and left 4:
........... .+-----+... .|.....|... .|..+--X-+. .|..|..|.|. .|.-X--+.|. .|..|....|. .|.......|. .o-------+. ...........These wires cross at two locations (marked X), but the lower-left one is closer to the central port: its distance is 3 + 3 = 6.
Here are a few more examples:
- R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83 = distance 159- R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 = distance 135
Part 1
What is the Manhattan distance from the central port to the closest intersection?
So pondered how to tackle this problem for a little while. When I looked at the input that I got, we were talking about some very large numbers (900+). I decided to go ahead and start easy using the example directions that were given for my development work.
I eventually decided that really the only way to do this was going to be through the use of a grid that would track both wires as the wound their way through the course. My basic idea became this:
- Build a grid that was big enough to hold the entire flow of both wires. I decided to do this using a list of lists. During the initial build testing, this was easy (all of the numbers were less than 100) but once it came to do it for real this was a lot more of a challenge then I though (more to follow).
- Start in the center of the grid and just put an arbitrary value in it to mark it as the start (I picked the value 99)
- Load the first instruction and decode it into first what direction we needed to move, and then the number of steps.
- Cycle across the grid each and every strep
- As I cycled through each step, I would check and see if there was a value already in that box, check it. If it had the number for the current wire, then just ignore it. If it had the value of another wire, change the value to 100 (to indicate an intersection). If there was no value, put in the number of the wire.
- Once all of the wires were mapped out, cycle through the entire grid again, and calculate the manhattan distance for each spot where there was a value of 100 and find the lowest.
rows, cols = (11000, 10000) startrow = 3100 startcolumn = 2700 wirecount = 1 def getNewCellValue (currentValue, wirecount): if currentValue == 0: return wirecount elif currentValue == wirecount: return wirecount elif currentValue == 99: return 99 else: return 100 #Intialize Array arr = [[0 for i in range(cols)] for j in range(rows)] #Set Start arr[startrow][startcolumn] = 99 input = open("directions.txt", "r") wire = input.readline() while wire: # print(wire) steps = wire.split(",") #Reset to beginning r = startrow c = startcolumn for step in steps: print(step) direction = step[0] count = int(step[1:]) if direction == "R": for column in range (c+1, c+count+1, 1): arr[r][column] = getNewCellValue(arr[r][column] ,wirecount) c = column print("row: " + str(r) + " col: " + str(c)) elif direction == "L": for column in range (c-1, c-count-1, -1): arr[r][column] = getNewCellValue(arr[r][column] ,wirecount) c = column print("row: " + str(r) + " col: " + str(c)) elif direction == "U": for row in range (r-1, r-count-1, -1): arr[row][c] = getNewCellValue(arr[row][c] ,wirecount) r = row print("row: " + str(r) + " col: " + str(c)) else: for row in range (r+1, r+count+1, 1): arr[row][c] = getNewCellValue(arr[row][c] ,wirecount) r = row print("row: " + str(r) + " col: " + str(c)) wirecount += 1 wire = input.readline() currentDistance = 1000000000 for r in range (0, rows): for c in range (0, cols): if arr[r][c] == 100: distance = abs(startrow - r) + abs(startcolumn - c) if distance < currentDistance: currentDistance = distance print(currentDistance)
Above is the code that I ended up going with. I had a very hard time finding a grid that was big enough to hold everything. I started out with 1000 x 1000 but I ran out of room. I went through several more iterations and eventually went to like 100,000 x 100,000 and then started to deal with memory problems. So if you notice on line 33, the line where it prints the “step”, I had it print out each instruction as it went through so when it ran out of room on the grid, I knew what the current instruction was so that I could increase the grid size appropriately. It took a few trials but I eventually settled on 11,000 x 10,000.
Part 2
What is the fewest combined steps the wires must take to reach an intersection?
OK, so I thought that this would be a relatively easy change. Boy was I wrong. My original plan had been to modify the grid. Now within each cell in my list of lists, I was going to add in two more lists (one for each wire) and within the list, record the wire number and the step count. This was going to be a pretty minor modification.
When I made the change and ran it, I very quickly ran into memory problems. I eventually started to do some research to see just how much memory a list took. I eventually found this post in the python documentation. It says (among a lot of other great information) that an empty list is 72 bytes. So doing some rough beer math, I start off with 1 big list with 10,000 sublists within it to build the initial grid. But then each item within all of those lists has two more lists giving me an equation of 72 bytes * 10,000 * ( 2 * 11,000) = 15,840,000,000 bytes or about 15 GB. That was without adding any data to the lists. Obviously that wasn’t going to work.
So because I couldn’t do this the way I wanted to, I went back to what had already worked with my single grid. I thought for a very long time about how to do this, and eventually came up with a solution that I hate (because it doesn’t scale) but at this point (this was a day later) I was willing to settle for something that worked. Here’s the plan:
- Build the grid as before
- Start in the center and decode as before
- Now if we are on the first wire, instead of recording the wire number, record the step count.
- If it was the second wire and the field was empty, record a super huge number (1,000,000,000) just to make it out of play. If there is a value the already, take that value, and add the step count to give the current distance.
- If the new value is smaller than the current smallest value, record it.
rows, cols = (11000, 10000) startrow = 3100 startcolumn = 2700 wirecount = 1 def getNewCellValue (currentValue, wirecount, stepCount): if wirecount == 1: if currentValue == 0: return stepCount elif currentValue == -1: return -1 else: return currentValue else: if currentValue > 0: return currentValue + stepCount else: return 1000000000 #Intialize Array arr = [[0 for i in range(cols)] for j in range(rows)] #Set Start arr[startrow][startcolumn] = -1 input = open("directions.txt", "r") wire = input.readline() currentDistance = 1000000000 while wire: # print(wire) steps = wire.split(",") #Reset to beginning r = startrow c = startcolumn stepCount = 1 for step in steps: print(step) direction = step[0] count = int(step[1:]) if direction == "R": for column in range (c+1, c+count+1, 1): value = getNewCellValue(arr[r][column] ,wirecount, stepCount) if wirecount == 1: arr[r][column] = value else: if value < currentDistance: currentDistance = value stepCount += 1 c = column print("row: " + str(r) + " col: " + str(c)) elif direction == "L": for column in range (c-1, c-count-1, -1): value = getNewCellValue(arr[r][column] ,wirecount, stepCount) if wirecount == 1: arr[r][column] = value else: if value < currentDistance: currentDistance = value stepCount += 1 c = column print("row: " + str(r) + " col: " + str(c)) elif direction == "U": for row in range (r-1, r-count-1, -1): value = getNewCellValue(arr[row][c] ,wirecount, stepCount) if wirecount == 1: arr[row][c] = value else: if value < currentDistance: currentDistance = value stepCount += 1 r = row print("row: " + str(r) + " col: " + str(c)) else: for row in range (r+1, r+count+1, 1): value = getNewCellValue(arr[row][c] ,wirecount, stepCount) if wirecount == 1: arr[row][c] = value else: if value < currentDistance: currentDistance = value stepCount += 1 r = row print("row: " + str(r) + " col: " + str(c)) wirecount += 1 wire = input.readline() print(currentDistance)