from core_algo.SGA import SGA
import alarm

import sys
import json
import time
from tabulate import tabulate

def average_res(ga, cal_times=100, **params):
    cost_sum = 0
    runtime_sum = 0
    gen_sum = 0
    speed_sum = 0
    for _ in range(cal_times):
        # solve the question
        res, runtime, last_gen = ga.solve(**params)
        # update data
        cost_sum += res[-1][1][0]
        runtime_sum += runtime
        gen_sum += last_gen
        speed_sum += (res[0][1][0] - res[-1][1][0]) / last_gen
    return (
        cost_sum / cal_times,
        runtime_sum / cal_times,
        gen_sum / cal_times,
        speed_sum / cal_times,
    )

def print_table(base, compare, header, filename=""):
    tabular_data = []
    tabular_data.append(["base", *base])
    for k, v in compare.items():
        tabular_data.append([k, *v])
    table = tabulate(tabular_data, headers=["", *header])
    print(table)

    if filename:
        with open(filename, "w") as f:
            f.write(table)

def param_compare():
    # get test data filename
    data = {}
    filename = input("test data file: ")
    with open(f"input_with_location/test_{filename}.in.json") as f:
        data = json.load(f)

    # get output filename
    output_filename = input("output filename: ")

    # init output file
    print_file = f"param_tbl/{output_filename}_{filename}_{round(time.time())}.tbl"

    # get params by input
    select_method = input("choose a select method(*rws, tourn): ") or "rws"
    select_args = []
    if select_method == "tourn":
        select_args = [4, 0.7]
    xo_method = input("choose a crossover method(*pmx, cbx): ") or "pmx"

    # get average times
    average_times = int(input("average times(*100): ") or 100)

    # init base params while create new GA instance
    base_inst_params = {
        "recovery_rate": 0,
        "pop_num": 50,
        "pfih_rate": 0,
        "data": data,
        "select_method": select_method,
        "select_args": select_args,
        "xo_method": xo_method,
    }

    # init solve params
    solve_params = {
        # "convergence": {
        #     "max_gen": 500,
        #     "min_gen": 100,
        #     "observe_gen": 100,
        #     "mode": "dev",
        # },
        "time-2500": {"max_gen": 1800, "limit_time": 2.5, "mode": "dev"},
    }

    # init compare params
    compare_params_dict = {
        "00r-50p-00pf-100el": {},
        "04-recovery": {"recovery_rate": 0.04},
        "08-recovery": {"recovery_rate": 0.08},
        "04-pfih": {"pfih_rate": 0.04},
        "08-pfih": {"pfih_rate": 0.08},
        "12-pfih": {"pfih_rate": 0.12},
    }

    for iteration_mode, param in solve_params.items():
        compare_res = []
        base_res = []
        for k, v in compare_params_dict.items():
            inst_params = {**base_inst_params, **v}
            ga = SGA(**inst_params)
            cost, runtime, gen, _ = average_res(ga, average_times, **param)
            if k == "00r-50p-00pf-100el":
                base_res = [cost, runtime]
                compare_res.append([k, cost, runtime, gen])
            else:
                base_cost, base_runtime = base_res
                compare_res.append(
                    [
                        k,
                        round((cost - base_cost) * 100 / base_cost, 2),
                        round((runtime - base_runtime) * 100 / base_runtime, 2),
                        gen,
                    ]
                )
        headers = ["", "cost(%)", "runtime(%)", "generation"]
        tbl = tabulate(compare_res, headers=headers)
        print(tbl)

        with open(print_file, "a") as f:
            f.write(f"{iteration_mode}\n")
            f.write(f"{tbl}\n\n")

def method_compare():
    # get test data filename
    data = {}
    filename = input("test data file: ")
    with open(f"input_with_location/test_{filename}.in.json") as f:
        data = json.load(f)

    # get output filename
    output_filename = input("output filename: ")

    # init output file
    print_file = f"method_tbl/{output_filename}_{filename}_{round(time.time())}.tbl"

    # get average times
    average_times = int(input("average times(*100): ") or 100)

    # init base params while create new GA instance
    base_inst_params = {
        "recovery_rate": 0,
        "pop_num": 50,
        "pfih_rate": 0,
        "data": data,
        "select_method": "rws",
        "select_args": [],
        "xo_method": "pmx",
    }

    # init solve params
    solve_params = {
        "convergence": {
            "max_gen": 500,
            "min_gen": 100,
            "observe_gen": 100,
            "mode": "dev",
        },
        "time-2500": {"max_gen": 1800, "limit_time": 2.5, "mode": "dev"},
    }

    # init compare params
    compare_params_dict = {
        "obx-rws": {},
        "obx-tourn": {"select_method": "tourn", "select_args": [4, 0.7]},
        "cbx-rws": {"xo_method": "cbx"},
        "cbx-tourn": {
            "select_method": "tourn",
            "select_args": [4, 0.7],
            "xo_method": "cbx",
        },
        "cbx-rws-rec03": {"xo_method": "cbx", "recovery_rate": 0.03},
        "cbx-tourn-rec03": {
            "select_method": "tourn",
            "select_args": [4, 0.7],
            "xo_method": "cbx",
            "recovery_rate": 0.03,
        },
    }

    for iteration_mode, param in solve_params.items():
        compare_res = []
        base_res = []
        for k, v in compare_params_dict.items():
            inst_params = {**base_inst_params, **v}
            ga = SGA(**inst_params)
            cost, runtime, gen, speed = average_res(ga, average_times, **param)
            if k == "obx-rws":
                base_res = [cost, runtime]
                compare_res.append([k, cost, 0, runtime, 0, gen, speed])
            else:
                base_cost, base_runtime = base_res
                compare_res.append(
                    [
                        k,
                        cost,
                        round((cost - base_cost) * 100 / base_cost, 2),
                        runtime,
                        round((runtime - base_runtime) * 100 / base_runtime, 2),
                        gen,
                        speed,
                    ]
                )
        headers = [
            "",
            "cost",
            "cost(%)",
            "runtime",
            "runtime(%)",
            "generation",
            "con speed",
        ]
        tbl = tabulate(compare_res, headers=headers)
        print(tbl)

        with open(print_file, "a") as f:
            f.write(f"{iteration_mode}\n")
            f.write(f"{tbl}\n\n")

