#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jul  1 14:58:18 2020

@author: jaisi8631
"""

# ------------------
# REGULAR IMPORTS
# ------------------
import pickle
import pandas as pd
import numpy as np
import math
from scipy import signal
import matplotlib.pyplot as plt

from sklearn.metrics import confusion_matrix
from sklearn.metrics import precision_recall_fscore_support
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from sklearn.utils import class_weight
from sklearn import decomposition
from sklearn.model_selection import GridSearchCV

import keras
from keras.utils.vis_utils import plot_model
from keras.models import Model
from keras.optimizers import Adam
from keras.models import load_model
from keras.callbacks import ModelCheckpoint
from keras.callbacks import EarlyStopping
from keras.wrappers.scikit_learn import KerasClassifier

# ------------------
# LSTM IMPORTS
# ------------------
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM

# ------------------
# CNN IMPORTS
# ------------------
from keras.optimizers import SGD
from keras.layers import Reshape
from keras.layers import Conv1D
from keras.layers import InputLayer
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers import MaxPooling1D
from keras.layers import GlobalAveragePooling1D

# ------------------
# CONSTANTS
# ------------------
START_TIME_INTERICTAL = 407898590000
END_TIME_INTERICTAL = 407998350000
START_TIME_ICTAL = 416039606029
END_TIME_ICTAL = 416112464960
FS = 1024
DOWN_SAMPLE_FACTOR = 10
STEP_SIZE = 256
SEQUENCE_LEN = 1024
SEQUENCE_PCA = 1000

TRAIN_SIZE = 0.8
TEST_SIZE = 0.1
VAL_SIZE = 0.1

EPOCHS = 5
BS = 128
LR = 0.01
NUM_CLASSES = 2
MOMENTUM = 0.1
DECAY = 1e-6

GS_EPOCHS = [1, 2]
GS_BS = [32, 64]
GS_OPTIMIZERS = ['adam', 'rmsprop']

ADAM_DEFAULT = 'adam'
SGD_DEFAULT = 'sgd'
ADAM_CUSTOM = Adam(lr = LR)
SGD_CUSTOM = SGD(lr = LR, momentum = MOMENTUM, decay = DECAY, nesterov = True)

PATH_INTERICTAL = "../datasets/hup138-interictal.pickle"
PATH_ICTAL = "../datasets/hup138-ictal.pickle"

# ------------------
# LOSS HISTORY
# ------------------
class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.history = {'loss':[],'acc':[]}

    def on_batch_end(self, batch, logs={}):
        self.history['loss'].append(logs.get('loss'))
        self.history['acc'].append(logs.get('acc'))

# ------------------
# LOAD DATA
# ------------------
def get_data(path):
    with open(path, 'rb') as f: data, fs = pickle.load(f)
    labels = pd.read_csv("labels/hup138-labels.csv", header = None)
    labels_list = labels[0].tolist()
    data = data[data.columns.intersection(labels_list)]
    return data

def iEEG_data_filter(data, fs, cutoff1, cutoff2, notch):
	column_names = data.columns
	data = np.array(data)
	number_of_channels = data.shape[1]

    # Cut-off frequency of the filter
	fc = np.array([cutoff1, cutoff2])
    # Normalize the frequency
	w = fc / np.array([(fs / 2), (fs / 2)])

	b, a = signal.butter(4, w, 'bandpass')
	filtered = np.zeros(data.shape)
	for i in np.arange(0,number_of_channels):
		filtered[:,i] = signal.filtfilt(b, a, data[:,i])
	filtered = filtered + (data[0] - filtered[0])
	f0 = notch
	q = 30
	b, a = signal.iirnotch(f0, q, fs)
	notched = np.zeros(data.shape)
	for i in np.arange(0, number_of_channels):
		notched[:, i] = signal.filtfilt(b, a, filtered[:, i])
	notched_df = pd.DataFrame(notched, columns=column_names)
	return notched_df

# ------------------
# DATASET OUTLINE
# ------------------
def create_timestamps(data):
    timestamps = []
    for i in range(0, data.shape[0]):
        timestamps.append(START_TIME_ICTAL + (i * FS))
    return timestamps

# ------------------
# PROCESS DATA
# ------------------
def create_merge_dataset(data, split_point, start_time_interictal,
                         end_time_interictal, start_time_ictal, end_time_ictal):
    dataset = []
    dataset_targets = []
    labels = pd.read_csv("labels/hup138-labels.csv", header = None)

    for column in data:

        print("Reading data for electrode " + column)

        col_list = data[column].tolist()
        col_data = labels.loc[labels[0] == column]

        col_start_time = col_data.iat[0, 1]
        col_end_time = col_data.iat[0, 2]

        if(col_start_time == '-' or col_end_time == '-'):
            col_start_time = int(start_time_ictal + 1)
            col_end_time = int(start_time_ictal + 1)
        else:
            col_start_time = int(col_start_time)
            col_end_time = int(col_end_time)

        # first process interictal data
        for index in range(SEQUENCE_LEN, split_point, STEP_SIZE):
            sequence = col_list[(index - SEQUENCE_LEN) : index]
            sequence = [[i] for i in sequence]
            dataset.append(sequence)

            sequence_end_time = start_time_interictal + (index * FS * 10)

            if(sequence_end_time >= col_start_time and
               sequence_end_time <= col_end_time):
                dataset_targets.append(1)
            else:
                dataset_targets.append(0)

        # then process ictal data
        for index in range(SEQUENCE_LEN, data.shape[0] - split_point, STEP_SIZE):
            new_index = index + split_point
            sequence = col_list[(new_index - SEQUENCE_LEN) : new_index]
            sequence = [[i] for i in sequence]
            dataset.append(sequence)

            sequence_end_time = start_time_ictal + (index * FS * 10)

            if(sequence_end_time >= col_start_time and
               sequence_end_time <= col_end_time):
                dataset_targets.append(1)
            else:
                dataset_targets.append(0)

    dataset = np.array(dataset)
    dataset_targets = np.array(dataset_targets)

    return dataset, dataset_targets

def scale_data(dataset):
    scaler = StandardScaler()
    dataset = scaler.fit_transform(dataset.reshape(-1, dataset.shape[-1])).reshape(dataset.shape)
    return dataset

def apply_pca(dataset, components):
    pca = decomposition.PCA(n_components = components)
    pca.fit(components)
    dataset = pca.transform(dataset)
    return dataset

def create_class_weights(y_train):
    class_weights = class_weight.compute_class_weight('balanced',
                                                      classes = np.unique(y_train),
                                                      y = y_train)
    class_weight_dict = dict(enumerate(class_weights))
    return class_weight_dict

# ------------------
# LSTM MODEL
# ------------------
def create_lstm_model():
    model = Sequential()
    model.add(LSTM(256, input_shape = (SEQUENCE_LEN, 1), dropout = 0.2, recurrent_dropout = 0.2, return_sequences = True))
    model.add(LSTM(32, dropout = 0.2, recurrent_dropout = 0.2, return_sequences = True))
    model.add(LSTM(32, return_sequences = False))
    model.add(Dense(1, activation = 'sigmoid'))
    print(model.summary())
    plot_model(model, to_file = 'figures/lstm_diagram.png',
               show_shapes = True, show_layer_names = True)
    return model

def train_lstm_model(model, model_name, x_train, y_train, x_val, y_val):
    chk = ModelCheckpoint('models/' + model_name + '.pkl',
                          monitor = 'val_accuracy',
                          save_best_only = True,
                          mode = 'max',
                          verbose = 1)
    lstm_history = LossHistory()
    callbacks_list = [
        chk,
        lstm_history
    ]

    model.compile(loss = 'binary_crossentropy',
                  optimizer = ADAM_CUSTOM,
                  metrics = ['accuracy'])

    class_weights = create_class_weights(y_train)

    model.fit(x_train, y_train,
              class_weight = class_weights,
              epochs = EPOCHS,
              batch_size = BS,
              callbacks = callbacks_list,
              validation_data = (x_val, y_val))

    return model, lstm_history

# ------------------
# CNN MODEL
# ------------------
def create_custom_cnn_model():
    model = Sequential()
    model.add(Conv1D(500, 100, activation='relu', input_shape = (SEQUENCE_LEN, 1)))
    model.add(Dropout(0.5))
    model.add(MaxPooling1D(3))
    model.add(Conv1D(100, 10, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Conv1D(100, 10, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Conv1D(10, 10, activation='relu'))
    model.add(GlobalAveragePooling1D())
    model.add(Dropout(0.5))
    model.add(Dense(1, activation = 'sigmoid'))
    print(model.summary())
    plot_model(model, to_file = 'figures/cnn_custom_diagram.png',
               show_shapes = True, show_layer_names = True)
    return model

def create_wavenet_cnn_model():
    model = Sequential()
    model.add(Conv1D(1, 100, activation='relu', input_shape = (SEQUENCE_LEN, 1)))
    for rate in (1, 2, 4, 8) * 2:
        model.add(Conv1D(filters = 20, kernel_size = 2, padding = 'causal',
                         activation = 'relu', dilation_rate = rate))
        model.add(Dropout(0.1))
    model.add(Conv1D(filters = 10, kernel_size = 1))
    model.add(GlobalAveragePooling1D())
    model.add(Dense(1, activation = 'sigmoid'))
    print(model.summary())
    plot_model(model, to_file = 'figures/cnn_wavenet_diagram.png',
               show_shapes = True, show_layer_names = True)
    return model

def train_cnn_model(model, model_name, x_train, y_train, x_val, y_val):
    chk = ModelCheckpoint('models/' + model_name + '.pkl',
                          monitor = 'val_accuracy',
                          save_best_only = True,
                          mode = 'max',
                          verbose = 1)
    cnn_history = LossHistory()
    callbacks_list = [
        chk,
        cnn_history
    ]

    model.compile(loss = 'binary_crossentropy',
                  optimizer = ADAM_CUSTOM,
                  metrics = ['accuracy'])

    class_weights = create_class_weights(y_train)

    model.fit(x_train, y_train,
              class_weight = class_weights,
              batch_size = BS,
              epochs = EPOCHS,
              callbacks = callbacks_list,
              validation_data = (x_val, y_val))

    return model, cnn_history

# ------------------
# GRID SEARCH
# ------------------
def lstm_gridsearch(optimizer = 'adam'):
    model = Sequential()
    model.add(LSTM(256, input_shape = (SEQUENCE_LEN, 1), dropout = 0.2, recurrent_dropout = 0.2, return_sequences = True))
    model.add(LSTM(32, dropout = 0.2, recurrent_dropout = 0.2, return_sequences = True))
    model.add(LSTM(32, return_sequences = False))
    model.add(Dense(1, activation = 'sigmoid'))
    model.compile(loss = 'binary_crossentropy', optimizer = optimizer, metrics = ['accuracy'])
    return model

def cnn_gridsearch(optimizer = 'adam'):
    model = Sequential()
    model.add(Conv1D(500, 100, activation='relu', input_shape = (SEQUENCE_LEN, 1)))
    model.add(Dropout(0.5))
    model.add(MaxPooling1D(3))
    model.add(Conv1D(100, 10, activation='relu'))
    model.add(Dropout(0.2))
    model.add(Conv1D(10, 10, activation='relu'))
    model.add(GlobalAveragePooling1D())
    model.add(Dropout(0.5))
    model.add(Dense(1, activation = 'sigmoid'))
    model.compile(loss = 'binary_crossentropy', optimizer = optimizer, metrics = ['accuracy'])
    return model

def build_gridsearch(build_function, x_train, y_train, model_name):
    grid = {'epochs': GS_EPOCHS,
            'batch_size': GS_BS,
            'optimizer': GS_OPTIMIZERS}
    load_model = KerasClassifier(build_fn = build_function)

    # train using GridSearch
    validator = GridSearchCV(load_model,
                             param_grid = grid,
                             scoring = 'accuracy',
                             n_jobs = 1,
                             verbose = 10)
    validator.fit(x_train, y_train)

    # retain best model
    print('Best Parameters: ')
    model = validator.best_estimator_.model
    print(validator.best_params_)
    model.save('models/' + model_name + '.pkl')

    return model

# ------------------
# EVALUATE MODEL
# ------------------
def plot_batch_losses(history, plot_name):
    y1 = history.history['loss']
    y2 = history.history['acc']

    y3 = []
    for index, value in enumerate(y1):
        if value > 100:
            y3.append(index)
    for i in reversed(y3):
        del y1[i]

    x1 = np.arange(len(y1))
    fig, ax = plt.subplots()
    line1, = ax.plot(x1, y1, label = 'loss')
    ax.set_xlabel('Batch')
    ax.set_ylabel('Loss')
    plt.savefig('figures/' + plot_name + '.png')
    plt.show()

def model_acc(model_name):
    pickle_name = 'models/' + model_name + '.pkl'
    model = load_model(pickle_name)
    y_preds = model.predict_classes(x_test)
    acc = accuracy_score(y_test, y_preds)
    cm = confusion_matrix(y_test, y_preds)
    scores = precision_recall_fscore_support(y_test, y_preds, average = 'macro')
    return acc, cm, scores

# ------------------
# MAIN METHOD
# ------------------
if __name__=="__main__":

    # --------------------
    # DATA WRANGLING
    # --------------------
    # interictal
    data = get_data(PATH_INTERICTAL)
    timestamps = create_timestamps(data)
    data_filtered = iEEG_data_filter(data, FS, 0.16, 200, 60)
    fs_downSample = FS / DOWN_SAMPLE_FACTOR
    data_filtered_tmp = signal.decimate(data_filtered, DOWN_SAMPLE_FACTOR, axis = 0)
    data_filtered = pd.DataFrame(data_filtered_tmp, columns = data_filtered.columns); del data_filtered_tmp
    data_interictal = data_filtered

    # ictal
    data = get_data(PATH_ICTAL)
    timestamps = create_timestamps(data)
    data_filtered = iEEG_data_filter(data, FS, 0.16, 200, 60)
    fs_downSample = FS / DOWN_SAMPLE_FACTOR
    data_filtered_tmp = signal.decimate(data_filtered, DOWN_SAMPLE_FACTOR, axis = 0)
    data_filtered = pd.DataFrame(data_filtered_tmp, columns = data_filtered.columns); del data_filtered_tmp
    data_ictal = data_filtered

    # concatenate
    data = pd.concat([data_interictal, data_ictal], ignore_index = True)
    dataset, dataset_targets = create_merge_dataset(data, data_interictal.shape[0],
                                              START_TIME_INTERICTAL, END_TIME_INTERICTAL,
                                              START_TIME_ICTAL, END_TIME_ICTAL)

    # --------------------
    # DATASET PROCESSING
    # --------------------
    # optional preprocessing methods
    """
    dataset = scale_data(dataset)
    dataset = apply_pca(dataset, SEQUENCE_PCA)
    """

    # random split:
    """
    x_train, x_test, y_train, y_test = train_test_split(dataset,
                                                        dataset_targets,
                                                        test_size = 0.2)
    x_test, x_val, y_test, y_val = train_test_split(x_test,
                                                    y_test,
                                                    test_size = 0.5)
    print("Number of seizing instances in training set: " +
          str(np.count_nonzero(y_train == 1)))
    print("Number of non-seizing instances in training set: " +
          str(np.count_nonzero(y_train == 0)))
    print("Number of seizing instances in testing set: " +
          str(np.count_nonzero(y_test == 1)))
    print("Number of non-seizing instances in testing set: " +
          str(np.count_nonzero(y_test == 0)))
    """

    # per-electrode split:
    rows, cols, depth = dataset.shape
    num_train = int(TRAIN_SIZE * rows)
    num_test = int(TEST_SIZE * rows)
    num_val = int(VAL_SIZE * rows)

    x_train = dataset[ : num_train, :]
    x_test = dataset[num_train : num_train + num_test, :]
    x_val = dataset[num_train + num_test : , :]

    y_train = dataset_targets[ : num_train]
    y_test = dataset_targets[num_train : num_train + num_test]
    y_val = dataset_targets[num_train + num_test : ]
    print("Number of seizing instances in training set: " +
          str(np.count_nonzero(y_train == 1)))
    print("Number of non-seizing instances in training set: " +
          str(np.count_nonzero(y_train == 0)))
    print("Number of seizing instances in testing set: " +
          str(np.count_nonzero(y_test == 1)))
    print("Number of non-seizing instances in testing set: " +
          str(np.count_nonzero(y_test == 0)))

    # --------------------
    # OPTION 1: Models
    # --------------------
    # train wavenet CNN
    cnn_model = create_wavenet_cnn_model()
    cnn_model_name = 'eeg-model-cnn-wavenet'
    cnn_model, cnn_history = train_cnn_model(cnn_model, cnn_model_name,
                                 x_train, y_train, x_val, y_val)
    plot_batch_losses(cnn_history, 'cnn-wavenet-history')

    # test wavenet CNN
    cnn_acc, cnn_cm, cnn_scores = model_acc(cnn_model_name)
    print("CNN WaveNet Test Set Accuracy: ")
    print("%.4f" % round(cnn_acc, 4))
    print("CNN WaveNet Test Set Confusion Matrix: ")
    print(cnn_cm)

    """
    # train LSTM
    lstm_model = create_lstm_model()
    lstm_model_name = 'eeg-model-lstm'
    lstm_model, lstm_history = train_lstm_model(lstm_model, lstm_model_name,
                                  x_train, y_train, x_val, y_val)
    plot_batch_losses(lstm_history, 'lstm-history')

    # test LSTM
    lstm_acc, lstm_cm, lstm_scores = model_acc(lstm_model_name)
    print("LSTM Test Set Accuracy: ")
    print("%.4f" % round(lstm_acc, 4))
    print("LSTM Test Set Confusion Matrix: ")
    print(lstm_cm)

    # train custom CNN
    cnn_model = create_custom_cnn_model()
    cnn_model_name = 'eeg-model-cnn'
    cnn_model, cnn_history = train_cnn_model(cnn_model, cnn_model_name,
                                 x_train, y_train, x_val, y_val)
    plot_batch_losses(cnn_history, 'cnn-history')

    # test CNN
    cnn_acc, cnn_cm, cnn_scores = model_acc(cnn_model_name)
    print("CNN Test Set Accuracy: ")
    print("%.4f" % round(cnn_acc, 4))
    print("CNN Test Set Confusion Matrix: ")
    print(cnn_cm)
    """

    # --------------------
    # OPTION 2: GridSearch
    # --------------------
    """
    # train and test LSTM
    gs_lstm_name = 'egg-lstm-gridsearch'
    gs_lstm = build_gridsearch(lstm_gridsearch, x_train, y_train, gs_lstm_name)
    gs_lstm_acc = model_acc(gs_lstm_name)
    print("GridSearch LSTM Test Set Accuracy: ")
    print("%.4f" % round(gs_lstm_acc, 4))

    # train and test CNN
    gs_cnn_name = 'eeg-cnn-gridsearch'
    gs_cnn = build_gridsearch(cnn_gridsearch, x_train, y_train, gs_cnn_name)
    gs_cnn_acc = model_acc(gs_cnn_name)
    print("GridSearch CNN Test Set Accuracy: ")
    print("%.4f" % round(gs_cnn_acc, 4))
    """


import pygame
import random

pygame.init()

black = (0, 0, 0)
blue = (65, 167, 198)
red = (255, 0, 0)
orange = (249, 102, 63)
grey = (109, 107, 107)
width, height = 600, 400

game_display = pygame.display.set_mode((width, height))

pygame.display.set_caption('Sanik Game')

snake_size = 10
snake_speed = 15

font_style = pygame.font.SysFont('ubuntu', 30)
'''def message(msg,color):
    mesg = font_style.render(msg, True, color)
    game_display.blit(mesg, [width/2, height/2])'''

clock = pygame.time.Clock()

def our_snake(snake_size, snake_List, color):
    if color==orange:
        for x in snake_List:
            pygame.draw.rect(game_display, color, [x[0], x[1], snake_size, snake_size])
    elif color==blue:
        for x in snake_List:
            pygame.draw.rect(game_display, color, [x[0], x[1], snake_size, snake_size])

#pygame.draw.rect(game_display, orange, [fuudx, fuudy, snake_size, snake_size])
#ygame.draw.rect(game_display, blue, [foodx, foody, snake_size, snake_size])

def main_game():

    global color,y,snake_speed
   # global foodx,foody,fuudx,fuudy

    foodx = round(random.randrange(0, width - snake_size) / 10.0) * 10.0
    foody = round(random.randrange(0, height - snake_size) / 10.0) * 10.0

    fuudx = round(random.randrange(0, width - snake_size) / 10.0) * 10.0
    fuudy = round(random.randrange(0, height - snake_size) / 10.0) * 10.0

    food_color = ['blue', 'orange']

    color=blue
    game_over = False
    game_close = False
    x1 = width / 2
    y1 = height / 2

    x1_new = 0
    y1_new = 0

    snake_List = []
    Length_of_snake = 1

    x = random.randrange(0, 2)
    y = food_color[x]

    while not game_over:
        while game_close == True:
            game_display.fill(black)

            mesg = font_style.render('You Lost! or Press R-Play Again or Press Q-Quit', True, grey)
            game_display.blit(mesg, [width / 11, height / 3])
            pygame.display.update()
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        game_over = True
                        game_close = False
                    elif event.key == pygame.K_r:
                        main_game()
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            if event.type == pygame.KEYDOWN:
                '''if Length_of_snake>3:
                    print(Length_of_snake)

                    if snake_List[0][0] > snake_List[1][0]:# moving left
                        if event.key == pygame.K_DOWN:
                            x1_new = 0
                            y1_new = 10
                        elif event.key == pygame.K_LEFT:
                            x1_new = -10
                            y1_new = 0
                        elif event.key == pygame.K_UP:
                            x1_new = 0
                            y1_new = -10
                        elif event.key == pygame.K_RIGHT:
                             pass

                    elif snake_List[0][0] < snake_List[1][0]:# moving right
                        if event.key == pygame.K_DOWN:
                            x1_new = 0
                            y1_new = 10
                        elif event.key == pygame.K_RIGHT:
                            x1_new = 10
                            y1_new = 0
                        elif event.key == pygame.K_UP:
                            x1_new = 0
                            y1_new = -10
                        elif event.key == pygame.K_LEFT:
                            pass

                    elif snake_List[0][1] < snake_List[1][1]:# moving up
                        if event.key == pygame.K_DOWN:
                            pass
                        elif event.key == pygame.K_LEFT:
                            x1_new = -10
                            y1_new = 0
                        elif event.key == pygame.K_RIGHT:
                            x1_new = 10
                            y1_new = 0
                        elif event.key == pygame.K_UP:
                            pass

                    elif snake_List[0][1] > snake_List[1][1]:# moving down
                        if event.key == pygame.K_UP:
                            x1_new = 0
                            y1_new = 10
                        elif event.key == pygame.K_LEFT:
                            x1_new = -10
                            y1_new = 0
                        elif event.key == pygame.K_RIGHT:
                            x1_new = 10
                            y1_new = 0
                        elif event.key == pygame.K_DOWN:
                            pass
                else:'''
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                            x1_new = -10
                            y1_new = 0
                    elif event.key == pygame.K_RIGHT:
                            x1_new = 10
                            y1_new = 0
                    elif event.key == pygame.K_UP:
                            x1_new = 0
                            y1_new = -10
                    elif event.key == pygame.K_DOWN:
                            x1_new = 0
                            y1_new = 10

        if x1 >= width or x1 < 0 or y1 > height or y1 < 0:
            game_close = True
            print('Your score:', Length_of_snake-1)

        x1 += x1_new
        y1 += y1_new
        game_display.fill(black)

        if y == 'orange':
            orange_snake=pygame.draw.rect(game_display, orange, [fuudx, fuudy, snake_size, snake_size])
        elif y=='blue':
            pygame.draw.rect(game_display, blue, [foodx, foody, snake_size, snake_size])

        snake_Head = []
        snake_Head.append(x1)
        snake_Head.append(y1)
        snake_List.append(snake_Head)
        if len(snake_List) > Length_of_snake:
            del snake_List[0]

        '''for x in snake_List[:-1]:
            if x[0] == snake_Head[0] and x[1]==snake_Head[1]:
                print(x, snake_Head)
                game_close = True

            if x == snake_Head:
                print('final hit', x, snake_Head)
                game_close = True
                print('Your score:', Length_of_snake-1)'''
        if x1 == foodx and y1 == foody:
            print(x1, y1)
            foodx = round(random.randrange(0, width - snake_size) / 10.0) * 10.0
            foody = round(random.randrange(0, height - snake_size) / 10.0) * 10.0
            x = random.randrange(0, 2)
            y = food_color[x]
            Length_of_snake += 1
            color=blue

        elif x1 == fuudx and y1 == fuudy:
            if snake_List[0][0]> snake_List[1][0]:
                x1_new = 10                             #moving left
                y1_new = 0
                x1 += x1_new
                y1 += y1_new
            elif snake_List[0][0]< snake_List[1][0]:
                x1_new = -10                            #moving right
                y1_new = 0
                x1 += x1_new
                y1 += y1_new
            elif snake_List[0][1]< snake_List[1][1]:
                x1_new = 0                              #moving up
                y1_new = -10
                x1 += x1_new
                y1 += y1_new
            elif snake_List[0][1]> snake_List[1][1]:
                x1_new = 0                               #moving down
                y1_new = 10
                x1 += x1_new
                y1 += y1_new
            print('orange food',x1, y1)
            fuudx = round(random.randrange(0, width - snake_size) / 10.0) * 10.0
            fuudy = round(random.randrange(0, height - snake_size) / 10.0) * 10.0
            print(snake_List)
            Length_of_snake += 1
            color = orange

            x = random.randrange(0, 2)
            y=food_color[x]

        our_snake(snake_size, snake_List, color)
        pygame.display.update()

        clock.tick(15)

    pygame.quit()
    quit()

main_game()


"""Change tasks to work with rankinglist tests

