""" Flappy Bird Clone by Chris Gingerich """
# Import standard modules.
import sys
import random
import time

# Import non-standard modules.
import pygame as pg
from pygame.locals import *

# Import local modules
import NeuralNetworkLib as NN
from settings import *
from sprites import Bird, TopPipe, BottomPipe

class App():
    # initialize app window, etc
    def __init__(self):
        # Initialise PyGame.
        pg.init()

        # Initalize some font action
        pg.font.init()
        self.font_name = pg.font.match_font(FONT)

        # Set up the clock. This will tick every frame and thus maintain a relatively constant framerate. Hopefully.
        self.fps = FPS
        self.fpsClock = pg.time.Clock()

        # Set up the window.
        self.width, self.height = WIDTH, HEIGHT
        self.screen = pg.display.set_mode((self.width, self.height))
        pg.display.set_caption(TITLE)
        self.floor = pg.image.load("floor.png")
        self.floor_rect = self.floor.get_rect()
        self.floor_rect.bottom = HEIGHT + 20

        # screen is the surface representing the window.
        # PyGame surfaces can be thought of as screen sections that you can draw onto.
        # You can also draw surfaces onto other surfaces, rotate surfaces, and transform surfaces.

        # Game type 0 is singleplayer
        self.gametype = 0
        self.running = True

        #self.best_fit = 0
        #self.best_bird = None

    # Contains event loop which waits for a mouse click or space press
    # changes gametype variable to 1 or 0
    def show_start_screen(self):
        running = True
        while running:

            # Event handling for intro screen
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    pg.quit()
                    quit()
                if event.type == pg.KEYDOWN:
                    if event.key == pg.K_SPACE:
                        self.gametype = 0
                        running = False
                if event.type == pg.MOUSEBUTTONDOWN:
                    self.gametype = 1
                    running = False

            # Displaying text on the intro screen
            largeText = pg.font.Font(pg.font.match_font("arial"), 50)
            text_surf, text_rect = self.text_objects(TITLE, largeText)
            text_rect.center = ((WIDTH / 2),(HEIGHT / 4))
            self.screen.blit(text_surf, text_rect)

            smaller_text = pg.font.Font(pg.font.match_font("arial"), 20)
            text_surf_2, text_rect_2 = self.text_objects("Press Space to start single player!", smaller_text)
            text_rect_2.center = ((WIDTH / 2),(3 * HEIGHT / 4))
            self.screen.blit(text_surf_2, text_rect_2)

            smaller_text = pg.font.Font(pg.font.match_font("arial"), 20)
            text_surf_2, text_rect_2 = self.text_objects("Click the screen to train!", smaller_text)
            text_rect_2.center = ((WIDTH / 2),(3.5 * HEIGHT / 4))
            self.screen.blit(text_surf_2, text_rect_2)

            pg.display.update()

            self.screen.fill(WHITE)

    # Preps the App for a solo game
    def setup_solo(self):
        self.playing = True
        self.dt = 1 / self.fps
        self.frame_count = 0

        self.player = Bird()
        self.player_group = pg.sprite.Group()
        self.player_group.add(self.player)

        # Set up pipe sprite group
        self.pipes_group = pg.sprite.Group()
        # Create 2 lists of pipes currently in action for future reference
        self.top_pipe_list = list()
        self.bottom_pipe_list = list()
        self.first_pipe_spawned = False

        self.speed_increase = 0
        #For first pipe timer
        self.start_ticks = pg.time.get_ticks()
        #pg.event.post(pg.event.Event(USEREVENT+4))
        pg.time.set_timer(USEREVENT+4, PIPE_TIMER)

    # Contains gameloop (for solo mode)
    def run_solo(self):
        self.setup_solo()
        while self.playing:
            self.events()
            self.update(self.dt)
            self.draw()
            self.dt = self.fpsClock.tick(self.fps)
            self.frame_count += 1

    # Pygame events are checked here (for solo mode)
    def events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                if self.playing:
                    self.playing = False
                self.running = False
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.player.flap()
            if event.type == USEREVENT+4:
                self.generate_pipe_pair()

            # USEREVENT+2 is a scored point event
            if event.type == USEREVENT+2:
                self.player.single_score += 1

            # USEREVENT+3 is a call to remove the first element of both pipe lists
            if event.type == USEREVENT+3:
                del self.bottom_pipe_list[0]
                del self.top_pipe_list[0]
                #self.generate_pipe_pair(self.speed_increase)
                #self.speed_increase += SPEED_INCREASE_RATE

    # Game logic is tested here and all sprites have their own update functions called (for solo mode)
    def update(self, dt):
        # Test for collision between player and pipes or player and the ground
        if self.player.collide(self.pipes_group) or self.player.rect.bottom == HEIGHT:
            self.playing = False

        self.pipes_group.update(dt)
        self.player.update(dt)
        # Show which pipe is focused on

        self.seconds = (pg.time.get_ticks()-self.start_ticks)/1000 #calculate how many seconds

    # Draws to the screen (for solo mode)
    def draw(self):
        self.screen.fill((0,204,255))  # Fill the screen with a color

        ##### Redraw screen here. #####
        # The pipes
        self.pipes_group.draw(self.screen)

        # Then draw ground
        #pg.draw.rect(self.screen, BLACK, (0, HEIGHT - BOTTOM_KILL_THRESHOLD - 20, WIDTH, BOTTOM_KILL_THRESHOLD + 20))
        self.screen.blit(self.floor, self.floor_rect)

        # Then the player groups
        self.player_group.draw(self.screen)

        # Then finally the score
        self.draw_text(self.screen, ("Score: " + str(self.player.single_score)), 30, 0, 0)

        # Flip the display so that the things we drew actually show up.
        pg.display.flip()

    # Similar to show_start_screen(), waits for spacebar
    def show_game_over_screen(self):
        running = True

        while running:

            # Event handling for intro screen
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    pg.quit()
                    quit()
                if event.type == pg.KEYDOWN:
                    if event.key == pg.K_SPACE:
                        self.player.y_velocity = 0
                        self.player.rect.center = (WIDTH / 2, HEIGHT / 2)
                        running = False

            largeText = pg.font.Font(pg.font.match_font("arial"), 40)
            text_surf, text_rect = self.text_objects("Press Space to try again", largeText)
            text_rect.center = ((WIDTH / 2),(3 * HEIGHT / 4))
            self.screen.blit(text_surf, text_rect)

            pg.display.update()

    # Preps the App for an AI experience
    # Generates first generation of birds and preps game stuff
    def setup_training(self, Num_birds):
        self.training = True
        self.playing = True
        self.dt = 1 / self.fps
        self.frame_count = 0

        self.players = [Bird() for i in range(Num_birds)]
        self.saved_players = []
        self.players_group = pg.sprite.Group()
        self.players_group.add(self.players)

        self.generation = 1
        self.record = 0

        # Set up pipe sprite group
        self.pipes_group = pg.sprite.Group()
        # Create 2 lists of pipes currently in action for future reference
        self.top_pipe_list = list()
        self.bottom_pipe_list = list()
        self.first_pipe_spawned = False

        self.speed_increase = 0
        self.game_score = 0
        #For first pipe timer
        self.start_ticks = pg.time.get_ticks()
        #pg.event.post(pg.event.Event(USEREVENT+4))
        pg.time.set_timer(USEREVENT+4, PIPE_TIMER)

    # Main training game loop
    def run_training(self):
        self.training = True
        while self.training:
            for player in self.players:
                player.think(self.top_pipe_list, self.bottom_pipe_list)
            self.t_events()
            self.t_update(self.dt)
            self.t_draw()
            self.dt = self.fpsClock.tick(self.fps)
            self.frame_count += 1

    # Called once all the birds are dead, resets the game stuff
    def reset_training(self):
        self.playing = True
        self.dt = 1 / self.fps
        self.frame_count = 0

        # Set up pipe sprite group
        self.pipes_group.empty()
        # Create 2 lists of pipes currently in action for future reference
        self.top_pipe_list = list()
        self.bottom_pipe_list = list()
        self.first_pipe_spawned = False

        self.speed_increase = 0
        #For first pipe timer
        self.start_ticks = pg.time.get_ticks()
        #pg.time.set_timer(USEREVENT+4, 0)
        pg.time.set_timer(USEREVENT+4, PIPE_TIMER)

    # Pygame events for training
    def t_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                if self.playing:
                    self.playing = False
                    self.training = False
                self.running = False
                break
            if event.type == USEREVENT+4:
                self.generate_pipe_pair()

            # USEREVENT+2 is a scored point event
            if event.type == USEREVENT+2:
                for player in self.players_group:
                    player.score += 1
                self.game_score += 1

            # USEREVENT+3 is a call to remove the first element of both pipe lists
            if event.type == USEREVENT+3:
                if self.bottom_pipe_list.__len__() != 0:
                    del self.bottom_pipe_list[0]
                if self.top_pipe_list.__len__() != 0:
                    del self.top_pipe_list[0]
                #self.generate_pipe_pair(self.speed_increase)
                #self.speed_increase += SPEED_INCREASE_RATE

    # Everyones update functions get called, game logic occurs
    # Calls next_generation() if all the birds die
    def t_update(self, dt):
        # Test for collision between player and pipes or player and the ground/roof
        for player in self.players_group:
            if player.collide(self.pipes_group) or player.rect.bottom == HEIGHT or player.rect.bottom < -40:
                player.alive = False

        self.pipes_group.update(dt)

        # Important
        if self.players.__len__() == 0:
            self.next_generation()
            self.training = False

        if self.record < self.game_score:
            self.record = self.game_score

        for player in self.players_group:
            player.update(dt)
            if not player.alive:
                self.saved_players.append(player)
                self.players.remove(player)
                player.kill()
        # Show which pipe is focused on
        self.seconds = (pg.time.get_ticks()-self.start_ticks)/1000 #calculate how many seconds

    # Draw the training mode to the screen
    def t_draw(self):
        self.screen.fill((0,204,255))  # Fill the screen with a color

        ##### Redraw screen here. #####
        # Then the pipes
        self.pipes_group.draw(self.screen)

        # Draw ground first
        self.screen.blit(self.floor, self.floor_rect)

        # Then the player groups
        self.players_group.draw(self.screen)

        # Then finally the generation number
        self.draw_text(self.screen, ("Generation: " + str(self.generation)), 30, 0, 0)
        self.draw_text(self.screen, ("Current # Pipes Cleared: " + str(self.game_score)), 30, 0, 35)
        self.draw_text(self.screen, ("Current Record: " + str(self.record)), 30, 0, 70)
        self.draw_text(self.screen, ("Current # Birds Alive: " + str(self.players.__len__())), 30, 0, 105)

        # Flip the display so that the things we drew actually show up.
        pg.display.flip()

    # Calculates the fitness of the birds, then calls pick_one_bird() n times until a new popluation has been created
    def next_generation(self):
        self.generation += 1
        self.game_score = 0

        self.calculate_fitness()

        # for bird in self.saved_players:
        #     if bird.fitness > self.best_fit:
        #         self.best_bird = Bird(bird.brain)
        #         self.best_fit = bird.fitness
        # self.players.append(self.best_bird)
        # self.players_group.add(self.best_bird)

        for i in range(NUM_OF_BIRDS):
            new_bird = self.pick_one_bird()
            self.players.append(new_bird)
            self.players_group.add(new_bird)

        self.saved_players = []

    # Assigns each player a fitness score based on how well each proformed relative to eachother
    def calculate_fitness(self):
        total = 0
        for player in self.saved_players:
            total += player.score
        for player in self.saved_players:
            player.fitness = player.score / total

    # Algorithm to pick one bird out of a pool of birds with un equal probabilities of being picked
    # The higher the birds fitness score, the more likely this function will create a mutated copy
    # oh yeah, after it selects the bird, it mutates that bird before returing it alowing us to explore the landscape of posibilities
    # :')
    def pick_one_bird(self):
        index = 0
        r = random.random()
        while (r > 0):
            r = r - self.saved_players[index].fitness
            index += 1
        index -= 1

        bird = self.saved_players[index]
        child = Bird(bird.brain)
        child.brain.mutate(MUTATION_RATE)
        return child

    # Creates a text object
    def text_objects(self, text, font):
        textSurface = font.render(text, True, BLACK)
        return textSurface, textSurface.get_rect()

    # A function used to draw text on the screen
    def draw_text(self, surf, text, size, x, y):
        font = pg.font.Font(self.font_name, size)
        text_surface = font.render(text, True, BLACK)
        text_rect = text_surface.get_rect()
        text_rect.x, text_rect.y = (x, y)
        surf.blit(text_surface, text_rect)

    # Creates 2 pipes that move to the left
    def generate_pipe_pair(self, speed_increase = 0):
        top_pipe_y = random.randrange(50, HEIGHT - BOTTOM_KILL_THRESHOLD - PIPE_GAP_SIZE - 50)
        #top_pipe_y = 200
        bottom_pipe_y = top_pipe_y + PIPE_GAP_SIZE

        top_pipe = TopPipe(top_pipe_y)
        top_pipe.x_velcoity += speed_increase
        self.top_pipe_list.append(top_pipe)
        self.pipes_group.add(top_pipe)

        bottom_pipe = BottomPipe(bottom_pipe_y)
        bottom_pipe.x_velcoity += speed_increase
        self.bottom_pipe_list.append(bottom_pipe)
        self.pipes_group.add(bottom_pipe)

