# This is a Python version of the game Deal or No Deal.
import math
import random
import statistics
import time

import sqlite3 as sl
db = "dond.db"

id_list = []  # new id list

class datarow:
    # just a super class from which game and round inherit because both are types of data
    def __init__(self, game_id, player_id, game_mode):
        self.game_id = game_id
        self.player_id = player_id
        self.game_mode = game_mode

class game(datarow):
    """
    holds data about one played game
    """
    def __init__(self, game_id, player_id, game_mode, end_result, stop_round, winnings):
        super().__init__(game_id, player_id, game_mode)
        self.stop_round = stop_round
        self.end_result = end_result
        self.winnings = winnings

    def save(self):
        # append to data file
        # with open("played_data.txt", "a") as f_in:
        #     f_in.write(str(self.game_id) + "\t" + str(self.player_id) + "\t" + "\t" + str(self.game_mode) + "\t" +
        #                self.end_result + "\t" + str(self.stop_round) + "\t" + str(self.winnings) + "\n")
        # open a connection
        conn = sl.connect(db)
        # get a cursor
        curs = conn.execute("INSERT INTO games (game_id, player_id, game_mode, end_result, stop_round, winnings)"
                            f"VALUES ({self.game_id}, {self.player_id}, {self.game_mode}, '{self.end_result}', {self.stop_round}, {self.winnings})")
        conn.commit()
        curs.close()
        conn.close()

class _round(datarow):
    """
    holds data about one round played by a player
    """
    def __init__(self, game_id, player_id, game_mode, round, offer, remaining_cases):
        super().__init__(game_id, player_id, game_mode)
        self.round = round
        self.offer = offer
        self.remaining_cases = remaining_cases

    def save(self):
        # append to data file
        # with open("round_data.txt", "a") as f_in:
        #     f_in.write(str(self.game_id) + "\t" + str(self.player_id) + "\t" + "\t" + str(self.game_mode) + "\t" +
        #                str(self.round) + "\t" + str(self.bankers_offer) + "\t" + str(self.remaining_cases) + "\n")
        conn = sl.connect(db)
        curs = conn.execute("INSERT INTO rounds (game_id, player_id, game_mode, round, offer, remaining_cases)"
                            f"VALUES ({self.game_id}, {self.player_id}, {self.game_mode}, {self.round}, {self.offer}, '{self.remaining_cases}')")
        conn.commit()
        curs.close()
        conn.close()

class error(datarow):
    """
    holds data about a player's offer entry mistake
    """
    def __init__(self, game_id, player_id, game_mode, round, error_descr):
        super().__init__(game_id, player_id, game_mode)  # won't use game_mode
        self.round = round
        self.error_descr = error_descr

    def save(self):
        # # append to data file
        # with open("errors.txt", "a") as f_in:
        #     f_in.write(str(self.game_id) + "\t" + str(self.player_id) + "\t" + str(self.round) + "\t" +
        #                str(self.error_descr) + "\n")
        conn = sl.connect(db)
        stmt = "INSERT INTO errors (game_id, player_id, round, error_descr) " \
                f"VALUES ({self.game_id}, {self.player_id}, {self.round}, '{self.error_descr}')"
        curs = conn.execute(stmt)
        conn.commit()
        curs.close()
        conn.close()

def pick_case(case_id_list_display, game_id, player_id, game_mode, round):
    """
    this function returns a case chosen by the player
    :param case_id_list_display: list of cases left
    :return: chosen case
    """
    print("Enter a case:")
    keepAsking = True
    while keepAsking:
        case_choice = input("> ").strip() # remove whitespace
        if case_choice.isnumeric(): # if a number was entered
            case_choice = int(case_choice) # convert to int
            if case_choice < 1 or case_choice > 26: # outside case id range
                print("Please enter a valid number.")
                new_error = error(game_id, player_id, game_mode, round, "chose case #" + str(case_choice) + " not in range")
                new_error.save()
            else:
                if case_choice in case_id_list_display: # not in list of cases left
                    keepAsking = False
                    return case_choice
                else: # is not in list of cases left
                    print("Case", case_choice, "was already chosen.")
                    new_error = error(game_id, player_id, game_mode, round, "chose case #" + str(case_choice) + " not available")
                    new_error.save()
        else:
            new_error = error(game_id, player_id, game_mode, round, "chose case #" + str(case_choice) + " not numeric")
            new_error.save()
            print("Please enter a valid number.")

def remove_id_from_display(chosen_case, case_id_list_display):
    """
    This function replaces the case id in the display with a space
    :param chosen_case: id of case choen
    :param case_id_list_display:
    :return: nothing
    """
    for id in case_id_list_display: # go through every id in the list
        if id == chosen_case: # find the one that matches players_case
            if id < 10:
                case_id_list_display[case_id_list_display.index(id)] = " " # one char, one space
            else:
                case_id_list_display[case_id_list_display.index(id)] = "  " # 2 chars, 2 spaces

def remove_prize_from_display(chosen_case, list_of_prizes_display, case_values):
    """
    this function replaces prizes in the display with spaces
    :param chosen_case: case id
    :param list_of_prizes_display: the thing being displayed
    :param case_values: to match case id to prize value
    :return: none
    """
    value_to_remove = case_values[chosen_case] # whatever prize at the case id in the dictionary
    for prize in list_of_prizes_display: # loop through prizes in the display
        if prize == value_to_remove: # find the prize
            spacestring = ""
            for i in range(len(str(prize))):
                spacestring += " " # make the string the same number of characters as the prize it's replacing
            list_of_prizes_display[list_of_prizes_display.index(prize)] = spacestring # replace it with a space

def displayBoard(case_id_list_display, list_of_prizes_display):
    """
    This function prints the board and prizes left
    This was adapted from the function written by @DrewSkotarczak on replit
    :param case_id_list_display: list with case id's left
    :param list_of_prizes_display: list with prizes left
    :return: nothing
    """
    print("Board:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRemaining Prizes:")
    print(' -----      -----      -----      -----      -----      -----      ----- ')
    print('| ', case_id_list_display[0], ' |    | ', case_id_list_display[1], ' |    | ', case_id_list_display[2], ' |    | ',
    case_id_list_display[3], ' |    | ', case_id_list_display[4], ' |    | ', case_id_list_display[5], ' |    | ',
    case_id_list_display[6], ' |         ', list_of_prizes_display[0], '           ', list_of_prizes_display[13])
    print(' -----      -----      -----      -----      -----      -----      -----             ', list_of_prizes_display[1],
          '           ', list_of_prizes_display[14])
    print('                                                                                ', list_of_prizes_display[2],
          '          ', list_of_prizes_display[15])
    print('      -----      -----      ------     ------     ------     ------                 ', list_of_prizes_display[3],
          '          ', list_of_prizes_display[16])
    print('     | ', case_id_list_display[7], ' |    | ', case_id_list_display[8], ' |    | ', case_id_list_display[9], ' |   | ',
          case_id_list_display[10], ' |   | ', case_id_list_display[11], ' |   | ', case_id_list_display[12], ' |                ',
          list_of_prizes_display[4], '          ', list_of_prizes_display[17])
    print('      -----      -----      ------     ------     ------     ------                 ', list_of_prizes_display[5],
          '          ', list_of_prizes_display[18])
    print('                                                                                ', list_of_prizes_display[6],
          '         ', list_of_prizes_display[19])
    print(' ------     ------     ------     ------     ------     ------     ------          ', list_of_prizes_display[7],
          '         ', list_of_prizes_display[20])
    print('| ', case_id_list_display[13], ' |   | ', case_id_list_display[14], ' |   | ', case_id_list_display[15], ' |   | ',
          case_id_list_display[16], ' |   | ', case_id_list_display[17], ' |   | ', case_id_list_display[18], ' |   | ',
          case_id_list_display[19], ' |         ', list_of_prizes_display[8], '         ', list_of_prizes_display[21])
    print(' ------     ------     ------     ------     ------     ------     ------          ', list_of_prizes_display[9],
          '         ', list_of_prizes_display[22])
    print('                                                                                ', list_of_prizes_display[10],
          '         ', list_of_prizes_display[23])
    print('      ------     ------     ------     ------     ------     ------                ', list_of_prizes_display[11],
          '         ', list_of_prizes_display[24])
    print('     | ', case_id_list_display[20], ' |   | ', case_id_list_display[21], ' |   | ', case_id_list_display[22], ' |   | ',
          case_id_list_display[23], ' |   | ', case_id_list_display[24], ' |   | ', case_id_list_display[25], ' |               ',
          list_of_prizes_display[12], '        ', list_of_prizes_display[25])
    print('      ------     ------     ------     ------     ------     ------ ')

def displayPrizes(list_of_prizes_display):
    """
    This function prints the prizes left
    :param list_of_prizes_display: list with prizes left
    :return: nothing
    """
    print("Remaining Prizes:")
    print()
    print(list_of_prizes_display[0], '           ', list_of_prizes_display[13])
    print(list_of_prizes_display[1], '           ', list_of_prizes_display[14])
    print(list_of_prizes_display[2], '          ', list_of_prizes_display[15])
    print(list_of_prizes_display[3], '          ', list_of_prizes_display[16])
    print(list_of_prizes_display[4], '          ', list_of_prizes_display[17])
    print(list_of_prizes_display[5], '          ', list_of_prizes_display[18])
    print(list_of_prizes_display[6], '         ', list_of_prizes_display[19])
    print(list_of_prizes_display[7], '         ', list_of_prizes_display[20])
    print(list_of_prizes_display[8], '         ', list_of_prizes_display[21])
    print(list_of_prizes_display[9], '         ', list_of_prizes_display[22])
    print(list_of_prizes_display[10], '         ', list_of_prizes_display[23])
    print(list_of_prizes_display[11], '         ', list_of_prizes_display[24])
    print(list_of_prizes_display[12], '        ', list_of_prizes_display[25])
    print()

def fill_case_values(case_id_list, list_of_prizes, case_values):
    """
    randomly assigns each case id to a prize and saves it to case_values dictionary
    :param case_id_list: ints 1 to 26
    :param list_of_prizes: list of prizes
    :param case_values: empty dictionary
    :return: none
    """
    list_of_prizes_copy = [] # create a copy so that the prizes don't actually get removed from list_of_prizes
    for prize in list_of_prizes:
        list_of_prizes_copy.append(prize)
    for case_id in case_id_list: # go through every case id in the list
        value = random.choice(list_of_prizes_copy) # randomly select one of the prizes
        case_values[case_id] = value # assign that value to the case id
        list_of_prizes_copy.remove(value) # remove that value from the list of prizes so it doesn't get picked again