Revision ID: 665d18e875e9
Revises: 91e86031f915
Create Date: 2020-06-06 14:26:50.428429

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '665d18e875e9'
down_revision = '91e86031f915'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('task', sa.Column('rankinglist_test_id', sa.Integer(), nullable=True))
    op.create_foreign_key(None, 'task', 'rankinglist_tests', ['rankinglist_test_id'], ['id'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'task', type_='foreignkey')
    op.drop_column('task', 'rankinglist_test_id')
    # ### end Alembic commands ###


from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.dependency.usecase import user_usecase

from app.presentation.form.user import UserCreateModel, UserReadModel
from app.usecase.user import UserUseCase

from app.dependency.repository import user_repository

router = APIRouter(
    prefix='/users',
    tags=["users"],
)

@router.post("/")
def create_user(
    user: UserCreateModel,
    useCase: UserUseCase = Depends(user_usecase)
):
    # db_user = useCase.get_by_email(email=user.email)
    # if db_user:
    #     raise HTTPException(status_code=400, detail="Email already registered")
    return useCase.register(user=user)

@router.get("/me")
async def read_user_me():
    return { "user_id": "the current user" }

@router.get("/{user_id}", response_model=UserReadModel)
def read_user(
    user_id: str,
    useCase: UserUseCase = Depends(user_usecase)
):
    user = useCase.get(user_id=user_id)
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user


# -*- coding: utf-8 -*-
# __author__ = "qingchen.zhang"
# 20190908

import os
import json
from biocluster.core.exceptions import OptionError
from biocluster.module import Module

class GenePredictGffModule(Module):
    """
    细菌比较基因组seq+gff序列预测模块
    分析内容：编码基因预测、tRNA预测、rRNA预测，及其相关信息统计
    这部分的基因预测不再区分染色体和质粒，因为主要是基因预测的软件不同，如果基因预测的结果cds数目为0，前端直接报错，不会传给后台；
    """

    def __init__(self, work_id):
        super(GenePredictGffModule, self).__init__(work_id)
        option = [
            {"name": "genome", "type": "infile", "format": "sequence.fasta"},  # 染色体序列文件
            {"name": "genome_plas", "type": "infile", "format": "sequence.fasta"},  # 质粒序列文件
            {"name": "gene_prefix", "type": "string"},  # 基因前缀，自定义前缀,默认gene
            # 质粒前缀,字典形式含一个或多个质粒，"{‘plassmidA’: ‘plasa", ‘plassmidB’: ‘plasb"}"
            {"name": "genome_name", "type": "string"}, # 基因组名称
            {"name": "genome_type", "type": "string"}, #基因组类型是属于完成图还是扫描图
            {"name": "sample", "type": "string"}, #样品名称
            {"name": "plasmid_prefix", "type": "string"},
            # 质粒前缀,字典形式含一个或多个质粒，"{‘plassmidA’: ‘plasa", ‘plassmidB’: ‘plasb"}"
            {"name": "input_gff", "type": "infile", "format": "gene_structure.gff3"},  # 输入的gff文件
            {"name": "trna", "type": "string", "default": "-"},  # tRNA输入数目，如果没有填写就默认为-，如果为0或其他数字不执行预测
            {"name": "rrna", "type": "string", "default": "-"},  # rRNA输入数目，如果没有填写就默认为-，如果为0或其他数字不执行预测
            {"name": "cds_fnn", "type": "outfile", "format": "sequence.fasta"},  # 预测出的染色体序列
            {"name": "seq_plas", "type": "outfile", "format": "sequence.fasta"},  # 完成图质粒预测的序列
            {"name": "sample_gene_gff", "type": "outfile", "format": "gene_structure.gff3"},  # 样品编码基因预测gff文件
            {"name": "sample_trna_gff", "type": "outfile", "format": "gene_structure.gff3"},  # tRNA预测结果
            {"name": "sample_rrna_gff", "type": "outfile", "format": "gene_structure.gff3"},  # rRNA预测结果
            {"name": "sample_gene_fnn", "type": "outfile", "format": "sequence.fasta"},
            {"name": "sample_gene_faa", "type": "outfile", "format": "sequence.fasta"},
        ]
        self.add_option(option)
        self.genome_tools = []  # 一个基因组运行编码基因预测、tRNA预测、rRNA预测
        #self.predict_tidy = [self.dna_predict_tidy]
        self.step.add_steps("trna", "trnap", "cds", "predict_tidy")
        self.tools = []
        self.database = ""

    def set_step(self, event):
        if "start" in event["data"].keys():
            event["data"]["start"].start()
        if "end" in event["data"].keys():
            event["data"]["end"].finish()
        self.step.update()

    def check_options(self):
        """
        检查参数
        :return:
        """
        if (not self.option("genome").is_set) and (not self.option("genome_plas").is_set):
            raise OptionError("必须提供基因组文件")
        if not self.option("genome_name"):
            raise OptionError("必须提供样品名")
        if not self.option("input_gff").is_set:
            raise OptionError("必须提供所需的gff文件")
        return True

    def run_cds_predict(self):
        """
        进行cds基因预测,从gff文件中提取出所需要的信息
        :return:
        """
        self.cds_predict = self.add_tool("bac_comp_genome.get_seq_from_gff")
        opts = {
            "sample": self.option("sample"),
            "genome": self.option("genome_name"),
            "input_gff": self.option("input_gff"),
            "genome_type": self.option("genome_type"),
            "type": "cds"
        }
        if self.option("gene_prefix"):
            opts["gene_tag"] = self.option("gene_prefix")
        else:
            opts["gene_tag"] = self.option("plasmid_prefix")
        self.cds_predict.set_options(opts)
        self.genome_tools.append(self.cds_predict)

    def run_trna_predict(self):
        """
        进行tRNA功能预测，先对tRNA个数进行判断，然后选择方式方法
        :return:
        """
        if self.option("trna") != "-":
            self.trna_predict = self.add_tool("bac_comp_genome.get_seq_from_gff")
            opts = {
                "sample": self.option("sample"),
                "genome": self.option("genome_name"),
                # "gene_tag": self.option("gene_prefix"),
                "input_gff": self.option("input_gff"),
                "genome_type": self.option("genome_type"),
                "type": "tRNA"
            }
        else:
            self.trna_predict = self.add_tool("bac_comp_genome.trnascanse")
            opts = {
                "input_genome": self.option("genome"),
                "genome_name": self.option("genome_name"),
                "genome_type": self.option("genome_type"),
                # "gene_tag": self.option("gene_prefix")
            }
        if self.option("gene_prefix"):
            opts["gene_tag"] = self.option("gene_prefix")
        else:
            opts["gene_tag"] = self.option("plasmid_prefix")
        self.trna_predict.set_options(opts)
        self.genome_tools.append(self.trna_predict)

    def run_rrna_predict(self):
        """
        进行rRNA功能预测，先对rRNA个数进行判断，然后选择方法去做
        :return:
        """
        if self.option("rrna") != "-":
            self.rrna_predict = self.add_tool("bac_comp_genome.get_seq_from_gff")
            opts = {
                "sample": self.option("sample"),
                "genome": self.option("genome_name"),
                # "gene_tag": self.option("gene_prefix"),
                "input_gff": self.option("input_gff"),
                "genome_type": self.option("genome_type"),
                "type": "rRNA"
            }
        else:
            self.rrna_predict = self.add_tool("bac_comp_genome.barrnap")
            opts = {
                "input_genome": self.option("genome"),
                "genome_name": self.option("genome_name"),
                "genome_type": self.option("genome_type"),
                # "gene_tag": self.option("gene_prefix")
            }
        if self.option("gene_prefix"):
            opts["gene_tag"] = self.option("gene_prefix")
        else:
            opts["gene_tag"] = self.option("plasmid_prefix")
        self.rrna_predict.set_options(opts)
        self.genome_tools.append(self.rrna_predict)

    def run_dna_predict_tidy(self):
        """
        对染色体的预测结果重新矫正，统计基础信息
        :return:
        """
        self.dna_predict_tidy = self.add_tool("bac_comp_genome.dna_predict_tidy")
        opts = {
            "input_genome": self.option("genome"),
            "gene_prefix": self.option("gene_prefix"),
            "gene_gff": self.cds_predict.option("gff"),
            "trna_gff": self.trna_predict.option("gff"),
            "rrna_gff": self.rrna_predict.option("gff"),
            "genome_name": self.option("genome_name"),
            "genome_type": self.option("genome_type"),
            "sample": self.option("sample"),
            "is_gff": "gff+seq"
        }
        self.dna_predict_tidy.set_options(opts)
        self.dna_predict_tidy.on("start", self.set_step, {"start": self.step.predict_tidy})
        self.dna_predict_tidy.on("end", self.set_step, {"end": self.step.predict_tidy})
        self.dna_predict_tidy.on("end", self.set_output, "predict_tidy")
        self.dna_predict_tidy.run()

    def run_dna_predict_tidy_pla(self):
        """
        根据质粒预测结果进行重新矫正，统计基础信息
        :return:
        """
        self.dna_predict_tidyp = self.add_tool("bac_comp_genome.dna_predict_tidy")
        opts = {
            "input_genome": self.option("genome_plas"),
            "plasmid_prefix": json.dumps({self.option("genome_name"): self.option("plasmid_prefix")}),
            "gene_gff": self.cds_predict.option("gff"),
            "trna_gff": self.trna_predict.option("gff"),
            "rrna_gff": self.rrna_predict.option("gff"),
            "genome_name": self.option("genome_name"),
            "genome_type": self.option("genome_type"),
            "is_gff": "gff+seq"
        }
        self.dna_predict_tidyp.set_options(opts)
        self.dna_predict_tidyp.on("start", self.set_step, {"start": self.step.predict_tidy})
        self.dna_predict_tidyp.on("end", self.set_step, {"end": self.step.predict_tidy})
        self.dna_predict_tidyp.on("end", self.set_output, "predict_tidy")
        self.dna_predict_tidyp.run()

    def run(self):
        self.logger.info("开始运行啦")
        super(GenePredictGffModule, self).run()
        if self.option("genome").is_set or self.option("genome_plas").is_set:
            self.logger.info("正在对染色体进行基因预测")
            self.run_cds_predict()
            self.run_trna_predict()
            self.run_rrna_predict()
            if self.option("plasmid_prefix"):
                self.on_rely(self.genome_tools, self.run_dna_predict_tidy_pla)
            else:
                self.on_rely(self.genome_tools, self.run_dna_predict_tidy)
            for tool in self.genome_tools:
                tool.run()

    def set_output(self, event):
        """
        设置结果目录
        :param event:
        :return:
        """
        self.logger.info("正在设置结果目录")
        if event["data"] == "predict_tidy":
            if self.option("genome").is_set:
                dir_path = self.dna_predict_tidy.output_dir
                for file in os.listdir(dir_path):
                    file_path = os.path.join(self.dna_predict_tidy.output_dir, file)
                    new_file = os.path.join(self.output_dir, file)
                    if os.path.exists(new_file):
                        os.remove(new_file)
                    os.link(file_path, new_file)
            elif self.option("genome_plas").is_set:
                dir_path = self.dna_predict_tidyp.output_dir
                for file in os.listdir(dir_path):
                    file_path = os.path.join(self.dna_predict_tidyp.output_dir, file)
                    new_file = os.path.join(self.output_dir, file)
                    if os.path.exists(new_file):
                        os.remove(new_file)
                    os.link(file_path, new_file)
            else:
                self.logger.info("没有正确的生成结果文件")
            self.logger.info("设置结果目录文件完成")
            self.end()

    def end(self):
        super(GenePredictGffModule, self).end()


import intcode, itertools
from numpy import loadtxt

inputProgram =  loadtxt("input.txt", delimiter=",", dtype=int)
nAmps = 5
# phaseSequences = map(lambda x:  list(x), list(itertools.permutations(range(nAmps), nAmps)))
#
# program, out = intcode.executeIntcode(inputProgram.copy(), [0,0])
#
#
# def executePhaseSequence(inputVector):
#     out = 0;
#     for _ in range(nAmps):
#         program, out = intcode.executeIntcode(inputProgram.copy(), [inputVector.pop(0),out])
#     return out
#
# outmax = max(map(executePhaseSequence, phaseSequences))
# print "OUTMAX %s" %outmax

phaseSequences = map(lambda x:  list(x), list(itertools.permutations(range(5,10), nAmps)))

def executePhaseSequenceFeedback(inputVector):
    print ("Executing Phase Sequence %s" %inputVector)
    amps = [intcode.IntcodeComputer(inputProgram.copy(), inputBuffer=[], outputBuffer=[], haltOnOutput=True) for _ in
            range(5)]
    i = 0
    lastOutput = 0;
    while not amps[0].halted:
        print ("Executing Amp %s" %(i%5))
        if inputVector:
            amps[i%5].inputBuffer.append(inputVector.pop(0))
        amps[i % 5].inputBuffer.append(lastOutput)
        amps[i % 5].execute()
        if not amps[i%5].halted:
            lastOutput = amps[i%5].outputBuffer.pop()
            i += 1
    print ("Value %s" %lastOutput)
    return lastOutput

outmax = max(map(executePhaseSequenceFeedback, phaseSequences))
print("OUTMAX %s" %outmax)


import base64
import json
import re
import socket
import sys
from urllib.parse import urlparse

import fitz
import pytesseract as tess
import requests
from pdf2image import convert_from_bytes

from batch.SpanishSpellChecker import SpanishSpellChecker
from common.TextractService import TextractService
from common.commonsLib import loggerElk

class ImageToTxt:
    ENCODING = 'utf-8'
    TIMEOUT_SEC = 160
    MIN_TEXT_LENGTH = 10

    def __init__(self, s3service):
        self.logger = loggerElk(__name__)
        self.spell_checker_service = SpanishSpellChecker()
        self.s3_service = s3service
        self.textract = TextractService(self.s3_service)

    def get_ocr_from_pdf2readable(self, s3_service, key):
        self.logger.Information("ImageToTxt::forced native OCR")
        decoded_pages = self.__get_text_by_pdf2readable(
            s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
            force=1, skip=0, force_no_native=0, disable=0)
        sum_native = sum(map(lambda x: len(self.clean(x)), decoded_pages))
        if sum_native < self.MIN_TEXT_LENGTH:
            self.logger.Information(f"ImageToTxt::forced readable OCR")
            decoded_pages = self.__get_text_by_pdf2readable(
                s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
                force=1, skip=sys.maxsize, disable=1)

        return decoded_pages

    def get_ocr_from_pdf2readable_with_cache(self, s3_service, key):
        decoded_pages = self.get_cache_from_pdf2readable(s3_service, key)
        if not decoded_pages:
            self.get_ocr_from_pdf2readable(s3_service, key)

        return decoded_pages

    def get_max_ocr_from_pdf2readable(self, s3_service, key):
        txt_decoded_list = list()
        txt_decoded = self.get_native_from_pdf2readable(s3_service, key)
        txt_decoded_list.append(txt_decoded)
        txt_decoded = self.get_native_ocr_from_pdf2readable(s3_service, key)
        txt_decoded_list.append(txt_decoded)
        txt_decoded = self.get_readable_from_pdf2readable(s3_service, key)
        txt_decoded_list.append(txt_decoded)

        try:
            txt_decoded_length = list(map(lambda x: len(' '.join(x)), txt_decoded_list))
            txt_decoded_max_id = txt_decoded_length.index(max(txt_decoded_length))
            txt_decoded = txt_decoded_list[txt_decoded_max_id]
        except Exception:
            pass

        return txt_decoded

    def get_cache_from_pdf2readable(self, s3_service, key):
        self.logger.Information("ImageToTxt::cached")
        decoded_pages = self.__get_text_by_pdf2readable(
            s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
            force=0, skip=0, force_no_native=0, disable=1)

        return decoded_pages

    def get_native_from_pdf2readable(self, s3_service, key, skip=0):
        self.logger.Information("ImageToTxt::forced native")
        decoded_pages = self.__get_text_by_pdf2readable(
            s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
            force=1, skip=skip, force_no_native=0, disable=1)

        return decoded_pages

    def get_native_ocr_from_pdf2readable(self, s3_service, key):
        self.logger.Information("ImageToTxt::forced native + OCR")
        decoded_pages = self.__get_text_by_pdf2readable(
            s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
            force=1, skip=0, force_no_native=0, disable=0)

        return decoded_pages

    def get_readable_from_pdf2readable(self, s3_service, key):
        self.logger.Information("ImageToTxt::forced readable OCR")
        decoded_pages = self.__get_text_by_pdf2readable(
            s3_service=s3_service, bucket=s3_service.real_bucket, key=key,
            force=1, skip=sys.maxsize, force_no_native=0, disable=1)

        return decoded_pages

    def get_text_by_tesseract(self, doc_image=None):
        full_text = ''

        if doc_image is not None:
            full_text = self.__get_string(doc_image)

        return full_text

    def get_text_by_textract(self, doc_image=None):
        full_text = ''

        if doc_image is not None:
            full_text = self.__get_string(doc_image)

        return full_text

    def __get_string(self, file_input):
        texto = self.__img_to_string(file_input)
        return texto

    @staticmethod
    def __img_to_string(img):
        lang = 'spa+cat'
        config = ('-l ' + lang + ' --oem 1 --psm 3')
        tess.pytesseract.tesseract_cmd = r"tesseract"
        text_string = tess.image_to_string(img, config=config)
        return text_string

    def __get_text_by_pdf2readable(self,
                                   s3_service,
                                   bucket, key,
                                   force=0,
                                   force_no_native=0,
                                   disable=1,
                                   skip=MIN_TEXT_LENGTH):
        pages = list()
        full_text = ''

        if bucket and key:
            full_text = self.__ocr_pdf2readable(
                s3_service, key, force, force_no_native, disable, skip)

        if len(full_text) > 0:
            pages = re.compile(r'\[\[\[\d+\]\]\]').split(full_text)
            if pages:
                pages = [page for page in pages if page]

        return pages

    def __ocr_pdf2readable(self, s3_service, key, force, force_no_native, disable, skip):
        full_text = ''

        headers = {'content-type': 'application/json'}
        payload_ocr = {
            'persistence': 'S3',
            'source': 'S3',
            'bucket': s3_service.bucket,
            'key': key,
            'data': key,
            'lang': 'spa',
            'forcescan': force,
            'disableocr': disable,
            "forcenonative": force_no_native,
            "max_pages": 99,
            "txtLenForSkip": skip,
            "kb_limit_size": 0,
            "queue": ""
        }

        try:
            r_ocr = None
            self.logger.Information(f'ImgToTxt::Real bucket:{s3_service.bucket}')
            self.logger.Information(f'ImgToTxt::Calling ocr_pdf2readable: '
                                    f'{s3_service.readable_url} with: {payload_ocr}')
            if self.ping(s3_service.readable_url):
                r_ocr = requests.post(s3_service.readable_url,
                                      data=json.dumps(payload_ocr),
                                      headers=headers,
                                      timeout=self.TIMEOUT_SEC)
        except Exception as e:
            if str(e) == "('Connection aborted.', RemoteDisconnected(" \
                         "'Remote end closed connection without response'))":
                self.logger.Error(f'ImgToTxt::Exception {e} after {self.TIMEOUT_SEC} seconds.')
            else:
                self.logger.Error(f"ImgToTxt::{e}")
            return ''

        if r_ocr is None or len(r_ocr.text) == 0:
            self.logger.Information(f'ImgToTxt::Error when calling pdf2readable')
            return ''

        try:
            try:
                json_ocr = json.loads(r_ocr.text)
                if not json_ocr or not len(json_ocr) or not isinstance(json_ocr, dict):
                    raise ValueError("JSON result malformed")
            except Exception as _:
                self.logger.Error('ImgToTxt::JSON format error')
                return ''

            if ('status' in json_ocr and json_ocr['status'] == 'False') or \
                    ('statusCode' in json_ocr and json_ocr['statusCode'] == 500):
                self.logger.Error('ImgToTxt::status False or 500')
                return ''
            rt_len = 0
            if json_ocr and 'resultText' in json_ocr and json_ocr['resultText']:
                rt_len = len(json_ocr['resultText'])
            rtn_len = 0
            if json_ocr and 'resultTextNonNative' in json_ocr and json_ocr['resultTextNonNative']:
                rtn_len = len(json_ocr['resultTextNonNative'])
            if rt_len or rtn_len:
                full_text += json_ocr['resultText'] if rt_len >= rtn_len else json_ocr['resultTextNonNative']
        except Exception as ex:
            self.logger.Error(f"ImgToTxt::ocr_pdf2readable service response -> [{str(ex)}]")
            return ''

        decoded_text = self.load_document(full_text)
        if len(decoded_text.strip()) > self.MIN_TEXT_LENGTH:
            self.logger.Information(f'ImgToTxt::ocr_pdf2readable got some text -> '
                                    f'{self.clean(decoded_text)[:self.MIN_TEXT_LENGTH]}')
        elif 0 < len(decoded_text.strip()) <= self.MIN_TEXT_LENGTH:
            self.logger.Information(f'ImgToTxt::ocr_pdf2readable got little text -> '
                                    f'{len(decoded_text.strip())} characters')
            return ''
        else:
            self.logger.Error(f'ImgToTxt::Error when calling pdf2readable. No text returned. '
                              f'Check that the bucket and credentials '
                              f'are in the pdf2readable config')
            return ''

        return decoded_text

    @staticmethod
    def clean(data):
        if data is None:
            return ''

        if type(data) == int or type(data) == float:
            return data

        data = data.strip()
        data = data.replace('\r', '')
        data = data.replace('\n', '')
        data = data.replace('\t', '')

        data = data.replace('.', '')
        data = data.replace(':', '')
        data = data.replace(';', '')
        data = data.replace('\'', '')
        data = data.lower()

        return data

    @staticmethod
    def check_doc_format(key, file_data, txt_decoded):
        try:
            fitz.Document(filetype='pdf', stream=file_data)
            convert_from_bytes(file_data, dpi=1)
        except Exception:
            raise ValueError(key)

        return key, file_data, txt_decoded

    @staticmethod
    def ping(srv_url):
        address = urlparse(srv_url)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        try:
            sock.connect((address.hostname, address.port))
        except Exception:
            return False
        else:
            sock.close()
            return True

    @staticmethod
    def load_document(content):
        try:
            full_text = content.decode(ImageToTxt.ENCODING, errors='replace')
        except (UnicodeDecodeError, AttributeError):
            full_text = content.encode(ImageToTxt.ENCODING, errors="replace").decode(
                ImageToTxt.ENCODING, errors='replace')
        try:
            data_encoded = full_text.encode(ImageToTxt.ENCODING, errors="replace")
            b64_encoded = base64.b64encode(data_encoded)
            b64_decoded = base64.b64decode(b64_encoded)
            full_text = b64_decoded.decode(ImageToTxt.ENCODING, errors='replace')
            try:
                if base64.b64encode(base64.b64decode(data_encoded)) == data_encoded:
                    b64_decoded = base64.b64decode(b64_decoded)
                    full_text = b64_decoded.decode(ImageToTxt.ENCODING, errors='replace')
            except Exception as _:
                pass
        except Exception as _:
            pass

        return full_text


import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases_v3 import *
from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward
import random

plt.rcParams['figure.figsize'] = (5.0, 4.0)  # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

np.random.seed(1)

# Initialization of 2 layer neural network
# input --> hidden1 --> output

def initialize_parameters(n_x, n_h, n_y):
    np.random.seed(1)

    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))

    assert(W1.shape == (n_h, n_x))
    assert(b1.shape == (n_h, 1))
    assert(W2.shape == (n_y, n_h))
    assert(b2.shape == (n_y, 1))

    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}

    return parameters