# Program starts here
if __name__ == "__main__":
    a = App()
    while a.running:

        a.show_start_screen()

        if a.gametype == 0:
            a.run_solo()
            a.show_game_over_screen()

        elif a.gametype == 1:
            a.setup_training(NUM_OF_BIRDS)
            while a.running:
                a.run_training()
                a.reset_training()

    pg.quit()


from logging import getLogger
from os import urandom
from urllib.parse import urlparse, urljoin, urlencode

import flask
from flask_login import current_user, login_user, \
            logout_user, login_required, LoginManager
from babel.dates import format_datetime

from .user import User
from ._version import __version__
from .forms import LoginForm, NewAuditForm
from .backend import Backend
from .constants import AuditState, AuditFrequency

app = flask.Flask(__name__)
app.secret_key = urandom(16)
app.config.update(
    #  SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Lax',
)

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.remember_cookie_http_only = True
login_manager.remember_cookie_samesite = True

backend = Backend()
log = getLogger(__name__)

class Pagination(object):
    def __init__(self, total_entries, page_size=5, p_name='page'):
        try:
            current_page = int(flask.request.args.get(p_name))
        except Exception:
            current_page = 1

        first_page = 1
        last_page = total_entries//page_size + 1

        if not (first_page <= current_page and current_page <= last_page):
            raise IndexError("Page index out of range")
        if first_page == last_page:
            self.needed = False
            return
        self.needed = True

        args = flask.request.args.to_dict()

        self.page = current_page
        self.first = first_page
        self.last = last_page

        self.previous = ""
        self.next = ""
        if self.page != self.first:
            args[p_name] = current_page - 1
            self.previous = "?" + urlencode(args)
        if self.page != self.last:
            args[p_name] = current_page + 1
            self.next = "?" + urlencode(args)

        self.links = []
        for i in range(first_page, last_page+1):
            args.update({p_name: i})
            url = '?' + urlencode(args)
            self.links.append({p_name: i, 'href': url})

        if len(self.links) > 7:
            if current_page - first_page < 4:
                right = max(5, current_page - first_page + 2)
                self.links = self.links[:right] + [None] + [self.links[-1]]
            elif last_page - current_page < 4:
                left = min(last_page - first_page - 4, last_page -
                           first_page - (last_page - current_page))
                self.links = [self.links[0]] + [None] + self.links[left:]
            else:
                left = current_page - first_page - 1
                right = current_page - first_page + 2
                self.links = [self.links[0]] + [None] + \
                    self.links[left:right] + \
                    [None] + [self.links[-1]]

def get_context():
    return dict(
        version=__version__,
        user=current_user.get_id(),
        config_errors=sum(backend.config.get_errors().values()),
    )

def is_safe_url(target):
    # Check if the URL belongs to our application
    ref_url = urlparse(flask.request.host_url)
    test_url = urlparse(urljoin(flask.request.host_url, target))
    return (test_url.scheme in ('http', 'https')
            and ref_url.netloc == test_url.netloc)

@login_manager.user_loader
def load_user(user_id):
    return User.logged_in_users[user_id]

@app.route('/login', methods=['POST', 'GET'])
def login():
    if current_user.is_authenticated:
        return flask.redirect(flask.url_for('home'))

    next_url = flask.request.args.get('next', "")

    form = LoginForm(next_url=next_url)
    if form.validate_on_submit():
        next_url = form.next_url.data
        # Prevent open redirection
        if not is_safe_url(next_url):
            return flask.abort(400)
        auth_config = backend.config['authentication']
        user = None
        try:
            user = User.authenticate(
                form.user.data,
                form.password.data,
                auth_config,
            )
        except Exception as e:
            log.warning(str(e))
        if not user:
            flask.flash("Invalid username or password.", 'danger')
            return flask.render_template('login.html', form=form)
        login_user(user, remember=form.remember_me.data)
        return flask.redirect(next_url or flask.url_for('home'))
    return flask.render_template('login.html', form=form)

@app.route('/')
@login_required
def home():
    try:
        status = backend.get_status()
    except Exception as e:
        log.exception(e)
        status = []
    return flask.render_template(
        'home.html',
        sections=status,
        **get_context(),
    )

@app.route('/audit')
@login_required
def audit():
    try:
        audit_uuid = flask.request.args.get('id')
        _, audit_record = backend.get_report_and_audit(audit_uuid)

        audit = {}
        dummy_form = NewAuditForm()
        display_str = {
            'uuid': "ID",
            'end': "End at",
            'state': "State",
            'report': "Report",
        }
        for k, v in audit_record.items():
            if k in ['metadata', 'id']:
                continue
            if k == 'frequency':
                audit[k] = AuditFrequency(v).name
            elif k == 'state':
                audit[k] = AuditState(v).name
            else:
                audit[k] = v
            try:
                display_str[k] = getattr(dummy_form, k).label
            except AttributeError:
                pass
    except Exception as e:
        log.error("Error while retrieving audit: %s" % str(e))
        log.exception(e)
        return str(e), 500
    return flask.render_template(
        'audit.html',
        display_str=display_str,
        audit=audit,
        **get_context(),
    )

@app.route('/audits')
@login_required
def audits():
    try:
        page = int(flask.request.args.get('page'))
    except Exception:
        page = 1
    page_size = 5

    past_audits = backend.get_past_audits()
    pagination = Pagination(len(past_audits))
    past_audits = past_audits[(page - 1)*page_size:page*page_size]

    scheduled_audits = backend.get_scheduled_audits()
    for audit in scheduled_audits:
        audit['start'] = format_datetime(audit['start'], format='short')
        if audit['end']:
            audit['end'] = format_datetime(audit['end'], format='short')

    return flask.render_template(
        'audits.html',
        running_audit=None,
        past_audits=past_audits,
        scheduled_audits=scheduled_audits,
        pagination=pagination,
        **get_context(),
    )

@app.route('/audits/delete', methods=['POST'])
@login_required
def delete_audit():
    audit_uuid = flask.request.args.get('id')
    backend.delete_audit(audit_uuid)
    return flask.redirect(flask.url_for('audits'))

@app.route('/audits/new', methods=['POST', 'GET'])
@login_required
def new_audit():
    AUDIT_MESSAGE = """\
Dear user,

your password has been identified as too weak during a fully automated
password audit. Please change your password to a more secure one soon.

This is an automated message. No human has seen your current password.
Please contact your IT department if you have further questions.
"""
    audit_uuid = flask.request.args.get('id')
    if audit_uuid:
        _, audit = backend.get_report_and_audit(audit_uuid)
        audit['password'] = ''
        audit['start'] = ''
        form = NewAuditForm(**dict(audit))
    else:
        form = NewAuditForm(message=AUDIT_MESSAGE)
    if form.validate_on_submit():
        values = flask.request.form.copy()
        if 'csrf_token' in values:
            del values['csrf_token']
        try:
            id = backend.add_audit(values)
            log.info("User '%s' created a new audit with ID %s" %
                     (current_user.get_id(), id))
            return flask.redirect(flask.url_for('home'))
        except Exception as e:
            log.error(
                "User '%s' attempted to create a new audit, but it failed: %s"  # noqa
                % (current_user.get_id(), str(e))
            )
            log.exception(e)
            flask.flash(str(e), 'danger')
    return flask.render_template(
        'new-audit.html',
        form=form,
        **get_context(),
    )

@app.route('/events')
@login_required
def events():
    if 'flat' in flask.request.args:
        response = flask.make_response(backend.get_log(), 200)
        response.mimetype = "text/plain"
        return response
    try:
        page = int(flask.request.args.get('page'))
    except Exception:
        page = 1
    page_size = 5
    pagination = Pagination(backend.get_event_count())
    event_entries = backend.get_events((page - 1)*page_size, page*page_size)
    return flask.render_template(
        'events.html',
        events=event_entries,
        pagination=pagination,
        **get_context(),
    )

@app.route('/config', methods=['POST', 'GET'])
@login_required
def config():
    sections = backend.config.get_sections()
    section = flask.request.args.get('section') or \
        flask.request.form.get('section') or \
        sections[0][0]

    errors = backend.config.get_errors()

    form = backend.config.get_form(section)
    form.validate()
    if form.validate_on_submit():
        try:
            values = form.data
            if 'csrf_token' in values:
                del values['csrf_token']
            backend.update_config(section, values)
            section = flask.request.form.get('section')
            flask.flash("Settings updated.", 'success')
            return flask.redirect(flask.url_for('config', section=section))
        except Exception as e:
            log.error("Error while updating the config: %s" % str(e))
            log.exception(e)
            flask.flash(str(e), 'danger')
    return flask.render_template(
        'config.html',
        config=config,
        section=section,
        sections=sections,
        form=form,
        errors=errors,
        **get_context(),
    )

