from tkinter import *
import logging
import traceback
from inspect import currentframe, getframeinfo
import re
import os

from five_in_a_row import fields, game_play, graphics

__author__    = "Eckhard Delfs"
__copyright__ = "2020"
__credits__   = ["{credit_list}"]
__license__   = "MIT"
__title__     = "Five In A Row"
__version__   = "0.1"
__status__    = "{dev_status}"

################################################
#
# Game play symbols and colors - edit here
#
################################################

__symbol_p1__ = "X"              # symbol used for player 1
__symbol_p2__ = "O"              # symbol used for player 2
__color_p1_default__ = "black"   # default color of player 1
__color_p2_default__ = "blue"    # default color of player 2
__color_p1_win__ = "green"       # win sequence color of player 1
__color_p2_win__ = "red"         # win sequenec color of player 2

class game_control():

	def __init__(self, tk_main_frame, op_mode, rows, columns):
		self.tk_main = tk_main_frame
		self.command_list = [["click_field", " "   , self.field_click_event_handler],
							 ["remove",    "\u2bc7", self.remove_last_move],  # left arrow symbol
							 ["restore",   "\u2bc8", self.restore_last_move], # right arrow symbol
							 ["edit",      "Edit"  , self.setup_scenario],
							 ["symbol_st", "Start" , self.toggle_first_player_edit_mode],
							 ["new",       "New"   , self.reset],
							 ["load_scen", "Scen"  , self.load_scenario],
							 ["save",      "Save"  , self.save_game_to_file],
							 ["load",      "Load"  , self.load_game_from_file],
							 ["step",      "Step"  , self.run_single_step],
							 ["auto",      "Auto"  , self.run_auto_mode],
							 ["quit",      "Quit"  , self.game_exit],
							 ["done",      "Done"  , self.leave_setup_scenario],
							 ["symbol_p1", __symbol_p1__ , self.set_symbol_p1],
							 ["symbol_p2", __symbol_p2__ , self.set_symbol_p2],
							 ["symbol_cl", "Clear" , self.clear_symbol]]

		self.menu_list = [["Load Game", self.load_game_dialogue],
						  ["Save Game", self.save_game_dialogue],
						  ["Options", self.set_game_options],
						  ["About", self.set_about_info],
						  ["Exit", self.game_exit]]
		self.info = {"author": __author__, "copyright": __copyright__, "version": __version__, "title":__title__}
		self.move_list = []
		self.__total_moves_copy = rows*columns
		self.total_moves_left = rows*columns
		self.total_moves = 0
		self.finished = False
		self.play = game_play.play( rows, columns, "X", "O")
		self.stat = game_stat()
		self.last_moves = []        # move history buffer
		self.graphics = graphics.graphics( tk_main_frame,
			(__symbol_p1__, __color_p1_default__,__color_p1_win__),
			(__symbol_p2__,__color_p2_default__,__color_p2_win__),
			rows, columns, self.command_list, self.menu_list, op_mode, self.info)
		self.edit_mode = False
		self.edit_player = 1
		self.op_mode = op_mode
		self.print_console = False  # controls debug info output on command shell (only from this class)
		self.win_list = []          # contains winning fields
		self.winner =  0            # player who won last game (1,2)
		self.busy = False
		self.scenario_number = 0  # used to select predefined scenario
		self.first_reset = True
		self.file_name = "match.txt"
		self.version_string = __version__
		self.first_player_edit_mode = 1

	def reset( self):
		if self.first_reset or self.total_moves != 0:
			self.busy = True # set busy flag set since play.reset() takes some time to clean Tk gadget list
			self.play.reset()
			if self.op_mode != "auto":
				self.graphics.reset()
			self.busy = False
			self.move_list = []
			self.total_moves_left = self.play.field.get_number_of_columns()*self.play.field.get_number_of_columns()
			self.total_moves = 0
			self.finished = False
			self.last_moves = []
			self.win_list = []
			self.winner = 0
			self.edit_mode = False
			self.first_reset = False
			self.first_player_edit_mode = 1 # controls which player starts when edit mode is left

################################################
#
# Handle "Load Game" menu dialogue
#
# Default file name, otherwise last selected name
# in file menus
#
################################################

	def load_game_dialogue( self):
		root_path = os.path.dirname(os.path.realpath(__file__))
		title = "Select game file to load"
		file_types = (("game files","*.txt"),("all files","*.*"))
		file_name = self.graphics.ask_file_name_dialogue( root_path, title, file_types)
		if len(file_name) == 0:
			return
		self.file_name = file_name
		self.load_game_from_file()
		return

################################################
#
# Handle "Save Game" menu dialogue
#
# Default file name, otherwise last selected name
# in file menu
#
################################################

	def save_game_dialogue( self):
		root_path = os.path.dirname(os.path.realpath(__file__))
		title = "Select game file to save"
		file_types = (("game files","*.txt"),("all files","*.*"))
		file_handle = self.graphics.ask_file_name_save_dialogue( root_path, title, file_types)
		if file_handle == None:
			return
		else:
			self.save_game_to_file_pre_opened( file_handle)
		return

################################################
#
# Handle "Options" menu dialogue
#
################################################

	def set_game_options( self):
		# not implemented yet
		return

################################################
#
# Handle "About" menu dialogue
#
################################################

	def set_about_info( self):
		self.graphics.about_message( self.version_string)
		return

	def set_symbol_p1( self):
		self.edit_player = 1 # player 1 uses default "X"
		return

	def set_symbol_p2( self):
		self.edit_player = 2 # player 2 uses default "O"
		return

	def clear_symbol( self):
		self.edit_player = 0 # used to clear field in edit mode
		return

	def game_exit( self):
		self.tk_main.destroy()

	def	save_game_to_file_pre_opened( self, file):
		list = self.convert_move_list_from_internal_to_standard()
		for item in list:
			file.write(str(item[0])+", "+ str(item[1])+"\n")
		file.close()
		return

################################################
#
# Save game to file (quick button in command row)
#
################################################

	def	save_game_to_file( self):
		with open(self.file_name, "w") as file:
			list = self.convert_move_list_from_internal_to_standard()
			for item in list:
				file.write(str(item[0])+", "+ str(item[1])+"\n")
		file.close()
		return

################################################
#
# Load game from file (quick button in command row)
#
################################################

	def	load_game_from_file( self):
		if self.total_moves != 0:
			self.reset()
		list = []
		index = 1
		try:
			with open(self.file_name, "r") as file:
				print("reading", self.file_name)
				file_content = file.readlines()
				for item in file_content:
					if "," in item:
						list.append([index, item[item.find(","):]])
						index += 1
			file.close()
		except:
			print("Error when trying to read", file_name)
			print(sys.exc_info()[0])
		list = self.convert_move_list_from_standard_to_internal( list, self.play.field.get_number_of_rows(), self.play.field.get_number_of_columns())
		for item in range(len(list)):
			player = 1+item%2
			i,j = list[item][0], list[item][1]
			self.play.field.set(player, i, j)
			self.win_list = self.play.field.check_if_won( player, i, j, False)
			if len( self.win_list) != 0:
				self.winner = player
				self.finished = True
				break
		for w in range(item+1):
			self.move_list.append([list[w][0], list[w][1]])
		self.total_moves = item+1
		self.total_moves_left = (self.play.field.get_number_of_rows()*self.play.field.get_number_of_columns()) - self.total_moves
		self.depict_game_scenario()
		if len( self.win_list) != 0:
			self.graphics.depict_winning_pattern( player, self.win_list, False)
		return

	def set_op_mode( self, op_mode):
		self.op_mode = op_mode

	def get_op_mode( self):
		return self.op_mode

	def leave_edit_mode( self):
		self.edit_mode = False

	def enter_edit_mode( self):
		self.edit_mode = True

	def set_edit_player( self, player):
		self.edit_player = player

################################################
#
# Determine next player
# This is handled through number of moves
# - even number -> player 1
# - odd number  -> player 2
#
################################################

	def get_next_player( self):
		if self.total_moves %2 == 0:
			player = 1
		else:
			player = 2
		return player

	def toggle_edit_player( self):
		if self.edit_player == 1:
			self.edit_player = 2
		else:
			self.edit_player = 1

	def update_move_list(self, i,j):
		self.move_list.append([i,j])

################################################
#
# Convert move list from internal (digit/digit)
# to external (letter/digit) format
#
################################################

	def convert_move_list_from_internal_to_standard( self):
		standard_list = []
		for i in range(len(self.move_list)):
			row_entry = self.play.field.get_number_of_rows() - self.move_list[i][0]
			column_entry = chr(65+self.move_list[i][1])
			standard_list.append([i+1, column_entry+str(row_entry)])
		return standard_list

################################################
#
# Convert move list from external (letter/digit)
# to external (digit/digit) format
#
################################################

	def convert_move_list_from_standard_to_internal( self, standard_list, max_rows, max_columns):
		move_list = []
		for i, item in standard_list:
			#print(i, item)
			text = re.search(r"[a-oA-O]{1}[0-9]{1,2}",item)
			if text == None:
				return move_list
			column = ord(text[0][0].upper()) - ord(chr(65))
			if column > max_columns-1:
				return move_list
			row = max_rows - int(text[0][1:])
			if row > max_rows-1:
				return move_list
			move_list.append([row, column])
		#print(move_list)
		return move_list

################################################
#
# Print move list on command window
#
# (proprietary format with rows/lines ranging from 0..14/19
#
################################################

	def print_move_list( self):
		print("Move list after", self.total_moves,"moves:")
		head_line_length = 5
		if self.total_moves < 10:
			head_line_length = 1+ self.total_moves >> 1
		for i in range( head_line_length):
			sys.stdout.write("{0:<9s}".format("Player1"))
			sys.stdout.write("{0:<9s}   ".format("Player2"))
		print("")

		diff = self.total_moves - len(self.move_list)
		if diff & 1 != 0:
			sys.stdout.write("{0:<9s}".format(""))
			j = 1
		else:
			j = 0
		for i in self.move_list:
			if not j & 1:
				sys.stdout.write("{0:<9s}".format(str(i)))
			else:
				sys.stdout.write("{0:<9s}   ".format(str(i)))
			j +=1
			if j % 10 == 0:
				print("")
		print("")

	def updateMoveCounters( self):
		if self.total_moves_left == 0:
			print("Error: updateMoveCounters", self.total_moves, self.total_moves_left)
			exit()
		self.total_moves+=1
		self.total_moves_left -=1

	def anyMoveLeft( self):
		return self.total_moves_left != 0
	def movesLeft( self):
		return self.total_moves_left
	def showMovesDone( self):
		return self.total_moves