# TEST
parameters = initialize_parameters(3, 2, 1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

# Initialization of L layer neural network
# input --> hidden{L} --> output

def initialize_parameters_deep(layer_dims):
    np.random.seed(3)
    parameters = {}
    L = len(layer_dims)

    for l in range(1, L):
        parameters['W' + str(l)] = (np.random.randn(layer_dims[l],
                                                    layer_dims[l - 1])) * (0.01)
        parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))

    return parameters

# TEST
parameters = initialize_parameters_deep([5, 4, 3])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

# FORWARD PROPAGATION
# 1. linear
# 2. linear -> activation(sigmoid + relu)
# 3. linear -> relu {L-1} --> linear ->sigmoid

def linear_forward(A, w, b):
    Z = np.dot(w, A) + b
    cache = (A, w, b)  # Store A, w, b in a tuple named cache

    return Z, cache  # This is actually a tuple (Z, cache)

# TEST
A, W, b = linear_forward_test_case()
Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))

def linear_activation_forward(A_prev, w, b, activation):
    if activation == "sigmoid":
        Z, linear_cache = linear_forward(A_prev, w, b)
        A, activation_cache = sigmoid(Z)

    elif activation == "relu":
        Z, linear_cache = linear_forward(A_prev, w, b)
        A, activation_cache = relu(Z)

    cache = (linear_cache, activation_cache)

    return A, cache

