import objdump_handler

class Builder(object):
    """For building a sequence of ROP gadgets from subclasses of GadgetType"""

    def __init__(self, pipeline):
        """
        pipeline -- list of GadgetType objects in the same order as the desire rop sequence
        """
        self.pipeline = pipeline
        self.rop_sequence = []
        self.rop_sequence_offsets = []

    def run(self):
        """Processes the pipeline and returns a valid rop sequence or None if not possible

        :raises Exception: if no gadgets are found for one of the gadget types in self.pipeline
        """
        for i, pipe in enumerate(self.pipeline):
            if not pipe.rop_gadgets:
                # there's no sense attempting to build if we don't have all the materials
                # usefully notifies of a failure early (and the cause) especially since, if the last pipe is empty,
                # recursive calls to Builder._build will iterate over every gadget of every pipe only to return None
                raise Exception("No gadgets found for type: %s. Canceling build." % pipe.__class__.__name__)

        result = Builder._build(self.pipeline, set(), [])
        self.rop_sequence = result if result else []
        self.rop_sequence_offsets = [gadget[0].offset for gadget in self.rop_sequence]

    @staticmethod
    def _build(pipeline, fresh_registers, rop_sequence):
        """Returns the first valid sequence of gadgets encountered by recursively calling itself, advancing through the
        pipeline as gadgets are found that are valid for in the context of rop_sequence.
        If a gadget fitting into the sequence cannot be found, the method returns and picks up where it left off for the
        previous gadget type in the pipeline.

        pipeline -- list containing GadgetType classes in the same order that's desired for rop_sequence
                    each recursive call causes pipeline to start with the next GadgetType
        fresh_registers -- list of registers currently available for use
        rop_sequence -- the current list of Gadgets up to but not including pipeline[0] that are compatible
        """
        crnt_gadget_type = pipeline[0]
        for gadget in crnt_gadget_type.rop_gadgets:
            if len(rop_sequence) == 0 or crnt_gadget_type.is_compatible(gadget, rop_sequence[-1], fresh_registers):
                new_sequence = rop_sequence + [gadget]
                if len(pipeline) > 1:
                    next_fresh_registers = fresh_registers.copy()
                    # update by removing any dependent registers since they were used by gadget
                    # and by removing all registers gadget changed with an operator that wasn't 'move', 'lw', or 'addiu'
                    next_fresh_registers -= gadget.dependent_registers | gadget.stale_registers
                    # update by adding all registers that gadget loaded with fresh memory values
                    next_fresh_registers |= gadget.fresh_registers
                    result = Builder._build(pipeline[1:], next_fresh_registers, new_sequence)
                    if result is not None:
                        return result
                else:
                    return new_sequence
        return None

    @staticmethod
    def intersect(gadgets_a, gadgets_b):
        """Returns the list of Gadgets where a Gadget from gadgets_a was contained within a Gadget from gadgets_b or
        vice-versa.

        The function works by iterating over gadgets_a and gadgets_b individually
        making maps of each Gadget's last Instruction's offset to the Gadget itself
        and making sets of those offsets. The sets are then intersected and the offsets are used to look up the Gadgets.
        The larger of the two Gadgets will always contain the smaller one,
        so it is added to the list of common gadgets that will be returned.

        gadgets_a -- list of Gadget objects
        gadgets_b -- list of Gadget objects
        """
        a_map_last_offset_to_gadget = {}
        a_offset_set = set()
        for gadget in gadgets_a:
            offset = gadget[-1].offset
            a_map_last_offset_to_gadget[offset] = gadget
            a_offset_set.add(offset)

        b_map_last_offset_to_gadget = {}
        b_offset_set = set()
        for gadget in gadgets_b:
            offset = gadget[-1].offset
            b_map_last_offset_to_gadget[offset] = gadget
            b_offset_set.add(offset)

        common = a_offset_set.intersection(b_offset_set)

        common_gadgets = []
        for offset in common:
            a_gadget = a_map_last_offset_to_gadget[offset]
            b_gadget = b_map_last_offset_to_gadget[offset]
            common_gadgets.append(a_gadget if len(a_gadget) > len(b_gadget) else b_gadget)

        return common_gadgets

class Gadget(objdump_handler.InstructionSequence):
    """Subclasses InstructionSequence to track properties used by rop.Builder"""

    def __init__(self, gadget, gadget_type):
        """
        gadget -- InstructionSequence object to initialize with
        gadget_type -- the subclass of GadgetType that describes this gadget
        """
        objdump_handler.InstructionSequence.__init__(self, gadget)
        self.type = gadget_type
        self.fresh_registers = set()
        self.dependent_registers = set()
        # registers to be removed from fresh_registers during build
        # since they were changed that is not a memory location
        self.stale_registers = set()

        # use self._get_last_change_to_jump_register giving it len(self) as the default index
        # to return if no controllable jump is found (so that it being returned means no controllable jump was found)
        self.has_controllable_jump = self._get_last_change_to_jump_register(len(self)) != len(self)

        for instruction in self:
            if instruction.operator_type in objdump_handler.Instruction.CHANGE_OP_TYPES:
                if (
                    instruction.operator == 'lw' or
                    (instruction.operator == 'addiu' and instruction.operands[1] == 'sp')
                ):
                    self.fresh_registers.add(instruction.operands[0])
                elif instruction.operator == 'move':
                    # maintain fresh and dependent register sets for 'move' instructions
                    # TODO: can't reliably use 'lw' until we start keeping track of changes to $sp
                    if instruction.operands[1] in self.fresh_registers:
                        self.fresh_registers.remove(instruction.operands[1])
                    else:
                        self.dependent_registers.add(instruction.operands[1])
                else:
                    # operation that changes a register, but to a value that isn't a memory address (ex: li)
                    if instruction.operands[0] in self.fresh_registers:
                        self.fresh_registers.remove(instruction.operands[0])
                    self.stale_registers.add(instruction.operands[0])

    def find_matching_instruction(self, pattern=None):
        """Uses pattern to find the first matching instruction in this gadget.

        pattern -- if is None:
                       self.type.search_pattern will be used.
                   else:
                       string in the 'search pattern' format or search criteria tuple
                       (see: InstructionSequence.extract_search_criteria())
        """
        pattern = pattern if pattern is not None else self.type.search_pattern
        search_criteria = objdump_handler.InstructionSequence.get_search_criteria(pattern)
        for i in xrange(len(self)):
            if self.instruction_matches(i, *search_criteria):
                return self[i]

class GadgetType(object):

    search_pattern = None  # must specify in subclass
    reverse_search_results = False

    def __init__(self, *args, **kwargs):
        self.rop_gadgets = []
        self.search()

    def search(self):
        """Finds all instruction sequences in objdump_handler that match self.search_pattern and contain
        controllable jumps and stores them as Gadget objects in self.rop_gadgets and orders them by self.prioritize()
        """
        results = objdump_handler.search(self.search_pattern)
        for result in results:
            gadget = Gadget(result, self.__class__)
            if gadget.has_controllable_jump:
                self.rop_gadgets.append(gadget)

        self.rop_gadgets = sorted(self.rop_gadgets, key=self.prioritize, reverse=self.reverse_search_results)
        return self.rop_gadgets

    def is_compatible(self, gadget, previous_gadget, fresh_registers):
        """Returns True if gadget is compatible with previous_gadget and fresh_registers.
        For gadget to be compatible, its dependent_registers must all be contained in fresh_registers

        gadget -- the Gadget object being checked for compatibility
        previous_gadget -- the Gadget object coming prior to gadget
        fresh_registers -- list of register names that are available for use by gadget
        """
        return gadget.dependent_registers <= fresh_registers

    @classmethod
    def prioritize(cls, gadget):
        """By default, rop_gadgets is prioritized its length in descending order
        so that gadgets with the fewest side effects come first.
        """
        return len(gadget)


import sys
import os.path
from enum import Enum
from typing import TYPE_CHECKING, Optional, Union

import pygame
from pygame import gfxdraw
from pygame.locals import *

from code.omok3.animations import SpaceAnimation, StoneDeployedAnimation, StoneIdleAnimation, StoneReservedAnimation
from code.network.udp.client_udp import OmokUDP

class Color(Enum):
    BLUE = (0, 0, 255)
    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)

class Team(Enum):
    NONE = 0
    BLACK = 1
    WHITE = 2

    def color(self) -> tuple[int, int, int]:
        if self == Team.NONE:
            return (127, 127, 127)
        elif self == Team.BLACK:
            return (0, 0, 0)
        elif self == Team.WHITE:
            return (255, 255, 255)
        else:
            raise ValueError()

class State(Enum):
    EMPTY = 0
    BLACK = 1
    WHITE = 2
    BLACK_RESERVED = 3
    WHITE_RESERVED = 4

class Validity(Enum):
    INVALID = 0
    RESERVE = 1
    DENY = 2
    CONFIRM = 3