################################################
#
# Step back by one move in history buffer
#
################################################

	def remove_last_move( self):
		if self.total_moves > 0 and len(self.move_list) > 0:
			if self.finished:
				self.graphics.depict_winning_pattern( self.winner, self.win_list, True)
				self.finished = False
				self.winner = 0
				self.win_list = []

				self.play.win_path = []
				self.play.win_path_winner = 0
				self.play.win_mode = False

			i, j = self.move_list.pop()
			self.total_moves-=1
			self.total_moves_left+=1
			self.play.field.unset( i, j)
			self.last_moves += [(i, j)]
			self.graphics.clear_field( i, j)
		return

################################################
#
# Advance by one move from history buffer
#
################################################

	def restore_last_move( self):
		if self.last_moves:

			player = self.get_next_player()
			self.total_moves_left-=1
			i, j = self.last_moves.pop()
			self.move_list.append([i, j])
			self.play.field.set( player, i, j)
			if len(self.win_list) == 0:
				self.win_list = self.play.field.check_if_won( player, i, j, True)
				if len( self.win_list) != 0:
					self.graphics.depict_winning_pattern( player, self.win_list, False)
					self.winner = player
					self.finished = True
				else:
					self.graphics.depict_field( player, i, j)
			self.total_moves+=1
		return

################################################
#
# Load predefined scenario ("Scen")
#
# - each click on "Scen" button loads new scenario
# - used for debugging purposes only
#
################################################

	def load_scenario_common( self, scenario):
		if self.total_moves != 0:
			self.reset()
		for item in scenario:
			self.play.field.set(*item)
		self.total_moves_left -= len(scenario)
		self.total_moves = len(scenario)
		self.depict_game_scenario()

	def load_scenario( self):
		if not self.busy:
			scenarios = (((1,7, 7), (2,7, 8), (1,6, 7), (2,8, 7), (1,6, 9), (2,6, 8), (1,5, 8), (2,5, 7), (1,7, 6), (2,8, 5), (1,8, 8), (2,6, 6), (1,7, 9), (2,4, 9), (1,7, 5), (2,8, 6), (1,5, 9), (2,8, 9), (1,4, 8), (2,6, 10), (1,7, 10), (2,4, 7), (1,5, 10), (2,7, 4), (1,9, 6), (2,9, 7), (1,3, 8), (2,8, 4), (1,8, 3), (2,9, 4), (1,10, 4), (2,3, 7), (1,6, 4), (2,4, 6), (1,5, 5), (2,3, 5), (1,2, 4), (2,4, 4), (1,1, 7), (2,2, 8), (1,2, 6), (2,5, 3), (1,7, 3), (2,6, 3), (1,3, 3), (2,4, 5), (1,4, 3), (2,5, 2), (1,4, 1), (2,7, 2), (1,9, 12), (2,8, 11), (1,5, 11), (2,5, 12), (1,8, 2), (2,9, 1), (1,5, 4), (2,6, 2), (1,7, 1), (2,9, 3), (1,3, 2), (2,6, 5), (1,2, 1), (2,1, 0), (1,9, 2)),# (2,2, 3)),
			((1,7, 7), (2,7, 6), (1,6, 7), (2,6, 6), (1,5, 7), (2,4, 7), (1,5, 6), (2,5, 8), (1,7, 8), (2,8, 9), (1,6, 9), (2,8, 7), (1,6, 8), (2,8, 6), (1,8, 8), (2,7, 9), (1,5, 9), (2,9, 8), (1,10, 9), (2,9, 6), (1,10, 6), (2,10, 7), (1,4, 5), (2,3, 4), (1,4, 10), (2,3, 11), (1,6, 11), (2,6, 10), (1,11, 6), (2,8, 5), (1,11, 8), (2,6, 5), (1,5, 4), (2,9, 5), (1,5, 5), (2,5, 3), (1,9, 7)),# (2,7, 5), (1,10, 5), (2,11, 5)),

						 ((1,7,7), (2,6,7), (1,6,6), (2,7,6), (1,7,5), (2,8,5), (1,9,4), (2,8,6),
						  (1,8,4), (2,5,7), (1,5,5)),
						 ((1,6,5), (1,6,6), (1,6,7), (2,7,4), (2,7,5), (1,7,6), (2,7,7),( 2,8,4),
						  (1,8,5), (1,8,6), (1,8,7), (2,8,8), (2,9,4), (1,9,5), (2,9,6), (2,9,8),
						  (2,10,3),(1,10,4),(1,10,5),(1,10,6),(2,11,5)),
						 ((1,10,10),(1,9,10),(2,8,6),(2,7,4), (1,7,5), (1,7,6), (1,7,7), (2,6,5),
						  (1,6,8), (2,5,5), (1,5,7), (2,4,8) ,(1,1,1)), #,(2,4,6),
						 ((1,10,10),(1,9,10),(2,8,6),(2,7,4), (1,7,5), (1,7,6), (1,7,7), (2,6,5),
						  (1,6,8), (2,5,5), (1,5,7), (2,4,6), (2,4,7), (2,4,8)),
						 ((1,5,7), (2,6,6), (2,7,5), (1,7,6), (1,7,7), (1,7,10),(2,8,6), (2,8,8)),
						 ((2,6,5), (1,6,6), (2,6,7), (1,7,5), (1,7,6), (2,7,8)),
						 ((2,4,10),(1,5,8), (1,5,9), (1,6,5), (2,6,6), (2,6,7), (1,6,8), (1,7,7),
						  (2,7,9),(2,8,8)))
			self.load_scenario_common( scenarios[self.scenario_number])
			self.scenario_number += 1
			if self.scenario_number == len(scenarios):
				self.scenario_number = 0

################################################
#
# Toggle Start symbol in "Edit" mode
#
################################################

	def toggle_first_player_edit_mode( self):
		if self.first_player_edit_mode == 1:
			self.first_player_edit_mode = 2
		else:
			self.first_player_edit_mode = 1
		i = self.play.field.get_number_of_rows()-1
		j = self.play.field.get_number_of_columns()-1
		self.graphics.clear_control_row( i)
		self.graphics.depict_control_row_setup_scenario( i, j, self.first_player_edit_mode == 1)

################################################
#
# Enter "Edit" mode
#
################################################

	def setup_scenario( self):
		self.enter_edit_mode()
		self.set_edit_player(1)
		i = self.play.field.get_number_of_rows()-1
		j = self.play.field.get_number_of_columns()-1
		self.graphics.clear_control_row( i)
		self.graphics.depict_control_row_setup_scenario( i, j, self.first_player_edit_mode == 1)

################################################
#
# Leave "Edit" mode
#
################################################

	def leave_setup_scenario( self):
		self.leave_edit_mode()
		self.finished = False
		i = self.play.field.get_number_of_rows()-1
		j = self.play.field.get_number_of_columns()-1
		self.graphics.clear_control_row( i)
		self.graphics.depict_control_row(i, j)
		self.edit_mode = False
		self.move_list  = []
		count = 0
		# calculate total number of moves by checking field usage
		for i in range(self.play.field.get_number_of_rows()):
			for j in range( self.play.field.get_number_of_columns()):
				if not self.play.field.is_free(i,j):
					count+=1
		# adjust number of moves in case that user has decided starting player at will
		if ((self.first_player_edit_mode == 2 and count & 1 == 0) or
		   (self.first_player_edit_mode == 1 and count & 1 == 1)):
			count +=1

		self.total_moves = count
		self.total_moves_left = self.play.field.get_number_of_rows()*self.play.field.get_number_of_columns() - count

################################################
#
# Depict loaded game scenario on screen
#
################################################

	def depict_game_scenario( self):
		for i in range( self.play.field.get_number_of_rows()):
			for j in range( self.play.field.get_number_of_columns()):
				if not self.play.field.is_free( i, j):
					self.graphics.depict_field( self.play.field.get_value(i, j), i, j)
		self.graphics.refresh()

################################################
#
# Depict winning sequence on game field
#
################################################

	def win_procedure( self, text1, text2):
		self.stat.update_wins_player( self.winner)
		if self.get_op_mode() != "auto":
			self.graphics.depict_winning_pattern( self.winner, self.win_list, False)
			self.graphics.finish_message(text1 + text2 +str( game.stat.get_wins_player( 1))+":"+str( game.stat.get_wins_player( 2)))

	def stale_result( self, text):
		if self.get_op_mode() != "auto":
			self.graphics.finish_message(text)

################################################
#
# Run next step by computer (player 1 or 2)
#
################################################

	def run_single_step( self):
		if not self.finished:
			self.last_moves = []
			self.computer_move()
			list = self.convert_move_list_from_internal_to_standard()
			self.convert_move_list_from_standard_to_internal( list, self.play.field.get_number_of_rows(), self.play.field.get_number_of_columns())

################################################
#
# Run full game in auto mode
#
################################################

	def run_auto_mode( self):
		while not self.finished:
			self.last_moves = []
			self.computer_move()
			if self.get_op_mode()  == "auto":
				sys.stdout.write("\rRunning game %i move %i" % (self.stat.get_total_matches()+1, self.total_moves) )
				sys.stdout.flush()
			sys.stdout.write("\r")