@app.route('/logout', methods=['POST'])
@login_required
def logout():
    logout_user()
    return flask.redirect(flask.url_for('login'))

@app.route('/report')
@login_required
def report():
    try:
        audit_id = flask.request.args.get('id', '')
        report, audit = backend.get_report_and_audit(audit_id)
        from .reports import create_text_report, create_figures
        figures = create_figures(report)
        text_report = create_text_report(report)
    except Exception as e:
        log.error("Error while retrieving report: %s" % str(e))
        log.exception(e)
        return "Unable to retrieve report", 500
    return flask.render_template(
        'report.html',
        audit_id=audit_id,
        thisaudit=audit,
        report=text_report,
        figures=figures,
        **get_context(),
    )


from keras.models import Model
from keras.layers import Layer
from keras.layers import Dense, Conv2D, BatchNormalization, Flatten, Dense, MaxPooling2D, AveragePooling2D, Activation, Add

class IdentityBlock(Layer):

    def __init__(self, filters):
        super(IdentityBlock, self).__init__()

        self.conv1 = Conv2D(filters=filters[0], kernel_size=(1, 1), strides = 1, padding='valid')
        self.batch_norm1 = BatchNormalization()
        self.activation1 = Activation('relu')

        self.conv2 = Conv2D(filters=filters[1], kernel_size=(3, 3), strides = 1, padding='same')
        self.batch_norm2 = BatchNormalization()
        self.activation2 = Activation('relu')

        self.conv3 = Conv2D(filters=filters[2], kernel_size=(1, 1), strides = 1, padding='valid')
        self.batch_norm3 = BatchNormalization()

        self.add = Add()
        self.activation3 = Activation('relu')

    def call(self, inputs):
        # First block in Identity block
        x = self.conv1(inputs)
        x = self.batch_norm1(x)
        x = self.activation1(x)

        # Second Block in identity block
        x = self.conv2(x)
        x = self.batch_norm2(x)
        x = self.activation2(x)

        # Third Block in identity block
        x = self.conv3(x)
        x = self.batch_norm3(x)

        # Add input_x to the main path
        x = self.add(inputs=[x, inputs])
        x = self.activation3(x)

        return x

class ConvBlock(Layer):

    def __init__(self, filters, stride=2):
        super(ConvBlock, self).__init__()

        self.conv1 = Conv2D(filters=filters[0], kernel_size=(1, 1), strides = stride, padding='valid')
        self.batch_norm1 = BatchNormalization()
        self.activation1 = Activation('relu')

        self.conv2 = Conv2D(filters=filters[1], kernel_size=(3, 3), strides = 1, padding='same')
        self.batch_norm2 = BatchNormalization()
        self.activation2 = Activation('relu')

        self.conv3 = Conv2D(filters=filters[2], kernel_size=(1, 1), strides = 1, padding='same')
        self.batch_norm3 = BatchNormalization()

        self.input_conv = Conv2D(filters=filters[2], kernel_size=(1, 1), strides = stride, padding='valid')
        self.batch_norm4 = BatchNormalization()

        self.add = Add()
        self.activation5 = Activation('relu')

    def call(self, inputs):
        # First block in Identity block
        x = self.conv1(inputs)
        x = self.batch_norm1(x)
        x = self.activation1(x)

        # Second Block in identity block
        x = self.conv2(x)
        x = self.batch_norm2(x)
        x = self.activation2(x)

        # Third Block in identity block
        x = self.conv3(x)
        x = self.batch_norm3(x)

        # shortcut path
        short_cut = self.input_conv(inputs)
        short_cut = self.batch_norm4(short_cut)

        # Add short_cut to the main path
        x = self.add(inputs=[x, short_cut])
        x = self.activation5(x)

        return x

class ResNet50(Model):

    def __init__(self):
        super(ResNet50, self).__init__(name='ResNet50')

        self.conv       = Conv2D(filters=64, kernel_size=(7, 7), strides= 2)
        self.batch_norm = BatchNormalization()
        self.activation = Activation('relu')
        self.max_pool   = MaxPooling2D(pool_size=(3, 3), strides=2)

        self.conv_block2      = ConvBlock    (filters=[64, 64, 256], stride=1)
        self.identity_block2a = IdentityBlock(filters=[64, 64, 256])
        self.identity_block2b = IdentityBlock(filters=[64, 64, 256])

        self.conv_block3      = ConvBlock    (filters=[128, 128, 512])
        self.identity_block3a = IdentityBlock(filters=[128, 128, 512])
        self.identity_block3b = IdentityBlock(filters=[128, 128, 512])

        self.conv_block4      = ConvBlock    (filters=[256, 256, 1024])
        self.identity_block4a = IdentityBlock(filters=[256, 256, 1024])
        self.identity_block4b = IdentityBlock(filters=[256, 256, 1024])

        self.conv_block5      = ConvBlock    (filters=[512, 512, 2048])
        self.identity_block5a = IdentityBlock(filters=[512, 512, 2048])
        self.identity_block5b = IdentityBlock(filters=[512, 512, 2048])

        self.average_pool = AveragePooling2D(pool_size=(2, 2), strides= 1)
        self.flatten      = Flatten()
        self.dense        = Dense(6, activation='softmax')

    def call(self, inputs):
        # Block-1
        x = self.conv(inputs)
        x = self.batch_norm(x)
        x = self.activation(x)
        x = self.max_pool(x)

        # Block-2
        x = self.conv_block2(x)
        x = self.identity_block2a(x)
        x = self.identity_block2b(x)

        # Block-3
        x = self.conv_block3(x)
        x = self.identity_block3a(x)
        x = self.identity_block3b(x)

        # Block-4
        x = self.conv_block4(x)
        x = self.identity_block4a(x)
        x = self.identity_block4b(x)

        # Block-5
        x = self.conv_block5    (x)
        x = self.identity_block5a(x)
        x = self.identity_block5b(x)

        # Pooling
        x = self.average_pool(x)

        # classifier
        x = self.flatten(x)
        x = self.dense(x)

        return x

resnet50 = ResNet50()
resnet50.build(input_shape=(None, 64, 64, 3))
resnet50.summary()

"""
Model: "ResNet50"
_______________________________________________________________________________
 Layer (type)                               Output Shape             Param #
===============================================================================
 conv2d (Conv2D)                            multiple                 9472

 batch_normalization (BatchNormalization)   multiple                 256

 activation (Activation)                    multiple                 0

 max_pooling2d (MaxPooling2D)               multiple                 0

 conv_block (ConvBlock)                     multiple                 76928

 identity_block (IdentityBlock)             multiple                 71552

 identity_block_1 (IdentityBlock)           multiple                 71552

 conv_block_1 (ConvBlock)                   multiple                 383232

 identity_block_2 (IdentityBlock)           multiple                 282368

 identity_block_3 (IdentityBlock)           multiple                 282368

 conv_block_2 (ConvBlock)                   multiple                 1520128

 identity_block_4 (IdentityBlock)           multiple                 1121792

 identity_block_5 (IdentityBlock)           multiple                 1121792

 conv_block_3 (ConvBlock)                   multiple                 6054912

 identity_block_6 (IdentityBlock)           multiple                 4471808

 identity_block_7 (IdentityBlock)           multiple                 4471808

 average_pooling2d (AveragePooling2D)       multiple                 0

 flatten (Flatten)                          multiple                 0

 dense (Dense)                              multiple                 12294

=================================================================================
Total params: 19,952,262
Trainable params: 19,909,894
Non-trainable params: 42,368
_________________________________________________________________________________
"""


from __future__ import annotations
from flask_sqlalchemy import SQLAlchemy
from typing import Optional, List

db = SQLAlchemy()

class Question(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    question_id = db.Column(db.Integer, nullable=False)
    question_text = db.Column(db.Text, nullable=False)
    answer_text = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, nullable=False)

    def __repr__(self):
        return f'Question({self.id})'

    @classmethod
    def get_latest(cls) -> Optional[Question]:
        max_id = db.session.query(db.func.max(cls.id)).scalar()
        return cls.query.filter_by(id=max_id).first()

    @classmethod
    def get_duplicates(cls, questions_ids: List[int]) -> List[int]:
        existing_questions = cls.query.filter(cls.question_id.in_(questions_ids)).all()
        return [q.question_id for q in existing_questions]


import pandas as pd
from imblearn.over_sampling import SMOTE
from typing import List, Union
from utils import *
from sklearn.model_selection import train_test_split

class CfModel:
    """
    A Classification model
    """

    def __init__(self, l90d: pd.DataFrame, n180d: pd.DataFrame, growth_data: pd.DataFrame, stable_data: pd.DataFrame,
                 features: List[str], subset_type: str):
        """Create a base classifier model object

        Args:
            l90d (pd.DataFrame): df for last 90 days training data.
            n180d (pd.DataFrame): df for next 180 days test data.
            growth_data (pd.DataFrame): df for growth data with growth labels
            stable_data (pd.DataFrame): df for growth data with growth labels
            features (List[str]): either growth/stable features
            subset_type (str): string specifying 'growth' or 'stable'
        """
        self.l90d = l90d
        self.n180d = n180d
        self.df270 = pd.concat([self.l90d, self.n180d], ignore_index=True) \
            .drop_duplicates() \
            .sort_values(by=['user_id', 'pt_date'])
        self.subset_type = subset_type
        self.growth_data = growth_data
        self.stable_data = stable_data
        self.data = self.growth_data if subset_type == 'growth' else self.stable_data
        self.features = features

        # model training variables
        self.X_train = None
        self.y_train = None
        self.X_test = None
        self.y_test = None
        self.train_users = None
        self.test_users = None
        self.y_pred = None
        self.model = None
        self.get_x_train_y_train()

    def get_x_train_y_train(self) -> None:
        """
        Prepares data for modeling by conducting train-test split and applying SMOTE.
        """
        data = self.data
        X = data.drop(columns=['label'])
        y = data['label']

        # Train-test split on user level
        X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, test_size=0.2, random_state=0)

        train_users = X_train['user_id'].tolist()
        test_users = X_test['user_id'].tolist()
        X_train = X_train.drop(columns=['user_id'])
        X_test = X_test.drop(columns=['user_id'])

        # oversample with SMOTE to overcome class imbalance
        smote = SMOTE(sampling_strategy='minority', random_state=42)
        X_train, y_train = smote.fit_resample(X_train, y_train)

        self.X_train, self.y_train, self.X_test, self.y_test, self.train_users, self.test_users = (
            X_train[self.features], y_train, X_test[self.features], y_test, train_users, test_users)

    def train(self):
        pass

    def predict(self):
        pass

    def evaluate(self):
        pass

    def show_metrics(self):
        metrics_df = get_metrics_df(self.y_test, self.y_pred)
        print(metrics_df)
        return metrics_df

    def get_results_df(self, user_features: pd.DataFrame) -> pd.DataFrame:
        """
        Adds user features to the test set of users

        Args:
            user_features (pd.DataFrame): Df containing user features

        Returns:
            pd.DataFrame: df with user labels, features and weight
        """
        results_df = pd.DataFrame({
            'user_id': self.test_users,
            'true_label': self.y_test,
            'predicted_label': self.y_pred
        })

        results_df = results_df.merge(user_features, on='user_id', how='left')

        # Weight of each user relative to the 10k validation set users (not entire portfolio of 50k users)
        results_df['weight'] = results_df['avg_balance'] / np.sum(results_df['avg_balance'])
        return results_df

    def get_subset_pred_true(self) -> (pd.DataFrame, pd.DataFrame):
        """
        Returns two separate predicted and true subsets
        """
        results_df = self.get_results_df(self.data)
        # Predicted subset
        subset_pred = results_df[results_df['predicted_label'] == 1].copy()
        subset_pred = self._get_subset_pred_true(subset_pred)

        # Actual subset
        subset_true = results_df[results_df['true_label'] == 1].copy()
        subset_true = self._get_subset_pred_true(subset_true)

        return subset_pred, subset_true

    @staticmethod
    def _get_subset_pred_true(df: pd.DataFrame) -> pd.DataFrame:
        """
        Calculate weight relative to subset & obtain weighted stability
        """
        df['subset_weight'] = df['avg_balance'] / df['avg_balance'].sum()
        df['weighted_stability'] = df['subset_weight'] * df['stability_index']

        return df