# TEST
A_prev, W, b = linear_activation_forward_test_case()
A, linear_activation_cache = linear_activation_forward(A_prev, W, b,
                                                       activation="sigmoid")
print("With sigmoid: A = " + str(A))
A, linear_activation_cache = linear_activation_forward(A_prev, W, b,
                                                       activation="relu")
print("With ReLU: A = " + str(A))

def L_model_forward(X, parameters):
    caches = []
    A = X
    L = len(parameters) // 2

    for l in range(1, L):
        A_prev = A
        A, cache = linear_activation_forward(A_prev,
                                             parameters['W' + str(l)],
                                             parameters['b' + str(l)],
                                             activation='relu')
        caches.append(cache)

    AL, cache = linear_activation_forward(A,
                                          parameters['W' + str(L)],
                                          parameters['b' + str(L)],
                                          activation='sigmoid')
    caches.append(cache)

    return AL, caches

# TEST
X, parameters = L_model_forward_test_case_2hidden()
AL, caches = L_model_forward(X, parameters)
print("AL = " + str(AL))
print("Length of caches list = " + str(len(caches)))

def compute_cost(AL, Y):
    m = Y.shape[1]
    cost = (-1/m) * (np.dot(Y, (np.log(AL)).T) + np.dot((1 - Y),
                                                        (np.log(1 - AL).T)))

    cost = np.squeeze(cost)      # e.g. this turns [[17]] into 17
    assert(cost.shape == ())

    return cost