def main():
    if len(sys.argv) > 1:
        if sys.argv[1] == "param":
            param_compare()
        elif sys.argv[1] == "method":
            method_compare()
        else:
            param_compare()
            method_compare()
    else:
        param_compare()
        method_compare()

    # notify program is finished
    alarm.main()

if __name__ == "__main__":
    main()


"""

Test the OOD DiscDist2 scorer. It disentangle the feature into discriminative space and the residual space

"""
import pdb
import numpy as np
import matplotlib.pyplot as plt
import os
import argparse
import torch
from tqdm import tqdm
import torch.backends.cudnn as cudnn

from ood_scores.get_scorers import get_scorer
from utils.argparser import OODArgs
from dataset.dataloaders import get_loaders_for_ood
from models.get_models import get_model
from utils.metrics import get_measures
from utils.utils import print_measures, load_features, get_feat_dims

np.random.seed(10)

# ==================== Prepare
# args
argparser = OODArgs()
args = argparser.get_args()

print(args)

# scorer
scorer = get_scorer(args.score, args)

# dataloaders
id_train_loader, id_test_loader, ood_loaders = get_loaders_for_ood(args)

# feature dims
net = get_model(arch=args.arch, args=args, load_file=args.load_file, strict=True)
net.eval()
if args.ngpu > 1:
    net = torch.nn.DataParallel(net, device_ids=list(range(args.ngpu)))
elif args.ngpu > 0:
    net.cuda()
    # torch.cuda.manual_seed(1)
device = "cuda" if torch.cuda.is_available() else "cpu"
cudnn.benchmark = True  # fire on all cylinders

embed_mode = "supcon" in args.arch or "clip" in args.arch
proj_dim, featdims = get_feat_dims(args, net, embed_mode=embed_mode)
last_start_idx = featdims[:-1].sum()    #<- For getting the last feature only

# ==================== Load id_train features and prepare the scorer
# Load id-train feature and append
id_train_feat, id_train_logit, id_train_label = load_features(args, id=True, split="train", last_idx=last_start_idx, \
    featdim=featdims[-1], projdim=proj_dim, feat_space=args.feat_space)
scorer.append_features(id_train_feat, id_train_label)
print(f"The ID data number: {scorer.N}; Feature dim: {scorer.D}; Class number: {scorer.num_class}")
scorer.fit()
print("Scorer fitting done.")

# ==================== Load id test features and cal ood score
print("Calculating the in distribution OOD scores...")
id_test_feat, id_test_logit, id_test_label = load_features(args, id=True, split="test", last_idx=last_start_idx, \
    featdim=featdims[-1], projdim=proj_dim, feat_space=args.feat_space)
if args.run_together:
    id_scores = scorer.cal_score(id_test_feat, return_all=True)
else:
    N = id_test_feat.shape[0]
    id_scores = []
    id_scores_disc = []
    id_scores_residual = []
    for i in tqdm(range(N)):
        id_score_this, id_score_disc_this, id_score_residual_this = scorer.cal_score(id_test_feat[i, :], return_all=True)
        id_scores.append(id_score_this)
        id_scores_disc.append(id_score_disc_this)
        id_scores_residual.append(id_score_residual_this)
    id_scores = np.array(id_scores)
    id_scores_disc = np.array(id_scores_disc)
    id_scores_residual = np.array(id_scores_residual)

# ==================== Load ood test features, cal ood score, and evaluate ood_performance
aurocs, fprs = [], []
aurocs_disc, fprs_disc = [], []
aurocs_residual, fprs_residual = [], []
ood_names = [ood_loader.dataset.name for ood_loader in ood_loaders]
for ood_name in ood_names:
    print(f"\n\n{ood_name} OOD Detection")

    # load feature
    ood_feat, ood_logit = load_features(args, id=False, ood_name=ood_name, last_idx=last_start_idx, \
        featdim=featdims[-1], projdim=proj_dim, feat_space=args.feat_space)

    # calculate ood_scores
    if args.run_together:
        ood_scores = scorer.cal_score(ood_feat, return_all=True)
    else:
        N = ood_feat.shape[0]
        ood_scores = []
        ood_scores_disc = []
        ood_scores_residual = []
        for i in tqdm(range(N)):
            ood_score_this, ood_score_disc_this, ood_score_residual_this = scorer.cal_score(ood_feat[i, :], return_all=True)
            ood_scores.append(ood_score_this)
            ood_scores_disc.append(ood_score_disc_this)
            ood_scores_residual.append(ood_score_residual_this)
        ood_scores = np.array(ood_scores)
        ood_scores_disc = np.array(ood_scores_disc)
        ood_scores_residual = np.array(ood_scores_residual)

    # evaluate - Use all the OOD samples
    for id_s, ood_s, name, aurocs_bin, fprs_bin in zip(
        [id_scores_disc, id_scores_residual, id_scores],
        [ood_scores_disc, ood_scores_residual, ood_scores],
        ["DiscSpace", "ResidualSpace", "Together"],
        [aurocs_disc, aurocs_residual, aurocs],
        [fprs_disc, fprs_residual, fprs],
    ):
        auroc_this, _, fpr_this = get_measures(id_s, ood_s)
        aurocs_bin.append(auroc_this)
        fprs_bin.append(fpr_this)

        # print
        print_measures(auroc=auroc_this, fpr=fpr_this, method_name=name)

# the mean performance
print("\n\n")
print("Mean results:")
for  name, aurocs_bin, fprs_bin in zip(
    ["DiscSpace", "ResidualSpace", "Together"],
    [aurocs_disc, aurocs_residual, aurocs],
    [fprs_disc, fprs_residual, fprs],
):
    auroc = np.array(aurocs_bin).mean()
    fpr = np.array(fprs_bin).mean()
    print_measures(auroc=auroc, fpr=fpr, method_name=name)


