Day 22

This problem involves a virus that has a particular movement pattern through a grid. The grid is composed of infected and non-infected tiles. The virus's behavior is:

  • If the current cell is infected turn right, otherwise turn left
  • Change the status of the current cell
  • Move forward one unit

We're asked how many times the virus changes a cell to being infected over 10,000 iterations.

In [1]:
from math import ceil
import numpy as np
# K we gotta do some work to get these into nice arrays
grid = open('/Users/Sven/py_files/aoc_2017/d22_input.txt').readlines()
grid = [cell.replace('\n', '') for cell in grid]
grid[:5]
Out[1]:
['#..#...##.#.###.#.#.#...#',
 '.####....#..##.#.##....##',
 '...#..#.#.#......##..#..#',
 '##.####.#.##........#...#',
 '##.#....##..#.####.###...']

The virus starts in the very middle of the grid so here's it's position as though this were a matrix.

In [2]:
start_row = ceil(len(grid)/2) - 1 
start_col = ceil(len(grid[1])/2) - 1 
start_loc = (start_row, start_col)
print(start_loc)
(12, 12)

Okay so because this is an infinite grid, I think we're going to be better off not trying to make a grid. Instead we can just keep track of 1x2 np.arrays and a dictionary. We'll add to this dictionary when needed.

Let's start by getting the dictionary set up. We know this is a 25x25 grid to start with so:

In [3]:
flattened_land = [val for row in grid for val in row]
# I suddenly understand the nested list comprehension.
# for some reason seeing it this way:
# [val (for row in grid: for val in row)]
# makes it a lot clearer to me why it's phrased this way

# I think -1 and 1 will be nicer than # and .
flattened_land = [-1 if x == '#' else 1 for x in flattened_land]
print(flattened_land[:5])
# now we make the associated tuples. 
grid_size = 25
grid_locs = [(row, col) for row in range(grid_size) for col in range(grid_size)]
print(grid_locs[:5])
# ez
location_dict = dict(zip(grid_locs, flattened_land))
[-1, 1, 1, -1, 1]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]
In [4]:
location_dict[(0,0)]
Out[4]:
-1

Okay that seems like an easy way to record this. Now we need to establish the directions. I guess we can use 0-3 as NESW:

In [ ]:
start_dir = 0
def turn(direction, left = True):
    return((direction + (-1) ** left) % 4)
print(turn(start_dir))
print(turn(start_dir, False))
3
1

And a function to move:

In [ ]:
move_dirs = dict(zip([0, 1, 2, 3], [(-1, 0), (0, 1), (1, 0), (0, -1)]))

def move(location, direction):
    new_loc = np.array(location) + np.array(move_dirs[direction])
    return(tuple(new_loc))
move(start_loc, 0)
Out[ ]:
(11, 12)

Okay I think we're good to go here...

In [ ]:
curr_loc = start_loc
curr_dir = start_dir
counter = 0

for i in range(10000):
    
    # Find the value we've got:
    if curr_loc in location_dict.keys():
        curr_cell = location_dict[curr_loc]
    else:
        curr_cell = 1
    
    # if infected, turn left
    if curr_cell == -1:
        curr_dir = turn(curr_dir, left = False)
    else:
        curr_dir = turn(curr_dir, left = True)
        counter += 1
        
    # update the cell:
    location_dict[curr_loc] = -curr_cell
    # move
    curr_loc = move(curr_loc, curr_dir)
    
In [ ]:
counter
Out[ ]:
5369

Part 2:

The second part of this is only a slight modification from the original. We now have 4 states that each cell can be in and the virus cycles through them. It also has different turning instructions based on those four states. I think this shouldn't be too hard because we just have to modify the turns and states.

In [ ]:
# let's copy this setup stuff again and this time use the values 0-3 for the states clean, weakened, infected, flagged
flattened_land = [val for row in grid for val in row]
flattened_land = [2 if x == '#' else 0 for x in flattened_land]

# now we make the associated tuples. 
grid_size = 25
grid_locs = [(row, col) for row in range(grid_size) for col in range(grid_size)]
location_dict = dict(zip(grid_locs, flattened_land))

Now let's make a modification on our turn function:

In [ ]:
def turn2(direction, cell):
    if cell == 0:
        direction -= 1
    elif cell == 2:
        direction += 1
    elif cell == 3:
        direction += 2
    return(direction % 4)

Next loop:

In [ ]:
curr_loc = start_loc
curr_dir = start_dir
counter = 0

for i in range(10000000):
    
    # Find the value we've got:
    if curr_loc in location_dict.keys():
        curr_cell = location_dict[curr_loc]
    else:
        # set this as zero since that's cleaned
        curr_cell = 0
    
    # Turn the appropriate direction
    curr_dir = turn2(curr_dir, curr_cell)
            
    # update the cell:
    curr_cell = (curr_cell + 1) % 4
    location_dict[curr_loc] = curr_cell
    
    # update counter
    if curr_cell == 2:
        counter += 1
    # move
    curr_loc = move(curr_loc, curr_dir)
In [ ]:
counter