Y, AL = compute_cost_test_case()

print("cost = " + str(compute_cost(AL, Y)))

# BACK PROPAGATION
# 1. linear backward
# 2. linear -> activation backward
# 3. Linear --> relu (L-1) --> linear --> sigmoid backward

def linear_backward(dZ, cache):
    A_prev, W, b = cache
    m = A_prev.shape[1]

    dW = (1/m) * np.dot(dZ, cache[0].T)
    db = (1/m) * np.squeeze(np.sum(dZ, axis=1, keepdims=True))
    dA_prev = np.dot(cache[1].T, dZ)
    return dA_prev, dW, db

# TEST
# Set up some test inputs
dZ, linear_cache = linear_backward_test_case()

dA_prev, dW, db = linear_backward(dZ, linear_cache)
print ("dA_prev = " + str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

def linear_activation_backward(dA, cache, activation):
    linear_cache, activation_cache = cache

    if activation == "relu":
        dZ = relu_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)

    elif activation == "sigmoid":
        dZ = sigmoid_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)

    return dA_prev, dW, db

# TEST
AL, linear_activation_cache = linear_activation_backward_test_case()

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "sigmoid")
print ("\n\n\n\n\n\n\nsigmoid:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db) + "\n")

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "relu")
print ("relu:")
print ("dA_prev = " + str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

def L_model_backward(AL, Y, caches):
    grads = {}
    L = len(caches)  # the number of layers
    m = AL.shape[1]
    Y = Y.reshape(AL.shape)  # after this line, Y is the same shape as AL

    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))

    current_cache = caches[L-1]
    grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = "sigmoid")

    for l in reversed(range(L-1)):
        current_cache = caches[l]
        dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, activation = "relu")
        grads["dA" + str(l + 1)] = dA_prev_temp
        grads["dW" + str(l + 1)] = dW_temp
        grads["db" + str(l + 1)] = db_temp

    return grads