class Game:
    FPS = 60

    def __init__(self, multiplay=False, udp: OmokUDP = None) -> None:
        self.multiplay = multiplay
        if self.multiplay:
            self.udp = udp
            self.udp.rx_event = lambda data: self.decode_udp_and_update(data)
            self.udp.start_listen()
            self.opponent_move: tuple[bool, tuple[int, int]] = (False, (0, 0))
            self.opponent_chat: str = ""
            self.encode_udp_and_send("HELLO")

        pygame.init()
        self.clock = pygame.time.Clock()

        pygame.mouse.set_visible(False)
        self.mouse_clicking = False

        pygame.display.set_caption("Real-time LiveOmok3")  # 창 제목 설정
        self.displaysurf = pygame.display.set_mode((875, 1075))
        root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))  # LiveOmok/
        img_path = os.path.join(root, "res", "board-PIL-step.png")  # LiveOmok/res/board-PIL-step.png
        self.background = pygame.image.load(img_path)
        self.dotum = pygame.font.SysFont("malgun gothic", 30)
        # self.dotum = pygame.font.SysFont("dotumche", 30)
        self.black_heuk = self.dotum.render("흑", True, (0, 0, 0))
        self.white_baek = self.dotum.render("백", True, (255, 255, 255))

        self.board = Board(self)
        self.black_gauge = 0.0
        self.white_gauge = 0.0

        self.displaysurf.blit(self.background, (0, 0))

    def event_lclick(self, click_pos: tuple[int, int]):
        target = self.find_mouse_pointed_space(click_pos)
        if (target is not None) and (target.valid(Team.BLACK) != Validity.INVALID):
            target.click(Team.BLACK)
            if self.multiplay:
                self.encode_udp_and_send(click_pos)

    def event_rclick(self, click_pos: tuple[int, int]):
        if self.multiplay:
            pass
        else:
            target = self.find_mouse_pointed_space(click_pos)
            if (target is not None) and (target.valid(Team.WHITE) != Validity.INVALID):
                target.click(Team.WHITE)

    def loop(self) -> str:
        mouse_over_shadow = pygame.Surface((51, 51), pygame.SRCALPHA)
        mouse_over_shadow.fill((255, 255, 255, 0))  # RGBA : totally transparent
        mouse_over_shadow.set_alpha(128)
        cursor_with_gauge = pygame.Surface((41, 41), pygame.SRCALPHA)

        while True:
            # 고정된 그림: 백 점수판, 배경, 흑 점수판
            pygame.draw.rect(
                self.displaysurf,
                Color.BLACK.value,
                [(0, 0), (875, 100)]
            )
            self.displaysurf.blit(self.background, (0, 100))
            pygame.draw.rect(
                self.displaysurf,
                Color.WHITE.value,
                [(0, 975), (875, 100)]
            )

            # 마우스 이벤트
            for event in pygame.event.get():
                left_clicked = pygame.mouse.get_pressed()[0]
                if event.type == pygame.MOUSEBUTTONDOWN and left_clicked:
                    if self.mouse_clicking:  # mouse holding
                        continue
                    self.mouse_clicking = True
                    pos = pygame.mouse.get_pos()
                    self.event_lclick(pos)
                elif event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[2]:
                    pos = pygame.mouse.get_pos()
                    self.event_rclick(pos)
                elif event.type == pygame.MOUSEBUTTONUP and not left_clicked:
                    self.mouse_clicking = False
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()

            # UDP 이벤트(넷 플레이에서만)
            if self.multiplay:
                if self.opponent_move[0] is True:
                    target = self.find_mouse_pointed_space(self.opponent_move[1])
                    if target is not None:
                        target.click(Team.WHITE)
                    self.opponent_move = (False, (0, 0))

            # 마우스 hover 시 그림자 표시
            pos = pygame.mouse.get_pos()
            target = self.find_mouse_pointed_space(pos)
            if target is not None:
                if target.state == State.EMPTY:
                    pygame.draw.circle(
                        mouse_over_shadow,
                        (127, 127, 127),
                        mouse_over_shadow.get_rect().center,
                        17
                    )
                    self.displaysurf.blit(mouse_over_shadow, target.rect.topleft)

            # 점수판 표시
            black_5, white_5 = self.board.count_5_connected()
            black_score = self.dotum.render(f"{black_5}/3", True, (0, 0, 0))
            white_score = self.dotum.render(f"{white_5}/3", True, (255, 255, 255))
            self.displaysurf.blit(self.black_heuk, (75, 1010))
            self.displaysurf.blit(self.white_baek, (700, 35))
            self.displaysurf.blit(black_score, (125, 1010))
            self.displaysurf.blit(white_score, (750, 35))

            # 게이지 표시
            if self.black_gauge < 3.0:
                self.black_gauge += 1/180  # 3초에 하나씩 채워진다
            if self.white_gauge < 3.0:
                self.white_gauge += 1/180
            self.draw_gauge()

            if black_5 >= 3:
                pygame.mouse.set_visible(True)
                return "Black wins!"
            elif white_5 >= 3:
                return "White wins!"

            self.board.update()

            # 마우스 커서 표시
            cursor_with_gauge.fill((255, 255, 255, 0))  # RGBA : totally transparent
            gfxdraw.aatrigon(cursor_with_gauge, 20, 22, 14, 4, 26, 4, (0, 0, 255))
            gfxdraw.aatrigon(cursor_with_gauge, 20, 22, 38, 27, 33, 36, (0, 0, 255))
            gfxdraw.aatrigon(cursor_with_gauge, 20, 22, 7, 36, 2, 27, (0, 0, 255))
            if 0.0 <= self.black_gauge < 1.0:
                gfxdraw.filled_trigon(
                    cursor_with_gauge,
                    20, 22,
                    round(20 - 6 * self.black_gauge), round(22 - 18 * self.black_gauge),
                    round(20 + 6 * self.black_gauge), round(22 - 18 * self.black_gauge),
                    (0, 0, 255)
                )
            elif 1.0 <= self.black_gauge < 2.0:
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 14, 4, 26, 4, (0, 0, 255))
                gfxdraw.filled_trigon(
                    cursor_with_gauge,
                    20, 22,
                    round(20 + 18 * (self.black_gauge-1)), round(22 + 5 * (self.black_gauge-1)),
                    round(20 + 13 * (self.black_gauge-1)), round(22 + 14 * (self.black_gauge-1)),
                    (0, 0, 255)
                )
            elif 2.0 <= self.black_gauge < 3.0:
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 14, 4, 26, 4, (0, 0, 255))
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 38, 27, 33, 36, (0, 0, 255))
                gfxdraw.filled_trigon(
                    cursor_with_gauge,
                    20, 22,
                    round(20 - 13 * (self.black_gauge-2)), round(22 + 14 * (self.black_gauge-2)),
                    round(20 - 18 * (self.black_gauge-2)), round(22 + 5 * (self.black_gauge-2)),
                    (0, 0, 255)
                )
            else:  # self.black_gauge >= 3.0
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 14, 4, 26, 4, (0, 0, 255))
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 38, 27, 33, 36, (0, 0, 255))
                gfxdraw.filled_trigon(cursor_with_gauge, 20, 22, 7, 36, 2, 27, (0, 0, 255))
            self.displaysurf.blit(cursor_with_gauge, (pos[0]-21, pos[1]-21))

            pygame.display.update()
            self.clock.tick(Game.FPS)

    def draw_gauge(self):
        # 기본 돌 3개 (새치기 시 돌려받는 걸 대비해 3개 더 그린다)
        for i in range(6):
            pygame.draw.circle(
                self.displaysurf,
                (0, 0, 0),
                (250 + 40*i, 1025),
                20
            )
            pygame.draw.circle(
                self.displaysurf,
                (255, 255, 255),
                (400 + 40*i, 50),
                20
            )
        # 가리개를 통해 원의 일부를 가려서 활꼴을 그린다
        pygame.draw.rect(
            self.displaysurf,
            (255, 255, 255),
            (230 + self.black_gauge * 40, 1000, (3.0 - self.black_gauge) * 40 + 130, 50)
        )
        pygame.draw.rect(
            self.displaysurf,
            (0, 0, 0),
            (380 + self.white_gauge * 40, 25, (3.0 - self.white_gauge) * 40 + 130, 50)
        )

    def decode_udp_and_update(self, msg: str) -> None:
        print("Opponent - " + msg)
        if msg.startswith("Move:"):
            # msg에는 Move 헤더와 클릭한 좌표가 들어온다. "Move:530,230"
            msg = msg[5:]
            x, y, *other = msg.split(',')
            if other:  # 좌표가 3개 이상
                return
            self.opponent_move = (True, (int(x), int(y)))

        elif msg.startswith("Chat:"):
            # msg에는 Chat 헤더와 채팅 내용이 들어온다. "Chat:Ez LOL"
            self.opponent_chat = msg[5:]
            print(self.opponent_chat)

    def encode_udp_and_send(self, pos_or_msg: Union[tuple[int, int], str]) -> None:
        if isinstance(pos_or_msg, tuple):
            msg = f"Move:{pos_or_msg[0]},{pos_or_msg[1]}"
        elif isinstance(pos_or_msg, str):
            msg = f"Chat:{pos_or_msg}"
        print("Me - " + msg)
        self.udp.send(msg)

    def find_mouse_pointed_space(self, pos: tuple[int, int]) -> Optional['Space']:
        target: Optional['Space'] = None
        for row in self.board.spaces:
            for space in row:
                if space.rect.inflate(-10, -10).collidepoint(pos):  # Taxi distance
                # if pygame.Vector2(space.rect.center).distance_to(pos) < 25:  # Euclidean distance
                    target = space
                    break
            if target is not None:
                break
        return target

class Board:

    def __init__(self, game: Game) -> None:
        self.game = game
        self.spaces = [[Space(self, j, i) for j in range(15)] for i in range(15)]

    def update(self) -> None:
        for row in self.spaces:
            for space in row:
                try:
                    next(space.animation.play())
                except StopIteration as e:
                    if isinstance(space.animation, StoneReservedAnimation):
                        space.animation = StoneDeployedAnimation(space)
                    elif isinstance(space.animation, StoneDeployedAnimation):
                        space.animation = StoneIdleAnimation(space)

    def count_5_connected(self) -> tuple[int, int]:
        # 참고로 6목은 5목 2개로 본다. 7목은 5목 3개로 보기 때문에 바로 이긴다.
        black_5 = 0
        white_5 = 0
        for row in self.spaces:
            for space in row:
                for team in [space.is_horizontal_5(), space.is_vertical_5(), space.is_slash_5(), space.is_backslash_5()]:
                    if team == Team.BLACK: black_5 += 1
                    elif team == Team.WHITE: white_5 += 1
        return black_5, white_5

class Space:

    def __init__(self, board: Board, x, y) -> None:
        self.board = board
        self.x = x  # 0-14
        self.y = y  # 0-14
        self.team = Team.NONE
        # self.team = Team.BLACK
        self.animation = SpaceAnimation(self)
        # self.animation = StoneIdleAnimation(self)

    def __repr__(self) -> str:
        row = str(15 - self.y)
        col = "ABCDEFGHIJKLMNOP"[self.x]
        return f"Space('{col}{row}')"

    @property
    def rect(self) -> Rect:
        return pygame.Rect(56 + 51 * self.x, 156 + 51 * self.y, 51, 51)

    @property
    def state(self) -> State:
        if isinstance(self.animation, SpaceAnimation):
            return State.EMPTY
        elif isinstance(self.animation, (StoneIdleAnimation, StoneDeployedAnimation)):
            if self.team == Team.BLACK:
                return State.BLACK
            elif self.team == Team.WHITE:
                return State.WHITE
            else:
                raise ValueError()
        elif isinstance(self.animation, StoneReservedAnimation):
            if self.team == Team.BLACK:
                return State.BLACK_RESERVED
            elif self.team == Team.WHITE:
                return State.WHITE_RESERVED
            else:
                raise ValueError()

    def valid(self, clicker_team: Team) -> Validity:
        if self.state == State.EMPTY:
            if (clicker_team == Team.BLACK and self.board.game.black_gauge >= 1.0) \
                or (clicker_team == Team.WHITE and self.board.game.white_gauge >= 1.0):
                return Validity.RESERVE
        elif self.state == State.BLACK_RESERVED:
            if (clicker_team == Team.BLACK and self.board.game.black_gauge >= 1.0):
                return Validity.CONFIRM
            if (clicker_team == Team.WHITE and self.board.game.white_gauge >= 1.5):
                return Validity.DENY
        elif self.state == State.WHITE_RESERVED:
            if (clicker_team == Team.BLACK and self.board.game.black_gauge >= 1.5):
                return Validity.DENY
            if (clicker_team == Team.WHITE and self.board.game.white_gauge >= 1.0):
                return Validity.CONFIRM
        else: return Validity.INVALID

    def click(self, clicker_team: Team):
        if self.state == State.EMPTY:
            if clicker_team == Team.BLACK:
                if self.board.game.black_gauge >= 1.0:
                    self.team = Team.BLACK
                    self.animation = StoneReservedAnimation(self)
                    self.board.game.black_gauge -= 1.0
            elif clicker_team == Team.WHITE:
                if self.board.game.white_gauge >= 1.0:
                    self.team = Team.WHITE
                    self.animation = StoneReservedAnimation(self)
                    self.board.game.white_gauge -= 1.0

        elif self.state == State.BLACK_RESERVED:
            if clicker_team == Team.BLACK:  # 흑 확정
                if self.board.game.black_gauge >= 1.0:
                    self.animation = StoneDeployedAnimation(self)
                    self.board.game.black_gauge -= 1.0
            elif clicker_team == Team.WHITE:  # 백 새치기
                if self.board.game.white_gauge >= 1.5:
                    if self.animation.current_frame > 15:
                        self.team = clicker_team
                        self.animation = StoneDeployedAnimation(self)
                        self.board.game.white_gauge -= 1.5
                        # self.board.game.black_gauge = min(3.0, self.board.game.black_gauge + 1.0)
                        self.board.game.black_gauge += 1.0

        elif self.state == State.WHITE_RESERVED:
            if clicker_team == Team.WHITE:  # 백 확정
                if self.board.game.white_gauge >= 1.0:
                    self.animation = StoneDeployedAnimation(self)
                    self.board.game.white_gauge -= 1.0
            elif clicker_team == Team.BLACK:  # 흑 새치기
                if self.board.game.black_gauge >= 1.5:
                    if self.animation.current_frame > 15:
                        self.team = clicker_team
                        self.animation = StoneDeployedAnimation(self)
                        self.board.game.black_gauge -= 1.5
                        # self.board.game.white_gauge = min(3.0, self.board.game.white_gauge + 1.0)
                        self.board.game.white_gauge += 1.0

    def is_horizontal_5(self) -> Team:
        if not 2 <= self.x <= 12:
            return Team.NONE
        if self.state not in [State.BLACK, State.WHITE]:
            return Team.NONE
        if self.board.spaces[self.y][self.x - 2].state \
            == self.board.spaces[self.y][self.x - 1].state \
            == self.state \
            == self.board.spaces[self.y][self.x + 1].state \
            == self.board.spaces[self.y][self.x + 2].state:
            return self.team
        else:
            return Team.NONE

    def is_vertical_5(self) -> Team:
        if not 2 <= self.y <= 12:
            return Team.NONE
        if self.state not in [State.BLACK, State.WHITE]:
            return Team.NONE
        if self.board.spaces[self.y - 2][self.x].state \
            == self.board.spaces[self.y - 1][self.x].state \
            == self.state \
            == self.board.spaces[self.y + 1][self.x].state \
            == self.board.spaces[self.y + 2][self.x].state:
            return self.team
        else:
            return Team.NONE

    def is_slash_5(self) -> Team:
        if (not 2 <= self.x <= 12) or (not 2 <= self.y <= 12):
            return Team.NONE
        if self.state not in [State.BLACK, State.WHITE]:
            return Team.NONE
        if self.board.spaces[self.y - 2][self.x + 2].state \
            == self.board.spaces[self.y - 1][self.x + 1].state \
            == self.state \
            == self.board.spaces[self.y + 1][self.x - 1].state \
            == self.board.spaces[self.y + 2][self.x - 2].state:
            return self.team
        else:
            return Team.NONE

    def is_backslash_5(self) -> Team:
        if (not 2 <= self.x <= 12) or (not 2 <= self.y <= 12):
            return Team.NONE
        if self.state not in [State.BLACK, State.WHITE]:
            return Team.NONE
        if self.board.spaces[self.y - 2][self.x - 2].state \
            == self.board.spaces[self.y - 1][self.x - 1].state \
            == self.state \
            == self.board.spaces[self.y + 1][self.x + 1].state \
            == self.board.spaces[self.y + 2][self.x + 2].state:
            return self.team
        else:
            return Team.NONE