def eliminate_case(num_cases_to_eliminate, case_id_list_display, list_of_prizes_display, case_values, game_id, player_id, game_mode, round):
    """
    this function is called everytime the player chooses a case to remove from the board
    :param num_cases_to_eliminate: number of times a case will be chosen
    :param case_id_list_display: for display
    :param list_of_prizes_display: for display
    :param case_values: for display
    :return: none
    """
    print("You must choose ", num_cases_to_eliminate, "case(s) to eliminate this round")
    for i in range(num_cases_to_eliminate):  # do this num_cases_to_eliminate times
        eliminated_case = pick_case(case_id_list_display, game_id, player_id, game_mode, round)  # pick case
        print("You chose case # " + str(eliminated_case))
        print("Opening case:")
        drumroll()
        print("The", case_values[eliminated_case], "point prize has been eliminated from the board.")
        userinput = input("Press enter to continue.\n")
        # update displays:
        remove_id_from_display(eliminated_case, case_id_list_display)
        remove_prize_from_display(eliminated_case, list_of_prizes_display, case_values)
        if (num_cases_to_eliminate - 1 - i) != 0: # if that wasn't the last case to be chosen
            displayBoard(case_id_list_display, list_of_prizes_display)
            print("Choose", num_cases_to_eliminate - 1 - i, "more.")

def get_prizes_left(list_of_prizes_display):
    """
    This function creats a list of remaining prizes without the blank spaces
    :param list_of_prizes_display: list of remaining prizes with blank spaces
    :return: prizes_left, no blank spaces
    """
    prizes_left = []
    for prize in list_of_prizes_display: # go through display prize list
        if str(prize).isnumeric() or prize == 0.01: # if entry contains number (ie still in play)
            prizes_left.append(prize) # add to list
    return prizes_left

def get_bank_offer(list_of_prizes_display, game_mode, round):
    """
    This function calculates the banker's offer.
    :param list_of_prizes_display: list of prizes displayed
    :return: banker's offer
    """
    prizes_left = get_prizes_left(list_of_prizes_display)
    sum = 0
    for prize in prizes_left:
        sum += prize

    ev = sum / len(prizes_left)  # find average case value
    # basic 0.8 algorithm:
    if game_mode == 1:
        return int(ev*0.8)
    # banker's offer algorithm from Chen and John 2018
    elif game_mode == 2 or game_mode == 4:
        sd = statistics.pstdev(prizes_left)
        cv = sd/ev
        log10ev = math.log10(ev)
        log10bo = 0.195 + 0.991*log10ev - 0.057*cv - 0.037*len(prizes_left)
        bo = math.pow(10, log10bo)
        return int(bo)
    # constantly increase ratio
    else: # game mode 3
        ratio = 0.5 + round/20.0
        return int(ev*ratio)

def drumroll():
    # creates a little countdown for dramatic effect
    for i in range(3):
        time.sleep(0.25)
        print(".")
        time.sleep(0.25)

def create_id():
    """
    This function generates a number 10000-99999 as a unique game id.
    It checks if ID was used before.
    :return: a unique ID
    """
    # with open("played_data.txt", "r") as f_in: # from past game data
    #     for line in f_in: # loop through lines
    #         id = line[:5] # set id as first 5 characters of the line
    #         id_list.append(id)

    # TODO: retrieve list of all previously used id's
    conn = sl.connect(db)
    curs = conn.execute("SELECT game_id, player_id FROM games")
    cols = curs.fetchall()
    game_ids = [cols[i][0] for i in range(len(cols))]
    player_ids = [cols[i][1] for i in range(len(cols))]
    id_list = game_ids + player_ids

    generateNew = True # true if need to generate a new id
    while generateNew: # while id has been used
        id = random.randrange(10000, 100000)  # get a number 10000-99999
        if id in id_list:
            generateNew = True
        else: # unique id was generated
            return id
            generateNew = False # no need to generate another one

def early_round_eliminate_cases(r, case_id_list_display, list_of_prizes_display, case_values, game_id, player_id, game_mode):
    """
    eliminating cases during rounds 1-6
    :param r: round (int)
    :param case_id_list_display: list of cases for gui display
    :param list_of_prizes_display: list of prizes on right hand side
    :param case_values: dict w/case values
    :return: none
    """
    print("Round", r, "\n")
    if r < 6:  # first 5 rounds, when more than 1 case will have to be eliminated
        num_cases_to_eliminate = 7 - r  # number of cases decreases as rounds increase
    elif r >= 6:  # at round 6 and later
        num_cases_to_eliminate = 1  # only 1 to eliminate
    displayBoard(case_id_list_display, list_of_prizes_display)  # show board
    eliminate_case(num_cases_to_eliminate, case_id_list_display, list_of_prizes_display,
                   case_values, game_id, player_id, game_mode, r)  # remove needed cases

def get_player_offer(case_values, game_id, player_id, round):
    """
    obtains player offer
    :return: player offer (int)
    """
    userinput = input("You will make an offer to the banker. Press enter to continue.\n")
    print("Enter an offer for the banker. If the banker accepts, you will win whatever amount of points you have entered.\
    \nIf your offer is too high, the banker will reject your offer.")
    player_offer = input("> ")
    while not player_offer.isnumeric(): # while non-int
        print("Please enter an integer.")
        player_offer = input("> ")
    while (int(player_offer) > max(case_values.values())) or (int(player_offer) < min(case_values.values())):
        if int(player_offer) > max(case_values.values()): # greater than max possible prize
            new_error = error(game_id, player_id, 4, round, "player offer > max prize = " +
                              str(max(case_values.values())))
            new_error.save()
            print("Your offer must be less than the biggest prize left in play. Try again.")
            player_offer = input("> ")
        elif int(player_offer) < min(case_values.values()): # less than min possible prize
            new_error = error(game_id, player_id, 4, round, "player offer < min prize = " +
                              str(min(case_values.values())))
            new_error.save()
            print("Your offer must be greater than the smallest prize left in play. Try again.")
            player_offer = input("> ")
    return int(player_offer)

def deal(offer, case_values, players_case, game_id, player_id, r, game_mode):
    time.sleep(1)
    print("You won", offer, "points!")
    time.sleep(1)
    print("Your case contained", case_values[players_case])
    print()
    new_game = game(game_id, player_id, game_mode, "D", r, offer)  # create game object
    new_game.save()  # write that object into the file

def print_instructions(game_mode):
    print("\nHere's how the game works: \
    \nThere are 26 briefcases on the board. Each case contains an unknown amount of points,\n \
ranging from 0.01 to 1,000,000 points. An unopened case will look like this:")
    print(" ---- ")
    print("| 19 |")
    print(" ---- ")
    print("The remaining prizes will be displayed to the right of the cases.")
    print("Press enter to continue.")
    userinput = input("")
    print("At the very beginning, you will choose one case to be your case. \n\
You will not know what is in it until the end of the game.\n \
It will be labeled on the board with an asterisk:")
    print(" --- ")
    print("| * |")
    print(" --- ")
    print("Press enter to continue. ")
    userinput = input("") # ask something
    print("Every round, you will have to eliminate a number of cases from the board.\n\
The case will be opened, revealing the number of points it contained. By eliminating a\n\
case, you eliminate the possibility of winning that prize.\nPress enter to continue. ")
    userinput = input("")
    if game_mode == 4:
        print("PLAYER'S OFFER MODE: You will make an offer at the end of every round after\nyou have \
eliminated cases. The banker will choose to accept\nyour deal and you will win whatever \
amount you offered (at which point the game ends),\nor he will decline your offer, and you must continue \
eliminating more cases from the board.\nPress enter to continue")
        userinput = input("")
        print("When/if the banker accepts the your offer, you will\n \
also find out how much was in the case you originally chose.\nPress enter to continue. ")
        userinput = input("")
    else:
        print("BANKER'S OFFER MODE: The banker will make you an offer at the end of\nevery round after you have \
eliminated cases. You may either choose \nto accept his deal and walk away with whatever \
amount he offers (at which point the game ends), \nor you can decline his offer and continue \
eliminating more cases from the board.\nPress enter to continue")
        userinput = input("")
        print("When/if you accept the banker’s offer, you will \
also find out how much was in the case you originally chose.\nPress enter to continue. ")
        userinput = input("")
    print("If you keep eliminating cases until there is one left on the board, \nyou can choose \
to either walk away with the case you originally chose or \nswitch with the one left on the \
board. Whichever one you choose will contain your prize winnings. \
\nPress enter to start the game. ")
    userinput = input("")

