from glob import glob
from pathlib import Path
from torch.utils.data import Dataset
import cv2
import numpy as np
import random
import torch
import matplotlib.pyplot as plt
from torchvision import transforms

from config import crop_cfg
from config.crop_cfg import crop_resize_dict, crop_folder_list
from utils.chalearn import get_labels, train_list, test_list
from torch.utils.data.dataloader import default_collate

# import line_profiler
# import atexit
# profile = line_profiler.LineProfiler()
# atexit.register(profile.print_stats)

# cfg = get_override_cfg()

# The crops and corresponding pixels

class ChalearnVideoDataset(Dataset):

    crop_resize = crop_resize_dict  # {"CropFolderName": size}

    def __init__(self, cfg, name_of_set:str, sampling:str=None) -> None:
        """name_of_set: train test val"""
        self.name_of_set = name_of_set
        self.cfg = cfg
        self.num_data_modality_channels = 21

        # Load label list
        self.labels = get_labels(name_of_set)
        self.clip_len = cfg.CHALEARN.CLIP_LEN  # length of clip (frames)

        # Preprocess for both train and test
        self.preprocess = transforms.Compose([
            transforms.ToTensor(),  #  x/255, HWC -> CHW
            transforms.Normalize(
                mean=[0.45] * self.num_data_modality_channels,
                std=[0.225] * self.num_data_modality_channels),
        ])

        self.rgb_augment = transforms.Compose([
            transforms.ColorJitter(brightness=0.5, hue=0.1, contrast=0.3, saturation=0.2)
        ])

        if sampling is None:
            if self.name_of_set == "train":
                self.sampling = "random"
            elif self.name_of_set == "valid" or self.name_of_set == "test":
                self.sampling = "uniform"
        else:
            self.sampling = sampling

    def _pad_resize_img(self, img, new_size:int):  # Pad to square and resize
        assert len(img.shape) == 3  # (H,W,C)

        h, w, c = img.shape
        m = max(h, w)
        nx = (m-w) // 2  # The x coord in new image
        ny = (m-h) // 2  # The y coord in new image
        new_img = np.zeros(shape=(m, m, c), dtype=img.dtype)
        new_img[ny:ny+h, nx:nx+w, :] = img  # A square image with original content at center
        resize_img = cv2.resize(new_img, (new_size, new_size), interpolation=cv2.INTER_CUBIC)

        return resize_img

    def _augment(self, feature_dict) -> None:
        """
        Data augmentation for training
        """

        for (folder, size) in crop_resize_dict.items():
            if folder in feature_dict.keys():
                padding = size // 10

                position_augment = transforms.Compose([
                    transforms.RandomCrop(size, padding)
                ])
                feature_dict[folder] = position_augment(feature_dict[folder])

                # feature_dict[folder][:, 0:3,] = self.rgb_augment(feature_dict[folder][:, 0:3,])  # aug RGB channels

    def _get_image_features(self, nsetx3x5img:Path):
        """
        Get features (RGB UV ...) from image path
        nsetx3x5img: train/001/M_00068/00000.jpg
        """
        # size = 100  # pixels

        # res_dict = {key: None for key in crop_folder_list}
        res_dict = {self.cfg.MODEL.R3D_INPUT: None}  # Load less data to prevent memory fail
        for crop_folder_name in res_dict.keys():
            size = self.crop_resize[crop_folder_name]
            frame_path = Path(self.cfg.CHALEARN.ROOT, crop_folder_name, nsetx3x5img)
            if frame_path.exists():
                img = cv2.imread(str(frame_path))
                img_U = cv2.imread(str(Path(frame_path.parent, 'U_'+frame_path.name)), cv2.IMREAD_GRAYSCALE)[..., np.newaxis]
                img_V = cv2.imread(str(Path(frame_path.parent, 'V_'+frame_path.name)), cv2.IMREAD_GRAYSCALE)[..., np.newaxis]
                img_F0 = cv2.imread(str(Path(frame_path.parent, 'F0_'+frame_path.name)))
                img_F1 = cv2.imread(str(Path(frame_path.parent, 'F1_'+frame_path.name)))
                img_F2 = cv2.imread(str(Path(frame_path.parent, 'F2_'+frame_path.name)))
                img_F3 = cv2.imread(str(Path(frame_path.parent, 'F3_'+frame_path.name)))
                img_F4 = cv2.imread(str(Path(frame_path.parent, 'F4_'+frame_path.name)))
                img_D = cv2.imread(str(Path(frame_path.parent, 'D_'+frame_path.name)), cv2.IMREAD_GRAYSCALE)[..., np.newaxis]
                # img, img_U, img_V, img_F, img_D = [self._pad_resize_img(x, size) for x in (img, img_U, img_V, img_F0, img_D)]  # HWC
                img_cat = np.concatenate([img, img_U, img_V, img_F0, img_F1, img_F2, img_F3, img_F4, img_D], axis=-1)
                img_cat = self._pad_resize_img(img_cat, size)
            else:
                img_cat = np.zeros((size, size, self.num_data_modality_channels), dtype=np.uint8) + 127

            input_tensor = self.preprocess(img_cat)
            res_dict[crop_folder_name] = input_tensor

        return res_dict

    def random_sampling(self, seq_len, clip_len):
        possible_start_idx = seq_len - clip_len
        possible_start_idx = max(0, possible_start_idx)
        start_idx = random.randint(0, possible_start_idx)  # (randint: start/end both included)
        clip_indices = range(start_idx, start_idx + clip_len)
        clip_indices = [i % seq_len for i in clip_indices]  # If clip is larger than video length, then pick from start
        return clip_indices

    def uniform_sampling(self, seq_len, clip_len):
        clips = []
        if(seq_len <= clip_len):
            clips.append(self.random_sampling(seq_len, clip_len))
        else:
            t = 0
            for t in range(0, seq_len - clip_len, 4):
                clip_indices = range(t, t + clip_len)
                clips.append(clip_indices)
        return clips

    def collect_features_from_indices(self, clip_indices, img_names, img_folder, label):
        """Collect features from indices like [5, 10, 15, ...]"""
        selected_imgs = [img_names[i] for i in clip_indices]
        nsetx3x5img_list = [Path(img_folder, n) for n in selected_imgs]
        selected_features = [self._get_image_features(img) for img in nsetx3x5img_list]

        # Collect dicts
        collected_features = {}  # TCHW
        for key in selected_features[0].keys():
            features = [f[key] for f in selected_features]
            collected = torch.stack(features)  # Stack time dim
            collected_features[key] = collected
        collected_features['label'] = label - 1  # Chalearn label starts from 1 while torch requires 0
        if self.name_of_set == "train":
            self._augment(collected_features)
        return collected_features

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

    def __getitem__(self, index):
        label = self.labels[index]
        m, k, l = label
        nsetx3x5 = Path(m).parent / Path(m).stem  # train/001/M_00068/
        avi_path = Path(self.cfg.CHALEARN.ROOT, self.cfg.CHALEARN.IMG, nsetx3x5)  # root/Images/train/001/M_00068/
        img_files = glob(str(avi_path / "*"))  # Images from 1 folder
        img_files = sorted(img_files)
        img_names = [Path(p).name for p in img_files]  # 00000.jpg 00005.jpg ...

        # Random / Uniform sampling
        # Random sampling
        seq_len = len(img_names)
        if self.sampling == "random":
            # Random sampling, take 1
            clip_indices = self.random_sampling(seq_len, self.clip_len)
            collected_features = self.collect_features_from_indices(clip_indices, img_names, nsetx3x5, l)
        elif self.sampling == "uniform":
            # Uniform sampling, take multiple
            clip_indices_list = self.uniform_sampling(seq_len, self.clip_len)
            collected_features = [self.collect_features_from_indices(x, img_names, nsetx3x5, l) for x in clip_indices_list]
        else:
            raise NotImplementedError()

        return collected_features

def _test():
    dataset = ChalearnVideoDataset('train')
    counter = 0
    for batch in dataset:
        for i in range(0, 5):
            # cv2.imwrite(f'./debug/{counter}_{i}_L.jpg', batch['CropLHand'][i], )
            cv2.imwrite(f'./debug/{counter}_{i}_R.jpg', batch['CropRHand'][i], )
        counter = counter + 1
        # plt.imshow()
        # plt.show()
        # plt.imshow(batch['CropRHand'][4])
        # plt.show()
        # plt.cla()
        pass


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

import asyncore
import socket
import json
import sys
import time
sys.path.append('..')
sys.path.append('../../')

from proto_python import train_pb2
from common.manager import ConnectionManager
from common.manager import SampleOpManager
from common.core import Acceptor
from utils.remoteai_utils import Util
from utils import log4py

logger = log4py.Logger(name='RemoteAIServer')

tick_cnt = 0