if __name__ == '__main__':
    Game().loop()


class Solution:
    def titleToNumber(self, s):
        """
        :type s: str
        :rtype: int
        """

        total = 0
        for i, char in enumerate(s[::-1]):
            total += (ord(char) - ord("A") + 1) * 26 ** i
        return total


abjad = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] #membuat aray yang berisi abjad A sampai Z

key = int(input('Masukkan cipher key yang anda inginkan (misal 7): ')) #menentukan key yang akan digunakan dalam metode caesar cipher

def encode(kalimat,cipher_key): # membuat fungsi yang bertujuan untuk menerapkan caesar cipher
  kalimat = kalimat.lower() # membuat variable kalimat dan berisi huruf kecil
  hasil_encode = ''  # membuat variable string untuk memuat hasil enkripsi
  for karakter in kalimat: # membuat perulangan untuk mengenkripsi kalimat
    if karakter in abjad:
      index_lama = abjad.index(karakter) # index lama adalah index pada array abjad
      index_baru = (index_lama + cipher_key ) % len(abjad) # membuat index baru dengan menggeser sejauh key yang telah ditentukan dengan batas index 26
      abjad_baru = abjad[index_baru] # membuat abjad baru dari index baru yang telah diperoleh
      hasil_encode = hasil_encode + abjad_baru # membuat variable baru yang memuat hasil enkripsi
    else:
       hasil_encode = hasil_encode + ' ' # jika karakter yang dimasukkan bukan berasal dari abjad A - Z maka akan menjadi spasi
  return hasil_encode # mengembalikan hasil enkripsi

kalimat = input('Masukkan kalimat yang ingin dienkripsi ') # memasukkan kalimat yang akan di enkripsi
# ENKRIPSI
kalimat_hasil = encode(kalimat,key) # memanggil fungsi Caesar_cipher untuk mengenkripsi kalimat
print('Kalimat yang dimasukkan adalah : ',kalimat)
print('Hasil enkripsi kalimat menggunakan Caesar Cipher dengan key:',key, 'adalah', kalimat_hasil) # Menampilkan hasil enkripsi
# DEKRIPSI (dengan enkripsi ulang menggunakan nilai minus key)
kalimat_awal = encode(kalimat_hasil,-key) # memanggil fungsi Caesar_cipher untuk mendekripsi kalimat
print('Jika hasil dienkripsi ulang menggunakan nilai negatif dari cipher key sebelumnya maka kalimat hasilnya adalah:',-key, 'adalah', kalimat_awal)
 # Menampilkan hasil dekripsi


#First line of the input is a number, which indicates how many pairs of words will follow (each pair in a separate line). The pairs are of the form COUNTRY CITY specifying in which country a city is located. The last line is the name of a city. Print the number of cities that are located in the same country as this city.

#Hint. Use dictionaries.

#For example, on input:
#6
#UK London
#US Boston
#UK Manchester
#UK Leeds
#US Dallas
#Russia Moscow
#Manchester
#output must be:
#3

city_dict = {}
dict_count = int(input())

while dict_count > 0:
  user_input = input()
  value, key = user_input.split(" ")
  city_dict[key] = value
  dict_count = dict_count - 1

key_input = input()
check_value = city_dict[key_input]
value_count = 0

my_list = list(city_dict.values())

for i in my_list:
  if i == check_value:
    value_count += 1

print(value_count)


# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

import bpy

def rna_idprop_ui_get(item, create=True):
    try:
        return item['_RNA_UI']
    except:
        if create:
            item['_RNA_UI'] = {}
            return item['_RNA_UI']
        else:
            return None

def rna_idprop_ui_del(item):
    try:
        del item['_RNA_UI']
    except KeyError:
        pass

def rna_idprop_ui_prop_update(item, prop):
    prop_rna = item.path_resolve("[\"%s\"]" % prop.replace("\"", "\\\""), False)
    if isinstance(prop_rna, bpy.types.bpy_prop):
        prop_rna.update()

def rna_idprop_ui_prop_get(item, prop, create=True):

    rna_ui = rna_idprop_ui_get(item, create)

    if rna_ui is None:
        return None

    try:
        return rna_ui[prop]
    except:
        rna_ui[prop] = {}
        return rna_ui[prop]

def rna_idprop_ui_prop_clear(item, prop, remove=True):
    rna_ui = rna_idprop_ui_get(item, False)

    if rna_ui is None:
        return

    try:
        del rna_ui[prop]
    except KeyError:
        pass
    if remove and len(item.keys()) == 1:
        rna_idprop_ui_del(item)

def rna_idprop_context_value(context, context_member, property_type):
    space = context.space_data

    if space is None or isinstance(space, bpy.types.SpaceProperties):
        pin_id = space.pin_id
    else:
        pin_id = None

    if pin_id and isinstance(pin_id, property_type):
        rna_item = pin_id
        context_member = "space_data.pin_id"
    else:
        rna_item = eval("context." + context_member)

    return rna_item, context_member

def rna_idprop_has_properties(rna_item):
    keys = rna_item.keys()
    nbr_props = len(keys)
    return (nbr_props > 1) or (nbr_props and '_RNA_UI' not in keys)

def draw(layout, context, context_member, property_type, use_edit=True):

    def assign_props(prop, val, key):
        prop.data_path = context_member
        prop.property = key

        try:
            prop.value = str(val)
        except:
            pass

    rna_item, context_member = rna_idprop_context_value(context, context_member, property_type)

    # poll should really get this...
    if not rna_item:
        return

    from bpy.utils import escape_identifier

    if rna_item.id_data.library is not None:
        use_edit = False

    assert(isinstance(rna_item, property_type))

    items = rna_item.items()
    items.sort()

    if use_edit:
        row = layout.row()
        props = row.operator("wm.properties_add", text="Add")
        props.data_path = context_member
        del row

    rna_properties = {prop.identifier for prop in rna_item.bl_rna.properties if prop.is_runtime} if items else None

    for key, val in items:

        if key == '_RNA_UI':
            continue

        row = layout.row()
        to_dict = getattr(val, "to_dict", None)
        to_list = getattr(val, "to_list", None)

        # val_orig = val  # UNUSED
        if to_dict:
            val = to_dict()
            val_draw = str(val)
        elif to_list:
            val = to_list()
            val_draw = str(val)
        else:
            val_draw = val

        box = row.box()

        if use_edit:
            split = box.split(percentage=0.75)
            row = split.row()
        else:
            row = box.row()

        row.label(text=key, translate=False)

        # explicit exception for arrays
        is_rna = (key in rna_properties)

        if to_dict or to_list:
            row.label(text=val_draw, translate=False)
        else:
            if is_rna:
                row.prop(rna_item, key, text="")
            else:
                row.prop(rna_item, '["%s"]' % escape_identifier(key), text="")

        if use_edit:
            row = split.row(align=True)
            if not is_rna:
                props = row.operator("wm.properties_edit", text="Edit")
                assign_props(props, val_draw, key)
                props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT')
                assign_props(props, val_draw, key)
            else:
                row.label(text="API Defined")

class PropertyPanel:
    """
    The subclass should have its own poll function
    and the variable '_context_path' MUST be set.
    """
    bl_label = "Custom Properties"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        rna_item, context_member = rna_idprop_context_value(context, cls._context_path, cls._property_type)
        return bool(rna_item)

    """
    def draw_header(self, context):
        rna_item, context_member = rna_idprop_context_value(context, self._context_path, self._property_type)
        tot = len(rna_item.keys())
        if tot:
            self.layout().label("%d:" % tot)
    """

    def draw(self, context):
        draw(self.layout, context, self._context_path, self._property_type)


### 注意：该代码仅能在开发板中运行
import numpy as np
import cv2
import os
from prepare_functions import get_bgr_image, preprocess_onboard, postprocess
from hobot_dnn import pyeasy_dnn as dnn

# 记得要安装一些包
# sudo pip3 install scipy opencv-contrib-python

def get_hw(pro):
    if pro.layout == "NCHW":
        return pro.shape[2], pro.shape[3]
    else:
        return pro.shape[1], pro.shape[2]

dataroot = "data/unet"
imgpath = os.path.join(dataroot, "mra_img_12.jpg")
binpath = os.path.join(dataroot, "model_output/unet.bin")

# 加载图像和BIN模型
img = get_bgr_image(imgpath)
models = dnn.load(binpath)

# 图像数据预处理，这里对几个地方进行解释：
# models[0]：BPU支持加载多个模型，这里只有一个unet，因此[0]表示访问第一个模型
# inputs[0]：模型输入可能多种，unet的输入只有一个，因此[0]表示获取第一个输入的参数
model_h, model_w = get_hw(models[0].inputs[0].properties)