################################################
#
# Run next move by computer
#
################################################

	def computer_move( self):
		self.busy = True
		if self.showMovesDone() == 0:
			# always start in center position
			[i,j] = self.play.field.get_center_position()
		#elif self.showMovesDone() == 1:
			# 2nd move is always adjacent to first
			#[i,j] = self.play.get_random_move_from_list( self.play.field.get_list_of_surrounding_fields( 1,1))
		else:
			# any other move gets processed
			result, [i,j] = self.play.get_next_move( self.get_next_player())
		if not self.play.field.set( self.get_next_player(), i, j):
			print("weird...")
			frameinfo = getframeinfo(currentframe())
			print(frameinfo.filename, frameinfo.lineno)
			self.debug()

		# for each new move, check if game is finished (True flag indicates that only 5 in a row count, e.g. 6 or 7 do not)
		self.win_list = self.play.field.check_if_won( self.get_next_player(), i, j, True)
		if len( self.win_list) != 0:
			self.finished = True
			self.winner = self.get_next_player()
			if self.print_console:
				print("Game end!!!")
				print(self.win_list)
			# highlight winning pattern on game field and run finish dialogue
			self.win_procedure( "Player " + str(self.graphics.get_symbols()[self.winner-1]) +
								" has won after " + str(self.total_moves+1) + " moves!\n\n", "Result: ")
		else:
			if self.get_op_mode() != "auto":
				# update game field with computer move
				self.graphics.depict_field( self.get_next_player(), i, j)
				self.graphics.refresh()
		self.updateMoveCounters()
		self.update_move_list(i,j)
		if self.print_console:
			self.print_move_list()
			self.play.field.print_game_field_on_console( self.graphics.get_symbols())

		if not self.anyMoveLeft():
			self.finished =  True
			self.stale_result("No winner!")
			self.stat.update_stale_counter()
		self.busy = False

################################################
#
# Handle clicks on game field map during game
#
################################################

	def field_click_event_handler( self, i, j):
		if self.edit_mode:
			if self.play.field.is_free( i, j) and self.edit_player != 0:
				self.play.field.set( self.edit_player, i, j)
				self.graphics.depict_field( self.edit_player, i, j)
				self.graphics.refresh()
			elif self.edit_player == 0:
				self.play.field.unset( i, j)
				self.graphics.reset_game_field( i, j, self.field_click_event_handler)
		else:
			# user has clicked on game field. Check if game is not yet finished and last computer move completed
			if not self.finished and not self.busy:
				player = self.get_next_player() 	  # get current player
				if self.play.field.is_free( i, j):    # ceck if field is free
					#self.last_moves = []
					# mark selected field and enter symbol on game field
					self.play.field.set( player, i, j)
					self.updateMoveCounters()
					self.update_move_list(i,j)
					# check if game is finished (True flag indicates that only 5 in a row count, e.g. 6 or 7 do not)
					self.win_list = self.play.field.check_if_won( player, i, j, True)
					if len( self.win_list) != 0:
						self.winner = player
						self.finished = True
						if self.print_console:
							print("You have won!!!")
							print(self.win_list)
						# highlight winning pattern on game field and run finish dialogue
						self.win_procedure( "You have won!\n\n", "Result: ")

					else:
						if self.last_moves != []:
							self.last_moves = []
							self.win_mode = False
						if self.anyMoveLeft():
							# update game field with players move
							self.graphics.depict_field( player, i, j)
							self.graphics.refresh()
							self.computer_move() # computer takes next move

################################################
#
# Write game field and call stack in case of
# observed error
#
################################################

	def debug( self):

		self.play.field.print_game_field_on_console( self.graphics.get_symbols())
		for item in vars(self).items():
			print(item)

		print("==========================================")
		print("|            CALL TRACE START             ")
		print("==========================================")
		for line in traceback.format_stack():
			print(line.strip())

		print("==========================================")
		print("|            CALL TRACE END               ")
		print("==========================================")
		input("check")
		exit()

################################################
#
# class game_stat to maintain game statistical data
#
################################################

class game_stat():
	def __init__( self):
		self.__player_1_wins = 0
		self.__player_2_wins = 0
		self.__stale_results = 0

	def update_wins_player( self, player):
		if player == 1:
			self.__player_1_wins +=1
		else:
			self.__player_2_wins +=1

	def get_total_matches( self):
		return self.__player_1_wins + self.__player_2_wins + self.__stale_results

	def update_stale_counter( self):
		self.__stale_results += 1

	def get_wins_player( self, player):
		if player == 1:
			return self.__player_1_wins
		else:
			return self.__player_2_wins

################################################
#
# Main processing loop
#
################################################

op_mode = "default"
for argument in sys.argv:
	if argument == "--auto":
		op_mode = "auto"
	elif argument == "--debug" or argument == "--d":
		op_mode = "debug"
	elif argument == "--h" or argument == "--help":
		print("Usage: ", sys.argv[0], "[--auto] [--d, --debug]")
		print("--auto:       computer plays against itself for 100 matches")
		print("--debug, --d: game field can be edited at will")
		exit()

# default run mode, either with or without debug features
# debug features allow to pre-edit game field
if op_mode != "auto":
		master = Tk()
		master.resizable(False, False) # user cannot change game field window
		master.title(__title__)
		game = game_control( master, op_mode, 15,15)  # default game field size 15*15
		game.reset()
		master.mainloop()

# run mode without graphical UI. Computer plays against itself and win/loss ratio is reported for 100 consecutive games
elif op_mode == "auto":
		game = game_control( None, op_mode, 15,15) # default game field size 15*15
		for i in range( 100): # 100 matches
			game.reset()
			game.run_auto_mode()
			print( "Result:", game.stat.get_wins_player( 1),":",game.stat.get_wins_player( 2), "with", game.total_moves, "moves." )
exit()


import re

handle = open("regex_sum_42.txt")

nums = list()

for line in handle:
    line = line.rstrip()
    stuff = re.findall('[0-9]+', line)
    if len(stuff) == 0 : continue
    for x in stuff:
        num = float(x)
        nums.append(num)
print(len(nums))
print(sum(nums))
# count = 0
# for x in nums:
#     count = count + x
# print(count)


cid = 'cid'
tot = 0
try:
    row = 0
    while True:
        lines = [i.split(':')[0] for i in input().split()]
        while True:
            try:
                q = input().split()
            except EOFError:
                break
            if len(q) == 0: break
            lines += [i.split(':')[0]  for i in q]

        lines = set(lines)
        if len(lines) >= 7 and cid not in lines:
            tot += 1
        elif len(lines) == 8:
            tot += 1
except: EOFError
print(tot)


# -*- coding: utf-8 -*-
# __author__ = zouguanqing
# last_modifiy = modified 201908

import os
import glob
import shutil
from biocluster.core.exceptions import OptionError
from biocluster.module import Module
import unittest
import pandas as pd
import numpy as np
import re

