#!/usr/bin/env python3

import sys
import re
import numpy as np
from PIL import Image

moves = { 'e': (2, 0), 'se': (1, 2), 'sw': (-1, 2), 'w': (-2, 0), 'nw': (-1, -2), 'ne': (1, -2) }

# Save (x, y): True/False in tiles. True = black, False = white.
tiles = {}

for line in open(sys.argv[1]).read().splitlines():
    pos = np.array((0, 0))
    for d in re.findall(r'e|se|sw|w|nw|ne', line):
        pos += moves[d]
    t = tuple(pos)
    if t in tiles:
        tiles[t] = not tiles[t]
    else:
        tiles[t] = True

# Part 1
print('black:', sum(val == True for val in tiles.values()))

# -- Part 2 --

# take a chance on how wide it needs to be
width = 300
heigth = 300
board = np.zeros(width * heigth, dtype=np.int8)
board = board.reshape(heigth, width)

# Fill in tiles, move to center
for key, value in tiles.items():
    x, y = key
    x += width // 2
    y += heigth // 2
    board[y][x] = value

def black_neighbours(y, x, b):
    num = 0
    for m in moves.values():
        num += b[(y + m[1], x + m[0])]
    return num

def game():
    board_copy = np.copy(board)
    w, h = board.shape
    # Don't do outer edge (to avoid special cases)
    for y in range(2, h - 2):
        for x in range(2, w - 2):
            tile = board_copy[(y, x)]
            n = black_neighbours(y, x, board_copy)
            if tile:
                # black
                if n == 0 or n > 2:
                    board[(y, x)] = False
            else:
                # white
                if n == 2:
                    board[(y, x)] = True

def save_image(day):
    colours = [(0, 0, 0), (255, 255, 255)]
    im = Image.new('RGB', (width, heigth))
    for y in range(heigth):
        for x in range(width):
            c = colours[board[y][x]]
            im.putpixel((x, y), c)
    im.save('img%03d.png' % (day))

save_image(0)
for day in range(1, 101):
    game()
    save_image(day)
    print('Day %d: %d' % (day, len(np.where(board == True)[0])))

ys, xs = np.where(board)
print(min(ys), max(ys), min(xs), max(xs))

[((31, 8, 31, 47), 'numpy.zeros', 'np.zeros', (), '', True, 'import numpy as np\n'), ((79, 9, 79, 24), 'numpy.where', 'np.where', ({(79, 18, 79, 23): 'board'}, {}), '(board)', True, 'import numpy as np\n'), ((14, 10, 14, 26), 'numpy.array', 'np.array', ({(14, 19, 14, 25): '(0, 0)'}, {}), '((0, 0))', True, 'import numpy as np\n'), ((15, 13, 15, 49), 're.findall', 're.findall', ({(15, 24, 15, 42): '"""e|se|sw|w|nw|ne"""', (15, 44, 15, 48): 'line'}, {}), "('e|se|sw|w|nw|ne', line)", False, 'import re\n'), ((48, 17, 48, 31), 'numpy.copy', 'np.copy', ({(48, 25, 48, 30): 'board'}, {}), '(board)', True, 'import numpy as np\n'), ((66, 9, 66, 42), 'PIL.Image.new', 'Image.new', ({(66, 19, 66, 24): '"""RGB"""', (66, 26, 66, 41): '(width, heigth)'}, {}), "('RGB', (width, heigth))", False, 'from PIL import Image\n'), ((77, 35, 77, 58), 'numpy.where', 'np.where', ({(77, 44, 77, 57): '(board == True)'}, {}), '(board == True)', True, 'import numpy as np\n')]