datain = preprocess_onboard(img, model_h, model_w) # 1x256x256x3

# 模型推理：相比于onnx推理，这里不用再重新构造一个inputs
t1 = cv2.getTickCount()
outputs = models[0].forward(datain)
t2 = cv2.getTickCount()
print('time consumption {0} ms'.format((t2-t1)*1000/cv2.getTickFrequency()))

pro = models[0]
print(type(pro.name), pro.name)
print(type(pro.estimate_latency), pro.estimate_latency)

# 后处理并保存结果，这里对几个地方进行解释
# outputs数据类型为tuple，每个元素的数据类型是pyDNNTensor
# 因此想要获取输出的矩阵的话，需要调用buffer
pred = postprocess(outputs[0].buffer) # outputs[0].buffer: (1, 2, 256, 256)
cv2.imwrite(os.path.join(dataroot, "pred_bin.png"), pred[0])


from PIL import Image
from absl import app
from options import FLAGS as opts
import data
import datetime
import functools
import glob
import losses
import networks
import numpy as np
import options
import os.path as osp
import random
import skimage.measure
import staged_model
import tensorflow as tf
import time
import utils

def build_model_fn(use_exponential_moving_average=True):
  """Builds and returns the model function for an estimator.

  Args:
    use_exponential_moving_average: bool. If true, the exponential moving
    average will be used.

  Returns:
    function, the model_fn function typically required by an estimator.
  """
  arch_type = opts.arch_type
  use_appearance = opts.use_appearance
  def model_fn(features, labels, mode, params):
    """An estimator build_fn."""
    del labels, params
    if mode == tf.estimator.ModeKeys.TRAIN:
      step = tf.train.get_global_step()

      x_in = features['conditional_input']
      x_gt = features['expected_output']  # ground truth output
      x_app = features['peek_input']

      if opts.training_pipeline == 'staged':
        ops = staged_model.create_computation_graph(x_in, x_gt, x_app=x_app,
                                                    arch_type=opts.arch_type)
        op_increment_step = tf.assign_add(step, 1)
        train_disc_op = ops['train_disc_op']
        train_renderer_op = ops['train_renderer_op']
        train_op = tf.group(train_disc_op, train_renderer_op, op_increment_step)

        utils.HookReport.log_tensor(ops['total_loss_d'], 'total_loss_d')
        utils.HookReport.log_tensor(ops['loss_d_real'], 'loss_d_real')
        utils.HookReport.log_tensor(ops['loss_d_fake'], 'loss_d_fake')
        utils.HookReport.log_tensor(ops['total_loss_g'], 'total_loss_g')
        utils.HookReport.log_tensor(ops['loss_g_gan'], 'loss_g_gan')
        utils.HookReport.log_tensor(ops['loss_g_recon'], 'loss_g_recon')
        utils.HookReport.log_tensor(step, 'global_step')

        return tf.estimator.EstimatorSpec(
            mode=mode, loss=ops['total_loss_d'] + ops['total_loss_g'],
            train_op=train_op)
      else:
        raise NotImplementedError('%s training is not implemented.' %
                                  opts.training_pipeline)
    elif mode == tf.estimator.ModeKeys.EVAL:
      raise NotImplementedError('Eval is not implemented.')
    else:  # all below modes are for difference inference tasks.
      # Build network and initialize inference variables.
      g_func = networks.RenderingModel(arch_type, use_appearance)
      if use_appearance:
        app_func = g_func.get_appearance_encoder()
      if use_exponential_moving_average:
        ema = tf.train.ExponentialMovingAverage(decay=0.999)
        var_dict = ema.variables_to_restore()
        tf.train.init_from_checkpoint(osp.join(opts.train_dir), var_dict)

      if mode == tf.estimator.ModeKeys.PREDICT:
        x_in = features['conditional_input']
        if use_appearance:
          x_app = features['peek_input']
          x_app_embedding, _, _ = app_func(x_app)
        else:
          x_app_embedding = None
        y = g_func(x_in, x_app_embedding)
        tf.logging.info('DBG: shape of y during prediction %s.' % str(y.shape))
        return tf.estimator.EstimatorSpec(mode=mode, predictions=y)

      # 'eval_subset' mode is same as PREDICT but it concatenates the output to
      # the input render, semantic map and ground truth for easy comparison.
      elif mode == 'eval_subset':
        x_in = features['conditional_input']
        x_gt = features['expected_output']
        if use_appearance:
          x_app = features['peek_input']
          x_app_embedding, _, _ = app_func(x_app)
        else:
          x_app_embedding = None
        y = g_func(x_in, x_app_embedding)
        tf.logging.info('DBG: shape of y during prediction %s.' % str(y.shape))
        x_in_rgb = tf.slice(x_in, [0, 0, 0, 0], [-1, -1, -1, 3])
        if opts.use_semantic:
          x_in_semantic = tf.slice(x_in, [0, 0, 0, 4], [-1, -1, -1, 3])
          output_tuple = tf.concat([x_in_rgb, x_in_semantic, y, x_gt], axis=2)
        else:
          output_tuple = tf.concat([x_in_rgb, y, x_gt], axis=2)
        return tf.estimator.EstimatorSpec(mode=mode, predictions=output_tuple)

      # 'compute_appearance' mode computes and returns the latent z vector.
      elif mode == 'compute_appearance':
        assert use_appearance, 'use_appearance is set to False!'
        x_app_in = features['peek_input']
        # NOTE the following line is a temporary hack (which is
        # specially bad for inputs smaller than 512x512).
        x_app_in = tf.image.resize_image_with_crop_or_pad(x_app_in, 512, 512)
        app_embedding, _, _ = app_func(x_app_in)
        return tf.estimator.EstimatorSpec(mode=mode, predictions=app_embedding)

      # 'interpolate_appearance' mode expects an already computed latent z
      # vector as input passed a value to the dict key 'appearance_embedding'.
      elif mode == 'interpolate_appearance':
        assert use_appearance, 'use_appearance is set to False!'
        x_in = features['conditional_input']
        x_app_embedding = features['appearance_embedding']
        y = g_func(x_in, x_app_embedding)
        tf.logging.info('DBG: shape of y during prediction %s.' % str(y.shape))
        return tf.estimator.EstimatorSpec(mode=mode, predictions=y)
      else:
        raise ValueError('Unsupported mode: ' + mode)

  return model_fn

def make_sample_grid_and_save(est, dataset_name, dataset_parent_dir, grid_dims,
                              output_dir, cur_nimg):
  """Evaluate a fixed set of validation images and save output.

  Args:
    est: tf,estimator.Estimator, TF estimator to run the predictions.
    dataset_name: basename for the validation tfrecord from which to load
      validation images.
    dataset_parent_dir: path to a directory containing the validation tfrecord.
    grid_dims: 2-tuple int for the grid size (1 unit = 1 image).
    output_dir: string, where to save image samples.
    cur_nimg: int, current number of images seen by training.

  Returns:
    None.
  """
  num_examples = grid_dims[0] * grid_dims[1]
  def input_val_fn():
    dict_inp = data.provide_data(
        dataset_name=dataset_name, parent_dir=dataset_parent_dir, subset='val',
        batch_size=1, crop_flag=True, crop_size=opts.train_resolution,
        seeds=[0], max_examples=num_examples,
        use_appearance=opts.use_appearance, shuffle=0)
    x_in = dict_inp['conditional_input']
    x_gt = dict_inp['expected_output']  # ground truth output
    x_app = dict_inp['peek_input']
    return x_in, x_gt, x_app

  def est_input_val_fn():
    x_in, _, x_app = input_val_fn()
    features = {'conditional_input': x_in, 'peek_input': x_app}
    return features

  images = [x for x in est.predict(est_input_val_fn)]
  images = np.array(images, 'f')
  images = images.reshape(grid_dims + images.shape[1:])
  utils.save_images(utils.to_png(utils.images_to_grid(images)), output_dir,
                    cur_nimg)

def visualize_image_sequence(est, dataset_name, dataset_parent_dir,
                             input_sequence_name, app_base_path, output_dir):
  """Generates an image sequence as a video and stores it to disk."""
  batch_sz = opts.batch_size
  def input_seq_fn():
    dict_inp = data.provide_data(
        dataset_name=dataset_name, parent_dir=dataset_parent_dir,
        subset=input_sequence_name, batch_size=batch_sz, crop_flag=False,
        seeds=None, use_appearance=False, shuffle=0)
    x_in = dict_inp['conditional_input']
    return x_in

  # Compute appearance embedding only once and use it for all input frames.
  app_rgb_path = app_base_path + '_reference.png'
  app_rendered_path = app_base_path + '_color.png'
  app_depth_path = app_base_path + '_depth.png'
  app_sem_path = app_base_path + '_seg_rgb.png'
  x_app = _load_and_concatenate_image_channels(
      app_rgb_path, app_rendered_path, app_depth_path, app_sem_path)
  def seq_with_single_appearance_inp_fn():
    """input frames with a fixed latent appearance vector."""
    x_in_op = input_seq_fn()
    x_app_op = tf.convert_to_tensor(x_app)
    x_app_tiled_op = tf.tile(x_app_op, [tf.shape(x_in_op)[0], 1, 1, 1])
    return {'conditional_input': x_in_op,
            'peek_input': x_app_tiled_op}

  images = [x for x in est.predict(seq_with_single_appearance_inp_fn)]
  for i, gen_img in enumerate(images):
    output_file_path = osp.join(output_dir, 'out_%04d.png' % i)
    print('Saving frame #%d to %s' % (i, output_file_path))
    with tf.gfile.Open(output_file_path, 'wb') as f:
      f.write(utils.to_png(gen_img))