def play(game_mode, first_time, player_id):
    print("Press enter to begin the ", end="")
    if first_time:
        print("first", end="")
    else:
        print("second", end="")
    print(" game.")
    userinput = input("")
    game_id = create_id()
    if game_mode == 1:
        print("You are now playing the banker's offer version")
        game_mode = random.randint(1, 3)  # pick from 3 algorithms to generate banker's offer
    elif game_mode == 2:
        print("You are now playing the player's offer version.")
        game_mode = 4 # no alg
    time.sleep(1)

    print_instructions(game_mode)

    # INITIALIZE STUFF

    # create a list of case id's
    case_id_list = []  # will contain ints 1-26
    case_id_list = [i for i in range(1, 27)] # populate with ints 1-26

    # create copy for display
    case_id_list_display = [case_id for case_id in case_id_list]
        # in this copy, the case id's will be replaced by spaces once chosen by the player

    list_of_prizes = [0.01, 1, 5, 10, 25, 50, 75, 100, 200, 300, 400, 500, 750, 1000, 5000, 10000, 25000, 50000, 75000,
                      100000, 200000, 300000, 400000, 500000, 750000, 1000000]
    case_values = {}  # dict with key = case_id, value=prize values
    fill_case_values(case_id_list, list_of_prizes, case_values)  # randomly populate cases

    list_of_prizes_display = []  # create a copy so that the prizes don't actually get removed from list_of_prizes
    for prize in list_of_prizes:
        list_of_prizes_display.append(prize)

    # player picks case
    displayBoard(case_id_list_display, list_of_prizes_display)
    print("Pick your case. You'll keep this case for the whole game.")
    players_case = pick_case(case_id_list_display, game_id, player_id, game_mode, 0)
    print("You chose case #", players_case, "\n")
    userinput = input("Press enter to begin.\n")
    if players_case < 10:
        case_id_list_display[case_id_list_display.index(players_case)] = "*"  # to distinguish it in display
    else:
        case_id_list_display[case_id_list_display.index(players_case)] = "* " # adjust for 2 digits
    remove_id_from_display(players_case, case_id_list_display)

    # START ROUNDS

    r = 1  # initialize round number
    gameOn = True  # stays true until player accepts deal or there is one case left
    while gameOn:
        if r < 10:
            early_round_eliminate_cases(r, case_id_list_display, list_of_prizes_display, case_values,
                                        game_id, player_id, game_mode)  # play rounds 1-6
            bank_offer = get_bank_offer(list_of_prizes_display, game_mode, r)  # obtain bank offer
            if game_mode == 4:  # player offer mode
                displayPrizes(list_of_prizes_display)
                player_offer = get_player_offer(case_values, game_id, player_id, r)  # obtain player offer
                offer = player_offer  # so it can be saved to a new round
                print("Banker deciding...")
                drumroll()
                if player_offer < bank_offer / 3:  # extremely low offer
                    # deal
                    print("Offer accepted.")
                    winnings = player_offer
                    gameOn = False  # stop looping through the rounds
                    deal(player_offer, case_values, players_case, game_id, player_id, r, game_mode)
                else:
                    # no deal
                    print("Offer rejected.")
                    time.sleep(1)
            else:  # bank offer mode
                displayPrizes(list_of_prizes_display)
                print("Banker making offer")
                drumroll()
                print("Offer:", bank_offer)
                offer = bank_offer  # so it can be saved to a new round
                dond = "m"  # get into while loop
                while dond != "1" and dond != "0":
                    dond = input("Deal or No Deal? Enter 1 for Deal. Enter 0 for No Deal.\n> ")
                    if dond == "1":
                        print("You chose Deal.")
                        winnings = bank_offer
                        gameOn = False  # stop looping through the rounds
                        deal(bank_offer, case_values, players_case, game_id, player_id, r, game_mode)
                    elif dond == "0":
                        print("You chose No Deal.")
                        userinput = input("Press enter to continue.\n")
                        displayBoard(case_id_list_display, list_of_prizes_display)
                    else:
                        new_error = error(game_id, player_id, game_mode, r, "entered neither 1 or 0 for DOND")
                        new_error.save()
                        print("Please enter either 1 or 0. ")
            # save new round data
            new_round = _round(game_id, player_id, game_mode, r, offer, get_prizes_left(list_of_prizes_display))
            new_round.save()
        else:
            # find last case:
            for case_id in case_id_list_display:
                if str(case_id).isnumeric():
                    lastcase = case_id
            print("There is 1 case left on the board, case # " + str(lastcase) + " . You chose case #", players_case,
                  "at the beginning.")
            print(
                "Do you wish to walk away with the amount in your case or switch it with case # " + str(lastcase) + "?")
            userswitch = "m"
            while userswitch != "1" and userswitch != "0":  # keep asking until user enters either 1 or 0
                userswitch = input("Enter 1 to switch or enter 0 to keep your case.\n> ")
                if userswitch == "1":
                    print("Switching cases...")
                    players_case = lastcase
                    print("Your case is now case # " + str(players_case))
                    winnings = case_values[players_case]
                    print("You won...")
                    drumroll()
                    print(winnings, "points!")
                    new_game = game(game_id, player_id, game_mode, "ND", r, winnings)  # create game object
                    new_game.save()  # write that object into the file
                    gameOn = False
                elif userswitch == "0":
                    print("You chose to keep your case.")
                    winnings = case_values[players_case]
                    print("You won...")
                    drumroll()
                    print(winnings, "points!")
                    new_game = game(game_id, player_id, game_mode, "ND", r, winnings)  # create game object
                    new_game.save()  # write that object into the file
                    gameOn = False
                else:
                    print("Please enter either 1 or 0.")
        r += 1
    print("You have finished playing the ", end="")
    if game_mode == 4:
        print("player's ", end="")
    else:
        print("banker's ", end="")
    print("offer mode. ")
    if first_time:
        print("You will now play the ", end="")
        if game_mode == 4:
            print("banker's ", end="")
        else:
            print("player's ", end="")
        print("offer mode. ")
        time.sleep(2)
    return winnings, game_id

def print_intro():
    print("\nWe thank you for participating in our study. \nToday you will play 2 modified versions of Deal or No Deal. \
Instructions on how to play will follow. \nClick the blank space below so that a blinking cursor appears and press enter to continue.")
    userinput = input("")
    print("Playing this game is voluntary, and you may stop at any time, though you will not receive\n\
compensation unless you reach the end of both games.\nPress enter to continue.")
    userinput = input("")
    print("In each game, you can win up to 1,000,000 points. Your final score will be divided by 100,000,\n\
converted to dollars, and added to your compensation for your participation.\nFor example, if you win a total \
of 623,211 points, you will be awarded an additional $6.23.\nPress enter to continue.")
    userinput = input("")
    print("Please do not refresh the web browser page or click the \"Stop\" button while you play.\
\nDoing so will cause you to lose your progress.\nPress enter to continue.")
    userinput = input("")

def main():
    # WELCOME
    print("Welcome to Deal or No Deal! Please zoom out in your browser (ctrl -) for a better display")
    time.sleep(1)
    print_intro()

    player_id = create_id()

    game_mode1 = random.randint(1, 2)  # randomly generate order in which to play
    if game_mode1 == 1:
        game_mode2 = 2
    elif game_mode1 ==2:
        game_mode2 = 1

    # play the games
    winnings1, game_id1 = play(game_mode1, True, player_id)
    winnings2, game_id2 = play(game_mode2, False, player_id)

    # concluding message.
    print("This concludes your participation in the study. Because you won", winnings1, "points during the first game\n\
and", winnings2, "in the second game for a total of", str(winnings1+winnings2), "points, you will be compensated\nan \
additional $" + str(round(0.00001*(winnings1+winnings2),2)) + ". Thanks for playing!")
    time.sleep(1)
    print("Your player ID was saved as", player_id, "and your game ID's were saved as", game_id1, "and", str(game_id2)
          + ". \nThese serve as confirmation that you have completed two games. ")

if __name__ == "__main__":
    main()


import os
import json
import numpy as np
import torch
import torch.nn.functional as F
import torch.distributions as distributions
from tqdm import tqdm
from torch.optim import Adam
from torch.utils.tensorboard import SummaryWriter
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')

from mode_disent.utils.mige import SpectralScoreEstimator, entropy_surrogate
from mode_disent.utils.mmd import compute_mmd_tutorial
from mode_disent.memory.memory import MyLazyMemory
from mode_disent.network.dynamics_model import DynLatentNetwork
from mode_disent.network.mode_model import ModeLatentNetwork
from mode_disent.test.action_sampler import ActionSamplerSeq, \
    ActionSamplerWithActionModel
from code_slac.utils import calc_kl_divergence, update_params

