import os
import pandas as pd
import pyecharts.options as opts
import matplotlib.pyplot as plt
from pyecharts.charts import Line,Bar
import numpy as np
import manage_folder
import statistics
import logging

class Body_point:

    def __init__(self,file,point,root_path):

        points_name = ['nose','left_eye_ionner','left_eye','left_eye_outer','right_eye_inner','right_eye','right_eye_outer',
        'left_ear','right_ear','left_month','right_month','left_shoulder','right_shoulder','left_elbow','right_elbow',
        'left_wrist','right_wrist','left_pinky','right_pinky','left_index','right_index','left_thumb','right_thumb',
        'left_hip','right_hip','left_knee','right_knee','left_ankle','right_ankle','left_heel','right_heel',
        'left_foot_index','right_foot_index','center',
        ]

        pose_data = pd.read_csv(root_path+'\\pose_landmarks\\'+file)
        self.data = pose_data[str(point+33)]
        self.frame_num = len(self.data)
        self.frame = range(self.frame_num)
        self.file_name = file.split('.')[0]
        self.var = 2.5
        self.body_part = points_name[point]
        self.save_path = root_path+'\\image\\'+self.file_name+'\\'
        self.shoulder_path = root_path+'\\shoulder\\'
        manage_folder.Check_folder(self.save_path)
        manage_folder.Check_folder(self.shoulder_path)
        self.average_wavelength = 0

    def get_amplitude(self):

        self.L_maximum = []
        self.R_maximum = []
        self.minimum = []
        self.amplitude = []
        self.wavelength = []
        self.dobule_amplitude = []

        # 找出所有區間極值，並過濾偽區間極值
        max_tmp = []
        min_tmp = []
        for i in range(1,self.frame_num-1):
            if self.data[i-1] < self.data[i] and self.data[i+1]< self.data[i]:

                left = i - int(( self.average_wavelength / 2 ) )
                right = i + int(( self.average_wavelength / 2 ) )

                if 0 > left :
                    left = 0
                if right > self.frame_num :
                    right = self.frame_num
                if self.average_wavelength == 0 :
                    right =  i+1

                if self.data[i] == np.max(self.data[left:right]):
                    max_tmp.append(i)

            elif self.data[i-1]>self.data[i] and self.data[i+1]>self.data[i]:

                left = i - int(( self.average_wavelength * 0.3) )
                right = i + int(( self.average_wavelength *0.3 ) )

                if 0 > left :
                    left = 0
                if right > self.frame_num :
                    right = self.frame_num
                if self.average_wavelength == 0 :
                    right =  i+1

                if self.data[i] == np.min(self.data[left:right]) :
                    min_tmp.append(i)

        #print(len(min_tmp))
        # 找出所有的波、振幅、波長
        i = 0
        j = 0
        k = 1
        minsize = len(min_tmp)
        maxsize = len(max_tmp)
        while i != minsize and j+k != maxsize:
            if  max_tmp[j] < min_tmp[i] < max_tmp[j+k] :
                self.minimum.append(min_tmp[i])
                self.L_maximum.append(max_tmp[j])
                self.R_maximum.append(max_tmp[j+k])
                self.amplitude.append(np.min( [self.data[max_tmp[j]]-self.data[min_tmp[i]],
                                            self.data[max_tmp[j+k]]-self.data[min_tmp[i]] ]))
                self.dobule_amplitude.append(self.data[max_tmp[j]]-self.data[min_tmp[i]]+
                                self.data[max_tmp[j+k]]-self.data[min_tmp[i]] )
                self.wavelength.append(max_tmp[j+k]-max_tmp[j]+1)
                i += 1
                j += k
            elif max_tmp[j] > min_tmp[i]:
                i += 1
            else:
                j += 1

        #print(len(self.amplitude))
        # 正規化振幅，並分段
        self.normalization_amplitude = self.amplitude/np.linalg.norm(self.amplitude)
        self.section_normalization_amplitude = []
        tmp2 = []
        for i in self.normalization_amplitude:
            if i > 0.05:
                num = round(i+0.025, 3)
                if num - round(num,1) < 0.05:
                    num = round(num,1)
                else:
                    num = round(num,1) + 0.5
                self.section_normalization_amplitude.append(num)
                tmp2.append(num)
            else:
                self.section_normalization_amplitude.append(0)

        #　取振幅眾數
        self.mode_amplitude = statistics.mode(tmp2)

        # 取振幅眾數平均數
        tmp = []
        for i in range(len(self.section_normalization_amplitude)):
            if self.section_normalization_amplitude[i] == self.mode_amplitude:
                tmp.append(self.amplitude[i])
        self.average_amplitude = np.mean(tmp)

        # 取振幅標準差
        self.std_amplitude = np.std(tmp, ddof=1)

        # 取振幅區間
        self.amplitude_upper_bound = self.average_amplitude + self.std_amplitude * self.var
        self.amplitude_lower_bound = self.average_amplitude - self.std_amplitude * self.var

    def get_wavelength(self):

        # 取波長平均數
        tmp = []
        for idx in range(len(self.section_normalization_amplitude)):
            if self.section_normalization_amplitude[idx] == self.mode_amplitude:
                tmp.append(self.wavelength[idx])
        self.average_wavelength = int( np.mean(tmp) + 0.5 )

        # 取波長區間
        self.wavelength_upper_bound = int(self.average_wavelength  * 1.5 + 0.5 )
        self.wavelength_lower_bound = int(self.average_wavelength  * 0.5 + 0.5 )
        #print(self.wavelength_upper_bound,self.wavelength_lower_bound,int(self.average_wavelength))

    def jump_rope_count(self):

        times = 0
        self.flag = []
        for i in range(len(self.minimum)):
            if self.dobule_amplitude[i] >= self.average_amplitude * 0.7 :
                if self.wavelength_lower_bound <= self.wavelength[i] <= self.wavelength_upper_bound:
                    self.flag.append(2)
                    times += 1
                else:
                    self.flag.append(1)
            else :
                self.flag.append(0)

        df = pd.DataFrame({'maxL':self.L_maximum,'min':self.minimum,'maxR':self.R_maximum,
                           'amplitude':self.amplitude,'wavelength':self.wavelength,'flag':self.flag})

        df.to_csv(self.save_path+'flag.csv',index=False)

        info = ['amplitude','wavelength']
        num = [self.average_amplitude,self.average_wavelength]
        df =  pd.DataFrame([info,num])
        df.to_csv(self.save_path+'info.csv',index=False)

        return times

    def amplitude_waveform(self):

        line=(
            Line(init_opts=opts.InitOpts(width='5000px', height='800px'))
            .add_xaxis(
                xaxis_data= self.frame,
            )
            .add_yaxis(
                series_name= 'amplitude',
                y_axis= self.amplitude,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .set_global_opts(
                xaxis_opts=opts.AxisOpts(
                    interval = 1,
                    is_show = True,
                    splitline_opts=opts.SplitLineOpts(  #顯示x軸線
                        is_show=True
                    ),
                    axislabel_opts=opts.LabelOpts(      #x軸文字設定
                        color='red',
                        font_size=10,
                        rotate = 60                    #旋轉角度90~-90
                    )
                ),
                yaxis_opts=opts.AxisOpts(
                    #min_=0.005
                ),
            )
        )
        line.render(self.save_path+'amplitude.html')

    def normalize_amplitude_waveform(self):

        line=(
            Line(init_opts=opts.InitOpts(width='5000px', height='800px'))
            .add_xaxis(
                xaxis_data= self.frame,
            )
            .add_yaxis(
                series_name= 'amplitude',
                y_axis= self.normalization_amplitude,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .set_global_opts(
                xaxis_opts=opts.AxisOpts(
                    interval = 1,
                    is_show = True,
                    splitline_opts=opts.SplitLineOpts(  #顯示x軸線
                        is_show=True
                    ),
                    axislabel_opts=opts.LabelOpts(      #x軸文字設定
                        color='red',
                        font_size=10,
                        rotate = 60                    #旋轉角度90~-90
                    )
                ),
                yaxis_opts=opts.AxisOpts(
                    #min_=0.005
                ),
            )
        )

        line.render(self.save_path+'normalization_amplitude.html')

    def section_normalize_amplitude_waveform(self):

        line=(
            Line(init_opts=opts.InitOpts(width='5000px', height='800px'))
            .add_xaxis(
                xaxis_data= self.frame,
            )
            .add_yaxis(
                series_name= 'amplitude',
                y_axis= self.section_normalization_amplitude,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .set_global_opts(
                xaxis_opts=opts.AxisOpts(
                    interval = 1,
                    is_show = True,
                    splitline_opts=opts.SplitLineOpts(  #顯示x軸線
                        is_show=True
                    ),
                    axislabel_opts=opts.LabelOpts(      #x軸文字設定
                        color='red',
                        font_size=10,
                        rotate = 60                    #旋轉角度90~-90
                    )
                ),
                yaxis_opts=opts.AxisOpts(
                    #min_=0.005
                ),
            )
        )

        line.render(self.save_path+'section_normalization_amplitude.html')

    def average_amplitude_waveform(self):

        line=(
            Line(init_opts=opts.InitOpts(width='5000px', height='800px'))
            .add_xaxis(
                xaxis_data= self.frame,
            )
            .add_yaxis(
                series_name= 'amplitude',
                y_axis= self.amplitude,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .add_yaxis(
                series_name= 'average_amplitude',
                y_axis= [ self.average_amplitude for i in range(len(self.amplitude))],
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .add_yaxis(
                series_name= 'upper bound',
                y_axis= [ self.amplitude_upper_bound for i in range(len(self.amplitude))],
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .add_yaxis(
                series_name= 'lower bound',
                y_axis= [ self.amplitude_lower_bound for i in range(len(self.amplitude))],
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),

            )
            .set_global_opts(
                xaxis_opts=opts.AxisOpts(
                    interval = 1,
                    is_show = True,
                    splitline_opts=opts.SplitLineOpts(  #顯示x軸線
                        is_show=True
                    ),
                    axislabel_opts=opts.LabelOpts(      #x軸文字設定
                        color='red',
                        font_size=10,
                        rotate = 60                    #旋轉角度90~-90
                    )
                ),
                yaxis_opts=opts.AxisOpts(
                    #min_=0.005
                ),
            )
        )

        line.render(self.save_path+'average_amplitude.html')

    def body_parts_waveform(self):

        maximum = [None]*self.frame_num
        f0 = [None]*self.frame_num
        f1 = [None]*self.frame_num
        f2 = [None]*self.frame_num

        for i in range(len(self.L_maximum)):
            maximum[self.L_maximum[i]] = self.data[self.L_maximum[i]]
        for i in range(len(self.R_maximum)):
            maximum[self.R_maximum[i]] = self.data[self.R_maximum[i]]
        for i in range(len(self.minimum)):
            if self.flag[i] == 2:
                f2[self.minimum[i]] = self.data[self.minimum[i]]
            elif self.flag[i] == 0:
                f0[self.minimum[i]] = self.data[self.minimum[i]]
            else:
                f1[self.minimum[i]] = self.data[self.minimum[i]]

        minimam = min(self.data)
        minimam = round(minimam-0.025, 2)
        line=(
            Line(init_opts=opts.InitOpts(width='10000px', height='800px'))
            .add_xaxis(
                xaxis_data=self.frame,
            )
            .add_yaxis(
                series_name= self.body_part,
                y_axis= self.data,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=1),
                color = '#007FFF'
            )
            .add_yaxis(
                series_name= 'maximum',
                y_axis= maximum,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=0),
                color = '#F28500'
            )
            .add_yaxis(
                series_name= 'amplitude',
                y_axis= f0,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=0),
                color = '#008000'
            )
            .add_yaxis(
                series_name= 'wavelength',
                y_axis= f1,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=0),
                color = '#FF00FF'
            )
            .add_yaxis(
                series_name= 'count',
                y_axis= f2,
                symbol="emptyCircle",
                is_symbol_show= 1,
                is_smooth=0,
                label_opts=opts.LabelOpts(is_show=False),
                linestyle_opts= opts.LineStyleOpts(width=0),
                color = '#FF0000'
            )
            .set_global_opts(
                xaxis_opts=opts.AxisOpts(
                    interval = 2,
                    is_show = True,
                    splitline_opts=opts.SplitLineOpts(  #顯示x軸線
                        is_show=True
                    ),
                axislabel_opts=opts.LabelOpts(      #x軸文字設定
                        color='red',
                        font_size=10,
                        rotate = 60,                    #旋轉角度90~-90

                    )
                ),
                yaxis_opts=opts.AxisOpts(
                    min_=minimam,
                ),
            )

        )

        line.render(self.save_path+self.body_part+'.html')
        line.render(self.shoulder_path+self.file_name+'.html')

def count_score(mycount):

    df = pd.read_csv('..\\data\\all.csv')
    a = []
    miss_rate = []
    error = 0
    for i in range(df.shape[0]):
        num = mycount[i]-int(df['score'][i])
        a.append(num)
        if df['score'][i] != 0:
            miss_rate.append(abs(num)/int(df['score'][i]))
        else:
            miss_rate.append(abs(num))
        if abs(num) > 5 :
            error += 1

    df['count'] = mycount
    df['dif'] = a
    df['miss'] = miss_rate
    df.to_csv('..\\data\\'+'my_count.csv',index = False)
    print('error {}/997 = {}'.format(error,error/997))
    print('miss {}'.format(np.mean(miss_rate)))

if __name__=="__main__":

    #'''
    data_path = '..\\2023_data'
    score = []
    student_id = []
    files = os.listdir(data_path+'\\pose_landmarks\\')
    logging.basicConfig(filename = data_path+'\\counterError.log', level=logging.ERROR)
    for file in files:

        try:
            a = Body_point(file,11,data_path)
            for i in range(2):
                a.get_amplitude()
                a.get_wavelength()

            ans = a.jump_rope_count()
            score.append(ans)

            student_id.append(file.split('.')[0])
            print(file)
            '''
            a.amplitude_waveform()
            a.normalize_amplitude_waveform()
            a.section_normalize_amplitude_waveform()
            a.average_amplitude_waveform()
            a.body_parts_waveform()
            '''

        except:
            logging.error(str(file)+' have error ')

    df = pd.DataFrame({'id':student_id,'score':score})
    df.to_csv(data_path+'\\my_count.csv',index=False)
    #count_score(times)

    '''
    data_path = '..\\2023_data'
    a = Body_point('11001.csv',11,data_path)
    for i in range(2):
        a.get_amplitude()
        a.get_wavelength()
    ans = a.jump_rope_count()

    a.amplitude_waveform()
    a.normalize_amplitude_waveform()
    a.average_amplitude_waveform()
    a.section_normalize_amplitude_waveform()
    a.body_parts_waveform()

    print('times = {}'.format(ans))

    #'''


#!/usr/bin/python2
import socket
import json
import os
import random
import math
import sys
from socket import error as SocketError
import errno
sys.path.append("../..")
import src.game.game_constants as game_consts
from src.game.character import *
from src.game.gamemap import *

# Game map that you can use to query
gameMap = GameMap()

# --------------------------- SET THIS IS UP -------------------------
teamName = "monty-python"
# ---------------------------------------------------------------------

# Set initial connection data
def initialResponse():
    processTurn.turn_count = 0

# ------------------------- CHANGE THESE VALUES -----------------------
    return {'TeamName': teamName,
            'Characters': [
                {"CharacterName": "King Arthur",
                 "ClassId": "Archer"},
                {"CharacterName": "BlackKnight",
                 "ClassId": "Archer"},
                {"CharacterName": "Lancelot",
                 "ClassId": "Archer"},
            ]}
# ---------------------------------------------------------------------

# Determine actions to take on a given turn, given the server response
def processTurn(serverResponse):
# --------------------------- CHANGE THIS SECTION -------------------------
    # Setup helper variables
    actions = []
    myteam = []
    enemyteam = []
    # Find each team and serialize the objects
    for team in serverResponse["Teams"]:
        if team["Id"] == serverResponse["PlayerInfo"]["TeamId"]:
            for characterJson in team["Characters"]:
                character = Character()
                character.serialize(characterJson)
                myteam.append(character)
        else:
            for characterJson in team["Characters"]:
                character = Character()
                character.serialize(characterJson)
                enemyteam.append(character)
# ------------------ You shouldn't change above but you can ---------------

    processTurn.turn_count += 1

    def get_priority(enemies):
        PRIORITY_LIST = ["Assassin", "Sorcerer", "Wizard", "Enchanter", "Archer", "Paladin", "Druid", "Warrior"]
        lowest_priorities = []

        for enemy in enemies:
            if len(lowest_priorities) == 0:
                lowest_priorities.append(enemy)
                continue

            old_priority = PRIORITY_LIST.index(lowest_priorities[0].classId)
            new_priority = PRIORITY_LIST.index(enemy.classId)

            if new_priority <= old_priority:
                if new_priority < old_priority:
                    lowest_priorities = []
                lowest_priorities.append(enemy)

        lowest_health = lowest_priorities[0]
        for enemy in lowest_priorities:
            if enemy.attributes.get_attribute("Health") < lowest_health.attributes.get_attribute("Health"):
                lowest_health = enemy

        return lowest_health

    def evaluate(first_pass):
        # All characters are on the same spot
        one = None
        for character in myteam:
            if not character.is_dead():
                one = character
                break

        if one:
            targets = []
            for character in enemyteam:
                if not character.is_dead() and one.in_range_of(character, gameMap):
                    targets.append(character)

            if len(targets) > 0:
                priority_enemy = get_priority(targets)

                total_damage = 0
                total_debuff = 0
                characters_to_cast = {}

                # Determine the max total debuff we can apply
                for character in myteam:
                    # Add up the damage each character could do
                    if not character.is_dead() and not character.attributes.get_attribute("Stunned"):
                        total_damage += character.attributes.get_attribute("Damage") - priority_enemy.attributes.get_attribute("Armor")

                    if character.can_use_ability(2) and total_debuff < priority_enemy.attributes.get_attribute("Armor"):
                        total_debuff -= game_consts.abilitiesList[2]["StatChanges"][0]["Change"]
                        characters_to_cast[character.id] = True
                    else:
                        characters_to_cast[character.id] = False

                if total_damage == 0:
                    should_debuff = False
                else:
                    turns_to_kill_no_debuff = math.ceil(priority_enemy.attributes.get_attribute("Health") / total_damage)
                    turns_to_kill_debuff = math.ceil((priority_enemy.attributes.get_attribute("Health")) / (total_damage + total_debuff) + 1)
                    should_debuff = turns_to_kill_debuff < turns_to_kill_no_debuff

                # Decide if each character should debuff or attack
                for character in list(myteam):
                    if not character.is_dead() and not character.attributes.get_attribute("Stunned"):
                        if should_debuff and characters_to_cast[character.id]:
                            actions.append({
                                "Action": "Cast",
                                "CharacterId": character.id,
                                "TargetId": priority_enemy.id,
                                "AbilityId": 2
                            })
                        else:
                            actions.append({
                                "Action": "Attack",
                                "CharacterId": character.id,
                                "TargetId": priority_enemy.id,
                            })

                            # Update enemy health
                            enemyteam[enemyteam.index(priority_enemy)].attributes.health -= character.attributes.get_attribute("Damage") - priority_enemy.attributes.get_attribute("Armor")

                        myteam.remove(character)
                        if enemyteam[enemyteam.index(priority_enemy)].is_dead():
                            evaluate(False)
                            break

            elif first_pass:

                one = None
                for character in myteam:
                    if not character.is_dead():
                        one = character
                        break

                if one.attributes.get_attribute("MovementSpeed") > 1:

                    valid_positions = gameMap.get_valid_adjacent_pos(one.position)

                    new_valid_positions = []

                    for valid_position in valid_positions:
                        new_valid_positions.extend(gameMap.get_valid_adjacent_pos(valid_position))

                    new_best_position = one.position
                    for valid_position in new_valid_positions:
                        new_distance = get_closest_enemy(valid_position)

                        if new_distance > get_closest_enemy(new_best_position):
                            new_best_position = valid_position

                    for character in myteam:
                        actions.append({
                            "Action": "Move",
                            "CharacterId": character.id,
                            "Location": new_best_position,
                        })
                else:
                    enemy_is_sprinting = False
                    for enemy in enemyteam:
                        if not enemy.is_dead() and enemy.attributes.get_attribute("MovementSpeed") > 1:
                            enemy_is_sprinting = True

                    if enemy_is_sprinting:
                        for character in myteam:
                            actions.append({
                                "Action": "Cast",
                                "CharacterId": character.id,
                                "TargetId": character.id,
                                "AbilityId": 12
                            })

                    elif processTurn.turn_count > 120:
                        target = get_priority(enemyteam)

                        for character in myteam:
                            actions.append({
                                "Action": "Move",
                                "CharacterId": character.id,
                                "TargetId": target.id,
                            })

    def get_closest_enemy(position):
        closest_distance = 100
        for enemy in enemyteam:
            if not enemy.is_dead():
                distance = len(gameMap.bfs(position, enemy.position))
                if distance < closest_distance:
                    closest_distance = distance

        return closest_distance

    evaluate(True)

    # Send actions to the server
    return {
        'TeamName': teamName,
        'Actions': actions
    }
# ---------------------------------------------------------------------

# Main method
# @competitors DO NOT MODIFY
if __name__ == "__main__":
    # Config
    conn = ('localhost', 1337)
    if len(sys.argv) > 2:
        conn = (sys.argv[1], int(sys.argv[2]))

    # Handshake
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(conn)

    # Initial connection
    s.sendall(json.dumps(initialResponse()) + '\n')

    # Initialize test client
    game_running = True
    members = None

    # Run game
    try:
        data = s.recv(1024)
        while len(data) > 0 and game_running:
            value = None
            if "\n" in data:
                data = data.split('\n')
                if len(data) > 1 and data[1] != "":
                    data = data[1]
                    data += s.recv(1024)
                else:
                    value = json.loads(data[0])

                    # Check game status
                    if 'winner' in value:
                        game_running = False

                    # Send next turn (if appropriate)
                    else:
                        msg = processTurn(value) if "PlayerInfo" in value else initialResponse()
                        s.sendall(json.dumps(msg) + '\n')
                        data = s.recv(1024)
            else:
                data += s.recv(1024)
    except SocketError as e:
        if e.errno != errno.ECONNRESET:
            raise  # Not error we are looking for
        pass  # Handle error here.
    s.close()


import random

class Voiture:

    def __init__(self,xi,yi,l,h):
        self.x = xi #position initiale en x
        self.y = yi #position initiale en y
        self.largeur = l #sens à déterminer en fct de taille de h et l
        self.hauteur = h

    def dessin_voiture(self, z):
        r = random.choice(["pink","tomato","OliveDrab1"])
        z.create_rectangle(self.x,self.y,self.x+self.largeur,self.y+self.hauteur,fill=r)

    #def deplacer(self, clicsouris):
        #if self.sens == ‘vertical’:
            #[…]
        #elif self.sens == ‘horizontal’:
            #[…]
        #else:
            #[exception]


import numpy as np
import matplotlib.pyplot as plt
from astropy.io import fits

def printname(name):
  print(name)

f = fits.open('NDWFS-J1425+3254/out_NDWFS-J1425+3254_J_db.fits')
#f = fits.open('NDWFS-J1425+3254/out_NDWFS-J1425+3254_J_residual.fits')

n_companions=int((f[1].header['TFIELDS']-6)/6)
n_chains=int((f[1].header['NAXIS2']))

mag=np.zeros((n_companions,n_chains))
index=np.zeros((n_companions,n_chains))
reff=np.zeros((n_companions,n_chains))
b_on_a=np.zeros((n_companions,n_chains))
xy=np.zeros((n_companions,n_chains,2))
xy_sep=np.zeros((n_companions,n_chains,2))

quasar_xy=f[1].data['1_PointSource_xy']
quasar_mag=f[1].data['1_PointSource_mag']

for ii in range(0,n_companions):
  mag[ii,:]=f[1].data['{}_Sersic_mag'.format(ii+2)]
  index[ii,:]=f[1].data['{}_Sersic_index'.format(ii+2)]
  reff[ii,:]=f[1].data['{}_Sersic_reff'.format(ii+2)]
  b_on_a[ii,:]=f[1].data['{}_Sersic_reff_b'.format(ii+2)]/reff[ii,:]
  xy[ii,:]=f[1].data['{}_Sersic_xy'.format(ii+2)]
  xy_sep[ii,:]=xy[ii,:]-quasar_xy

#plt.hist(mag[0,:],bins=20)
#plt.show()
plt.plot(mag[0,:],reff[0,:],'.')
plt.show()


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

from django.contrib import admin
from django.contrib import messages
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
from django.contrib.auth.models import User
from django.urls import reverse, NoReverseMatch
from django.utils.html import format_html

from crashstats.crashstats.models import (
    BugAssociation,
    GraphicsDevice,
    MissingProcessedCrash,
    Platform,
    ProductVersion,
    Signature,
    # Middleware
    PriorityJob,
)

ACTION_TO_NAME = {ADDITION: "add", CHANGE: "change", DELETION: "delete"}

@admin.register(LogEntry)
class LogEntryAdmin(admin.ModelAdmin):
    date_hierarchy = "action_time"

    list_display = [
        "action_time",
        "admin",
        "object_link",
        "action",
        "get_change_message",
    ]
    list_display_links = ["action_time", "get_change_message"]

    def admin(self, obj):
        return obj.user.email

    def action(self, obj):
        return ACTION_TO_NAME[obj.action_flag]

    def obj_repr(self, obj):
        edited_obj = obj.get_edited_object()

        if isinstance(edited_obj, User):
            # For user objects, return the email address as an identifier
            return edited_obj.email
        return edited_obj

    def object_link(self, obj):
        object_link = self.obj_repr(obj)  # Default to just name
        content_type = obj.content_type

        if obj.action_flag != DELETION and content_type is not None:
            # try returning an actual link instead of object repr string
            try:
                url = reverse(
                    "admin:{}_{}_change".format(
                        content_type.app_label, content_type.model
                    ),
                    args=[obj.object_id],
                )
                object_link = format_html('{}', url, object_link)
            except NoReverseMatch:
                pass
        return object_link

    object_link.admin_order_field = "object_repr"
    object_link.short_description = "object"

    def get_change_message(self, obj):
        return obj.get_change_message()

    get_change_message.short_description = "change message"

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

@admin.register(BugAssociation)
class BugAssociationAdmin(admin.ModelAdmin):
    list_display = ["bug_id", "signature"]
    search_fields = ["bug_id", "signature"]

@admin.register(GraphicsDevice)
class GraphicsDeviceAdmin(admin.ModelAdmin):
    list_display = ["id", "vendor_hex", "adapter_hex", "vendor_name", "adapter_name"]
    search_fields = ["vendor_hex", "adapter_hex", "vendor_name", "adapter_name"]

@admin.register(Platform)
class PlatformAdmin(admin.ModelAdmin):
    list_display = ["name", "short_name"]

@admin.register(ProductVersion)
class ProductVersionAdmin(admin.ModelAdmin):
    list_display = [
        "product_name",
        "release_channel",
        "major_version",
        "release_version",
        "version_string",
        "build_id",
        "archive_url",
    ]

    search_fields = ["version_string"]

    list_filter = ["major_version", "product_name", "release_channel"]

@admin.register(Signature)
class SignatureAdmin(admin.ModelAdmin):
    list_display = ["signature", "first_build", "first_date"]
    search_fields = ["signature"]

def process_crashes(modeladmin, request, queryset):
    """Process selected missing processed crashes from admin page."""
    priority_api = PriorityJob()
    crash_ids = list(queryset.values_list("crash_id", flat=True))
    priority_api.post(crash_ids=crash_ids)
    messages.add_message(
        request, messages.INFO, "Sent %s crashes for processing." % len(crash_ids)
    )

process_crashes.short_description = "Process crashes"

@admin.register(MissingProcessedCrash)
class MissingProcessedCrashAdmin(admin.ModelAdmin):
    list_display = [
        "crash_id",
        "created",
        "collected_date",
        "is_processed",
        "report_url_linked",
    ]
    actions = [process_crashes]

    list_filter = ["is_processed"]

    def report_url_linked(self, obj):
        return format_html('{}', obj.report_url(), obj.report_url())


import random
import matplotlib.pyplot as plt
import numpy as np
import math

# Calculate the mean and standard deviation of the sample
def plot_hist(data):
    sample_mean = np.mean(data)
    sample_std = np.std(data)

    # Print the sample mean and standard deviation
    print(f"Sample mean reaction time (ms): {sample_mean:.2f}")
    print(f"Sample standard deviation : {sample_std:.2f}")
    num_trials = len(data)
    if num_trials <100:
        num_bins = 10
    else:
        num_bins = int(math.sqrt(num_trials))
    # Create a histogram of the sample heights
    plt.hist(data, bins=num_bins, density=True)
    plt.xlabel("reaction time (ms)")
    plt.ylabel("frequency")
    plt.show()

# Define the mean and standard deviation of the normal distribution
mu = 500
sigma = 50

if __name__ == '__main__':
    while True:
        num_trial = int(input("Enter the number of trials: "))
        if num_trial == 0:
            break
        reaction = [random.normalvariate(mu, sigma) for i in range(num_trial)]
        plot_hist(reaction)


from abc import ABCMeta, abstractmethod
import chess
import math
import time
import numpy as np
from collections import namedtuple

from search_helpers import quickselect, material_balance
import guerilla.data_handler as dh

# Note:
#   Moves are stored as UCI strings
#   Leaf FEN is stripped
Transposition = namedtuple("Transposition", "best_move value leaf_fen node_type")

PV_NODE = 0
CUT_NODE = 1
ALL_NODE = 2
LEAF_NODE = 3  # Additional type, we use it to demark nodes which were leafs to reduce the # of evaluation

class TranspositionTable:
    def __init__(self, exact_depth=True):
        self.table = {}  # Main transposition table {FEN: Transposition Entry}
        self.exact_depth = exact_depth  # If we require an exact depth match

        self.cache_hits = {} # Cache hits by depth
        self.cache_miss = 0
        self.num_transpositions = 0

    def __str__(self):
        return "[TT] {} entries | {} transpositions".format(len(self.table), self.num_transpositions)

    def fetch(self, fen, requested_depth):
        """
        Fetches the transposition for the input FEN
        :param fen: [String] Input FEN for which depth is queried.
        :param requested_depth: [Int] Requested depth. Effect depends on self.exact_depth:
            (True): Returns a transposi tion for which the input FEN was searched to EXACTLY requested_depth.
            (False): Return a transposition for which the input FEN was search to AT LEAST requested_depth.
        :return:
        """
        # Returns {Best Move, Value, Type, Depth}

        # Check for entry in table
        white_fen = dh.flip_to_white(fen)
        # print "Fetching ORIGINAL {} WHITE {}".format(fen, white_fen)
        entry = self._get_entry(white_fen)
        if not entry or entry.deepest < requested_depth:
            self.cache_miss += 1
            return

        # If not using exact depth, then get deepest depth
        if not self.exact_depth:
            requested_depth = entry.deepest

        # Fetch depth result from table
        if requested_depth in entry.value_dict:
            if requested_depth not in self.cache_hits:
                self.cache_hits[requested_depth]= 0
            self.cache_hits[requested_depth] += 1

            transpo = entry.value_dict[requested_depth]

            # If black is next then flip move AND leaf fen
            if dh.black_is_next(fen):
                transpo = self._flip_transposition(transpo)

            return transpo
        else:
            self.cache_miss += 1

    def update(self, fen, search_depth, best_move, score, leaf_fen, node_type):

        # Flip to white
        transpo = Transposition(best_move, score, leaf_fen, node_type)

        # Flip transposition if necessary
        if dh.black_is_next(fen):
            transpo = self._flip_transposition(transpo)

        entry = self._get_entry(dh.flip_to_white(fen), create=True)
        if search_depth not in entry.value_dict:
            self.num_transpositions += 1
        entry.add_depth(search_depth, transpo)

    def exists(self, fen):
        assert (dh.white_is_next(fen))
        return dh.strip_fen(fen) in self.table

    def _get_entry(self, fen, create=False):
        assert (dh.white_is_next(fen))
        key = dh.strip_fen(fen)
        if key not in self.table and create:
            self.table[key] = TranpositionEntry()
        return self.table.get(key)

    def _flip_transposition(self, entry):
        best_move = dh.flip_move(entry.best_move) if entry.best_move is not None else entry.best_move
        leaf_fen = dh.flip_board(entry.leaf_fen)
        return Transposition(best_move, entry.value, leaf_fen, entry.node_type)

    def clear(self):
        # Clears the transposition table
        self.table.clear()
        self.cache_hits = {}
        self.cache_miss = 0

    def get_value(self, fen):
        """
        Returns the value for the input FEN, and the depth where that value comes from.
        :param fen: [String] FEN.
        :return:
            [(Transposition, depth)] If FEN exists in cache else [(None, None)]
        """

        result = (None, None)

        # Check for entry entry
        white_fen = dh.flip_to_white(fen)
        entry = self._get_entry(white_fen)

        # Get deepest transposition with an exact value
        if entry is not None and entry.deepest_value is not None:
            transpo = entry.value_dict[entry.deepest_value]

            # Flip if necessary
            if dh.black_is_next(fen):
                transpo = self._flip_transposition(transpo)

            result = (transpo, entry.deepest_value)

        return result

class TranpositionEntry:
    def __init__(self):
        self.value_dict = {}  # {Depth: Transposition}
        self.deepest = None # Deepest transposition
        self.deepest_value = None # Deepest transposition for which we have an exact value (Leaf or PV Node)

    def add_depth(self, depth, transposition):
        # Update value dict and deepest
        self.value_dict[depth] = transposition
        self.deepest = max(depth, self.deepest)

        # Update deepest value
        if transposition.node_type == PV_NODE or transposition.node_type == LEAF_NODE:
            self.deepest_value = max(depth, self.deepest_value)

class Search:
    __metaclass__ = ABCMeta

    def __init__(self, evaluation_function):
        """
        Initializes the Player abstract class.
        Input:
            evaluation_function [function]:
                function to evaluate leaf nodes (usually the neural net)
        """
        self.evaluation_function = evaluation_function
        self.win_value = dh.WIN_VALUE
        self.lose_value = dh.LOSE_VALUE
        self.draw_value = dh.DRAW_VALUE
        self.num_evals = 0
        self.num_visits = []  # Index: depth, Value: Number of vists at that depth
        self.depth_reached = 0
        # Transposition table
        self.tt = TranspositionTable(exact_depth=True)  # TODO: We can change this

    def evaluate(self, fen):
        # Returns the value of the input FEN from the perspective of the next player to play
        # Depth is used to update information

        self.num_evals += 1

        # Evaluate
        board = chess.Board(fen)
        if board.is_checkmate():
            return self.lose_value
        elif board.can_claim_draw() or board.is_stalemate():
            return self.draw_value
        else:
            return self.evaluation_function(dh.flip_to_white(fen))

    @abstractmethod
    def __str__(self):
        raise NotImplementedError("You should never see this")

    @abstractmethod
    def run(self, board, time_limit=None, clear_cache=False):
        """
        Runs search based on parameter.
        Inputs:
                board [chess.Board]:
                    current state of board
            Outputs:
                best_move [chess.Move]:
                    Best move to play
                best_value [float]:
                    value achieved by best move
                best_leaf [String]
                    FEN of the board of the leaf node which yielded the highest value.
        """
        raise NotImplementedError("You should never see this")

    def reset(self):
        # Clears the transposition table
        self.tt.clear()

        # Reset some logging variables
        self.num_evals = 0
        self.num_visits = []
        self.depth_reached = 0

class IterativeDeepening(Search):
    """
    Searches game tree in an Iterative Deepening Depth search.
    At each depth optionally prune from remaining possibilities
    Implements alpha_beta pruning by default
    """

    def __init__(self, evaluation_function, time_limit=10,
                 max_depth=None, h_prune=False, prune_perc=0.0,
                 ab_prune=True, verbose=True, use_partial_search=False):

        """
            Constructor
            Inputs:
                evaluation_function[function]:
                    function used to evaluate leaf nodes
                time_limit[float]:
                    time limit per move
                max_depth[int]:
                    If not None, limit depth to max_depth
                h_prune[bool]:
                    Heuristic_prune. If true, prune between between depth-limited-searches
                prune_perc[float range([0,1])]:
                    Percent of nodes to prune for heuristic prune
                ab_prune[bool]:
                    Alpha-beta pruning. Same results on or off, only set to off for td training
                use_partial_search [Bool]:
                    Whether or not to use partial search results (i.e. when a timeout occurs during DFS).
        """
        super(IterativeDeepening, self).__init__(evaluation_function)

        self.time_limit = time_limit
        self.buff_time = time_limit * 0.02
        self.depth_limit = 1  # Depth limit for DFS
        self.max_depth = max_depth
        self.order_moves = True  # Whether moves should be ordered
        self.h_prune = h_prune
        self.prune_perc = prune_perc
        self.ab_prune = ab_prune
        self.root = None  # holds root node
        self.is_partial_search = False  # Marks if the last DFS call was a partial search
        self.use_partial_search = use_partial_search

        # Holds the Killer Moves by depth. Each Entry is (set of moves, sorted array of (value, moves)).
        self.killer_table = None
        self.num_killer = 2  # Number of killer moves store for each depth
        self._reset_killer()

        # Move value for ordering when board not found in transposition table
        self.order_fn_fast = material_balance

    def __str__(self):
        return "IterativeDeepening"

    def _uci_to_move(self, uci):
        return chess.Move.from_uci(uci) if uci is not None else uci

    def DLS(self, node, alpha, beta):
        """
        Recursive depth limited negamax search with alpha_beta pruning.
        Source: https://en.wikipedia.org/wiki/Negamax#Negamax_with_alpha_beta_pruning_and_transposition_tables
        :param node: [SearchNode] Roote node for search.
        :param alpha: [Float] Lower bound.
        :param beta: [Float] Upper bound.
        :return:
            best_score [float]:
                Score achieved by best move
            best_move [chess.Move]:
                Best move to play
            best_leaf [String]
                FEN of the board of the leaf node which yielded the highest value.
        """

        # Track some info
        if not node.visited:
            # We've never seen this node before -> Track some info
            try:
                self.num_visits[node.depth] += 1
            except IndexError:
                self.num_visits.append(1)

            self.depth_reached = max(self.depth_reached, node.depth)

            # Check if a new killer table entry should be created
            if node.depth >= len(self.killer_table):
                self.killer_table.append({'moves': set(), 'values': list()})

            node.visited = True

        alpha_original = alpha

        # Check transposition table
        result = self.tt.fetch(node.fen, requested_depth=self.depth_limit - node.depth)
        if result:
            if result.node_type == PV_NODE:
                return result.value, self._uci_to_move(result.best_move), result.leaf_fen
            elif result.node_type == CUT_NODE:
                # lower bound
                alpha = max(alpha, result.value)
            elif result.node_type == ALL_NODE:
                # upper bound
                beta = min(beta, result.value)

            if alpha >= beta:
                return result.value, self._uci_to_move(result.best_move), result.leaf_fen

        # Check children
        if not node.children:
            node.gen_children()

        # Check if limit reached
        self.time_left = (time.time() - self.start_time) <= self.time_limit - self.buff_time
        if node.depth == self.depth_limit or not node.expand or not self.time_left or not node.children:
            # Evaluate IF: depth limit reached, pruned, no time left OR no children
            if not self.time_left:
                self.is_partial_search = True

            # Check if we have previously evaluated this node as a leaf node
            if result and result.node_type == LEAF_NODE:
                return result.value, None, node.fen

            # Otherwise evaluate node
            node.value = self.evaluate(node.fen)

            # Update transposition table
            self.tt.update(node.fen, 0, None, node.value, node.fen, LEAF_NODE)

            return node.value, None, node.fen

        # Get Ordered children
        moves = self.get_ordered_moves(node) if self.order_moves else node.child_moves

        # Find best move (recursive)
        best_value = float("-inf")
        best_move = None
        leaf_fen = None

        for move in moves:
            # Get best score for opposing player and flip it to your perspective
            value, _, lf = self.DLS(node.children[move], alpha=-beta, beta=-alpha)
            value = -value

            if value > best_value:
                best_move = move
                best_value = value
                leaf_fen = lf

            # Check for pruning
            alpha = max(alpha, value)
            if alpha >= beta:
                self.update_killer(move, value, node.depth)
                break

        # Update transposition table
        if best_value <= alpha_original:
            # ALL NODES searched, no good moves found -> value is an upper bound
            node_type = ALL_NODE
        elif best_value >= beta:
            # CUT NODE, pruning occurred -> value is a lower bound
            node_type = CUT_NODE
        else:
            # PV NODE Otherwise its potentially part of the the principal variation
            node_type = PV_NODE

        # Update transposition table
        self.tt.update(node.fen, self.depth_limit - node.depth, best_move, best_value, leaf_fen, node_type)

        # Return result of search
        return best_value, self._uci_to_move(best_move), leaf_fen

    def get_ordered_moves(self, node):
        """
        Orders the child moves of the node.
        Ordering is based on:
            (1) Killer moves
            (2) Moves for which we have a value, ordering by (-depth, value) in increasing order
            (3) Other moves
        :param node: [SearchNode] Node who's child moves we need to order.
        :return: [List of Strings] Ordered moves
        """
        killer_moves = []  # Children that are "killer" moves
        value_moves = [] # Moves with values
        other_moves = []

        for move in node.child_moves:
            child_fen = node.children[move].fen

            # Favor killer moves
            if self.is_killer(move, node.depth):
                killer_moves.append((move, self.order_fn_fast(child_fen)))
                continue

            # Check if we have an estimate for the move value
            #   Assign it to a group accordingly
            transpo, depth = self.tt.get_value(child_fen)
            if transpo:
                # Note: take negative of depth since want to look at moves scored deeper first
                value_moves.append((move, (-depth, transpo.value)))
            else:
                other_moves.append((move, self.order_fn_fast(child_fen)))

        # Order in ascending order (want to check boards which are BAD for opponent first)
        killer_moves.sort(key=lambda x: x[1])
        value_moves.sort(key=lambda x: x[1])
        other_moves.sort(key=lambda x: x[1])

        moves = killer_moves + value_moves + other_moves

        assert(len(moves) == len(node.child_moves))

        move_order = [x[0] for x in moves]

        return move_order

    def update_killer(self, killer_move, value, depth):
        """
        Updates the killer move table.
        Input:
            killer_move [Chess.Move]
                The move which caused the A-B pruning to trigger.
            value [Float]
                The value yielded by the killer move.
            depth [Int]
                The depth FROM which the move was played.
        """

        k_tab = self.killer_table[depth]

        # Skip if already in killer table
        if killer_move in k_tab['moves']:
            return

        # Check if table is full
        if len(k_tab['moves']) < self.num_killer:
            # Not Full
            self._add_killer_move(depth, value, killer_move)
        else:
            # Full
            # Check if move is better than worst current move
            if value > k_tab['values'][0]:
                # Remove Worst
                _, worst_move = k_tab['values'].pop(0)
                k_tab['moves'].remove(worst_move)

                # Add Item
                self._add_killer_move(depth, value, killer_move)

    def _add_killer_move(self, depth, value, killer_move):
        """
        Adds killer move to the table.
        """
        # Update moves
        self.killer_table[depth]['moves'].add(killer_move)

        # Update values
        self.killer_table[depth]['values'].append((value, killer_move))
        self.killer_table[depth]['values'].sort(key=lambda x: x[0])  # Sorting each time is OK since length is small.

    def is_killer(self, move, depth):
        """
        Checks if the current move is a killer move.
        Input:
            move [chess.Move]
                Move to check.
        Output:
            output [Boolean]
                True if it is a kill move, False if not.
        """
        return move in self.killer_table[depth]['moves']

    def _reset_killer(self):
        """
        Resets the killer moves table.
        :return:
        """
        self.killer_table = [{'moves': set(), 'values': list()}]

    def prune(self, node):
        """
            Recursive pruning of nodes
        """
        if not node.expand or not node.children:
            return

        children = list(node.get_child_nodes())
        # k = number of nodes that I keep
        k = max(min(len(children), 2),
                int(math.ceil(len(children) * (1 - self.prune_perc))))
        quickselect(children, 0, len(children) - 1, k - 1, key=lambda x: x.value)

        for child in children[:k]:
            self.prune(child)
            child.expand = True
        for child in children[k:]:
            child.expand = False

    def run(self, board, time_limit=None, reset=False):
        """
            For the duration of the time limit and depth limit:
                1. Depth Limited Search
                2. If enabled: Prune nodes
                3. Increase max depth
            Inputs:
                board[chess.Board]:
                    Chess board to search the best move for
                time_limit[float]:
                    time limit for search. If None, defaults to time_limit set in init
                reset [bool]:
                    Resets the search instance. Used for training.
            Outputs:
                best_value [float]:
                    value achieved by best move
                best_move [chess.Move]:
                    Best move to play
                best_leaf [String]
                    FEN of the board of the leaf node which yielded the highest value.
        """
        if time_limit is None:
            time_limit = self.time_limit

        if reset:
            self.reset()
        self.num_evals = 0
        self.eval_time = 0
        self.num_visits = []
        self._reset_killer()

        # Start timing
        if time_limit is not None:
            self.time_limit = time_limit
        self.start_time = time.time()
        self.time_left = True

        self.root = SearchNode(board.fen(), 0)

        self.depth_limit = 1
        value = best_move = leaf_board = None
        while self.time_left and (self.max_depth is None or self.depth_limit <= self.max_depth):

            # Run search
            new_results = self.DLS(self.root, alpha=float("-inf"), beta=float("inf"))
            if not self.is_partial_search or (self.is_partial_search and self.use_partial_search):
                value, best_move, leaf_board = new_results
            self.is_partial_search = False

            # Prune if necessary
            if self.h_prune:
                self.prune(self.root)

            # Increase depth
            self.depth_limit += 1

        return value, best_move, leaf_board

class SearchNode:
    def __init__(self, fen, depth, value=None):
        """
        Generic node used for searching in 'rank_prune'.
        Input:
            fen [String]
                FEN of the board.
            depth [Int]
                Depth at which the node occurs.
            value [Float] (Optional)
                Value of the node.
        Non-Input Class Attributes
            children [Dict of SearchNode's]
                Children of the current SearchNode, key is Move which yields that SearchNode.
        """
        self.fen = fen
        self.depth = depth
        self.children = {}
        self.child_moves = []
        self.expand = True
        self.visited = False

    def _add_child(self, move, child):
        assert (isinstance(child, SearchNode))

        # Convert move to uci
        uci_move = move.uci()

        self.children[uci_move] = child
        self.child_moves.append(uci_move)

    def gen_children(self):
        # Create children
        board = chess.Board(self.fen)
        for move in board.legal_moves:
            board.push(move)
            self._add_child(move, SearchNode(board.fen(), self.depth + 1))
            board.pop()

    def get_child_nodes(self):
        """
        Returns a list of child nodes.
        """
        return self.children.values()

    def __str__(self):
        return "Node[{}, {}, {} children]".format(self.fen, self.depth, len(self.children))

class Minimax(IterativeDeepening):
    """
        Uses a recursive function to perform a simple minimax with
        alpha-beta pruning.
    """

    def __init__(self, leaf_eval, max_depth=2, ab_prune=True):
        """
            Constructor
            Inputs:
                leaf_eval[function]:
                    function used to evaluate leaf nodes
                max_depth[int]:
                    depth to go to
                ab_prune[bool]:
                    Alpha-beta pruning. Same results on or off, only set to off for td training
        """
        super(Minimax, self).__init__(leaf_eval, time_limit=np.inf, max_depth=max_depth,
                                      prune_perc=0.0, ab_prune=ab_prune, use_partial_search=False)

        # Set depth limit to max depth
        self.depth_limit = max_depth

        # Turn off propagation
        self.use_prop = False

        # No buffer time needed
        self.buff_time = 0

    def __str__(self):
        return "Minimax"

    def run(self, board, time_limit=None, reset=False):
        """ Reset some variables, call recursive function """
        if reset:
            self.reset()
        self.num_evals = 0
        self.eval_time = 0
        self.num_visits = []
        self.time_left = True

        # Run to depth limit
        self.start_time = 0
        self.root = SearchNode(board.fen(), 0)  # Note: store unflipped fen
        return self.DLS(self.root, alpha=float("-inf"), beta=float("inf"))


from settings import get_settings, parse_settings

def display_settings(settings: dict):
    """
    Displays the settings menu

    Args:
        settings (dict): Contains dictionary of settings

    Returns:
        choice (str): Returns a choice to influence cli

    """

    print(f"\n############# SETTINGS ############")

    # Loop over keys and values and print them
    for key, value in settings.items():

        print(f"\n{key}: {value}")

    # Prompt options
    choice = input("""
    Select the letter of the option you choose.

    ############# OPTIONS ############
    a) Edit Settings         b) Exit

    > """)

    return choice

def cli():
    """
    Basic command line interface for the program

    """

    # Load settings
    settings = get_settings()

    # Get name
    name = parse_settings(settings, "user")

    # Keeps loop running
    running = True

    # CLI starts
    while(running):

        try:
             # Display welcome message
            choice = int(input(f"""
            ########## POSTFLOW CLI ##########

            Hello, {[name if name is not None else "No Name"]}!

            Type the number for the option you choose.

            ############# OPTIONS ############

            1) Settings

            2) End Session
            > """))

            # View settings
            if choice == 1:
                display_settings(settings)

            # End session
            elif choice == 2:
                running = False

        except:

            print("\n\033[31mWrong option! Try again!\033[0m")


import numpy as np
def cart2polar(x, y):
    """
    Transform a set of cartesienne 2D parameter into polar coordinate parameter
    :param x:
    :param y:
    :return:
    """
    import copy
    is_x_negative = copy.deepcopy(x)  # Pour avoir de 0 a 360 deg
    is_x_negative[x < 0] = 1
    is_x_negative[x >= 0] = 0
    R = np.sqrt(x ** 2 + y ** 2)
    Theta = np.arctan(y / x) + is_x_negative * np.pi

    return R, np.rad2deg(Theta)

def polar2cart(r, theta):
    """
    Transform a set of polar coordinate into 2D cartesienne coordinates
    :param r:
    :param theta:
    :return:
    """
    theta = theta
    x = r / np.sqrt((1 + np.tan(np.deg2rad(theta)) ** 2))
    y = x * np.tan(np.deg2rad(theta))
    return x, y


from Bucket.Policy.multiple_policy import multiple_policy
from Bucket.Policy.public_read_policy import public_read_policy

def assign_policy(aws_s3_client, policy_function, bucket_name):
  policy = None
  if policy_function == "public_read_policy":
    policy = public_read_policy(bucket_name)
  elif policy_function == "multiple_policy":
    policy = multiple_policy(bucket_name)

  if (not policy):
    print('please provide policy')
    return

  aws_s3_client.put_bucket_policy(Bucket=bucket_name, Policy=policy)
  print("Bucket multiple policy assigned successfully")


import random

class Fighter():
    def __init__(self, name='Bot'):
        self.name = name
        self.head = 'Head'
        self.body = 'Body'
        self.legs = 'Legs'
        self.health = random.randint(75, 150)
        self.damage = random.randint(10, 25)

    def __str__(self):
        return f'Боец: {self.name}, Здоровье: {self.health}, Урон: {self.damage}'

    def fight(self, p2):
        print(f'Здоровье бойца {self.name}: {self.health}')
        print(f'Здоровье бойца {p2.name}: {p2.health}')

        while p2.health > 0 and self.health > 0:  # Исправлено or на and
            bot_attack = random.choice((self.head, self.legs, self.body))
            bot_block = random.choice((self.head, self.legs, self.body))

            print(f'{self.name}, куда бьем? (Head/Legs/Body)')
            player_attack = input().strip().capitalize()  # Нормализация ввода
            print(f'{self.name}, на что ставим блок? (Head/Legs/Body)')
            player_block = input().strip().capitalize()   # Нормализация ввода

            print(f'Атакуем {player_attack}, блокируем {player_block}')

            PP = {
                'Head': self.damage * 3,
                'Body': self.damage * 2,
                'Legs': self.damage
            }

            if player_attack == bot_block:
                print(f'{self.name} наносит удар в {bot_block}, но {p2.name} блокирует удар!')
            if bot_attack == player_block:
                print(f'{p2.name} наносит удар в {player_block}, но {self.name} блокирует удар!')

            if player_attack != bot_block:
                p2.health -= PP.get(player_attack, 0)
                print(f'{self.name} попадает в {player_attack} и наносит {PP.get(player_attack, 0)} урона')
                print(f'Здоровье {p2.name} теперь {p2.health}')
            if bot_attack != player_block:
                self.health -= PP.get(bot_attack, 0)
                print(f'{p2.name} попадает в {bot_attack} и наносит {PP.get(bot_attack, 0)} урона')
                print(f'Здоровье {self.name} теперь {self.health}')

            if self.health <= 0:
                return 'Вы проиграли'
            if p2.health <= 0:
                return 'Вы победили'
# Пример реализации
player1 = Fighter('Player 1')
player2 = Fighter('Player 2')

print(player1)
print(player2)

result = player1.fight(player2)
print(result)


from django.core.management import call_command
from django.test import TestCase
import pytz
import datetime

from core.tests.factories import create_offering_ads
from core.models import Province, Municipality, Area

from django.contrib.auth import get_user_model

User = get_user_model()

class TestDeactivationOfAdsRunningOutOfTime(TestCase):
    fixtures = [
        '/home/dockeruser/web/core/fixtures/breeds.json',
        '/home/dockeruser/web/core/fixtures/geographies.json',
        '/home/dockeruser/web/core/fixtures/size_choices.json'
    ]

    def setUp(cls):

        cls.user = User.objects.create(email='test@test.se')
        cls.province = Province.objects.get(name='Stockholm')
        cls.municipality = Municipality.objects.get(name='Stockholms stad')
        cls.area_1 = Area.objects.get(name='Enskede, Årsta, Skarpnäck')
        cls.area_2 = Area.objects.get(name='Hägersten, Liljeholmen')

        cls.today = datetime.datetime.now(pytz.timezone('Europe/Stockholm')).date()
        cls.yesterday = cls.today - datetime.timedelta(days=1)
        cls.tomorrow = cls.today + datetime.timedelta(days=1)

        cls.ad_deletion_date_today = create_offering_ads(
            province=cls.province,
            municipality=cls.municipality,
            user=cls.user,
            is_published=True,
            deletion_date=cls.today,
        )

        cls.ad_deletion_date_yesterday = create_offering_ads(
            province=cls.province,
            municipality=cls.municipality,
            user=cls.user,
            is_published=True,
            deletion_date=cls.yesterday,
        )

        cls.ad_deletion_date_tomorrow = create_offering_ads(
            province=cls.province,
            municipality=cls.municipality,
            user=cls.user,
            is_published=True,
            deletion_date=cls.tomorrow,
        )

    def test_only_ads_with_deletion_date_today_or_earlier_gets_deleted(self):
        result = call_command('deactivate_ads_on_deletion_date')

        self.assertEqual(self.ad_deletion_date_yesterday[0].is_deleted, False)
        self.assertEqual(self.ad_deletion_date_today[0].is_deleted, False)
        self.assertEqual(self.ad_deletion_date_tomorrow[0].is_deleted, False)

        self.assertIn(str(self.ad_deletion_date_yesterday[0].pk), result)
        self.assertIn(str(self.ad_deletion_date_today[0].pk), result)
        self.assertNotIn(str(self.ad_deletion_date_tomorrow[0].pk), result)

        self.ad_deletion_date_yesterday[0].refresh_from_db()
        self.ad_deletion_date_today[0].refresh_from_db()
        self.ad_deletion_date_tomorrow[0].refresh_from_db()

        self.assertEqual(self.ad_deletion_date_yesterday[0].is_deleted, True)
        self.assertEqual(self.ad_deletion_date_today[0].is_deleted, True)
        self.assertEqual(self.ad_deletion_date_tomorrow[0].is_deleted, False)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
import csv
import time
import json
import argparse
import numpy as np
import math

sys.path.append('../')
import tensorflow as tf
from tensorflow.data import Iterator

from model import AlexNet, AlexNetHex

from datagenerator import ImageDataGenerator

# methods to prepare X_rd and X_re for NGLCM usage
def preparion(img, args):
    row = args.row
    column = args.col
    x = np.copy(img)
    x_d = np.copy(img)
    x_re = np.copy(img)

    x = x.reshape(x.shape[0], 128*128)
    x_re = x_re.reshape(x_re.shape[0], 128*128)
    x_d = x_d.reshape(x_d.shape[0], 128*128)

    direction = np.diag((-1) * np.ones(128*128))
    for i in range(128*128):
        x = int(math.floor(i / 128))
        y = int(i % 128)
        if x + row < 128 and y + column < 128:
            direction[i][i + row * 128 + column] = 1

    for i in range(x_re.shape[0]):
        x_re[i] = np.asarray(1.0 * x_re[i] * (args.ngray - 1) / x_re[i].max(), dtype=np.int16)
        x_d[i] = np.dot(x_re[i], direction)
    return x_d, x_re
# ----------------------------

def train(args):
    num_classes = 1000

    tr_data = ImageDataGenerator('../data/trainDataPath.txt',
                                 dataroot='/media/haohanwang/Info/ImageNet/train/',
                                 mode='training',
                                 batch_size=args.batch_size,
                                 num_classes=num_classes,
                                 shuffle=False)
    val_data = ImageDataGenerator('../data/valDataPath.txt',
                                  dataroot='/media/haohanwang/Info/ImageNet/val/',
                                  mode='inference',
                                  batch_size=args.batch_size,
                                  num_classes=num_classes,
                                  shuffle=False)

    iterator = Iterator.from_structure(tr_data.data.output_types,
                                       tr_data.data.output_shapes)
    next_batch = iterator.get_next()

    training_init_op = iterator.make_initializer(tr_data.data)
    validation_init_op = iterator.make_initializer(val_data.data)

    train_batches_per_epoch = int(np.floor(tr_data.data_size / args.batch_size))
    val_batches_per_epoch = int(np.floor(val_data.data_size / args.batch_size))

    x_re = tf.placeholder(tf.float32, (None, 128 * 128))
    x_d = tf.placeholder(tf.float32, (None, 128 * 128))
    x = tf.placeholder(tf.float32, (None, 227, 227, 3))
    y = tf.placeholder(tf.float32, (None, num_classes))

    model = AlexNetHex(x, y, x_re, x_d, args, Hex_flag=True)
    # model = AlexNet(x, y)

    optimizer = tf.train.AdamOptimizer(1e-5).minimize(model.loss)

    saver = tf.train.Saver(tf.trainable_variables())

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        print('Starting training')
        print('load Alex net weights')
        model.load_initial_weights(sess)

        validation = True

        val_acc = []
        for epoch in range(args.epochs):

            begin = time.time()
            sess.run(training_init_op)

            train_accuracies = []
            train_losses = []
            for i in range(train_batches_per_epoch):
                batch_x, img_batch, batch_y = sess.run(next_batch)
                batch_xd, batch_re = preparion(img_batch, args)

                _, acc, loss = sess.run([optimizer, model.accuracy, model.loss], feed_dict={x: batch_x,
                                                                                x_re: batch_re,
                                                                                x_d: batch_xd,
                                                                                y: batch_y,
                                                                                model.keep_prob: 0.5,
                                                                                model.top_k: 5})

                train_accuracies.append(acc)
                train_losses.append(loss)

                train_acc_mean = np.mean(train_accuracies[-10:])
                train_loss_mean = np.mean(train_losses[-10:])

                if (i + 1) % 10 == 0:
                    print("Epoch %d, Batch %d/%d, time = %ds, train accuracy = %.4f, loss = %.4f " % (
                        epoch, i+1, train_batches_per_epoch, time.time() - begin, train_acc_mean, train_loss_mean))

            train_acc_mean = np.mean(train_accuracies)
            train_loss_mean = np.mean(train_losses)

            # compute loss over validation data
            if validation:
                sess.run(validation_init_op)
                val_accuracies = []
                for i in range(val_batches_per_epoch):
                    batch_x, img_batch, batch_y = sess.run(next_batch)
                    batch_xd, batch_re = preparion(img_batch, args)
                    acc = sess.run(model.accuracy, feed_dict={x: batch_x,
                                                              x_re: batch_re,
                                                              x_d: batch_xd,
                                                              y: batch_y,
                                                              model.keep_prob: 1.0, model.top_k: 5})
                    val_accuracies.append(acc)
                val_acc_mean = np.mean(val_accuracies)
                val_acc.append(val_acc_mean)
                # log progress to console
                print("\nEpoch %d, time = %ds, validation accuracy = %.4f" % (epoch, time.time() - begin,  val_acc_mean))
            sys.stdout.flush()

            if (epoch + 1) % 10 == 0:
                ckpt_file = os.path.join(args.ckpt_dir, 'mnist_model.ckpt')
                saver.save(sess, ckpt_file)

        ckpt_file = os.path.join(args.ckpt_dir, 'mnist_model.ckpt')
        saver.save(sess, ckpt_file)

        weights = {}
        for v in tf.trainable_variables():
            weights[v.name] = v.eval()
        np.save('/tuned/weights', weights)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--ckpt_dir', type=str, default='ckpts/', help='Directory for parameter checkpoints')
    parser.add_argument('-l', '--load_params', dest='load_params', action='store_true',
                        help='Restore training from previous model checkpoint?')
    parser.add_argument('-e', '--epochs', type=int, default=5, help='How many epochs to run in total?')
    parser.add_argument('-b', '--batch_size', type=int, default=128, help='Batch size during training per GPU')
    parser.add_argument('-row', '--row', type=int, default=0, help='direction delta in row')
    parser.add_argument('-col', '--col', type=int, default=0, help='direction delta in column')
    parser.add_argument('-ng', '--ngray', type=int, default=128, help='regularization gray level')
    args = parser.parse_args()

    # pretty print args
    # print('input args:\n', json.dumps(vars(args), indent=4, separators=(',',':')))

    if not os.path.exists(args.ckpt_dir):
        os.makedirs(args.ckpt_dir)

    train(args)


"""
Cross-Validation Data Classes
=============================

Scikit-learn compatible classes for performing various
types of cross-validation

"""

__all__ = ["KFoldStratified", "set_cv"]
__author__ = ["Luke Chang"]
__license__ = "MIT"

from sklearn.model_selection._split import _BaseKFold
from sklearn.utils.validation import check_array
import numpy as np
import pandas as pd

class KFoldStratified(_BaseKFold):
    """K-Folds cross validation iterator which stratifies continuous data
    (unlike scikit-learn equivalent).

    Provides train/test indices to split data in train test sets. Split
    dataset into k consecutive folds while ensuring that same subject is
    held out within each fold.  Each fold is then used a validation set
    once while the k - 1 remaining folds form the training set.
    Extension of KFold from scikit-learn cross_validation model

    Args:
        n_splits: int, default=3
            Number of folds. Must be at least 2.
        shuffle: boolean, optional
            Whether to shuffle the data before splitting into batches.
        random_state: None, int or RandomState
            Pseudo-random number generator state used for random
            sampling. If None, use default numpy RNG for shuffling

    """

    def __init__(self, n_splits=3, shuffle=False, random_state=None):
        super(KFoldStratified, self).__init__(
            n_splits=n_splits, shuffle=shuffle, random_state=random_state
        )

    def _make_test_folds(self, X, y=None, groups=None):
        y = pd.DataFrame(y)
        y_sort = y.sort_values(0)
        test_folds = np.nan * np.ones(len(y_sort))
        for k in range(self.n_splits):
            test_idx = y_sort.index[np.arange(k, len(y_sort), self.n_splits)]
            test_folds[y_sort.iloc[test_idx].index] = k
        return test_folds

    def _iter_test_masks(self, X, y=None, groups=None):
        test_folds = self._make_test_folds(X, y)
        for i in range(self.n_splits):
            yield test_folds == i

    def split(self, X, y, groups=None):
        """Generate indices to split data into training and test set.

        Args:
            X : array-like, shape (n_samples, n_features)
                Training data, where n_samples is the number of samples
                and n_features is the number of features.
                Note that providing ``y`` is sufficient to generate the splits
                and hence ``np.zeros(n_samples)`` may be used as a placeholder
                for ``X`` instead of actual training data.
            y : array-like, shape (n_samples,)
                The target variable for supervised learning problems.
                Stratification is done based on the y labels.
            groups : (object) Always ignored, exists for compatibility.

        Returns:
            train : (ndarray) The training set indices for that split.
            test : (ndarray) The testing set indices for that split.

        """
        y = check_array(y, ensure_2d=False, dtype=None)
        return super(KFoldStratified, self).split(X, y, groups)

def set_cv(Y=None, cv_dict=None, return_generator=True):
    """Helper function to create a sci-kit learn compatible cv object using
    common parameters for prediction analyses.

    Args:
        Y:  (pd.DataFrame) Pandas Dataframe of Y labels
        cv_dict: (dict) Type of cross_validation to use. A dictionary of
            {'type': 'kfolds', 'n_folds': n},
            {'type': 'kfolds', 'n_folds': n, 'stratified': Y},
            {'type': 'kfolds', 'n_folds': n, 'subject_id': holdout}, or
            {'type': 'loso', 'subject_id': holdout}
        return_generator (bool): return a cv generator instead of an instance; default True
    Returns:
        cv: a scikit-learn model-selection generator

    """

    if isinstance(cv_dict, dict):
        if cv_dict["type"] == "kfolds":
            if "subject_id" in cv_dict:  # Hold out subjects within each fold
                from sklearn.model_selection import GroupKFold

                cv_inst = GroupKFold(n_splits=cv_dict["n_folds"])
                cv = cv_inst.split(
                    X=np.zeros(len(Y)), y=Y, groups=cv_dict["subject_id"]
                )
            elif "stratified" in cv_dict:  # Stratified K-Folds Continuous
                from nltools.cross_validation import KFoldStratified

                cv_inst = KFoldStratified(n_splits=cv_dict["n_folds"])
                cv = cv_inst.split(X=np.zeros(len(Y)), y=Y)
            else:  # Normal K-Folds
                from sklearn.model_selection import KFold

                cv_inst = KFold(n_splits=cv_dict["n_folds"])
                cv = cv_inst.split(X=np.zeros(len(Y)), y=Y)
        elif cv_dict["type"] == "loso":  # Leave One Subject Out
            from sklearn.model_selection import LeaveOneGroupOut

            cv_inst = LeaveOneGroupOut()
            cv = cv_inst.split(X=np.zeros(len(Y)), y=Y, groups=cv_dict["subject_id"])
        else:
            raise ValueError(
                """Make sure you specify a dictionary of
                            {'type': 'kfolds', 'n_folds': n},
                            {'type': 'kfolds', 'n_folds': n, 'stratified': Y},
                            {'type': 'kfolds', 'n_folds': n,
                            'subject_id': holdout}, or {'type': 'loso',
                            'subject_id': holdout}, where n = number of folds,
                            and subject = vector of subject ids that
                            corresponds to self.Y"""
            )
    else:
        raise ValueError("Make sure 'cv_dict' is a dictionary.")
    if return_generator:
        return cv
    else:
        return cv_inst


import sys, string
import os
import socket
import time
import operator
import boto3
import json
from pyspark.sql import SparkSession
from datetime import datetime

if __name__ == "__main__":

    spark = SparkSession\
        .builder\
        .appName("NASDAQ")\
        .getOrCreate()

    def good_trans(line):
        try:
            fields = line.split(',')
            if len(fields)!=15:
                return False
            float(fields[7])
            return True
        except:
            return False

    # shared read-only object bucket containing datasets
    s3_data_repository_bucket = os.environ['DATA_REPOSITORY_BUCKET']

    s3_endpoint_url = os.environ['S3_ENDPOINT_URL']+':'+os.environ['BUCKET_PORT']
    s3_access_key_id = os.environ['AWS_ACCESS_KEY_ID']
    s3_secret_access_key = os.environ['AWS_SECRET_ACCESS_KEY']
    s3_bucket = os.environ['BUCKET_NAME']

    hadoopConf = spark.sparkContext._jsc.hadoopConfiguration()
    hadoopConf.set("fs.s3a.endpoint", s3_endpoint_url)
    hadoopConf.set("fs.s3a.access.key", s3_access_key_id)
    hadoopConf.set("fs.s3a.secret.key", s3_secret_access_key)
    hadoopConf.set("fs.s3a.path.style.access", "true")
    hadoopConf.set("fs.s3a.connection.ssl.enabled", "false")

    lines = spark.sparkContext.textFile("s3a://" + s3_data_repository_bucket + "/ECS765/ethereum-parvulus/transactions.csv")
    clean_lines=lines.filter(good_trans)
    transValue = clean_lines.map(lambda t: (t.split(",")[6], t.split(",")[7]))
    print(transValue.take(1))

    contracts = spark.sparkContext.textFile("s3a://" + s3_data_repository_bucket + "/ECS765/ethereum-parvulus/contracts.csv")

    bytecode = contracts.map(lambda b: (b.split(",")[0], b.split(",")[1]))
    print(bytecode.take(1))

    contract_wei = transValue.join(bytecode)

    contract_wei = contract_wei.map(lambda w: (w[1][1], int(w[1][0])))
    contract_wei = contract_wei.reduceByKey(operator.add)

    top10 = contract_wei.takeOrdered(10, lambda x : -x[1])

    now = datetime.now() # current date and time
    date_time = now.strftime("%d-%m-%Y_%H:%M:%S")

    my_bucket_resource = boto3.resource('s3',
            endpoint_url='http://' + s3_endpoint_url,
            aws_access_key_id=s3_access_key_id,
            aws_secret_access_key=s3_secret_access_key)

    my_result_object = my_bucket_resource.Object(s3_bucket,'task-2_' + date_time + '/top10.txt')
    my_result_object.put(Body=json.dumps(top10))

    spark.stop()