from statistics import mean, median
import time

import pygame
import sys
import copy
import math

NEGRU = (0, 0, 0)
ROSU = (255, 0, 0)
VERDE = (0, 255, 0)
ALB = (255, 255, 255)

LINE_WIDTH = 1

# dimensiunea ferestrei in pixeli
L = 75
CELL_SIZE = (L, L)
DISPLAY_SIZE = (L*5, L*10)

class Buton:
	def __init__(self, display=None, left=0, top=0, w=0, h=0, culoareFundal=(53, 80, 115), culoareFundalSel=(89, 134, 194), text="", font="arial", fontDimensiune=16, culoareText=(255, 255, 255), valoare=""):
		self.display = display
		self.culoareFundal = culoareFundal
		self.culoareFundalSel = culoareFundalSel
		self.text = text
		self.font = font
		self.w = w
		self.h = h
		self.selectat = False
		self.fontDimensiune = fontDimensiune
		self.culoareText = culoareText
		# creez obiectul font
		fontObj = pygame.font.SysFont(self.font, self.fontDimensiune)
		self.textRandat = fontObj.render(self.text, True, self.culoareText)
		self.dreptunghi = pygame.Rect(left, top, w, h)
		# aici centram textul
		self.dreptunghiText = self.textRandat.get_rect(
			center=self.dreptunghi.center)
		self.valoare = valoare

	def selecteaza(self, sel):
		self.selectat = sel
		self.deseneaza()

	def selecteazaDupacoord(self, coord):
		if self.dreptunghi.collidepoint(coord):
			self.selecteaza(True)
			return True
		return False

	def updateDreptunghi(self):
		self.dreptunghi.left = self.left
		self.dreptunghi.top = self.top
		self.dreptunghiText = self.textRandat.get_rect(
			center=self.dreptunghi.center)

	def deseneaza(self):
		culoareF = self.culoareFundalSel if self.selectat else self.culoareFundal
		pygame.draw.rect(self.display, culoareF, self.dreptunghi)
		self.display.blit(self.textRandat, self.dreptunghiText)

class GrupButoane:
	def __init__(self, listaButoane=[], indiceSelectat=0, spatiuButoane=10, left=0, top=0):
		self.listaButoane = listaButoane
		self.indiceSelectat = indiceSelectat
		self.listaButoane[self.indiceSelectat].selectat = True
		self.top = top
		self.left = left
		leftCurent = self.left
		for b in self.listaButoane:
			b.top = self.top
			b.left = leftCurent
			b.updateDreptunghi()
			leftCurent += (spatiuButoane+b.w)

	def selecteazaDupacoord(self, coord):
		for ib, b in enumerate(self.listaButoane):
			if b.selecteazaDupacoord(coord):
				self.listaButoane[self.indiceSelectat].selecteaza(False)
				self.indiceSelectat = ib
				return True
		return False

	def deseneaza(self):
		# atentie, nu face wrap
		for b in self.listaButoane:
			b.deseneaza()

	def getValoare(self):
		return self.listaButoane[self.indiceSelectat].valoare

# ECRAN INITIAL

def deseneaza_alegeri(display, tabla_curenta):
	btn_alg = GrupButoane(
		top=30,
		left=30,
		listaButoane=[
			Buton(display=display, w=80, h=30, text="minimax", valoare="1"),
			Buton(display=display, w=80, h=30, text="alphabeta", valoare="2")
		],
		indiceSelectat=1)
	btn_juc = GrupButoane(
		top=100,
		left=30,
		listaButoane=[
			Buton(display=display, w=50, h=30, text="White", valoare="W"),
			Buton(display=display, w=50, h=30, text="Black", valoare="B")
		],
		indiceSelectat=0)
	btn_dificultate = GrupButoane(
		top=170,
		left=30,
		listaButoane=[
			Buton(display=display, w=60, h=30, text="Easy", valoare=1),
			Buton(display=display, w=60, h=30, text="Medium", valoare=3),
			Buton(display=display, w=60, h=30, text="Hard", valoare=5)
		],
		indiceSelectat=0)
	ok = Buton(display=display, top=240, left=30, w=40,
			   h=30, text="ok", culoareFundal=(155, 0, 55))
	btn_alg.deseneaza()
	btn_juc.deseneaza()
	btn_dificultate.deseneaza()
	ok.deseneaza()
	while True:
		for ev in pygame.event.get():
			if ev.type == pygame.QUIT:
				pygame.quit()
				sys.exit()
			elif ev.type == pygame.MOUSEBUTTONDOWN:
				pos = pygame.mouse.get_pos()
				if not btn_alg.selecteazaDupacoord(pos):
					if not btn_juc.selecteazaDupacoord(pos):
						if not btn_dificultate.selecteazaDupacoord(pos):
							if ok.selecteazaDupacoord(pos):
								display.fill((0, 0, 0))  # stergere ecran
								tabla_curenta.deseneaza_grid()
								return btn_juc.getValoare(), btn_alg.getValoare(), btn_dificultate.getValoare()
		pygame.display.update()