# TEST
AL, Y_assess, caches = L_model_backward_test_case()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = " + str(grads["dW1"]))
print ("db1 = " + str(grads["db1"]))
print ("dA1 = " + str(grads["dA1"]))

def update_parameters(parameters, grads, learning_rate):
    L = len(parameters) // 2
    for l in range(L):
        parameters["W" + str(l+1)] = parameters["W" + str(l+1)] - learning_rate * grads["dW" + str(l+1)]
        parameters["b" + str(l+1)] = parameters["b" + str(l+1)] - learning_rate * grads["db" + str(l+1)]

    return parameters

# TEST
parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads, 0.1)

print ("W1 = " + str(parameters["W1"]))
print ("b1 = " + str(parameters["b1"]))
print ("W2 = " + str(parameters["W2"]))
print ("b2 = " + str(parameters["b2"]))


import numpy as np
import random
import pygame

SCREEN_SIZE = 500
TILE_SIZE = SCREEN_SIZE // 5
GAP = SCREEN_SIZE // 25
SCORE_HEIGHT = 100

BACKGROUND = (187, 173, 160)
COLOURS = {
    0: (205, 193, 180),
    2: (238, 228, 218),
    4: (242, 224, 200),
    8: (242, 177, 121),
    16: (245, 149, 99),
    32: (246, 124, 95),
    64: (246, 94, 59),
    128: (237, 207, 114),
    256: (237, 204, 97),
    512: (237, 200, 80),
    1024: (237, 197, 63),
    2048: (238, 194, 46)
}

pygame.init()

win = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE + SCORE_HEIGHT))
pygame.display.set_caption('2048')
clock = pygame.time.Clock()

font = pygame.font.Font('sourcesanspro.ttf', 60)