import discord
from discord.ext import commands
from discord_ui import UI , Button, Interaction
from keystone import keystone_const as ksconst

from asyncio import sleep as asleep
from convertor import b64_convert
from help import getHelp
from helper import get_Author, findURL, translateURL

# permissions integer: 534723951680
bot = commands.Bot(command_prefix='.')
ui = UI(bot)

@bot.event
async def on_message(message):
    ctx = await bot.get_context(message)
    author = get_Author(ctx)
    msg, hasURL = findURL(message)

    # Dont reply to self, bots
    if ctx.author.bot:
        return

    # Nothing in DMs
    if ctx.guild == None:
        await bot.process_commands(message)
        return

    # Dont respond to normal messages
    if not hasURL:
        await bot.process_commands(message)
        return

    embed = discord.Embed(title=f"", description=msg, colour=discord.Colour.green())
    embed.set_author(name = ctx.author, icon_url=author[1])
    embed.set_footer(text = "Links are encoded.")

    if message.reference is not None:
        # Fetching the message
        channel = bot.get_channel(message.reference.channel_id)
        referenced_message = await channel.fetch_message(message.reference.message_id)

        await ui.components.send(channel=message.channel,embed=embed,
                                reference=message.reference,
                                mention_author= referenced_message.author.mentioned_in(message),
                                components=[Button("Decode Links", "translate", "green")])

    else:
        await(ui.components.send(channel=message.channel, embed = embed,
                                components=[Button("Decode Links", "translate", "green")]))

        await message.delete()

    await bot.process_commands(message)

@bot.listen("on_interaction_received")
async def on_interact(interaction: Interaction):
    # See if translate button is clicked
    if interaction.data["custom_id"] == 'translate':
        msg = interaction.message
        embed = msg.embeds[0]
        links = translateURL(embed.description)
        await interaction.respond(content="**Links:**\n```\n" + links + "```", hidden=True)

@bot.command()
async def sh(ctx, task, string=None):
    username = get_Author(ctx)[0]

    if task == 'encode':
        embed = discord.Embed(title=f"Encoded string for {username}:", description=b64_convert(string, False), colour=discord.Colour.green())
        await ctx.send(embed=embed)

    elif task == 'decode':
        embed = discord.Embed(title=f"Decoded string for {username}:", description=b64_convert(string, True), colour=discord.Colour.green())
        await ctx.send(embed=embed)

    elif task == 'help':
        embed = discord.Embed(title="List of commands:", description=getHelp(), colour=discord.Colour.green())
        await ctx.send(embed=embed)

    else:
        embed = discord.Embed(title="List of commands:", description=getHelp(), colour=discord.Colour.green())
        await ctx.send(embed=embed)

@bot.event
async def on_ready():
    await bot.change_presence(activity = discord.Activity(name="Trying to Dream", type=discord.ActivityType.custom))
    print("Bot is ready!")

bot.load_extension("cogs.asm")
bot.run("MY BOT TOKEN")


from sklearn.model_selection import GridSearchCV

from chronus.domain.interfaces.optimizer_interface import OptimizerInterface
from chronus.domain.Run import Run

class RandomTreeOptimizer(OptimizerInterface):
    @staticmethod
    def name() -> str:
        return "random-tree"

    def make_model(self, runs: list[Run]):
        import numpy as np
        from sklearn.model_selection import train_test_split

        X = []
        y = []

        for run in runs:
            X.append([run.cores, run.threads_per_core, run.frequency])
            y.append(run.gflops_per_watt)

        X = np.array(X)
        y = np.array(y)

        param_grid = {
            "n_estimators": [10, 50, 100, 200],
            "max_depth": [None, 10, 20, 30],
            "min_samples_split": [2, 5, 10],
            "min_samples_leaf": [1, 2, 4],
            "max_features": ["auto", "sqrt"],
            "bootstrap": [True, False],
        }
        from sklearn.ensemble import RandomForestRegressor

        rf = RandomForestRegressor(random_state=42)
        grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5, n_jobs=-1, verbose=2)

        grid_search.fit(X, y)
        best_params = grid_search.best_params_

        print("Best hyperparameters:")
        print(best_params)

        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

        # random forrest regressor with these paremters {'bootstrap': False, 'max_depth': None, 'max_features': 'auto', 'min_samples_leaf': 1, 'min_samples_split': 2, 'n_estimators': 10}
        model = RandomForestRegressor(**best_params, random_state=42)
        model.fit(X, y)

        from sklearn.metrics import mean_squared_error, r2_score

        # Define the range of cores, threads_per_core, and frequencies you want to test
        cores_range = range(1, 33)
        threads_per_core_range = range(1, 3)
        frequencies_range = [1500000.0, 2200000.0, 2500000.0]

        # Generate all possible combinations of configurations
        configurations = [
            {"cores": cores, "threads_per_core": tpc, "frequency": freq}
            for cores in cores_range
            for tpc in threads_per_core_range
            for freq in frequencies_range
        ]

        configurations_np = np.array([list(config.values()) for config in configurations])
        predicted_efficiencies = model.predict(configurations_np)
        best_config_index = np.argmax(predicted_efficiencies)

        best_config = configurations[best_config_index]
        best_efficiency_pred = predicted_efficiencies[best_config_index]

        print("Best predicted configuration:")
        print(f"Cores: {best_config['cores']}")
        print(f"Threads per core: {best_config['threads_per_core']}")
        print(f"Frequency: {best_config['frequency']} Hz")
        print(f"Predicted energy efficiency: {best_efficiency_pred} GFLOPS/Watt")


import random
import utils.util as util
import copy

class Trade:
    def __init__(self):
        self.mastery: int = 0
        self.offer: str = ""
        self.offer_quantity: int = 0

        self.needing: str = ""
        self.needing_quantity: int = 0

    @staticmethod
    def generateFromTradeTable(lore_village, villager, trade_table_original: dict, material_replacements: dict):
        number_required: int = 0

        trade_table: dict = Trade.filterTableTrade(lore_village, villager, trade_table_original)

        if "numberOfRequired" in trade_table.keys():
            number_required = trade_table["numberOfRequired"]

        chances: list = []
        trade: dict
        for trade in trade_table["trades"]:
            chances.append(trade["chance"])

        trades: list = util.selectNWithChanceForOther(trade_table["trades"], chances, number_required, True)
        for tradeModel in trades:
            trade: Trade = Trade()

            trade.needing = util.changeNameWithReplacements(tradeModel["required"], material_replacements)[1]

            if type(tradeModel["amount"]) == list:
                trade.needing_quantity = random.randint(tradeModel["amount"][0], tradeModel["amount"][1])
            else:
                trade.needing_quantity = tradeModel["amount"]

            trade.offer = util.changeNameWithReplacements(tradeModel["offered"], material_replacements)[1]
            if type(tradeModel["amountOffered"]) == list:
                trade.offer_quantity = random.randint(tradeModel["amountOffered"][0], tradeModel["amountOffered"][1])
            else:
                trade.offer_quantity = tradeModel["amountOffered"]

            villager.trades.append(trade)

        if "generateTrades" in trade_table.keys():
            Trade.handleGeneratedTrades(lore_village, villager, trade_table, material_replacements)

    @staticmethod
    def filterTableTrade(lore_village, villager, trade_table: dict) -> dict:
        table_copy: dict = copy.deepcopy(trade_table.copy())

        for i in range(len(table_copy["trades"]) - 1, -1, -1):
            trade = table_copy["trades"][i]

            if "conditions" in trade.keys():
                if not Trade.conditionsSatisfied(lore_village, villager, trade["conditions"]):
                    del table_copy["trades"][i]

        return table_copy

    @staticmethod
    def conditionsSatisfied(lore_village, villager, conditions: dict) -> bool:
        for condition in conditions.keys():
            if not Trade.checkOneConditions(lore_village, villager, conditions, condition):
                return False

        return True

    @staticmethod
    def checkOneConditions(lore_village, villager, conditions: dict, condition: str):
        if condition == "tier":
            min_tier: int = 0
            max_tier: int = 0

            if "min" in conditions["tier"].keys():
                min_tier = conditions["tier"]["min"]

            if "max" in conditions["tier"].keys():
                max_tier = conditions["tier"]["max"]

            if "value" in conditions["tier"].keys():
                min_tier = conditions["tier"]["value"]
                max_tier = conditions["tier"]["value"]

            return min_tier <= lore_village.tier <= max_tier

        return False

    @staticmethod
    def handleGeneratedTrades(lore_village, villager, trade_table: dict, material_replacements: dict):
        if villager.job == "Exchanger":
            for interactionKey in lore_village.village_interactions:
                interaction: VillageInteraction = lore_village.village_interactions[interactionKey]

                if interaction.economicalRelation:

                    anotherVillage = interaction.village1 if (interaction.village1 != lore_village) else interaction.village2

                    trade: Trade = Trade()
                    trade.needing = util.returnCurrencyTrade(lore_village.name + " gem")
                    trade.offer = util.returnCurrencyTrade(anotherVillage.name + " gem")

                    trade.needing_quantity = 1 if (anotherVillage.tier == lore_village.tier)\
                        else 2 if (anotherVillage.tier > lore_village.tier) else 3
                    trade.offer_quantity = 1 if (anotherVillage.tier == lore_village.tier) \
                        else 2 if (anotherVillage.tier < lore_village.tier) else 3

                    villager.trades.append(trade)

    def toStr(self, isFirstTrade=False) -> str:
        result: str

        if isFirstTrade:
            result = "{"
        else:
            result = ",{"

        # buy <-> dict

        if "{" not in self.needing and "[" not in self.needing:
            result += "buy:{id:\"" + self.needing + "\", "
        else:
            result += "buy:{id:" + self.needing + ", "

        result += "Count:" + str(self.needing_quantity) + "}"
        # buy B <-> dict
        if "{" not in self.offer and "[" not in self.offer:
            result += ", sell:{id:\"" + self.offer + "\","
        else:
            result += ", sell:{id:" + self.offer + ","

        result += "Count:" + str(self.offer_quantity) + "}"
        # maxUses <-> int
        result += ", maxUses:999999"
        # Xp <-> int
        # rewardXp <-> boolean
        # uses <-> int
        # specialPrice <-> float
        # price multiplier <-> float
        # demand <-> float

        result += "}"

        return result