class Joc:
	"""
	Clasa care defineste jocul. Se va schimba de la un joc la altul.
	"""
	NR_LINII = 10
	NR_COLOANE = 5
	JMIN = None
	JMAX = None
	GOL = '#'
	INACCESIBIL = "X"
	WHITE = "W"
	BLACK = "B"

	@classmethod
	def initializeaza(cls, display, NR_LINII=NR_LINII, NR_COLOANE=NR_COLOANE, dim_celula=L):
		cls.display = display
		cls.dim_celula = dim_celula
		cls.white_circle_iamge = pygame.image.load('white_circle.png')
		cls.white_circle_iamge = pygame.transform.scale(cls.white_circle_iamge, (dim_celula, math.floor(
			dim_celula*cls.white_circle_iamge.get_height()/cls.white_circle_iamge.get_width())))
		cls.black_circle_image = pygame.image.load('black_circle.png')
		cls.black_circle_image = pygame.transform.scale(cls.black_circle_image, (dim_celula, math.floor(
			dim_celula*cls.black_circle_image.get_height()/cls.black_circle_image.get_width())))
		cls.celuleGrid = []  # este lista cu patratelele din grid
		for linie in range(NR_LINII):
			cls.celuleGrid.append([])
			for coloana in range(NR_COLOANE):
				patrat = pygame.Rect(coloana*(dim_celula),
									 linie*(dim_celula), dim_celula, dim_celula)
				cls.celuleGrid[linie].append(patrat)

	def deseneaza_linii_grid(self):
		# desenez liniile tablei
		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][0].center,
						 self.__class__.celuleGrid[2][2].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][0].center,
						 self.__class__.celuleGrid[0][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[1][1].center,
						 self.__class__.celuleGrid[1][3].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][1].center,
						 self.__class__.celuleGrid[1][1].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][2].center,
						 self.__class__.celuleGrid[9][2].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][3].center,
						 self.__class__.celuleGrid[1][3].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[0][4].center,
						 self.__class__.celuleGrid[2][2].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[2][0].center,
						 self.__class__.celuleGrid[2][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[3][0].center,
						 self.__class__.celuleGrid[3][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[4][0].center,
						 self.__class__.celuleGrid[4][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[5][0].center,
						 self.__class__.celuleGrid[5][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[6][0].center,
						 self.__class__.celuleGrid[6][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[7][0].center,
						 self.__class__.celuleGrid[7][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[2][0].center,
						 self.__class__.celuleGrid[7][0].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[2][1].center,
						 self.__class__.celuleGrid[7][1].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[2][3].center,
						 self.__class__.celuleGrid[7][3].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[2][4].center,
						 self.__class__.celuleGrid[7][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[7][2].center,
						 self.__class__.celuleGrid[9][0].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[7][2].center,
						 self.__class__.celuleGrid[9][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[8][1].center,
						 self.__class__.celuleGrid[8][3].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[9][0].center,
						 self.__class__.celuleGrid[9][4].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[8][1].center,
						 self.__class__.celuleGrid[9][1].center,
						 width=LINE_WIDTH)

		pygame.draw.line(self.__class__.display, NEGRU,
						 self.__class__.celuleGrid[8][3].center,
						 self.__class__.celuleGrid[9][3].center,
						 width=LINE_WIDTH)

		pygame.display.update()

	# tabla de exemplu este ["#","x","#","0",......]
	def deseneaza_grid(self, marcaj=None, posibile_pozitii=[]):

		for linie in range(Joc.NR_LINII):
			for coloana in range(Joc.NR_COLOANE):
				if marcaj == (linie, coloana):
					# daca am o patratica selectata, o desenez cu rosu
					culoare = ROSU
				elif (linie, coloana) in posibile_pozitii:
					culoare = VERDE
				else:
					# altfel o desenez cu alb
					culoare = ALB
				# alb = (255,255,255)
				pygame.draw.rect(self.__class__.display, culoare,
								 self.__class__.celuleGrid[linie][coloana])

		self.deseneaza_linii_grid()

		for linie in range(Joc.NR_LINII):
			for coloana in range(Joc.NR_COLOANE):
				if self.matr[linie][coloana] == self.__class__.WHITE:
					self.__class__.display.blit(self.__class__.white_circle_iamge, (coloana*(self.__class__.dim_celula), linie*(
						self.__class__.dim_celula) + (self.__class__.dim_celula-self.__class__.white_circle_iamge.get_height())//2))
				elif self.matr[linie][coloana] == self.__class__.BLACK:
					self.__class__.display.blit(self.__class__.black_circle_image, (coloana*(self.__class__.dim_celula), linie*(
						self.__class__.dim_celula)+(self.__class__.dim_celula-self.__class__.black_circle_image.get_height())//2))
		# pygame.display.flip() # !!! obligatoriu pentru a actualiza interfata (desenul)

		pygame.display.update()

	@classmethod
	def matrice_initiala(cls):
		matr = []
		for i in range(4):
			matr.append([cls.BLACK] *
						cls.NR_COLOANE)
		for i in range(cls.NR_LINII-8):
			matr.append([cls.GOL] *
						cls.NR_COLOANE)
		for i in range(4):
			matr.append([cls.WHITE] *
						cls.NR_COLOANE)

		matr[1][0] = cls.INACCESIBIL
		matr[1][4] = cls.INACCESIBIL

		matr[8][0] = cls.INACCESIBIL
		matr[8][4] = cls.INACCESIBIL

		matr[3][0] = cls.GOL
		matr[3][4] = cls.GOL

		matr[6][0] = cls.GOL
		matr[6][4] = cls.GOL

		return matr

	def __init__(self, tabla=None):
		if tabla:
			self.matr = tabla
		else:
			self.matr = self.__class__.matrice_initiala()

	@classmethod
	def jucator_opus(cls, jucator):
		return cls.JMAX if jucator == cls.JMIN else cls.JMIN

	@classmethod
	def directii(cls, x, y):
		# directiile in care se poate deplasa de la coordonatele (x, y)
		sus = (-1, 0)
		dreapta = (0, 1)
		jos = (1, 0)
		stanga = (0, -1)
		directii_normale = [sus, jos, stanga, dreapta]

		if (x, y) in [(0, 0), (1, 1)]:
			return [(1, 1), (-1, -1), sus, dreapta]

		if (x, y) == (2, 2):
			return [(-1, -1), (-1, 1)] + directii_normale

		if (x, y) in [(0, 4), (1, 3)]:
			return [(-1, 1), (1, -1), sus, stanga]

		if (x, y) == (7, 2):
			return [(1, 1), (1, -1)] + directii_normale

		if (x, y) in [(8, 1), (9, 0)]:
			return [(1, -1), (-1, 1), dreapta, jos]

		if (x, y) in [(8, 3), (9, 4)]:
			return [(1, 1), (-1, -1), stanga, jos]

		if x == 2:
			return [stanga, dreapta, jos]

		if x == 7:
			return [stanga, dreapta, sus]

		return directii_normale

	def final(self, jucator):
		nr_piese_JMAX = 0
		nr_piese_JMIN = 0
		castiga_alb = True
		castiga_negru = True

		# verific daca un triunghi a fost cucerit de adversar
		for i in range(2):
			for j in range(self.NR_COLOANE):
				if self.matr[i][j] != self.WHITE and self.matr[i][j] != self.INACCESIBIL:
					castiga_alb = False
					break
		if self.matr[2][2] != self.WHITE and self.matr[2][2] != self.INACCESIBIL:
			castiga_alb = False
		if castiga_alb:
			print("Alb a cucerit triunghiul negru")
			return self.WHITE

		for i in range(self.NR_LINII-2, self.NR_LINII):
			for j in range(self.NR_COLOANE):
				if self.matr[i][j] != self.BLACK and self.matr[i][j] != self.INACCESIBIL:
					castiga_negru = False
					break
		if self.matr[7][2] != self.BLACK and self.matr[7][2] != self.INACCESIBIL:
			castiga_negru = False
		if castiga_negru:
			print("Negru a cucerit triunghiul alb")
			return self.BLACK

		# calculez cate piese are jucatorul si calculatorul
		for i in range(self.NR_LINII):
			for j in range(self.NR_COLOANE):
				if self.matr[i][j] == self.JMAX:
					nr_piese_JMAX += 1
				if self.matr[i][j] == self.JMIN:
					nr_piese_JMIN += 1
		if nr_piese_JMAX == 0:
			print("Calculatorul nu mai are piese")
			return self.JMIN
		if nr_piese_JMIN == 0:
			print("Jucatorul nu mai are piese")
			return self.JMAX

		# verific daca jucatorul curent nu mai are mutari, daca da a castigat adversarul
		mutari = self.mutari(jucator)
		if len(mutari) == 0:
			print(jucator + " nu mai are mutari")
			return self.jucator_opus(jucator)

		return False

	def pozitii_in_care_poate_muta(self, x, y):
		# pozitiile in care poate muta jucatorul pentru a le colora cu verde si afisa in interfata
		# se refera strict la locuri pe tabla nu la starile rezultate
		jucator = self.matr[x][y]
		inamic = self.jucator_opus(jucator)
		pozitii = []

		are_capturi = self.are_capturi(jucator)

		if are_capturi:
			if self.are_capturi_din_punct(inamic, x, y):
				directii = Joc.directii(x, y)
				for (i, j) in directii:
					x_nou = x + i
					y_nou = y + j
					x_dupa = x + 2*i
					y_dupa = y + 2*j
					if x_nou < 0 or y_nou < 0 or x_nou >= Joc.NR_LINII or y_nou >= Joc.NR_COLOANE:
						continue
					if x_dupa < 0 or y_dupa < 0 or x_dupa >= Joc.NR_LINII or y_dupa >= Joc.NR_COLOANE:
						continue

					if self.matr[x_nou][y_nou] == inamic and self.matr[x_dupa][y_dupa] == Joc.GOL:
						pozitii.append((x_dupa, y_dupa))
				return pozitii
		else:
			directii = Joc.directii(x, y)
			for (i, j) in directii:
				x_nou = x + i
				y_nou = y + j
				if x_nou < 0 or y_nou < 0 or x_nou >= Joc.NR_LINII or y_nou >= Joc.NR_COLOANE:
					continue

				if self.matr[x_nou][y_nou] == Joc.GOL:
					# se poate muta
					copie_matr = copy.deepcopy(self.matr)
					copie_matr[x][y] = Joc.GOL
					copie_matr[x_nou][y_nou] = jucator
					pozitii.append((x_nou, y_nou))

		return pozitii

	def are_capturi_din_punct(self, inamic, x, y, matrice=None):
		# returneaza daca din punctul (x, y) se poate realiza o captura
		if matrice == None:
			matrice = self.matr

		# verific daca piesa aceasta poate captura
		directii = Joc.directii(x, y)
		for (i, j) in directii:
			x_nou = x + i
			y_nou = y + j
			x_dupa = x + 2*i
			y_dupa = y + 2*j
			if x_nou < 0 or y_nou < 0 or x_nou >= Joc.NR_LINII or y_nou >= Joc.NR_COLOANE:
				continue
			if x_dupa < 0 or y_dupa < 0 or x_dupa >= Joc.NR_LINII or y_dupa >= Joc.NR_COLOANE:
				continue

			if matrice[x_nou][y_nou] == inamic and matrice[x_dupa][y_dupa] == Joc.GOL:
				return True

		return False

	def stari_dupa_capturi(self, jucator, inamic, x, y, matrice):
		# genereaza starile din (x, y) care rezulta cu ajutorul unei capturi
		# captura poate fi multipla
		l_mutari = []
		if self.are_capturi_din_punct(inamic, x, y, matrice):
			directii = Joc.directii(x, y)
			for (i, j) in directii:
				x_nou = x + i
				y_nou = y + j
				x_dupa = x + 2*i
				y_dupa = y + 2*j
				if x_nou < 0 or y_nou < 0 or x_nou >= Joc.NR_LINII or y_nou >= Joc.NR_COLOANE:
					continue
				if x_dupa < 0 or y_dupa < 0 or x_dupa >= Joc.NR_LINII or y_dupa >= Joc.NR_COLOANE:
					continue

				if matrice[x_nou][y_nou] == inamic and matrice[x_dupa][y_dupa] == Joc.GOL:
					# poate captura
					copie_matr = copy.deepcopy(matrice)
					copie_matr[x][y] = Joc.GOL
					copie_matr[x_nou][y_nou] = Joc.GOL
					copie_matr[x_dupa][y_dupa] = jucator
					# Continua recursia la (x_dupa, y_dupa)
					l_mutari.extend(self.stari_dupa_capturi(
						jucator, inamic, x_dupa, y_dupa, copie_matr))
			return l_mutari
		else:
			# opreste recursia si returneaza jocul obtinut
			return [Joc(matrice)]

	def mutari_cu_capturi(self, jucator):
		# returneaza daca exista capturi in jocul curent si o lista cu starile de dupa capturi
		l_mutari = []
		are_capturi = False
		inamic = Joc.jucator_opus(jucator)
		for i in range(self.NR_LINII):
			for j in range(self.NR_COLOANE):
				if self.matr[i][j] == jucator:
					if self.are_capturi_din_punct(inamic, i, j):
						are_capturi = True
						l_mutari.extend(self.stari_dupa_capturi(
							jucator, inamic, i, j, self.matr))
		return are_capturi, l_mutari

	def are_capturi(self, jucator):
		# returneaza daca exista capturi in jocul curent
		are_capturi = False
		inamic = Joc.jucator_opus(jucator)
		for i in range(self.NR_LINII):
			for j in range(self.NR_COLOANE):
				if self.matr[i][j] == jucator:
					if self.are_capturi_din_punct(inamic, i, j):
						are_capturi = True
		return are_capturi

	def mutari_fara_capturi(self, jucator):
		l_mutari = []
		for x in range(self.NR_LINII):
			for y in range(self.NR_COLOANE):
				if self.matr[x][y] == jucator:
					directii = Joc.directii(x, y)
					for (i, j) in directii:
						x_nou = x + i
						y_nou = y + j
						if x_nou < 0 or y_nou < 0 or x_nou >= Joc.NR_LINII or y_nou >= Joc.NR_COLOANE:
							continue

						if self.matr[x_nou][y_nou] == Joc.GOL:
							# se poate muta
							copie_matr = copy.deepcopy(self.matr)
							copie_matr[x][y] = Joc.GOL
							copie_matr[x_nou][y_nou] = jucator
							l_mutari.append(Joc(copie_matr))

		return l_mutari

	def mutari(self, jucator):  # jucator = simbolul jucatorului care muta
		are_capturi_din_punct, l_mutari = self.mutari_cu_capturi(jucator)
		if are_capturi_din_punct:
			return l_mutari
		else:
			return self.mutari_fara_capturi(jucator)

	def estimeaza_scor(self, adancime, jucator, estimare=2):
		t_final = self.final(jucator)
		# if (adancime==0):
		if t_final == self.__class__.JMAX:
			return (990+adancime)
		elif t_final == self.__class__.JMIN:
			return (-999-adancime)
		elif t_final == 'remiza':
			return 0
		else:
			if estimare == 1:
				# returnez nr de piese ale computerului - nr de piese ale jucatorului
				nr_piese_JMAX = 0
				nr_piese_JMIN = 0
				for i in range(self.NR_LINII):
					for j in range(self.NR_COLOANE):
						if self.matr[i][j] == self.JMAX:
							nr_piese_JMAX += 1
						if self.matr[i][j] == self.JMIN:
							nr_piese_JMIN += 1
				return nr_piese_JMAX - nr_piese_JMIN
			else:
				# returnez (nr de piese ale computerului - nr de piese ale jucatorului) * 5 +
    			# + (avansul pieselor calculatorului - avansul pieselor jucatorului)
				nr_piese_JMAX = 0
				nr_piese_JMIN = 0
				for i in range(self.NR_LINII):
					for j in range(self.NR_COLOANE):
						if self.matr[i][j] == self.JMAX:
							nr_piese_JMAX += 1
						if self.matr[i][j] == self.JMIN:
							nr_piese_JMIN += 1

				avans_JMAX = 0
				avans_JMIN = 0
				for i in range(self.NR_LINII):
					for j in range(self.NR_COLOANE):
						if self.matr[i][j] == self.JMAX:
							avans_JMAX += i
						if self.matr[i][j] == self.JMIN:
							avans_JMIN += self.NR_LINII-1-i
				return (nr_piese_JMAX - nr_piese_JMIN) * 5 + avans_JMAX - avans_JMIN

	def sirAfisare(self):
		sir = "  |"
		sir += " ".join([str(i) for i in range(self.NR_COLOANE)])+"\n"
		sir += "-"*(self.NR_COLOANE+1)*2+"\n"
		for i in range(self.NR_LINII):  # itereaza prin linii
			sir += str(i)+" |"+" ".join([str(x) for x in self.matr[i]])+"\n"
		return sir

	def __str__(self):
		return self.sirAfisare()

	def __repr__(self):
		return self.sirAfisare()

class Stare:
	"""
	Clasa folosita de algoritmii minimax si alpha-beta
	Are ca proprietate tabla de joc
	Functioneaza cu conditia ca in cadrul clasei Joc sa fie definiti JMIN si JMAX (cei doi jucatori posibili)
	De asemenea cere ca in clasa Joc sa fie definita si o metoda numita mutari() care ofera lista cu configuratiile posibile in urma mutarii unui jucator
	"""

	def __init__(self, tabla_joc: Joc, j_curent, adancime, parinte=None, estimare=None):
		self.tabla_joc = tabla_joc
		self.j_curent = j_curent
		self.parinte = parinte

		# adancimea in arborele de stari
		self.adancime = adancime

		# estimarea favorabilitatii starii (daca e finala) sau al celei mai bune stari-fiice (pentru jucatorul curent)
		self.estimare = estimare

		# lista de mutari posibile din starea curenta
		self.mutari_posibile = []

		# cea mai buna mutare din lista de mutari posibile pentru jucatorul curent
		self.stare_aleasa = None

	def mutari(self):
		l_mutari = self.tabla_joc.mutari(self.j_curent)
		juc_opus = Joc.jucator_opus(self.j_curent)
		l_stari_mutari = [
			Stare(mutare, juc_opus, self.adancime-1, parinte=self) for mutare in l_mutari]

		# print(f"Din starea {self} se poate ajunge in starile {l_stari_mutari}")
		return l_stari_mutari

	def __str__(self):
		sir = str(self.tabla_joc) + "(Juc curent:"+self.j_curent+")\n"
		return sir

""" Algoritmul MinMax """

def min_max(stare: Stare):

	if stare.adancime == 0 or stare.tabla_joc.final(stare.j_curent):
		stare.estimare = stare.tabla_joc.estimeaza_scor(
			stare.adancime, stare.j_curent)
		return stare

	# calculez toate mutarile posibile din starea curenta
	stare.mutari_posibile = stare.mutari()

	# aplic algoritmul minimax pe toate mutarile posibile (calculand astfel subarborii lor)
	mutariCuEstimare = [min_max(mutare) for mutare in stare.mutari_posibile]

	if stare.j_curent == Joc.JMAX:
		# daca jucatorul e JMAX aleg starea-fiica cu estimarea maxima
		stare.stare_aleasa = max(mutariCuEstimare, key=lambda x: x.estimare)
	else:
		# daca jucatorul e JMIN aleg starea-fiica cu estimarea minima
		stare.stare_aleasa = min(mutariCuEstimare, key=lambda x: x.estimare)
	stare.estimare = stare.stare_aleasa.estimare
	return stare

def alpha_beta(alpha, beta, stare: Stare):
	if stare.adancime == 0 or stare.tabla_joc.final(stare.j_curent):
		stare.estimare = stare.tabla_joc.estimeaza_scor(
			stare.adancime, stare.j_curent)
		return stare

	if alpha > beta:
		return stare  # este intr-un interval invalid deci nu o mai procesez

	stare.mutari_posibile = stare.mutari()

	if stare.j_curent == Joc.JMAX:
		estimare_curenta = float('-inf')

		for mutare in stare.mutari_posibile:
			# calculeaza estimarea pentru starea noua, realizand subarborele
			stare_noua = alpha_beta(alpha, beta, mutare)

			if (estimare_curenta < stare_noua.estimare):
				stare.stare_aleasa = stare_noua
				estimare_curenta = stare_noua.estimare
			if(alpha < stare_noua.estimare):
				alpha = stare_noua.estimare
				if alpha >= beta:
					break

	elif stare.j_curent == Joc.JMIN:
		estimare_curenta = float('inf')

		for mutare in stare.mutari_posibile:

			stare_noua = alpha_beta(alpha, beta, mutare)

			if (estimare_curenta > stare_noua.estimare):
				stare.stare_aleasa = stare_noua
				estimare_curenta = stare_noua.estimare

			if(beta > stare_noua.estimare):
				beta = stare_noua.estimare
				if alpha >= beta:
					break
	stare.estimare = stare.stare_aleasa.estimare

	return stare

def afis_daca_final(stare_curenta: Stare, timpi_gandire_calculator):
	final = stare_curenta.tabla_joc.final(stare_curenta.j_curent)
	if(final):
		if (final == "remiza"):
			print("Remiza!")
		else:
			print("A castigat "+final)

			# afisam date despre timpii de gandire ai calculatorului
			print(f"Timp minim de gantire {min(timpi_gandire_calculator)}")
			print(f"Timp maxim de gantire {max(timpi_gandire_calculator)}")
			print(f"Mediana timpilor de gantire {median(timpi_gandire_calculator)}")
			print(f"Media timpilor de gantire {mean(timpi_gandire_calculator)}")
		return True

	return False

def muta_piesa(de_mutat, linie, coloana, stare_curenta: Stare, cu_captura=False):

	stare_actualizata = Stare(copy.deepcopy(stare_curenta.tabla_joc),
							  stare_curenta.j_curent, stare_curenta.adancime, stare_curenta)

	stare_actualizata.tabla_joc.matr[de_mutat[0]][de_mutat[1]] = Joc.GOL
	x, y = de_mutat
	de_mutat = False

	# verific daca captureaza o piesa
	if cu_captura:
		x_sters = int((x + linie) / 2)
		y_sters = int((y + coloana) / 2)
		stare_actualizata.tabla_joc.matr[x_sters][y_sters] = Joc.GOL

	# plasez simbolul pe "tabla de joc"
	stare_actualizata.tabla_joc.matr[linie][coloana] = Joc.JMIN
	stare_actualizata.tabla_joc.deseneaza_grid()

	return stare_actualizata

def main():
	# initializare tabla
	tabla_curenta = Joc()
	print("Tabla initiala")
	print(str(tabla_curenta))

	# setari interf grafica
	pygame.init()
	pygame.display.set_caption('ASTAR Bina Mircea')

	ecran = pygame.display.set_mode(size=DISPLAY_SIZE)
	Joc.initializeaza(ecran)
	# initializare algoritm

	Joc.JMIN, tip_algoritm, ADANCIME_MAX = deseneaza_alegeri(
		ecran, tabla_curenta)

	Joc.JMAX = 'W' if Joc.JMIN == 'B' else 'B'

	# creare stare initiala
	stare_curenta = Stare(tabla_curenta, "W", ADANCIME_MAX)

	de_mutat = False
	posibile_mutari = []
	captura_in_progres = False
	tabla_curenta.deseneaza_grid()
	timpi_gandire_calculator = []
	t_inainte = int(round(time.time() * 1000))

	while True:

		if (stare_curenta.j_curent == Joc.JMIN):
			# muta jucatorul
			# [MOUSEBUTTONDOWN, MOUSEMOTION,....]
			# l=pygame.event.get()
			for event in pygame.event.get():
				if event.type == pygame.QUIT:
					pygame.quit()  # inchide fereastra
					sys.exit()

				elif event.type == pygame.KEYUP and event.key == pygame.K_r:
					print("RESTART JOC")
					tabla_curenta = Joc()
					stare_curenta = Stare(tabla_curenta, "W", ADANCIME_MAX)
					stare_curenta.tabla_joc.deseneaza_grid()

				elif event.type == pygame.KEYUP and event.key == pygame.K_u:
					print("UNDO JOC")
					if stare_curenta.parinte == None:
						print("Nu se poate da undo pentru ca este in starea initiala")
						continue
					stare_curenta = stare_curenta.parinte
					stare_curenta.tabla_joc.deseneaza_grid()

				elif event.type == pygame.MOUSEBUTTONDOWN:  # click

					pos = pygame.mouse.get_pos()  # coordonatele clickului

					for linie in range(Joc.NR_LINII):
						for coloana in range(Joc.NR_COLOANE):

							# verifica daca punctul cu coord pos se afla in dreptunghi(celula)
							if Joc.celuleGrid[linie][coloana].collidepoint(pos):
								###############################
								if stare_curenta.tabla_joc.matr[linie][coloana] == Joc.JMIN:
									if (de_mutat and linie == de_mutat[0] and coloana == de_mutat[1]):
										# daca am facut click chiar pe patratica selectata, o deselectez
										de_mutat = False
										posibile_mutari = []
										stare_curenta.tabla_joc.deseneaza_grid()
									else:
										de_mutat = (linie, coloana)
										posibile_mutari = stare_curenta.tabla_joc.pozitii_in_care_poate_muta(
											linie, coloana)
										# desenez gridul cu patratelul marcat
										stare_curenta.tabla_joc.deseneaza_grid(
											de_mutat, posibile_mutari)

								elif stare_curenta.tabla_joc.matr[linie][coloana] == Joc.GOL and de_mutat and (linie, coloana) in posibile_mutari:
									x, y = de_mutat

									# jucatorul muta o piesa intr-o pozitie posibila
									if captura_in_progres:
										stare_curenta = muta_piesa(
											de_mutat, linie, coloana, stare_curenta, cu_captura=True)
										if not stare_curenta.tabla_joc.are_capturi_din_punct(Joc.JMAX, linie, coloana):
											captura_in_progres = False
										else:
											posibile_mutari = stare_curenta.tabla_joc.pozitii_in_care_poate_muta(
												linie, coloana)
											t_dupa = int(
												round(time.time() * 1000))
											print("Jucatorul a \"gandit\" timp de " +
												  str(t_dupa-t_inainte)+" milisecunde.")

									else:
										if stare_curenta.tabla_joc.are_capturi_din_punct(Joc.JMAX, x, y):
											captura_in_progres = True
											posibile_mutari = stare_curenta.tabla_joc.pozitii_in_care_poate_muta(
												linie, coloana)

										stare_curenta = muta_piesa(
											de_mutat, linie, coloana, stare_curenta, cu_captura=captura_in_progres)

										if not captura_in_progres:
											t_dupa = int(
												round(time.time() * 1000))
											print("Jucatorul a \"gandit\" timp de " +
												  str(t_dupa-t_inainte)+" milisecunde.")

									# afisarea starii jocului in urma mutarii utilizatorului
									print("\nTabla dupa mutarea jucatorului")
									print(str(stare_curenta))

									if not stare_curenta.tabla_joc.are_capturi_din_punct(Joc.JMAX, linie, coloana):
										captura_in_progres = False

									# testez daca jocul a ajuns intr-o stare finala
									# si afisez un mesaj corespunzator in caz ca da
									if (afis_daca_final(stare_curenta, timpi_gandire_calculator)):
										break

									# S-a realizat o mutare. Schimb jucatorul cu cel opus
									if not captura_in_progres:
										stare_curenta.j_curent = Joc.jucator_opus(
											stare_curenta.j_curent)

		# --------------------------------
		else:  # jucatorul e JMAX (calculatorul)
			# Mutare calculator

			# preiau timpul in milisecunde de dinainte de mutare
			t_inainte = int(round(time.time() * 1000))
			if tip_algoritm == '1':
				stare_actualizata = min_max(stare_curenta)
			else:  # tip_algoritm==2
				stare_actualizata = alpha_beta(-500, 500, stare_curenta)
			stare_curenta.tabla_joc = stare_actualizata.stare_aleasa.tabla_joc
			print("Tabla dupa mutarea calculatorului")
			print(str(stare_curenta))

			stare_curenta.tabla_joc.deseneaza_grid()
			# preiau timpul in milisecunde de dupa mutare
			t_dupa = int(round(time.time() * 1000))
			timpi_gandire_calculator.append(t_dupa-t_inainte)
			print("Calculatorul a \"gandit\" timp de " +
				  str(t_dupa-t_inainte)+" milisecunde.")

			if (afis_daca_final(stare_curenta, timpi_gandire_calculator)):
				break

			# S-a realizat o mutare. Schimb jucatorul cu cel opus
			stare_curenta.j_curent = Joc.jucator_opus(stare_curenta.j_curent)

			# incep timerul pt jucator
			t_inainte = int(round(time.time() * 1000))

if __name__ == "__main__":
	main()
	while True:
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				pygame.quit()
				sys.exit()


import numpy as np

def read_file(filename):
    with open( filename, 'r') as f:
        data = [line.strip().split(' ') for line in f]

    return data

def check(myanswer, answer):
    if not np.array_equal(myanswer, answer):
        print('\n' + 'ERROR'.center(50,'*'))
        print('Correct answer:')
        print(answer)
        print('Returned answer:')
    else:
        print('Check passed!')
    print(myanswer)

def solve(data, is_part1=True):
    data = np.squeeze(data)
    import string
    score_dict = dict(zip(string.ascii_lowercase + string.ascii_uppercase,
                          np.linspace(1, 52, 52, dtype=int)))
    score = 0
    if is_part1:
        for line in data:
            comp1 = set(line[:len(line)//2])
            comp2 = set(line[len(line)//2:])

            score += score_dict[(comp1 & comp2).pop()]
    else:
        for idx in range(0, len(data), 3):
            elves = [set(data[idx + offset]) for offset in [0, 1, 2]]
            inter = elves[0] & elves[1] & elves[2]
            score += score_dict[inter.pop()]

    return score

test_case = read_file('test_case.txt')
puzz_input = read_file('puzzle_input.txt')

print('Part 1'.center(50,'-'))
check( solve(test_case), 157 )
print(solve(puzz_input))

print('\n\n' + 'Part 2'.center(50,'-'))
check( solve(test_case, False), 70 )
print(solve(puzz_input, False))


from httmock import all_requests, HTTMock, response

from pyzoopla.listing import PropertyHistoricalListing, PropertyListing

def test_listing_few_details():

    @all_requests
    def zoopla_mock(url, request):
        content = open('tests/test_data/listing.txt', 'r').read()
        return response(content=content, request=request)

    with HTTMock(zoopla_mock):
        results = PropertyListing(47902463)

    assert str(results) == 'https://ww2.zoopla.co.uk/for-sale/details/47902463'
    assert results.listing_id == 47902463
    assert results.slug == 'for-sale/details'

    data = results.details(dataframe=False)
    del data['date_generated']
    assert data == {
        'listing_id': 47902463,
        'description': "\n                        A lovely three, three bathroom bedroom third floor Marylebone "
                       "apartment in a prestigious mansion block with lift and porter. Beautifully presented "
                       "throughout, comprising a master bedroom with en suite shower room, two further double "
                       "bedrooms, two further shower rooms, and a large semi open plan kitchen/reception room with "
                       "dining area. Further features bright and charming rooms, neutral décor and ample storage.You "
                       "may download, store and use the material for your own personal use and research. You may not "
                       "republish, retransmit, redistribute or otherwise make the material available to any party or "
                       "make the same available on any website, online service or bulletin board of your own or of "
                       "any other party or make the same available in hard copy or in any other media without the "
                       "website owner's express prior written consent. The website owner's copyright must remain on "
                       "all reproductions of material taken from this website.\n                    ",
        'main_features': ['3                bedrooms', '3                bathrooms', '1                reception room',
                          'floor area1,163 sq. ft'],
        'more_features': [],
        'price_history': {'date': ['7th Jun 2018'],
                          'price': [2295000],
                          'detail': ['First listed']}
    }

def test_listing_more_details():

    @all_requests
    def zoopla_mock(url, request):
        content = open('tests/test_data/listing2.txt', 'r').read()
        return response(content=content, request=request)

    with HTTMock(zoopla_mock):
        results = PropertyListing(38834402)

    assert str(results) == 'https://ww2.zoopla.co.uk/for-sale/details/38834402'
    assert results.listing_id == 38834402
    assert results.slug == 'for-sale/details'

    data = results.details(dataframe=True).to_dict()
    del data['date_generated']
    assert data == {
        'listing_id': {0: 38834402},
        'description': {0: '\n                        Set within a superb portered building just south of Oxford '
                           'Street, this fantastic two bedroom, two bathroom apartment offers beautifully presented '
                           'living space with classic décor.A wealth of exclusive boutiques and eateries can be found '
                           'throughout Mayfair, Oxford Street and Regent Street offer world class shops and department'
                           ' stores. Hyde Park is also moments away.\n                    '},
        'main_features': {0: ['2                bedrooms']},
        'more_features': {0: ['Secure entry and lift access to the second floor',
                              'Generous reception room with lots of natural light',
                              'Separate modern kitchen with ample storage space',
                              'Master bedroom with fitted wardrobe and en suite',
                              'Good-sized second bedroom with fitted wardrobe',
                              'Well presented shower room',
                              'Large entrance hall with storage cupboards']},
        'price_history': {0: {'date': ['18th Apr 2018', '22nd Dec 2015', '19th Feb 2015', '29th Oct 2014'],
                              'price': [2300000, 2599000, 2800000, 3000000],
                              'detail': ['Price reduced by £299,000', 'Price reduced by £201,000',
                                         'Price reduced by £200,000', 'First listed']}}
    }

def test_historical_listing_details():

    @all_requests
    def zoopla_mock(url, request):
        content = open('tests/test_data/historical.txt', 'r').read()
        return response(content=content, request=request)

    with HTTMock(zoopla_mock):
        results = PropertyHistoricalListing(37047136)

    assert str(results) == 'Property history of 108 Shoreditch High Street, London E1 6JN, \n29th May 2015'
    assert results.listing_id == 37047136
    assert results.slug == 'property-history'

    data = results.details(dataframe=False)
    del data['date_generated']
    assert data == {
        'listing_id': 37047136,
        'description': ". . .                 This wonderfully bright and spacious one bedroom apartment occupies the "
                       "third floor of a sympathetically restored Victorian building.Offering approximately 734 sq. "
                       "Ft. Of space this larger than average one bedroom boasts a stylish finish in the form of "
                       "exposed brick work, wood flooring, double glazed sash windows and a bespoke kitchen with "
                       "Siemens ovens and induction hob.Comprising an open plan dual aspect kitchen and living space, "
                       "generous double bedroom with fitted wardrobes and a high quality bathroom with a large "
                       "walk-in shower and storage spaces.Enjoying a fantastic locationin the heart of vibrant "
                       "Shoreditch, home to an increasing number of boutique clothing shops, the Ace Hotel and an "
                       "array of excellent bars and restaurants. Fashionable Brick Lane and Columbia Road are also "
                       "close by.A number of transport links serve the property including Shoreditch High Street "
                       "(Overground) just a stone's throw away, Old Street Station (National Rail, Northern Line) and "
                       "the major hub of Liverpool Street.Offered with no onward chain.. .             . . . . ",
        'features': '. . 734 sq. Ft. One bedroom apartment. Victorian conversion. High specification finish. Exposed '
                    'brick/sash windows/wood flooring. Central Shoreditch location. . . . '
    }


import base64
import os
import uuid
from pathlib import Path

from rest_framework import serializers

class ImageConvertField(serializers.Serializer):
    def to_representation(self, value):
        return value

    def to_internal_value(self, data):
        filename = uuid.uuid4().hex + '.jpg'
        complete_path = os.path.join(
            Path(__file__).resolve().parent.parent,
            'media/recipes',
            filename
        )
        represent_path = os.path.join(
            'recipes',
            filename
        )
        data = data[data.find(",") + 1:]
        imgdata = base64.b64decode(data)
        with open(complete_path, 'wb') as f:
            f.write(imgdata)
        return represent_path


import tensorflow as tf
from tensorflow import keras
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Conv2D
import os
from tensorflow.python.framework import graph_io
import json

image_size = 128 # All images will be resized to 224x224
batch_size = 12
train_dir = r'E:\Estek-AIProject\INTEL-SOOP\RaspberryPiFolder\Images\Training\combinedForTrainingColour'
validation_dir = r'E:\Estek-AIProject\INTEL-SOOP\RaspberryPiFolder\Images\Training\ValidationSet'
# Rescale all images by 1./255 and apply image augmentation
train_datagen = keras.preprocessing.image.ImageDataGenerator(
                rescale=1./255)

validation_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)

# Flow training images in batches of 20 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
                train_dir,  # Source directory for the training images
                target_size=(image_size, image_size),
                batch_size=batch_size,
                # Since we use binary_crossentropy loss, we need binary labels
                class_mode='binary')

print(train_generator.class_indices)
print(train_generator.classes)
labels = train_generator.class_indices
labels = dict((v,k) for k,v in labels.items())
jason = json.dumps(labels)
f = open('labels.json', 'w')
f.write(jason)
f.close()


#!/usr/bin/env python
#
import os

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template

from django.utils import simplejson as json

from models import PhotoRecord
from KML import KML

import base64

class MainHandler(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
        self.response.out.write(template.render(path, {}))

class ImageServer(webapp.RequestHandler):
    def get(self):

        respKML = KML()

        q = PhotoRecord.all()
        results = q.fetch(25)
        for p in results:

            # set up description
            #
            desc = " <![CDATA[ \
                "
            if p.description:
                desc += "
Notlar

" + p.description + "
"
            desc += "]]>"

            if p.date:
                name = p.date
            else:
                name = "undef"

            respKML.addPlacemark(p.lat,p.lon,description = desc,name=name)

        respKML.close()

        self.response.headers["Content-Type"]= "application/vnd.google-earth.kml+xml"
        self.response.out.write(respKML.content)

    def post(self):

        pack = json.loads(self.request.body)

        r = PhotoRecord()

        r.lon = pack["lon"]
        r.lat = pack["lat"]
        r.photo = pack["photo"]
        r.sender = pack["sender"]
        r.date = pack["date"]
        r.description = pack["description"]

        r.put()

        self.response.out.write(json.dumps(pack))

class ContentServer(webapp.RequestHandler):
    def get(self,id):

        r = PhotoRecord.get_by_id(int(id))
        imStr = r.photo
        image = base64.b64decode(imStr)

        self.response.headers["Content-Type"] = "image/jpeg"
        self.response.out.write(image)

def main():
    application = webapp.WSGIApplication([
        ('/', MainHandler),
        ('/imageServer',ImageServer),
        ('/content/(.*)',ContentServer)],
        debug=True)
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()


from pyostie.parsers import *
from pyostie.insights_ext import *
from pyostie.convert import *
from pyostie.utils import *

class extract:

    def __init__(self, filename, insights=False, tess_path=None, extension=None):
        """
        :param filename:
        :param insights:
        :param tess_path:
        :param extension:

        :return:
        """
        self.file = filename
        self.insights = insights
        self.path = tess_path
        self.ext = extension

    # noinspection PyBroadException
    def start(self):
        """

        :return: Main function to start the process.
        """
        @extension_type_check(self.ext, str)
        def ext_type_check(extnsn):
            return extnsn
        ext = ext_type_check(self.ext)
        print(ext)

        if ext.upper() == "PDF":
            if isinstance(self.file, str):
                try:
                    pdf = PDFParser(self.file, insights=self.insights)
                    output = pdf.extract_pypdf2()
                    return output
                except Exception:
                    try:
                        pdf = PDFParser(self.file)
                        output = pdf.extract_pdfplumber()
                        return output
                    except Exception as ex:
                        raise ex

        elif ext == "CSV":
            if isinstance(self.file, str):
                csv_output = CSVParser(self.file)
                output = csv_output.extract_csv()
                return output

        elif ext == "TXT":
            if isinstance(self.file, str):
                txt = TXTParser(self.file)
                output = txt.extract_txt()
                return output

        elif ext == "XLSX":
            if isinstance(self.file, str):
                excel = XLSXParser(self.file)
                output = excel.extract_xlsx()
                return output

        elif ext == "DOCX":
            if isinstance(self.file, str):
                docx = DOCXParser(self.file)
                output = docx.extract_docx()
                return output

        elif ext == "JPG":
            if self.insights:
                image = generate_insights(self.file, df)
                output_df = image.generate_df()
                image = ImageParser(self.file)
                output = image.extract_image()
                return output_df, output

            elif not self.insights:
                image = ImageParser(self.file)
                output = image.extract_image()
                return output

        elif ext == "PPTX":
            pptx = PPTXParser(self.file)
            output = pptx.extract_pptx()
            return output


from itertools import chain, combinations
import sys
from dataclasses import dataclass, field
from typing import List

@dataclass
class Item:
    name: str
    cost: int
    damage: int
    armor: int

SHOP = dict(
    weapons=[
        Item(name="Dagger", cost=8, damage=4, armor=0),
        Item(name="Shortsword", cost=10, damage=5, armor=0),
        Item(name="Warhammer", cost=25, damage=6, armor=0),
        Item(name="Longsword", cost=40, damage=7, armor=0),
        Item(name="Greataxe", cost=74, damage=8, armor=0),
    ],
    armor=[
        Item(name="Leather", cost=13, damage=0, armor=1),
        Item(name="Chainmail", cost=31, damage=0, armor=2),
        Item(name="Splintmail", cost=53, damage=0, armor=3),
        Item(name="Bandedmail", cost=75, damage=0, armor=4),
        Item(name="Platemail", cost=102, damage=0, armor=5),
    ],
    rings=[
        Item(name="Damage +1", cost=25, damage=1, armor=0),
        Item(name="Damage +2", cost=50, damage=2, armor=0),
        Item(name="Damage +3", cost=100, damage=3, armor=0),
        Item(name="Defense +1", cost=20, damage=0, armor=1),
        Item(name="Defense +2", cost=40, damage=0, armor=2),
        Item(name="Defense +3", cost=80, damage=0, armor=3),
    ],
)

@dataclass
class Character:
    hit_points: int
    damage: int
    armor: int

@dataclass
class Scenario:
    cost: int
    player: Character
    items: List[Item] = field(default_factory=list)

def is_victor1(player: Character, boss: Character):
    # count how many turns the player survives
    player_dmg_per_turn = boss.damage - player.armor
    turns_to_kill_player = (player.hit_points + player_dmg_per_turn - 1) // player_dmg_per_turn
    # count how many turns the boss survives
    boss_dmg_per_turn = player.damage - boss.armor
    turns_to_kill_boss = (boss.hit_points + boss_dmg_per_turn - 1) // boss_dmg_per_turn
    return turns_to_kill_boss <= turns_to_kill_player

def is_boss_victor2(player: Character, boss: Character):
    # count how many turns the player survives
    player_dmg_per_turn = max(0, boss.damage - player.armor)
    if player_dmg_per_turn == 0:
        return False
    turns_to_kill_player = (player.hit_points + player_dmg_per_turn - 1) // player_dmg_per_turn
    # count how many turns the boss survives
    boss_dmg_per_turn = max(0, player.damage - boss.armor)
    if boss_dmg_per_turn == 0:
        return True
    turns_to_kill_boss = (boss.hit_points + boss_dmg_per_turn - 1) // boss_dmg_per_turn
    return turns_to_kill_boss > turns_to_kill_player

def parse_input(input):
    values = {}
    for line in input:
        key, value = line.rstrip().split(": ")
        values[key] = int(value)
    return Character(
        hit_points=values["Hit Points"], damage=values["Damage"], armor=values["Armor"]
    )

def compute_scenario():
    for weapon in SHOP["weapons"]:
        for armor in chain((None,), SHOP["armor"]):
            for ring1, ring2 in chain(
                ((None, None),),
                combinations(SHOP["rings"], 2),
                [(r[0], None) for r in combinations(SHOP["rings"], 1)],
            ):
                scenario = Scenario(cost=0, player=Character(hit_points=100, damage=0, armor=0))
                scenario.cost += weapon.cost
                scenario.player.damage += weapon.damage
                scenario.items.append(weapon)
                if armor:
                    scenario.cost += armor.cost
                    scenario.player.armor += armor.armor
                    scenario.items.append(armor)
                if ring1:
                    scenario.cost += ring1.cost
                    scenario.player.damage += ring1.damage
                    scenario.player.armor += ring1.armor
                    scenario.items.append(ring1)
                if ring2:
                    scenario.cost += ring2.cost
                    scenario.player.damage += ring2.damage
                    scenario.player.armor += ring2.armor
                    scenario.items.append(ring2)
                yield scenario

def main1(input):
    boss = parse_input(input)
    min_cost = 1e9
    best_scenario = None
    for scenario in compute_scenario():
        if scenario.cost > min_cost:
            continue
        if is_victor1(scenario.player, boss) and scenario.cost < min_cost:
            min_cost = scenario.cost
            best_scenario = scenario
    return min_cost, best_scenario

def main2(input):
    boss = parse_input(input)
    max_cost = 0
    worst_scenario = None
    for scenario in compute_scenario():
        if scenario.cost < max_cost:
            continue
        if is_boss_victor2(scenario.player, boss) and scenario.cost > max_cost:
            max_cost = scenario.cost
            worst_scenario = scenario
    return max_cost, worst_scenario

if __name__ == "__main__":
    input = sys.stdin
    main = main2 if "--two" in sys.argv else main1
    print(main(input), file=sys.stdout)


#!/usr/bin/env python3

from rhapi import RhApi
import pprint
import json
from datetime import datetime, timezone
import matplotlib.pyplot as plt
import calendar
import sys

class cumulativePlot:

    def __init__(self, hybridtype, dtbs):

        self.hybridtype  = hybridtype
        self.dtbs = dtbs

    def ConnectProductionDB(self):

        db_client = RhApi('https://cmsdca.cern.ch/trk_rhapi', False, sso='login')

        if self.hybridtype == "2SFEH":
            print("\n###  Creating query to get 2SFEH data from production database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_cmsr.c9800 c')
            response = db_client.json2(query) # Get a JSON object

        elif self.hybridtype == "2SSEH":
            print("\n###  Creating query to get 2SSEH data from production database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_cmsr.c12600 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSFEH":
            print("\n###  Creating query to get PSFEH data from production database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_cmsr.c9820 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSROH":
            print("\n###  Creating query to get PSROH data from production database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_cmsr.c9840 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSPOH":
            print("\n###  Creating query to get PSPOH data from production database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_cmsr.c13000 c')
            response = db_client.json2(query)

        return response

    def ConnectDevelopmentDB(self):

        db_client = RhApi('https://cmsdca.cern.ch/trk_rhapi', False, sso='login')
    #    pprint = pprint.PrettyPrinter(indent=4)

        if self.hybridtype == "2SFEH":
            print("\n###  Creating query to get 2SFEH data from development database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_int2r.c16220 c')
            response = db_client.json2(query)

        elif self.hybridtype == "2SSEH":
            print("\n###  Creating query to get 2SSEH data from development database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_int2r.c20220 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSFEH":
            print("\n###  Creating query to get PSFEH data from development database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_int2r.c16240 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSROH":
            print("\n###  Creating query to get PSROH data from development database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_int2r.c16260 c')
            response = db_client.json2(query)

        elif self.hybridtype == "PSPOH":
            print("\n###  Creating query to get PSPOH data from development database ###\n")
            query = ('SELECT c.START_TIME, c.END_TIME, c.QUALIFICATION FROM trker_int2r.c21020 c')
            response = db_client.json2(query)

        return response

    def GettingInfo(self):

        tInspection = []
        NumberOfInspected = []
        NumberOfInspectedGood = []
        NumberOfInspectedBad = []
        QualityOfHybrid = []
        EndTime = []
        StartTime = []

        if self.dtbs == "production":

            response = self.ConnectProductionDB()

        elif self.dtbs == "development":

            response = self.ConnectDevelopmentDB()

        else:

            print("No correct database")

        for rr in response['data']:

            StartTime.append(rr["startTime"])
            EndTime.append(rr["endTime"])
            QualityOfHybrid.append(rr["qualification"])

        StartTime.sort(reverse=False)
        EndTime.sort(reverse=False)

        n = len(StartTime)
        for i in range(n):
            stt_time = datetime.fromisoformat(StartTime[i])
            end_time = datetime.fromisoformat(EndTime[i])
      #      tInspection.append(stt_time.strftime('Year:%y\nMonth:%m\nDay:%d'))
            tInspection.append(stt_time.strftime('%y\n%m\n%d'))

        j = 0
        nn = 0
        while j < n:
            if  QualityOfHybrid[j] == 'Bad' or QualityOfHybrid[j] == 'Good':
                nn+=1
                NumberOfInspected.append(nn)
            else:
                nn = nn
                NumberOfInspected.append(nn)
            j+=1

        m = 0
        ngood = 0
        while m < n:
            if QualityOfHybrid[m] == 'Good':
                ngood+=1
                NumberOfInspectedGood.append(ngood)
            else:
                ngood = ngood
                NumberOfInspectedGood.append(ngood)
            m+=1

        mm = 0
        nbad = 0
        while mm < n:
            if QualityOfHybrid[mm] == 'Bad':
                nbad+=1
                NumberOfInspectedBad.append(nbad)
            else:
                nbad = nbad
                NumberOfInspectedBad.append(nbad)
            mm+=1

        return tInspection, NumberOfInspected, NumberOfInspectedGood, NumberOfInspectedBad, QualityOfHybrid

    def plotting(self):

        tInspection, NumberOfInspected, NumberOfInspectedGood, NumberOfInspectedBad,QualityOfHybrid = self.GettingInfo()

        plt.figure(1)
        plt.tick_params(labelright=True, labelleft=True, length=5, width=2, direction='in')
        plt.ylabel('Number of inspected hybrids')
        pltTitle = "CMSDB,"+self.dtbs+", "+self.hybridtype
        plt.title(pltTitle)
        plotNTotal = plt.plot(tInspection,NumberOfInspected, label='Total Number of Inspected')
        plotNGood = plt.plot(tInspection,NumberOfInspectedGood, label='Number of Good Hybrids')
        plotNBad = plt.plot(tInspection,NumberOfInspectedBad, label='Number of Bad Hybrids')
        plt.setp(plotNBad, linestyle='--')
        plt.setp(plotNBad, linewidth=1, color='g')
        plt.xticks(tInspection, tInspection, rotation =30, fontsize=6)
        plt.legend(loc='upper left')
        outputplot = "output/NInspectedHybrid_Time_"+self.hybridtype+self.dtbs+".pdf"
        plt.savefig(outputplot)
        outputplot = "output/NInspectedHybrid_Time_"+self.hybridtype+self.dtbs+".png"
        plt.savefig(outputplot)
        plt.close()
        with open('output/lastrun.txt', 'w') as f:
            f.write('%s (UTC)' % datetime.now(timezone.utc))

        tInspection.clear()
        NumberOfInspected.clear()
        NumberOfInspectedGood.clear()
        NumberOfInspectedBad.clear()
        QualityOfHybrid.clear()

 #      plt.show()

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

tsfh_pr = cumulativePlot("2SFEH","production")
tsfh_pr.plotting()
del tsfh_pr

tssh_pr = cumulativePlot("2SSEH","production")
tssh_pr.plotting()
del tssh_pr

psfh_pr = cumulativePlot("PSFEH","production")
psfh_pr.plotting()
del psfh_pr

psrh_pr = cumulativePlot("PSROH","production")
psrh_pr.plotting()
del psrh_pr

psph_pr = cumulativePlot("PSPOH","production")
psph_pr.plotting()
del psph_pr

tsfh_de = cumulativePlot("2SFEH","development")
tsfh_de.plotting()
del tsfh_de

tssh_de = cumulativePlot("2SSEH","development")
tssh_de.plotting()
del tssh_de

psfh_de = cumulativePlot("PSFEH","development")
psfh_de.plotting()
del psfh_de

psrh_de = cumulativePlot("PSROH","development")
psrh_de.plotting()
del psrh_de

psph_de = cumulativePlot("PSPOH","development")
psph_de.plotting()
del psph_de


"""
Tries to buy a specified product (by its index on a page, `product_number`) until it succeeds (retries every 10 minutes)
"""
from pprint import pprint
import time

if __name__ == "__main__":
    from config import USER_INFO
    from shop import Shop

    shop = Shop("http://some-website")
    catalog_url = "/shop/all/tops_sweaters"
    product_number = 21

    while True:
        product_url = shop.get_all_product_urls(catalog_url=catalog_url)[
            product_number - 1
        ]
        product = shop.buy_product(
            product_url=product_url,
            product_style="White",
            product_size="XLarge",
            user_info=USER_INFO,
        )
        if product is not None:
            pprint(product)
            break

        # Retry in 10 minutes
        time.sleep(10 * 60)


from glob import glob
from os.path import isabs, basename, join as pjoin

from lightflow.queue import JobType
from lightflow.logger import get_logger
from lightflow.models import BaseTask, TaskParameters, Action
from .exceptions import LightflowFilesystemPathError

logger = get_logger(__name__)

class GlobTask(BaseTask):
    """ Returns list of files from path using glob. """
    def __init__(self, name, paths, callback, pattern='*', recursive=False,
                 return_abs=True, *, queue=JobType.Task,
                 callback_init=None, callback_finally=None,
                 force_run=False, propagate_skip=True):
        """ Initialize the glob task object.

        All task parameters except the name, callback, queue, force_run and propagate_skip
        can either be their native type or a callable returning the native type.

        Args:
            name (str): The name of the task.
            paths (str/list/callable): A path, or list of paths, to look in for files.
                                       The paths have to be absolute paths, otherwise an
                                       exception is thrown. This parameter can either be
                                       a string, a list of strings or a callable that
                                       returns a string or a list of strings.
            callback (callable): A callable object that is called with the result of the
                                 glob operation. The function definition is
                                 def callback(files, data, store, signal, context).
            pattern (str): The glob style pattern to match when returning files.
            recursive (bool): Recursively look for files. Use ** to match any files
                              and zero or more directories and subdirectories.
                              May slow things down if lots of files.
            return_abs (bool): If True return absolute paths,
                               if False return filename only.
            queue (str): Name of the queue the task should be scheduled to. Defaults to
                         the general task queue.
            callback_init (callable): A callable that is called shortly before the task
                                      is run. The definition is:
                                        def (data, store, signal, context)
                                      where data the task data, store the workflow
                                      data store, signal the task signal and
                                      context the task context.
            callback_finally (callable): A callable that is always called at the end of
                                         a task, regardless whether it completed
                                         successfully, was stopped or was aborted.
                                         The definition is:
                                           def (status, data, store, signal, context)
                                         where status specifies whether the task was
                                           success: TaskStatus.Success
                                           stopped: TaskStatus.Stopped
                                           aborted: TaskStatus.Aborted
                                           raised exception: TaskStatus.Error
                                         data the task data, store the workflow
                                         data store, signal the task signal and
                                         context the task context.
            force_run (bool): Run the task even if it is flagged to be skipped.
            propagate_skip (bool): Propagate the skip flag to the next task.
        """
        super().__init__(name, queue=queue,
                         callback_init=callback_init, callback_finally=callback_finally,
                         force_run=force_run, propagate_skip=propagate_skip)

        self.params = TaskParameters(
            paths=paths,
            pattern=pattern,
            recursive=recursive,
            return_abs=return_abs
        )
        self._callback = callback

    def run(self, data, store, signal, context, **kwargs):
        """ The main run method of the glob task.

        Args:
            data (MultiTaskData): The data object that has been passed from the
                                  predecessor task.
            store (DataStoreDocument): The persistent data store object that allows the
                                       task to store data for access across the current
                                       workflow run.
            signal (TaskSignal): The signal object for tasks. It wraps the construction
                                 and sending of signals into easy to use methods.
            context (TaskContext): The context in which the tasks runs.

        Raises:
            LightflowFilesystemPathError: If the specified path is not absolute.

        Returns:
            Action: An Action object containing the data that should be passed on
                    to the next task and optionally a list of successor tasks that
                    should be executed.
        """
        params = self.params.eval(data, store)
        paths = [params.paths] if isinstance(params.paths, str) else params.paths

        if not all([isabs(path) for path in paths]):
            raise LightflowFilesystemPathError(
                'The specified path is not an absolute path')

        files = [file if params.return_abs else basename(file) for path in paths
                 for file in glob(pjoin(path, params.pattern),
                                  recursive=params.recursive)]

        if self._callback is not None:
            self._callback(files, data, store, signal, context)

        return Action(data)


import math
import torch
import numpy as np
import torch.nn as nn
from torch.nn.parameter import Parameter
from torch.nn.modules.module import Module
from torch_geometric.nn import MessagePassing

class Graphsn_GCN_MM(Module):
    def __init__(self, in_features, out_features, bias=True):
        super(Graphsn_GCN_MM, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.FloatTensor(in_features, out_features))
        self.eps = nn.Parameter(torch.FloatTensor(1))
        if bias:
            self.bias = Parameter(torch.FloatTensor(out_features))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        stdv = 0.9 / math.sqrt(self.weight.size(1))
        self.weight.data.uniform_(-stdv, stdv)

        stdv_eps = 0.21 / math.sqrt(self.eps.size(0))
        nn.init.constant_(self.eps, stdv_eps)

        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    def forward(self, input, sc):
        v = (self.eps) * torch.diag(sc)
        mask = torch.diag(torch.ones_like(v))
        # print(f"v = {v}")
        # print(f"\tmask = {mask}")
        sc = mask * torch.diag(v) + (1. - mask) * sc
        support = torch.mm(input, self.weight)
        output = torch.spmm(sc, support)
        if self.bias is not None:
            return output + self.bias
        else:
            return output

    def __repr__(self):
        return self.__class__.__name__ + ' (' \
               + str(self.in_features) + ' -> ' \
               + str(self.out_features) + ')'

class Graphsn_GCN_MP(MessagePassing):
    def __init__(self, in_features, out_features, bias=True):
        super(Graphsn_GCN_MP, self).__init__(aggr='add')
        self.in_features = in_features
        self.out_features = out_features

        self.weight = nn.Parameter(torch.FloatTensor(in_features, out_features))
        self.eps = nn.Parameter(torch.FloatTensor(1))

        if bias:
            self.bias = nn.Parameter(torch.FloatTensor(out_features))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        stdv = 0.9 / math.sqrt(self.weight.size(1))
        self.weight.data.uniform_(-stdv, stdv)

        stdv_eps = 0.21 / math.sqrt(self.eps.size(0))
        nn.init.constant_(self.eps, stdv_eps)

        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    def forward(self, x, edge_index, edge_weight):
        # print(f"\tedge_index.shape = {edge_index.shape}")
        v = (self.eps) * torch.diag(edge_weight)
        mask = torch.diag(torch.ones_like(v))
        edge_weight = mask * torch.diag(v) + (1. - mask) * edge_weight

        support = torch.matmul(x, self.weight)
        output = self.propagate(edge_index, x=support, edge_weight=edge_weight)

        if self.bias is not None:
            output = output + self.bias

        return output

    def message(self, x_j, edge_weight):
        return edge_weight.view(-1, 1) * x_j

    # def update(self, aggr_out):
    #     print(f"\taggr_out.shape = {aggr_out.shape}")
    #     return aggr_out

    def __repr__(self):
        return self.__class__.__name__ + ' (' \
               + str(self.in_features) + ' -> ' \
               + str(self.out_features) + ')'


"""
This module creates and maintains HTTP request ecosystem.
It now uses requests for easy compatibility acroos python versions
"""
from __future__ import (absolute_import, division,
                        print_function, unicode_literals)
from contextlib import contextmanager
import logging
import time

import requests
import requests.packages.urllib3
from requests_toolbelt.auth.http_proxy_digest import HTTPProxyDigestAuth

from vms import logger_name

requests.packages.urllib3.disable_warnings()
LOG_NAME = logger_name + ".request"
LOG = logging.getLogger(LOG_NAME)
_REQUEST_ATTRS = {"verify": False, "auth": None, "proxies": {}}

def init(settings):
    """
    Initialize important settings related to request framework
    :param settings: A dict with following keys:
                       - insecure. Boolean. Should HTTPS verification be off
                       - use_socks. Boolean. Use a socks proxy
                       - username. String. Proxy auth username.
                                   If not given, no auth is setup
                       - password. String. Proxy auth pass.
                       - proxy_server. String. Full name of the proxy server
                                       Including port
                       - proxy_auth. String. One of "basic" or "digest"
    """
    # Insecure setting
    insecure = settings.get("insecure", False)
    # Check if proxy is required
    pr = settings.get("proxy_server", "")
    proxies = {}
    auth = None
    global _REQUEST_ATTRS
    _REQUEST_ATTRS["init"] = True
    if not pr:
        # No proxy and no auth
        pass
    else:
        user = settings.get("username", "")
        pass_ = settings.get("password", "")
        if not user:
            noauth = True
        else:
            noauth = False
        use_socks = settings.get("use_socks", False)
        proxy_auth = settings.get("proxy_auth", "basic")
        # Build the auth string first
        pr_str = ""
        set_digest_auth = False
        if not noauth:
            if proxy_auth == "basic" or use_socks:
                pr_str = "{0}:{1}@{2}".format(user, pass_, pr)
            else:
                set_digest_auth = True
        if not pr_str:
            pr_str = "{0}".format(pr)
        if use_socks:
            pr_str = "socks5h://{0}".format(pr_str)
            proxies = {"http": pr_str, "https": pr_str}
        else:
            proxies = {"http": "http://{0}".format(pr_str),
                       "https": "https://{0}".format(pr_str)}
        if set_digest_auth:
            auth = HTTPProxyDigestAuth(user, pass_)
    _REQUEST_ATTRS["verify"] = not insecure
    _REQUEST_ATTRS["auth"] = auth
    _REQUEST_ATTRS["proxies"] = proxies

def _get_request_obj():
    global _REQUEST_ATTRS
    r = requests.Session()
    if _REQUEST_ATTRS["auth"] is not None:
        r.auth = _REQUEST_ATTRS["auth"]
    if _REQUEST_ATTRS["proxies"]:
        r.proxies = _REQUEST_ATTRS["proxies"]
    r.verify = _REQUEST_ATTRS["verify"]
    return r

@contextmanager
def get_request_obj():
    try:
        s = _get_request_obj()
        yield s
    finally:
        s.close()

def _open_url(req_ses, url, timeout=5):
    """
    Open a URL and return response

    :param req_ses: A Requests.session object
    :param url: - The URL to open
    :param timeout: The timeout interval in seconds Defaults to 2.

    Returs repsonse of the website as unicode or raises IOError if
    URL can't be opened
    """
    if "://" not in url:
        url = "http://" + url
    error = None
    try:
        resp = req_ses.get(url, timeout=timeout)
        resp.raise_for_status()
    except requests.Timeout:
        LOG.warning("Request Timed out for URL {0}".format(url))
        error = "Timeout"
    except requests.HTTPError as e:
        error = "{0}".format(e)
    except requests.ConnectionError:
        error = "Connection Failed"
    if error is None:
        return resp.text
    else:
        raise IOError(error)

def open_url(req_ses, url, timeout=5, retries=3):
    """
    Opens a URL and return response.
    This function is a wrapper around the _open_url call

    :param req_ses: A Requests.session object
    :param url: - The URL to open
    :param timeout: The timeout interval in seconds Defaults to 2.
    :param retries: Number of times we should retry a call

    Returs repsonse of the website as unicode or raises IOError if
    URL can't be opened
    """
    count = 1
    err = False
    while True:
        try:
            resp = _open_url(req_ses, url, timeout=timeout)
        except IOError as e:
            msg = "HTTP call failed with error: {0}.".format(e)
            count += 1
            if count <= retries:
                msg = msg + " Will Retry"
            else:
                msg = msg + " Retries exhausted. Giving up"
                err = True
            LOG.debug(msg)
            if err:
                raise
            else:
                time.sleep(0.2)
        else:
            return resp


class Farmer:

    def __init__(self,name,crop,earning):

        self.name=name
        self.crop=crop
        self.earning=earning

    def display(self):
        print('name : {} \n crop : {} \n earning : {}'.format(self.name,self.crop,self.earning))

obj1=Farmer('ram','wheat',80000.00)
obj2=Farmer('shyam','corn',120000.00)

if obj1.earning > obj2.earning:
    obj1.display()
else:
    obj2.display()


# creating snake class as a child class of reptile

from reptile import Reptile

class Snake(Reptile):

    def __init__(self):
        super().__init__()
        self.cold_blooded = True
        self.forked_tongue = True
        self.venom = None
        self.limbs = False

    def use_tongue_to_smell(self):
        return "I can smell the taste :) "

# snake_object = Snake()
# print(snake_object.use_tongue_to_smell()) # method from Snake class
# print(snake_object.seek_heat()) # method from Reptile class
# print(snake_object.venom()) # method from Animal class


from torch import nn

class Unit(nn.Module):
    def __init__(self, argslist, in_channels, out_channels):
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_sizes = argslist[0]  # problem ?
        self.stride_pixels = argslist[1]
        self.padding_pixels = argslist[2]
        self.dropout_factor = argslist[3]

        super(Unit, self).__init__()

        self.conv = nn.Conv2d(in_channels=self.in_channels,
                              out_channels=self.out_channels,
                              kernel_size=self.kernel_sizes,
                              stride=self.stride_pixels,
                              padding=self.padding_pixels)
        self.bn = nn.BatchNorm2d(num_features=self.out_channels)
        self.relu = nn.ReLU()
        # self.do = nn.Dropout(p=self.dropout_factor)

    def forward(self, input):
        output = self.conv(input)
        output = self.bn(output)
        output = self.relu(output)
        # output = self.do(output)

        return output


# This scripts collects timeout info for 405 models under -timout 700 in 12 experiments

__author__ = "Elaheh"
__date__ = "$Feb 1, 2016 5:51:01 PM$"

import os, sys, shelve

TIMEOUT_DIR = os.path.join('timedout1', 'timedout_results')
INFO = 'timeout_info.txt'
SETTINGS = ['z3_both', 'z3_k_ind', 'z3_pdr',
            'yices_both', 'yices_k_ind', 'yices_pdr',
            'smtinterpol_both', 'smtinterpol_k_ind', 'smtinterpol_pdr',
            'mathsat_both', 'mathsat_k_ind', 'mathsat_pdr']

if not os.path.exists(TIMEOUT_DIR):
    print(TIMEOUT_DIR + " does not exists!")
    sys.exit(-1)

#
# Collect data
#

info = []
for i in range(len(SETTINGS)):
    info.append(0)

for dir in os.listdir(TIMEOUT_DIR):
    reader = open(os.path.join(TIMEOUT_DIR, dir, INFO))
    for line in reader.readlines():
        for i, setting in enumerate(SETTINGS):
            if setting in line:
                info[i] += 1
        if 'ts = 12' in line:
            print(dir)
    reader.close()

#
# Report
#

'''writer = open('timeout_info_for_exp1.txt', 'w')
writer.write('total number of the experiments that timed out is: ' + str(len(os.listdir(TIMEOUT_DIR))))
writer.write('\nnumber of timeouts for each setting is as follows:\n')

print('total number of the experiments that timed out is: ' + str(len(os.listdir(TIMEOUT_DIR))))
print('number of timeouts for each setting is as follows:')
for i, setting in enumerate(SETTINGS):
    print(setting + ':  ' + str(info[i]))
    writer.write(setting + ':  ' + str(info[i]) + '\n')'''


from flask import render_template, request
from werkzeug.utils import secure_filename
from os import path
from config import app
from models.employee import Employee

class EmployeeController(object):

    @staticmethod
    @app.route('/', methods=['GET', 'POST'])
    def add():
        if request.method == 'GET':
            return render_template('home/index.html')
        elif request.method == 'POST':
            name = request.form.get('name')
            surname = request.form.get('surname')
            file = request.files.get('photo')
            filename = secure_filename(file.filename)
            extensions = ['png', 'jpg', 'jpeg', 'gif', 'bmp']
            size_limit = 10 * 1024 * 1024  # 10Mb
            if filename.split('.')[-1] not in extensions:
                message = 'Wrong file format!'
                mess_color = 'red'
            elif len(file.read()) > size_limit:
                message = f'Size limit of {size_limit} Mb'
                mess_color = 'red'
            else:
                local_dir = path.dirname(path.abspath(__file__))
                root_dir = path.abspath(local_dir + '\\..\\')
                save_dir = root_dir + '\\media\\upload'
                save_path = path.join(save_dir, filename)
                file.stream.seek(0)
                file.save(save_path)

                photo = f'../media/upload/{filename}'
                Employee.add_employee(name, surname, photo)

                message = 'Employee successfully added'
                mess_color = 'green'

            return render_template('home/index_info.html', context={
                'message': message,
                'mess_color': mess_color
            })
        # else:
        #     return render_template('access/page403.html')


'''
This file contains custom managers and querysets for various core models.
'''
from django.db import models

from danceschool.core.constants import getConstant

class InvoiceQuerySet(models.QuerySet):
    '''
    Only preliminary invoices may be deleted.  Paid invoices are ignored, while
    all other invoices are cancelled.
    '''

    def delete(self):
        self.exclude(status__in=[
            self.model.PaymentStatus.preliminary, self.model.PaymentStatus.paid
        ]).update(
            status=self.model.PaymentStatus.cancelled
        )
        filtered_query = self.__deepcopy__({}).filter(
            status=self.model.PaymentStatus.preliminary
        )
        super(InvoiceQuerySet, filtered_query).delete()

class InvoiceManager(models.Manager):
    ''' Use InvoiceQuerySet to allow deletion only of preliminary invoices. '''
    def get_queryset(self):
        return InvoiceQuerySet(self.model, using=self._db)

class SeriesTeacherManager(models.Manager):
    '''
    Limits SeriesTeacher queries to only staff reported as teachers, and ensures that
    these individuals are reported as teachers when created.
    '''

    def get_queryset(self):
        return super().get_queryset().filter(
            category=getConstant('general__eventStaffCategoryInstructor')
        )

    def create(self, **kwargs):
        kwargs.update({
            'category': getConstant('general__eventStaffCategoryInstructor').id,
            'occurrences': kwargs.get('event').eventoccurrence_set.all(),
        })
        return super().create(**kwargs)

class SubstituteTeacherManager(models.Manager):
    '''
    Limits SeriesTeacher queries to only staff reported as teachers, and ensures that
    these individuals are reported as teachers when created.
    '''

    def get_queryset(self):
        return super().get_queryset().filter(
            category=getConstant('general__eventStaffCategorySubstitute')
        )

    def create(self, **kwargs):
        kwargs.update({
            'category': getConstant('general__eventStaffCategorySubstitute').id})
        return super().create(**kwargs)

class EventDJManager(models.Manager):
    '''
    Limits Dj queries to only staff reported as DJs, and ensures that
    these individuals are reported as DJs when created.
    '''

    def get_queryset(self):
        return super().get_queryset().filter(
            category=getConstant('general__eventStaffCategoryDJ')
        )

    def create(self, **kwargs):
        kwargs.update({
            'category': getConstant('general__eventStaffCategoryDJ').id,
        })
        return super().create(**kwargs)

class SeriesStaffManager(models.Manager):
    '''
    Limits SeriesStaff queries to exclude SeriesTeachers and SubstituteTeachers
    '''

    def get_queryset(self):
        return super().get_queryset().exclude(
            category__in=[
                getConstant('general__eventStaffCategoryInstructor'),
                getConstant('general__eventStaffCategorySubstitute'),
            ],
        )


from time import time
start = time()

with open('day19input.txt', 'r') as fin:
    tmp = fin.read().split('\n')
    routing_diagram = list()
    for row in tmp:
        routing_diagram.append(list(row))

def find_start(diagram):
    for i in range(len(diagram[0])):
        if diagram[0][i] == '|':
            return i

def next_step(diagram, x, y, direction, letters):
    if diagram[y][x] == '+':
        if direction == 'up' or direction == 'down':
            if x == 0:
                direction = 'right'
            elif x == len(diagram[y]) - 1:
                direction = 'left'
            elif diagram[y][x+1] == '-' or diagram[y][x+1].isalpha():
                direction = 'right'
            else:
                direction = 'left'
        else:
            if y == 0:
                direction = 'down'
            elif y == len(diagram) - 1:
                direction = 'up'
            elif diagram[y+1][x] == '|' or diagram[y+1][x].isalpha():
                direction = 'down'
            else:
                direction = 'up'
    elif diagram[y][x].isalpha():
        letters.append(diagram[y][x])
    if direction == 'down':
        y += 1
    elif direction == 'up':
        y -= 1
    elif direction == 'left':
        x -= 1
    else:
        x += 1
    return x, y, direction, letters

def solution():
    found_letters = list()
    direction = 'down'
    x = find_start(routing_diagram)
    y = 0
    count_steps = 0
    while 0 <= x <= len(routing_diagram[y]) - 1 and 0 <= y <= len(routing_diagram) - 1:
        count_steps += 1
        # print(x, y, found_letters)
        x, y, direction, found_letters = next_step(routing_diagram, x, y, direction, found_letters)
    print(''.join(found_letters), count_steps - 1)

solution()

print('finished in ' + str(time() - start) + ' seconds')


from django.shortcuts import render

# Create your views here.

import requests
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
    response = HttpResponse()
    response.writelines("test")
    return response

def binCheck(request):
    response = HttpResponse()
    dataDict = request.GET
    try:
        bin = dataDict['bin']
        binCheckUrl = "https://lookup.binlist.net/"
        res = requests.get(binCheckUrl + bin )
        if res.status_code == 200:
            response.writelines(res.text)
            return response

    except:
        bin = ''

    response.writelines(bin)
    return response


import pytest

from db.models.manga import Manga

@pytest.fixture
async def seed_manga(session) -> Manga:
    manga = Manga(
        title="Title",
        title_slug="title",
    )
    session.add(manga)
    await session.commit()
    await session.refresh(manga)
    yield manga


from __future__ import print_function
import numpy
import cmor
from time import localtime, strftime
today = strftime("%Y%m%d", localtime())

print('Done importing')
try:
    import cdms2
except BaseException:
    print("This test code needs cdms2 interface for i/0")
    import sys
    sys.exit()
import os

dpth = "data"
myaxes = numpy.zeros(9, dtype='i')
myaxes2 = numpy.zeros(9, dtype='i')
myvars = numpy.zeros(9, dtype='i')

def read_input(var, order=None):
    f = cdms2.open(os.path.join(dpth, "%s_sample.nc" % var))
    ok = f(var)
    if order is None:
        s = f(var)
    else:
        s = f(var, order=order)
    s.units = f[var].units
    s.id = var
    f.close()
    return s, ok

def prep_var(data):
    rk = data.rank()
    axes = []
    for i in range(rk):
        ax = data.getAxis(i)
        if ax.isLongitude():
            id = cmor.axis(
                table_entry='longitude',
                units=ax.units,
                coord_vals=ax[:],
                cell_bounds=ax.getBounds())
        elif ax.isLatitude():
            id = cmor.axis(
                table_entry='latitude',
                units=ax.units,
                coord_vals=ax[:],
                cell_bounds=ax.getBounds())
        else:
            id = cmor.axis(
                table_entry=str(
                    ax.id),
                units=ax.units,
                coord_vals=ax[:],
                cell_bounds=ax.getBounds())
            print(i, 'units:', ax.units, ax[0])
        axes.append(id)
    var = cmor.variable(table_entry=data.id,
                        units=data.units,
                        axis_ids=numpy.array(axes),
                        missing_value=data.missing_value,
                        history="rewrote by cmor via python script")
    return var

def prep_cmor():
    cmor.setup(
        inpath="Tables",
        set_verbosity=cmor.CMOR_QUIET,
        netcdf_file_action=cmor.CMOR_REPLACE,
        exit_control=cmor.CMOR_EXIT_ON_MAJOR)
    cmor.dataset_json("Test/CMOR_input_example.json")

    tables = []
    a = cmor.load_table("CMIP6_Omon.json")
    tables.append(a)
    tables.append(cmor.load_table("CMIP6_Amon.json"))
    return

for var in ['tas', ]:
    print('Testing var:', var)
    orders = ['tyx...', 'txy...', 'ytx...', 'yxt...', 'xyt...', 'xty...', ]
    for o in orders:
        print('\tordering:', o)
        data, data_ordered = read_input(var, order=o)
        prep_cmor()
        print(data.shape)
        var_id = prep_var(data)
        df = data.filled(data.missing_value)
        cmor.write(var_id, df)
        cmor.close()
#        fn = "CMIP6/CMIP/CSIRO-BOM/NICAM/piControl/r1i1p1f1/Amon/%s/gn/v%s/%s_Amon_piControl_NICAM_r1i1p1f1_gn_197901-199605.nc" %(var,today,var)
        fn = "CMIP6/CMIP6/ISMIP6/PCMDI/PCMDI-test-1-0/piControl-withism/r3i1p1f1/Amon/%s/gn/v%s/%s_Amon_PCMDI-test-1-0_piControl-withism_r3i1p1f1_gn_197901-199605.nc" % (
            var, today, var)
        f = cdms2.open(fn)
        s = f(var)
        if not numpy.allclose(s, data_ordered):
            raise "Error reordering: %s" % o
        else:
            print('order: %s, passed' % o)
        f.close()
print('Done')
# cmor.close()
print('Finito')


# import the necessary packages
#coding:utf8
from skimage.measure import compare_ssim
from skimage.measure import compare_psnr
import argparse
import imutils
import cv2

# construct the argument parse and parse the arguments
# ap = argparse.ArgumentParser()
# ap.add_argument("-f", "--first", required=True,
#                 help="first input image")
# ap.add_argument("-s", "--second", required=True,
#                 help="second")
# args = vars(ap.parse_args())

# load the two input images
imageAPath = r"./stitching_by_opencv.jpg"
imageBPath = r"./stitching_by_ICE.jpg"
imageCPath = r"./stitching_by_PanoramaStudio.jpg"
imageA = cv2.imread(imageAPath)
imageB = cv2.imread(imageBPath)
imageC = cv2.imread(imageCPath)

# convert the images to grayscale
imageA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
imageB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
imageC = cv2.cvtColor(imageC, cv2.COLOR_BGR2GRAY)

imageA = cv2.resize(imageA, (3800,2533),interpolation=cv2.INTER_CUBIC)
imageB = cv2.resize(imageB, (3800,2533),interpolation=cv2.INTER_CUBIC)
imageC = cv2.resize(imageC, (3800,2533),interpolation=cv2.INTER_CUBIC)
# compute the Structural Similarity Index (SSIM) between the two
# images, ensuring that the difference image is returned

PSNR_val = compare_psnr(imageB, imageC)
print("PSNR: {}".format(PSNR_val))

(score, diff) = compare_ssim(imageB, imageC, full=True)
diff = (diff * 255).astype("uint8")
print("SSIM: {}".format(score))


import numpy as np
import cv2
import tensorflow as tf
import imutils
import pyttsx3
import math
import os
import speech_recognition as sr

from sklearn.externals import joblib
from scipy.spatial import distance as dist
from imutils.video import FPS
from utils import label_map_util
from utils import visualization_utils as vis_util

def find_angle(aX, aY, bX, bY):
	if(bX-aX != 0):
		m = abs((bY-aY)/(bX-aX))
		z = 90-math.degrees(math.atan(m))
		return int(z)
	elif bY-aY == 0:
		return 90
	else:
		return 0

def left_or_right(k, centX, input):
	if k == 0:
		engine.say(input + "is not in frame")
		engine.runAndWait()
	elif k==1:
		if (centX[j] < 187):
			engine.say(input + "is at the left")
		elif (centX[j] < 222):
			engine.say(input + "is at the center")
		else:
			engine.say(input + "is at the right")
		engine.runAndWait()
	else:
		for j in range(k):
			if (centX[j] < 187):
				engine.say(input + str(j+1) + "is at the left")
			elif (centX[j] < 222):
				engine.say(input + str(j+1) + "is at the center")
			else:
				engine.say(input + str(j+1) + "is at the right")
			engine.runAndWait()

def distance_calculation(k, centX, centY, center, reg):
	if k == 0:
		engine.say(input + "is not in frame")
		engine.runAndWait()
	else:
		for j in range(k):
			D = dist.euclidean((center[0], center[1]), (centX[j], centY[j]))
			angle = find_angle(center[0], center[1], centX[j], centY[j])
			D = D*0.0265
			D = int(reg.predict(np.reshape([D, angle],(1,-1))))
			print(angle, " Degrees")
			print(D, " Centimeters")
			D = str(D)
			angle = str(angle)
			engine.say("At" + D + " centimeters and" + angle + "degrees")
			engine.runAndWait()

os.environ["TF_CPP_MIN_LOG_LEVEL"]="3"
PATH_TO_CKPT = 'ssd_mobilenet.pb'
PATH_TO_LABELS = 'mscoco_label_map.pbtxt'
NUM_CLASSES = 90

print("[INFO] loading model...")
detection_graph = tf.Graph()
with detection_graph.as_default():
	od_graph_def = tf.GraphDef()
	with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
		serialized_graph = fid.read()
		od_graph_def.ParseFromString(serialized_graph)
		tf.import_graph_def(od_graph_def, name='')

label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)

print("[INFO] starting video stream...")
cap = cv2.VideoCapture(0)
fps = FPS().start()
engine = pyttsx3.init()
r = sr.Recognizer()
reg = joblib.load('gradient_boosting_model.pkl')
with detection_graph.as_default():
	with tf.Session(graph=detection_graph) as sess:
		while True:
			ret, frame = cap.read()
			frame = imutils.resize(frame, width=400)
			image_np_expanded = np.expand_dims(frame, axis=0)
			ch = cv2.waitKey(1)
			image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
			boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
			scores = detection_graph.get_tensor_by_name('detection_scores:0')
			classes = detection_graph.get_tensor_by_name('detection_classes:0')
			num_detections = detection_graph.get_tensor_by_name('num_detections:0')
			(boxes, scores, classes, num_detections) = sess.run([boxes, scores, classes, num_detections], feed_dict={image_tensor: image_np_expanded})
			vis_util.visualize_boxes_and_labels_on_image_array(frame, np.squeeze(boxes), np.squeeze(classes).astype(np.int32), np.squeeze(scores), category_index, use_normalized_coordinates=True)
			if ch == ord('y'):
				objects = vis_util.objects_in_frame(np.squeeze(scores), np.squeeze(classes))
				for i in objects:
					engine.say("There is" + category_index[int(objects[i])]['name'])
					engine.runAndWait()
			if ch == ord('s'):
				with sr.Microphone() as source:
					audio = r.listen(source)
				try:
					input = r.recognize_google(audio)
					objects = vis_util.objects_in_frame(np.squeeze(scores), np.squeeze(classes))
					flag = False
					for i in objects:
						if category_index[int(objects[i])]['name'] == input:
							flag = True
							break
					if flag:
						engine.say("Yes! There is" + input)
						engine.runAndWait()
						idx = int(objects[i])
						while True:
							val, map_frame = cap.read()
							map_frame = imutils.resize(map_frame, width=400)
							img_np_expanded = np.expand_dims(map_frame, axis=0)
							ch2 = cv2.waitKey(1)
							img_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
							boxes_arr = detection_graph.get_tensor_by_name('detection_boxes:0')
							scores_arr = detection_graph.get_tensor_by_name('detection_scores:0')
							classes_arr = detection_graph.get_tensor_by_name('detection_classes:0')
							num_detections_arr = detection_graph.get_tensor_by_name('num_detections:0')
							(boxes_arr, scores_arr, classes_arr, num_detections_arr) = sess.run([boxes_arr, scores_arr, classes_arr, num_detections_arr], feed_dict={img_tensor: img_np_expanded})
							k, centX, centY = vis_util.visualize_only_mapped_boxes_on_image_array(map_frame, idx, np.squeeze(boxes_arr), np.squeeze(classes_arr).astype(np.int32), np.squeeze(scores_arr), category_index, use_normalized_coordinates=True)
							cv2.circle(map_frame, (centX[0], centY[0]), 3,(0, 255, 255), 2)
							#glove detection
							frame_to_thresh = cv2.cvtColor(map_frame, cv2.COLOR_BGR2HSV)
							thresh = cv2.inRange(frame_to_thresh, (30, 80, 35), (65, 255, 166)) # set min & max HSV values
							kernel = np.ones((5,5),np.uint8)
							mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
							mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
							cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
							center = None
							if len(cnts) > 0:
								c = max(cnts, key=cv2.contourArea)
								((x, y), radius) = cv2.minEnclosingCircle(c)
								M = cv2.moments(c)
								center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
								cv2.circle(map_frame, (int(x), int(y)), int(radius),(0, 255, 255), 2)
								cv2.circle(map_frame, center, 3, (0, 0, 255), -1)
								cv2.putText(map_frame,"centroid", (center[0]+10,center[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4,(0, 0, 255),1)
								cv2.putText(map_frame,"("+str(center[0])+","+str(center[1])+")", (center[0]+10,center[1]+15), cv2.FONT_HERSHEY_SIMPLEX, 0.4,(0, 0, 255),1)
							if ch2 == ord("x"):
								left_or_right(k, centX, input)
							if ch2 == ord("d"):
								if len(cnts) > 0:
									distance_calculation(k, centX, centY, center, reg)
								else:
									engine.say("Hand is not in frame")
									engine.runAndWait()
							cv2.imshow('Mapped',map_frame)
							if ch2 == ord('m'):
								cv2.destroyWindow("Mapped")
								break
					else:
						engine.say("There is no" + input)
						engine.runAndWait()
				except:
					engine.say("Couldn't hear you!")
					engine.runAndWait()
			cv2.imshow('Input',frame)
			if ch == ord('q'):
				break
			fps.update()
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
cv2.destroyAllWindows()
cap.release()


class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        result = []
        used = [False]*len(nums)
        path = []
        nums.sort()
        def dfs():
            if len(path) == len(nums):
                result.append(path[:])
                return

            for i in range(len(nums)):
                if used[i]:
                    continue
                if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]:
                    continue
                used[i] = True
                path.append(nums[i])
                dfs()
                path.pop()
                used[i] = False
        dfs()
        return result


import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
import threading
import time
from nothread import nothreadtime

start_time = time.time()

URL = "https://realpython.github.io/fake-jobs/"
page = requests.get(URL)
soup = BeautifulSoup(page.content, "html.parser")
results = soup.find(id="ResultsContainer")
job_elements = results.find_all("div", class_="card-content")

URLs = []
loops = 1
jobs = {'job_id': [], 'title': [], 'company': [], 'location': [], 'description': [], 'url': []}
for job_element in job_elements:
    title_element = job_element.find("h2", class_="title")
    title_element = title_element.text.strip()
    company_element = job_element.find("h3", class_="company")
    company_element = company_element.text.strip()
    location_element = job_element.find("p", class_="location")
    location_element = location_element.text.strip()
    url_element = job_element.find(attrs={'href': re.compile("^https://")}, string='Apply')
    url_element = url_element.get('href')
    jobs['job_id'].append(loops)
    jobs['title'].append(title_element)
    jobs['company'].append(company_element)
    jobs['location'].append(location_element)
    jobs['url'].append(url_element)
    loops += 1

def find_description(start, end):
    for url in jobs['url'][start:end]:
        page = requests.get(url)
        soup = BeautifulSoup(page.content, "html.parser")
        results = soup.find(id="ResultsContainer")
        job_elements = results.find_all("div", class_="content")
        for job_element in job_elements:
            description = results.find('p', string='')
            description = description.text.strip()
            jobs['description'].append(description)

trd1 = threading.Thread(target=find_description, args=[0, 20])
trd2 = threading.Thread(target=find_description, args=[20, 40])
trd3 = threading.Thread(target=find_description, args=[40, 60])
trd4 = threading.Thread(target=find_description, args=[60, 80])
trd5 = threading.Thread(target=find_description, args=[80, 100])

trd1.start()
trd2.start()
trd3.start()
trd4.start()
trd5.start()

trd1.join()
trd2.join()
trd3.join()
trd4.join()
trd5.join()

df = pd.DataFrame(jobs, columns=['job_id', 'title', 'company', 'location', 'description', 'url'])
#print(df.to_string(index=False))

print("With threading, this took %s seconds" % (time.time() - start_time))