class DisentAgent:

    def __init__(self,
                 env,
                 log_dir,
                 min_steps_sampling,
                 batch_size,
                 num_sequences,
                 train_steps_dyn,
                 train_steps_mode,
                 lr,
                 state_rep,
                 feature_dim,
                 latent1_dim,
                 latent2_dim,
                 std_dyn_decoder,
                 mode_dim,
                 mode_encode_rnn_dim,
                 mode_encode_num_rnn_layers,
                 mode_encode_rnn_dropout,
                 hidden_units,
                 hidden_units_encoder,
                 hidden_units_dyn_decoder,
                 hidden_units_mode_encoder,
                 hidden_units_action_decoder,
                 std_action_decoder,
                 memory_size,
                 skill_policy,
                 log_interval,
                 dyn_latent,
                 mode_latent,
                 memory,
                 test_memory,
                 mode_repeating,
                 info_loss_params,
                 run_id,
                 run_hp,
                 device,
                 normalize_states,
                 leaky_slope=0.2,
                 seed=0
                 ):

        self.env = env
        self.observation_shape = self.env.observation_space.shape
        self.action_shape = self.env.action_space.shape
        self.feature_dim = int(self.observation_shape[0]
                               if state_rep else feature_dim)

        self.seed = seed
        torch.manual_seed(self.seed)
        np.random.seed(self.seed)
        self.env.seed(self.seed)

        self.device = torch.device(
            device if torch.cuda.is_available() else "cpu"
        )

        if dyn_latent is None:
            self.dyn_latent = DynLatentNetwork(
                observation_shape=self.observation_shape,
                action_shape=self.action_shape,
                feature_dim=self.feature_dim,
                latent1_dim=latent1_dim,
                latent2_dim=latent2_dim,
                hidden_units=hidden_units,
                hidden_units_encoder=hidden_units_encoder,
                hidden_units_decoder=hidden_units_dyn_decoder,
                std_decoder=std_dyn_decoder,
                device=self.device,
                leaky_slope=0.2,
                state_rep=state_rep).to(self.device)
            self.dyn_loaded = False
        else:
            self.dyn_latent = dyn_latent.to(self.device)
            self.dyn_loaded = True

        ones_like_action = np.ones_like(self.env.action_space.sample())
        action_bound_low = self.env.action_space.low
        action_bound_high = self.env.action_space.high
        action_bound_low_test = action_bound_low == -1 * ones_like_action
        action_bound_high_test = action_bound_high == ones_like_action
        if np.all(action_bound_high_test & action_bound_low_test):
            action_normalized = True
        else:
            action_normalized = False

        if mode_latent is None:
            self.mode_latent = ModeLatentNetwork(
                mode_dim=mode_dim,
                rnn_dim=mode_encode_rnn_dim,
                num_rnn_layers=mode_encode_num_rnn_layers,
                rnn_dropout=mode_encode_rnn_dropout,
                hidden_units_mode_encoder=hidden_units_mode_encoder,
                hidden_units_action_decoder=hidden_units_action_decoder,
                mode_repeating=mode_repeating,
                feature_dim=self.feature_dim,
                action_dim=self.action_shape[0],
                dyn_latent_network=self.dyn_latent,
                std_decoder=std_action_decoder,
                leaky_slope=leaky_slope,
                action_normalized=action_normalized,
                device=self.device).to(self.device)
            self.mode_loaded = False
        else:
            self.mode_latent = mode_latent.to(self.device)
            self.mode_loaded = True

        self.dyn_optim = Adam(self.dyn_latent.parameters(), lr=lr)
        self.mode_optim = Adam(self.mode_latent.parameters(), lr=lr)

        if memory is None:
            self.memory = MyLazyMemory(
                state_rep=state_rep,
                capacity=memory_size,
                num_sequences=num_sequences,
                observation_shape=self.observation_shape,
                action_shape=self.action_shape,
                device=self.device
            )
            self.memory_loaded = False
        else:
            self.memory = memory
            self.memory_loaded = True

        if test_memory is None:
            self.test_memory = MyLazyMemory(
                state_rep=state_rep,
                capacity=memory_size,
                num_sequences=num_sequences,
                observation_shape=self.observation_shape,
                action_shape=self.action_shape,
                device=self.device
            )
            self.memory_test_loaded = False
        else:
            self.test_memory = test_memory
            self.memory_test_loaded = True

        self.spectral_j = SpectralScoreEstimator(n_eigen_threshold=0.99)
        self.spectral_m = SpectralScoreEstimator(n_eigen_threshold=0.99)

        self.log_dir = log_dir
        self.model_dir = os.path.join(log_dir, 'model', str(run_id))
        self.summary_dir = os.path.join(log_dir, 'summary', str(run_id))
        if not os.path.exists(self.model_dir):
            os.makedirs(self.model_dir)
        if not os.path.exists(self.summary_dir):
            os.makedirs(self.summary_dir)

        hparam_save_path = os.path.join(self.model_dir, 'run_hyperparameter.json')
        with open(hparam_save_path, 'w', encoding='utf-8') as f:
            json.dump(run_hp, f, ensure_ascii=False, indent=4)

        self.writer = SummaryWriter(log_dir=self.summary_dir)

        self.skill_policy = skill_policy

        self.info_loss_params = info_loss_params
        self.normalize_states = normalize_states
        self.num_skills = self.skill_policy.stochastic_policy.skill_dim
        self.steps = np.zeros(shape=self.num_skills, dtype=np.int)
        self.steps_test = np.zeros(shape=self.num_skills, dtype=np.int)
        self.learn_steps_dyn = 0
        self.learn_steps_mode = 0
        self.episodes = 0
        self.mode_dim = mode_dim
        self.latent1_dim = latent1_dim
        self.latent2_dim = latent2_dim
        self.min_steps_sampling = min_steps_sampling
        self.num_sequences = num_sequences
        self.batch_size = batch_size
        self.log_interval = log_interval
        self.train_steps_dyn = train_steps_dyn
        self.train_steps_mode = train_steps_mode
        self.state_rep = state_rep
        self.run_id = run_id

    def run(self):
        self.sample_sequences(
            memory_to_fill=self.memory,
            min_steps=self.min_steps_sampling,
            step_cnt=self.steps)

        self.env.seed(self.seed + 1)
        #self.sample_sequences(
        #    memory_to_fill=self.test_memory,
        #    min_steps=self.min_steps_sampling,
        #    step_cnt=self.steps_test)

        if not self.dyn_loaded:
            self.train_dyn()

        if not self.mode_loaded:
            self.train_mode()

        self.save_models()
        self._plot_whole_mode_map(to=['file', 'writer'])

    def run_dual_training(self):
        self.sample_sequences(memory=self.memory,
                              min_steps=self.min_steps_sampling,
                              step_cnt=self.steps)

        self.env.seed(self.seed + 1)
        self.sample_sequences(memory=self.test_memory,
                              min_steps=self.min_steps_sampling,
                              step_cnt=self.steps_test)

        train_steps = min(self.train_steps_dyn, self.train_steps_mode)
        train_steps_ratio = self.train_steps_dyn / self.train_steps_mode

        for _ in tqdm(range(train_steps)):
            if train_steps_ratio > 1:
                for _ in range(int(train_steps_ratio)):
                    self.learn_dyn()
                self.learn_mode()

            else:
                self.learn_dyn()
                for _ in range(int(train_steps_ratio**-1)):
                    self.learn_mode()

        self.save_models()
        self._plot_whole_mode_map(to=['file', 'writer'])

    def _plot_whole_mode_map(self, to: list):
        all_seqs = self.memory.sample_sequence(batch_size=self.batch_size * 1)
        feature_seq = self.dyn_latent.encoder(all_seqs['states_seq'])
        post = self.mode_latent.sample_mode_posterior(
            features_seq=feature_seq, actions_seq=all_seqs['actions_seq'])
        base_str = 'Mode Model/'
        self._plot_mode_map(skill_seq=all_seqs['skill_seq'],
                            mode_post_samples=post['mode_sample'],
                            base_str=base_str,
                            to=to)

    def sample_sequences(self, memory_to_fill, min_steps, step_cnt):
        skill = 0
        while np.sum(step_cnt) < min_steps:
            self.sample_equal_skill_dist(memory=memory_to_fill,
                                         skill=skill,
                                         step_cnt=step_cnt)
            skill = min(skill + 1, (skill + 1) % self.num_skills)

            self.episodes += 1

        print(self.steps)
        memory_to_fill.skill_histogram(self.writer)

        # Save memory
        #path_name_memory = os.path.join(self.model_dir, 'memory.pth')
        #if os.path.exists(path_name_memory):
        #    path_name_memory = os.path.join(self.model_dir, 'memory_test.pth')

        #    if os.path.exists(path_name_memory):
        #        raise NotImplementedError

        #torch.save(memory_to_fill, path_name_memory)

    def sample_seq(self):
        episode_steps_repeat = 0
        episode_steps = 0
        done = False
        state = self.env.reset()
        self.memory.set_initial_state(state)
        skill = np.random.randint(self.num_skills)
        self.set_policy_skill(skill)

        next_state = state
        while not done and episode_steps < self.num_sequences + 1:
            action = self.get_skill_policy_action(next_state)
            next_state, reward, done, _ = self.env.step(action)
            episode_steps_repeat += self.env.action_repeat
            self.steps[skill] += 1
            episode_steps += 1

            self.memory.append(action=action,
                               skill=np.array([skill], dtype=np.uint8),
                               state=next_state,
                               done=np.array([done], dtype=np.bool))

        print(f'episode: {self.episodes:<4}  '
              f'episode_steps: {episode_steps:<4}  '
              f'skill: {skill: <4}  ')

    def sample_equal_skill_dist(self,
                                memory: MyLazyMemory,
                                skill: int,
                                step_cnt: np.ndarray):
        episode_steps = 0
        state = self.env.reset()
        memory.set_initial_state(state)
        self.set_policy_skill(skill)

        next_state = state
        done = False

        while self.steps[skill] <= np.max(self.steps):
            if done:
                next_state = self.env.reset()
                done = False

            action = self.get_skill_policy_action(next_state)
            next_state, reward, done, _ = self.env.step(action)

            episode_steps += 1
            step_cnt[skill] += 1

            seq_pushed = memory.append(action=action,
                                       skill=np.array([skill], dtype=np.uint8),
                                       state=next_state,
                                       done=np.array([done], dtype=np.bool))
            if seq_pushed:
                break

        print(f'episode: {self.episodes:<4}  '
              f'episode_steps: {episode_steps:<4}  '
              f'skill: {skill: <4}  ')

    def save_models(self):
        path_name_dyn = os.path.join(self.model_dir, 'dyn_model.pth')
        path_name_mode = os.path.join(self.model_dir, 'mode_model.pth')
        torch.save(self.dyn_latent, path_name_dyn)
        torch.save(self.mode_latent, path_name_mode)

    def train_dyn(self):
        for _ in tqdm(range(self.train_steps_dyn)):
            self.learn_dyn()
        self.save_models()

    def train_mode(self):
        for _ in tqdm(range(self.train_steps_mode)):
            self.learn_mode()

            if self._is_interval(self.log_interval * 10, self.learn_steps_mode):
                self.save_models()
                self._plot_whole_mode_map(to=['file'])

    def learn_dyn(self):
        sequences = self.memory.sample_sequence(self.batch_size)
        loss = self.calc_dyn_loss(sequences)
        update_params(self.dyn_optim, self.dyn_latent, loss)

        self.learn_steps_dyn += 1

    def calc_dyn_loss(self, sequence):
        actions_seq = sequence['actions_seq']
        features_seq = self.dyn_latent.encoder(sequence['states_seq'])

        # Sample from posterior dynamics
        post = self.dyn_latent.sample_posterior(
            features_seq=features_seq, actions_seq=actions_seq
        )

        # Sample from prior dynamics
        pri = self.dyn_latent.sample_prior_train(features_seq=features_seq,
                                                 actions_seq=actions_seq)

        # KLD
        kld_loss = calc_kl_divergence(post['latent1_dists'],
                                      pri['latent1_dists'])

        # Reconstruction loss
        states_seq_dists = self.dyn_latent.decoder(
            [post['latent1_samples'], post['latent2_samples']]
        )
        ll = states_seq_dists.log_prob(sequence['states_seq']).mean(dim=0).sum()
        mse_loss = F.mse_loss(states_seq_dists.loc, sequence['states_seq'])

        loss = kld_loss - ll

        # Logging
        base_str = 'Dynamics Model/'
        if self._is_interval(self.log_interval, self.learn_steps_dyn):
            self._summary_log_dyn(base_str + 'stats/reconstruction loss', mse_loss)
            self._summary_log_dyn(base_str + 'stats/ll-loss', -ll)
            self._summary_log_dyn(base_str + 'stats/kld', kld_loss)
            print('reconstruction error ' + str(mse_loss.item()))

        # Testing
        if self._is_interval(self.log_interval * 2, self.learn_steps_dyn) \
                and self.observation_shape[0] + self.action_shape[0] < 8:
            # Actions not in the training set
            if self.action_shape[0] == 1:
                action_seq = np.array([np.sin(np.arange(0, 5, 0.05))])
                action_seq = self.numpy_to_tensor(action_seq).float().view(-1, 1)
                action_sampler = ActionSamplerSeq(action_seq)
                self._ar_dyn_test(
                    seq_len=200,
                    action_sampler=action_sampler,
                    writer_base_str=base_str + 'Auto_regressive_test_unseen_actions'
                )

                #test_seq = self.test_memory.sample_sequence(batch_size=1)
                #action_seq = test_seq['actions_seq'][0]
                #action_sampler = ActionSamplerSeq(action_seq)
                #self._ar_dyn_test(
                #    seq_len=200,
                #    action_sampler=action_sampler,
                #    writer_base_str=base_str + 'Auto Reg with test set'
                #)
                test_seq = self.memory.sample_sequence(batch_size=1)
                action_seq = test_seq['actions_seq'][0]
                action_sampler = ActionSamplerSeq(action_seq)
                self._ar_dyn_test(
                    seq_len=200,
                    action_sampler=action_sampler,
                    writer_base_str=base_str + 'Auto Reg with training set'
                )

        if self._is_interval(self.log_interval * 10, self.learn_steps_dyn):
            # Scaling Test
            seq = sequence['states_seq'][0]
            recon_seq = states_seq_dists.loc[0]
            for dim in range(*self.observation_shape):
                plt.plot(self.tensor_to_numpy(seq[:, dim]),
                         label='states_seq_dim' + str(dim))
                plt.plot(self.tensor_to_numpy(recon_seq[:, dim]))
                plt.gca().axes.set_ylim([-1.5, 1.5])
                plt.legend()
                self.writer.add_figure('scaling test/dim' + str(dim),
                                       plt.gcf(),
                                       global_step=self.learn_steps_dyn)
                plt.clf()

        return loss

    def learn_mode(self):
        sequences = self.memory.sample_sequence(self.batch_size)
        loss = self.calc_mode_loss(sequences)
        update_params(self.mode_optim, self.mode_latent, loss)

        self.learn_steps_mode += 1

    def calc_mode_loss(self, sequence):
        actions_seq = sequence['actions_seq']
        features_seq = self.dyn_latent.encoder(sequence['states_seq'])
        skill_seq = sequence['skill_seq']

        # Posterior and prior from mode
        mode_post = self.mode_latent.sample_mode_posterior(features_seq=features_seq,
                                                           actions_seq=actions_seq)
        mode_pri = self.mode_latent.sample_mode_prior(self.batch_size)

        # KLD
        kld = calc_kl_divergence([mode_post['mode_dist']],
                                 [mode_pri['mode_dist']])

        # MMD
        mmd = compute_mmd_tutorial(mode_pri['mode_sample'],
                                   mode_post['mode_sample'])

        # Mutual info gradient estimation
        features_actions_seq = torch.cat([features_seq[:, :-1, :],
                                          actions_seq], dim=2)
        xs = features_actions_seq.view(self.batch_size, -1)
        ys = mode_post['mode_sample']
        xs_ys = torch.cat([xs, ys], dim=1)
        # gradient_estimator_m_data = entropy_surrogate(self.spectral_j, xs_ys) \
        #                            - entropy_surrogate(self.spectral_m, ys)

        # Reconstruct action auto-regressive
        action_recon = self.reconstruct_action_seq_ar(features_seq, mode_post)

        # Reconstruction loss
        ll = action_recon['dists'].log_prob(actions_seq).mean(dim=0).sum()
        mse = F.mse_loss(action_recon['samples'], actions_seq)

        # Sample wise analysis
        if self._is_interval(self.log_interval, self.learn_steps_mode):
            with torch.no_grad():
                distance_sample_wise = ((action_recon['samples'] - actions_seq)**2)\
                    .sum(dim=2).sum(dim=1).squeeze()
                skill_batch = skill_seq.float().mean(dim=1).int().squeeze()
                error_per_skill = []
                for skill in range(self.num_skills):
                    idx = skill_batch == skill
                    num_occurence = torch.sum(idx.int())
                    error_per_skill.append(
                        (distance_sample_wise[idx].sum()/num_occurence).item())

                error_per_skill_np = np.array([int(el) for el in error_per_skill])
                print(error_per_skill_np.astype(np.int))

                highest_error_skill = np.argmax(error_per_skill)
                skill_seq_mean_per_episode = skill_seq.float().mean(dim=1).int()
                bool_idx = skill_seq_mean_per_episode == torch.tensor(highest_error_skill)
                seq_with_skill_to_plot = skill_seq_mean_per_episode[bool_idx]
                rand_idx = np.random.randint(low=0, high=seq_with_skill_to_plot.size(0))
                action_seq_to_plot = actions_seq[rand_idx]
                action_seq_to_plot_recon = action_recon['samples'][rand_idx]

                for dim in range(*self.action_shape):
                    plt.interactive(False)
                    plt.ylim([-1.5, 1.5])
                    plt.plot(self.tensor_to_numpy(action_seq_to_plot[:, dim]),
                             label='real ' + 'skill' + str(highest_error_skill))
                    plt.plot(self.tensor_to_numpy(action_seq_to_plot_recon[:, dim]),
                             label='recon' + 'skill' + str(highest_error_skill))

                    plt.legend()
                    fig = plt.gcf()
                    base_str = 'Mode Model'
                    self.writer.add_figure(base_str + 'reconstruction_test_on_dataset_dim' + str(dim),
                                           fig,
                                           global_step=self.learn_steps_mode)
                    plt.clf()

        # Classic beta-VAE loss
        beta = 1.
        classic_loss = beta * kld - ll

        alpha = self.info_loss_params.alpha
        lamda = self.info_loss_params.lamda
        kld_info = (1 - alpha) * kld
        mmd_info = (alpha + lamda - 1) * mmd
        info_loss = mse + kld_info + mmd_info
        if self.info_loss_params.kld_diff_desired is not None:
            kld_desired_scalar = self.info_loss_params.kld_diff_desired
            kld_desired = torch.tensor(kld_desired_scalar).to(self.device)
            kld_diff_control = 0.07 * F.mse_loss(kld_desired, kld)
            info_loss += kld_diff_control

        # MI Gradient Estimation Loss (input-data - m)
        mi_grad_data_m_weight = 2
        beta_mi_grad = 1
        mi_grad_kld = beta_mi_grad * kld
        #mi_grad_data_m_loss = \
        #    mse + mi_grad_kld - mi_grad_data_m_weight * gradient_estimator_m_data

        base_str_stats = 'Mode Model stats/'
        base_str_info = 'Mode Model info vae/'
        if self._is_interval(self.log_interval, self.learn_steps_mode):
            self._summary_log_mode(base_str_stats + 'log-likelyhood', ll)
            self._summary_log_mode(base_str_stats + 'mse', mse)
            self._summary_log_mode(base_str_stats + 'kld', kld)
            self._summary_log_mode(base_str_stats + 'mmd', mmd)

            self._summary_log_mode(base_str_info + 'kld info weighted', kld_info)
            self._summary_log_mode(base_str_info + 'mmd info weighted', mmd_info)
            self._summary_log_mode(base_str_info + 'loss on latent', mmd_info + kld_info)

            base_str = 'Mode Model/'
            self._plot_mode_map(skill_seq, mode_post['mode_sample'], base_str, to=['writer'])

        base_str = 'Mode Model/'
        if self._is_interval(self.log_interval * 2, self.learn_steps_mode) \
            and self.observation_shape[0] + self.action_shape[0] < 8:

            rand_batch_idx = np.random.randint(low=0, high=self.batch_size)
            self._plot_recon_comparison(actions_seq[rand_batch_idx],
                                        action_recon['samples'][rand_batch_idx],
                                        sequence['states_seq'][rand_batch_idx],
                                        base_str)
            self._test_mode_influence(mode_post['mode_sample'])

        elif self._is_interval(self.log_interval * 2, self.learn_steps_mode) \
            and self.observation_shape[0] + self.action_shape[0] >= 8:
            rand_batch_idx = np.random.randint(low=0, high=self.batch_size)
            self._plot_recon_comparison_high_dim(actions_seq[rand_batch_idx],
                                                 action_recon['samples'][rand_batch_idx],
                                                 base_str)

        return 10 * info_loss

    def _plot_recon_comparison_high_dim(self,
                                        action_seq,
                                        action_seq_recon,
                                        base_str):
        dims = self.action_shape[0]

        action_seq = self.tensor_to_numpy(action_seq)
        action_seq_recon = self.tensor_to_numpy(action_seq_recon)

        for dim in range(dims):
            plt.interactive(False)
            axes = plt.gca()
            axes.set_ylim([-1.5, 1.5])
            plt.plot(action_seq[:, dim], label='real action dim' + str(dim))
            plt.plot(action_seq_recon[:, dim], label='recon action dim' + str(dim))

            plt.legend()
            fig = plt.gcf()
            self.writer.add_figure(base_str + 'reconstruction_test_on_dataset dim' + str(dim),
                                   fig,
                                   global_step=self.learn_steps_mode)
            plt.gcf()

    def _plot_recon_comparison(self,
                               action_seq,
                               action_seq_recon,
                               state_seq,
                               base_str):
        """
        Args:
            action_seq       :  (S, action_dim) tensor
            action_seq_recon :  (S, action_dim) tensor
        """
        dims = self.action_shape[0]

        action_seq = self.tensor_to_numpy(action_seq)
        action_seq_recon = self.tensor_to_numpy(action_seq_recon)
        state_seq = self.tensor_to_numpy(state_seq)

        plt.interactive(False)
        axes = plt.gca()
        axes.set_ylim([-1.5, 1.5])
        for dim in range(dims):
            plt.plot(action_seq[:, dim], label='real action dim' + str(dim))
            plt.plot(action_seq_recon[:, dim], label='recon action dim' + str(dim))

        for dim in range(state_seq.shape[1]):
            plt.plot(state_seq[:, dim], label='state dim' + str(dim))

        plt.legend()
        fig = plt.gcf()
        self.writer.add_figure(base_str + 'reconstruction_test_on_dataset',
                               fig,
                               global_step=self.learn_steps_mode)
        plt.clf()

    def _plot_mode_map(self,
                       skill_seq,
                       mode_post_samples,
                       base_str,
                       to: list):
        """
        Args:
            skill_seq            : (N, S, 1) - tensor
            mode_post_samples    : (N, S, 2) - tensor
            base_str             : string
        """
        if not(self.mode_dim == 2):
            raise ValueError('mode dim does not equal 2')

        assert len(mode_post_samples.shape) == 2

        skill_seq = self.tensor_to_numpy(skill_seq.float()
                                         .mean(dim=1)).astype(np.uint8)
        skill_seq = skill_seq.squeeze()
        mode_post_samples = self.tensor_to_numpy(mode_post_samples)

        colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'darkorange', 'gray', 'lightgreen']
        plt.interactive(False)
        axes = plt.gca()
        axes.set_ylim([-3, 3])
        axes.set_xlim([-3, 3])

        for skill in range(skill_seq.max() + 1):
            bool_idx = skill_seq == skill
            color = colors[skill]
            plt.scatter(mode_post_samples[bool_idx, 0],
                        mode_post_samples[bool_idx, 1],
                        label=skill,
                        c=color)

        axes.legend()
        axes.grid(True)
        fig = plt.gcf()

        for dev in to:
            if dev == 'writer':
                self.writer.add_figure(base_str + 'Latent test/mode mapping',
                                       fig,
                                       global_step=self.learn_steps_mode)

            elif dev == 'fig':
                return fig

            elif dev == 'file':
                path_name_fig = os.path.join(self.model_dir, 'mode_mapping.fig')
                torch.save(obj=fig, f=path_name_fig)

            else:
                raise NotImplementedError("option for 'to' is not known")

        plt.clf()

    def _create_mode_grid(self):
        """
        Return:
            modes   : (N, mode_dim) tensor
        """
        grid_vec = torch.linspace(-1.7, 1.7, 4)
        grid_vec_list = [grid_vec] * self.mode_dim
        grid = torch.meshgrid(*grid_vec_list)
        modes = torch.stack(list(grid)).view(self.mode_dim, -1)\
            .transpose(0, -1).to(self.device)
        return modes

    def _test_mode_influence(self, mode_post_samples, seq_len=250):
        """
        take mode_post_samples and reconstruct
        apply mode_action_sampler with every mode to the environemnt
        plot the outcomes
        Args:
            mode_post_samples     : (N, mode_dim)
        """

        with torch.no_grad():
            mode_action_sampler = ActionSamplerWithActionModel(
                self.mode_latent,
                self.dyn_latent,
                self.device
            )

            if not(self.mode_dim == 2):
                modes = mode_post_samples[:10]
            else:
                modes = self._create_mode_grid()

            for mode in modes:

                mode_action_sampler.reset(mode=mode.unsqueeze(0))
                obs = self.env.reset()
                action_save = []
                obs_save = []
                for _ in range(seq_len):
                    obs_tensor = torch.from_numpy(obs.astype(np.float))\
                        .unsqueeze(0).to(self.device).float()

                    action_tensor = mode_action_sampler(
                        feature=self.dyn_latent.encoder(obs_tensor))

                    action = action_tensor[0].detach().cpu().numpy()
                    obs, _, done, _ = self.env.step(action)

                    action_save.append(action)
                    if obs.shape[0] == 2:
                        obs = obs.reshape(-1)
                    obs_save.append(obs)

                actions = np.concatenate(action_save, axis=0)
                obs = np.stack(obs_save, axis=0)

                plt.interactive(False)
                ax = plt.gca()
                ax.set_ylim([-1.5, 1.5])
                plt.plot(actions, label='actions')
                for dim in range(obs.shape[1]):
                    plt.plot(obs[:, dim], label='state_dim' + str(dim))
                plt.legend()
                fig = plt.gcf()
                self.writer.add_figure('mode_grid_plot_test/mode' + str(mode),
                                       figure=fig,
                                       global_step=self.learn_steps_mode)

    def reconstruct_action_seq_ar(self, feature_seq, mode_post):
        feature_seq = feature_seq.transpose(0, 1)
        action_recon_dists_loc = []
        action_recon_dists_scale = []
        actions_recon = []
        latent1_samples = []
        latent2_samples = []

        for t in range(self.num_sequences):
            with torch.no_grad():
                if t == 0:
                    latent1_dist = self.dyn_latent.latent1_init_posterior(feature_seq[t])
                    latent1_sample = latent1_dist.rsample()

                    latent2_dist = self.dyn_latent.latent2_init_posterior(latent1_sample)
                    latent2_sample = latent2_dist.rsample()

                else:
                    post_t = self.dyn_latent.sample_posterior_single(
                        feature=feature_seq[t],
                        action=actions_recon[t-1],
                        latent2_sample_before=latent2_samples[t-1]
                    )
                    latent1_sample = post_t['latent1_sample']
                    latent2_sample = post_t['latent2_sample']

            action_recon_dist = self.mode_latent.action_decoder(
                latent1_sample=latent1_sample,
                latent2_sample=latent2_sample,
                mode_sample=mode_post['mode_sample'])
            action_recon_dists_loc.append(action_recon_dist.loc)
            action_recon_dists_scale.append(action_recon_dist.scale)
            actions_recon.append(action_recon_dist.loc)

            latent1_samples.append(latent1_sample)
            latent2_samples.append(latent2_sample)

        action_recon_dists = distributions.normal.Normal(
            loc=torch.stack(action_recon_dists_loc, dim=1),
            scale=torch.stack(action_recon_dists_scale, dim=1)
        )
        actions_recon = torch.stack(actions_recon, dim=1)

        return {'dists': action_recon_dists,
                'samples': actions_recon}

    def get_skill_action_pixel(self):
        obs_state_space = self.env.get_state_obs()
        action, info = self.skill_policy.get_action(obs_state_space)
        return action

    def get_skill_action_state_rep(self, observation):
        action, info = self.skill_policy.get_action(observation)
        return action

    def set_policy_skill(self, skill):
        self.skill_policy.stochastic_policy._skill = skill

    def get_skill_policy_action(self, obs):
        if self.state_rep:
            obs = self.env.denormalize(obs) if self.normalize_states else obs
            action = self.get_skill_action_state_rep(obs)
        else:
            action = self.get_skill_action_pixel()
        return action

    def _summary_log_dyn(self, data_name, data):
        if type(data) == torch.Tensor:
            data = data.detach().cpu().item()
        self.writer.add_scalar(data_name, data, self.learn_steps_dyn)

    def _summary_log_mode(self, data_name, data):
        if type(data) == torch.Tensor:
            data = data.detach().cpu().item()
        self.writer.add_scalar(data_name, data, self.learn_steps_mode)

    @staticmethod
    def _is_interval(log_interval, steps):
        return True if steps % log_interval == 0 else False

    def _ar_dyn_test(self, seq_len, action_sampler, writer_base_str):
        """
        Auto-regressive test of the dynamics model
        """
        if self.state_rep:
            pass
        else:
            raise NotImplementedError

        # Create action sampler
        #action_seq = np.array(
        #    [self.env.action_space.sample() for _ in range(self.num_sequences)])

        # Sample prior
        pri = self.dyn_latent.sample_prior_eval(
            env=self.env,
            action_sampler=action_sampler,
            num_sequences=seq_len,
        )

        # Reconstruction
        state_seq_rec_dists = self.dyn_latent.decoder(
            [pri['latent1_samples'], pri['latent2_samples']]
        )
        state_seq_rec = state_seq_rec_dists.loc

        # Plot
        writer_base_str = writer_base_str

        plt.interactive(False)
        axes = plt.gca()
        axes.set_ylim([-1.5, 1.5])
        for dim in range(*self.action_shape):
            plt.plot(self.tensor_to_numpy(pri['action_seq'][:, dim]),
                     label='action_seq_dim' + str(dim))
        plt.legend()
        self.writer.add_figure(writer_base_str + '/random_actions',
                               plt.gcf(),
                               global_step=self.learn_steps_dyn)

        for dim in range(*self.observation_shape):
            plt.plot(self.tensor_to_numpy(pri['state_seq'][:, dim]),
                     label='state_seq_real_dim' + str(dim))
            plt.plot(self.tensor_to_numpy(state_seq_rec[:, dim]),
                     label='state_seq_reconstructed_dim' + str(dim))
            plt.legend()
            self.writer.add_figure(writer_base_str + '/states dim' + str(dim),
                                   plt.gcf(),
                                   global_step=self.learn_steps_dyn)
            plt.clf()

    @staticmethod
    def tensor_to_numpy(tensor):
        return tensor.detach().cpu().numpy()

    def numpy_to_tensor(self, nd_array):
        return torch.from_numpy(nd_array).to(self.device)