class MetabsetsModule(Module):
    """
    单个代谢集分析
    """

    def __init__(self,work_id):
        super(MetabsetsModule, self).__init__(work_id)
        options = [
            #公用
            {"name": "anno_overview", "type": "infile", "format": "metabolome.overview_table"},
            {"name": "mul_metabset", "type": "infile", "format": "metabolome.mul_metabset"},
            ##keggp
            {"name": "ko_overview","type": "infile", "format":"sequence.profile_table" },
            #hmdb
            {"name": "hmdb_overview","type": "infile", "format":"sequence.profile_table" },
            # vip
            {'name': 'diff_dir', 'type': 'infile', 'format': 'annotation.mg_anno_dir'},
            ## 公用
            {"name": "metab_trans", "type": "infile", "format": "sequence.profile_table"},
            {"name": "metab_table", "type": "infile", "format": "sequence.profile_table"}, # 标准化时用，用原始丰度表
            ##cluster
            # {"name": "scale_exp","type": "infile", "format": "sequence.profile_table"},
            ###{"name": "skip_analysis","type": "string", "default":""}
            {"name": "species", "type": "string", "default": "all"},
            {"name": "task_id", "type": "string", "default": ""}, # 用于区分新老工作流
            {"name": "database_version", "type": "string", "default": ""}, # 用于区分新老工作流

        ]
        self.add_option(options)

    def check_options(self):
        if not self.option("anno_overview").is_set:
            raise OptionError("请传入anno_overview！")
        if not self.option("mul_metabset").is_set:
            raise OptionError("请传入mul_metabset！")
        if not self.option("ko_overview").is_set:
            raise OptionError("请传入ko_overview！")
        # if not self.option("hmdb_overview").is_set:
        #     raise OptionError("请传入hmdb_overview！")
        return True

    def scale_data(self, file_path):
        table = pd.read_table(file_path, sep="\t", index_col=0)
        scaled_data = table.apply(lambda x: (x - np.mean(x)) / np.std(x, ddof=1), axis=1)
        exp_profile = os.path.join(self.work_dir, "scale_data.xls")
        scaled_data.to_csv(exp_profile, index=True, header=True, sep="\t")
        return exp_profile

    def divide_metabset(self):
        mul_metabset = self.option('mul_metabset').path
        dir = self.work_dir
        self.map_file = {}
        with  open(mul_metabset) as f:
            for line in f:
                line = line.strip()
                if line =="" :
                    continue
                spline = line.split('\t')
                set_name = spline[0]
                metab_num = len(spline[1].split(','))
                self.map_file[set_name] = {
                    'file':dir+'/set_'+set_name,
                    'file_row': dir+'/set_'+set_name+'.row',
                    'metab_num': metab_num,
                    'metab_list' : spline[1].split(',')
                }
                with open(dir+'/set_'+set_name,'w') as fw , open(dir+'/set_'+set_name+'.row','w') as fw2:
                    fw2.write(line+'\n')
                    fw.write('\n'.join(spline[1].split(',')))

    def rm_qc_sample(self):
        metab_table = self.option('metab_table').path
        data = pd.read_table(metab_table,sep='\t',header=0)
        cols = data.columns
        rm_qc_cols = []
        for c in cols:
            if not re.match('^QC\d*$', c):
                rm_qc_cols.append(c)
        new_data = data[rm_qc_cols]
        self.rm_qc_abund = self.work_dir+'/rm_QC_abund.xls'
        new_data.to_csv(self.rm_qc_abund,sep='\t',index=False)

    def _check_skip_hmdb(self,db,metab_set):
        sub_db = db[db['Metab_id'].apply( lambda  x:  x in metab_set)]
        if len(sub_db) > 0:
            return False
        else:
            return  True

    def run_modules(self):
        self.divide_metabset()
        self.rm_qc_sample()
        self.scale_exp = self.scale_data( self.rm_qc_abund)
        if self.option('hmdb_overview').is_set:
            self.hmdb_all_data = pd.read_table(self.option('hmdb_overview').path,sep='\t',header=0)
        else:
            self.hmdb_all_data = ''

        for set_name in  self.map_file.keys():

            if self.map_file[set_name]['metab_num'] == 0:
                self.logger.info('%s metab_num is 0, skip metabset_analysis module'%set_name)
                skip = 'all'
                continue
            elif self.map_file[set_name]['metab_num'] < 4:

                skip = 'cluster,vip,corr_cluster'
                if isinstance(self.hmdb_all_data, str):
                    skip += ',hmdb'
                else:
                    if self._check_skip_hmdb(self.hmdb_all_data,self.map_file[set_name]['metab_list']):
                        skip += ',hmdb'
            else:
                skip = ''
                if isinstance(self.hmdb_all_data ,str):
                    skip = 'hmdb'
                else:
                    if self._check_skip_hmdb(self.hmdb_all_data,self.map_file[set_name]['metab_list']):
                        skip = 'hmdb'

            metab_module = self.add_module("metabolome.metabset_analysis")
            opts = {
                'metabset' : self.map_file[set_name]['file'],
                'metabset_row' : self.map_file[set_name]['file_row'],
                'anno_overview': self.option('anno_overview'),
                'ko_overview': self.option('ko_overview'),
                'metab_trans': self.option('metab_trans'),
                'metab_table': self.rm_qc_abund,       #self.option('metab_table'),
                'scale_exp' : self.scale_exp,
                'species' : self.option("species")
            }
            if 'hmdb' not in skip:
                opts['hmdb_overview'] = self.option('hmdb_overview')

            #如果self.option("diff_dir") 路径下不存在diff.exp.xls ，则不作vip分析。

            if not self.option("diff_dir").is_set or not os.path.exists(self.option("diff_dir").path+'/{}.diff.exp.xls'.format(set_name)):
                skip = skip+',vip'
                opts['skip_analysis'] = skip
            else:
                opts['diff_dir'] = self.option('diff_dir')
                opts['skip_analysis'] = skip
            opts['database_version'] = self.option("database_version") ## add by qingchen.zhang用于区分新老工作流
            self.logger.info('%s skip %s'%(set_name,skip))
            metab_module.set_options(opts)
            self.map_file[set_name]['module'] = metab_module

    def run(self):
        super(MetabsetsModule, self).run()
        self.run_modules()
        module_list = [ self.map_file[k]['module'] for k in self.map_file]
        if len(module_list) == 1:
            module_list[0].on('end',self.set_output)
        elif len(module_list) > 1:
            self.on_rely(module_list,self.set_output)
        for m in module_list:
            m.run()

    def set_output(self):
        dirs = [
            'MetabsetCluster',
            'MetabsetVip',
            'MetabsetCorr',
            'MetabsetKeggc',
            'MetabsetKeggp',
            'MetabsetEnrich',
            'MetabsetHmdb' ,
            'MetabsetIpath'
        ]
        for d in dirs:
            out_dir = self.output_dir +'/' +d
            if os.path.exists(out_dir):
                shutil.rmtree(out_dir)
            os.mkdir(out_dir)

        for set_name in self.map_file:
            if 'module' not in self.map_file[set_name].keys():
                 continue
            for d in dirs:
                ori_dir = self.map_file[set_name]['module'].output_dir + '/' + d
                if not os.path.exists(ori_dir):
                    continue
                tar_dir = self.output_dir +'/'+ d + '/' + set_name
                shutil.copytree(ori_dir,tar_dir)

        self.end()

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

class TestFunction(unittest.TestCase):
    '''
    This is test for the module. Just run this script to do test.
    '''

    def test(self):
        from mbio.workflows.single import SingleWorkflow
        from biocluster.wsheet import Sheet

        data = {
            "id": "metabset2",
            "type": "module",
            "name": "metabolome.metabsets",
            "instant": False,
            "options": dict(
                anno_overview = '/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/Anno/AnnoOverview/output/anno.xls',
                mul_metabset = '/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/CreatTable/output/merge_mul.metabset.xls',
                ko_overview = '/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/Anno/AnnoOverview/output/ko.xls',
                hmdb_overview = "/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/AnnoHmdb/output/HmdbLevel_Origin.xls",
                diff_dir = "/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/DiffPls/MergeDiff/output/tmp_DiffStat",
                metab_trans = "/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/Preprocess/output/mix/metab_desc.txt",
                metab_table="/mnt/ilustre/users/sanger-dev/workspace/20190816/Metabolome_tsg_35242/Preprocess/output/mix/metab_abund.txt",
               # scale_exp=
            )
        }
        wsheet = Sheet(data=data)
        wf = SingleWorkflow(wsheet)
        wf.run()

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


import random
import math
import operator

razas = {0:[1,0,0,0,0,0,0,0,1,1,"liquido"],1:[0,1,0,1,0,0,1,0,0,0,"comida"],
         2:[0,0,1,0,1,0,0,0,0,0,"documento"],3:[0,0,0,0,0,1,0,1,0,0,"electronico"],
         4:[1,1,0,1,0,0,1,0,1,1,"licomida"],5:[0,0,1,0,1,1,0,1,0,0,"electromento"],
         6:[0,1,0,1,0,1,1,1,0,0,"elecomida"]}

def crear_articulos(Articulos):
	fichero=open('Articulos.txt','r')
	linea=fichero.readline()
	cont=1
	while linea!='':
		x=linea.split(",")
		Articulos[cont]={'Peso':int(x[0]),'Importancia':int(x[1]),
                   'Existencia':int(x[2]),'Tipo':x[3][:-1]}
		linea=fichero.readline()
		cont+=1
	print(Articulos)

def creacion_razizta(n_articulos,razas,n_soluciones,Articulos,pesoMax,lista_cajas):
    pos=0
    peso=0
    individuos_raza= n_soluciones/7
    individuos = 0
    raza=0
    while pos < n_soluciones:
        nuevo_cromosoma = razas[raza].copy()
        for i in range(0,n_articulos):
            if nuevo_cromosoma[i] == 1:
                nuevo_cromosoma[i] = random.randint(0,Articulos[i+1]['Existencia'])
        for i in range(0,n_articulos):
            peso=peso+(nuevo_cromosoma[i]*Articulos[i+1]['Peso'])

        if peso <= pesoMax:
            lista_cajas.append(nuevo_cromosoma)
            pos+=1
            individuos += 1
            if individuos == individuos_raza:
                individuos=0
                raza += 1
        peso = 0
    return lista_cajas

def crear_cromosomas(n_articulos,n_soluciones,Articulos,pesoMax,lista_cajas,modo):
	pos=0
	peso=0
	while pos < n_soluciones:
		nuevo_cromosoma = []
		for i in range(0, n_articulos):
			if modo==1:
				n = random.randint(0,Articulos[i+1]['Existencia'])
			else:
				n = random.randint(0,1)
			nuevo_cromosoma.append(n)
		print(nuevo_cromosoma)
		for i in range(0,n_articulos):
			peso=peso+(nuevo_cromosoma[i]*Articulos[i+1]['Peso'])
		print(peso)

		if peso <= pesoMax:
				lista_cajas.append(nuevo_cromosoma)
				pos+=1
		peso=0
	return lista_cajas

def evaluacion_razizta(evaluacion_ordenada,lista_cajas,razas,n_articulos,Articulos):
    print("\n####################\n######Evaluando individuos######\n####################\n")
    if len(evaluacion_ordenada) > 0:
        evaluacion_ordenada.clear()
    pos = 0
    cromosomas_evaluados = {}
    for lista in lista_cajas:
        peso=0
        importancia=0
        for i in range(0,n_articulos):
            	peso=peso+(lista[i]*Articulos[i+1]['Peso'])
        for i in range(0,n_articulos):
            	importancia=importancia+(lista[i]*Articulos[i+1]['Importancia'])
        if importancia == 0:
            evaluacion = 10000
        else:
            evaluacion = peso/importancia
        cromosomas_evaluados[pos] = (evaluacion,lista[n_articulos])
        pos += 1

    evaluacion_ordenada = sorted(
        cromosomas_evaluados.items(), key=operator.itemgetter(1))
    return evaluacion_ordenada

def evaluacion(evaluacion_ordenada, lista_cajas,n_articulos,Articulos):
    print("######Evaluando individuos######")
    if len(evaluacion_ordenada) > 0:
        evaluacion_ordenada.clear()
    pos = 0
    cromosomas_evaluados = {}
    for lista in lista_cajas:
        peso=0
        importancia=0
        for i in range(0,n_articulos):
            	peso=peso+(lista[i]*Articulos[i+1]['Peso'])
        for i in range(0,n_articulos):
            	importancia=importancia+(lista[i]*Articulos[i+1]['Importancia'])
        if importancia == 0:
            evaluacion = 10000
        else:
            evaluacion = peso/importancia
        cromosomas_evaluados[pos] = evaluacion
        pos += 1
    evaluacion_ordenada = sorted(
        cromosomas_evaluados.items(), key=operator.itemgetter(1))

    return evaluacion_ordenada

