File size: 3,353 Bytes
a4da721
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def load_data(file):
    with open(file) as f:
        data = f.readlines()
    coords = [line.strip("\n").split(",") for line in data]

    # swap i, j because this is easier for my brain at this point
    coords = [(int(j), int(i)) for i,j in coords]
    return coords


def build_grid(M, N, coords):
    grid = []
    for i in range(M):
        row = []
        for j in range(N):
            c = "." if (i,j) not in coords else "#"
            row.append(c)
        grid.append(row)
    return grid


def pprint(grid):
    grid_str = "\n".join(["".join(l) for l in grid])
    print(grid_str)


def pprint2(grid):


    new_grid = copy.deepcopy(grid)

    for i in range(M):
        for j in range(N):
            if isinstance(grid[i][j], tuple):
                new_grid[i][j] = "O"

    grid_str = "\n".join(["".join(l) for l in new_grid])
    print(grid_str)


def get_neighbours(pos, grid):
    directions = [(0,1), (1,0), (-1,0), (0, -1)]

    M = len(grid)
    N = len(grid[0])

    ns = []
    i, j = pos
    for dx, dy in directions:
        ni, nj = (i+dx, j+dy)
        if ni in range(M) and nj in range(N):
            if grid[ni][nj] != "#":
                ns.append((ni, nj))

    return ns


import copy

def bfs(grid):

    parents = copy.deepcopy(grid)

    start = (0, 0)
    q = []
    q.append(start)

    visited = set()

    count = 0
    while len(q) > 0:

        #  # Visualize grid filling up
        #  # So much fun!
        #  if count % 5 == 0:
        #      print()
        #      pprint2(parents)
        #      print()

        pos = q.pop(0)

        if pos in visited:
            continue

        ns = get_neighbours(pos, grid)
        for n in ns:
            if n not in visited:
                q.append(n)
                ni, nj = n
                parents[ni][nj] = (pos)

        visited.add(pos)
        #  print(len(visited))
        count += 1

    return parents


# M, N = 7, 7
# n_bytes = 12
# file = "test.txt"

M, N = 71, 71
n_bytes = 1024
file = "input.txt"

coords = load_data(file)
grid = build_grid(M, N, coords[:n_bytes])

#  Run bfs, collect parents info
parents = bfs(grid)


shortest_grid = copy.deepcopy(grid)
shortest_path = []
next_ = (M-1,N-1)
while next_ != (0, 0):

    shortest_path.append(next_)
    i, j = next_
    shortest_grid[i][j] = "O"
    next_ = parents[i][j]

print(len(shortest_path))

# Visualize shortest path
#  pprint(shortest_grid)

## Part 2

def is_dead_end(coords, n_bytes, M, N):
    grid = build_grid(M, N, coords[:n_bytes])

    #  Run bfs, collect parents info
    parents = bfs(grid)

    return parents[M-1][N-1] == "."


def euler(coords, n_bytes):
    """Returns coord of first cause of dead end, use mid-point to swap out"""

    mid = len(n_bytes) // 2
    left = n_bytes[:mid]
    right = n_bytes[mid:]

    if len(n_bytes) == 1:
        return n_bytes[0] - 1  # Off by one because the last one left is still a dead end

    if is_dead_end(coords, left[-1], M, N):
        return euler(coords, left)
    else:
        return euler(coords, right)


M, N = 71, 71
n_bytes = 1024
file = "input.txt"

coords = load_data(file)
grid = build_grid(M, N, coords[:n_bytes])

n_bytes = list(range(len(coords)))
max_n_bytes = euler(coords, n_bytes)

i, j = coords[max_n_bytes]
print(f"{j},{i}") # Reverse it because we read coordinates in reverse at loading time