# -*- coding: utf-8 -*-

from django.http import HttpResponse
from django.template import RequestContext, Template
from django.views.decorators.csrf import csrf_exempt
from django.utils.encoding import smart_str, smart_unicode

import xml.etree.ElementTree as ET
import urllib, urllib2, time, hashlib

@csrf_exempt
def handle_request(request):
    if request.method == 'GET':
        response = HttpResponse(check_signature(request),content_type="text/plain")
        return response
    elif request.method == 'POST':
        response = HttpResponse(response_msg(request),content_type="application/xml")
        return response
    else:
        return HttpResponse("Hello world ")

def check_signature(request):
    token = "wangqihui0324"
    signature = request.GET.get("signature")
    timestamp = request.GET.get("timestamp")
    nonce = request.GET.get("nonce")
    echostr = request.GET.get("echostr")
    signature_tmp = [token,timestamp,nonce]
    #lexicographical sorting
    signature_tmp.sort()
    #string concatenation
    # signature_tmp = ''.join(signature_tmp)
    signature_tmp = "%s%s%s" % tuple(signature_tmp)
    #sha1 encryption
    signature_tmp = hashlib.sha1(signature_tmp).hexdigest()
    #compare with signature
    if signature_tmp == signature:
        return echostr
    else:
        return HttpResponse("Hello world ! ")

def response_msg(request):
    #get post message
    msg = request.body
    #parser xml format
    msg_xml = ET.fromstring(msg)
    msg = {}
    for element in msg_xml:
        msg[element.tag] = smart_str(element.text)
    # content = msg.get('Content')
    # url_get_base = "http://api.ltp-cloud.com/analysis/?"
    # api_key = "YourApikey"
    # format = "plain"
    # pattern = "pos"
    #call for ltp-cloud api
    # result = urllib2.urlopen(url_get_base + 'api_key='+api_key+'&text='+content+'&format='+format+'&pattern='+pattern)
    # content = result.read().strip()
    content = "1"
    response = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%d</CreateTime><MsgType><![CDATA[%s]]></MsgType><Content><![CDATA[%s]]></Content></xml>"
    #generate xml formt response
    response = response % (msg['FromUserName'],msg['ToUserName'],int(time.time()),'text',content)
    return response


# coding: utf-8

import tkinter as tk
from tkinter import messagebox, filedialog
import os
from PIL import Image, ImageTk

from detector import PlateDetector
from util import resized_size

class LPRGUI:
    max_image_width = 600
    max_image_height = 600

    def __init__(self):
        self.detector = PlateDetector(chinese_cnn_path='./models/chinese/79.pth',
                                      others_cnn_path='./models/others/49.pth')

        root = tk.Tk()
        root.title('Licence Plate Recognition')
        root.geometry('800x800')
        root.resizable(0, 0)

        self.plate_number_var = tk.StringVar(root, value='None')

        self.add_gap(root, 20)

        # Show image
        self.image_canvas = tk.Canvas(root, width=self.max_image_width, height=self.max_image_height, bg='#E0E0E0')
        self.image_canvas.pack()
        self.tk_image = None

        self.add_gap(root, 20)

        # Show detected plate number
        frame = tk.Frame(root)
        frame.pack()
        tk.Label(frame, text='Plate Number:', font=('Times New Roman', 20)).grid(row=0, column=0)
        tk.Label(frame, textvariable=self.plate_number_var, font=('Times New Roman', 20)).grid(row=0, column=1)

        # Load image button
        self.load_button = tk.Button(root, text="Load Image", width=16, command=self.on_load_image)
        self.load_button.pack(expand=True)

        # Detect button
        self.detect_button = tk.Button(root, text="Detect", width=16, command=self.on_detect)
        self.detect_button.pack(expand=True)
        self.detect_button['state'] = 'disabled'

        # Clear button
        self.clear_button = tk.Button(root, text="Clear", width=16, command=self.on_clear)
        self.clear_button.pack(expand=True)

        print("-------------init success-------------")
        root.mainloop()

    def on_load_image(self):
        file_path = filedialog.askopenfilename(title='Load Image',
                                               filetypes=[('Image Files', '*.jfif *.jpg *.png *.gif'),
                                                          ('All Files', '*')],
                                               initialdir=os.getcwd())

        assert os.path.exists(file_path)

        image = Image.open(file_path)
        self.draw_canvas(image=image)

        self.detector.load_img(file_path)
        self.detect_button['state'] = 'active'
        self.plate_number_var.set('None')

    def on_detect(self):
        try:
            self.detector.find_plate_location()
            self.draw_canvas(Image.fromarray(self.detector.img_after_detected[..., ::-1]))
            self.detector.split_characters()
            self.detector.classify_characters()
            self.plate_number_var.set(''.join(self.detector.result_list))
        except Exception:
            messagebox.showwarning('Error', 'Detection failed!')

    def on_clear(self):
        self.tk_image = None
        self.image_canvas.delete('all')
        self.detector.clear_img()
        self.detect_button['state'] = 'disabled'
        self.plate_number_var.set('None')

    def draw_canvas(self, image):
        self.image_canvas.delete('all')
        image = image.resize(resized_size(image.size, (self.max_image_width, self.max_image_height), mode='scale'))
        self.tk_image = ImageTk.PhotoImage(image)

        horizontal_padding, vertical_padding = 0, 0
        if image.width < self.max_image_width:
            horizontal_padding = (self.max_image_width - image.width) // 2
        else:
            vertical_padding = (self.max_image_height - image.height) // 2
        image_item = self.image_canvas.create_image(horizontal_padding, vertical_padding, anchor='nw')
        self.image_canvas.itemconfig(image_item, image=self.tk_image)

    @classmethod
    def add_gap(cls, root, height):
        tk.Frame(root, height=height).pack()

if __name__ == '__main__':
    LPRGUI()


"""
Resample Raster Files
"""