def train(dataset_name, dataset_parent_dir, load_pretrained_app_encoder,
          load_trained_fixed_app, save_samples_kimg=50):
  """Main training procedure.

  The trained model is saved in opts.train_dir, the function itself does not
   return anything.

  Args:
    save_samples_kimg: int, period (in KiB) to save sample images.

  Returns:
    None.
  """
  image_dir = osp.join(opts.train_dir, 'images')  # to save validation images.
  tf.gfile.MakeDirs(image_dir)
  config = tf.estimator.RunConfig(
      save_summary_steps=(1 << 10) // opts.batch_size,
      save_checkpoints_steps=(save_samples_kimg << 10) // opts.batch_size,
      keep_checkpoint_max=5,
      log_step_count_steps=1 << 30)
  model_dir = opts.train_dir
  if (opts.use_appearance and load_trained_fixed_app and
      not tf.train.latest_checkpoint(model_dir)):
    tf.logging.warning('***** Loading resume_step from %s!' %
                       opts.fixed_appearance_train_dir)
    resume_step = utils.load_global_step_from_checkpoint_dir(
        opts.fixed_appearance_train_dir)
  else:
    tf.logging.warning('***** Loading resume_step (if any) from %s!' %
                       model_dir)
    resume_step = utils.load_global_step_from_checkpoint_dir(model_dir)
  if resume_step != 0:
    tf.logging.warning('****** Resuming training at %d!' % resume_step)

  model_fn = build_model_fn()  # model function for TFEstimator.

  hooks = [utils.HookReport(1 << 12, opts.batch_size)]

  if opts.use_appearance and load_pretrained_app_encoder:
    tf.logging.warning('***** will warm-start from %s!' %
                       opts.appearance_pretrain_dir)
    ws = tf.estimator.WarmStartSettings(
        ckpt_to_initialize_from=opts.appearance_pretrain_dir,
        vars_to_warm_start='appearance_net/.*')
  elif opts.use_appearance and load_trained_fixed_app:
    tf.logging.warning('****** finetuning will warm-start from %s!' %
                       opts.fixed_appearance_train_dir)
    ws = tf.estimator.WarmStartSettings(
        ckpt_to_initialize_from=opts.fixed_appearance_train_dir,
        vars_to_warm_start='.*')
  else:
    ws = None
    tf.logging.warning('****** No warm-starting; using random initialization!')

  est = tf.estimator.Estimator(model_fn, model_dir, config, params={},
                               warm_start_from=ws)

  for next_kimg in range(opts.save_samples_kimg, opts.total_kimg + 1,
                         opts.save_samples_kimg):
    next_step = (next_kimg << 10) // opts.batch_size
    if opts.num_crops == -1:  # use random crops
      crop_seeds = None
    else:
      crop_seeds = list(100 * np.arange(opts.num_crops))
    input_train_fn = functools.partial(
        data.provide_data, dataset_name=dataset_name,
        parent_dir=dataset_parent_dir, subset='train',
        batch_size=opts.batch_size, crop_flag=True,
        crop_size=opts.train_resolution, seeds=crop_seeds,
        use_appearance=opts.use_appearance)
    est.train(input_train_fn, max_steps=next_step, hooks=hooks)
    tf.logging.info('DBG: kimg=%d, cur_step=%d' % (next_kimg, next_step))
    tf.logging.info('DBG: Saving a validation grid image %06d to %s' % (
        next_kimg, image_dir))
    make_sample_grid_and_save(est, dataset_name, dataset_parent_dir, (3, 3),
                              image_dir, next_kimg << 10)

def _build_inference_estimator(model_dir):
  model_fn = build_model_fn()
  est = tf.estimator.Estimator(model_fn, model_dir)
  return est

def evaluate_sequence(dataset_name, dataset_parent_dir, virtual_seq_name,
                      app_base_path):
  output_dir = osp.join(opts.train_dir, 'seq_output_%s' % virtual_seq_name)
  tf.gfile.MakeDirs(output_dir)
  est = _build_inference_estimator(opts.train_dir)
  visualize_image_sequence(est, dataset_name, dataset_parent_dir,
                           virtual_seq_name, app_base_path, output_dir)

def evaluate_image_set(dataset_name, dataset_parent_dir, subset_suffix,
                       output_dir=None, batch_size=6):
  if output_dir is None:
    output_dir = osp.join(opts.train_dir, 'validation_output_%s' % subset_suffix)
  tf.gfile.MakeDirs(output_dir)
  model_fn_old = build_model_fn()
  def model_fn_wrapper(features, labels, mode, params):
    del mode
    return model_fn_old(features, labels, 'eval_subset', params)
  model_dir = opts.train_dir
  est = tf.estimator.Estimator(model_fn_wrapper, model_dir)
  est_inp_fn = functools.partial(
      data.provide_data, dataset_name=dataset_name,
      parent_dir=dataset_parent_dir, subset=subset_suffix,
      batch_size=batch_size, use_appearance=opts.use_appearance, shuffle=0)

  print('Evaluating images for subset %s' % subset_suffix)
  images = [x for x in est.predict(est_inp_fn)]
  print('Evaluated %d images' % len(images))
  for i, img in enumerate(images):
    output_file_path = osp.join(output_dir, 'out_%04d.png' % i)
    print('Saving file #%d: %s' % (i, output_file_path))
    with tf.gfile.Open(output_file_path, 'wb') as f:
      f.write(utils.to_png(img))

def _load_and_concatenate_image_channels(rgb_path=None, rendered_path=None,
                                         depth_path=None, seg_path=None,
                                         size_multiple=64):
  """Prepares a single input for the network."""
  if (rgb_path is None and rendered_path is None and depth_path is None and
      seg_path is None):
    raise ValueError('At least one of the inputs has to be not None')

  channels = ()
  if rgb_path is not None:
    rgb_img = np.array(Image.open(rgb_path)).astype(np.float32)
    rgb_img = utils.crop_to_multiple(rgb_img, size_multiple)
    channels = channels + (rgb_img,)
  if rendered_path is not None:
    rendered_img = np.array(Image.open(rendered_path)).astype(np.float32)
    if not opts.use_alpha:
      rendered_img = rendered_img[:, :, :3]  # drop the alpha channel
    rendered_img = utils.crop_to_multiple(rendered_img, size_multiple)
    channels = channels + (rendered_img,)
  if depth_path is not None:
    depth_img = np.array(Image.open(depth_path))
    depth_img = depth_img.astype(np.float32)
    depth_img = utils.crop_to_multiple(depth_img[:, :, np.newaxis],
                                       size_multiple)
    channels = channels + (depth_img,)
    # depth_img = depth_img * (2.0 / 255) - 1.0
  if seg_path is not None:
    seg_img = np.array(Image.open(seg_path)).astype(np.float32)
    seg_img = utils.crop_to_multiple(seg_img, size_multiple)
    channels = channels + (seg_img,)
  # Concatenate and normalize channels
  img = np.dstack(channels)
  img = np.expand_dims(img, axis=0)
  img = img * (2.0 / 255) - 1.0
  return img

def infer_dir(model_dir, input_dir, output_dir):
  tf.gfile.MakeDirs(output_dir)
  est = _build_inference_estimator(opts.train_dir)

  def read_image(base_path, is_appearance=False):
    if is_appearance:
      ref_img_path = base_path + '_reference.png'
    else:
      ref_img_path = None
    rendered_img_path = base_path + '_color.png'
    depth_img_path = base_path + '_depth.png'
    seg_img_path = base_path + '_seg_rgb.png'
    img = _load_and_concatenate_image_channels(
        rgb_path=ref_img_path, rendered_path=rendered_img_path,
        depth_path=depth_img_path, seg_path=seg_img_path)
    return img

  def get_inference_input_fn(base_path, app_base_path):
    x_in = read_image(base_path, False)
    x_app_in = read_image(app_base_path, True)
    def infer_input_fn():
      return {'conditional_input': x_in, 'peek_input': x_app_in}
    return infer_input_fn

  file_paths = sorted(glob.glob(osp.join(input_dir, '*_depth.png')))
  base_paths = [x[:-10] for x in file_paths]  # remove the '_depth.png' suffix
  for inp_base_path in base_paths:
    est_inp_fn = get_inference_input_fn(inp_base_path, inp_base_path)
    img = next(est.predict(est_inp_fn))
    basename = osp.basename(inp_base_path)
    output_img_path = osp.join(output_dir, basename + '_out.png')
    print('Saving generated image to %s' % output_img_path)
    with tf.gfile.Open(output_img_path, 'wb') as f:
      f.write(utils.to_png(img))

def joint_interpolation(model_dir, app_input_dir, st_app_basename,
                        end_app_basename, camera_path_dir):
  """
  Interpolates both viewpoint and appearance between two input images.
  """
  # Create output direcotry
  output_dir = osp.join(model_dir, 'joint_interpolation_out')
  tf.gfile.MakeDirs(output_dir)

  # Build estimator
  model_fn_old = build_model_fn()
  def model_fn_wrapper(features, labels, mode, params):
    del mode
    return model_fn_old(features, labels, 'interpolate_appearance', params)
  def appearance_model_fn(features, labels, mode, params):
    del mode
    return model_fn_old(features, labels, 'compute_appearance', params)
  config = tf.estimator.RunConfig(
      save_summary_steps=1000, save_checkpoints_steps=50000,
      keep_checkpoint_max=50, log_step_count_steps=1 << 30)
  model_dir = model_dir
  est = tf.estimator.Estimator(model_fn_wrapper, model_dir, config, params={})
  est_app = tf.estimator.Estimator(appearance_model_fn, model_dir, config,
                                   params={})

  # Compute appearance embeddings for the two input appearance images.
  app_inputs = []
  for app_basename in [st_app_basename, end_app_basename]:
    app_rgb_path = osp.join(app_input_dir, app_basename + '_reference.png')
    app_rendered_path = osp.join(app_input_dir, app_basename + '_color.png')
    app_depth_path = osp.join(app_input_dir, app_basename + '_depth.png')
    app_seg_path = osp.join(app_input_dir, app_basename + '_seg_rgb.png')
    app_in = _load_and_concatenate_image_channels(
        rgb_path=app_rgb_path, rendered_path=app_rendered_path,
        depth_path=app_depth_path, seg_path=app_seg_path)
    # app_inputs.append(tf.convert_to_tensor(app_in))
    app_inputs.append(app_in)

  embedding1 = next(est_app.predict(
      lambda: {'peek_input': app_inputs[0]}))
  embedding1 = np.expand_dims(embedding1, axis=0)
  embedding2 = next(est_app.predict(
      lambda: {'peek_input': app_inputs[1]}))
  embedding2 = np.expand_dims(embedding2, axis=0)

  file_paths = sorted(glob.glob(osp.join(camera_path_dir, '*_depth.png')))
  base_paths = [x[:-10] for x in file_paths]  # remove the '_depth.png' suffix

  # Compute interpolated appearance embeddings
  num_interpolations = len(base_paths)
  interpolated_embeddings = []
  delta_vec = (embedding2 - embedding1) / (num_interpolations - 1)
  for delta_iter in range(num_interpolations):
    x_app_embedding = embedding1 + delta_iter * delta_vec
    interpolated_embeddings.append(x_app_embedding)

  # Generate and save interpolated images
  for frame_idx, embedding in enumerate(interpolated_embeddings):
    # Read in input frame
    frame_render_path = osp.join(base_paths[frame_idx] + '_color.png')
    frame_depth_path = osp.join(base_paths[frame_idx] + '_depth.png')
    frame_seg_path = osp.join(base_paths[frame_idx] + '_seg_rgb.png')
    x_in = _load_and_concatenate_image_channels(
        rgb_path=None, rendered_path=frame_render_path,
        depth_path=frame_depth_path, seg_path=frame_seg_path)

    img = next(est.predict(
        lambda: {'conditional_input': tf.convert_to_tensor(x_in),
                 'appearance_embedding': tf.convert_to_tensor(embedding)}))
    output_img_name = '%s_%s_%03d.png' % (st_app_basename, end_app_basename,
                                          frame_idx)
    output_img_path = osp.join(output_dir, output_img_name)
    print('Saving interpolated image to %s' % output_img_path)
    with tf.gfile.Open(output_img_path, 'wb') as f:
      f.write(utils.to_png(img))