from generation.data.villageInteraction import VillageInteraction


import sys
with open('input.txt', 'r') as f:
    measurements = f.read().splitlines()

    # This is the main thing of part 2:
    #
    # We solve it with a list comprehension: we loop over the origin list and sum the numbers from i to i+3 for every i in measurements.
    # We use the map function inside the sum function to convert the strings to integers.

    triple_mes = [sum(map(int, measurements[i:i+3]))
                  for i in range(0, len(measurements), 1)]

    # we copy the counter from the first part of day 1 - we could write a reusable function, but since we only use it twice and its only for one challenge, we are fine.
    counter = 0
    tmp = sys.maxsize
    for measurement in triple_mes:
        if int(measurement) > tmp:
            counter += 1
        tmp = int(measurement)

    print(counter)


APP_NAME = "Appsilon's Port Dashboard"

TAB1_NAME = "Port map"
TAB2_NAME = "Port stats"
TAB3_NAME = "Compare ports"
BTN_REQUEST_PORT = "Request a port"

TAB1_CARD_BODY_TEXT = "comparing to other Balic ports"
TAB1_CARD_FOOTER_TEXT = "Base port:"
TAB1_CARD1_HEADING = "Avg. vessels # in port"
TAB1_CARD2_HEADING = "Avg. mooring time"
TAB1_CARD3_HEADING = "Avg. vessel dwt in port"

MODAL_HEADER = "Contact us to see more"
MODAL_BODY_HEADING = "This is a demo. It is limited in data."
MODAL_BODY_TEXT = (
    "If you want to see our dataset for other ports and other time frame get in touch with us. We "
    "collect hundreds gigabytes of maritime data to deliver business insights to our customers. "
)
MODAL_PHONE = "+1 415 429 3903"
MODAL_EMAIL = "hello@appsilon.com"
MODAL_BTN_MAIL = "Get more data"
MODAL_BTN_CANCEL = "Cancel"

CITY_ALL = "All"
CITY_GDANSK = "Gdańsk"
CITY_GDYNIA = "Gdynia"
CITY_KALINGRAD = "Kalingrad"
CITY_KLAIPEDA = "Klaipeda"
CITY_STPETERBURG = "St. Petersburg"

STYPE_UNSPECIFIED = "Unspecified"
STYPE_NAVIGATION = "Navigation"
STYPE_FISHING = "Fishing"
STYPE_TUG = "Tug"
STYPE_PASSENGER = "Passenger"
STYPE_CARGO = "Cargo"
STYPE_TANKER = "Tanker"
STYPE_PLEASURE = "Pleasure"

LABEL_PORT = "port"
LABEL_VESSEL = "vessel type"
LABEL_YEAR = "year"
LABEL_MONTH = "month"
LABEL_PORT_COMPARE = "port to compare"

SIP_CARD_MAP_IN_PORT = "Vessels in port"
SIP_CARD_MAP_SAILING = "Sailing"
SIP_CARD_MAP_MOORED = "Moored"
SIP_CARD_MAP_DATE = "Status at"

MAP_POPUP_VESSEL = "Vessel name: "
MAP_POPUP_LATITUDE = "Latitude: "
MAP_POPUP_LONGITUDE = "Longitude: "
MAP_POPUP_STOP_DUR = "Stop duration in min: "

COMPARISON_TITLE_END = "'s insights"
COMPARISON_NUM_INCREASE = "Consecutive days, when number of vessels increased:"
COMPARISON_NUM_DECREASE = "Consecutive days, when number of vessels decreased:"
COMPARISON_TREND = "Detected trend:"

CHART_STATS_TOTAL_VESSELS_TITLE = "Total number of vessels in port"
CHART_STATS_TOTAL_VESSELS_X = "Date"
CHART_STATS_TOTAL_VESSELS_Y = "Number of vessels in the port"
CHART_STATS_STOP_DUR_TITLE = "Average vessel stop duration by type"
CHART_STATS_STOP_DUR_X = "Vessel type"
CHART_STATS_STOP_DUR_Y = "Average stop time in minutes"
CHART_STATS_TOTAL_CAP_VESSELS_TITLE = "Total capacity of vessels in the port"
CHART_STATS_TOTAL_CAP_VESSELS_X = "Date"
CHART_STATS_TOTAL_CAP_VESSELS_Y = "Total capacity (DWT)"
CHART_COMPARE_NUM_VESSELS_TITLE = (
    "Total number of vessels in the ports for selected vessel type"
)
CHART_COMPARE_NUM_VESSELS_X = "Date"
CHART_COMPARE_NUM_VESSELS_Y = "Number of vessels in ports"
CHART_COMPARE_AVG_DURATION_TITLE = (
    "Average vessel stop duration for selected vessel type"
)
CHART_COMPARE_AVG_DURATION_X = "Port"
CHART_COMPARE_AVG_DURATION_Y = "Average stop time in minutes"
CHART_COMPARE_CAPACITY_TITLE = (
    "Total capacity of vessels in the ports for selected vessel type"
)
CHART_COMPARE_CAPACITY_X = "Date"
CHART_COMPARE_CAPACITY_Y = "Sum capacity (DWT)"


import pygame
import os
from .piece import Piece, colors
import game

class Pawn(Piece):
    def __init__(self, color):
        super().__init__(color)

        self.making_promotion = False
        self.img = pygame.transform.scale(pygame.image.load(os.path.join("assets/pieces/" + self.color, self.color + "_pawn.png")), (50, 50))

    def check_valid_move(self, whites, blacks, row, col):

        for p in (whites + blacks):
            if p is not self:
                if self.current_col == col and p.current_col == self.current_col and row == p.current_row:
                    return False

                if p.color == self.enemy_color and p.current_row == row and p.current_col == col and (p.current_col == self.current_col + 1 or p.current_col == self.current_col - 1):
                    if self.color == colors[0]:
                        # color is white
                        if p.current_row == self.current_row - 1:
                            self.piece_to_kill = p
                            self.pieces_to_kill = blacks
                            return True
                    else:
                        # color is black
                        if p.current_row == self.current_row + 1:
                            self.piece_to_kill = p
                            self.pieces_to_kill = whites
                            return True

        for p in (whites + blacks):
            if col == self.current_col:
                if self.color == colors[0]:
                    if self.current_row == 6:
                        if self.current_row - row == 2:
                            if p.current_row == self.current_row - 1 and p.current_col == col:
                                return False
                            return True
                    if self.current_row - row == 1:
                        return True
                    else:
                        return False

                else:
                    if self.current_row == 1:
                        if row - self.current_row == 2:
                            if p.current_row == self.current_row + 1 and p.current_col == col:
                                return False
                            return True
                    if row - self.current_row == 1:
                        return True
                    else:
                        return False
            else:
                return False

        return True

    def draw(self, win):
        super().draw(win)

    def click_promotion(self, x, y, whites, blacks):

        if self.making_promotion:
            for rect_num, rect in enumerate(self.rects):
                if x <= rect.right and x >= rect.left and y >= rect.top and y <= rect.bottom:
                    self.possible_selections[rect_num].set_position(self.current_row, self.current_col, self.current_square)
                    self.making_promotion = False
                    game.rules_check = True

                    if self.color == colors[0]:
                        # color is white
                        whites.append(self.possible_selections[rect_num])
                        whites.remove(self)
                    else:
                        # color is black
                        blacks.append(self.possible_selections[rect_num])
                        blacks.remove(self)

                    return

    def create_promotion_screen(self):
        from .queen import Queen
        from .rook import Rook
        from .bishop import Bishop
        from .knight import Knight

        self.wide_rect = pygame.Rect(75, 225, 270, 160)

        self.current_color_index = colors.index(self.color)

        rook = Rook(self.current_color_index)
        rook.has_moved = True

        self.possible_selections = [Queen(self.current_color_index), rook, Bishop(self.current_color_index), Knight(self.current_color_index)]
        self.rects = []

        for rect_num in range(len(self.possible_selections)):
            self.rects.append(pygame.Rect(self.wide_rect.left + (50*rect_num) + 10*(rect_num+1) + 10, self.wide_rect.top + self.wide_rect.height*0.5, 50, 50))

        self.font = pygame.font.SysFont('Comic Sans MS', 25, bold = True)

    def draw_promotion(self, win):
        if self.making_promotion:
            pygame.draw.rect(win, (255, 0, 0), self.wide_rect)

            for r_num, r in enumerate(self.rects):
                pygame.draw.rect(win, (255, 255, 255), r)
                win.blit(self.possible_selections[r_num].img, (r.left, r.top))

            win.blit(self.font.render("Choose a promotion", True, (255, 255, 255)), (self.wide_rect.left + 20, self.wide_rect.top + 15))


#!/usr/bin/env python3
from collections import defaultdict

class Day1:
  def __init__(self):
    with open('day1/in.txt') as f:
      content = f.readlines()
    self.list = [int(x.strip()) for x in content]

  def part1(self):
    print('Day 1 pt1 : %d ' % sum(self.list))

  def part2(self):
    dd = defaultdict(int)
    running_sum = 0
    found = False
    while not found:
      for i in self.list:
        running_sum += i
        dd[running_sum] = dd[running_sum] + 1
        if dd[running_sum] > 1:
          found = True
          print('Day 2 tp 2: %d' %  running_sum)
          break


from __future__ import annotations

WEEK = {
    0: "DOMINGO",
    1: "LUNES",
    2: "MARTES",
    3: "MIÉRCOLES",
    4: "JUEVES",
    5: "VIERNES",
    6: "SÁBADO",
}

