Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2018-12-13 - Mine Cart Madness

(original .ipynb)
puzzle_input = open("puzzle_input/day13.txt").readlines()

test_input = [
    "/> <\\  ",
    "       ",
    "  /<+ \\",
    "      v",
    "\\>+/",
]
class Elf(object):
    def __init__(self, x, y, elf_char):
        self.x = x
        self.y = y
        self.dir_x = 0
        self.dir_y = 0
        self.intersections = 0
        self.active = True
        if elf_char == "<":
            self.dir_x = -1
        elif elf_char == ">":
            self.dir_x = 1
        elif elf_char == "^":
            self.dir_y = -1
        elif elf_char == "v":
            self.dir_y = 1
        else:
            raise Exception("INVALID ELF:", elf_char)
    
    def any_collisions(self, elves):
        return len([ elf for elf in elves if elf.x == self.x and elf.y == self.y ]) != 0
    
    def corner(self, turn_type):
        tmp = self.dir_x
        if turn_type == "/":
            self.dir_x = -self.dir_y
            self.dir_y = -tmp
        else:
            self.dir_x = self.dir_y
            self.dir_y = tmp
            
    def intersection(self):
        intersection_mod3 = self.intersections % 3
        if intersection_mod3 == 0:
            self.turn_left()
        elif intersection_mod3 == 1:
            pass
        elif intersection_mod3 == 2:
            self.turn_right()
        self.intersections += 1
    
    def turn_left(self):
        if self.dir_x == 1 and self.dir_y == 0:
            self.dir_x = 0
            self.dir_y = -1
        elif self.dir_x == 0 and self.dir_y == -1:
            self.dir_x = -1
            self.dir_y = 0
        elif self.dir_x == -1 and self.dir_y == 0:
            self.dir_x = 0
            self.dir_y = 1
        elif self.dir_x == 0 and self.dir_y == 1:
            self.dir_x = 1
            self.dir_y = 0
            
    def turn_right(self):
        if self.dir_x == 1 and self.dir_y == 0:
            self.dir_x = 0
            self.dir_y = 1
        elif self.dir_x == 0 and self.dir_y == 1:
            self.dir_x = -1
            self.dir_y = 0
        elif self.dir_x == -1 and self.dir_y == 0:
            self.dir_x = 0
            self.dir_y = -1
        elif self.dir_x == 0 and self.dir_y == -1:
            self.dir_x = 1
            self.dir_y = 0

    def step(self):
        self.x += self.dir_x
        self.y += self.dir_y

def split(str):
    return [ c for c in str ]

def step_part_one(elves, track):
    elves_to_move = list(sorted(elves, key=lambda elf: float(str(elf.y)+"."+str(elf.x))))
    
    for i, elf in enumerate(elves_to_move):
        if not elf.active:
            continue
            
        elf.step()
        if track[elf.y][elf.x] in "/\\":
            elf.corner(track[elf.y][elf.x])
        elif track[elf.y][elf.x] == '+':
            elf.intersection()
        
        for j, other_elf in enumerate(elves_to_move):
            if not other_elf.active:
                continue
                
            if i != j and elf.x == other_elf.x and elf.y == other_elf.y:
                print("part one:", elf.x, elf.y)
                return True
    
    return False

def process_track(raw_track):
    track = [ split(row) for row in raw_track ]
    elves = []
    for i, row in enumerate(track):
        for j, col in enumerate(row):
            if col in "<>^v":
                elves.append(Elf(j, i, col))
                track[i][j] = "."
    return (elves, track)

def part_one(raw_track):
    elves, track = process_track(raw_track)
    done = False
    while not done:
        done = step_part_one(elves, track)

part_one(puzzle_input)

# def dump(elves, track):
#     output = []
#     for row in track:
#         output.append([ c for c in row])
    
#     for elf in elves:
#         if elf.active:
#             output[elf.y][elf.x] = "E"
    
#     for row in output:
#         print("".join(row))
#     print()
part one: 123 18
def step_part_two(elves, track):
    elves_to_move = list(sorted(elves, key=lambda elf: float(str(elf.y)+"."+str(elf.x))))
    
    for i, elf in enumerate(elves_to_move):
        if not elf.active:
            continue
            
        elf.step()
        if track[elf.y][elf.x] in "/\\":
            elf.corner(track[elf.y][elf.x])
        elif track[elf.y][elf.x] == '+':
            elf.intersection()
        
        for j, other_elf in enumerate(elves_to_move):
            if not other_elf.active:
                continue
                
            if i != j and elf.x == other_elf.x and elf.y == other_elf.y:
                elf.active  = False
                other_elf.active = False
                break
    
    remaining_elves = [elf for elf in elves if elf.active]
    
    if len(remaining_elves) == 1:
        print("part two:", remaining_elves[0].x, remaining_elves[0].y)
        return True
    
    return False

def part_two(raw_track):
    elves, track = process_track(raw_track)
    done = False
    while not done:
        done = step_part_two(elves, track)
    
part_two(puzzle_input)
part two: 71 123
# let's animate this :D

from advent import grid_animated_render_frame, grid_animated_combine

palette = {
    ".":  "orange",
    "/":  "orange",
    "\\": "orange",
    "/":  "orange",
    "-":  "orange",
    "|":  "orange",   
    "+":  "brown",
    "e":  "blue",
    " ":  "white",
    "\n": "white"
}

def create_frame(elves, track):
    frame = []
    for row in track:
        new_row = []
        for tile in row:
            new_row.append(tile)
        frame.append(new_row)
    
    for elf in elves:
        frame[elf.y][elf.x] = "e"
        
    return grid_animated_render_frame(frame, palette, cell_size=2)
            
def part_one_rendered(raw_track):
    elves, track = process_track(raw_track)
    done = False
    frames = []
    
    while not done:
        done = step_part_one(elves, track)
        frames.append(create_frame(elves, track))
        if len(frames) == 500: # it goes too long
            break
    
    grid_animated_combine(frames, "day13-p1.gif", duration=33, show=True)
        
part_one_rendered(puzzle_input)
part one: 123 18