def match_cellsize_and_clip(rstBands, refRaster, outFolder,
                            clipgeo=None, isint=None, ws=None):
    """
    Resample images to make them with the same resolution and clip

    Good to resample Sentinel bands with more than 10 meters.

    Dependencies:
    * GRASS GIS;
    * GDAL/OGR.
    """

    import os
    from glass.prop.df  import is_rst
    from glass.prop.prj import rst_epsg
    from glass.wenv.grs import run_grass
    from glass.pys.oss  import fprop, mkdir
    from glass.pys.tm   import now_as_str

    # Check if outfolder exists
    if not os.path.exists(outFolder):
        mkdir(outFolder, overwrite=None)

    # Get EPSG from refRaster
    epsg = rst_epsg(refRaster, returnIsProj=None)

    """
    Start GRASS GIS Session
    """
    ws = mkdir(os.path.join(outFolder, now_as_str())) \
        if not ws else ws
    grsb = run_grass(
        ws, grassBIN='grass78', location='resample',
        srs=epsg
    )

    import grass.script.setup as gsetup

    gsetup.init(grsb, ws, 'resample', 'PERMANENT')

    """
    Import packages related with GRASS GIS
    """
    from glass.it.rst    import rst_to_grs, grs_to_rst, grs_to_mask
    from glass.wenv.grs  import rst_to_region
    from glass.it.shp    import shp_to_grs
    from glass.dtt.torst import grsshp_to_grsrst as shp_to_rst

    # Send Ref Raster to GRASS GIS and set region
    extRst = rst_to_grs(refRaster, 'ext_rst')
    rst_to_region(extRst)

    # Import all bands in rstBands
    grs_bands = [rst_to_grs(i, fprop(i, 'fn')) for i in rstBands]

    if clipgeo:
        clip_is_rst = is_rst(clipgeo)

        # Add clipgeo to GRASS
        if not clip_is_rst:
            if '.gdb' in clipgeo:
                lyr = os.path.basename(clipgeo)

                clipgeo = os.path.dirname(clipgeo)

                if clipgeo[-4:] != '.gdb':
                    clipgeo = os.path.dirname(clipgeo)

            else:
                lyr = None

            grs_clip = shp_to_grs(clipgeo, asCMD=True, lyrname=lyr)

            # SHP to Raster
            rst_clip = shp_to_rst(
                grs_clip, 1, f'rst_{grs_clip}',
                cmd=True
            )

        else:
            rst_clip = rst_to_grs(clipgeo, fprop(clipgeo, 'fn'))

        # Set region using
        rst_to_region(rst_clip)

        # Set mask
        grs_to_mask(rst_clip)

    # Export bands
    return [grs_to_rst(
        i, os.path.join(outFolder, i + '.tif'),
        rtype=int if isint else float
    ) for i in grs_bands]

def resample_by_majority(refrst, valrst, out_rst):
    """
    Resample valrst based on refrst:
        Get Majority value of valrst for each cell in refrst

    Useful when ref raster has cellsize greater
    than value raster.

    TODO: Valrst must be of int type
    """

    import numpy        as np
    from osgeo          import gdal
    from glass.prop.img import get_cell_size, get_nd, rst_epsg
    from glass.wt.rst   import obj_to_rst

    # Data to Array
    if type(refrst) == gdal.Dataset:
        refsrc = refrst

    else:
        refsrc = gdal.Open(refrst)

    if type(valrst) == gdal.Dataset:
        valsrc = valrst
    else:
        valsrc = gdal.Open(valrst)

    refnum = refsrc.ReadAsArray()
    valnum = valsrc.ReadAsArray()

    # Get Ref shape
    ref_shape = refnum.shape

    # in a row, how many cells valnum are for each refnum cell
    refcs = int(get_cell_size(refsrc)[0])
    valcs = int(get_cell_size(valsrc)[0])
    dcell = int(refcs / valcs)

    # Valnum must be of int type

    # Create generalized/resampled raster
    resnum = np.zeros(ref_shape, dtype=valnum.dtype)

    for row in range(ref_shape[0]):
        for col in range(ref_shape[1]):
            resnum[row, col] = np.bincount(
                valnum[row*dcell:row*dcell+dcell, col*dcell : col*dcell+dcell].reshape(dcell*dcell)
            ).argmax()

    # Export out raster
    return obj_to_rst(
        resnum, out_rst,
        refsrc.GetGeoTransform(), rst_epsg(refsrc),
        noData=get_nd(valsrc)
    )


import pygame
from settings import BOSSTELEPORTINGSOUND, ENEMYHITSOUND, importFolder, bossPositions
from random import randint
from os import path