MONTHS = {
    1: ["ENERO", 31],
    2: ["FEBRERO", 28],
    3: ["MARZO", 31],
    4: ["ABRIL", 30],
    5: ["MAYO", 31],
    6: ["JUNIO", 30],
    7: ["JULIO", 31],
    8: ["AGOSTO", 31],
    9: ["SEPTIEMBRE", 30],
    10: ["OCTUBRE", 31],
    11: ["NOVIEMBRE", 30],
    12: ["DICIEMBRE", 31],
}
class Date:
    def __init__(self, day: int, month: int, year: int):
        '''Validar día, mes y año. Se comprobará si la fecha es correcta
        (entre el 1-1-1900 y el 31-12-2050); si el día no es correcto, lo pondrá a 1;
        si el mes no es correcto, lo pondrá a 1; y si el año no es correcto, lo pondrá a 1900.
        Ojo con los años bisiestos.'''
        if year > 2050 or year < 1900:
            year = 1900
        if month > 12 or month < 1:
            month = 1
        if day > 31 or day < 1:
            day = 1
        self.day = day
        self.month = month
        self.year = year

    @staticmethod
    def is_leap_year(year: int) -> bool:
        return year % 4 == 0 and year % 100 != 0 or year % 400 == 0

    def get_delta_days(self) -> int:
        '''Número de días transcurridos desde el 1-1-1900 hasta la fecha'''
        total_days = 0
        for year in range(1900, self.year):
            if Date(1, 1, year).is_leap_year(year):
                total_days += 366
            else:
                total_days += 365

        for month in range(1, self.month):
            new_calc_date = Date(1, month, self.year)
            total_days += new_calc_date.days_in_month

        total_days += self.day - 1
        return total_days

    @property
    def days_in_month(self) -> int:
        '''Día de la semana de la fecha (0 para domingo, ..., 6 para sábado).
        El 1-1-1900 fue domingo.'''
        if self.month == 2 and Date.is_leap_year(self.year):
            return 29
        else:
            days_in_month = MONTHS[self.month][1]
            return days_in_month

    @property
    def weekday(self) -> int:
        total_days = self.get_delta_days() + 1
        return total_days % 7

    @property
    def is_weekend(self) -> bool:
        return True if self.weekday == 6 or self.weekday == 0 else False

    @property
    def short_date(self) -> str:
        '''02/09/2003'''
        if self.day >= 10:
            new_type_day = str(self.day)
        else:
            new_type_day = "0" + str(self.day)
        if self.month >= 10:
            new_type_month = str(self.month)
        else:
            new_type_month = "0" + str(self.month)
        new_type_year = str(self.year)

        return f"{new_type_day}/{new_type_month}/{new_type_year}"

    def __str__(self):
        '''MARTES 2 DE SEPTIEMBRE DE 2003'''
        day_name = WEEK[self.weekday]
        month_name = MONTHS[self.month][0]
        return f"{day_name} {self.day} DE {month_name} DE {self.year}"

    def __add__(self, days: int) -> Date:
        '''Sumar un número de días a la fecha'''
        new_date = Date(self.day, self.month, self.year)
        days_to_add = days
        while days_to_add > 0:
            days_in_month = new_date.days_in_month
            if new_date.day + days_to_add > days_in_month:
                days_to_add -= days_in_month - new_date.day + 1
                new_date.day = 1
                if new_date.month == 12:
                    new_date.month = 1
                    new_date.year += 1
                else:
                    new_date.month += 1
            else:
                new_date.day += days_to_add
                days_to_add = 0
        return new_date

    def __sub__(self, other: Date | int) -> int | Date:
        '''Dos opciones:
        1) Restar una fecha a otra fecha -> Número de días
        2) Restar un número de días a la fecha -> Nueva fecha'''
        if isinstance(other, Date):
            days1 = self.get_delta_days()
            days2 = other.get_delta_days()
            days_difference = days1 - days2
            return days_difference
        elif isinstance(other, int):
            new_date = Date(self.day, self.month, self.year)
            days_to_subtract = other
            while days_to_subtract > 0:
                if new_date.day > days_to_subtract:
                    new_date.day -= days_to_subtract
                    days_to_subtract = 0
                else:
                    days_to_subtract -= new_date.day
                    new_date.month -= 1
                    if new_date.month == 0:
                        new_date.month = 12
                        new_date.year -= 1
                    new_date.day = new_date.days_in_month
            return new_date

    def __eq__(self, other: Date) -> bool:
        if isinstance(other, Date):
            return (
                self.day == other.day
                and self.month == other.month
                and self.year == other.year
            )
        return False

    def __gt__(self, other: Date) -> bool:
        if self.year > other.year:
            return True
        elif self.year == other.year and self.month > other.month:
            return True
        elif (
            self.year == other.year
            and self.month == other.month
            and self.day > other.day
        ):
            return True
        else:
            return False

    def __lt__(self, other: Date) -> bool:
        if self.year < other.year:
            return True
        elif self.year == other.year and self.month < other.month:
            return True
        elif (
            self.year == other.year
            and self.month == other.month
            and self.day < other.day
        ):
            return True
        else:
            return False


"""
aeiou
for each of the vowels, ^mask with 1 << (ord(vowels) - ord('a'))
even counts of vowels means equal mask value
iterate the whole string
use a hash map to record the first occurence of each mask value
"""
class Solution:
    def findTheLongestSubstring(self, s: str) -> int:
        vowels = 'aeiou'
        res = 0
        mask = 0
        loc = {0:-1}

        for i in range(len(s)):
            if s[i] in vowels:
                mask ^= 1 << (ord(s[i])-ord('a'))
            if mask in loc:
                res = max(res, i-loc[mask])
            else:
                loc[mask] = i
        return res


class Node:
    def __init__(self, val, prev=None, next=None, child=None):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child

    def __str__(self):
        return str(self.val) + "->" + (str(self.next) if self.next else "")

class FlattenAMultilevelDoublyLinkedList:
    def flatten(self, head: Node) -> Node:
        if not head:
            return head

        stack=[head]

        result=Node(0, None, None, None)
        dummy=result

        while stack:
            node=stack.pop()

            result.next=node
            node.prev=result
            result=result.next

            if node.next:
                stack.append(node.next)
                node.next=None
            if node.child:
                stack.append(node.child)
                node.child=None

        dummy.next.prev=None
        return dummy.next

if __name__ == '__main__':
    init = FlattenAMultilevelDoublyLinkedList()

    one = Node(1)
    two = Node(2)
    three = Node(3)
    four = Node(4)
    five = Node(5)
    six = Node(6)
    seven = Node(7)
    eight = Node(8)
    nine = Node(9)
    ten = Node(10)
    eleven = Node(11)
    twelve = Node(12)

    one.next = two
    two.next = three
    three.next = four
    four.next = five
    five.next = six

    three.child = seven
    seven.next = eight
    eight.next = nine
    nine.next = ten

    eight.child = eleven
    eleven.next = twelve

    print(init.flatten(one)) #1->2->3->7->8->11->12->9->10->4->5->6->


import os, sys
import numpy as np
import pandas as pd
import ROOT as r
from ROOT import TTree, TObjArray, TCanvas, TPad, TFile, TPaveText
import json
from collections import OrderedDict
from multiprocessing import Pool
from memory_profiler import profile
from root_pandas import read_root
import rootpy
from rootpy.io import root_open
from tqdm import tqdm, trange
from collections import defaultdict

selection = sys.argv[1]

XS_table ={ 'ww'              :  12178,
            'wz_2l2q'         :  5595,
            'wz_3lnu'         :  4430,
            'zz_2l2nu'        :  564,
            'zz_2l2q'         :  3220,
            'zz_4l'           :  1210,

           'zjets_m-10to50'   : 18610000,
           'z1jets_m-10to50'  : 1.18*730300,
           'z2jets_m-10to50'  : 1.18*387400,
           'z3jets_m-10to50'  : 1.18*95020,
           'z4jets_m-10to50'  : 1.18*36710,

           'zjets_m-50'       :  5765400,
           'z1jets_m-50'      :  1.18*1012000,
           'z2jets_m-50'      :  1.18*334700,
           'z3jets_m-50'      :  1.18*102300,
           'z4jets_m-50'      :  1.18*54520,

           'w1jets'           :  9493000,
           'w2jets'           :  3120000,
           'w3jets'           :  942300,
           'w4jets'           :  524100,

           't_tw'             :  35850,
           'tbar_tw'          :  35850,
           'ttbar_inclusive'  :  832000,

           'TTZToLLNuNu'      : 252.9,
           'TTZToQQ'          : 529.7,
           'TTWJetsToLNu'     : 204.3,
           'TTWJetsToQQ'      : 406.2,
           'ttHJetTobb'       : 295.0,

           'qcd_ht100to200'   :27990000000,
           'qcd_ht200to300'   :1712000000,
           'qcd_ht300to500'   :347700000,
           'qcd_ht500to700'   :32100000,
           'qcd_ht700to1000'  :6831000,
           'qcd_ht1000to1500' :1207000,
           'qcd_ht1500to2000' :119900,
           'qcd_ht2000'       :25240,
          }

def make_directory(filePath, clear=True):
    if not os.path.exists(filePath):
        os.system('mkdir -p '+filePath)

    if clear and len(os.listdir(filePath)) != 0:
        os.system('rm '+filePath+'/*')

def fill_lepton_vars(tree, name, SF):

    out_dict = {}
    out_dict['nMuons']       =  tree.nMuons
    out_dict['nElectrons']   =  tree.nElectrons
    out_dict['nJets']        =  tree.nJets
    out_dict['nBJets']       =  tree.nBJets
    out_dict['nPV']          =  tree.nPV
    out_dict['nPU']          =  tree.nPU

    correction = tree.eventWeightPu
    correction *= tree.eventWeightTrigger
    correction *= tree.eventWeightId
    correction *= tree.eventWeightIso

    weight = SF * correction
    if name in ['zjets_m-50','zjets_m-10to50']:
        out_dict['nPartons']  =  tree.nPartons
    #     if 0 < tree.nPartons < 5:
    #         weight  = 0.0
    out_dict['eventWeight']  =  weight
    out_dict['met']          =  tree.met
    out_dict['metPhi']       =  tree.metPhi

    # 1. Filling leptons
    lep1 = tree.leptonOneP4
    lep2 = tree.leptonTwoP4
    dilepton = lep1+lep2

    out_dict['lepton1_pt']  = lep1.Pt()
    out_dict['lepton1_eta'] = lep1.Eta()
    out_dict['lepton1_phi'] = lep1.Phi()

    out_dict['lepton2_pt']  = lep2.Pt()
    out_dict['lepton2_eta'] = lep2.Eta()
    out_dict['lepton2_phi'] = lep2.Phi()

    out_dict['dilepton_mass'] = dilepton.M()

    lep3 = tree.leptonThreeP4
    out_dict['lepton3_iso']     = tree.leptonThreeISO

    out_dict['lepton3_deltaPhi']= lep3.DeltaPhi(dilepton) #tree.leptonThreeDeltaPhi
    out_dict['lepton3_pt']      = lep3.Pt()
    out_dict['lepton3_eta']     = lep3.Eta()
    out_dict['lepton3_phi']     = lep3.Phi()

    return out_dict

def fill_ntuple(tree, name, SF):
    n = int(tree.GetEntriesFast())

    for i in trange(n,
                    desc       = selection+' selection *** '+name,
                    leave      = True,
                    unit_scale = True,
                    ncols      = 100,
                    total      = n
                    ):
        tree.GetEntry(i)
        entry = {}
        entry.update(fill_lepton_vars(tree, name, SF))
        n -= 1
        yield entry