def seleccion_razizta(evaluacion_ordenada,lista_cajas):
    print("######Seleccionando individuos######")
    tamaño = len(lista_cajas)
    pes = 0
    raza = 0
    cont_liquido = 0
    cont_comida = 0
    cont_electronico = 0
    cont_documento = 0
    cont_elecomida = 0
    cont_electromento = 0
    cont_licomida = 0
    suma_total = 0
    pos = -1

    while suma_total < tamaño / 2:

        if evaluacion_ordenada[pos][1][1] == "liquido" and cont_liquido < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_liquido += 1
            pes += 1
        elif evaluacion_ordenada[pos][1][1] == "comida" and cont_comida < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_comida += 1
            pes += 1
        elif evaluacion_ordenada[pos][1][1] == "electronico" and cont_electronico < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_electronico += 1
            pes += 1
        elif evaluacion_ordenada[pos][1][1] == 'documento' and cont_documento < 2:
            print(evaluacion_ordenada[pos][1][1])
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) -1):
                print(lista_cajas.pop(evaluacion_ordenada[pos][0] - pes))
            else:
                print(lista_cajas.pop(evaluacion_ordenada[pos][0]))
            evaluacion_ordenada.pop(pos)
            cont_documento += 1
            print(cont_documento)
            pes += 1
        elif evaluacion_ordenada[pos][1][1] == "elecomida" and cont_elecomida < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_elecomida += 1
            pes += 1
        elif  evaluacion_ordenada[pos][1][1] == "electromento" and cont_electromento < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_electromento += 1
            pes += 1
        elif evaluacion_ordenada[pos][1][1] == "licomida" and cont_licomida < 2:
            if evaluacion_ordenada[pos][0] > (len(lista_cajas) - 1):
                lista_cajas.pop(evaluacion_ordenada[pos][0] - pes)
            else:
                lista_cajas.pop(evaluacion_ordenada[pos][0])
            evaluacion_ordenada.pop(pos)
            cont_licomida += 1
            pes += 1
        else:
            pos -= 1

        suma_total = (cont_liquido + cont_comida+cont_documento+cont_elecomida+
                      cont_electromento+cont_electronico+cont_licomida)

    return lista_cajas

def seleccionar(evaluacion_ordenada, lista_cajas):
    print("\n####################\n######Seleccionando individuos######\n####################\n")
    tamaño = len(lista_cajas)
    pos = 0
    while pos < tamaño / 2:
        if evaluacion_ordenada[-1][0] > (len(lista_cajas) - 1):
            lista_cajas.pop(evaluacion_ordenada[-1][0] - pos)
        else:
            lista_cajas.pop(evaluacion_ordenada[-1][0])
        evaluacion_ordenada.pop(-1)
        pos += 1
    return lista_cajas

def cruzamiento(lista_cajas, intervalo, n_articulos, cont, largo,intervalo2,
                modo,Articulos):
    print("#######CRUZAMIENTO#######")
    if (cont+1)<largo:
        nuevo_cromosoma=[]
        pos=0
        while pos<intervalo:
            nuevo_cromosoma.append(lista_cajas[cont][pos])
            pos += 1
        while pos<intervalo2:
            nuevo_cromosoma.append(lista_cajas[cont+1][pos])
            pos += 1
        while pos<(len(lista_cajas[0])):
            nuevo_cromosoma.append(lista_cajas[cont][pos])
            pos += 1
        print(nuevo_cromosoma)
        lista_cajas.append(nuevo_cromosoma)
        print(lista_cajas)
        nuevo_cromosoma.clear()
        pos=0
        while pos<intervalo:
            nuevo_cromosoma.append(lista_cajas[cont+1][pos])
            pos += 1
        while pos<intervalo2:
            nuevo_cromosoma.append(lista_cajas[cont][pos])
            pos += 1
        while pos<(len(lista_cajas[0])):
            nuevo_cromosoma.append(lista_cajas[cont+1][pos])
            pos += 1
        print(nuevo_cromosoma)
        if modo == 1:
            nuevo_cromosoma=mutacion_chida(nuevo_cromosoma,[],n_articulos,Articulos)
        else:
            nuevo_cromosoma=mutacion_simple(nuevo_cromosoma)
        lista_cajas.append(nuevo_cromosoma)
        print(lista_cajas)
        cruzamiento(lista_cajas,intervalo,n_articulos,cont+2,largo, intervalo2,
                    modo,Articulos)

def deli_razizta(lista_cajas, intervalo, n_articulos, cont, largo,intervalo2):
    print("\n####################\n#######CRUZAMIENTO#######\n####################\n")
    if (cont+1)<largo:
        if lista_cajas[cont][n_articulos] == lista_cajas[cont+1][n_articulos]:
            if lista_cajas[cont][n_articulos] == "liquido":
                exclusiones = [1,2,3,4,5,6,7]
            elif lista_cajas[cont][n_articulos] == "comida":
               exclusiones = [0,2,4,5,7,8,9]
            elif lista_cajas[cont][n_articulos] == "electronico":
                exclusiones = [0,1,2,3,4,6,8,9]
            elif lista_cajas[cont][n_articulos] == 'documento':
               exclusiones = [0,1,3,5,6,7,8,9]
            elif lista_cajas[cont][n_articulos] == "elecomida" :
                exclusiones = [0,2,4,8,9]
            elif  lista_cajas[cont][n_articulos] == "electromento":
                exclusiones = [0,1,3,6,8,9]
            elif lista_cajas[cont][n_articulos] == "licomida":
                exclusiones = [2,4,5,7]
            print(lista_cajas[cont][n_articulos])
            print(lista_cajas[cont+1][n_articulos])
            nuevo_cromosoma=[]
            pos=0
            while pos<intervalo:
                nuevo_cromosoma.append(lista_cajas[cont][pos])
                pos += 1
            while pos<intervalo2:
                nuevo_cromosoma.append(lista_cajas[cont+1][pos])
                pos += 1
            while pos<(len(lista_cajas[0])):
                nuevo_cromosoma.append(lista_cajas[cont][pos])
                pos += 1
            print(nuevo_cromosoma)
            lista_cajas.append(nuevo_cromosoma)
            print(lista_cajas)
            nuevo_cromosoma2=[]
            pos=0
            while pos<intervalo:
                nuevo_cromosoma2.append(lista_cajas[cont+1][pos])
                pos += 1
            while pos<intervalo2:
                nuevo_cromosoma2.append(lista_cajas[cont][pos])
                pos += 1
            while pos<(len(lista_cajas[0])):
                nuevo_cromosoma2.append(lista_cajas[cont+1][pos])
                pos += 1
            print(nuevo_cromosoma)
            nuevo_cromosoma=mutacion_chida(nuevo_cromosoma,exclusiones)
            lista_cajas.append(nuevo_cromosoma2)
            print(lista_cajas)
        deli_razizta(lista_cajas,intervalo,n_articulos,cont+1,largo,intervalo2)

def mutacion_simple(cromosoma,n_articulos):
    print("######Una mutación salvaje ha aparecido######")
    pos = random.randint(0,n_articulos-1)
    if cromosoma[pos] == 0:
        cromosoma[pos] = 1
    elif cromosoma[pos] == 1:
        cromosoma[pos] = 0
    return cromosoma

def mutacion_chida(cromosoma,exclusiones,n_articulos,Articulos):
    pos = random.randint(0,n_articulos-1)
    exclusiones.append(pos)
    nueva_cantidad = random.randint(0,Articulos[pos+1]['Existencia'])
    cantidad_original = cromosoma[pos]
    no_disponibles = True
    if nueva_cantidad > cantidad_original:
        print("######Una mutación salvaje ha aparecido######")
        pos2 = random_personalizado(exclusiones,n_articulos-1)
        cromosoma[pos] = nueva_cantidad
        cromosoma[pos2] = random.randint(0,cromosoma[pos2])
        return cromosoma
    elif nueva_cantidad < cantidad_original:
        print("######Una mutación salvaje ha aparecido######")
        pos2 = random_personalizado(exclusiones,n_articulos-1)
        cromosoma[pos] = nueva_cantidad
        cromosoma[pos2]= random.randint(cromosoma[pos2],Articulos[pos2+1]['Existencia'])
        return cromosoma
    else:
        return cromosoma

def random_personalizado(exclusiones,rango):
    randInt = random.randint(0,rango)
    return random_personalizado(exclusiones,rango) if randInt in exclusiones else randInt

def resultado(lista_cajas,evaluacion_ordenada,n_articulos,Articulos):
    print("#######RESULTADO#######")
    evaluacion_ordenada=evaluacion(evaluacion_ordenada,lista_cajas,n_articulos,Articulos)
    for lista in evaluacion_ordenada:
        print("Cromosoma:"+str(lista_cajas[lista[0]]))
        peso=0
        importancia=0
        for i in range(0,n_articulos):
        	peso=peso+(lista_cajas[lista[0]][i]*Articulos[i+1]['Peso'])
        for i in range(0,n_articulos):
        	importancia=importancia+(lista_cajas[lista[0]][i]*Articulos[i+1]['Importancia'])
        print("Peso:"+str(peso))
        print("Importancia:"+str(importancia))
        print("Evaluacion:"+str(lista[1]))
        print("\n")
    print("#######Mejor resultado#######")
    print("Cromosoma:"+str(lista_cajas[evaluacion_ordenada[0][0]]))
    peso=0
    importancia=0
    for i in range(0,n_articulos):
    	peso=peso+(lista_cajas[evaluacion_ordenada[0][0]][i]*Articulos[i+1]['Peso'])
    for i in range(0,n_articulos):
    	importancia=importancia+(lista_cajas[evaluacion_ordenada[0][0]][i]*Articulos[i+1]['Importancia'])
    print("Peso:"+str(peso))
    print("Importancia:"+str(importancia))
    print("Evaluacion:"+str(evaluacion_ordenada[0][1]))