def interpolate_appearance(model_dir, input_dir, target_img_basename,
                           appearance_img1_basename, appearance_img2_basename):
  # Create output direcotry
  output_dir = osp.join(model_dir, 'interpolate_appearance_out')
  tf.gfile.MakeDirs(output_dir)

  # Build estimator
  model_fn_old = build_model_fn()
  def model_fn_wrapper(features, labels, mode, params):
    del mode
    return model_fn_old(features, labels, 'interpolate_appearance', params)
  def appearance_model_fn(features, labels, mode, params):
    del mode
    return model_fn_old(features, labels, 'compute_appearance', params)
  config = tf.estimator.RunConfig(
      save_summary_steps=1000, save_checkpoints_steps=50000,
      keep_checkpoint_max=50, log_step_count_steps=1 << 30)
  model_dir = model_dir
  est = tf.estimator.Estimator(model_fn_wrapper, model_dir, config, params={})
  est_app = tf.estimator.Estimator(appearance_model_fn, model_dir, config,
                                   params={})

  # Compute appearance embeddings for the two input appearance images.
  app_inputs = []
  for app_basename in [appearance_img1_basename, appearance_img2_basename]:
    app_rgb_path = osp.join(input_dir, app_basename + '_reference.png')
    app_rendered_path = osp.join(input_dir, app_basename + '_color.png')
    app_depth_path = osp.join(input_dir, app_basename + '_depth.png')
    app_seg_path = osp.join(input_dir, app_basename + '_seg_rgb.png')
    app_in = _load_and_concatenate_image_channels(
        rgb_path=app_rgb_path, rendered_path=app_rendered_path,
        depth_path=app_depth_path, seg_path=app_seg_path)
    # app_inputs.append(tf.convert_to_tensor(app_in))
    app_inputs.append(app_in)

  embedding1 = next(est_app.predict(
      lambda: {'peek_input': app_inputs[0]}))
  embedding2 = next(est_app.predict(
      lambda: {'peek_input': app_inputs[1]}))
  embedding1 = np.expand_dims(embedding1, axis=0)
  embedding2 = np.expand_dims(embedding2, axis=0)

  # Compute interpolated appearance embeddings
  num_interpolations = 10
  interpolated_embeddings = []
  delta_vec = (embedding2 - embedding1) / num_interpolations
  for delta_iter in range(num_interpolations + 1):
    x_app_embedding = embedding1 + delta_iter * delta_vec
    interpolated_embeddings.append(x_app_embedding)

  # Read in the generator input for the target image to render
  rendered_img_path = osp.join(input_dir, target_img_basename + '_color.png')
  depth_img_path = osp.join(input_dir, target_img_basename + '_depth.png')
  seg_img_path = osp.join(input_dir, target_img_basename + '_seg_rgb.png')
  x_in = _load_and_concatenate_image_channels(
      rgb_path=None, rendered_path=rendered_img_path,
      depth_path=depth_img_path, seg_path=seg_img_path)

  # Generate and save interpolated images
  for interpolate_iter, embedding in enumerate(interpolated_embeddings):
    img = next(est.predict(
        lambda: {'conditional_input': tf.convert_to_tensor(x_in),
                 'appearance_embedding': tf.convert_to_tensor(embedding)}))
    output_img_name = 'interpolate_%s_%s_%s_%03d.png' % (
        target_img_basename, appearance_img1_basename, appearance_img2_basename,
        interpolate_iter)
    output_img_path = osp.join(output_dir, output_img_name)
    print('Saving interpolated image to %s' % output_img_path)
    with tf.gfile.Open(output_img_path, 'wb') as f:
      f.write(utils.to_png(img))

def main(argv):
  del argv
  configs_str = options.list_options()
  tf.gfile.MakeDirs(opts.train_dir)
  with tf.gfile.Open(osp.join(opts.train_dir, 'configs.txt'), 'wb') as f:
    f.write(configs_str)
  tf.logging.info('Local configs\n%s' % configs_str)

  if opts.run_mode == 'train':
    dataset_name = opts.dataset_name
    dataset_parent_dir = opts.dataset_parent_dir
    load_pretrained_app_encoder = opts.load_pretrained_app_encoder
    load_trained_fixed_app = opts.load_from_another_ckpt
    batch_size = opts.batch_size
    train(dataset_name, dataset_parent_dir, load_pretrained_app_encoder,
          load_trained_fixed_app)
  elif opts.run_mode == 'eval':  # generate a camera path output sequence from TFRecord inputs.
    dataset_name = opts.dataset_name
    dataset_parent_dir = opts.dataset_parent_dir
    virtual_seq_name = opts.virtual_seq_name
    inp_app_img_base_path = opts.inp_app_img_base_path
    evaluate_sequence(dataset_name, dataset_parent_dir, virtual_seq_name,
                      inp_app_img_base_path)
  elif opts.run_mode == 'eval_subset':  # generate output for validation set (encoded as TFRecords)
    dataset_name = opts.dataset_name
    dataset_parent_dir = opts.dataset_parent_dir
    virtual_seq_name = opts.virtual_seq_name
    evaluate_image_set(dataset_name, dataset_parent_dir, virtual_seq_name,
                       opts.output_validation_dir, opts.batch_size)
  elif opts.run_mode == 'eval_dir':  # evaluate output for a directory with input images
    input_dir = opts.inference_input_path
    output_dir = opts.inference_output_dir
    model_dir = opts.train_dir
    infer_dir(model_dir, input_dir, output_dir)
  elif opts.run_mode == 'interpolate_appearance':  # interpolate appearance only between two images.
    model_dir = opts.train_dir
    input_dir = opts.inference_input_path
    target_img_basename = opts.target_img_basename
    app_img1_basename = opts.appearance_img1_basename
    app_img2_basename = opts.appearance_img2_basename
    interpolate_appearance(model_dir, input_dir, target_img_basename,
                           app_img1_basename, app_img2_basename)
  elif opts.run_mode == 'joint_interpolation':  # interpolate viewpoint and appearance between two images
    model_dir = opts.train_dir
    app_input_dir = opts.inference_input_path
    st_app_basename = opts.appearance_img1_basename
    end_app_basename = opts.appearance_img2_basename
    frames_dir = opts.frames_dir
    joint_interpolation(model_dir, app_input_dir, st_app_basename,
                        end_app_basename, frames_dir)
  else:
    raise ValueError('Unsupported --run_mode %s' % opts.run_mode)

if __name__ == '__main__':
  app.run(main)


from django.core.serializers import get_serializer
from rest_framework import status, generics
from rest_framework.response import Response
import logging

from mFactor.api.cart.utils import CartMixin
from mFactor.api.unit.models import Unit
from mFactor.api.cart.serializers import CartSerializer, CartUnitSerializer, CartDeleteSerializer, CartSelectAllSerializer

logger = logging.getLogger('django')