from typing import List

class Solution:
    def duplicateZeros_bf(self, arr: List[int]) -> None:
        """
        Do not return anything, modify arr in-place instead.
        """
        i, n = 0, len(arr)
        while i < n - 1:
            if arr[i] == 0:
                for j in reversed(range(i + 1, n)):
                    arr[j] = arr[j - 1]
                i += 2
            else:
                i += 1
        return arr

    def duplicateZeros(self, arr: List[int]) -> None:
        n = len(arr)
        j = n + arr.count(0)

        for i in reversed(range(n)):
            j = j - 1
            if j < n:
                arr[j] = arr[i]
            if arr[i] == 0:
                j = j - 1
                if j < n:
                    arr[j] = arr[i]
        return arr

def test():
    sol = Solution()
    cases = [
        [1, 0, 2, 3, 0, 4, 5, 0],
        [1, 2, 3],
        [],
        [0],
        [1, 0],
        [0, 1],
    ]
    for case in cases:
        print(sol.duplicateZeros(case))

if __name__ == '__main__':
    test()


from nmigen import *
from nmigen.back.pysim import *

class Transmitter(Elaboratable):
    def __init__(self, sim, ch, sr, bd):
        self.sim = sim                  # Simulation flag (no I/O)
        self.ch = ch                    # Number of channels
        self.sr = sr                    # Audio sample rate
        self.bd = bd                    # Audio sample bit depth (max 24)
        self.br = sr * 32 * ch * 2      # S/PDIF block rate (bitrate * 2)
        self.out = Signal()             # S/PDIF output signal

    def elaborate(self, platform):
        m = Module()

        ####    Reset    ####
        self.resetgen = ResetGen(16)
        m.submodules.resetgen = self.resetgen

        ####    Clocks    ####
        self.clkgen = ClockGen(72e6, self.br)                           # Create clock generator instance (72 MHz input)
        m.submodules.clkgen = self.clkgen                               # Add clock generator submodule to top module
        m.domains.spdif = cd_spdif = ClockDomain(reset_less=False)      # Create new clock domain for S/PDIF clock
        cd_spdif.clk = self.clkgen.clk_out                              # Assign clock generator output to clock domain
        m.d.comb += cd_spdif.rst.eq(self.resetgen.resetn)               # Synchronous reset signal from ResetGen
        if not self.sim: platform.add_clock_constraint(cd_spdif.clk, self.br)

        ####    Counters    ####
        subframe = Signal(9)        # Each S/PDIF block contains 192 frames, each containing two subframes
        cell = Signal(6)            # Within each subframe there are 32 cells (time-slots)

        # Increment cell counter on S/PDIF clock rising edge
        m.d.spdif += cell.eq(cell + 1)

        # Increment subframe counter after cell counter resets
        with m.If(cell == 63):
            with m.If(subframe == 383):
                m.d.spdif += subframe.eq(0)
            with m.Else():
                m.d.spdif += subframe.eq(subframe + 1)

        ####    Encoder    ####
        PREAMBLE_B = 0b00010111     # Channel A (Left) and start of block
        PREAMBLE_M = 0b00100111     # Channel A (Left)
        PREAMBLE_W = 0b01000111     # Channel B (Right)

        # Left channel flag
        is_left = Signal()
        m.d.comb += is_left.eq(~(subframe[0]))

        # Preamble
        with m.If(cell <= 7):
            # Left channel (B or M)
            with m.If(is_left):
                with m.If(subframe == 0):
                    m.d.spdif += self.out.eq(PREAMBLE_B >> cell)
                with m.Else():
                    m.d.spdif += self.out.eq(PREAMBLE_M >> cell)

            # Right channel (W)
            with m.Else():
                m.d.spdif += self.out.eq(PREAMBLE_W >> cell)

        with m.Else():
            m.d.spdif += self.out.eq(~cell[1])

        ####    I/O    ####
        if not self.sim:
            # Get LED and PMOD pins
            led = platform.request("led", 0)
            pmod = platform.request("pmod", 0)

            # Assign S/PDIF output to LED and PMOD pin
            m.d.comb += led.o.eq(self.out)      # B6
            m.d.comb += pmod.o[3].eq(self.out)  # B1

        return m