class Boss(pygame.sprite.Sprite):
    def __init__(self, pos, surface, level):
        super().__init__()
        #animation
        self.displaySurface = surface
        self.frameIndex = 0
        self.animationSpeed = 0.15
        self.status = 'idle'
        self.importCharacterAssets()
        self.image = self.animations['idle'][self.frameIndex]
        self.rect = self.image.get_rect(center = pos)
        self.hitbox = pygame.Rect((0,0), (40, 50))

        self.canTakeAction = False
        self.spawnTime = pygame.time.get_ticks()
        self.level = level
        self.defeated = False
        #stats
        self.maxHp = 50
        self.hp = self.maxHp
        self.specialDamage = 3

        #status-gameplay
        self.facingRight = False
        self.alive = True
        self.hasIFrames = False
        self.iFramesCD = 500
        self.hitTime = 0
        self.hit = False
        self.location = 2

        #bossmagic
        self.specialAttackHitboxes = pygame.sprite.Group()
        self.canSpecialAttack = True
        self.specialAttack = False
        self.specialAttackDuration = 600
        self.specialAttackCD = 5000
        self.specialAttackTime = 0

        #bossTeleport
        self.teleporting = False
        self.canTeleport = False
        self.teleportTime = 0
        self.teleportDuration = 500
        self.teleportCD = 5000

    def importCharacterAssets(self):
        characterPath = path.join('Assets','enemies','boss')
        self.animations = {'idle':[],'attack':[],'death':[],'idle':[],'skill':[],'summon':[]}

        for animation in self.animations.keys():
            fullPath = path.join(characterPath,animation)
            self.animations[animation] = importFolder(fullPath)

    def animate(self):
        animation = self.animations[self.status]
        #loop over frame index
        self.frameIndex += self.animationSpeed
        if self.frameIndex >= len(animation):
            if not self.alive:
                self.defeated = True
                return
            self.frameIndex = 0

        image = animation[int(self.frameIndex)]
        if self.facingRight:
            self.image = image
        else:
            flippedImage = pygame.transform.flip(image, True, False)
            self.image = flippedImage

        #set the rect
        self.hitbox.center = self.rect.center

    def cooldowns(self):
        self.hit = False
        currentTime = pygame.time.get_ticks()

        if self.specialAttack:
            self.canSpecialAttack = False
            if currentTime - self.specialAttackTime >= self.specialAttackDuration:
                self.specialAttack = False

        if not self.canSpecialAttack:
            if currentTime - self.specialAttackTime >= self.specialAttackCD:
                self.canSpecialAttack = True

        if self.teleporting:
            self.canTeleport = False
            if currentTime - self.teleportTime >= self.teleportDuration:
                self.teleporting = False

        if not self.canTeleport:
            if currentTime - self.teleportTime >= self.teleportCD:
                self.canTeleport = True

        if self.hasIFrames:
            self.hit = False
            if currentTime - self.hitTime >= self.iFramesCD:
                self.hasIFrames = False

        if not self.canTakeAction:
            if currentTime - self.spawnTime >= 1000:
                self.canTakeAction = True

    def getStatus(self):
        if not self.alive:
            self.status = 'death'
        elif self.teleporting:
            self.status = 'skill'
        elif self.specialAttack:
            self.status = 'summon'
        else:
            self.status = 'idle'

    def createSpecialAttackHitbox(self):
        self.specialAttack = True
        self.specialAttackTime = pygame.time.get_ticks()
        summon1 = BossSummon((160, 176), self.level.player.sprite.hitbox.center)
        summon2 = BossSummon((160, 304), self.level.player.sprite.hitbox.center)
        summon3 = BossSummon((656, 176), self.level.player.sprite.hitbox.center)
        summon4 = BossSummon((656, 304), self.level.player.sprite.hitbox.center)
        self.specialAttackHitboxes.add(summon1)
        self.specialAttackHitboxes.add(summon2)
        self.specialAttackHitboxes.add(summon3)
        self.specialAttackHitboxes.add(summon4)

    def loseHP(self, damage):
        if not self.hasIFrames and self.alive:
            self.hit = True
            self.hasIFrames = True
            self.hitTime = pygame.time.get_ticks()
            self.hp -= damage
            ENEMYHITSOUND.play(0)
            if self.hp <= 0:
                self.alive = False

    def changePosition(self):
        if self.canTeleport:
            BOSSTELEPORTINGSOUND.play(0)
            self.teleportTime = pygame.time.get_ticks()
            self.teleporting = True
            newPosIndex = randint(0,4)
            if self.location == bossPositions[newPosIndex]:
                if newPosIndex == 4:
                    newPosIndex = 0
                else:
                    newPosIndex += 1
            if newPosIndex in (0,3,2):
                self.facingRight = True
            else:
                self.facingRight = False
            self.rect.center = bossPositions[newPosIndex]

    def drawing(self, scrolling):
        x = int(self.rect.x - scrolling.x)
        y = int(self.rect.y - scrolling.y)
        self.displaySurface.blit(self.image, (x, y))

    def update(self):
        if not self.defeated:
            self.getStatus()
            self.animate()
            self.cooldowns()
            if self.canTakeAction:
                self.changePosition()
                if self.canSpecialAttack:
                    self.createSpecialAttackHitbox()

class BossSummon(pygame.sprite.Sprite):
    def __init__(self, spawnPosition, target):
        super().__init__()
        self.frameIndex = 0
        self.animationSpeed = 0.15
        self.status = 'idle'
        self.importSpellAssets()
        self.image = self.animations['idle'][self.frameIndex]
        self.rect = self.image.get_rect(center = spawnPosition)
        self.hitbox = pygame.Rect(0, 0, 11, 23)
        self.target = target
        self.spawnPosition = spawnPosition
        self.spawnTime = pygame.time.get_ticks()
        self.startMove = 1000
        self.endLife = 5000
        self.speed = 4
        if self.target[0] - spawnPosition[0] > 0:
            self.goingRight = False
        else:
            self.goingRight = True

    def importSpellAssets(self):
        characterPath = path.join('Assets','enemies','bossSummon')
        self.animations = {'idle':[],'moving':[]}

        for animation in self.animations.keys():
            fullPath = path.join(characterPath,animation)
            self.animations[animation] = importFolder(fullPath)

    def animate(self):
        animation = self.animations[self.status]
        #loop over frame index
        self.frameIndex += self.animationSpeed
        if self.frameIndex >= len(animation):
            self.frameIndex = 0

        image = animation[int(self.frameIndex)]
        if self.goingRight:
            self.image = image
        else:
            flippedImage = pygame.transform.flip(image, True, False)
            self.image = flippedImage

        self.hitbox.center = self.rect.center

    def moveTowardTarget(self):
        projectileVector = pygame.math.Vector2(self.spawnPosition)
        targetVector = pygame.math.Vector2(self.target)
        towards = (targetVector - projectileVector).normalize() * self.speed
        self.rect.x += towards.x
        self.rect.y += towards.y

    def drawing(self, surface, scrolling):
        x = int(self.rect.x - scrolling.x)
        y = int(self.rect.y - scrolling.y)
        surface.blit(self.image, (x, y))

    def update(self):
        self.animate()
        currentTime = pygame.time.get_ticks()
        if currentTime - self.spawnTime >= self.startMove:
            self.status = 'moving'
            self.moveTowardTarget()
        elif currentTime - self.spawnTime >= self.endLife:
            self.kill()


from zipline.pipeline.factors import CustomFactor
from zipline.pipeline.data import USEquityPricing
import numpy as np
import warnings