class Block:
    def __init__(self, value):
        self.value = value
        self.colour = COLOURS[value]

    def draw(self, x, y):
        pygame.draw.rect(win, self.colour, (x, y, TILE_SIZE, TILE_SIZE))
        if self.value != 0:
            text = font.render(str(self.value), 1, (120, 120, 120))
            win.blit(text, text.get_rect(center=(x + TILE_SIZE // 2, y + TILE_SIZE // 2)))

    def __add__(self, other):
        if self.value == other.value:
            return Block(self.value * 2)

    def __eq__(self, other):
        if self.value == other.value:
            return True
        return False

class Grid:
    def __init__(self):
        self.blocks = list([Block(0) for _ in range(4)] for _ in range(4))
        self.new_turn()
        self.score = 0

    def __gt__(self, other):
        if self.score > other.score:
            return True
        return False

    def new_turn(self):
        for _ in range(2):
            new_spawn(self.blocks)

    def draw(self):
        for i in range(4):
            for j in range(4):
                x = GAP + i * (TILE_SIZE + GAP)
                y = SCORE_HEIGHT + GAP + j * (TILE_SIZE + GAP)
                self.blocks[j][i].draw(x, y)

        text = font.render(str(self.score), 1, (50, 50, 50))

        win.blit(text, text.get_rect(center=(SCREEN_SIZE // 2, SCORE_HEIGHT // 2)))

    def shift(self):
        for row in self.blocks:
            for index, block in enumerate(row):
                if block.value == 0:
                    row.pop(index)
                    row.insert(0, Block(0))

        self.blocks = np.flip(self.blocks, 1).tolist()
        self.merge()
        self.blocks = np.flip(self.blocks, 1).tolist()

    def check_end(self):
        for row in self.blocks:
            for block in row:
                if block.value == 0:
                    return False

        arr = convert(self.blocks)

        for direction in range(4):
            arr = np.rot90(arr, direction).tolist()
            for row in arr:
                for index in range(len(row) - 1):
                    if row[index + 1] == row[index] and row[index] != 0:
                        row[index] = row[index] + row[index + 1]
                        row.pop(index + 1)
                        row.append(0)

            arr = np.rot90(arr, 4 - direction).tolist()

            if arr != convert(self.blocks):
                return False

        return True

    def merge(self):
        for row in self.blocks:
            for index in range(len(row) - 1):
                if row[index + 1] == row[index] and row[index].value != 0:
                    row[index] = row[index] + row[index + 1]
                    row.pop(index + 1)
                    row.append(Block(0))
                    self.score += row[index].value

    def move(self, direction):
        original = convert(self.blocks)

        if direction == 'right':
            self.shift()

        elif direction == 'left':
            self.blocks = np.flip(self.blocks, 1).tolist()

            self.shift()

            self.blocks = np.flip(self.blocks, 1).tolist()

        elif direction == 'down':
            self.blocks = np.rot90(self.blocks, 1).tolist()

            self.shift()

            self.blocks = np.rot90(self.blocks, 3).tolist()

        elif direction == 'up':
            self.blocks = np.rot90(self.blocks, 3).tolist()

            self.shift()

            self.blocks = np.rot90(self.blocks, 1).tolist()

        if original != convert(self.blocks):
            self.blocks = new_spawn(self.blocks)

def convert(arr):
    output = []
    for row in arr:
        output.append(list((map(lambda x: x.value, row))))
    return output

def new_spawn(arr):
    choices = np.argwhere(np.array(arr) == Block(0)).tolist()
    x, y = tuple(random.choice(choices))

    block = Block(2)

    if random.random() > 0.9:
        block = Block(4)

    arr[x][y] = block

    return arr

def draw(grid):
    win.fill(BACKGROUND)
    grid.draw()

def end(score):
    s = pygame.Surface((1000, 750), pygame.SRCALPHA)
    s.fill((255, 255, 255, 200))
    win.blit(s, (0, 0))

    text = font.render('Congratulations!', 1, (0, 0, 0))
    text2 = font.render('Your Score: {}'.format(score), 1, (0, 0, 0))

    win.blit(text, text.get_rect(center=(SCREEN_SIZE // 2, (SCREEN_SIZE + SCORE_HEIGHT) // 2 - 50)))
    win.blit(text2, text2.get_rect(center=(SCREEN_SIZE // 2, (SCREEN_SIZE + SCORE_HEIGHT) // 2 + 50)))

    pygame.display.update()

    play = True
    while play:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
                pygame.quit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    play = False
                    main()

def main():
    grid = Grid()

    play = True
    while play:
        clock.tick(30)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
                pygame.quit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    grid.move('left')
                elif event.key == pygame.K_RIGHT:
                    grid.move('right')
                elif event.key == pygame.K_UP:
                    grid.move('up')
                elif event.key == pygame.K_DOWN:
                    grid.move('down')

        if play:
            draw(grid)
            pygame.display.update()

            if grid.check_end():
                end(grid.score)

main()


#Edouard d.S.d.L 18072

import os
import sys, random, pygame
from pygame.locals import *
from abc import ABC, abstractmethod
from timeit import Timer

#Initialize pygame and setting up the window
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

WIDTH = 1080
HEIGHT = 720
FPS = 30
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("EcosysPOO")

#Declaring the color variables used to differenciate different lifeforms
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
dark_green = (0, 50, 0)
brown = (100, 40, 0)
gender = ["male", "female"]

#Initializing the base class called lifeforms which all organisms in the ecosystem will descend
class lifeforms(pygame.sprite.Sprite, ABC):
    def __init__(self, width, height, pos_x, pos_y):
        pygame.sprite.Sprite.__init__(self)
        self.hp = 10
        self.dead = False
        self.energy = 100
        self.isOrgWas  = False
        self.image = pygame.Surface([width, height]).convert_alpha()
        self.width = width
        self.height = height
        self.rect = self.image.get_rect()
        self.rect.center = [pos_x, pos_y]

    def isDead(self):
        if self.hp >= 0:
           return True
        else:
            return False

    def get_posX(self):
        return self.rect.x

    def get_posY(self):
        return self.rect.y

    def set_posX(self, value):
        self.rect.x = value

    def set_posY(self, value):
        self.rect.y = value

    def set_color(self, value):
        self.color = value

    def set_meat(self):
        self.image.fill(red)
        self.color = red

    def set_waste(self):
        self.image.fill(brown)
        self.color = brown

    def set_black(self):
        self.image.fill(black)
        self.color = black

    def get_color(self):
        return self.color

    def set_hp(self, value):
        i = value
        self.hp += i

    def get_hp(self):
        return self.hp

    def set_energy(self, value):
        i = value
        self.energy += i

    def get_energy(self):
        return self.energy

    def get_width(self):
        return self.width

    def get_height(self):
        return self.height

    def set_X(self, value):
        self.rect.x = value

#Initializing the base class for the two main animalforms there are going to be predator and prey
class Animals(lifeforms):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        self.isGender = random.choice(gender)
        self.isFood = False
        self.speed = random.randrange(1, 5)
        self.move = [None, None]
        self.direction = None
        self.preg = False

    def set_direction(self):
        self.direction *= -1

    def get_direction(self):
        return self.direction

    def get_gender(self):
        return self.isGender

    def pregnant(self):
        self.preg = True

    def not_pregnant(self):
        self.preg = False

    def get_pregnant(self):
        return self.preg

    def get_isFood(self):
        return self.isFood

    def set_isFood(self, value):
        self.isFood = value

#here we are defining the classes of the two types of animals there are going to be predators and herbivores
class Predator(Animals):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        Animals.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)

        self.color = white
        self.image.fill(self.color)
        self.zoneVisionP = pygame.Rect(self.rect.x-10, self.rect.y-10, width+20, height+20)

    def set_x(self, value):
        self.zoneVisionP.x = value

    def set_y(self, value):
        self.zoneVisionP.y = value

    def get_x(self):
        return self.zoneVisionP.x

    def get_y(self):
        return self.zoneVisionP.y
 #here is an update on the state of the animal and to add a random movement in any direction
    def update(self):
        if self.hp > 0:
            self.energy -= 0.05
            if self.energy <= 0 :
                self.energy = 0
                if self.hp > 0:
                    self.hp -= 0.5

            directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
            directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
            if random.randrange(0,5) == 2: #move about once every 5 frames
                if self.direction == None: #if no direction is set, set a random one
                    self.direction = random.choice(directionsName)
                else:
                    a = directionsName.index(self.direction) #get the index of direction in directions list
                    b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
                    if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
                        b = 0
                        self.direction = directionsName[b]
                    self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                    self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.rect.x < 5 or self.rect.x > WIDTH - 5 or self.rect.y < 5 or self.rect.y > HEIGHT - 5: #if cell is near the border of the screen, change direction
                if self.rect.x < 5:
                    self.direction = "E"
                elif self.rect.x > WIDTH - 5:
                    self.direction = "W"
                elif self.rect.y < 5:
                    self.direction = "S"
                elif self.rect.y > HEIGHT - 5:
                    self.direction = "N"
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.move[0] != None: #add the relative coordinates to the cells coordinates
                self.rect.x += self.move[0]
                self.zoneVisionP.x += self.move[0]
                self.rect.y += self.move[1]
                self.zoneVisionP.y += self.move[1]

        elif self.hp <= 0 and self.isFood == False:
            self.hp = 0
            self.image.fill(red)
            self.color = red

class Herbivore(Animals):
    def __init__(self, width, height, pos_x, pos_y):
        Animals.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height]).convert_alpha()
        self.color = dark_green
        self.image.fill(self.color)
        self.zoneVisionH = pygame.Rect(self.rect.x-10, self.rect.y-10, self.width+20, self.height+20)

    def set_x(self, value):
        self.zoneVisionH.x = value

    def set_y(self, value):
        self.zoneVisionH.y = value

    def get_x(self):
        return self.zoneVisionH.x

    def get_y(self):
        return self.zoneVisionH.y
 #here is an update on the state of the animal and to add a random movement in any direction
    def update(self):
        if self.hp > 0:
            self.energy -= 0.05
            if self.energy <= 0 :
                self.energy = 0
                if self.hp > 0:
                    self.hp -= 0.5
            directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
            directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
            if random.randrange(0,5) == 2: #move about once every 5 frames
                if self.direction == None: #if no direction is set, set a random one
                    self.direction = random.choice(directionsName)
                else:
                    a = directionsName.index(self.direction) #get the index of direction in directions list
                    b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
                    if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
                        b = 0
                    self.direction = directionsName[b]
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.rect.x < 5 or self.rect.x > WIDTH - 5 or self.rect.y < 5 or self.rect.y > HEIGHT - 5: #if cell is near the border of the screen, change direction
                if self.rect.x < 5:
                    self.direction = "E"
                elif self.rect.x > WIDTH - 5:
                    self.direction = "W"
                elif self.rect.y < 5:
                    self.direction = "S"
                elif self.rect.y > HEIGHT - 5:
                    self.direction = "N"
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.move[0] != None: #add the relative coordinates to the cells coordinates
                self.rect.x += self.move[0]
                self.zoneVisionH.x += self.move[0]
                self.rect.y += self.move[1]
                self.zoneVisionH.y += self.move[1]
        elif self.hp <= 0 and self.isFood == False:
            self.hp = 0
            self.image.fill(red)
            self.color = red

#defining a new lifeform here the plants which can not move in our game
class Plants(lifeforms):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height]).convert_alpha()

        self.color = green
        self.image.fill(self.color)
        self.zoneVisionPlant = pygame.Rect(self.rect.x-50, self.rect.y-50, self.width+100, self.height+100)

    def update(self):
        if self.isDead() == False and self.energy > 0 :
            self.energy -= 0.05
        elif self.energy <= 0 :
            self.energy = 0
            if self.hp > 0:
                self.hp -= 0.05
        elif self.hp <= 0 :
            self.hp = 0
            self.image.fill(brown)
            self.color = brown

pred_group = pygame.sprite.Group()
herb_group = pygame.sprite.Group()
plant_group = pygame.sprite.Group()
child_group = pygame.sprite.Group()

#Declaring the number of initial objects
number_of_Predators = range(10)
number_of_Herbivores = range(25)
number_of_Plants = range(50)

objPr = list()
for i in number_of_Predators:
    objPr.append(Predator(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    pred_group.add(objPr[i])

objH = list()
for i in number_of_Herbivores:
    objH.append(Herbivore(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    herb_group.add(objH[i])

objP = list()
for i in number_of_Plants:
    objP.append(Plants(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    plant_group.add(objP[i])

#here we define the method where the predators hunt the herbivores
def hunt():
    global number_of_Herbivores_alive
    for i in range(0,len(objPr)):
         for j in range(0,len(objH)):
             if objH[j].get_hp() > 0:
                collide = pygame.Rect.colliderect(objPr[i].rect, objH[j].rect)
                if collide and objH[j].get_color != brown :
                    objH[j].set_hp(-5)
                    if objH[j].isDead() == True:
                        if objPr[i].get_energy() <= 5:
                            objPr[i].set_energy(5)

                        elif objPr[i].get_energy() == 6:
                            objPr[i].set_energy(4)
                            if objPr[i].get_hp() <= 9:
                                objPr[i].set_hp(1)

                        elif objPr[i].get_energy() == 7:
                            objPr[i].set_energy(3)
                            if objPr[i].get_hp() <= 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 8:
                            objPr[i].set_energy(2)
                            if objPr[i].get_hp() <= 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objH[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 9:
                            objPr[i].set_energy(1)
                            if objPr[i].get_hp() <= 6:
                                objPr[i].set_hp(4)
                            elif objPr[i].get_hp() == 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 10:
                            objPr[i].set_energy(0)
                            if objPr[i].get_hp() <= 5:
                                objPr[i].set_hp(5)
                            elif objPr[i].get_hp() == 6:
                                objPr[i].set_hp(4)
                            elif objPr[i].get_hp() == 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

#here we define the method where the herbivores hunt the plant that are alive
def eatplant():
    global number_of_Plants_alive
    for i in range(0,len(objH)):
         for j in range(0,len(objP)):
             if objP[j].get_hp() > 0:
                collide = pygame.Rect.colliderect(objH[i].rect, objP[j].rect)
                if collide:
                    objP[j].set_hp(-10)
                    if objP[j].isDead() == True:
                        objP[j].set_X(WIDTH+50)
                    if objH[i].get_energy() <= 5:
                        objH[i].set_energy(5)
                        number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 6:
                        objH[i].set_energy(4)
                        if objH[i].get_hp() <= 9:
                            objH[i].set_hp(1)
                            number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 7:
                        objH[i].set_energy(3)
                        if objH[i].get_hp() <= 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 8:
                        objH[i].set_energy(2)
                        if objH[i].get_hp() <= 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 9:
                        objH[i].set_energy(1)
                        if objH[i].get_hp() <= 6:
                            objH[i].set_hp(4)
                        elif objH[i].get_hp() == 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 10:
                        objH[i].set_energy(0)
                        if objH[i].get_hp() <= 5:
                            objH[i].set_hp(5)
                        elif objH[i].get_hp() == 6:
                            objH[i].set_hp(4)
                        elif objH[i].get_hp() == 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

#here we define the methods where the dead herbivores and predators are absorbed by the plants
def absorbHerb():
    global number_of_Herbivores_alive
    for i in range(0,len(objH)):
        for j in range(0,len(objP)):
            if objH[i].get_color() == brown:
                collide = pygame.Rect.colliderect(objH[i].rect, objP[j].zoneVisionPlant)
                if collide:
                    objH[i].set_X(WIDTH+50)
                    number_of_Herbivores_alive -= 1

def absorbPred():
    global number_of_Predators_alive
    for i in range(0,len(objPr)):
        for j in range(0,len(objP)):
            if objPr[i].get_color() == brown:
                collide = pygame.Rect.colliderect(objPr[i].rect, objP[j].zoneVisionPlant)
                if collide:
                    objPr[i].set_X(WIDTH+50)
                    number_of_Predators_alive -= 1

#here we create the objects aka our 3 lifeforms
def create_herb():
    global number_of_Herbivores_alive
    objH.append(Herbivore(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    herb_group.add(objH)
    number_of_Herbivores_alive += 1

def create_pred():
    global number_of_Predators_alive
    objPr.append(Predator(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    pred_group.add(objPr)
    number_of_Predators_alive += 1

def create_plant():
    global number_of_Plants_alive
    objP.append(Plants(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    plant_group.add(objP)
    number_of_Plants_alive += 1

#Reproduction of a male and femal predator also reproduction of plants
def reproduction_Herbivores():
    for i in range(0,len(objH)):
        for j in range(0,len(objH)):
            collide = pygame.Rect.colliderect(objH[i].zoneVisionH, objH[j].zoneVisionH)
            if objH[j].get_energy() >= 8 and objH[i].get_energy() >= 8:
                if collide and objH[j].get_gender() == "male" and objH[i].get_gender() == "female" and objH[j].isDead() == False and objH[i].isDead() == False and objH[i].get_pregnant() == False:
                    objH[i].pregnant()
                if random.randrange(0,20) == 2 and objH[i].get_pregnant() == True and number_of_Herbivores_alive < 20:
                    create_herb()
                    objH[i].not_pregnant()

def reproduction_Predators():
    for i in range(0,len(objPr)):
        for j in range(0,len(objPr)):
            collide = pygame.Rect.colliderect(objPr[i].zoneVisionP, objPr[j].zoneVisionP)
            if objPr[j].get_energy() >= 8 and objPr[i].get_energy() >= 8:
                if collide and objPr[j].get_gender() == "male" and objPr[i].get_gender() == "female" and objPr[j].isDead() == False and objPr[i].isDead() == False and objPr[i].get_pregnant() == False:
                    objPr[i].pregnant()
                if random.randrange(0,20) == 2 and objPr[i].get_pregnant() == True and number_of_Predators_alive < 20:
                    create_pred()
                    objPr[i].not_pregnant()

def reproduction_Plants():
    if random.randrange(0,20) == 2:
        for i in range(0,len(objP)):
            if objP[i].get_energy() >= 5 and objP[i].get_hp() != 0 and random.randrange(0,20) == 2:
                    create_plant()
            else:
                return None

def zoneVisionPlant():
    for i in number_of_Plants:
        pygame.draw.rect(screen, (255, 255, 0), objP[i].zoneVisionPlant )

def become_orgw():
    for j in range(0,len(objH)):
        for i in range(0,len(objPr)):
            if objH[j].get_color() == red and random.randrange(0,100) == 2:
                objH[j].set_isFood(True)
                objH[j].set_waste()

def become_orgw2():
    for i in range(0,len(objPr)):
        if objPr[i].get_color() == red and random.randrange(0,100) == 2:
            objPr[i].set_isFood(True)
            objPr[i].set_waste()

def become_orgw3():
    for i in range(0,len(objP)):
        if objP[i].get_hp() == 0 and random.randrange(0,100) == 2:
            objP[i].set_X(WIDTH+50)

number_of_Herbivores_alive = 0
initial_Herbivores = len(objH)

number_of_Predators_alive = 0
initial_Predators = len(objPr)

number_of_Plants_alive = 0
initial_Plants = len(objP)

#Initializing the mainloop for our pygame
def mainloop():
    while True:
        pygame.time.Clock().tick(FPS)
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        screen.fill(0)

        hunt()
        eatplant()
        absorbHerb()
        absorbPred()
        reproduction_Predators()
        reproduction_Herbivores()
        reproduction_Plants()
        pred_group.update()
        herb_group.update()
        plant_group.update()
        become_orgw()
        become_orgw2()
        become_orgw3()
        pred_group.draw(screen)
        herb_group.draw(screen)
        plant_group.draw(screen)
        pygame.display.flip()

mainloop()