def init():
    Articulos={}
    lista_cajas = []
    n_soluciones=28
    n_articulos = 10
    n_cogelones=n_articulos/2
    stop=3
    evaluacion_ordenada = []
    maxOmin = 2
    intervalo = 4
    intervalo2 = 4
    n_gen = 10
    pesoMax=25
    modo=1

    if modo == 0 or modo == 1:
        crear_articulos(Articulos)
        lista_cajas=crear_cromosomas(n_articulos,n_soluciones,Articulos,pesoMax,
                                     lista_cajas,modo)
        for i in range(0,n_gen):
            print(lista_cajas)
            evaluacion_ordenada=evaluacion(evaluacion_ordenada,lista_cajas,n_articulos,
                                           Articulos)
            print(evaluacion_ordenada)
            lista_cajas=seleccionar(evaluacion_ordenada,lista_cajas)
            cruzamiento(lista_cajas,intervalo,n_articulos,0,len(lista_cajas),
                        intervalo2,modo,Articulos)
        resultado(lista_cajas,evaluacion_ordenada,n_articulos,Articulos)

    elif modo == 2:
        crear_articulos(Articulos)
        lista_cajas=creacion_razizta(n_articulos,razas,n_soluciones,Articulos,pesoMax,lista_cajas)

        for i in range(0,n_gen):
            print(lista_cajas)
            evaluacion_ordenada=evaluacion_razizta(evaluacion_ordenada,lista_cajas,razas,n_articulos,Articulos)
            print(evaluacion_ordenada)
            lista_cajas=seleccionar(evaluacion_ordenada,lista_cajas)
            print(lista_cajas)
            print(evaluacion_ordenada)
            deli_razizta(lista_cajas,intervalo,n_articulos,0,len(lista_cajas),intervalo2)
        resultado(lista_cajas,evaluacion_ordenada)

if __name__ == '__main__':
        init()


# This module find the type of angle. """
# zero angle=0
# acute angle is >0 and <90
# obtuse angle is >90 and <180
# striaght angle is 180
# Right angle is 90
# Reflex angle is more than 180 and < 360(0)
#
# Author: Joseph Emmanuel
# March 2019

def ang_type(angle):
	"""finds out the type of angle for the given angle in degrees.
	Valid angles are from 0 to 360 degrees"""

	if angle == 0 or angle==360:
		return( " Zero angle " )
	elif angle > 0 and angle <90 :
		return( " Acute angle " )
	elif angle >90 and angle <180 :
		return( " Obtuse angle " )
	elif angle ==180 :
		return( " Striaght angle " )
	elif angle ==90:
		return( " Right angle " )
	else :
		return( " ERROR!" )
#end of function

a=int(input("Enter an angle 0 to 360 degrees only : "))
angletypestring=ang_type(a)
print("The entered angle is of type : ", angletypestring)


import math

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
# from model.CNN import CNN_only

import numpy as np

class PositionEncoder(nn.Module):
    def __init__(self, f_dim, max_seq_len, dropout_rate=0.5):
        super(PositionEncoder, self).__init__()

        self.max_seq_len = max_seq_len
        self.f_dim = f_dim
        self.dropout = nn.Dropout(dropout_rate)
        # self.LayerNorm = nn.LayerNorm([max_seq_len, f_dim])

        self.position_embeddings = nn.Embedding(max_seq_len, f_dim)
        # initialize_weight(self.position_embeddings)

    def forward(self, x):
        position_ids = torch.arange(self.max_seq_len, dtype=torch.long, device=x.device)

        position_ids = position_ids.unsqueeze(0).expand(x.shape[0], self.max_seq_len)

        position_embeddings = self.position_embeddings(position_ids)

        x = x + position_embeddings
        return self.dropout(x)

def initialize_weight(x):
    nn.init.xavier_uniform_(x.weight)
    if x.bias is not None:
        nn.init.constant_(x.bias, 0)

class GEGLU(nn.Module):
    # https://arxiv.org/pdf/2002.05202.pdf
    # 有关于GEGLU函数的定义：GEGLU(x, W, V, b, c) = GELU(xW + b) ⊗ (xV + c)
    # GLU = gated linear units

    def forward(self, x):
        # print('in geglu x={}'.format(x.shape)) # x=[1, 256, 4096]
        x, gates = x.chunk(2, dim = -1)
        # 按照最后一列切割成两个tensor
        # print('in middle of geglu x={}, gates={}'.format(x.shape, gates.shape))
        # x=[1, 256, 2048], gates=[1, 256, 2048]

        out = x * F.gelu(gates)
        # print('out geglu out={}'.format(out.shape))
        # out=[1, 256, 2048]
        return out

def fourier_encode(x, max_freq, num_bands = 4, base = 2):
    x = x.unsqueeze(-1)
    device, dtype, orig_x = x.device, x.dtype, x

    scales = torch.logspace(1., np.log(max_freq / 2) / np.log(base), num_bands,
                            base = base, device = device, dtype = dtype)
    scales = scales[(*((None,) * (len(x.shape) - 1)), Ellipsis)]

    x = x * scales * np.pi
    x = torch.cat([x.sin(), x.cos()], dim=-1)
    x = torch.cat((x, orig_x), dim = -1)
    return x

class MultiHeadAtten(nn.Module):
    """
    The multi-head attention module in transformer.

    f_dim: the dimension of input data;
    atten_layer_type: the type of the layers to generate Q, K, V. (1D-Conv, Linear);
    n_heads: the number of heads in the multi-head attention module;
    mask: the mask added to the attentional score;
    """
    def __init__(self, f_dim, atten_layer_type, dim_per_head, n_heads=8, need_mask=False, dropout_rate=0.5):
        super(MultiHeadAtten, self).__init__()
        self.f_dim = f_dim
        self.n_heads = n_heads
        self.dim_per_head = dim_per_head

        self.atten_layer_type = atten_layer_type
        self.need_mask = need_mask
        self.dropout = nn.Dropout(dropout_rate)

        self.K_layer = self.kqv_generate_layer(atten_layer_type, f_dim, dim_per_head*n_heads)
        # initialize_weight(self.K_layer)
        self.Q_layer = self.kqv_generate_layer(atten_layer_type, f_dim, dim_per_head*n_heads)
        # initialize_weight(self.Q_layer)
        self.V_layer = self.kqv_generate_layer(atten_layer_type, f_dim, dim_per_head*n_heads)
        # initialize_weight(self.V_layer)
        self.combine_heads = nn.Linear(in_features=self.n_heads*self.dim_per_head, out_features=f_dim)
        # initialize_weight(self.combine_heads)

    def forward(self, x):
        batch_size = x.size(0)
        # the shape of x: [batch_size, num_segments, f_dim]
        # separated shape: [batch_size, n_heads, number_segments, f_dim/n_heads]
        K = self.K_layer(x)
        K = self.seperate_heads(K, self.n_heads, self.dim_per_head)
        Q = self.Q_layer(x)
        Q = self.seperate_heads(Q, self.n_heads, self.dim_per_head)
        V = self.V_layer(x)
        V = self.seperate_heads(V, self.n_heads, self.dim_per_head)

        attn_x = self.attention(K, Q, V, self.need_mask)
        attn_x = attn_x.transpose(1,2).contiguous().view(batch_size, -1, self.n_heads*self.dim_per_head)

        attn_x = self.combine_heads(attn_x)

        return attn_x

    def kqv_generate_layer(self, atten_layer_type, f_dim,  out_dim):
        if atten_layer_type == "Linear":
            return nn.Linear(in_features=f_dim, out_features=out_dim)
        elif atten_layer_type == '1D-Conv':
            return None
        else:
            print("The 'atten_layer_type' must be the one of {'Linear', '1D-Conv'}!")
            return None

    def seperate_heads(self, x, num_heads, dim_per_head):
        batch_size = x.size(0)
        x = x.view(batch_size, -1, num_heads, dim_per_head)
        return x.permute(0, 2, 1, 3)

    def generate_mask(self, mask):
        pass

    def attention(self, K, Q, V, need_mask):
        if need_mask:
            pass
        else:
            weight = F.softmax(torch.matmul(Q, K.permute(0, 1, 3, 2))/np.power(K.shape[-1], 0.5), dim=-1)
            output = torch.matmul(weight, V)

            return output

class FeedForwardLayer(nn.Module):
    def __init__(self, f_dim, ffn_dim, dropout_rate=0.5):
        super(FeedForwardLayer, self).__init__()
        self.f_dim = f_dim
        self.ffn1 = nn.Linear(f_dim, ffn_dim)
        # initialize_weight(self.ffn1)
        self.dropout = nn.Dropout(dropout_rate)
        # self.ffn2 = nn.Linear(ffn_dim, ffn_dim)
        # # initialize_weight(self.ffn2)
        # self.ffn3 = nn.Linear(ffn_dim, ffn_dim)
        # initialize_weight(self.ffn3)

        self.ffn6 = nn.Linear(ffn_dim, f_dim)
        # initialize_weight(self.ffn6)

        self.geglu = GEGLU()

    def forward(self, x):
        batch_size = x.shape[0]
        seq_len = x.shape[1]
        x = x.view(batch_size*seq_len, self.f_dim)

        x = F.relu(self.dropout(self.ffn1(x)))
        # x = F.relu(self.dropout(self.ffn2(x)))
        # x = F.relu(self.dropout(self.ffn3(x)))

        output = F.relu(self.dropout(self.ffn6(x)))
        output = output.view(batch_size, seq_len, self.f_dim)
        return output

class EncoderLayer(nn.Module):
    def __init__(self, max_seq_len, f_dim, dim_per_head, num_heads, ffn_dim, dropout_rate):
        super(EncoderLayer, self).__init__()

        self.attention = MultiHeadAtten(f_dim=f_dim, atten_layer_type='Linear',
                                        dim_per_head=dim_per_head, n_heads=num_heads)
        self.LayerNorm = nn.LayerNorm([max_seq_len, f_dim])
        self.FeedForwardLayer = FeedForwardLayer(f_dim, ffn_dim=ffn_dim, dropout_rate=dropout_rate)
        self.dropout = nn.Dropout(dropout_rate)

    def forward(self, x):
        atten_x = self.attention(x)
        resnorm_x = self.dropout(self.LayerNorm(atten_x))
        ffn_x = self.dropout(self.FeedForwardLayer(resnorm_x)) + x

        return ffn_x