def recurs_sum(arr):
    arr_sum = np.zeros(arr.shape)
    arr_sum[0] = arr[0]

    for i in range(1, len(arr)):
        arr_sum[i] = arr_sum[i-1]+arr[i]

    return arr_sum

class AD(CustomFactor):
    """
    Chaikin Accumulation Distribution Oscillator

    Volume indicator

    **Default Inputs:** USEquityPricing.high, USEquityPricing.low, USEquitypricing.close, USEquityPricing.volume

    **Default Window Length:** 14

    http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:accumulation_distribution_line
    """
    inputs = [USEquityPricing.close, USEquityPricing.high, USEquityPricing.low, USEquityPricing.volume]
    window_length = 14 + 1
    outputs = 'cho_yesterday', 'AD_yesterday', 'cho_today', 'AD_today'

    def compute(self, today, assets, out, close, high, low, vol):

        # ignore unimportant nan warnings
        warnings.filterwarnings('ignore')

        # close location value
        clv = ((close - low) - (high - close)) / (high - low)
        ad = clv * vol

        # today
        ad_revised = recurs_sum(ad[-14:])

        sma_3d = np.nanmean(ad_revised[-3:], axis=0)
        sma_10d = np.nanmean(ad_revised[-10:], axis=0)

        cho_today = sma_3d - sma_10d

        out.cho_today[:] = cho_today
        out.AD_today[:] = ad_revised[-1]

        # yesterday
        ad_revised = recurs_sum(ad[:14])

        sma_3d = np.nanmean(ad_revised[-3:], axis=0)
        sma_10d = np.nanmean(ad_revised[-10:], axis=0)

        cho_yesterday = sma_3d - sma_10d

        out.cho_yesterday[:] = cho_yesterday
        out.AD_yesterday[:] = ad_revised[-1]


from discord.ext.commands import bot, has_permissions
import discord.ext
from discord.ext import commands
from config import *
import asyncio
import random

# noinspection PyPackageRequirements
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix=PREFIX, intents=intents, description="Test bot for discord.py")

# Event for when the bot is ready and connected to Discord
@bot.event
async def on_ready():
    print('Bot is online and connected to Discord')
    print('------')

    # Send a heartbeat message to a specific channel when the bot comes online
    channel = bot.get_channel(1015712143145959504)
    await channel.send('Bot is online and ready!')

# Member join event notification
@bot.event
async def on_member_join(member, ctx, guild):
    guild = ctx.guild
    welcomeEmbed = discord.Embed(title=f"A new member has joined!",
                                 description=f"{member.name} has joined the {guild.name} server!",
                                 color=discord.Color.blue())

    await bot.get_channel(1015712143145959504).send(embed=welcomeEmbed)

# Define the "hello" command
@bot.command()
async def hello(ctx):
    await ctx.send(f"Hello, {ctx.author.name}!")

# Define the "Goodbye" command
@bot.command()
async def goodbye(ctx):
    await ctx.send(f"Goodbye, {ctx.author.name}!")

@bot.command()
async def clear(ctx, amount=5):  # Specify the amount of messages to clear, default is 5
    await ctx.channel.purge(limit=amount + 1)  # +1 to include the command message itself
    await ctx.send(f'{amount} messages cleared!',
                   delete_after=5)  # Send a confirmation message and delete it after 5 seconds

# Event: on_member_join
@bot.event
async def on_member_join(member):
    # Send a welcome message
    welcome_channel = discord.utils.get(member.guild.text_channels, name='general')  # Specify the channel
    welcome_message = f"Welcome {member.mention} to the server! Please choose a role from the following options: Cincinnati, Kentucky, Indiana"
    await welcome_channel.send(welcome_message)

# Define a server info command
@bot.event
async def on_message(message, ctx):
    if message.author == bot.user:
        return

    if message.content.startswith('!serverinfo'):
        server = ctx.guild
    server_name = server.name
    server_id = server.id
    server_owner = server.owner
    server_region = server.region
    server_member_count = server.member_count

    # Create an embed to display the server information
    embed = discord.Embed(title='Server Info', color=discord.Color.green())
    embed.add_field(name='Server Name', value=server_name, inline=False)
    embed.add_field(name='Server ID', value=server_id, inline=False)
    embed.add_field(name='Server Owner', value=server_owner, inline=False)
    embed.add_field(name='Server Region', value=server_region, inline=False)
    embed.add_field(name='Member Count', value=server_member_count, inline=False)

    # Send the embed to the server info command invoker
    await ctx.send(embed=embed)

# Slow mode feature
@bot.event
async def on_message(message):
    if message.author.bot:
        return  # Ignore messages from bots

    # Implement slow mode
    slowmode_seconds = 5  # Set the slow mode duration in seconds
    max_messages = 3  # Set the maximum number of messages allowed in the slow mode duration
    cooldown = 10  # Initialize the cooldown counter

    async for prev_message in message.channel.history(limit=max_messages, before=message):
        if prev_message.author == message.author:
            # Check if the user has sent enough messages to trigger slow mode
            cooldown += 1
            if cooldown >= max_messages:
                await message.channel.send(f'{message.author.mention}, you are sending messages too quickly. '
                                           f'Please wait {slowmode_seconds} seconds before sending another message.')
                await asyncio.sleep(slowmode_seconds)
                cooldown = 10
            break

    await bot.process_commands(message)

# Kick command
@bot.command(name="kick", pass_context=True)
@has_permissions(manage_roles=True, ban_members=True)
async def kick(ctx, member: discord.Member, *, reason=None):
    await member.kick(reason=reason)
    await ctx.send(f'{member.name} has been kicked.')

# Ban command
@bot.command(name="ban", pass_context=True)
@has_permissions(ban_members=True)
async def ban(ctx, member: discord.Member, *, reason=None):
    await member.ban(reason=reason)
    await ctx.send(f'{member.name} has been banned.')

# Unban command
@bot.command()
@has_permissions(ban_members=True)
async def unban(ctx, member_id):
    banned_users = await ctx.guild.bans()
    member = discord.utils.get(banned_users, user__id=int(member_id))
    await ctx.guild.unban(member.user)
    await ctx.send(f'{member.user.name} has been unbanned.')

