import pygame
import random
from common import View, Controller


class Tile:
    def __init__(self, x, y, h):
        self.pos = (40 + x * 9 + y * 9, 540 - x * 5 + y * 5)
        self.height = h * 2
        self.parent = self
        self.covered = False
        self.color = (100, random.randint(100, 240), random.randint(100, 240), 160)
        self.done = False

    def find(self):
        if self.parent != self:
            self.parent = self.parent.find()
        return self.parent

    def union(self, other):
        a = self.find()
        b = other.find()
        if a != b:
            a.parent = b

    def render_block(self, surface):
        (x, y) = self.pos
        y0 = y - self.height

        col1 = (160, 160, 160)
        col2 = (255, 255, 255)
        pygame.draw.polygon(surface, col1, [(x, y0), (x+8, y0-4), (x+16, y0), (x+8, y0+4)])
        pygame.draw.polygon(surface, col2, [(x, y0), (x+8, y0-4), (x+16, y0), (x+8, y0+4)], 1)
        col3 = (80, 80, 80)
        col4 = (40, 40, 40)
        pygame.draw.polygon(surface, col4, [(x, y0), (x+8, y0+4), (x+16, y0),
                                            (x+16, y), (x+8, y+4), (x, y)])
        pygame.draw.line(surface, col3, (x, y0), (x, y))
        pygame.draw.line(surface, col3, (x+8, y0+4), (x+8, y+4))
        pygame.draw.line(surface, col3, (x+16, y0), (x+16, y))

    def render_water(self, surface, level):
        (x, y) = self.pos
        if int(level) > self.height:
            (r, g, b, a) = self.find().color
            yw = y - int(level) + random.randint(-1, 1)
            pygame.draw.polygon(surface, (r, g, b, a),
                                [(x, yw), (x+8, yw-4), (x+16, yw), (x+8, yw+4)])
            pygame.draw.polygon(surface, (r, g, b, a + 40),
                                [(x-1, yw), (x+8, yw-5), (x+17, yw), (x+8, yw+5)], 1)
            self.covered = True


class Background:
    def __init__(self, board):
        self.surf = pygame.Surface((1920, 1080))
        self.surf.fill((0, 0, 0, 0))
        for y in range(len(board)):
            for x in range(len(board[y])):
                board[y][x].render_block(self.surf)

    def update(self, view, _):
        if not controller.animate:
            return
        view.win.fill((0, 0, 0, 0))
        view.win.blit(self.surf, (0, 0))


class Water:
    def __init__(self, board):
        self.surf = pygame.Surface((1920, 1080), pygame.SRCALPHA)
        self.level = 0.0
        self.board = board

    def update(self, view, controller):
        if not controller.animate:
            return
        self.surf.fill((0, 0, 0, 0))
        merge = 0
        if self.level < 17:
            self.level += 0.1
        else:
            merge = 10
        for y in range(len(self.board)):
            for x in range(len(self.board[y])):
                tile = self.board[y][x]
                if merge > 0 and tile.covered and not tile.done:
                    if x > 0 and self.board[y][x - 1].covered:
                        tile.union(self.board[y][x - 1])
                    if y > 0 and self.board[y - 1][x].covered:
                        tile.union(self.board[y - 1][x])
                    tile.done = True
                    merge -= 1
                tile.render_water(self.surf, self.level)
        view.win.blit(self.surf, (0, 0))
        if merge > 0:
            controller.animate = False


def init(controller):
    board = []
    with open(controller.workdir() + "/input/day09.txt") as f:
        y = 0
        for l in f.read().splitlines():
            w = len(l) - 1
            line = [Tile(w - x, y, int(l[w - x])) for x in range(len(l))]
            board.append(line)
            y += 1
    controller.add(Background(board))
    controller.add(Water(board))
    return controller


view = View(1920, 1080, 60)
view.setup("Day 09")
controller = Controller()
init(controller).run(view)