class ClockGen(Elaboratable):
    """
    Fractional clock divider
    """

    def __init__(self, f_in, f_out):
        assert f_in >= f_out, "Output frequency must be smaller than input frequency"

        self.width = 32                         # Counter register width
        self.max = 2 ** self.width              # Maximum counter value
        self.div = f_in / (f_out * 2)           # Division factor
        self.step = round(self.max / self.div)  # Counter step size
        self.clk_out = Signal()                 # Output clock signal

        print(
            f"[clk] f_in={f_in / 1e6} MHz  " \
            f"f_out={f_out / 1e6} MHz  " \
            f"width={self.width} bits  " \
            f"div={self.div}  " \
            f"step=0x{self.step:X}"
        )

    def elaborate(self, platform):
        m = Module()

        # Increment counter on each input clock cycle
        counter = Signal(self.width)
        m.d.sync += counter.eq(counter + self.step)

        # Check for overflow condition
        ovf = Signal()
        m.d.comb += ovf.eq(counter + self.step > self.max)

        # Toggle output clock on overflow strobe
        with m.If(ovf):
            m.d.sync += self.clk_out.eq(~self.clk_out)

        return m

class ResetGen(Elaboratable):
    """
    Synchronous reset signal generator
    """

    def __init__(self, cycles):
        self.cycles = cycles        # Number of cycles to wait until de-asserting resetn
        self.resetn = Signal()      # Reset output signal

    def elaborate(self, platform):
        m = Module()

        # Shift 1 into x register on each cycle
        x = Signal(self.cycles)
        m.d.sync += x.eq(Cat(1, x))

        # Assign resetn signal to inverse of x register MSB
        m.d.comb += self.resetn.eq(~x[self.cycles - 1])

        return m