# Define the mute and unmute commands
@bot.command()
async def mute(ctx, member: discord.Member):
    # Check if the bot has the "manage_roles" permission
    if not ctx.author.guild_permissions.manage_roles:
        await ctx.send("You don't have permission to mute members.")
        return
    if not ctx.me.guild_permissions.manage_roles:
        await ctx.send("I don't have permission to mute members.")
        return

    # Find the "Muted" role in the server
    muted_role = discord.utils.get(ctx.guild.roles, name="Muted")

    # If the "Muted" role doesn't exist, create it
    if not muted_role:
        muted_role = await ctx.guild.create_role(name="Muted", reason="Mute command")
        for channel in ctx.guild.channels:
            await channel.set_permissions(muted_role, send_messages=False)

    # Add the "Muted" role to the member
    await member.add_roles(muted_role)
    await ctx.send(f"{member.display_name} has been muted.")

@bot.command()
async def unmute(ctx, member: discord.Member):
    # Check if the bot has the "manage_roles" permission
    if not ctx.author.guild_permissions.manage_roles:
        await ctx.send("You don't have permission to unmute members.")
        return
    if not ctx.me.guild_permissions.manage_roles:
        await ctx.send("I don't have permission to unmute members.")
        return

    # Find the "Muted" role in the server
    muted_role = discord.utils.get(ctx.guild.roles, name="Muted")

    # If the "Muted" role doesn't exist, send an error message
    if not muted_role:
        await ctx.send("The 'Muted' role doesn't exist.")
        return

    # Remove the "Muted" role from the member
    await member.remove_roles(muted_role)
    await ctx.send(f"{member.display_name} has been unmuted.")

# Tictactoe
@bot.event
async def on_message(message):
    if message.author == bot.user:
        return

    if message.content.startswith('!tictactoe'):
        player1 = message.author
        player2 = message.mentions[0] if message.mentions else None

        if not player2:
            await message.channel.send('Please mention another player to play with.')
            return

        if player1 == player2:
            await message.channel.send('You cannot play against yourself!')
            return

        board = [':white_large_square:', ':white_large_square:', ':white_large_square:',
                 ':white_large_square:', ':white_large_square:', ':white_large_square:',
                 ':white_large_square:', ':white_large_square:', ':white_large_square:']
        winner = None
        turn = player1
        game_over = False

        while not game_over:
            if turn == player1:
                sign = ':regional_indicator_x:'
            else:
                sign = ':o:'

            await message.channel.send(f'{turn.mention}\'s turn. Choose a position to place {sign} in.')

            def check(m):
                return m.author == turn and m.content.isdigit() and int(m.content) in range(1, 10) and board[
                    int(m.content) - 1] == ':white_large_square:'

            try:
                msg = await bot.wait_for('message', check=check, timeout=30.0)
            except asyncio.TimeoutError:
                await message.channel.send(f'{turn.mention} took too long to respond. Game ended.')
                game_over = True
                break

            position = int(msg.content) - 1
            board[position] = sign

            await print_board(message.channel, board)

            if check_win(board):
                winner = turn
                game_over = True
            elif check_draw(board):
                game_over = True
            else:
                turn = player2 if turn == player1 else player1

        if winner:
            await message.channel.send(f'Congratulations, {winner.mention}! You won!')
        else:
            await message.channel.send('It\'s a draw!')

async def print_board(channel, board):
    line = ''
    for i, sign in enumerate(board):
        if i % 3 == 2:
            line += sign + '\n'
        else:
            line += sign
    await channel.send(line)

def check_win(board):
    return (board[0] == board[1] == board[2] != ':white_large_square:' or
            board[3] == board[4] == board[5] != ':white_large_square:' or
            board[6] == board[7] == board[8] != ':white_large_square:' or
            board[0] == board[3] == board[6] != ':white_large_square:' or
            board[1] == board[4] == board[7] != ':white_large_square:' or
            board[2] == board[5] == board[8] != ':white_large_square:' or
            board[0] == board[4] == board[8] != ':white_large_square:' or
            board[2] == board[4] == board[6] != ':white_large_square:')

def check_draw(board):
    return all([sign != ':white_large_square:' for sign in board])

 # Define a class for the blackjack game

class Blackjack:
    def __init__(self, ctx):
        self.ctx = ctx
        self.deck = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] * 4
        self.player_hand = []
        self.dealer_hand = []
        self.player_score = 0
        self.dealer_score = 0

    def deal(self):
        for _ in range(2):
            card = random.choice(self.deck)
            self.player_hand.append(card)
            self.deck.remove(card)
            card = random.choice(self.deck)
            self.dealer_hand.append(card)
            self.deck.remove(card)

    def hit(self, hand):
        card = random.choice(self.deck)
        hand.append(card)
        self.deck.remove(card)

    def calculate_score(self, hand):
        score = 0
        num_aces = 0
        for card in hand:
            if card == 'A':
                num_aces += 1
            elif card in ['J', 'Q', 'K']:
                score += 10
            else:
                score += int(card)
        while num_aces > 0 and score > 21:
            score -= 10
            num_aces -= 1
        return score

    async def play(self):
        self.deal()
        await self.ctx.send(f"**Your hand:** {', '.join(self.player_hand)}")
        await self.ctx.send(f"**Dealer's up card:** {self.dealer_hand[0]}")
        while True:
            await self.ctx.send("Do you want to hit or stand? (Type `hit` or `stand`)")
            try:
                msg = await bot.wait_for('message', check=lambda message: message.author == self.ctx.author,
                                         timeout=30)
                if msg.content.lower() == 'hit':
                    self.hit(self.player_hand)
                    await self.ctx.send(f"**Your hand:** {', '.join(self.player_hand)}")
                    self.player_score = self.calculate_score(self.player_hand)
                    if self.player_score > 21:
                        await self.ctx.send("Bust! You lost!")
                        break
                elif msg.content.lower() == 'stand':
                    while self.calculate_score(self.dealer_hand) < 17:
                        self.hit(self.dealer_hand)
                    self.dealer_score = self.calculate_score(self.dealer_hand)
                    await self.ctx.send(f"**Dealer's hand:** {', '.join(self.dealer_hand)}")
                    if self.dealer_score > 21:
                        await self.ctx.send("Dealer busts! You win!")
                    elif self.dealer_score < self.player_score:
                        await self.ctx.send("You win!")
                    elif self.dealer_score > self.player_score:
                        await self.ctx.send("You lost!")
                    else:
                        await self.ctx.send("It's a tie!")
                    break
                else:
                    await self.ctx.send("Invalid choice! Type `hit` or `stand`.")
            except asyncio.TimeoutError:
                await self.ctx.send("Timeout! You took too long to respond.")
                break