class Encoder(nn.Module):
    """
    add the encoder linear to control the hidden dimension in transformer
    remove the LayerNorm operation before the classifier
    investigate: using the combine_head layer / remove the combine_layer
    """
    def __init__(self, max_seq_len=251, f_dim=60, dim_per_head=64, num_heads=8, ffn_dim=256, dropout_rate=0.1, num_layers=6):
        super(Encoder, self).__init__()

        # self.PositionEcoder = PositionEncoder(f_dim=f_dim, max_seq_len=max_seq_len, dropout_rate=dropout_rate)

        self.EncodingLayer = EncoderLayer(max_seq_len=max_seq_len,
                                          f_dim=f_dim, dim_per_head=dim_per_head, num_heads=num_heads,
                                          ffn_dim=ffn_dim, dropout_rate=dropout_rate)
        self.LayerNorm = nn.LayerNorm([max_seq_len, f_dim])
        self.Encoders = nn.ModuleList([self.EncodingLayer for _ in range(num_layers)])

        # ffn for classification of emotion and gender

        self.emotion_ffn = nn.Linear(in_features=max_seq_len*f_dim, out_features=4)
        self.emotion_ffn_center = nn.Linear(in_features=max_seq_len*f_dim, out_features=4)
        self.gender_ffn = nn.Linear(in_features=max_seq_len*f_dim, out_features=2)
        self.gender_ffn_center = nn.Linear(in_features=max_seq_len*f_dim, out_features=2)

    def forward(self, x):
        # x_local = self.CNN(x)

        x = x.transpose(2,1)

        # weights = []
        # x = self.encoder_linear(x)
        # pos_x = self.PositionEcoder(x)
        # x = pos_x
        for encoder in self.Encoders:
            x = encoder(x)
            # weights.append(weight.cpu().detach().numpy())

        x = self.LayerNorm(x).view(x.shape[0], -1)

        # x = torch.cat((x_local, x), dim=-1)
        # classifier
        emotion = self.emotion_ffn(x)
        emotion_center = self.emotion_ffn_center(x)
        gender = self.gender_ffn(x)
        gender_center = self.gender_ffn_center(x)

        #

        return emotion, gender, emotion_center, gender_center

class cleanEncoder(nn.Module):
    def __init__(self, max_seq_len, f_dim, dim_per_head, num_heads, ffn_dim, dropout_rate, num_layers):
        super(cleanEncoder, self).__init__()

        # self.PositionEcoder = PositionEncoder(f_dim=f_dim, max_seq_len=max_seq_len, dropout_rate=dropout_rate)
        self.EncodingLayer = EncoderLayer(max_seq_len=max_seq_len,
                                          f_dim=f_dim, dim_per_head=dim_per_head, num_heads=num_heads,
                                          ffn_dim=ffn_dim, dropout_rate=dropout_rate)

        self.Encoders = nn.ModuleList([self.EncodingLayer for _ in range(num_layers)])

    def forward(self, x):
        x = x.transpose(2,1)
        # pos_x = self.PositionEcoder(x)
        # x = pos_x
        # weights = []

        for encoder in self.Encoders:
            x = encoder(x)

            # weights.append(weight.cpu().detach().numpy())

        return x

if __name__ == "__main__":
    x = torch.randn(32, 60, 260)
    # model = Encoder(max_seq_len=260, f_dim=60, dim_per_head=60, num_heads=8, ffn_dim=2048, dropout_rate=0.1, num_layers=6)
    # emotion,  emotion_center, gender, gender_center = model(x)
    x = fourier_encode(x, 15)
    print(x.shape)
    b, *axis, _, device = x.shape

    axis_pos = list(map(lambda size: torch.linspace(-1., 1.,
                                                    steps=size), (60)))
    print(len(axis_pos))
    pos = torch.stack(torch.meshgrid(*axis_pos), dim=-1)
    print('pos={}'.format(pos.shape))  # pos=torch.size([224, 224, 2])


"""
CS101 Webscraping Project Example
Gwen Urbanczyk
"""
from bs4 import BeautifulSoup
import requests

def get_actors(movie_num):
    """
    Function goes through the first movie_num movies on the IMDb top 1000 movies page.
    (movie_num must be a multiple of 50 and 0 < x <= 1000) and scrapes thier titles
    and cast member list.
    Returns a tuple of a list of titles and a list of actor lists.
    """
    if movie_num % 50 != 0:
        raise ValueError("movie_num must be a multiple of 50")
    if movie_num < 0 or movie_num > 1000:
        raise ValueError("movie_num must be between 0 and 1000")

    list_base_url = 'https://www.imdb.com/search/title/?groups=top_1000&'
    titles = []
    actor_lists = []

    for page in range(1, movie_num, 50):
        # Building the URL...
        url = list_base_url
        if page != 1:
            url += "start={}".format(page)

        # Scraping the source code
        source = requests.get(url).text
        soup = BeautifulSoup(source, 'html.parser')
        url_base = 'https://www.imdb.com'

        soup = soup.find('div', class_ = "lister-list")

        # For each movie on the page...
        for entry in soup.find_all('div', class_="lister-item mode-advanced"):
            # Get the link to the movie's page
            content = entry.find('div', class_="lister-item-content")
            title = content.h3.a.text
            url_extension = content.h3.a['href']

            # Get the source code off the movie's page
            new_url = url_base + url_extension
            source2 = requests.get(new_url).text
            soup2 = BeautifulSoup(source2, 'html.parser')
            cast_list = []

            soup2 = soup2.find('div', id = 'pagecontent').find('div', id = 'main_bottom')
            soup2 = soup2.find('div', id="titleCast").find('table', class_="cast_list")

            # For each cast member in the grid...
            for cast_member in soup2.find_all('tr'):
                name = cast_member.find('td', class_="")
                # Needed because sometimes there are empty entries
                if name:
                    name = name.a.text
                    name = name.strip()
                    cast_list.append(name)

            # Append to result a tuple of the title and the cast_list
            titles.append(title)
            actor_lists.append(cast_list)

    return (titles, actor_lists)

def main():
    """
    Program scrapes the IMDb top 1000 movies page for the movie's titles and
    list of cast members, then prompts the user to enter a movie title, then an actor
    from that movie, and outputs all the movies that include that actor. Users can continue
    to enter movies and actors until they choose to exit the prompt loop.

    Warning! Program is very slow, even with low number of movies (50-100)
    """

    print("Hello! Below, please enter the number of movies you want to scrape off of IMDb's " + \
          "top 1000 movies list. Please note that this number should be a multiple of 50, as " + \
          "the list is organized into pages of 50 movies each. Also note the larger the number, " + \
          "the longer the program will take.")
    print()
    movies_to_scrape = int(input("Please enter the number of movies you want to scrape: "))

    # Scrape the data off the IMDb site
    titles, actors = get_actors(movies_to_scrape)

    done = False

    # Menu Loop for find movies with a given actor in them
    while not done:
        user_in = input("Enter \'t\' to see list of titles, " + \
                        "enter title name to get actor list, enter nothing to exit: ")

        # exit
        if user_in == "":
            done = True
        # print list of titles
        elif user_in == "t":
            print(titles)
        # Enter sub-menu for actor selection
        elif user_in in titles:
            # Print the list of actors
            print(actors[titles.index(user_in)])
            actor = input("Enter a actor to find other movies with them in it: ")
            print()

            result = ""
            # Find all the other movies with that actor in them
            for actor_list in actors:
                current_title = titles[actors.index(actor_list)]
                if current_title != user_in:
                    if actor in actor_list:
                        result += current_title + '\n'

            result = result.rstrip('\n')

            # Output result
            if result != "":
                print("Here are some other movies with {} in them that you might like!".format(actor))
                print(result)
            else:
                print(("Sorry! I couldn't find any other movies with {} in them, try " + \
                      "increasing the number of movies to scrape!").format(actor))

        else:
            print("Oops! Sorry! Command or Movie not recognized!")

if __name__ == "__main__":
    main()


import radio
from microbit import *
import time
radio.on()
while True:
    radio.config(channel=42, power=0)
    button_a.was_pressed()
    button_b.was_pressed()
    for i in range(10): radio.receive() # clear queue
    while not radio.receive(): # wait for msg
        time.sleep(5)

    send = ""
    while not send: # get attention
        display.show(Image("99999:"*5))
        time.sleep(0.2)
        display.clear()
        time.sleep(0.2)
        if button_a.was_pressed():
            send = "N"
        if button_b.was_pressed():
            send = "Y"

    radio.config(channel=42, power=7)
    im = Image("99900:90000:99900:00900:99900")
    for i in range(3):
        radio.send(send)
        display.show(im)
        time.sleep(0.5)
        display.clear()
        time.sleep(0.5)


import serial
import time
import urllib.request
import urllib.parse

class Serial_Controller:
    def __init__(self, port, baudrate):
        print("Serial Connecting...")
        self.ser = serial.Serial(port, baudrate, timeout=1)
        time.sleep(2)

    def send_msg(self, msg):
        self.ser.write(msg)
        print("send ", msg)

    def read_reply(self):
        reply = self.ser.readline()
        reply = reply.decode()
        reply = reply.rstrip("\n")
        reply = reply.rstrip("\r")
        print("recv ", reply)
        return reply

    def close(self):
        self.ser.close()

class Char_Getter:
    def __init__(self):
        self.url = "https://arcane-bayou-55620.herokuapp.com/getArray/"

    def get_charArray(self, c):
        print("REQUEST...")
        char = urllib.parse.quote(c)
        print("GET...")
        url = self.url + char
        array = urllib.request.urlopen(url).read()
        a = array.decode()
        a = self.reshape(a)
        return a

    def reshape(self, a):
        b = ""
        for i in range(8):
            for j in range(8):
                tmp = a[j*8+i]
                b += tmp
        return b


from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
from rest_framework.permissions import AllowAny
from rest_framework import status
from notificaciones.models import NotificacionRespaldo
from notificaciones.serializers import NotificacionRespaldoSerializer
from rest_framework.response import Response