import pygame
import random
from common import View, Controller


class Tile:
    def __init__(self, x, y, h):
        self.pos = (40 + x * 9 + y * 9, 540 - x * 5 + y * 5)
        self.height = h * 2
        self.parent = self
        self.covered = False
        self.color = (100, random.randint(100, 240), random.randint(100, 240), 160)
        self.done = False

    def find(self):
        if self.parent != self:
            self.parent = self.parent.find()
        return self.parent

    def union(self, other):
        a = self.find()
        b = other.find()
        if a != b:
            a.parent = b

    def render_block(self, surface):
        (x, y) = self.pos
        y0 = y - self.height

        col1 = (160, 160, 160)
        col2 = (255, 255, 255)
        pygame.draw.polygon(surface, col1, [(x, y0), (x+8, y0-4), (x+16, y0), (x+8, y0+4)])
        pygame.draw.polygon(surface, col2, [(x, y0), (x+8, y0-4), (x+16, y0), (x+8, y0+4)], 1)
        col3 = (80, 80, 80)
        col4 = (40, 40, 40)
        pygame.draw.polygon(surface, col4, [(x, y0), (x+8, y0+4), (x+16, y0),
                                            (x+16, y), (x+8, y+4), (x, y)])
        pygame.draw.line(surface, col3, (x, y0), (x, y))
        pygame.draw.line(surface, col3, (x+8, y0+4), (x+8, y+4))
        pygame.draw.line(surface, col3, (x+16, y0), (x+16, y))

    def render_water(self, surface, level):
        (x, y) = self.pos
        if int(level) > self.height:
            (r, g, b, a) = self.find().color
            yw = y - int(level) + random.randint(-1, 1)
            pygame.draw.polygon(surface, (r, g, b, a),
                                [(x, yw), (x+8, yw-4), (x+16, yw), (x+8, yw+4)])
            pygame.draw.polygon(surface, (r, g, b, a + 40),
                                [(x-1, yw), (x+8, yw-5), (x+17, yw), (x+8, yw+5)], 1)
            self.covered = True


class Background:
    def __init__(self, board):
        self.surf = pygame.Surface((1920, 1080))
        self.surf.fill((0, 0, 0, 0))
        for y in range(len(board)):
            for x in range(len(board[y])):
                board[y][x].render_block(self.surf)

    def update(self, view, _):
        if not controller.animate:
            return
        view.win.fill((0, 0, 0, 0))
        view.win.blit(self.surf, (0, 0))


class Water:
    def __init__(self, board):
        self.surf = pygame.Surface((1920, 1080), pygame.SRCALPHA)
        self.level = 0.0
        self.board = board

    def update(self, view, controller):
        if not controller.animate:
            return
        self.surf.fill((0, 0, 0, 0))
        merge = 0
        if self.level < 17:
            self.level += 0.1
        else:
            merge = 10
        for y in range(len(self.board)):
            for x in range(len(self.board[y])):
                tile = self.board[y][x]
                if merge > 0 and tile.covered and not tile.done:
                    if x > 0 and self.board[y][x - 1].covered:
                        tile.union(self.board[y][x - 1])
                    if y > 0 and self.board[y - 1][x].covered:
                        tile.union(self.board[y - 1][x])
                    tile.done = True
                    merge -= 1
                tile.render_water(self.surf, self.level)
        view.win.blit(self.surf, (0, 0))
        if merge > 0:
            controller.animate = False


def init(controller):
    board = []
    with open(controller.workdir() + "/input/day09.txt") as f:
        y = 0
        for l in f.read().splitlines():
            w = len(l) - 1
            line = [Tile(w - x, y, int(l[w - x])) for x in range(len(l))]
            board.append(line)
            y += 1
    controller.add(Background(board))
    controller.add(Water(board))
    return controller


view = View(1920, 1080, 60)
view.setup("Day 09")
controller = Controller()
init(controller).run(view)