class RemoteAIServer(object):
    def __init__(self, service, ip, port):
        self._service = service
        self._service.connection_manager = ConnectionManager.instance()
        self._client_stub_cls = train_pb2.TrainClient_Stub
        self._addr = (ip, port)
        self._acceptor = Acceptor(self._service, self._client_stub_cls)
        self._pvp_id = int(time.time())
        self._last_tick = 0
        logger.debug('RemoteAIServer created.')

    def start(self):
        '''启动服务端，并不断从MQ中读取startPVP请求，发送至训练客户端'''
        self._acceptor.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self._acceptor.socket.setsockopt(
            socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._acceptor.bind(self._addr)
        self._acceptor.listen(5)
        logger.info('RemoteAIServer started.')

        try:
            while True:
                second = (int)(time.time())
                if self._last_tick == 0:
                    self._last_tick = second
                    self.tick()
                elif second - self._last_tick >= 30:
                    self._last_tick = second
                    self.tick()
                Util.load_ops()
                if SampleOpManager.instance().qsize() > 0 and Util.train_client_cnt() > 0:
                    op = SampleOpManager.instance().get_op()
                    if op is not None:
                        if op.has_key('op') and op.has_key('AI') and op.has_key('BT') and op['op'] == 'start' and op['AI'] != '' and op['BT'] != '':
                            self.startPVP(op['AI'], op['BT'])
                asyncore.loop(1, True, None, 1)
        except KeyboardInterrupt:
            self.stop()

    def stop(self):
        self._acceptor.handle_close()
        logger.info('RemoteAIServer stopped.')

    # --------------------------------
    # 本地接口，服务端 -> 客户端请求，服务端主动
    # --------------------------------
    def startPVP(self, AI, BT, _addr=None):
        '''主动向训练客户端发送startPVP请求，开始一场PVP战斗'''
        data = {'AI': AI, 'BT': BT, "pvpid": self._pvp_id}
        self._pvp_id += 1
        # 不指定具体的客户端，则向所有客户端都发送
        if _addr is None or Util.has_train_client(_addr) == False:
            for stub in Util.all_train_clients():
                stub.startPVP(None, train_pb2.start_pvp_data(
                    data=json.dumps(data)))

            logger.info('RemoteAIServer sent start_pvp request to %d client(s) with data %s.' % (
                Util.train_client_cnt(), data))
        else:
            Util.train_client(_addr).startPVP(
                None, train_pb2.start_pvp_data(data=json.dumps(data)))
            logger.info('RemoteAIServer sent start_pvp request to %s.' % _addr)

    @staticmethod
    def onRequestAction(self, data, _addr=None):
        '''主动向训练客户端发送onRequestAction请求，指导AI进行一下步action'''
        # 不指定具体的客户端，则向所有客户端都发送
        if _addr is None or Util.has_train_client(_addr) == False:
            for stub in Util.all_train_clients():
                stub.onRequestAction(
                    None, train_pb2.default_data(data=json.dumps(data)))

            #logger.info('RemoteAIServer sent on_request_action request to %d client(s) with data %s.' % (Util.train_client_cnt(), data))
        else:
            Util.train_client(_addr).onRequestAction(
                None, train_pb2.default_data(data=json.dumps(data)))
            #logger.info('RemoteAIServer sent on_request_action request to %s.' % _addr)

    def tick(self, _addr=None):
        # 不指定具体的客户端，则向所有客户端都发送
        if _addr is None or Util.has_train_client(_addr) == False:
            for stub in Util.all_train_clients():
                stub.tick(None, train_pb2.train_none())

            logger.info('RemoteAIServer sent tick request to %d client(s).' % Util.train_client_cnt())
        else:
            Util.train_client(_addr).tick(None, train_pb2.train_none())
            logger.info('RemoteAIServer sent tick request to %s.' % _addr)


import pandas as pd
import cudf

class LuxGroupBy(cudf.core.groupby.GroupBy):

    _metadata = [
        "_intent",
        "_inferred_intent",
        "_data_type",
        "unique_values",
        "cardinality",
        "_rec_info",
        "_min_max",
        "_current_vis",
        "_widget",
        "_recommendation",
        "_prev",
        "_history",
        "_saved_export",
        "_sampled",
        "_toggle_pandas_display",
        "_message",
        "_pandas_only",
        "pre_aggregated",
        "_type_override",
    ]

    def __init__(self, *args, **kwargs):
        super(LuxGroupBy, self).__init__(*args, **kwargs)

    def aggregate(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).aggregate(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        return ret_val

    def _agg_general(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self)._agg_general(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        return ret_val

    def _cython_agg_general(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self)._cython_agg_general(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        return ret_val

    def get_group(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).get_group(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        ret_val.pre_aggregated = False  # Returned LuxDataFrame isn't pre_aggregated
        return ret_val

    def filter(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).filter(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        ret_val.pre_aggregated = False  # Returned LuxDataFrame isn't pre_aggregated
        return ret_val

    def apply(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).apply(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        ret_val.pre_aggregated = False  # Returned LuxDataFrame isn't pre_aggregated
        return ret_val

    def size(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).size(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        return ret_val

    def __getitem__(self, *args, **kwargs):
        ret_val = super(LuxGroupBy, self).__getitem__(*args, **kwargs)
        for attr in self._metadata:
            ret_val.__dict__[attr] = getattr(self, attr, None)
        return ret_val

    agg = aggregate

class LuxDataFrameGroupBy(LuxGroupBy, pd.core.groupby.generic.DataFrameGroupBy):
    def __init__(self, *args, **kwargs):
        super(LuxDataFrameGroupBy, self).__init__(*args, **kwargs)

class LuxSeriesGroupBy(LuxGroupBy, pd.core.groupby.generic.SeriesGroupBy):
    def __init__(self, *args, **kwargs):
        super(LuxSeriesGroupBy, self).__init__(*args, **kwargs)


import datetime
import json
import traceback
from flask import Flask, jsonify, request
from flask import Blueprint
from common.baseResponseSingle import BaseResponseSingle
from models.role import Role
from schema.roleSchema import RoleSchema
from services.roleService import RoleService
from common.baseResponse import BaseResponse
from common.errorResponse import ErrorResponse
from app import db

ts = datetime.datetime.utcnow()

role_api = Blueprint('role_api', __name__)

@role_api.route("/role", methods=["GET"])
def get_list():

    args = request.args
    search = args.get("search", "")
    page = args.get("page", 1)
    try:
        page = int(page)
    except ValueError:
        pass
    limit = args.get("limit", 10)
    try:
        limit = int(limit)
    except ValueError:
        pass
    sortBy = args.get("sortBy", "id,desc")

    try:
        role_schema = RoleSchema(many=True)
        data = RoleService.get_list(search, page, limit, sortBy)
        print(role_schema.dump(data))
        return jsonify(
            BaseResponse(
                role_schema.dump(data),
                "Role successfully Show",
                page,
                limit,
                len(role_schema.dump(data)),
                200,
            ).serialize()
        ), 200

    except Exception as e:
        traceback.print_exc()
        response = BaseResponse(None, str(e), 0, 0, 0, 400)
        return jsonify(response.serialize())

@role_api.route('/role', methods=["POST"])
def add_role():
    json = request.json

    role = json.get('role')

    try:
        add_role = Role(
            role = role,
            created_at=ts,
            updated_at=None,
            deleted_at=None
        )
        db.session.add(add_role)
        db.session.commit()

        return (
            jsonify(
                BaseResponseSingle(
                    role,
                    "Role successfully Add",
                    200,
                ).serialize()
            ),
            200
        )

    except Exception as e:
        traceback.print_exc()
        db.session.rollback()
        response = BaseResponse(None, str(e), 0, 0, 0, 400)
        return jsonify(response.serialize())

@role_api.route('/role/<string:id>', methods=["GET"])
def get_by_id(id):
    try:
        data = RoleService.get_by_id(id)
        if not data:
            return ErrorResponse(exception="Role is Not Found", code=400).serialize()
        role_schema = RoleSchema()
        return (
            jsonify(
                BaseResponseSingle(
                    role_schema.dump(data),
                    "Role successfully Showed",
                    200,
                ).serialize()
            ),
            200
        )
    except Exception as e:
        traceback.print_exc()
        response = BaseResponse(None, str(e), 0, 0, 0, 400)
        return jsonify(response.serialize())

@role_api.route('/role/<string:id>', methods=["PUT"])
def update_role(id):

    role = request.json.get('role')
    try:
        data = RoleService.get_by_id(id)
        if not data or data.deleted_at is not None:
            return ErrorResponse(exception="Role is Not Found", code=400).serialize()

        data.role = role
        data.updated_at = ts
        db.session.commit()

        role_schema = RoleSchema()

        return (
            jsonify(
                BaseResponseSingle(
                    role_schema.dump(data),
                    "Role successfully Updated",
                    200,
                ).serialize()
            ),
            200
        )
    except Exception as e:
        traceback.print_exc()
        db.session.rollback()
        response = BaseResponse(None, str(e), 0, 0, 0, 400)
        return jsonify(response.serialize())

@role_api.route('/role/delete/<string:id>', methods=["PUT"])
def delete_role(id):

    try:
        data = RoleService.get_by_id(id)
        if data.deleted_at is not None:
            return ErrorResponse(exception="Role is Not Found", code=400).serialize()

        data.deleted_at = ts
        db.session.commit()

        return (
            jsonify(
                BaseResponseSingle(
                    data.id,
                    "Role successfully Deleted",
                    200,
                ).serialize()
            ),
            200
        )
    except Exception as e:
        traceback.print_exc()
        response = BaseResponse(None, str(e), 0, 0, 0, 400)
        return jsonify(response.serialize())


import re

sea=re.compile('\d+')
num=sea.findall(input())
D=int(input())
load=int(max(num))
day=0
while D!=day:
    day=0
    ship=0
    for i in range(len(num)):
        ship+=int(num[i])
        if ship>load:
            ship=int(num[i])
            day+=1
        else:
            if ship==load:
                ship=0
                day+=1
    if ship!=0:
        day+=1
    if D<day:
        load+=1
        if load>2*int(max(num)):
            day=D
    else:
        day=D
print(load)

'''
import re

sea=re.compile('\d+')
num=sea.findall(input())
D=int(input())
if num[3]=='1':
    print(3)
else:
    if num[4]=='5':
        print(15)
    else:
        if num[4]=='1':
            print(num)
        else:
            print(num)
'''


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Aug  4 13:27:22 2019

@author: clara
class that represents a run with different parameters and an Run#run function that
carries out simulation runs.
"""
from Simulation import Simulation
from Plot import Plot
from Analyze import Analyze
import numpy as np
"""measure dying times, how to plot multihomers?"""
class Run:
    def __init__(self, runs, timesteps, pf_number, con_number, con_share,
                 rest_number):
        self.runs = runs
        self.timesteps = timesteps + 2
        self.pf_number = pf_number
        self.con_number = con_number
        self.con_share = con_share
        self.rest_number = rest_number
        """arrays to store distribution object in """
        self.trans_distrib = np.zeros([self.pf_number, self.runs,
                                       self.timesteps])
        self.rest_distrib = np.zeros([self.pf_number, self.runs,
                                      self.timesteps])
        self.mult_distrib = np.zeros([self.pf_number, self.runs,
                                      self.timesteps])
        self.mult_distrib_abs = np.zeros([self.runs, self.timesteps])
        """arrays to store averages in"""
        self.trans_av = np.zeros([self.pf_number, self.timesteps])
        self.rest_av = np.zeros([self.pf_number, self.timesteps])
        #absolut number of multihomers would be interesting
        #self.mult_av = np.zeros([self.pf_number, self.timesteps])
        #self.mult_av_abs = np.zeros([self.timesteps])
    def run(self):
        for i in range(self.runs):
            S = Simulation(self.timesteps, self.pf_number, self.con_number,
                           self.con_share, self.rest_number)
            S.iterate()
            an = Analyze(S)
            sorted_results = an.sort_pf_by_survival()
            for pf_n in range(self.pf_number):
                self.trans_distrib[pf_n,i] = sorted_results['trans'][pf_n]
                self.rest_distrib[pf_n,i] = sorted_results['rest'][pf_n]
                self.mult_distrib[pf_n,i] = sorted_results['mult'][pf_n]
        self.calc_av(self.trans_distrib, self.trans_av)
        self.calc_av(self.rest_distrib,self.rest_av)

    def calc_av(self,distrib,output_ar):
        """ output array: array of size [pf_n, timesteps] where average
        values per timestep are stored"""
        for pf_n in range(self.pf_number):
            output_ar[pf_n] = sum(distrib[pf_n][:])/self.runs

    def plot(self):
        p = Plot(self)
        p.stackplot(self.trans_av, 'Average transactions per platform')
        p.simple_hist_plot(self.rest_av, 'Average rest per platform')


# ЛЕКЦИИ + СЕМИНАР:
# Напишите программу, которая принимает на вход два числа и проверяет, является ли одно число квадратом другого.
n = int(input())
m = int(input())
if n == m ** 2 or m == n ** 2:
    print("Yes")
else:
    print("No")

# ДОМАШНЕЕ ЗАДАНИЕ № 1:
# Напишите программу, которая принимает на вход цифру, обозначающую день недели, и проверяет, является ли этот день выходным.
# *Пример:*
#- 6 -> да
#- 7 -> да
#- 1 -> нет
def InputNumbers(inputText):
    is_OK = False
    while not is_OK:
        try:
            number = int(input(f"{inputText}"))
            is_OK = True
        except ValueError:
            print("It's mot a number!")
    return number

def checkNumber(num):
    if 6 <= num <= 7:
        print("Выходной :)")
    elif 0 < num < 6:
        print("Рабочий день")
    else:
        print("Введите от 1 до 7")

num = InputNumbers("Введите номер дня недели от 1 до 7: ")
checkNumber(num)


# python3.7
# encoding: utf-8
"""
@author: Chenjin.Qian
@email:  chenjin.qian@xquant.com
@file:   operate.py
@time:   2020-06-30 16:29
"""

class DatabaseOperate(object):
    def __init__(self, current_conn):
        self.conn = current_conn
        self.cursor = self.conn.create_cursor()

    def check_connection(self):
        flag = self.conn.reconnect()
        return flag

    def get_all(self, sql, **kwargs):
        if not self.check_connection():
            return None
        self.cursor.execute(sql, kwargs)
        out = self.cursor.fetchall()
        return out

    def get_one(self, sql, **kwargs):
        if not self.check_connection():
            return None
        self.cursor.execute(sql, kwargs)
        out = self.cursor.fetchone()
        return out

    def one_column_list(self, sql, **kwargs):
        if not self.check_connection():
            return None
        self.cursor.execute(sql, kwargs)
        rows = self.cursor.fetchall()
        out = [i[0] for i in rows]
        return out

    def get_df(self, sql, index_name=None, **kwargs):
        import pandas as pd
        if not self.check_connection():
            return None
        self.cursor.execute(sql, kwargs)
        rows = self.cursor.fetchall()
        col_name = [i[0] for i in self.cursor.description]
        data = [list(i) for i in rows]
        df = pd.DataFrame(data, columns=col_name)
        table = df if not index_name else df.set_index(index_name)
        return table

    def insert_data(self, sql, data=[]):
        if not self.check_connection():
            return None
        self.cursor.prepare(sql)
        self.cursor.execute(None, data)
        self.conn.commit()

    def execute_sql(self, sql, **kwargs):
        if not self.check_connection():
            return None
        try:
            self.cursor.execute(sql, kwargs)
            self.conn.commit()
            flag = 1
        except Exception as e:
            flag = 0
        finally:
            return flag

    def close_cursor(self):
        self.cursor.close()


from headers import *
from buildings import *

# 200 hp

class Walls(Building):
    def __init__(self, x, y, gameboard, type, col):
        if type == 0:
            self._body = '|'
        else:
            self._body = '_'
        self.color = col
        Building.__init__(self, x, y, 200, 1, 1)
        gameboard.updateboard(x, y, self._body)
        gameboard.updatecolorboard(x,y,col)

    def remove(self, gameboard):
        x = self.getx()
        y = self.gety()
        # gameboard[x][y]=' '


# YOLOv5 ЁЯЪА by Ultralytics, AGPL-3.0 license
"""
Download utils
"""
import re
import logging
import shutil
import subprocess
import urllib
from pathlib import Path
from multiprocessing.pool import ThreadPool
import requests
import torch
from urllib import parse, request
from itertools import repeat
from utils import LOGGER, TQDM, checks, clean_url, emojis, is_online, url2file

GITHUB_ASSETS_REPO = 'ultralytics/assets'
GITHUB_ASSETS_NAMES = [f'yolov8{k}{suffix}.pt' for k in 'nsmlx' for suffix in ('', '-cls', '-seg', '-pose')] + \
                      [f'yolov5{k}{resolution}u.pt' for k in 'nsmlx' for resolution in ('', '6')] + \
                      [f'yolov3{k}u.pt' for k in ('', '-spp', '-tiny')] + \
                      [f'yolo_nas_{k}.pt' for k in 'sml'] + \
                      [f'sam_{k}.pt' for k in 'bl'] + \
                      [f'FastSAM-{k}.pt' for k in 'sx'] + \
                      [f'rtdetr-{k}.pt' for k in 'lx'] + \
                      ['mobile_sam.pt']
GITHUB_ASSETS_STEMS = [Path(k).stem for k in GITHUB_ASSETS_NAMES]

def is_url(url, check=True):
    # Check if string is URL and check if URL exists
    try:
        url = str(url)
        result = urllib.parse.urlparse(url)
        assert all([result.scheme, result.netloc])  # check if is url
        return (urllib.request.urlopen(url).getcode() == 200) if check else True  # check if exists online
    except (AssertionError, urllib.request.HTTPError):
        return False

def gsutil_getsize(url=''):
    # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du
    output = subprocess.check_output(['gsutil', 'du', url], shell=True, encoding='utf-8')
    if output:
        return int(output.split()[0])
    return 0

def url_getsize(url='https://ultralytics.com/images/bus.jpg'):
    # Return downloadable file size in bytes
    response = requests.head(url, allow_redirects=True)
    return int(response.headers.get('content-length', -1))

def curl_download(url, filename, *, silent: bool = False) -> bool:
    """
    Download a file from a url to a filename using curl.
    """
    silent_option = 'sS' if silent else ''  # silent
    proc = subprocess.run([
        'curl',
        '-#',
        f'-{silent_option}L',
        url,
        '--output',
        filename,
        '--retry',
        '9',
        '-C',
        '-', ])
    return proc.returncode == 0

# def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''):
#     # Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes
#     from utils.general import LOGGER

#     file = Path(file)
#     assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}"
#     try:  # url1
#         LOGGER.info(f'Downloading {url} to {file}...')
#         torch.hub.download_url_to_file(url, str(file), progress=LOGGER.level <= logging.INFO)
#         assert file.exists() and file.stat().st_size > min_bytes, assert_msg  # check
#     except Exception as e:  # url2
#         if file.exists():
#             file.unlink()  # remove partial downloads
#         LOGGER.info(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...')
#         # curl download, retry and resume on fail
#         curl_download(url2 or url, file)
#     finally:
#         if not file.exists() or file.stat().st_size < min_bytes:  # check
#             if file.exists():
#                 file.unlink()  # remove partial downloads
#             LOGGER.info(f'ERROR: {assert_msg}\n{error_msg}')
#         LOGGER.info('')
def unzip_file(file, path=None, exclude=('.DS_Store', '__MACOSX'), exist_ok=False, progress=True):
    """
    Unzips a *.zip file to the specified path, excluding files containing strings in the exclude list.

    If the zipfile does not contain a single top-level directory, the function will create a new
    directory with the same name as the zipfile (without the extension) to extract its contents.
    If a path is not provided, the function will use the parent directory of the zipfile as the default path.

    Args:
        file (str): The path to the zipfile to be extracted.
        path (str, optional): The path to extract the zipfile to. Defaults to None.
        exclude (tuple, optional): A tuple of filename strings to be excluded. Defaults to ('.DS_Store', '__MACOSX').
        exist_ok (bool, optional): Whether to overwrite existing contents if they exist. Defaults to False.
        progress (bool, optional): Whether to display a progress bar. Defaults to True.

    Raises:
        BadZipFile: If the provided file does not exist or is not a valid zipfile.

    Returns:
        (Path): The path to the directory where the zipfile was extracted.

    Example:
        ```python
        from ultralytics.utils.downloads import unzip_file

        dir = unzip_file('path/to/file.zip')
        ```
    """
    from zipfile import BadZipFile, ZipFile, is_zipfile

    if not (Path(file).exists() and is_zipfile(file)):
        raise BadZipFile(f"File '{file}' does not exist or is a bad zip file.")
    if path is None:
        path = Path(file).parent  # default path

    # Unzip the file contents
    with ZipFile(file) as zipObj:
        files = [f for f in zipObj.namelist() if all(x not in f for x in exclude)]
        top_level_dirs = {Path(f).parts[0] for f in files}

        if len(top_level_dirs) > 1 or not files[0].endswith('/'):  # zip has multiple files at top level
            path = extract_path = Path(path) / Path(file).stem  # i.e. ../datasets/coco8
        else:  # zip has 1 top-level directory
            extract_path = path  # i.e. ../datasets
            path = Path(path) / list(top_level_dirs)[0]  # i.e. ../datasets/coco8

        # Check if destination directory already exists and contains files
        if path.exists() and any(path.iterdir()) and not exist_ok:
            # If it exists and is not empty, return the path without unzipping
            LOGGER.warning(f'WARNING тЪая╕П Skipping {file} unzip as destination directory {path} is not empty.')
            return path

        for f in TQDM(files, desc=f'Unzipping {file} to {Path(path).resolve()}...', unit='file', disable=not progress):
            zipObj.extract(f, path=extract_path)

    return path  # return unzip dir

def get_google_drive_file_info(link):
    """
    Retrieves the direct download link and filename for a shareable Google Drive file link.

    Args:
        link (str): The shareable link of the Google Drive file.

    Returns:
        (str): Direct download URL for the Google Drive file.
        (str): Original filename of the Google Drive file. If filename extraction fails, returns None.

    Example:
        ```python
        from ultralytics.utils.downloads import get_google_drive_file_info

        link = "https://drive.google.com/file/d/1cqT-cJgANNrhIHCrEufUYhQ4RqiWG_lJ/view?usp=drive_link"
        url, filename = get_google_drive_file_info(link)
        ```
    """
    file_id = link.split('/d/')[1].split('/view')[0]
    drive_url = f'https://drive.google.com/uc?export=download&id={file_id}'
    filename = None

    # Start session
    with requests.Session() as session:
        response = session.get(drive_url, stream=True)
        if 'quota exceeded' in str(response.content.lower()):
            raise ConnectionError(
                emojis(f'тЭМ  Google Drive file download quota exceeded. '
                       f'Please try again later or download this file manually at {link}.'))
        for k, v in response.cookies.items():
            if k.startswith('download_warning'):
                drive_url += f'&confirm={v}'  # v is token
        cd = response.headers.get('content-disposition')
        if cd:
            filename = re.findall('filename="(.+)"', cd)[0]
    return drive_url, filename

def check_disk_space(url='https://ultralytics.com/assets/coco128.zip', sf=1.5, hard=True):
    """
    Check if there is sufficient disk space to download and store a file.

    Args:
        url (str, optional): The URL to the file. Defaults to 'https://ultralytics.com/assets/coco128.zip'.
        sf (float, optional): Safety factor, the multiplier for the required free space. Defaults to 2.0.
        hard (bool, optional): Whether to throw an error or not on insufficient disk space. Defaults to True.

    Returns:
        (bool): True if there is sufficient disk space, False otherwise.
    """
    try:
        r = requests.head(url)  # response
        assert r.status_code < 400, f'URL error for {url}: {r.status_code} {r.reason}'  # check response
    except Exception:
        return True  # requests issue, default to True

    # Check file size
    gib = 1 << 30  # bytes per GiB
    data = int(r.headers.get('Content-Length', 0)) / gib  # file size (GB)
    total, used, free = (x / gib for x in shutil.disk_usage('/'))  # bytes
    if data * sf < free:
        return True  # sufficient space

    # Insufficient space
    text = (f'WARNING тЪая╕П Insufficient free disk space {free:.1f} GB < {data * sf:.3f} GB required, '
            f'Please free {data * sf - free:.1f} GB additional disk space and try again.')
    if hard:
        raise MemoryError(text)
    LOGGER.warning(text)
    return False

def safe_download(url,
                  file=None,
                  dir=None,
                  unzip=True,
                  delete=False,
                  curl=False,
                  retry=3,
                  min_bytes=1E0,
                  progress=True):
    """
    Downloads files from a URL, with options for retrying, unzipping, and deleting the downloaded file.

    Args:
        url (str): The URL of the file to be downloaded.
        file (str, optional): The filename of the downloaded file.
            If not provided, the file will be saved with the same name as the URL.
        dir (str, optional): The directory to save the downloaded file.
            If not provided, the file will be saved in the current working directory.
        unzip (bool, optional): Whether to unzip the downloaded file. Default: True.
        delete (bool, optional): Whether to delete the downloaded file after unzipping. Default: False.
        curl (bool, optional): Whether to use curl command line tool for downloading. Default: False.
        retry (int, optional): The number of times to retry the download in case of failure. Default: 3.
        min_bytes (float, optional): The minimum number of bytes that the downloaded file should have, to be considered
            a successful download. Default: 1E0.
        progress (bool, optional): Whether to display a progress bar during the download. Default: True.
    """

    # Check if the URL is a Google Drive link
    gdrive = url.startswith('https://drive.google.com/')
    if gdrive:
        url, file = get_google_drive_file_info(url)

    f = dir / (file if gdrive else url2file(url)) if dir else Path(file)  # URL converted to filename
    if '://' not in str(url) and Path(url).is_file():  # URL exists ('://' check required in Windows Python<3.10)
        f = Path(url)  # filename
    elif not f.is_file():  # URL and file do not exist
        assert dir or file, 'dir or file required for download'
        desc = f"Downloading {url if gdrive else clean_url(url)} to '{f}'"
        LOGGER.info(f'{desc}...')
        f.parent.mkdir(parents=True, exist_ok=True)  # make directory if missing
        check_disk_space(url)
        for i in range(retry + 1):
            try:
                if curl or i > 0:  # curl download with retry, continue
                    s = 'sS' * (not progress)  # silent
                    r = subprocess.run(['curl', '-#', f'-{s}L', url, '-o', f, '--retry', '3', '-C', '-']).returncode
                    assert r == 0, f'Curl return value {r}'
                else:  # urllib download
                    method = 'torch'
                    if method == 'torch':
                        torch.hub.download_url_to_file(url, f, progress=progress)
                    else:
                        with request.urlopen(url) as response, TQDM(total=int(response.getheader('Content-Length', 0)),
                                                                    desc=desc,
                                                                    disable=not progress,
                                                                    unit='B',
                                                                    unit_scale=True,
                                                                    unit_divisor=1024) as pbar:
                            with open(f, 'wb') as f_opened:
                                for data in response:
                                    f_opened.write(data)
                                    pbar.update(len(data))

                if f.exists():
                    if f.stat().st_size > min_bytes:
                        break  # success
                    f.unlink()  # remove partial downloads
            except Exception as e:
                if i == 0 and not is_online():
                    raise ConnectionError(emojis(f'тЭМ  Download failure for {url}. Environment is not online.')) from e
                elif i >= retry:
                    raise ConnectionError(emojis(f'тЭМ  Download failure for {url}. Retry limit reached.')) from e
                LOGGER.warning(f'тЪая╕П Download failure, retrying {i + 1}/{retry} {url}...')

    if unzip and f.exists() and f.suffix in ('', '.zip', '.tar', '.gz'):
        from zipfile import is_zipfile

        unzip_dir = dir or f.parent  # unzip to dir if provided else unzip in place
        if is_zipfile(f):
            unzip_dir = unzip_file(file=f, path=unzip_dir, progress=progress)  # unzip
        elif f.suffix in ('.tar', '.gz'):
            LOGGER.info(f'Unzipping {f} to {unzip_dir.resolve()}...')
            subprocess.run(['tar', 'xf' if f.suffix == '.tar' else 'xfz', f, '--directory', unzip_dir], check=True)
        if delete:
            f.unlink()  # remove zip
        return unzip_dir

def get_github_assets(repo='ultralytics/assets', version='latest', retry=False):
    """Return GitHub repo tag and assets (i.e. ['yolov8n.pt', 'yolov8s.pt', ...])."""
    if version != 'latest':
        version = f'tags/{version}'  # i.e. tags/v6.2
    url = f'https://api.github.com/repos/{repo}/releases/{version}'
    r = requests.get(url)  # github api
    if r.status_code != 200 and r.reason != 'rate limit exceeded' and retry:  # failed and not 403 rate limit exceeded
        r = requests.get(url)  # try again
    if r.status_code != 200:
        LOGGER.warning(f'тЪая╕П GitHub assets check failure for {url}: {r.status_code} {r.reason}')
        return '', []
    data = r.json()
    return data['tag_name'], [x['name'] for x in data['assets']]  # tag, assets

def attempt_download_asset(file, repo='ultralytics/assets', release='v0.0.0'):
    """
    Attempt file download from GitHub release assets if not found locally.

    release = 'latest', 'v6.2', etc.
    """
    from ultralytics.utils import SETTINGS  # scoped for circular import

    # YOLOv3/5u updates
    file = str(file)
    file = checks.check_yolov5u_filename(file)
    file = Path(file.strip().replace("'", ''))
    if file.exists():
        return str(file)
    elif (SETTINGS['weights_dir'] / file).exists():
        return str(SETTINGS['weights_dir'] / file)
    else:
        # URL specified
        name = Path(parse.unquote(str(file))).name  # decode '%2F' to '/' etc.
        if str(file).startswith(('http:/', 'https:/')):  # download
            url = str(file).replace(':/', '://')  # Pathlib turns :// -> :/
            file = url2file(name)  # parse authentication https://url.com/file.txt?auth...
            if Path(file).is_file():
                LOGGER.info(f'Found {clean_url(url)} locally at {file}')  # file already exists
            else:
                safe_download(url=url, file=file, min_bytes=1E5)

        elif repo == GITHUB_ASSETS_REPO and name in GITHUB_ASSETS_NAMES:
            safe_download(url=f'https://github.com/{repo}/releases/download/{release}/{name}', file=file, min_bytes=1E5)

        else:
            tag, assets = get_github_assets(repo, release)
            if not assets:
                tag, assets = get_github_assets(repo)  # latest release
            if name in assets:
                safe_download(url=f'https://github.com/{repo}/releases/download/{tag}/{name}', file=file, min_bytes=1E5)

        return str(file)

def download(url, dir=Path.cwd(), unzip=True, delete=False, curl=False, threads=1, retry=3):
    """Downloads and unzips files concurrently if threads > 1, else sequentially."""
    dir = Path(dir)
    dir.mkdir(parents=True, exist_ok=True)  # make directory
    if threads > 1:
        with ThreadPool(threads) as pool:
            pool.map(
                lambda x: safe_download(
                    url=x[0], dir=x[1], unzip=unzip, delete=delete, curl=curl, retry=retry, progress=threads <= 1),
                zip(url, repeat(dir)))
            pool.close()
            pool.join()
    else:
        for u in [url] if isinstance(url, (str, Path)) else url:
            safe_download(url=u, dir=dir, unzip=unzip, delete=delete, curl=curl, retry=retry)

# def attempt_download(file, repo='ultralytics/yolov5', release='v.0.0.0'):
#     # Attempt file download from GitHub release assets if not found locally. release = 'latest', 'v7.0', etc.
#     from utils.general import LOGGER

#     def github_assets(repository, version='latest'):
#         # Return GitHub repo tag (i.e. 'v7.0') and assets (i.e. ['yolov5s.pt', 'yolov5m.pt', ...])
#         if version != 'latest':
#             version = f'tags/{version}'  # i.e. tags/v7.0
#         # https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8s.pt
#         response = requests.get(f'https://api.github.com/repos/{repository}/releases/{version}').json()  # github api
#         return response['tag_name'], [x['name'] for x in response['assets']]  # tag, assets

#     file = Path(str(file).strip().replace("'", ''))
#     if not file.exists():
#         # URL specified
#         name = Path(urllib.parse.unquote(str(file))).name  # decode '%2F' to '/' etc.
#         if str(file).startswith(('http:/', 'https:/')):  # download
#             url = str(file).replace(':/', '://')  # Pathlib turns :// -> :/
#             print(url)
#             file = name.split('?')[0]  # parse authentication https://url.com/file.txt?auth...
#             # if Path(file).is_file():
#             #     LOGGER.info(f'Found {url} locally at {file}')  # file already exists
#             # else:
#             #     safe_download(file=file, url=url, min_bytes=1E5)
#             # return file

#         # GitHub assets
#         assets = [f'yolov8{size}{suffix}.pt' for size in 'nsmlx' for suffix in ('', '6', '-cls', '-seg')]  # default
#         try:
#             tag, assets = github_assets(repo, release)
#         except Exception:
#             try:
#                 tag, assets = github_assets(repo)  # latest release
#             except Exception:
#                 try:
#                     tag = subprocess.check_output('git tag', shell=True, stderr=subprocess.STDOUT).decode().split()[-1]
#                 except Exception:
#                     tag = release

#         if name in assets:
#             file.parent.mkdir(parents=True, exist_ok=True)  # make parent dir (if required)
#             safe_download(file,
#                           url=f'https://github.com/{repo}/releases/download/{tag}/{name}',
#                           min_bytes=1E5,
#                           error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/{tag}')

#     return str(file)


# add new item in a dict using update method
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
d.update({"id": 400}) # it will add a new item in a dict "d" if given key doesn't exist
print(d)

# access item in a dict "d" using get method
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
d.get("name") # danish
d.get("xyz")  # None -> since the key 'xyz' doen't exist, get method will return None
d.get("xyz", "optional value")  # the value specified in 2nd parameter will return if key 'xyz' doesn't exist

# update item in a dict using update method
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
d.update({"name": "fahad"})  # it will change the value if the key "name" exists
print(d)

# delete an item from a dict
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}

del d['name']
print(d)

# delete all items from a dict
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
# d.clear()
d = {}
print(d)

# display all the keys of the dict
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
d.keys() # name, gender, date_of_birth

# display all the value of the dict
d = {
    "name": "danish",
    "gender": "male",
    "date_of_birth": "2023"
}
d.keys()  # danish, male, 2023

# how to merge 2 dicts
d1 = {"a": 1, "b": 2, "c": 3} # req
d2 = {"d": 4, "e": 5, "f": 6} # fetched from db

# d3 = d1 | d2 # this syntax is not working on python 3.6
d3 = {**d1, **d2}
print(d3)


#scrip que calcula o valor de delta para saber se uma equação de segundo grau possui 1, 2 ou nenhuma raiz.
import math

def main():
    a= int(input("Digite o valor de a: "))
    b= int(input("Digite o valor de b: "))
    c= int(input("Digite o valor de c: "))
    imprime_valores(a, b, c)

def delta(a, b, c):
     return (b**2) - 4 * a * c

def imprime_valores(a, b, c):
    delta_local = delta(a, b, c)
    if delta_local > 0:
        raiz1 = (-b+(math.sqrt(delta_local)))/2*a
        raiz2 = (-b-(math.sqrt(delta_local)))/2*a
        if raiz1 < raiz2:
            print(f"as raízes da equação são {raiz1} e {raiz2}")
        else:
            print(f"as raízes da equação são {raiz2} e {raiz1}")
    elif delta_local < 0:
        print("esta equação não possui raízes reais")
    else:
        raiz3 = (-b+(math.sqrt(delta_local)))/2*a
        print(f"a raiz desta equação é {raiz3}")


from collections import defaultdict
import logging

import networkx as nx
from networkx.classes.function import add_path
from python_cypher import python_cypher
from xpark import settings
from xpark.plan.base import BaseOptimizedPlan
from xpark.utils.iter import take_pairs

_LOG = logging.getLogger(__name__)
PARSER = python_cypher.CypherToNetworkx()

class OptimizationRule(object):
    rule_str = None

    def __init__(self):
        self.rule = PARSER.parse(' '.join(self.rule_str.split()))

    def __repr__(self):
        return '<rule: %s\n%s>' % (self.__class__.__name__, self.rule_str.strip())

    def transform_path(self, path, g):
        return path, {}

    def replace_path(self, g, path, new_path):
        g = g.copy()
        paths = list(take_pairs(path))
        g.remove_edges_from(paths)
        add_path(g, new_path)
        g.remove_nodes_from(list(nx.isolates(g)))
        return g

    def apply(self, g):
        paths = list(PARSER.yield_return_values(g, self.rule))
        has_changed = False
        all_path_stats = []
        if paths:
            _LOG.info('[%s] paths matched rule:%s', len(paths), self)
            for path in paths:
                new_path, stats = self.transform_path(path, g)
                if path != new_path:
                    has_changed = True
                    g = self.replace_path(g, path, new_path)
                    all_path_stats.append(stats)
        return g, has_changed, all_path_stats

    @staticmethod
    def apply_all(g):
        all_rule_stats = defaultdict(list)
        for rule_class in rule_classes:
            i = 0
            while True:
                _LOG.info('Checking rule:[%s] iteration:[%s]', rule_class, i)
                g, has_changed, all_path_stats = rule_class().apply(g)
                if has_changed:
                    all_rule_stats[rule_class].append(all_path_stats)
                else:
                    break
                i += 1
        return g, all_rule_stats

class PruneSerialization(OptimizationRule):
    rule_str = '''
    MATCH (start: {is_pure_compute: "True"})-->(n1:Serializechunkop)-->(n2:Deserializechunkop)-->(end: {is_pure_compute: "True"})
    RETURN start, n1, n2, end
    '''

    def transform_path(self, path, g):
        start, n1, n2, end = path
        new_path = [start, end]
        stats = {'old_path': path, 'new_path': new_path}
        return new_path, stats

class PushDownSelect(OptimizationRule):
    rule_str = '''
    MATCH (first)-->(read:Readdatasetop)-[*]->(select:Selectchunkop)-->(last)
    RETURN first, read, nodes, select, last
    '''

    def transform_path(self, path, g):
        from xpark.plan.dataframe.physical import ReadDatasetOp

        first_op = path[0]
        read_op = path[1]
        select_op = path[-2]
        last_op = path[-1]
        new_schema = {}
        new_read_op = ReadDatasetOp(dataset=read_op.dataset, schema=new_schema,
                                    plan=read_op.plan, part_id=read_op.part_id)
        for col in select_op.ac_kwargs['cols']:
            new_schema[col] = read_op.schema[col]
        new_path = [first_op, new_read_op] + path[2:-2] + [last_op]
        stats = {'old_schema': read_op.schema, 'new_schema': new_schema, 'dataset': read_op.dataset}
        return new_path, stats

class PushDownImplicitSelect(OptimizationRule):
    rule_str = '''
    MATCH (read:Readdatasetop)-[*]->(last)
    RETURN read, nodes, last
    '''
    # '''
    # MATCH p=(read:Readdatasetop)-[*]->(last)
    # WHERE any(node in nodes(p) WHERE labels(node) IN ["Filterchunkop"])
    # RETURN nodes(p)
    # '''

    def transform_path(self, path, g):
        return path, {}

class PushDownCount(OptimizationRule):
    rule_str = '''
    MATCH (start:Physicalstartop)-->(read:Readdatasetop)-->(count:Countchunkop)-->(sum:Sumop)
    RETURN start, read, count, sum
    '''
    # '''
    # MATCH p=(read:Readdatasetop)-[*]->(count:Countchunkop)-->(sum:Sumop)
    # WHERE none(node in nodes(p) WHERE labels(node) IN ["Filterchunkop"])
    # RETURN start, read, count, sum
    # '''

    def transform_path(self, path, g):
        from xpark.dataset.files import FileList
        from xpark.plan.dataframe.physical import ReadDatasetCountOp

        start_op = path[0]
        read_op = path[1]
        sum_op = path[-1]
        if read_op.dataset.file_list.file_type == FileList.FILE_TYPE_PARQUET:
            new_read_op = ReadDatasetCountOp(dataset=read_op.dataset, schema=read_op.schema,
                                             plan=read_op.plan, part_id=read_op.part_id)
            new_path = [start_op, new_read_op, sum_op]
            stats = {'dataset': read_op.dataset}
            return new_path, stats
        else:
            return path, {}

class PruneChunks(OptimizationRule):
    rule_str = '''
    MATCH (first)-->(read:Readdatasetop)-[*]->(last: {is_terminal: "True"})
    RETURN first, read, nodes, last
    '''
    # '''
    # MATCH p=(read:Readdatasetop)-[*]->(filter:Filterchunkop)-[*]->(last)
    # RETURN nodes(p)
    # '''

    def transform_path(self, path, g):
        from xpark.dataset import FileDataset
        from xpark.dataset.files import FileList
        from xpark.dataset.utils import pq_prune_chunks_min_max
        from xpark.plan.dataframe.physical import FilterChunkOp

        first_op = path[0]
        read_op = path[1]
        dataset = read_op.dataset
        filter_ops = [op for op in path if isinstance(op, FilterChunkOp)]
        combined_filter = None
        if filter_ops and isinstance(dataset, FileDataset) and dataset.file_list.file_type == FileList.FILE_TYPE_PARQUET:
            for op in filter_ops:
                op_filter = op.ac_kwargs['expr']
                if combined_filter is None:
                    combined_filter = op_filter
                else:
                    combined_filter = combined_filter & op_filter

            to_keep = set(pq_prune_chunks_min_max(read_op.dataset, combined_filter))
            if read_op.part_id not in to_keep:
                new_path = [first_op]
                stats = {'skip': read_op.part_id}
                return new_path, stats
        return path, {}

class UseIndexForFilter(OptimizationRule):
    rule_str = '''
    MATCH (first)-->(read:Readdatasetop)-[*]->(last: {is_terminal: "True"})
    RETURN first, read, nodes, last
    '''
    # '''
    # MATCH p=(read:Readdatasetop)-[*]->(filter:Filterchunkop)-[*]->(last)
    # RETURN nodes(p)
    # '''

    def transform_path(self, path, g):
        from xpark.dataset.tables import Table
        from xpark.plan.dataframe.physical import PostIndexFilterChunkOp, ReadIndexFilterChunkOp, FilterChunkOp

        first_op = path[0]
        read_op = path[1]
        table = read_op.dataset
        filter_ops = [(i, op) for i, op in enumerate(path) if isinstance(op, FilterChunkOp)]
        if filter_ops and isinstance(table, Table):
            op_idx, filter_op = filter_ops[0]
            expr = filter_op.ac_kwargs['expr']
            indexed_cols, index_expr, extra_cols, extra_expr = table.extract_cols_from_expr(expr, filter_op.schema)
            if indexed_cols:
                new_path = [first_op]
                if extra_cols:
                    new_path.append(read_op)
                new_path.extend(path[2:op_idx])
                new_index_filter_op = ReadIndexFilterChunkOp(
                    plan=filter_op.plan,
                    schema=filter_op.schema,
                    part_id=filter_op.part_id,
                    table=table,
                    expr=index_expr,
                    augment_cols=indexed_cols + extra_cols
                )
                new_path.append(new_index_filter_op)
                if extra_expr:
                    new_filter_op = PostIndexFilterChunkOp(
                        plan=filter_op.plan, schema=filter_op.schema,
                        part_id=filter_op.part_id, expr=extra_expr
                    )
                    new_path.append(new_filter_op)
                new_path.extend(path[op_idx + 1:])
                stats = {
                    'indexed_cols': indexed_cols,
                    'index_expr': index_expr,
                    'extra_cols': extra_cols,
                    'extra_expr': extra_expr
                }
                return new_path, stats
        return path, {}

rule_classes = []
if settings.ENABLE_OPTIMIZER:
    rule_classes.extend([
        PruneSerialization,
        PushDownImplicitSelect,
        PushDownSelect,
        PruneChunks,
        UseIndexForFilter,
        PushDownCount,
    ])

class OptimizedPlan(BaseOptimizedPlan):
    stats = None


from mmseg.datasets import BaseSegDataset
from mmseg.registry import DATASETS

@DATASETS.register_module()
class Conic2022SegDataset(BaseSegDataset):
    """Conic2022SegDataset dataset.

    In segmentation map annotation for Conic2022SegDataset,
    ``reduce_zero_label`` is fixed to False. The ``img_suffix``
    is fixed to '.png' and ``seg_map_suffix`` is fixed to '.png'.

    Args:
        img_suffix (str): Suffix of images. Default: '.png'
        seg_map_suffix (str): Suffix of segmentation maps. Default: '.png'
    """
    METAINFO = dict(
        classes=('background', 'neutrophil', 'epithelial', 'lymphocyte',
                 'plasma', 'eosinophil', 'connective'))

    def __init__(self,
                 img_suffix='.png',
                 seg_map_suffix='.png',
                 **kwargs) -> None:
        super().__init__(
            img_suffix=img_suffix,
            seg_map_suffix=seg_map_suffix,
            reduce_zero_label=False,
            **kwargs)


from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def connect_db(app):
    """Connect to database."""
    db.app = app
    db.init_app(app)

class Pet(db.Model):
    __tablename__ = "pets"

    # def __repr__(self):
    #     p = self
    #     return f" Hello my name is {p.first_name} {p.last_name}, here is my user picture {p.image_url}"

    # def get_first_name(cls, first_name):
    #  return cls.query.filter(first_name)

    id = db.Column(db.Integer,
                   primary_key=True,
                   autoincrement=True)
    name = db.Column(db.String(50),
                     nullable=False,
                     unique=True)
    species = db.Column(db.String)
    photo_url = db.Column(db.String, nullable=True)
    age = db.Column(db.Integer, nullable = True)
    notes = db.Column(db.String, nullable=True)
    available = db.Column(db.Boolean, nullable = False)


from datetime import datetime, timedelta
import time
import os
from logging import getLogger
from misskaty.helper.http import http
from pyrogram import enums, filters
from pyrogram.types import ChatMemberUpdated, InlineKeyboardButton, InlineKeyboardMarkup
from pyrogram.errors import (
    ChatSendMediaForbidden,
    MessageTooLong,
    RPCError,
    SlowmodeWait,
)
from misskaty import app, BOT_USERNAME
from misskaty.core.decorator.errors import capture_err, asyncify
from PIL import Image, ImageChops, ImageDraw, ImageFont
import textwrap
from database.users_chats_db import db
from utils import temp
from pyrogram.errors import ChatAdminRequired
from misskaty.vars import SUDO, LOG_CHANNEL, SUPPORT_CHAT, COMMAND_HANDLER

LOGGER = getLogger(__name__)

def circle(pfp, size=(215, 215)):
    pfp = pfp.resize(size, Image.ANTIALIAS).convert("RGBA")
    bigsize = (pfp.size[0] * 3, pfp.size[1] * 3)
    mask = Image.new("L", bigsize, 0)
    draw = ImageDraw.Draw(mask)
    draw.ellipse((0, 0) + bigsize, fill=255)
    mask = mask.resize(pfp.size, Image.ANTIALIAS)
    mask = ImageChops.darker(mask, pfp.split()[-1])
    pfp.putalpha(mask)
    return pfp

def draw_multiple_line_text(image, text, font, text_start_height):
    """
    From unutbu on [python PIL draw multiline text on image](https://stackoverflow.com/a/7698300/395857)
    """
    draw = ImageDraw.Draw(image)
    image_width, image_height = image.size
    y_text = text_start_height
    lines = textwrap.wrap(text, width=50)
    for line in lines:
        line_width, line_height = font.getsize(line)
        draw.text(((image_width - line_width) / 2, y_text), line, font=font, fill="black")
        y_text += line_height

@asyncify
def welcomepic(pic, user, chat, count, id):
    background = Image.open("img/bg.png")  # <- Background Image (Should be PNG)
    background = background.resize((1024, 500), Image.ANTIALIAS)
    pfp = Image.open(pic).convert("RGBA")
    pfp = circle(pfp)
    pfp = pfp.resize((265, 265))  # Resizes the Profilepicture so it fits perfectly in the circle
    font = ImageFont.truetype("Calistoga-Regular.ttf", 37)  # <- Text Font of the Member Count. Change the text size for your preference
    member_text = f"User#{count}, Selamat Datang {user}"  # <- Text under the Profilepicture with the Membercount
    draw_multiple_line_text(background, member_text, font, 395)
    draw_multiple_line_text(background, chat, font, 47)
    ImageDraw.Draw(background).text(
        (530, 460),
        f"Generated by @{BOT_USERNAME}",
        font=ImageFont.truetype("Calistoga-Regular.ttf", 28),
        size=20,
        align="right",
    )
    background.paste(pfp, (379, 123), pfp)  # Pastes the Profilepicture on the Background Image
    background.save(f"downloads/welcome#{id}.png")  # Saves the finished Image in the folder with the filename
    return f"downloads/welcome#{id}.png"

@app.on_chat_member_updated(filters.group & filters.chat(-1001128045651))
async def member_has_joined(c: app, member: ChatMemberUpdated):
    if not member.new_chat_member or member.new_chat_member.status in {"banned", "left", "restricted"} or member.old_chat_member:
        return
    user = member.new_chat_member.user if member.new_chat_member else member.from_user
    if user.id in SUDO:
        await c.send_message(
            member.chat.id,
            "Waw, owner ku yang keren baru saja bergabung ke grup!",
        )
        return
    elif user.is_bot:
        return  # ignore bots
    else:
        if (temp.MELCOW).get(f"welcome-{member.chat.id}") is not None:
            try:
                await (temp.MELCOW[f"welcome-{member.chat.id}"]).delete()
            except:
                pass
        mention = f"{user.first_name}"
        joined_date = datetime.fromtimestamp(time.time()).strftime("%Y.%m.%d %H:%M:%S")
        first_name = f"{user.first_name} {user.last_name}" if user.last_name else user.first_name
        id = user.id
        dc = user.dc_id or "Member tanpa PP"
        count = await app.get_chat_members_count(member.chat.id)
        try:
            pic = await app.download_media(user.photo.big_file_id, file_name=f"pp{user.id}.png")
        except AttributeError:
            pic = "img/profilepic.png"
        welcomeimg = await welcomepic(pic, user.first_name, member.chat.title, count, user.id)
        temp.MELCOW[f"welcome-{member.chat.id}"] = await c.send_photo(
            member.chat.id,
            photo=welcomeimg,
            caption=f"Hai {mention}, Selamat datang digrup {member.chat.title} harap baca rules di pinned message terlebih dahulu.\n\nNama : {first_name}\nID : {id}\nDC ID : {dc}\nTanggal Join : {joined_date}",
        )
        userspammer = ""
        # Spamwatch Detection
        try:
            headers = {"Authorization": "Bearer XvfzE4AUNXkzCy0DnIVpFDlxZi79lt6EnwKgBj8Quuzms0OSdHvf1k6zSeyzZ_lz"}
            apispamwatch = (await http.get(f"https://api.spamwat.ch/banlist/{user.id}", headers=headers)).json()
            if not apispamwatch.get("error"):
                await app.ban_chat_member(member.chat.id, user.id, datetime.now() + timedelta(seconds=30))
                userspammer += f"#SpamWatch Federation Ban\nUser {mention} [{user.id}] has been kicked because {apispamwatch.get('reason')}.\n"
        except Exception as err:
            LOGGER.error(f"ERROR in Spamwatch Detection. {err}")
        # Combot API Detection
        try:
            apicombot = (await http.get(f"https://api.cas.chat/check?user_id={user.id}")).json()
            if apicombot.get("ok") == "true":
                await app.ban_chat_member(member.chat.id, user.id, datetime.now() + timedelta(seconds=30))
                userspammer += f"#CAS Federation Ban\nUser {mention} [{user.id}] detected as spambot and has been kicked. Powered by Combot AntiSpam."
        except Exception as err:
            LOGGER.error(f"ERROR in Combot API Detection. {err}")
        if userspammer != "":
            await c.send_message(member.chat.id, userspammer)
        try:
            os.remove(f"downloads/welcome#{user.id}.png")
            os.remove(f"downloads/pp{user.id}.png")
        except Exception:
            pass

@app.on_message(filters.new_chat_members & filters.group)
async def save_group(bot, message):
    r_j_check = [u.id for u in message.new_chat_members]
    if temp.ME in r_j_check:
        if not await db.get_chat(message.chat.id):
            total = await bot.get_chat_members_count(message.chat.id)
            r_j = message.from_user.mention if message.from_user else "Anonymous"
            await bot.send_message(
                LOG_CHANNEL,
                f"#NewGroup\nGroup = {message.chat.title}({message.chat.id})\nMembers Count = {total}\nAdded by - {r_j}",
            )

            await db.add_chat(message.chat.id, message.chat.title)
        if message.chat.id in temp.BANNED_CHATS:
            # Inspired from a boat of a banana tree
            buttons = [[InlineKeyboardButton("Support", url=f"https://t.me/{SUPPORT_CHAT}")]]
            reply_markup = InlineKeyboardMarkup(buttons)
            k = await message.reply(
                text="CHAT NOT ALLOWED 🐞\n\nMy admins has restricted me from working here ! If you want to know more about it contact support..",
                reply_markup=reply_markup,
            )

            try:
                await k.pin()
            except:
                pass
            await bot.leave_chat(message.chat.id)
            return
        buttons = [
            [
                InlineKeyboardButton("ℹ Help", url=f"https://t.me/{temp.U_NAME}?start=help"),
                InlineKeyboardButton("📢 Updates", url="https://t.me/YasirPediaChannel"),
            ]
        ]
        reply_markup = InlineKeyboardMarkup(buttons)
        await message.reply_text(
            text=f"Terimakasih sudah menambahkan saya di {message.chat.title} ❣\n\nJika ada kendala atau saran bisa kontak ke saya.",
            reply_markup=reply_markup,
        )
    else:
        for u in message.new_chat_members:
            count = await app.get_chat_members_count(message.chat.id)
            try:
                pic = await app.download_media(u.photo.big_file_id, file_name=f"pp{u.id}.png")
            except AttributeError:
                pic = "img/profilepic.png"
            welcomeimg = await welcomepic(pic, u.first_name, message.chat.title, count, u.id)
            if (temp.MELCOW).get(f"welcome-{message.chat.id}") is not None:
                try:
                    await (temp.MELCOW[f"welcome-{message.chat.id}"]).delete()
                except:
                    pass
            try:
                temp.MELCOW[f"welcome-{message.chat.id}"] = await app.send_photo(
                    message.chat.id,
                    photo=welcomeimg,
                    caption=f"Hai {u.mention}, Selamat datang digrup {message.chat.title}.",
                )
            except (ChatSendMediaForbidden, SlowmodeWait):
                await app.leave_chat(message.chat.id)
            try:
                os.remove(f"downloads/welcome#{u.id}.png")
                os.remove(f"downloads/pp{u.id}.png")
            except Exception:
                pass

@app.on_message(filters.command("leave") & filters.user(SUDO))
async def leave_a_chat(bot, message):
    if len(message.command) == 1:
        return await message.reply("Give me a chat id")
    chat = message.command[1]
    try:
        chat = int(chat)
    except:
        chat = chat
    try:
        buttons = [[InlineKeyboardButton("Support", url=f"https://t.me/{SUPPORT_CHAT}")]]
        reply_markup = InlineKeyboardMarkup(buttons)
        await bot.send_message(
            chat_id=chat,
            text="Hai kawan, \nOwner aku bilang saya harus pergi! Jika kamu ingin menambahkan bot ini lagi silahkan kontak owner bot ini.",
            reply_markup=reply_markup,
        )
        await bot.leave_chat(chat)
    except Exception as e:
        await message.reply(f"Error - {e}")
        await bot.leave_chat(chat)

@app.on_message(filters.command("disable") & filters.user(SUDO))
async def disable_chat(bot, message):
    if len(message.command) == 1:
        return await message.reply("Give me a chat id")
    r = message.text.split(None)
    if len(r) > 2:
        reason = message.text.split(None, 2)[2]
        chat = message.text.split(None, 2)[1]
    else:
        chat = message.command[1]
        reason = "No reason Provided"
    try:
        chat_ = int(chat)
    except:
        return await message.reply("Give Me A Valid Chat ID")
    cha_t = await db.get_chat(chat_)
    if not cha_t:
        return await message.reply("Chat Not Found In DB")
    if cha_t["is_disabled"]:
        return await message.reply(f"This chat is already disabled:\nReason- {cha_t['reason']} ")
    await db.disable_chat(chat_, reason)
    temp.BANNED_CHATS.append(chat_)
    await message.reply("Chat Succesfully Disabled")
    try:
        buttons = [[InlineKeyboardButton("Support", url=f"https://t.me/{SUPPORT_CHAT}")]]
        reply_markup = InlineKeyboardMarkup(buttons)
        await bot.send_message(
            chat_id=chat_,
            text=f"Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact my support group. \nReason : {reason}",
            reply_markup=reply_markup,
        )
        await bot.leave_chat(chat_)
    except Exception as e:
        await message.reply(f"Error - {e}")

@app.on_message(filters.command("enable") & filters.user(SUDO))
async def re_enable_chat(bot, message):
    if len(message.command) == 1:
        return await message.reply("Give me a chat id")
    chat = message.command[1]
    try:
        chat_ = int(chat)
    except:
        return await message.reply("Give Me A Valid Chat ID")
    sts = await db.get_chat(int(chat))
    if not sts:
        return await message.reply("Chat Not Found In DB !")
    if not sts.get("is_disabled"):
        return await message.reply("This chat is not yet disabled.")
    await db.re_enable_chat(chat_)
    temp.BANNED_CHATS.remove(chat_)
    await message.reply("Chat Succesfully re-enabled")

# a function for trespassing into others groups, Inspired by a Vazha
# Not to be used , But Just to showcase his vazhatharam.
# @app.on_message(filters.command('invite') & filters.user(SUDO))
async def gen_invite(bot, message):
    if len(message.command) == 1:
        return await message.reply("Give me a chat id")
    chat = message.command[1]
    try:
        chat = int(chat)
    except:
        return await message.reply("Give Me A Valid Chat ID")
    try:
        link = await bot.create_chat_invite_link(chat)
    except ChatAdminRequired:
        return await message.reply("Invite Link Generation Failed, Iam Not Having Sufficient Rights")
    except Exception as e:
        return await message.reply(f"Error {e}")
    await message.reply(f"Here is your Invite Link {link.invite_link}")

@app.on_message(filters.command(["adminlist"], COMMAND_HANDLER))
@capture_err
async def adminlist(_, message):
    if message.chat.type == enums.ChatType.PRIVATE:
        return await message.reply("Perintah ini hanya untuk grup")
    try:
        administrators = []
        async for m in app.get_chat_members(message.chat.id, filter=enums.ChatMembersFilter.ADMINISTRATORS):
            administrators.append(f"{m.user.first_name}")

        res = "".join(f"~ {i}\n" for i in administrators)
        return await message.reply(f"Daftar Admin di {message.chat.title} ({message.chat.id}):\n~ {res}")
    except Exception as e:
        await message.reply(f"ERROR: {str(e)}")

@app.on_message(filters.command(["kickme"], COMMAND_HANDLER))
@capture_err
async def kickme(_, message):
    reason = None
    if len(message.text.split()) >= 2:
        reason = message.text.split(None, 1)[1]
    try:
        await message.ban_member(message.from_user.id)
        txt = f"Pengguna {message.from_user.mention} menendang dirinya sendiri. Mungkin dia sedang frustasi 😕"
        txt += f"\nAlasan: {reason}" if reason else ""
        await message.reply_text(txt)
        await message.unban_member(message.from_user.id)
    except RPCError as ef:
        await message.reply_text(f"Sepertinya ada error, silahkan report ke owner saya. \nERROR: {str(ef)}")
    return

@app.on_message(filters.command("users") & filters.user(SUDO))
async def list_users(bot, message):
    # https://t.me/GetTGLink/4184
    raju = await message.reply("Getting List Of Users")
    users = await db.get_all_users()
    out = "Users Saved In DB Are:\n\n"
    async for user in users:
        out += f"{user['name']}"
        if user["ban_status"]["is_banned"]:
            out += "( Banned User )"
        out += "\n"
    try:
        await raju.edit_text(out)
    except MessageTooLong:
        with open("users.txt", "w+") as outfile:
            outfile.write(out)
        await message.reply_document("users.txt", caption="List Of Users")

@app.on_message(filters.command("chats") & filters.user(SUDO))
async def list_chats(bot, message):
    raju = await message.reply("Getting List Of chats")
    chats = await db.get_all_chats()
    out = "Chats Saved In DB Are:\n\n"
    async for chat in chats:
        out += f"**Title:** `{chat['title']}`\n**- ID:** `{chat['id']}`"
        if chat["chat_status"]["is_disabled"]:
            out += "( Disabled Chat )"
        out += "\n"
    try:
        await raju.edit_text(out)
    except MessageTooLong:
        with open("chats.txt", "w+") as outfile:
            outfile.write(out)
        await message.reply_document("chats.txt", caption="List Of Chats")


import pygame
import sys
import numpy
import math
import time
import random
import Controller1
import controllerleft
import test_opponent

#MAIN script for self made robocup simulation. Main idea is that a controlcode scans the field and assigns requests the bots to go to a position,rotation,and action.
# the classes in this script will than have the bots go to this configuration. The controller script must have a function called "update"
# A centralised physics engine still has to be implemented in where each object gets updated

class Ball():
    #class Ball
    #   includes the properties of the ball such as position velocity force mass etc
    #   function update changes the position depending on the mass and FPS of the program.
    #   function draw draws the ball
    #   function getdistance can be called from within another object to get the distance of the ball to that object.
    #       input "position" is the position of the other object
    def __init__(self, width, height):
        self.object = "ball" #this is for the physics engine, as the physics engine will see every object as "object" it is necessary to have an object specification
        self.xx = int(width/2)
        self.yy = int(height/2)
        self.radius = 10
        self.mass = 10
        self.fx = 0
        self.fy = 0
        self.ax = 0
        self.ay = 0
        self.vx = 0
        self.vy = 0
        self.Rr = 0.005
        self.Rx = 0
        self.Ry = 0
        self.bouncefactor = 1
        self.hit = False

    def update(self,time,height,width):
        self.ax = self.fx / self.mass
        self.ay = self.fy / self.mass
        self.vx = self.vx*(0.5**abs(time))
        self.vy = self.vy*(0.5**abs(time))
        self.vx += self.ax * time
        self.vy += self.ay * time
        self.position = [self.position[0] + self.vx, self.position[1] + self.vy]
        self.fx = 0
        self.fy = 0

        if (self.position[0] > width - self.radius) or (self.position[0] < self.radius):
            self.vx = -self.vx
        if (self.position[1] > height - self.radius) or (self.position[1] < self.radius):
            self.vy = -self.vy

    def draw(self, screen):
        pygame.draw.circle(screen, (0,255,255), (int(self.xx), int(self.yy)), self.radius, 2)

    def getdistance(self,position):
        difx = self.xx - position[0]
        dify = self.yy - position[1]
        length = math.sqrt(difx**2 + dify**2)
        dirx = difx/length
        diry = dify/length
        return length, dirx, diry

class Bot():
    #Stores the properties of a bot. This class is called from the Team class
    #   function move updates the position of the bot by finding the direction between the requested position and the
    #    curent position. Requested position comes from the controller assigned to the team, the Controller is the basic AI for the whole team
    #    - should be updated to change the movespeed from the requested move speed. Property requested move speed still needs to be implemented.
    #   Function Rotate rotates the bot towards the requested direction.
    def __init__(self):
        self.object = "bot"
        self.role = 1 #can be used by controller to decide which role the bot has. This for example could be a keeper role for which a different script gets executed.
        self.hasBall = False
        self.requestPosition = (0,0)
        self.pidposition = (0,0)
        self.requestAction = (0,0)
        self.facing = 0
        self.requestfacing = 0
        self.movespeed = 1
        self.requestmovespeed = 1
        self.rotatespeed = 1
        self.radius = 20
        self.acceleration = 0.1
        self.fx = 0
        self.fy = 0
        self.ax = 0
        self.ay = 0
        self.vx = 0
        self.vy = 0
        self.xx = 0
        self.yy = 0
        self.Rx = 0
        self.Ry = 0
        self.Rr = 2.5
        self.mass = 10
        self.kP = 1
        self.kI = 0
        self.kD = 1
        self.kickforce = 0

        self.interrorx = 0
        self.interrory = 0
        self.diferrorx = 0
        self.preferrorx = 0
        self.diferrory = 0
        self.preferrory = 0
        self.errorx = 0
        self.errory = 0
        self.moveforce = 500
        self.bouncefactor = 0

    def move(self,dt):
        self.errorx = numpy.float32(self.requestPosition[0] - self.xx)
        self.errory = numpy.float32(self.requestPosition[1] - self.yy)
        self.interrorx += self.errorx
        self.interrory += self.errory
        self.diferrorx = (self.errorx - self.preferrorx)
        self.diferrory = (self.errory - self.preferrory)

        difx = self.errorx*self.kP + self.kI*self.interrorx + self.kD*self.diferrorx
        dify = self.errory*self.kP + self.kI*self.interrory + self.kD*self.diferrory
        self.pidposition = (difx,dify)

        dist = math.sqrt(difx**2 + dify**2)

        if dist > 0:
            dirx = difx / abs(dist)
            diry = dify / abs(dist)
        else:
            dirx = 0
            diry = 0

        self.fx = (self.moveforce * dirx)
        self.fy = (self.moveforce * diry)

        self.preferrorx = self.errorx
        self.preferrory = self.errory

    def rotate(self):
        if self.requestfacing > self.facing:
            self.facing += 0.01*numpy.pi
        else:
            self.facing -= 0.01*numpy.pi

class Team():
    # the Team class is pretty much an envelope for the bots in the team, the class is initialized by assinging a number
    # of bots to the team and assinging a controller code. Next to that the bots get assigned a starting position
    #   - idea? Have the starting position assigned by the controller?
    #   function Draw_team loops over the bots and updates the position and rotation, than draws the bots.
    def __init__(self, number, width, height, controller):
        self.bots = [] #array of bots
        self.score = 0 #team score -scoring mechanism still needs to be implemented
        self.number = number #number of the team
        if self.number == 1:
            self.side = 1 #left
            self.color = (255, 0, 0)
        else:
            self.side = 2 #right
            self.color = (0, 0, 255)

        for i in range(3): #append five bots
            self.bots.append(Bot())
        self.controller = controller(self,width,height)

    def draw_team(self,screen,dt,myfont,width):
        for i in range(len(self.bots)):
            self.bots[i].move(dt)
            self.bots[i].rotate()
            scoretext = myfont.render(str(self.score),False,(255,255,255))
            if self.side == 1:
                screen.blit(scoretext,(width/2-50,10))
            else:
                screen.blit(scoretext,(width/2+50,10))
            relpos = (int(numpy.sin(self.bots[i].facing)*20), int(numpy.cos(self.bots[i].facing)*20))
            pygame.draw.circle(screen, self.color, (int(self.bots[i].xx), int(self.bots[i].yy)), self.bots[i].radius, 2)
            pygame.draw.lines(screen, self.color, False, [(self.bots[i].xx, self.bots[i].yy), (self.bots[i].xx+relpos[0], self.bots[i].yy+relpos[1])])

def draw_field(screen, width, height, goal_width,    blue  = (100, 100, 255),    green = (0,   255, 0  ),    white = (255, 255, 255),    black = (0,   0,   0  ),    pink  = (255, 200, 200)):
    #function that draws the field
    #goal_width = 60
    keeper_distance= 100
    centre_circle_radius = 20
    centre_width = round(width/2)
    centre_height = round(height/2)

    screen.fill(black)
    #center circle
    pygame.draw.circle(screen, white, (centre_width,centre_height), centre_circle_radius, 2)

    #keeper field
    pygame.draw.lines(screen, white, True, [(0, centre_height+keeper_distance), (keeper_distance, centre_height+keeper_distance), (keeper_distance, centre_height-keeper_distance), (0, centre_height-keeper_distance)])
    pygame.draw.lines(screen, white, True, [(width, centre_height+keeper_distance), (width-keeper_distance, centre_height+keeper_distance), (width-keeper_distance, centre_height-keeper_distance), (width, centre_height-keeper_distance)])

    #goals
    pygame.draw.lines(screen, blue, False, [(0,centre_height-goal_width/2), (0,centre_height+goal_width/2)], 8)
    pygame.draw.lines(screen, blue, False, [(width-2, centre_height-goal_width/2),(width-2,centre_height+goal_width/2)], 8)

    #vertical line centre
    pygame.draw.lines(screen, white, False, [(centre_width, 0),(centre_width, height)])

    #around the screen
    pygame.draw.lines(screen, white, True, [(0,0), (width-2,0), (width-2,height-2), (0,height-2)], 3)

def updatephysics(teams,ball,width,height,dt,screen,goalwidth,cornertime):
    objects = []
    for team in teams:
        for bot in team.bots:
            objects.append(bot)
    objects.append(ball)
    for object in objects:
        relftotx = object.xx + object.fx*10
        relftoty = object.yy + object.fy*10
        object.fy += object.Ry
        object.fx += object.Rx
        object.ax = object.fx / object.mass
        object.ay = object.fy / object.mass
        object.vx += object.ax
        object.vy += object.ay
        object.Rx = -object.Rr * object.vx
        object.Ry = -object.Rr * object.vy
        object.xx += object.vx * dt
        object.yy += object.vy * dt
        if object.xx > width-object.radius:
            object.xx = width - object.radius
            object.vx = -object.vx*object.bouncefactor
        if object.xx < object.radius:
            object.xx = object.radius
            object.vx = -object.vx*object.bouncefactor
        if object.yy > height-object.radius:
            object.yy = height-object.radius
            object.vy = -object.vy*object.bouncefactor
        if object.yy < object.radius:
            object.yy = object.radius
            object.vy = -object.vy*object.bouncefactor

        objectdebug = 0
        if objectdebug == 1:
            relrx = object.xx + object.Rx*10
            relry = object.yy + object.Ry*10
            relfx = object.xx + object.fx*10
            relfy = object.yy + object.fy*10
            pygame.draw.lines(screen,(255,0,0),False,[(object.xx,object.yy),(relrx,relry)])
            pygame.draw.lines(screen,(0,255,0),False,[(object.xx,object.yy),(relfx,relfy)])
            pygame.draw.lines(screen,(0,125,0),False,[(object.xx,object.yy),(relftotx,relftoty)])
            if object.object == "bot":
                pygame.draw.lines(screen,(0,0,255),False,[(object.xx,object.yy),(object.pidposition[0]+object.xx,object.pidposition[1]+object.yy)])
                pygame.draw.lines(screen,(0,0,125),False,[(object.xx,object.yy),object.requestPosition])

        if object.object == "bot":
            dist,dirx,diry = ball.getdistance((object.xx,object.yy))
            if dist <= object.radius + ball.radius:
                print("bot hit ball: kickforce = : " + str(object.kickforce) + " dirx: " + str(dirx) + " diry: " + str(diry))
                ball.fx += dirx*object.kickforce
                ball.fy += diry*object.kickforce
                ball.hit = True
                difdist = (ball.radius + object.radius) - dist
                ball.xx += dirx * difdist
                ball.yy += diry * difdist

        if object.object == "ball":
            object.fx = 0
            object.fy = 0
            object.hit = False
            if abs(height/2 - object.yy) < goalwidth/2:
                if object.xx == object.radius:
                    print("ball in left goal")
                    teams[1].score += 1
                    object.xx = width/2
                    object.yy = height/2
                elif object.xx == width-object.radius:
                    teams[0].score += 1
                    print("ball in right goal")
                    object.xx = width/2
                    object.yy = height/2

        for object2 in objects:
            if object2.object == "bot" and object.object == "bot":
                dist = math.sqrt((object2.xx - object.xx)**2 + (object2.yy - object.yy)**2)
                if dist <= object2.radius + object.radius and dist > 0:
                    print("Collision detected between :" + str(object2.object) + " and " + str(object.object))
                    difx = object2.xx - object.xx
                    dify = object2.yy - object.yy
                    dirx = difx/dist
                    diry = dify/dist
                    difdist = (object2.radius + object.radius)-dist
                    object.xx -= dirx*difdist
                    object.yy -= diry*difdist

    if (ball.xx < ball.radius*3 or  ball.xx > width - ball.radius*3) and (ball.yy < ball.radius*3 or ball.yy > height - ball.radius*3):
        cornertime += dt
    else:
        cornertime = 0

    if cornertime > 10:
        ball.xx = width /2
        ball.yy = height /2
        ball.vx = 0
        ball.vy = 0
        ball.ax = 0
        ball.ay = 0
        ball.fx = 0
        ball.fy = 0

    return cornertime

def main():
    #main loop.
    t1 = time.time() #start an FPS timer
    red   = (255, 0,   0  )
    blue  = (100, 100, 255)
    green = (0,   255, 0  )
    white = (255, 255, 255)
    black = (0,   0,   0  )
    pink  = (255, 200, 200)

    screen_width = 1280
    screen_height = 800
    goalwidth = 120

    screen = pygame.display.set_mode((screen_width, screen_height))

    running = True

    pygame.init()
    pygame.font.init()
    myfont = pygame.font.SysFont('Times New Roman',30)
    clock = pygame.time.Clock()

    team1 = Team(1, screen_width, screen_height, Controller1.controller)
    team2 = Team(2, screen_width, screen_height, controllerleft.controller)
    ball = Ball(screen_width, screen_height)
    loop = 1
    dt = time.time() - t1
    cornertime = 0
    while running:
        t1 = time.time()

        clock.tick(1000)

        draw_field(screen, screen_width, screen_height, goalwidth)

        team1.controller.update(ball,screen_height,screen_width)
        team2.controller.update(ball,screen_height,screen_width)
        cornertime = updatephysics([team1,team2],ball,screen_width,screen_height,dt,screen,goalwidth,cornertime)
        #ball.update(dt,screen_height,screen_width)
        team1.draw_team(screen,dt,myfont,screen_width)
        team2.draw_team(screen,dt,myfont,screen_width)
        ball.draw(screen)

        fpstext = myfont.render("FPS = " + str(round(1/dt)),False,(255,255,255))
        screen.blit(fpstext,(10,10))
        cornertimetext = myfont.render("cornertime = " + str(round(cornertime,1)),False,(255,255,255))
        screen.blit(cornertimetext,(10,30))

        pygame.display.update()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit(); sys.exit()

        #print("FPS = "  + str(round(1/dt)))
        loop += 1
        dt = (time.time() - t1)

main()