def pickle_ntuple(ntuple_data):
    # unpack input data
    name        = ntuple_data[0]
    SF          = ntuple_data[1]
    input_file  = ntuple_data[2]
    output_path = ntuple_data[3]
    if name in datalist:
        output_path +="data2016/"
    elif name in mcttbosonlist:
        output_path +="mcttboson/"
    elif name in mcqcdlist:
        output_path +="mcqcd/"
    elif name in mcdibosonlist:
        output_path +="mcdiboson/"
    elif name in mcdylist:
        output_path +="mcdy/"
    elif name in mctlist:
        output_path +="mct/"
    elif name in mcttlist:
        output_path +="mctt/"
    make_directory(output_path, clear=False)

    # get the tree, convert to dataframe, and save df to pickle
    #froot  = TFile(input_file)
    #tree   = froot.Get('tree_{}'.format(name))
    tree   = input_file.Get('tree_{}'.format(name))

    if tree.GetEntriesFast() >0:

        ntuple = fill_ntuple(tree, name, SF)
        df     = pd.DataFrame(ntuple)
        df.to_pickle('{0}/ntuple_{1}.pkl'.format(output_path, name))

###########################################
###########################################
#               Main
###########################################
###########################################

input_root_file_name  = "/home/zchen/Documents/Analysis/workplace/data/root/{}.root".format(selection)
input_root_file = TFile(input_root_file_name)
output_directory = "/home/zchen/Documents/Analysis/workplace/data/fake/{}/".format(selection)

## 1. define datalist
datalist      = ['muon_2016B', 'muon_2016C', 'muon_2016D','muon_2016E',
                 'muon_2016F','muon_2016G','muon_2016H']
## 2. define mclist
mcttbosonlist = ['TTZToLLNuNu','TTZToQQ','TTWJetsToLNu','TTWJetsToQQ','ttHJetTobb']

mcqcdlist     = ['qcd_ht100to200','qcd_ht200to300','qcd_ht300to500',
                 'qcd_ht500to700','qcd_ht700to1000','qcd_ht1000to1500',
                 'qcd_ht1000to1500','qcd_ht1500to2000','qcd_ht2000']
mcdibosonlist = ['ww','wz_2l2q','wz_3lnu','zz_2l2nu','zz_2l2q','zz_4l' ]
mcdylist      = ['zjets_m-10to50','zjets_m-50',
                 #'z1jets_m-10to50','z2jets_m-10to50','z3jets_m-10to50','z4jets_m-10to50',
                 #'z1jets_m-50','z2jets_m-50','z3jets_m-50','z4jets_m-50',
                 'w1jets','w2jets','w3jets','w4jets']
mctlist       = ['t_tw','tbar_tw']
mcttlist      = ['ttbar_inclusive']
mclist =  mcttbosonlist + mcdibosonlist + mcdylist + mctlist + mcttlist

## 3. Calculate SF for each element in datalist and mclist
SF_table = {}
SF_table = defaultdict(lambda: 1.0, SF_table)
for mc in mclist:
    h = input_root_file.Get("TotalEvents_"+mc)
    nGenTotal = h.GetBinContent(1)
    crossection = XS_table[mc]
    SF_table[mc] = crossection * 35.864 / nGenTotal
for data in datalist:
    SF_table[data] = 1.0

## 4. pickle ntuples
dataset_list = datalist + mclist
for dataset in dataset_list:
    SF = SF_table[dataset]
    root2df_config = [dataset,SF,input_root_file,output_directory]
    pickle_ntuple(root2df_config)


# coding: utf-8

import pygame
import random

SCREEN_WIDTH = 700
SCREEN_HEIGHT = 500
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PURPLE = (155, 89, 182)
YELLOW = (237, 194, 51)
PLAYER_SPEED = 7 # pixel per frame
ENEMY_SPEED = 5
WALL_WIDTH = 10
WALL_HEIGHT = 300