from datetime import datetime

LISTA_DIAS_DICT = {0:'LUNES',1:'MARTES',2:'MIÉRCOLES',3:'JUEVES',4:'VIERNES',5:'SÁBADO',6:'DOMINGO'}
# Create your views here.

# Obtener las notificaciones por user_tag y si fueron o no revisadas
@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication, TokenAuthentication])
@permission_classes([AllowAny])
def notificaciones(request):
   if request.user.is_authenticated:

      print(request.GET)

      #Obtener params de un url
      user_tag = request.GET["user_tag"]

      response = []
      if "was_reviewed" in request.GET:
         fue_revisada = request.GET["was_reviewed"]
         response = NotificacionRespaldo.objects.filter(user_tag=user_tag).filter(fue_revisada=fue_revisada).distinct()
      else:
         response = NotificacionRespaldo.objects.filter(user_tag=user_tag).distinct()

      responseFinal = []
      for rsp in response:
         data_rsp = NotificacionRespaldoSerializer(rsp).data
         data_rsp['dia'] = LISTA_DIAS_DICT[data_rsp['dia']]

         if data_rsp['fecha'] is not None:
            data_rsp['fecha'] = data_rsp['fecha'].split(".")[0]
            data_rsp['fecha'] = datetime.strptime(data_rsp['fecha'], "%Y-%m-%dT%H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")

         responseFinal.append(data_rsp)
      return Response(responseFinal, status=status.HTTP_200_OK)

   msg = {
      'error': 'Permission Denied!'
   }
   return Response(msg, status=status.HTTP_403_FORBIDDEN)

# Actualizar la notificación
@api_view(['PUT'])
@authentication_classes([SessionAuthentication, BasicAuthentication, TokenAuthentication])
@permission_classes([AllowAny])
def notificacionUpdate(request, pk):

   #Valida que exista la relación
   if request.user.is_authenticated:
      try:
         data = NotificacionRespaldo.objects.get(pk=pk)
      except NotificacionRespaldo.DoesNotExist:
         return Response({'message': 'La notificación no existe'},status=status.HTTP_404_NOT_FOUND)

   #Actualiza el registro por pk, se puede actualizar por partes
   if request.method == 'PUT' and request.user.is_authenticated:
      requestFinal = {}
      requestFinal['fue_revisada'] = request.data['was_reviewed']
      serializer = NotificacionRespaldoSerializer(data, data=requestFinal, partial=True)
      if serializer.is_valid():
         serializer.save()
         return Response(serializer.data, status=status.HTTP_200_OK)
      return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

   else:
      msg = {
         'error': 'Permission Denied!'
      }
      return Response(msg, status=status.HTTP_403_FORBIDDEN)


import cv2
import numpy as np

canvas = np.ones([640,640,3], 'uint8')*255 # making a white canvas for drawing
color = (0,0,0)
radius = 5
pressed = False

def painter(event, x, y, flags, params):
    global canavs, pressed
    if event == cv2.EVENT_LBUTTONDOWN:
        pressed = True
        cv2.circle(canvas, (x,y), radius, color, -2)
    elif event == cv2.EVENT_MOUSEMOVE and pressed == True:
        cv2.circle(canvas, (x,y), radius, color, -2)
    elif event == cv2.EVENT_LBUTTONUP:
        pressed = False

cv2.namedWindow("canvas")
cv2.setMouseCallback("canvas", painter)

while True:
    cv2.imshow("canvas", canvas)

    k = cv2.waitKey(1)

    if k & 0xff == ord('q'):
        break

    if k & 0xff == ord('y'):
        color = (0, 255, 255) # yellow color

    if k & 0xff == ord('b'):
        color = (255,0,0) # blue color

    if k & 0xff == ord('r'):
        color = (0,0,255) # red color

    if k & 0xff == ord('g'):
        color = (0,255,0) # green color

    if k & 0xff == ord('c'):
        canvas = np.ones([640,640,3], 'uint8')*255

cv2.destroyAllWindows()


"""
Convenient setup for jupyter notebook explorations
Use via inserting:
   `from proteinworkshop.utils.notebook import *`
as the last import in your jupyter notebook imports cell.
"""

import os
import pathlib

import hydra
from loguru import logger as log

from proteinworkshop import constants

try:
    # Convenient for debugging
    import lovely_tensors as lt

    lt.monkey_patch()
except:
    pass

def init_hydra_singleton(
    path: os.PathLike = constants.HYDRA_CONFIG_PATH,
    reload: bool = False,
    version_base: str = "1.2",
) -> None:
    """Initialises the hydra singleton.

    .. seealso::
        https://stackoverflow.com/questions/60674012/how-to-get-a-hydra-config-without-using-hydra-main

    :param path: Path to hydra config, defaults to ``constants.HYDRA_CONFIG_PATH``
    :type path: os.PathLike, optional
    :param reload: Whether to reload the hydra config if it has already been
        initialised, defaults to ``False``
    :type reload: bool, optional
    :raises ValueError: If hydra has already been initialised and ``reload`` is
        ``False``
    """
    # See: https://stackoverflow.com/questions/60674012/how-to-get-a-hydra-config-without-using-hydra-main
    if reload:
        clear_hydra_singleton()
    try:
        path = pathlib.Path(path)
        # Note: hydra needs to be initialised with a relative path. Since the hydra
        #  singleton is first created here, it needs to be created relative to this
        #  file. The `rel_path` below takes care of that.
        rel_path = os.path.relpath(path, start=pathlib.Path(__file__).parent)
        hydra.initialize(rel_path, version_base=version_base)
        log.info(f"Hydra initialised at {path.absolute()}.")
    except ValueError:
        log.info("Hydra already initialised.")

def clear_hydra_singleton() -> None:
    """
    Clears the initialised hydra singleton.
    """
    if hydra.core.global_hydra.GlobalHydra not in hydra.core.singleton.Singleton._instances:  # type: ignore
        return
    hydra_singleton = hydra.core.singleton.Singleton._instances[hydra.core.global_hydra.GlobalHydra]  # type: ignore
    hydra_singleton.clear()
    log.info("Hydra singleton cleared and ready to re-initialise.")


from SkinCancer.Model import Net
from SkinCancer.Dataloader import TrainDataset
from torch import save
from torch.tensor import Tensor
import torch.nn as nn
from torch.optim import Adam
from torch.utils.data import DataLoader

model = Net()
optimizer = Adam(model.parameters())
criterion = nn.BCELoss()

train_dataset = TrainDataset()
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

def train(epoch):
	for step, data in enumerate(train_loader):
		train_x, train_y = data
		train_y = train_y.type('torch.FloatTensor')
		optimizer.zero_grad()
		y_pred = model.forward(train_x)
		loss = criterion(y_pred, train_y)
		loss.backward()
		optimizer.step()
		if step == len(train_dataset) // 32 - 1:
			print('Epoch {}, loss={}'.format(epoch, loss.item()))

for i in range(1, 11):
	train(i)

save(model.state_dict(), 'model_skin_cancer.pt')


__author__ = 'Kalyan'

max_marks = 25

notes = '''
A magic number is a number which results in 1 when the following process is done.

1. replace number with sum of squares of each digit (the result should be converted to same base)
2. repeat till you get a single digit

If result is 1, then it is a magic number, else it is not.

e.g. 28  is a magic number in base 10.
28 -> 68 ( 4 + 64) -> 100 (36 + 64) -> 1

12 is not a magic number in base 10
12 -> 5 ( 1 + 4)

12 is not a magic number is base 5
22 (12 in base 10 is 22 in base 5)
-> 13 (4 + 4 = 8 in base 5 is 13)
-> 20 (1 + 9 = 10 in base 5 is 20) ->
-> 4 (single digit and it is not 1, so not a magic number).

18 is a magic number in base 8

22 (18 in base 10 is 22 in base 8)
-> 10 ( 4+ 4 = 8 which is 10 in base 8)
-> 1 (single digit and it is 1, so it is a magic number)

Your job for this question is to write a couple of routines to find the first K magic numbers in base 8 in ascending order.

Notes:
1. k < 0 should raise ValueError.
2. Fill up the two routines below to get the job done. The 2nd method should call the first in a loop till
   it gets the 1st k magic numbers.
3. Use python builtins and data structures to solve this problem.
4. Use the debugger or pytutor or add prints if you get stuck :).
5. Note that both routines will be tested independently, so do not rename the methods or write all the logic in
   get_oct_magic_numbers method.
'''

# Given a number, returns True if it is a magic number is base 8, else False
# raise ValueError if number < 0
def check_magic(digits):
    s = sum([i**2 for i in digits])
    if s<8 and s>=0:
        return s
    else:
        while s > 7:
            a=[int(i) for i in list(oct(s)[2:])]
            s = check_magic(a)
        return s

def is_oct_magic(number):
    if number<0:
        raise ValueError
    dig =[int(i) for i in list(oct(number)[2:])]
    try:
        if check_magic(dig) == 1:
            return True
        else:
            return False
    except RecursionError:
        return False

def gen():
    i = 1
    while True:
        if is_oct_magic(i):
            yield(i)
        i = i+1

# This method makes use of is_oct_magic and returns a list of 1st k magic numbers in base 8
def get_oct_magic_numbers(k):
    if k<0:
        raise ValueError
    a = gen()
    lis = []
    for i in range(k):
        num = next(a)
        lis.append(num)
    return lis

# some basic tests given, write more according to given constraints. atleast check that
# you can generate 10 magic numbers

def test_is_oct_magic():
    assert is_oct_magic(7) == False
    assert is_oct_magic(1)==True
    assert is_oct_magic(85) == False
    assert is_oct_magic(26) == False
    assert is_oct_magic(14) == False
    assert is_oct_magic(1) == True
    assert is_oct_magic(18) == True

def test_get_oct_magic_numbers():
    assert [1, 8] == get_oct_magic_numbers(2)
    assert [1, 8, 18, 27, 64, 76, 77, 97, 105, 130]==get_oct_magic_numbers(10)