# Define a command for playing blackjack

@bot.command()
async def blackjack(ctx):
    game = Blackjack(ctx)
    await game.play()

# Define the slots emojis
SLOTS_EMOJIS = ['🍇', '🍊', '🍒', '🍓', '🍌', '🍏', '🍆']

@bot.event
async def on_message(message):
    if message.author.bot:
        return

    if message.content.startswith('!slots'):
        # Spin the slots
        slots_result = spin_slots()

        # Send the slots result
        await message.channel.send(slots_result)

def spin_slots():
    # Generate a list of random slots emojis
    slots = [random.choice(SLOTS_EMOJIS) for _ in range(3)]

    # Check for a winning combination
    if slots[0] == slots[1] and slots[1] == slots[2]:
        # If all three emojis are the same, it's a win
        return f'{slots[0]} {slots[1]} {slots[2]}\nYou win!'
    else:
        # Otherwise, it's a loss
        return f'{slots[0]} {slots[1]} {slots[2]}\nYou lose.'

@bot.command()
async def roulette(ctx, bet: int, guess: str):
    # Convert guess to lowercase for case-insensitive comparison
    guess = guess.lower()
    valid_guesses = ['red', 'black', 'green']

    if guess not in valid_guesses:
        await ctx.send('Invalid guess. Please choose from red, black, or green.')
        return

    if bet <= 0:
        await ctx.send('Invalid bet. Please place a bet greater than 0.')
        return

    # Simulate the roulette spin
    spin = random.choice(valid_guesses)

    if spin == 'green':
        win_amount = bet * 14
        if guess == 'green':
            await ctx.send(f'Congratulations! The spin is **{spin}**. You win {win_amount} credits!')
        else:
            await ctx.send(f'Unlucky! The spin is **{spin}**. You lose {bet} credits.')
    else:
        win_amount = bet * 2
        if guess == spin:
            await ctx.send(f'Congratulations! The spin is **{spin}**. You win {win_amount} credits!')
        else:
            await ctx.send(f'Unlucky! The spin is **{spin}**. You lose {bet} credits.')

@bot.event
async def on_message(message):
    if message.author.bot:
        return

    if message.content.startswith('!rps'):
        # Split the message into two parts: command and opponent's choice
        parts = message.content.split()
        if len(parts) != 2:
            await message.channel.send('Usage: !rps <rock/paper/scissors>')
            return

        # Store the user's choice and generate a random choice for the opponent
        user_choice = parts[1].lower()
        choices = ['rock', 'paper', 'scissors']
        bot_choice = random.choice(choices)

        # Check if the user's choice is valid
        if user_choice not in choices:
            await message.channel.send('Invalid choice. Choose either rock, paper, or scissors.')
            return

        # Determine the winner and send the result to the channel
        if user_choice == bot_choice:
            await message.channel.send(f'Tie! Both chose {user_choice}.')
        elif (user_choice == 'rock' and bot_choice == 'scissors') or \
                (user_choice == 'paper' and bot_choice == 'rock') or \
                (user_choice == 'scissors' and bot_choice == 'paper'):
            await message.channel.send(f'You won! You chose {user_choice} and the bot chose {bot_choice}.')
        else:
            await message.channel.send(f'You lost! You chose {user_choice} and the bot chose {bot_choice}.')

bot.run(TOKEN, reconnect=True)


import bisect
from typing import List

class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        items = sorted(envelopes, key=lambda x: (x[0], -x[1]))
        piles = []
        for item in items:
            v = item[1]
            i = bisect.bisect_left(piles, v)
            if i == len(piles):
                piles.append(v)
            else:
                piles[i] = v
        return len(piles)

solution = Solution()
envelopes = [[5,4],[6,4],[6,7],[2,3]]
assert solution.maxEnvelopes(envelopes) == 3, "Should be 3"

leetcode/354. Russian Doll Envelopes/index.py


import os
import requests
from bs4 import BeautifulSoup

#os.system("clear")

def crawl():
    url = "https://www.iban.com/currency-codes"

    iban_result = requests.get(url)
    iban_soup = BeautifulSoup(iban_result.text, "html.parser")

    table = iban_soup.find("table", {"class": "table table-bordered downloads tablesorter"})

    tbody = table.find("tbody")
    tds = tbody.find_all("td")

    information = {}
    length = len(tds)
    key = 0
    country = []
    for i in range(0, length, 4):
        if tds[i + 1].string == "No universal currency":
            continue
        else:
            country.append(tds[i].string.capitalize())
            country.append(tds[i + 1].string.capitalize())
            country.append(tds[i + 2].string)
            country.append(tds[i + 3].string)
            information[key] = country
            key = key + 1
            country = []

    return information

def main():
    country_dic = crawl()
    print("Hello! Please choose select a country by number: ")

    for i in range(len(country_dic)):
        print('# {} {}'.format(i, country_dic[i][0]))

    while (True):
        try:
            country_num = int(input("#: "))
            if country_num >= len(country_dic):
                print("Choose a number from the list.")
                continue
            else:
                break
        except:
            print("That wasn't a number.")
            continue

    print(f"You choose {country_dic[country_num][0]}")
    print(f"The currency code is {country_dic[country_num][2]}")

main()