# Build or simulate
if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    p_action = parser.add_subparsers(dest="action")
    p_action.add_parser("simulate")
    p_action.add_parser("generate")
    args = parser.parse_args()

    ch = 2      # Number of channels
    sr = 48e3   # Audio sample rate
    bd = 24     # Sample bit depth

    if args.action == "simulate":
        tx = Transmitter(True, ch, sr, bd)

        sim = Simulator(tx)
        with sim.write_vcd("spdif.vcd"):
            def process():
                for i in range(int(5000)): yield Tick()

            sim.add_clock(1/72e6)
            sim.add_sync_process(process)
            sim.run()

    elif args.action == "generate":
        tx = Transmitter(False, ch, sr, bd)

        from icesugar_nano import ICESugarNanoPlatform
        ICESugarNanoPlatform().build(tx, do_program=False)


#! /usr/bin/env python3

"""
Create environment variables from a hierarchy of configuration files.

Return codes:
0: no error
2: file not found
3: invalid json
4. could not substitute environment variable
"""

import argparse
import logging
import subprocess
import os
import pprint
import sys
from pathlib import Path
from datetime import datetime
import json
import re

lvs_vars = [
    'TOP_SOURCE',
    'TOP_LAYOUT',
    'LAYOUT_FILE',
    'EXTRACT_FLATGLOB',
    'EXTRACT_ABSTRACT',
    'EXTRACT_TYPE',
    'LVS_FLATTEN',
    'LVS_NOFLATTEN',
    'LVS_IGNORE',
    'LVS_SPICE_FILES_TO_FIX',
    'LVS_SPICE_FILES',
    'LVS_VERILOG_FILES'
]

def substitute_env_variables(input_string, env):
    """ Return the string after replacing all environment variables ($varname) with the corresponding values from the environment.

    Only handles simple variables $[A-Za-z0-9_]*. Does not handle ${varname}, $$, etc.
    Missing variables are fatal errors.
    """
    string = input_string
    if "$" in string:
        words = re.findall(r'\$\w+', string)  # returns a list of all environment variables used.
        for w in words:
            env_var = w[1:]  # remove leading '$'
            if env_var in env:
                string = string.replace(w, env.get(env_var), 1)  # only replace first occurence. Others will be replaced later.
            else:
                print(f"ERROR: couldn't find environment variable {w} used in {input_string}", file=sys.stderr)
                sys.exit(4)

    return string

def parse_config_file(json_file, lvs_env):
    """ Parses a json file which may reference other json files and uses the values to set environment variables.

    List values are accumulated while scalar values are overwritten when processing sub files.
    Duplicate list values are silently ignored.
    json syntax errors are fatal errors.
    """
    if not os.path.exists(f"{json_file}"):
        print(f"ERROR: Could not find configuration file {json_file}", file=sys.stderr)
        sys.exit(2)

    print(f"Loading LVS environment from {json_file}", file=sys.stderr)
    try:
        with open(json_file, "r") as f:
            data = json.load(f)
        for key, value in data.items():
            if type(value) == list:
                exports = lvs_env[key].split() if key in lvs_env else []  # current environment list values
                for val in value:
                    val = substitute_env_variables(val, lvs_env)
                    if val not in exports:  # only add if not already in list
                        exports.append(val)
                        if key == 'INCLUDE_CONFIGS':  # load child configs
                            lvs_env['INCLUDE_CONFIGS'] += " " + val  # prevents loading same config twice
                            parse_config_file(val, lvs_env)
                if key != 'INCLUDE_CONFIGS':  # the value of the INCLUDE_CONFIGS key is already updated before the recursive call.
                    lvs_env[key] = ' '.join(exports)
            else:  # value is not a list. new value overrides any previous value.
                value = substitute_env_variables(value, lvs_env)
                lvs_env[key] = value
    except Exception as err:
        print(type(err), file=sys.stderr)
        print(err.args, file=sys.stderr)
        print(f"ERROR: with file {json_file}", file=sys.stderr)
        sys.exit(3)

def clear_env(env, keys):
    """ Clear any previous setting for keys in the environment.
    """
    for key in keys:
        if key in env:
            del env[key]
            print(f"unset {key}")

def print_env(env, keys):
    """ Print assignment statements for keys in env.
    """
    for key in keys:
        if key in env:
            print(f"export {key}='{env[key]}'")
            #pprint.pprint({key: env[key]}, stream=sys.stderr)
        else:
            print(f"export {key}=")
            #pprint.pprint({key: ''}, stream=sys.stderr)

def set_lvs_env(config_file, design_name):
    """ Print a list of shell export commands to set LVS variables.

    Any errors are fatal.
    """
    lvs_env = os.environ.copy()
    clear_env(lvs_env, lvs_vars)
    lvs_env['INCLUDE_CONFIGS'] = f"{config_file}"
    lvs_env['DESIGN_NAME'] = design_name
    parse_config_file(config_file, lvs_env)
    print_env(lvs_env, lvs_vars)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Creates the LVS environment')
    parser.add_argument('--config_file', '-c', required=True, help='LVS config file')
    parser.add_argument('--design_name', '-d', required=False, help='Top source override')
    args = parser.parse_args()
    config_file = Path(args.config_file)
    set_lvs_env(config_file, args.design_name)


import os

from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404, redirect

# Create your views here.
from django.urls import reverse
from django.urls import reverse_lazy
from django.views import View
from django.views.generic import DeleteView
from django.views.generic import FormView
from django.views.generic import ListView

from albums.forms import AddImagesForm,CreatAlbumForm
from albums.models import Album, Photo
from bowling.settings import MEDIA_URL

class AlbumsListView(ListView):
    model = Album
    template_name = 'albums/albums_list.html'

class AlbumDetailView(View):
    def get(self,request,pk):
        album=get_object_or_404(Album,pk=pk)
        return render(request,'albums/album_detail.html',{
            'form':AddImagesForm,
            'photos':Photo.objects.filter(album_id=pk),
            'album':album
        })

    def post(self,request,pk):
        if request.FILES:
            album=get_object_or_404(Album,pk=pk)
            photos=[]
            photos_id={}
            for f in request.FILES.getlist('images'):
                photo = Photo.objects.create(
                    image=f,
                    album=album
                )
                photo.save()
                photos.append(str(photo.image))
                photos_id[str(photo.image)]=photo.id
            return JsonResponse({
                'new_photos':photos,
                'photos_id':photos_id
            },status=200,safe=False)
        else:
            return JsonResponse("Файлы не выбраны",status=500)

class CreateAlbumView(FormView):
    model = Album
    form_class = CreatAlbumForm
    template_name = 'albums/add_album.html'
    success_url = reverse_lazy('album:albums_list')

    def form_valid(self, form):
        album=form.save()
        album.save()
        return super().form_valid(form)

class AlbumDeletelView(DeleteView):
    model = Album
    success_url = reverse_lazy('album:albums_list')

class AlbumEditlView (View):
    def get(self,request,pk):
        album=get_object_or_404(Album,pk=pk)
        form = CreatAlbumForm(initial={
            'name':album.name,
            'cover':album.cover,
            'tournament':album.tournament
        })
        return render(request,'albums/edit_album.html',{'form':form,'album_id':pk})

    def post(self,request,pk):
        instance = get_object_or_404(Album, pk=pk)
        form=CreatAlbumForm(request.POST,request.FILES,instance=instance)
        if form.is_valid():
            form.save()
            return redirect(reverse('album:album_detail',kwargs={'pk':pk}))
        else:
            return render(request,'albums/edit_album.html',{'form':form,'album_id':pk})

class PhotoDeleteView(View):
    def post(self,request,album_id,photo_id):
        try:
            photo = Photo.objects.get(id=photo_id)
            photo.image.delete()
            photo.delete()
            return JsonResponse("ok",status=200,safe=False)
        except Exception:
            return JsonResponse("Oops",status=500,safe=False)


import time, datetime, requests
from common.utils.LoggingUtil import LoggingController

class AddPoiUtil():

    def __init__(self, env):
        self.log4py = LoggingController()
        self.poi_type = {
            '交通检查': '10002',
            '封路': '10003',
            '施工': '10006',
            '拥堵': '10007',
            '积水': '10008',
            '浓雾': '10010',
            '结冰': '10011',
            '交通事故': '10013',
            '实时路况': '10015'
        }
        self.__env = env
        self.path = None
        if self.__env == 'online':
            self.path = r'https://dzt.xxxxxxxxxx.com/deva/pc/pathAndPoi/no/addInfo'
        else:
            self.path = r'https://dzt-test.xxxxxxxxx.com/deva/pc/pathAndPoi/no/addInfo'

    def get_data(self, poi_type, direction, lat, lon):
        data = {
            "uploadUser": "123",
            "sn": 'ZD802B1932L00622',
            "sourceType": '10003',  # 新鲜事
            "poiType": poi_type,  # 交通检查
            "direction": direction,
            "nickName": "小松鼠艾德蒙",
            "likeNum": 1,
            "lat": lat,
            "lon": lon,
            "uploadTimestamp": int(round(time.time() * 1000)),
            "timeOutTimestamp": int(str((time.mktime(time.strptime((datetime.datetime.now() + datetime.timedelta(days=3)).strftime("%Y-%m-%d 00:00:00"), '%Y-%m-%d %H:%M:%S'))) * 1000).split(".")[0]),
            "uploadAddress": "安定门外大街",
            "headImgUrl": "http://yycp-static-1255510688.cos.ap-beijing.myqcloud.com/sso-server-image/1592476328925.png?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDvfAPQlotZ1IJ5T8cKm02oKkxub2FfySs%26q-sign-time%3D1592476329%3B1592620329%26q-key-time%3D1592476329%3B1592620329%26q-header-list%3D%26q-url-param-list%3D%26q-signature%3D66cc5afe29f73df008113bf21b8586793c151239",
            "type": 1,
            "data": "[{\"url\":\"http://petchfile-1255510688.cos.ap-beijing.myqcloud.com/sso-server-image/1592546939076.mp4?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDCWfcNwD5PXVWLxwejccR3Tiz5zhIkx0T%26q-sign-time%3D1592546939%3B1592550539%26q-key-time%3D1592546939%3B1592550539%26q-header-list%3D%26q-url-param-list%3D%26q-signature%3D74a4058ad7579ea210dafcf78d7a19460cffb899\",\"thumbnail\":\"http://petchfile-1255510688.cos.ap-beijing.myqcloud.com/sso-server-image/1592546956790.png?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDCWfcNwD5PXVWLxwejccR3Tiz5zhIkx0T%26q-sign-time%3D1592546956%3B1592550556%26q-key-time%3D1592546956%3B1592550556%26q-header-list%3D%26q-url-param-list%3D%26q-signature%3Dcc9a35349fc55e433f934af88df576ae792b3987\"}]"
        }
        return data

    def add_poi(self, poi_type, direction, lat, lon):
        r = requests.post(url=self.path, json=self.get_data(poi_type, direction, lat, lon))
        print(r.json())