class Game:

    def __init__(self, screen):
        self.screen = screen
        self.started = False
        self.game_over = False
        coin_pickup = pygame.mixer.Sound("coin_pickup.wav")
        coin_pickup.set_volume(0.3)
        self.sounds = {
                "coin pickup":coin_pickup,
                "lose":pygame.mixer.Sound("lose.wav")
        }
        self.score = 0
        self.best = 0
        self.all_sprites = pygame.sprite.Group()
        self.walls = pygame.sprite.Group()
        self.enemies = pygame.sprite.Group()
        self.coins = pygame.sprite.Group()
        self.create_objects()
        self.enemy_respawn_event = pygame.USEREVENT+1
        pygame.time.set_timer(self.enemy_respawn_event, 1000)

    def create_objects(self):
        self.create_walls()
        self.player = Player(SCREEN_WIDTH / 2 - WALL_HEIGHT / 2  + 20, WALL_HEIGHT / 2 + SCREEN_HEIGHT / 2 - WALL_HEIGHT / 3.5 - 10)
        self.player.walls = self.walls
        self.all_sprites.add(self.player)
        self.create_coin()

    def create_walls(self):
        # left wall
        wall_1 = Wall(SCREEN_WIDTH / 2 - WALL_HEIGHT / 2, SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2, WALL_WIDTH, WALL_HEIGHT)
        self.walls.add(wall_1)
        self.all_sprites.add(wall_1)
        # bottom wall
        wall_2 = Wall(SCREEN_WIDTH / 2 - WALL_HEIGHT / 2, SCREEN_HEIGHT / 2 + WALL_HEIGHT / 2, WALL_HEIGHT, WALL_WIDTH)
        self.walls.add(wall_2)
        self.all_sprites.add(wall_2)
        # right wall
        wall_3 = Wall(SCREEN_WIDTH / 2 + WALL_HEIGHT / 2, SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2, WALL_WIDTH, WALL_HEIGHT + WALL_WIDTH)
        self.walls.add(wall_3)
        self.all_sprites.add(wall_3)
        # top wall
        wall_4 = Wall(SCREEN_WIDTH / 2 - WALL_HEIGHT / 2, SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2, WALL_HEIGHT, WALL_WIDTH)
        self.walls.add(wall_4)
        self.all_sprites.add(wall_4)

    def create_coin(self):
        x_pos = SCREEN_WIDTH / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2)* (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
        y_pos = SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2) * (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
        self.coin = Coin(x_pos, y_pos)
        self.all_sprites.add(self.coin)
        self.coins.add(self.coin)

    def create_enemy(self):
        _dir = random.choice(["U", "D", "L", "R"])
        x_speed, y_speed = 0, 0
        if _dir == "U":
                x_pos = SCREEN_WIDTH / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2)* (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
                y_pos = SCREEN_HEIGHT - 20
                y_speed = -ENEMY_SPEED
        elif _dir == "D":
                x_pos = SCREEN_WIDTH / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2)* (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
                y_pos = 20
                y_speed = ENEMY_SPEED
        elif _dir == "L":
                y_pos = SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2)* (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
                x_pos = SCREEN_WIDTH - 20
                x_speed = -ENEMY_SPEED
        elif _dir == "R":
                y_pos = SCREEN_HEIGHT / 2 - WALL_HEIGHT / 2 + WALL_WIDTH + random.randint(0, 2)* (WALL_HEIGHT / 3.5) + (WALL_HEIGHT / 7)
                x_pos = 20
                x_speed = ENEMY_SPEED
        enemy = Enemy(x_pos, y_pos, x_speed, y_speed)
        self.all_sprites.add(enemy)
        self.enemies.add(enemy)

    def run(self):
        clock = pygame.time.Clock()
        while not self.game_over:
                self.process_events()
                self.run_logic()
                self.display_frame()
                clock.tick(60)

    def process_events(self):
        for event in pygame.event.get():
            if self.started:
                if event.type == self.enemy_respawn_event:
                    self.create_enemy()

            if event.type == pygame.QUIT:
                self.game_over = True
            elif event.type == pygame.KEYDOWN and self.player.traveled_distance == 0:
                self.started = True
                if event.key == pygame.K_LEFT:
                    self.player.change_speed(-PLAYER_SPEED, 0)
                elif event.key == pygame.K_RIGHT:
                    self.player.change_speed(PLAYER_SPEED, 0)
                elif event.key == pygame.K_UP:
                    self.player.change_speed(0, -PLAYER_SPEED)
                elif event.key == pygame.K_DOWN:
                    self.player.change_speed(0, PLAYER_SPEED)

    def run_logic(self):
        if not self.game_over:
            self.all_sprites.update()
        player_coin_collide = pygame.sprite.spritecollide(self.player, self.coins, True)
        if player_coin_collide:
            self.score += 1
            self.sounds["coin pickup"].play()
            self.create_coin()
        player_enemy_collide = pygame.sprite.spritecollide(self.player, self.enemies, True)
        if player_enemy_collide:
            if self.score > self.best:
                    self.best = self.score
            self.score = 0
            self.sounds["lose"].play()

    def display_frame(self):
        screen.fill(PURPLE)
        if not self.game_over:
            font = pygame.font.SysFont("Arial", 20)
            score = font.render("score: " + str(self.score), True, WHITE)
            best = font.render("best: " + str(self.best), True, WHITE)
            self.screen.blit(score, (30, 30))
            self.screen.blit(best, (30, 50))
            if not self.started:
                i = "Use arrow keys to take the coin"
                instructions = font.render(i, True, WHITE)
                self.screen.blit(instructions, (200, 50))
            self.all_sprites.draw(self.screen)
        pygame.display.flip()

class Player(pygame.sprite.Sprite):

    def __init__(self, x, y):
        self.image = pygame.Surface([WALL_HEIGHT / 3.5, WALL_HEIGHT / 3.5])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.x_speed = 0
        self.y_speed = 0
        self.walls = None
        self.traveled_distance = 0
        super().__init__()

    def change_speed(self, x, y):
        self.x_speed += x
        self.y_speed += y

    def update(self):
        self.rect.x += self.x_speed
        self.traveled_distance += abs(self.x_speed)
        walls_hit = pygame.sprite.spritecollide(self, self.walls, False)
        for wall in walls_hit:
            if self.x_speed > 0:
                self.rect.right = wall.rect.left - (WALL_HEIGHT/3 - WALL_HEIGHT/3.5)
            else:
                self.rect.left = wall.rect.right + (WALL_HEIGHT/3 - WALL_HEIGHT/3.5)
            self.x_speed = 0
            self.traveled_distance = 0

        self.rect.y += self.y_speed
        self.traveled_distance += abs(self.y_speed)
        walls_hit = pygame.sprite.spritecollide(self, self.walls, False)
        for wall in walls_hit:
            if self.y_speed > 0:
                self.rect.bottom = wall.rect.top - (WALL_HEIGHT/3 - WALL_HEIGHT/3.5)
            else:
                self.rect.top = wall.rect.bottom + (WALL_HEIGHT/3 - WALL_HEIGHT/3.5)
            self.y_speed = 0
            self.traveled_distance = 0
        if self.traveled_distance > WALL_HEIGHT / 3.5:
            self.x_speed = 0
            self.y_speed = 0
            self.traveled_distance = 0

class Coin(pygame.sprite.Sprite):

    def __init__(self, x, y):
        self.image = pygame.Surface([30, 30])
        self.image.fill(PURPLE)
        pygame.draw.circle(self.image, YELLOW, (15, 15), 15)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        super().__init__()

class Enemy(pygame.sprite.Sprite):

    def __init__(self, x, y, x_speed, y_speed):
        self.image = pygame.Surface([20, 20])
        self.image.fill(BLACK)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.x_speed = x_speed
        self.y_speed = y_speed
        super().__init__()

    def update(self):
        self.rect.x += self.x_speed
        self.rect.y += self.y_speed

class Wall(pygame.sprite.Sprite):

    def __init__(self, x, y, width, height):
        self.image = pygame.Surface([width, height])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        super().__init__()

if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("PGSQR")
    game = Game(screen)
    game.run()
    pygame.quit()


#!/usr/bin/python

"""
Program: HeavyChainCDRH3Finder
File: HeavyChainCDRH3Finder.py
Version: 1.1
Date: 23/10/2020
Function: Locate CDRH3 loop within antibody heavy chains and output summary statistics about those Normal_chains
Author: James Sweet-Jones
Address: Institute of Structural and Molecular Biology, Division of Biosciences, University College London
#############################################################################
Description:
===========

Takes in a number of Antibody heavy chains amino acid sequences and output summary of CDRH3 loop

#############################################################################

Usage:
=======

HeavyChainCDRH3Finder.py [x]

where x is a .txt file containing sequences separated by whitespace

#############################################################################
Revision History:
================

v1.0 - took in inputs from command line
v1.1 - takes in inputs from file
"""
#############################################################################
#Import libraries

import sys
sys.argv

#############################################################################

def Normal_chain_identifier(x):
    """

    Input: x --- An antibody heavy chain amino acid sequence in one letter format
    Return: Print statement of statistics regarding CDRH3 loop of x

    23/10/2020 Original by JSJ
    """

    k = len(x)
    Number_of_cysteines = 0
    CAR = 0
    First_Cys_motif_start_position = 0
    Second_Cys_motif_start_position = 0
    WG = 0
    WG_position = 0
    CDRH3_loop = ""
    Max_CDRH3_insertions = 8

    #Define number and location of cysteine residues in sequence and if the distance between residues is within expected range
    for i in range(k):
        if x[i] == "C" and Number_of_cysteines == 0:
            Number_of_cysteines += 1
            First_Cys_motif_start_position = i+1
            continue
        elif x[i] == "C" and Number_of_cysteines == 1:
            Second_Cys_motif_start_position = i+1
            Number_of_cysteines += 1
        elif x[i] == "C" and Number_of_cysteines >1:
            Number_of_cysteines += 1
    Cys_distance = Second_Cys_motif_start_position - First_Cys_motif_start_position

    #Define number and location of tryptophan residues in sequence and locate the last tryptophan

    W_positions = []
    for i in range(Second_Cys_motif_start_position-1, k):
        if x[i] == "W":
            W_positions.append(i+1)

    #Define CDRH3 loop by printing all residues between second cysteine residue +2 and final tryptophan residue

    if len(W_positions) > 0:
        WG_position = W_positions[-1]
        for i in range(Second_Cys_motif_start_position+2, WG_position-1):
            CDRH3_loop = CDRH3_loop + x[i]

        #Calculates number of inserted bases by subtracting length of CDRH3 by length of CDRH3 without insertions (8)
    len_CDRH3 = len(CDRH3_loop)
    CDRH3_insertion_length = len(CDRH3_loop) - Max_CDRH3_insertions
    CDRH3_insertion = ""
    CDRH3_insertion_modulus = len(CDRH3_insertion)
    if CDRH3_insertion_length != 0:
        for i in range(WG_position-3-CDRH3_insertion_length, WG_position-3):
            CDRH3_insertion = CDRH3_insertion + x[i]

    #Print out results

    if Number_of_cysteines != 2:
        print(x)
        print("Your sequence is not typical, there are ", Number_of_cysteines ," cysteine residues where there should be 2")
        return False
    elif CDRH3_insertion_modulus > 9:
        print(x)
        print("Your sequence has a particularly long insertion in the CDRH3 loop, which may compromise its binding ability")
        return False
    elif len_CDRH3 == 0:
        print(x)
        print("Your sequence has no or an irregular CDRH3 loop, which may compromise its binding ability")
        return False
    elif Number_of_cysteines == 2 and CDRH3_insertion_length <= 0:
        print(x)
        print("Your sequence is ", k, "residues long with a CDRH3 loop", CDRH3_loop)
        return True
    elif len(W_positions) == 0:
        print(x)
        print("The CDRH3 loop could not be found on this sequence as the tryptophan closing the loop could not be found")
    elif Number_of_cysteines == 2 and 70 <= Cys_distance <= 80:
        print(x)
        print("Your sequence is ", k, "residues long with a CDRH3 loop", CDRH3_loop,". This loop has an insertion of", CDRH3_insertion_length, "residues, which are :", CDRH3_insertion)
        return True
    else:
        print(x)
        print("This is an irregular sequence")
        return False
#*********************************************************
#*** Main program  ***
#*********************************************************

#Run Normal_chain_identifier on input sequences and run tally on normal and irregular sequences
#Return results to display

Normal_chains = 0
Irregular_chains = 0
with open(sys.argv[1], 'r') as f:
    for line in f:
        if Normal_chain_identifier(line) == True:
            Normal_chains +=1
        else:
            Irregular_chains +=1

print("You have entered", Normal_chains, "normal chains and ", Irregular_chains ," irregular chains.")


from services.grid import Grid

class Minesweeper:
    """Game functionality for Minesweeper

    Grid is a list for a nxn matrix used in Minesweeper.
    After setting up the grid, the values of the list show which tiles
    have mines and how many mines surround each non-mine tile.

    Args:
        grid: Grid to be used in the game

    Attributes:
        grid: Grid-object with the mines and tiles with neighbor-values related to the mines
        showntiles: A set of all the opened tiles
        flags: A set of tiles flagged by the user

    Example:

    msw = Minesweeper(Grid(3))

    g = Grid(5)

    minesw = Minesweeper(g)
    """

    def __init__(self, grid: Grid):
        self.grid = grid
        self.showntiles = set()
        self.flags = set()

    def get_tile(self, index):
        """Returns the number in the grid corresponding the given index.

        Checks also if index is viable.

        Args:
            index: an index of the grid.list

        Returns:
            Integer with the number

        """
        if self.grid.check_index_viability(index) is False:
            return False
        return self.grid.list[index]

    def find_nearby_zeros(self, index, zeros: set):
        """Edits a given a set by adding nearby indices which have zeros
        and does the same for each zero.

        Finds zeros neighboring each other and collects a set of their indices using recursion.

        Args:
            index: an index of the grid.list
            zeros: set of indices for found nearby zeros

        Returns:
            The set with indices for the cluster of zeros found

        """
        neighbors = self.grid.neighbor_indices(index)
        for i in neighbors:
            if self.get_tile(i) == 0 and i not in zeros:
                zeros.add(i)
                self.find_nearby_zeros(i, zeros)
        return zeros

    def add_shown_tiles(self, index):
        """Adds index to shown tiles, if 0, adds surrounding tiles for each surrounding zero.

        If the tile has no surrounding mines (is a zero), then the neighbours will be added
        to showntiles. If zero is neighbor to other zeros, this will be done to them as well.
        If the tile was flagged, it will be unflagged and opened.

        Returns:
            An int containing the number of the index in the grid
            False if index couldn't be added

        """
        if self.grid.check_index_viability(index) is False:
            return False

        if index in self.flags:
            self.remove_flag(index)

        # is this useful?
        if index in self.showntiles:
            print("Already opened")
            return False

        number = self.grid.list[index]
        if number == 0 and index not in self.showntiles:
            neighbors = set()
            zeros = self.find_nearby_zeros(index, set())
            zeros |= {index}
            for i in zeros:
                neighbors = neighbors.union(self.grid.neighbor_indices(i))
                if i in self.flags:
                    self.flags.remove(i)
            self.showntiles = self.showntiles.union(zeros)
            self.showntiles = self.showntiles.union(neighbors)

        self.showntiles.add(index)
        return number

    def check_win(self):
        """Returns true if win condition is fulfilled.

        Checks win condition if amount of flags equals amount of mines in the game.
        If all the flags are set on the mines, game has been won.
        If all tiles are shown except the mines, the game is also won.

        Returns:
            Boolean, True on win and False otherwise.

        """
        if len(self.flags) == self.grid.mines:
            for i in self.flags:
                if self.grid.list[i] != 9:
                    return False
            return True

        if self.grid.len - self.grid.mines == len(self.showntiles):
            for i in self.showntiles:
                if self.grid.list[i] == 9:
                    return False
            return True
        return False

    def check_loss(self):
        """Returns true if a mine has been opened.

        Checks loss condition by checking if showntiles
        has any indices of the grid with a mine in them.

        Returns:
            Boolean, True on loss and False otherwise.

        """
        for i in self.showntiles:
            if self.grid.list[i] == 9:
                return True
        return False

    def set_flag(self, index):
        """Removes flag from given list index if the flag exists.

            If the index isn't suitable, nothing happens.
            Prints "lippuja liikaa! poista ensin lippu " if user has set too many
            flags and the flag can't be set.
            Max amount of flags is the total amount of mines.
            Prints "ei voitu asettaa lippua, ruutu oli avattu tai siinä oli jo lippu!"
            if tile in the index has been opened or already has a flag.

            Args:
                index: location of the flag

        """
        if self.grid.check_index_viability(index) is False:
            return
        flags_left = self.grid.mines - len(self.flags)
        if flags_left <= 0:
            print("lippuja liikaa! poista ensin lippu ")
            return
        if index in self.showntiles or index in self.flags:
            print("ei voitu asettaa lippua, ruutu oli avattu tai siinä oli jo lippu!")
            return
        self.flags.add(index)

    def remove_flag(self, index):
        """Removes flag from given list index if the flag exists.

            If the index isn't suitable, nothing happens.
            Prints "ei ollut poistettavaa lippua" if couldn't find flag from that index.

            Args:
                index: location of the flag

        """
        if self.grid.check_index_viability(index) is False:
            return
        if index not in self.flags:
            print("ei ollut poistettavaa lippua")
            return
        self.flags.remove(index)

    def get_shown_tile(self, index):
        """Returns the tile value to show to user.

            Args:
                index: tile to check

            Returns:
                "_": if tile isn't opened and doesn't have a flag
                "F": if tile isn't opened and has a flag
                Else a number indicating a mine (9) or the amount of nearby mines a tile has (0-8).

        """
        if index in self.showntiles:
            return self.get_tile(index)
        if index in self.flags:
            return "F"
        return "_"

    def current_state(self):
        """Returns a string showing the current game matrix, from the user's point of view.

            The string will show which tiles have been opened and the value in the
            shown tiles (0-8 for indicating amount of surrounding mines)
            and it shows the flags the user has set.

            Meanings of the symbols:
                0: empty, no neighboring mines
                1-8: indicates the number of mines in surrounding tiles (neighbors)
                9: a mine

            Returns:
                String presenting the grid's shown tiles to the user.

        """
        bgrid = self.grid
        printable = ""
        for i in range(bgrid.len):
            if i % bgrid.width == 0:
                printable += "\n"
            printable += f" {self.get_shown_tile(i)} "
        return printable