class CartSelectAllView(CartMixin, generics.GenericAPIView):
    serializer_class = CartSelectAllSerializer

    def put(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']

        cart_dict = self.get_cart(request)

        for sku_key in cart_dict:
            cart_dict[sku_key][1] = True

        response = Response(serializer.data)
        self.post_cart(request, cart_dict, response)

        return response

class CartView(CartMixin, generics.GenericAPIView):
    serializer_class = CartSerializer

    def get(self, request):
        cart_dict = self.get_cart(request)

        sku_list = cart_dict.keys()
        skus = list()

        try:
            skus = Unit.objects.filter(sku__in=sku_list)
        except Exception as e:
            logger.error(f'Query Error:[message: {e}]')

        for sku in skus:
            sku.quantity = cart_dict[sku][0]
            sku.selected = cart_dict[sku][1]

        serializer = CartUnitSerializer(skus, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        attrs = serializer.validated_data
        sku, quantity, selected = attrs['sku'], attrs['quantity'], attrs['selected']

        cart_dict = self.get_cart(request)

        cart_dict[sku] = [quantity, selected]

        response = Response(serializer.data)
        self.post_cart(request, cart_dict, response)

        return response

    def put(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        attrs = serializer.validated_data
        sku, quantity, selected = attrs['sku'], attrs['quantity'], attrs['selected']

        cart_dict = self.get_cart(request)

        cart_dict[sku] = [quantity, selected]

        response = Response(serializer.data)
        self.post_cart(request, cart_dict, response)

        return response

    def delete(self, request):
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku = serializer.validated_data['sku']

        cart_dict = self.get_cart(request)

        try:
            cart_dict.pop(sku)
        except Exception:
            return Response({'message': 'Unit is not in cart'}, status=status.HTTP_404_NOT_FOUND)

        response = Response(serializer.data)
        self.post_cart(request, cart_dict, response)


# Ćwiczenie 12-1

"""
ĆWICZENIE: FileNotFoundError exception
Stwórz funkcję, która obsługuje otwieranie pliku do wczytywania danych.

Zapytaj się użytkownika o nazwę pliku, który chce otworzyć do wczytania.

Jeśli plik nie istnieje wypisz mu odpowiedni komunikat.

Jeśli plik istnieje wczytaj całą jego zawartość i zwróć jako wynik funkcji.

Podpowiedź: skorzystaj z wiedzy dotyczącej obsługi wyjątków z poprzedniej lekcji:

except FileNotFoundError:
"""

def odczytywanie (plik):
    try:
        with open (plik, "r", encoding = "UTF-8") as file:
            return file.read ()
    except FileNotFoundError:
        print ("Nie ma takiego pliku")

uzytkownik = input ("Podaj nazwę pliku: ")

print (odczytywanie (uzytkownik))


from django.urls import path
from .views import *
from django.conf.urls import url

urlpatterns = [
    url(r"^$", test7),
    url(r"^test1/$", test1),
    url(r"^test2/$", test2),
    url(r"^test3/$", test3),
    url(r"^hello_world/$", hello_world),
    url(r"^register/$", TestView.as_view()),
]


#diasPerdidosPorFumar - Recebe uma quantidade de cigarros fumados por dia e a quantidade de anos que fuma,
# e retorna o total de dias perdidos, sabendo que cada cigarro reduz a vida em 10 minutos.

cigarros = int(input("Cigarros fumados por dia: "))
anos = int(input("Quantidade de anos que fumou: "))

cigarros_fumados = (anos * 365) * cigarros
minutos_perdidos = cigarros_fumados * 10
dias_perdidos = minutos_perdidos / 1440

print("Você perdeu", dias_perdidos, "dias")


import argparse
import sys
from nxfvars import __version__
from nxfvars.execute import nxfvars_execute

def main():
    parser = argparse.ArgumentParser("nxfvars")
    subparsers = parser.add_subparsers(
        title="subcommands",
        dest="subcommand",
        required=True,
    )
    parser_version = subparsers.add_parser("version", help="Print the version and exit")
    parser_execute = subparsers.add_parser(
        "execute", help="Execute a jupyter/jupytext notebook and create a HTML report."
    )
    parser_execute.add_argument(
        "notebook",
        help="A jupyter notebook in any format compatible with jupytext.",
    )
    parser_execute.add_argument("report", help="Path to write the HTML report to.")
    parser_execute.add_argument(
        "-k",
        "--kernel_name",
        default="python3",
        help="The kernel used to execute the notebook. [Default: python3]",
        required=False,
    )
    args = parser.parse_args()

    if args.subcommand == "execute":
        nxfvars_execute(args.notebook, args.report, kernel_name=args.kernel_name)
    elif args.subcommand == "version":
        print(__version__)
        sys.exit(0)
    else:
        parser.print_help()

if __name__ == "__main__":
    main()


#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Some classes for setting up a simple chess-bot.
"""

# Package import statements
import numpy as np
from random import sample
from pretty_board import pretty_board
from flavor import flavor_spitter

# Define Constants
color_list = ['white', 'black']
pawn_dir = dict(zip(color_list, [1, -1]))
x_index = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
y_index = [1, 2, 3, 4, 5, 6, 7, 8]
lookup_dict = {} # Use numbers to get letter/number indices
rev_lookup = {} # Use letter/number indices to get numbers
for x in range(8):
    for y in range(8):
        lookup_dict[(x,y)] = x_index[x] + '_' + str(y_index[y])
        rev_lookup['_'.join([x_index[x], str(y_index[y])])] = (x,y)

# Define Functions
def get_btwn(pos, new_pos):
    """Returns list of tuples between pos tuple and new_pos tuple.
    (1,2) and (3,4) would return [(2,3)]"""
    x_btwn = list(range(pos[0] + 1, new_pos[0]))
    # Logic if first x is larger than second x
    x_btwn = list(range(pos[0] - 1, new_pos[0], -1)) if not x_btwn else x_btwn
    y_btwn = list(range(pos[1] + 1, new_pos[1]))
    # Logic if first y is larger than second y
    y_btwn = list(range(pos[1] - 1, new_pos[1], -1)) if not y_btwn else y_btwn
    # Lists must be equal, even if only moving in x or y direction
    x_btwn = [pos[0]] * len(y_btwn) if len(x_btwn) < len(y_btwn) else x_btwn
    y_btwn = [pos[1]] * len(x_btwn) if len(x_btwn) > len(y_btwn) else y_btwn
    return [(x, y) for x, y in zip(x_btwn, y_btwn)]

# Define Classes
class CustArray:
    """A custom array to hold Piece and Square data. Size varies based on the
    information it needs to hold. For in_bound, unobstructed, valid moves, and
    targets this is 27. Move history is more, rest are intuitive."""
    dtype = [('field', (np.str_, 10)), ('x', np.int8), ('y', np.int8)]
    size_dict = {
        'ib_moves': 27, # Most possible moves is 27 by the queen
        'uo_moves': 27,
        'v_moves': 27,
        'hist': 100, # A single piece is not likely to be moved more
        'sq_tgts': 27,
        'backing_up': 15, # 15 allied pieces
        'backups': 15, # 15 allied pieces
        'targets': 8, # Queen and Knight can only attack 8
        'threats': 16, # 16 enemy pieces
        }

    def __init__(self, array_type):
        n = CustArray.size_dict[array_type]
        self.array_type = array_type
        self.array = np.empty((n,),dtype=CustArray.dtype)
        self.len = 0

    def __repr__(self):
        return repr(self.array)

    def __iter__(self):
        self.curr_index = 0
        return self

    def __next__(self):
        if self.curr_index < self.len:
            val = self.array[self.curr_index]
            self.curr_index = self.curr_index + 1
            return val
        else:
            raise StopIteration

    def add(self, obj):
        """Insert an item into next position of array."""
        self.array = np.insert(self.array, self.len, obj)
        self.len += 1

    def reset(self):
        """Reset the array to being filled with zeros."""
        n = CustArray.size_dict[self.array_type]
        self.array = np.empty((n,),dtype=CustArray.dtype)
        self.len = 0

    def filt(self, objs):
        """Return filtered array based on list of (field, val) tuples."""
        filt = (self.array[objs[0][0]] == objs[0][1])
        for i in objs[1:]:
            filt = (filt) & (self.array[i[0]] == i[1])
        return self.array[filt]

    def __getitem__(self, sliced):
        return self.array[sliced]

class ChessSquare:
    """Class representing game squares with info about surroundings."""
    def __init__(self, color, x, y, occ=None):
        self.color = color
        self.x = x
        self.y = y
        self.pos = (x, y)
        self.occ = occ

    def __repr__(self):
        return self.color.title() + " square at " + str(self.pos)

class Piece():
    """Class representing game pieces and holding info about their position."""
    def __init__(self, color, piece_type, x, y):
        self.color = color
        self.type = piece_type
        self.x = x
        self.y = y
        self.pos = (x, y)
        self.symbol = color[0].upper() + "_" + piece_type.title()
        self.kill_list = []
        self.ib_moves = CustArray('ib_moves')
        self.uo_moves = CustArray('uo_moves')
        self.v_moves = CustArray('v_moves')
        self.hist = CustArray('hist')
        self.backups = CustArray('backups')
        self.backing_up = CustArray('backing_up')
        self.targets = CustArray('targets')
        self.threats = CustArray('threats')

    def __repr__(self):
        return self.symbol + " at " + str(self.pos)

    def info(self):
        """Gets attributes for the piece"""

        def ti(tuple_list):
            """takes a tuple turns it into a tuple with a7 style indexing"""
            new_list = []
            for i, j, k in tuple_list:
                new_pos = lookup_dict[(j, k)].split('_')
                new_list += [(i, new_pos[0].upper()+str(new_pos[1]))]
            return new_list

        new_pos = "".join(lookup_dict[self.pos].split('_')).upper()
        print(self.symbol + " on " + new_pos + ".")
        print("\nNote: Valid moves does not consider check!")
        print("Valid moves: " + str(ti(self.v_moves)))
        print("Move history: " + str(ti(self.hist)))
        print("Backed up by: " + str(ti(self.backups)))
        print("Backing up: " + str(ti(self.backing_up)))
        print("Targeting: " + str(ti(self.targets)))
        print("Targeted by: " + str(ti(self.threats)))
        print("Killed: " + str(self.kill_list))

    def get_dest(self, x_diff, y_diff):
        """Returns destination given some difference from the orig. position"""
        return self.x + x_diff, self.y + y_diff

    def get_pawn_ib_moves(self):
        """All possible in-bounds moves for pawn. Includes en passant."""
        assert self.type == "pawn"
        p_num, p_dir = (-1, 's') if self.color == 'black' else (1, 'n')
        moves = {
            p_dir + '_1': self.get_dest(0, p_num),
            p_dir + '_2': self.get_dest(0, p_num * 2),
            p_dir + 'e_1': self.get_dest(1, p_num),
            p_dir + 'w_1': self.get_dest(-1, p_num)
            }
        # Add in-bound moves to ib_moves
        for move, dest in moves.items():
            if max(dest) <= 7 and min(dest) >= 0: # Ensure in-bounds
                self.ib_moves.add((move, *dest))

    def get_rook_ib_moves(self):
        """Get all possible moves for rook (or queen) that stay in-bounds.
        Does not consider castling, which is attributed to the King."""
        assert self.type == 'rook' or self.type == 'queen'
        n_moves = {'n_' + str(i): (self.x, i) for i in range(8) if i < self.y}
        e_moves = {'e_' + str(i): (i, self.y) for i in range(8) if i > self.x}
        s_moves = {'s_' + str(i): (self.x, i) for i in range(8) if i > self.y}
        w_moves = {'w_' + str(i): (i, self.y) for i in range(8) if i < self.x}
        moves = {**n_moves, **e_moves, **s_moves, **w_moves}
        [self.ib_moves.add((move, *dest)) for move, dest in moves.items()]

    def get_bishop_ib_moves(self):
        """Get all possible moves for bishop (or queen) that stay in-bounds."""
        assert self.type == 'bishop' or self.type == 'queen'
        ne_dests = {'ne_' + str(i): (self.x + i, self.y - i) for i in
                    range(1,8) if self.x + i < 8 and self.y - i > -1}
        se_dests = {'se_' + str(i): (self.x + i, self.y + i) for i in
                    range(1,8) if self.x + i < 8 and self.y + i < 8}
        sw_dests = {'sw_' + str(i): (self.x - i, self.y + i) for i in
                    range(1,8) if self.x - i > -1 and self.y + i < 8}
        nw_dests = {'nw_' + str(i): (self.x - i, self.y - i) for i in
                    range(1,8) if self.x - i > -1 and self.y - i > -1}
        moves = {**ne_dests, **se_dests, **sw_dests, **nw_dests}
        [self.ib_moves.add((move, *dest)) for move, dest in moves.items()]

    def get_knight_ib_moves(self):
        """Gets all possible moves for knight that stay in-bounds."""
        assert self.type == 'knight'
        diffs = {'nne_': (1, -2), 'ene_': (2, -1), 'ese_':(2, 1),
                 'sse_': (1, 2), 'ssw_': (-1, 2), 'wsw_':(-2, 1),
                 'wnw_': (-2, -1), 'nnw_': (-1, -2)}
        moves = {move: self.get_dest(*diff) for move, diff in diffs.items()}
        # Add in-bound moves to ib_moves
        for move, dest in moves.items():
            if max(dest) <= 7 and min(dest) >= 0: # Ensure in-bounds
                self.ib_moves.add((move, *dest))

    def get_queen_ib_moves(self):
        assert self.type == 'queen'
        self.get_bishop_ib_moves()
        self.get_rook_ib_moves()

    def get_king_ib_moves(self):
        """Gets all possible non-castle moves for king that stay in-bounds."""
        assert self.type == 'king'
        diffs = {'n_1': (0, -1), 'ne_1': (1, -1), 'e_1':(1, 0),
                 'se_1': (1, 1), 's_1': (0, 1), 'sw_1':(-1, 1),
                 'w_1': (-1, 0), 'nw_1': (-1, -1)}
        moves = {move: self.get_dest(*diff) for move, diff in diffs.items()}
        # Add in-bound moves to ib_moves
        for move, dest in moves.items():
            if max(dest) <= 7 and min(dest) >= 0: # Ensure in-bounds
                self.ib_moves.add((move, *dest))

    def get_ib_moves(self):
        """Gets all in-bound moves for a piece based on the piece type."""
        self.ib_moves.reset()
        move_dict = {
        'pawn':self.get_pawn_ib_moves,
        'rook':self.get_rook_ib_moves,
        'knight':self.get_knight_ib_moves,
        'bishop':self.get_bishop_ib_moves,
        'queen':self.get_queen_ib_moves,
        'king':self.get_king_ib_moves
        }
        return move_dict[self.type]()

class Chessboard:
    """A class used to hold squares and pieces as well as help those objects
    understand their surroundings. Also handles moves."""
    def __init__(self, turn='white', player_color = 'white'):
        flipper = 0 # flips between 0 and 1 each iteration
        row_list = []
        for y in range(8):
            square_list = []
            for x in range(8):
                square = ChessSquare(color_list[flipper], x, y)
                square_list = square_list + [square]
                flipper = flipper * -1 + 1
            row_list = row_list + [square_list]
        self.board = np.array(row_list)
        self.turn = turn
        self.nonturn = 'black' if turn == 'white' else 'white'
        self.turn_num = 1
        self.last_capture_turn = 1
        self.alive = []
        self.player_color = player_color
        self.move_history = []

    def __getitem__(self, tup):
        return self.board[tup[1],tup[0]]

    def set_up_board(self):
        """Puts pieces in starting positions."""
        piece_list = ['rook', 'knight', 'bishop', 'queen',
                      'king', 'bishop', 'knight', 'rook']
        for x, piece in enumerate(piece_list):
            for y, color in zip([0, 7], color_list):
                self[x,y].occ = Piece(color, piece, x, y)
            for y, color in zip([1, 6], color_list):
                self[x,y].occ = Piece(color, 'pawn', x, y)

    def set_up_board_randomly(self):
        """Puts starting pieces in randomly assigned positions. For testing."""
        full_pos_list = [(x,y) for x in range(8) for y in range(8)]
        pos_list = sample(full_pos_list, 32)
        piece_list = ['rook','knight','bishop']*2+['king','queen']+['pawn']*8
        color_list = np.repeat(sample(color_list, 2), 16)
        for color, pos, piece in zip(color_list, pos_list, piece_list*2):
            self[pos].occ=Piece(color,piece,pos[0],pos[1])
            self[pos].occ.has_moved = True

    def view(self, reverse = False):
        """Returns nice-looking view of board. Not used in calculations."""
        pretty_board(self, reverse)

    def get_pieces(self, piece_type=[], color=[]):
        """Returns a list of pieces meeting the parameters (and, not or).
        Ex: if you provide king, queen and no color, it gets 4 pieces.
        Ex: If you provide king, queen and white, it gets 2 pieces."""
        piece_list = []
        for x in range(8):
            for y in range(8):
                if self[x,y].occ:
                    piece_list = piece_list + [self[x,y].occ]
        if color:
            for piece in piece_list.copy():
                if piece.color not in color:
                    piece_list.remove(piece)
        if piece_type:
            for piece in piece_list.copy():
                if piece.type not in piece_type:
                    piece_list.remove(piece)
        return piece_list

    def get_alive_pieces(self):
        self.alive = self.get_pieces(
            piece_type=['pawn', 'rook', 'knight','bishop', 'queen', 'king']
        )

    def get_ib_moves(self):
        """Given each piece's position, get all moves for each piece that are
        within the boundaries of the board. Ignores castling."""
        for piece in self.alive:
            piece.ib_moves.reset()
            piece.get_ib_moves()

    def get_unobstructed_moves(self):
        """Given the board's pieces', each piece has its uo_moves attribute
        populated based on which moves have no pieces between it and dest.
        Knight/King/Pawn will always have the same ib_moves and uo_moves."""
        for piece in self.alive:
            if piece.type in ['knight', 'king']:
                for move, x, y in piece.ib_moves:
                    piece.uo_moves.add((move, x, y))
            else:
                for move, x, y in piece.ib_moves:
                    add = True
                    for btwn_pos in get_btwn(piece.pos, (x, y)):
                        if self[btwn_pos].occ:
                            add = False
                            break
                    if add: piece.uo_moves.add((move, x, y))

    def get_valid_pawn_moves(self, piece):
        """Get legal moves for pawns including en passant."""
        assert piece.type == 'pawn'
        for move, x, y in piece.uo_moves:
            # Advance Single Space
            if (move == 's_1' or move == 'n_1') and not self[x, y].occ:
                piece.v_moves.add((move, x, y))
            # Advance Two Spaces
            elif move.split('_')[-1] == '2' and piece.hist.len == 0:
                if not self[x, y].occ: piece.v_moves.add((move, x, y))
            elif len(move) == 4: # Only look at diagonal moves which have len 4
                occ = self[x, y].occ
                # Attacks
                if occ and occ.color != piece.color:
                    piece.v_moves.add((move, x, y))
                    piece.targets.add((occ.type, x, y))
                    occ.threats.add((piece.type, piece.x, piece.y))
                    continue
                elif occ and occ.color == piece.color:
                    occ.backups.add((piece.type, piece.x, piece.y))
                    piece.backing_up.add((occ.type, x, y))
                # En Passant
                elif not occ:
                    # Identify east/west neighbor in attack direction
                    neighbor = self[x, piece.y].occ
                    if (neighbor and neighbor.color != piece.color and
                        neighbor.type == 'pawn' and
                        neighbor.hist.len == 1 and
                        '2' in list(iter(neighbor.hist))[0][0]):
                        piece.v_moves.add((move, x, y))
                        piece.targets.add((neighbor.type, x, y))
                        neighbor.threats.add((piece.type, piece.x, piece.y))

    def get_valid_other_moves(self, piece):
        "Get legal moves for rooks excluding castles."""
        assert piece.type != 'pawn'
        for move, x, y in piece.uo_moves:
            occ = self[x, y].occ
            # If square is occupied by the enemy
            if occ and occ.color != piece.color:
                piece.v_moves.add((move, x, y))
                piece.targets.add((occ.type, x, y))
                occ.threats.add((piece.type, piece.x, piece.y))
            # If square is occupied by ally
            elif occ and occ.color == piece.color:
                occ.backups.add((piece.type, piece.x, piece.y))
                piece.backing_up.add((occ.type, x, y))
            # If square is empty
            elif not occ:
                piece.v_moves.add((move, x, y))

    def get_valid_moves(self):
        """Given the board's pieces', gets all legal moves for each piece.
        Does not consider pinned pieces, which are accounted for later."""
        move_dict = {
        'pawn':self.get_valid_pawn_moves,
        'rook':self.get_valid_other_moves,
        'knight':self.get_valid_other_moves,
        'bishop':self.get_valid_other_moves,
        'queen':self.get_valid_other_moves,
        'king':self.get_valid_other_moves
        }
        for piece in self.alive:
            move_dict[piece.type](piece)

    def are_squares_safe(self, square_list, color):
        """False if color has valid moves targeting any of squarelist."""
        pos_list = [square.pos for square in square_list]
        color_pieces = self.get_pieces(color=[color])
        for piece in color_pieces:
            for move, x, y in piece.v_moves:
                if (x, y) in pos_list:
                    return False # threatened
        return True

    def get_valid_castles(self):
        # Loop through what OUGHT to be the kings. Confirmed if hist.len==0.
        for k in [self[4,0].occ, self[4,7].occ]:
            if (not k or k.hist.len != 0):
                continue
            y = k.y
            color = list(set(['black','white']) - set([k.color]))[0]
            # Loop through what ought to be the rook's matching k's color
            rooks = [self[0,y].occ, self[7,y].occ]
            safe_sqs = [[2, 3, 4], [6, 5, 4]]
            emp_sqs = [(not self[1,y].occ and not self[2,y].occ
                       and not self[3,y].occ),
                       (not self[5,y].occ and not self[6,y].occ)]
            moves = ['c_q','c_k']
            # First element of each list has info for queenside castling
            for r, sqs, emp, move in zip(rooks, safe_sqs, emp_sqs, moves):
                if (not r or r.hist.len != 0):
                    continue
                if not emp:
                    continue
                square_list = [self[sq_x, y] for sq_x in sqs]
                if self.are_squares_safe(square_list, color):
                    k.v_moves.add((move, sqs[0], y))

    def reset_info(self):
        """Resets most information about the board's pieces."""
        for piece in self.alive:
            piece.uo_moves.reset()
            piece.v_moves.reset()
            piece.backups.reset()
            piece.backing_up.reset()
            piece.targets.reset()
            piece.threats.reset()

    def full_set_up(self, mode="standard"):
        """Set up board and generate all valid moves. Mode can be "random"."""
        if mode == "standard":
            self.set_up_board()
        elif mode == "random":
            self.set_up_board_randomly()
        self.get_alive_pieces()
        self.get_ib_moves()
        self.get_unobstructed_moves()
        self.get_valid_moves()
        self.get_valid_castles()

    def move_piece(self, piece, dest, validate=True, printer = False,
                   human=True):
        """Move piece, potentially capture, and update all values."""
        # Validate move
        if validate:
            assert piece
            assert dest in [(x, y) for move, x, y in piece.v_moves]
            assert piece.color == self.turn
        # Get move name
        move = piece.v_moves.filt([('x',dest[0]),('y',dest[1])])[0][0]
        # Format print statement
        origin_string = lookup_dict[piece.pos].replace('_',"").upper()
        dest_string = lookup_dict[dest].replace('_',"").upper()
        statement = "Moved " + piece.symbol + " from " + origin_string + \
                    " to " + dest_string + ". "
        dest_piece = self[dest].occ
        flavor = False
        if dest_piece: # Handle capture
            statement = statement + dest_piece.symbol + " has been captured!"
            self.alive.remove(dest_piece)
            self.move_history += [piece.symbol + ' ' + origin_string + ' > ' \
                                  + dest_string + ': ' + dest_piece.symbol \
                                  + " captured."]
            self.last_capture_turn = self.turn_num
            piece.kill_list += [dest_piece]
            if self.turn != self.player_color:
                flavor = True
        else:
            self.move_history += [piece.symbol + ' ' + origin_string + ' > ' + \
                                  dest_string + ': No capture.']
        # Update board information
        self[piece.pos].occ = None
        self[dest].occ = piece
        self.turn, self.nonturn = self.nonturn, self.turn
        self.turn_num += 1
        # Update piece information
        piece.x, piece.y = dest
        piece.pos = dest
        piece.hist.add((piece.v_moves.filt([('x',dest[0]),('y',dest[1])])))
        piece.get_ib_moves()
        # Handle castling
        if move == 'c_k' or move == 'c_q':
            if move == 'c_k':
                if printer: print("Kingside castle!")
                rook = self[7, piece.y].occ
                new_dest = (5, piece.y)
            elif move == 'c_q':
                if printer: print("Queenside castle!")
                rook = self[0, piece.y].occ
                new_dest = (3, piece.y)
            self.turn, self.nonturn = self.nonturn, self.turn
            self.turn_num -= 1
            self.move_piece(rook, new_dest, False, False)
            self.move_history = self.move_history[:-1]
        # Handle pawn promotion
        if piece.type == 'pawn' and dest[1] in [0, 7]:
            if human == False:
                promotion = 'queen'
                if printer: print(piece.symbol + " promoted to queen!")
            else:
                promotion = input("Pawn promotion! What piece would you like? "
                              "Enter the name of the piece \nyou want, such "
                              "as 'queen' or 'knight' without quotes: ")
            if promotion.lower() == 'quit':
                quit()
            elif promotion.lower() in ['queen', 'rook', 'knight', 'bishop']:
                invalid_type = False
            else:
                invalid_type = True
            while invalid_type:
                promotion = input("Try again. Name the type of piece you want "
                                  "your pawn to be promoted to.")
                if promotion.lower() == 'quit':
                    quit()
                elif promotion.lower() in ['queen', 'rook', 'knight', 'bishop']:
                    invalid_type = False
            new_queen = Piece(piece.color, promotion, piece.x, piece.y)
            self[piece.x, piece.y].occ = new_queen
            self.alive += [new_queen]
            new_queen.get_ib_moves()
            del piece
        # Update board
        self.reset_info()
        self.get_unobstructed_moves()
        self.get_valid_moves()
        self.get_valid_castles()
        # Check if in check for printing purposes
        check = False
        try:
            king = self.get_pieces(['king'],[self.turn])[0]
            if king.threats.len > 0:
                check = True
        except:
            pass
        # Display
        if printer:
            if check:
                statement = "Check! " + statement
            print(statement)
            if self.player_color == 'white':
                self.view(True)
            else:
                self.view(False)
            if flavor:
                flavor_spitter(dest_piece.type)
        return check

    def game_over_check(self):
        """The computer wins if the player loses all their pieces except the
        king, and the AI has a certain number of specific pieces. This is to
        avoid tedious endgame states (i.e. you can force a win with a rook
        and a king, but there is no need to go through the exercise.) Also
        ends the game if a certain number of turns have passed. Returns boolean
        for whether the game will end plus a reason."""
        # 50 Move Rule
        if self.turn_num - self.last_capture_turn >= 100:
            return True, "The game is a draw due to the 50-move rule."
        # AI has specific pieces and human has just a king
        reason = "You resign. Checkmate is trivial at this point."
        ai_pieces = []
        human_pieces = []
        for i in self.alive:
            if i.color != self.player_color:
                ai_pieces += [i.type]
            else:
                human_pieces += [i.type]
        if len(human_pieces) > 1:
            return False, ""
        else:
            if 'queen' in ai_pieces or 'rook' in ai_pieces:
                return True, reason
            elif ai_pieces.count('bishop') >= 2:
                return True, reason
            elif 'knight' in ai_pieces and 'bishop' in ai_pieces:
                # There are rare cases where this can't win, but I don't care.
                return True, reason
            else:
                return False, ""