if __name__ == '__main__':
    env = 'qa'
    a = AddPoiUtil(env)
    a.add_poi(a.poi_type['封路'], 180, 39.966487, 116.407653)


import imageio
import math
cnt=0
sm=0
var=0

test = [1,2,3,4]

im = imageio.imread("HDR.jpg")
img = im.tolist()
imgsum = sum(img, [])
ls = [0 for i in range(256)]
for i in imgsum:
	ls[i]+=1

print(len(imgsum))

ls[0]=ls[0]/len(imgsum)
for i in range(1,256):
	ls[i]=ls[i-1]+ls[i]/len(imgsum)

for i in range(len(img)):
	for j in range(len(img[i])):
		im[i][j]=math.floor((256)*ls[im[i][j]])

imageio.imwrite('HDRCOM.jpg',im)


import calendar
import json
import uuid

import requests

from django.conf import settings
from django.urls import reverse
from django.utils import timezone

from ads.utils import get_kwd_usd_rate
from core.utils import build_client_absolute_url

__all__ = (
    'get_payment_info',
    'KNet',
    'months',
    'years',
)

def get_payment_info(payment):
    return {
        'business': settings.PAYPAL_RECEIVER_EMAIL,
        'amount': payment.cost * get_kwd_usd_rate(),
        'item_name': payment.ad.title,
        'currency_code': 'USD',
        'invoice': f'{payment.pk}-{uuid.uuid4()}',
        'notify_url': build_client_absolute_url(reverse('paypal-ipn')),
        'return': build_client_absolute_url(reverse('payments:success')),
        'cancel_return': build_client_absolute_url(reverse('payments:error')),
        'custom': payment.pk,
    }

class KNet(object):

    def __init__(self):
        self.headers = {
            'content-type': 'application/json',
            'Authorization': f'Bearer {settings.KNET_API_KEY}'
        }
        self.token_url = 'https://api.tap.company/v2/tokens'
        self.charge_url = 'https://api.tap.company/v2/charges'

    def create_token(self, card_info):
        payload = {
            'card': card_info
        }
        response = requests.post(self.token_url, json.dumps(payload), headers=self.headers)
        return response.json()

    def charge(self, payment, user, token=None):
        country_code, _, phone_number = user.phone_number.partition('-')

        currency_code = payment.currency
        if not token:
            token = 'src_kw.knet'
            currency_code = 'KWD'

        payload = {
            'amount': payment.cost,
            'currency': currency_code,
            'customer': {
                'first_name': user.first_name,
                'last_name': user.last_name,
                'email': user.email,
                'phone': {
                    'country_code': country_code,
                    'number': phone_number,
                }
            },
            'source': {
                'id': token,
            },
            'redirect': {
                'url': build_client_absolute_url(reverse('payments:proceed')),
            }
        }

        response = requests.post(self.charge_url, json.dumps(payload), headers=self.headers)
        return response.json()

    def retrieve_charge(self, charge_id):
        retrieve_charge_url = f'{self.charge_url}/{charge_id}'
        response = requests.get(retrieve_charge_url, headers=self.headers)
        return response.json()

def months():
    return [(f'0{key}'[-2:], value) for key, value in enumerate(calendar.month_name) if value]

def years():
    current_year = timezone.now().year
    return [(str(year)[2:], year) for year in range(current_year, current_year + 10)]


import io
from os import listdir
from os.path import isfile, join

import numpy as np

def get_dataset(path):
    '''
    :param path: Path to the directory
    :return: Parallel dataset of sentences.
    '''
    onlyfiles = list_of_files(path)
    return load_from_files(onlyfiles, path)

def list_of_files(path):
    '''
    :param path: Path of the directory
    :return: A list containing all the names of the files inside Path
    '''
    list_files = []
    for f in listdir(path):
        if isfile(join(path, f)) and f[:-3] not in list_files:
            list_files.append(f[:-3])

    return list_files

def file_len(fname):
    '''
    :param fname: Path to the file
    :return: Number of lines in the file
    '''
    with io.open(fname, encoding="utf-8") as f:
        for i, l in enumerate(f):
            pass
    return i + 1

def get_size(onlyfiles, path):
    '''
    :param onlyfiles:  List of files inside the directory.
    :param path: Path to the directory.
    :return: Total of sentences present in the dataset
    '''
    i = 0
    for file in onlyfiles:
        i += file_len(path + "/" + file + ".L1")

    return i

def load_from_files(onlyfiles, path):
    '''
    :param onlyfiles:  List of files inside the directory.
    :param path: Path to the directory.
    :return: Parallel dataset of sentences.
    '''
    dataset_size = get_size(onlyfiles, path)
    dataset = np.empty((dataset_size, 2), dtype=np.object)

    base = 0
    for file in onlyfiles:
        print("Loading " + file)
        f_en = io.open(path + "/" + file + ".L1", encoding="utf-8")
        f_de = io.open(path + "/" + file + ".L2", encoding="utf-8")

        offset = 0
        for line in f_en:
            line = line.replace('\n', '')
            dataset[base + offset][0] = line
            offset += 1

        offset = 0
        for line in f_de:
            line = line.replace('\n', '')
            dataset[base + offset][1] = line
            offset += 1

        base += offset

    return dataset


'''
Main module of the game
'''

import os
import sys

import pygame
from constant import *
from helper import load_font
from level import Level
from pygame.locals import *
from tile import Tile

class Game:
    '''
    Main class of the game
    '''

    def __init__(self) -> None:
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        self.clock = pygame.time.Clock()
        self.level = Level(LEVEL[0], self.screen)

    def run(self) -> None:
        '''
        Main loop of the game
        '''
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                if self.level.gameover or self.level.gamefinish:
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_SPACE:
                            self.loading_screen()
                            self.level = Level(LEVEL[0], self.screen)

            self.screen.fill(PRIMARY)

            if self.level.gameover:
                self.gameover_screen()
            elif self.level.gamefinish:
                self.gamefinish_screen()
            else:
                self.level.run()

            pygame.display.update()
            self.clock.tick(FRAME)

    def gameover_screen(self) -> None:
        '''
        Game over screen
        '''
        self.screen.fill(PRIMARY)

        # create text
        font = load_font(50)
        text = font.render('Game Over', True, (255, 255, 255))
        text_rect = text.get_rect()
        text_rect.center = (WIDTH / 2, HEIGHT / 2)

        # create text space to restart
        font = load_font(30)
        text_space = font.render('Press Space to Restart', True, (255, 255, 255))
        text_space_rect = text_space.get_rect()
        text_space_rect.center = (WIDTH / 2, HEIGHT / 2 + 50)

        # draw text
        self.screen.blit(text, text_rect)
        self.screen.blit(text_space, text_space_rect)

    def loading_screen(self) -> None:
        '''
        Loading screen
        '''
        self.screen.fill(PRIMARY)

        # create text
        font = load_font(50)
        text = font.render('Loading...', True, (255, 255, 255))
        text_rect = text.get_rect()
        text_rect.center = (WIDTH / 2, HEIGHT / 2)

        # draw text
        self.screen.blit(text, text_rect)
        pygame.display.update()

    def gamefinish_screen(self) -> None:
        '''
        Game finish screen
        '''
        self.screen.fill(PRIMARY)

        # create text
        font = load_font(50)
        text = font.render('You Win', True, (255, 255, 255))
        text_rect = text.get_rect()
        text_rect.center = (WIDTH / 2, HEIGHT / 2)

        # create text space to restart
        font = load_font(30)
        text_space = font.render('Press Space to Restart', True, (255, 255, 255))
        text_space_rect = text_space.get_rect()
        text_space_rect.center = (WIDTH / 2, HEIGHT / 2 + 50)

        # draw text
        self.screen.blit(text, text_rect)
        self.screen.blit(text_space, text_space_rect)

def main() -> None:
    '''
    Main function to start the game
    '''
    # initialize pygame
    pygame.init()
    pygame.display.set_caption('Platformer Game')

    # run game
    game = Game()
    game.run()
    return

if __name__ == '__main__':
    # set directory and run game
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    main()


import json
import pickle
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse

import pandas as pd
from fastapi import FastAPI
from pydantic import BaseModel, Field

from starter.ml.data import process_data

cat_features = [
    "workclass",
    "education",
    "marital-status",
    "occupation",
    "relationship",
    "race",
    "sex",
    "native-country",
]

# Load the trained model and encoder
model_filepath = "model/random_forest_model.pkl"
encoder_filepath = "model/encoder.pkl"

with open(model_filepath, "rb") as file:
    model = pickle.load(file)

with open(encoder_filepath, "rb") as file:
    encoder = pickle.load(file)

# Instantiate the app
app = FastAPI()

class Data(BaseModel):
    age: int = Field(example=39)
    workclass: str = Field(example="State-gov")
    fnlgt: int = Field(example=77516)
    education: str = Field(example="Bachelors")
    education_num: int = Field(alias="education-num", example=13)
    marital_status: str = Field(alias="marital-status", example="Never-married")
    occupation: str = Field(example="Adm-clerical")
    relationship: str = Field(example="Not-in-family")
    race: str = Field(example="White")
    sex: str = Field(example="Male")
    capital_gain: int = Field(alias="capital-gain", example=2174)
    capital_loss: int = Field(alias="capital-loss", example=0)
    hours_per_week: int = Field(alias="hours-per-week", example=40)
    native_country: str = Field(alias="native-country", example="United-States")

    class Config:
        allow_population_by_field_name = True

# GET on greeting
@app.get("/")
async def welcome_intent():
    return {"greeting": "Welcome to my Model!"}

# POST for model inference
@app.post("/inference")
async def inference(data: Data):
    # Converting input data into Pandas DataFrame
    input_df = pd.json_normalize([jsonable_encoder(data)])

    X, _, _, _ = process_data(
        input_df, categorical_features=cat_features, label=None, training=False, encoder=encoder, lb=None
    )

    # Getting the prediction from the Random Forest Model
    pred = model.predict(X)
    if pred.tolist()[0] == 0:
        pred_label = "<=50K"
    else:
        pred_label = ">50K"

    return {"Prediction": pred_label}