import sys
import ctypes
import time

from sdl2 import *
from sdl2.sdlttf import *
from math import cos, sin, pi
import sdl2.sdlgfx as gfx

from katago import KataGo
from board import Board, coordToStd
from history import Node
import parser
import sgffiles

DEBUG = False

#
#  Board layout parameters
#

# Cell size, should be odd because the intersection is 1px wide.
CELL_SIZE = 23
# Margin around the goban, in all four directions
MARGIN = 40
# Height of the control area at the bottom
CONTROLS = 100
# Stone radius, should be less than CELL_SIZE // 2
STONE_RADIUS = 10

SHOW_BLACK_HINTS = True
SHOW_WHITE_HINTS = True
SHOW_HEAT_MAP = True
SHOW_VARIATION = True
SHOW_DEAD_STONES = True

# The following are calculated values and not parameters:

# Window width
WIDTH = MARGIN + 19 * CELL_SIZE + MARGIN
# Window height
HEIGHT = MARGIN + 19 * CELL_SIZE + MARGIN + CONTROLS

#
#  Basic colors
#

WHITE = (255, 255, 255, 255)
BLACK = (0, 0, 0, 255)
GRAY  = lambda x: (x, x, x, 255)

# Hint colors
HINT_COLOR = (240, 240, 5, 255)
# Border color of the best move
PV_COLOR = (255, 0, 0, 255)

# Heat colors
HEAT_RED = (150, 25, 0, 255)
HEAT_BLACK = (25, 25, 25, 255)
def HEAT(x):
	if x == 0: return WHITE
	elif x < 0:
		x = - x
		mr, mg, mb, a = HEAT_BLACK
	else:
		mr, mg, mb, a = HEAT_RED
	r = int(x * mr + (1 - x) * 255)
	g = int(x * mg + (1 - x) * 255)
	b = int(x * mb + (1 - x) * 255)
	return (r, g, b, 255)

#
#  Global data
#

# Parsing informations
args = None

# Last time katago played
ltime = None

# Maximum number of shown hints
HINT_LIMIT = 33

# SDL Renderer
renderer = None

# Texture for black stones
tblackstone = None
# Texture for white stones
twhitestone = None

# Font for rendering, loaded from a TTF.
font = None
smallfont = None
tinyfont = None
# SDL event for KataGO
SDL_KATAGO = None

# Row names
ROWS = "ABCDEFGHJKLMNOPQRST"

# A class to make input management easier
class Inputs:

	def __init__(self):
		self.mousex = None
		self.mousey = None

	# set mouse coordinates
	def setMouse(self, x, y):
		self.mousex, self.mousey = x, y

	# Convert mouse coordinates to board coordinates
	def getCoordinates(self):
		if self.mousex == None or self.mousey == None: return None
		coord = getCoordinates(self.mousex, self.mousey)
		if coord == None: return None
		return coord
#
#  Coordinate abstraction
#  The following functions calculate coordinates.
#

# Intersection coordinates. Rows and columns are counted from 1 to 19.
def inter(row, col):
	half = (CELL_SIZE + 1) // 2
	x = MARGIN + half + CELL_SIZE * (row - 1)
	y = MARGIN + half + CELL_SIZE * (col - 1)
	return (x, y)

def getCoordinates(x, y):
	i = (x - MARGIN) // CELL_SIZE
	j = (y - MARGIN) // CELL_SIZE
	if i < 0 or i >= 19: return None
	if j < 0 or j >= 19: return None
	return j, i

# The rectangle around an intersection.
def cell_rect(row, col):
	half = (CELL_SIZE + 1) // 2
	x, y = inter(row, col)
	return (x - half, y - half, CELL_SIZE, CELL_SIZE)

#
#  Short drawing functions
#  These functions have implicit renderer, font, and wrap SDL_Rect and
#  SDL_Color arguments.
#

# Clears window.
def clear(color):
	SDL_SetRenderDrawColor(renderer, *color)
	SDL_RenderClear(renderer)

# Fills a rectangle.
def fillrect(x, y, width, height, color):
	SDL_SetRenderDrawColor(renderer, *color)
	SDL_RenderFillRect(renderer, SDL_Rect(x, y, width, height))

# Draws a line.
def line(x1, y1, x2, y2, color):
	SDL_SetRenderDrawColor(renderer, *color)
	SDL_RenderDrawLine(renderer, x1, y1, x2, y2)

# Renders text.
# Size of font if specified with the tfont parameter.
# - tfont can be "normal" or "tiny"
# The alignment of the string is specified with the two last parameters:
# - align_x can be "left", "center" or "right"
# - align_y can be "top", "center" or "bottom"
def text(x, y, string, color=BLACK, tfont="normal", align_x="center", align_y="center"):

	if tfont == "normal":
		tfont = font
	elif tfont == "tiny":
		tfont = tinyfont
	string = bytes(string, "utf8")
	surface = TTF_RenderText_Blended(tfont, string, SDL_Color(*color))
	texture = SDL_CreateTextureFromSurface(renderer, surface)

	w = surface.contents.w
	h = surface.contents.h

	if align_x == "center":
		x -= w // 2
	if align_x == "right":
		x -= w
	if align_y == "center":
		y -= h // 2
	if align_y == "bottom":
		y -= h

	SDL_RenderCopy(renderer, texture, None, SDL_Rect(x, y, w, h))
	SDL_FreeSurface(surface)
	SDL_DestroyTexture(texture)

# Draws a circle, because gfx's filledCircle is terrible.
def circle(cx, cy, radius, color):
	SDL_SetRenderDrawColor(renderer, *color)

	x = 0
	y = radius
	m = 5 - 4 * radius

	while x <= y:
		line(cx - x, cy + y, cx + x, cy + y, color)
		line(cx - y, cy + x, cx + y, cy + x, color)
		line(cx - x, cy - y, cx + x, cy - y, color)
		line(cx - y, cy - x, cx + y, cy - x, color)

		if m > 0:
			y -= 1
			m -= 8 * y
		x += 1
		m += 8 * x + 4

# Draw a triangle.
def triangle(cx, cy, radius, color):
	SDL_SetRenderDrawColor(renderer, *color)

	rcospi = int(radius*cos(pi/6))
	rsinpi = int(radius*sin(pi/6))
	line(cx, cy - radius, cx + rcospi, cy + rsinpi, color)
	line(cx + rcospi, cy + rsinpi, cx - rcospi, cy + rsinpi, color)
	line(cx - rcospi, cy + rsinpi, cx, cy - radius, color)

# Draws a stone. The owner can be either "black" or "white".
# - param - mode is "texture" or "draw". If to "texture", use tblackstone
#   and twhitestone to draw the stone. Otherwise, draw directly the stone.
def stone(x, y, owner, mode="texture"):

	if mode == "texture":
		if owner == "black" and tblackstone != None:
			w, h = 2*STONE_RADIUS, 2*STONE_RADIUS
			x, y = x - w//2, y - w//2
			SDL_RenderCopy(renderer, tblackstone, None, SDL_Rect(x, y, w+1, h+1))
			return None
		if owner == "white" and twhitestone != None:
			w, h = 2*STONE_RADIUS, 2*STONE_RADIUS
			x, y = x - w//2, y - w//2
			SDL_RenderCopy(renderer, twhitestone, None, SDL_Rect(x, y, w+1, h+1))
			return None

	main, border = (BLACK, WHITE) if owner == "black" else (WHITE, BLACK)
	r = STONE_RADIUS

	if owner == "white":
			circle(x, y, r, border)

	circle(x, y, r-2, main)
	gfx.aacircleRGBA(renderer, x, y, r-2, *main)
	gfx.aacircleRGBA(renderer, x, y, r-1, *border)
	gfx.aacircleRGBA(renderer, x, y, r, *border)

# Create the texture for a black stone
# - param - color is either "black" or "white" to accordingly initialize
# tblackstone and twhitestone.
def createStoneTexture(color):

	if color == "black":
		global tblackstone
		tblackstone = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
			SDL_TEXTUREACCESS_TARGET, STONE_RADIUS*2+1, STONE_RADIUS*2+1)
		SDL_SetRenderTarget(renderer, tblackstone)
		SDL_SetTextureBlendMode(tblackstone, SDL_BLENDMODE_BLEND)
		SDL_SetRenderDrawColor(renderer, *(255, 255, 255, 0))
		SDL_RenderFillRect(renderer, None)
		stone(STONE_RADIUS, STONE_RADIUS, "black", mode="draw")
		SDL_SetRenderTarget(renderer, None)

	if color == "white":
		global twhitestone
		SDL_SetRenderDrawColor(renderer, *(255, 255, 255, 0))
		twhitestone = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
			SDL_TEXTUREACCESS_TARGET, STONE_RADIUS*2+1, STONE_RADIUS*2+1)
		SDL_SetRenderTarget(renderer, twhitestone)
		SDL_SetTextureBlendMode(twhitestone, SDL_BLENDMODE_BLEND)
		SDL_SetRenderDrawColor(renderer, *(255, 255, 255, 0))
		SDL_RenderFillRect(renderer, None)
		stone(STONE_RADIUS, STONE_RADIUS, "white", mode="draw")
		SDL_SetRenderTarget(renderer, None)

	# SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND)

def destroyStoneTexture(color="black"):

	if color == "black":
		global tblackstone
		SDL_DestroyTexture(tblackstone)
	if color == "white":
		global twhitestone
		SDL_DestroyTexture(twhitestone)

def circ_mark(x, y, owner):
	main, border = (BLACK, WHITE) if owner == "black" else (WHITE, BLACK)
	r = STONE_RADIUS // 2
	gfx.aacircleRGBA(renderer, x, y, r, *border)

# Draw the score diagram
# - scores - list of scores (should be color-constant)
def drawScoreList(scores):
	if len(scores) <= 1: return None
	if not SHOW_BLACK_HINTS or not SHOW_WHITE_HINTS: return None

	GLOW_WHITE = (255, 255, 255, 125)
	srWidth = WIDTH//2
	srHeight = CONTROLS
	fillrect(0, HEIGHT - CONTROLS + 1, srWidth, CONTROLS, GRAY(50))
	m = min(-5, min(scores)) - 1
	M = max(5, max(scores)) + 1
	num = len(scores)
	dx = srWidth / (num - 1)

	x = 0
	prevx = None
	prevy = None

	middle = int(HEIGHT - CONTROLS * m / (m - M) + 1)
	line(0, middle, srWidth, middle, GRAY(192))

	five = 5 * (m // 5)
	while five <= M:
		l = (five - m) / (M - m)
		y = int(HEIGHT - CONTROLS * l + 1)
		line(0, y, srWidth, y, GLOW_WHITE)
		five += 5

	for i, score in enumerate(scores):
		l = (score - m) / (M - m)
		y = int(HEIGHT - CONTROLS * l + 1)
		x = int(i * dx)
		if prevx != None:
			delta = (y - prevy) / dx
			v = prevy
			for u in range(prevx, x):
				line(u, middle, u, int(v), GLOW_WHITE)
				v += delta
			# line(prevx, prevy, x, y, GRAY(0))
		prevx, prevy = x, y

# Draw a sequence of moves
# - moves - ordered coordinates list
# - pla - player playing first in the sequence
def draw_moves(moves, pla, limit=99):
	getowner = lambda c: "black" if c == Board.BLACK else "white"
	getcolor = lambda c: WHITE if c == Board.BLACK else BLACK
	for i, (col, row) in enumerate(moves):
		if i >= limit: break
		col, row = col + 1, row + 1
		stone(*inter(row, col), getowner(pla), mode="texture")
		if i < 9:
			text(*inter(row, col), str(i+1), color=getcolor(pla))
		else:
			text(*inter(row, col), str(i+1), color=getcolor(pla), tfont=smallfont)
		pla = Board.getOpponent(pla)

# Mark dead stones. Stones are marked according to the heat map.
# A stone is considered probably dead if it is landing in the opponent's
# teritory.
# board - a board object
def draw_dead_stones(board):
	for row in range(1, 20):
		for col in range(1, 20):
			owner = board.stones[col-1][row-1]
			p = board.deadValue(row-1, col-1)
			if p > 0:
				color = WHITE if owner == Board.BLACK else BLACK
				radius = int((1 + p) * STONE_RADIUS // 3)
				triangle(*inter(row, col), radius, color)

# Draw a inting stone
# - intensity - for the alpha-transparency
# - isFirst - set to Trye to draw a thick read border
def hint_stone(x, y, intensity=0.5, isFirst=False):
	r, g, b, a = HINT_COLOR
	a = int(255 * intensity)
	main = r, g, b, a
	border = PV_COLOR if isFirst else HINT_COLOR

	r = STONE_RADIUS
	circle(x, y, r-2, main)
	gfx.aacircleRGBA(renderer, x, y, r-2, *main)
	gfx.aacircleRGBA(renderer, x, y, r-1, *border)
	gfx.aacircleRGBA(renderer, x, y, r, *border)
	if isFirst: gfx.aacircleRGBA(renderer, x, y, r+1, *border)

# Adjust a string representing an integer to be 3 characters.
# e.g 1028 becomes 1.0k
# e.g 208512 becomes 0.2M
def adjustStr(txt):
	e = len(txt)
	if e <= 2: return txt
	if e == 3: return "0.{}k".format(txt[0])
	if e == 4: return "{}.{}k".format(txt[0], txt[1])
	if e == 5: return "{}{}k".format(txt[0], txt[1])
	if e == 6: return "0.{}M".format(txt[0])
	if e == 7: return "{}.{}M".format(txt[0], txt[1])
	if e == 8: return "{}{}M".format(txt[0], txt[1])
	if e == 9: return "0.{}G".format(txt[0])
	else: return "Lolwut did you truly made this many visits ?"

# Draw hint_informations - number of visits and score expectation
def hint_info(x, y, visits, score):
	visitstr = str(visits)
	if score > 0: scorestr = "+{:.0f}".format(score)
	else: scorestr = "-{:.0f}".format(-score)
	text(x, y-5, adjustStr(visitstr), tfont="tiny")
	text(x, y+5, scorestr, tfont="tiny")

#
#  Hint rendering function
#

def render_hints(pv, turn, board, coord=None):

	if turn == Board.BLACK and not SHOW_BLACK_HINTS:
		return None
	if turn == Board.WHITE and not SHOW_WHITE_HINTS:
		return None

	global SHOW_VARIATION
	SHOW_VARIATION = SHOW_BLACK_HINTS and SHOW_WHITE_HINTS

	maxVisits = 0
	for visits, _, _, _, _ in pv:
		maxVisits = max(maxVisits, visits)
	if maxVisits == 0: maxVisits = 1

	drawnSeq = False
	for _, _, _, _, moves in pv:
		i, j = moves[0]
		if moves[0] == coord and board.stones[i][j] == Board.EMPTY:
			if SHOW_VARIATION:
				# Show the whole sequence only if 'show_variation' is on
				draw_moves(moves, turn)
			else:
				# Else, just draw one move
				draw_moves([moves[0]], turn)
			drawnSeq = True

	if drawnSeq: return None

	isFirst = True
	for i, (visits, winrate, scoreMean, scoreStDev, moves) in enumerate(pv):
		if i >= HINT_LIMIT: break
		col, row = moves[0]
		if board.stones[col][row] != Board.EMPTY: continue
		hint_stone(*inter(row+1, col+1), intensity=visits/maxVisits, isFirst=isFirst)
		if SHOW_BLACK_HINTS and SHOW_WHITE_HINTS:
			hint_info(*inter(row+1, col+1), visits, scoreMean)
		isFirst = False

#
#  Board rendering function
#

def render(board, history, coord=None):

	## Getting informations
	# - pla : color of current player
	# - pv : principal variation_S_ information
	# - lmove : coordinates of last move (None of none)
	pla = history.getTurn()
	pv = history.getPV()
	lmove = history.getLastMove()

	## Clear the board - draw mondain rectangles and text
	clear(WHITE)
	fillrect(0, HEIGHT - CONTROLS + 1, WIDTH, CONTROLS, GRAY(192))

	text(3 * WIDTH // 4, HEIGHT - CONTROLS // 2, "KataGo Analyzer", BLACK)

	## Heat map
	if SHOW_HEAT_MAP:
		for row in range(1, 20):
			for col in range(1, 20):
				fillrect(*cell_rect(row, col), HEAT(board.heat[row-1][col-1]))

	## Goban lines - Hoshi
	for i in range(1, 20):
		line(*inter(1, i), *inter(19, i), GRAY(128))
		line(*inter(i, 1), *inter(i, 19), GRAY(128))
	for i in [4, 10, 16]:
		for j in [4, 10, 16]:
			x, y = inter(i, j)
			circle(x, y, 3, GRAY(128))
	# Goban boundary
	line(*inter(1, 1),  *inter(1, 19),  BLACK)
	line(*inter(1, 19), *inter(19, 19), BLACK)
	line(*inter(1, 1),  *inter(19, 1),  BLACK)
	line(*inter(19, 1), *inter(19, 19), BLACK)
	# Coordinates
	for i in range(1, 20):
		x, y = inter(i, 1)
		text(x, y - 24, ROWS[i-1], BLACK, align_x="center", align_y="bottom")
		x, y = inter(i, 19)
		text(x, y + 24, ROWS[i-1], BLACK, align_x="center", align_y="top")
		x, y = inter(1, i)
		text(x - 32, y, str(20-i), BLACK, align_x="center", align_y="center")
		x, y = inter(19, i)
		text(x + 32, y, str(20-i), BLACK, align_x="center", align_y="center")

	## Stones
	for row in range(1, 20):
		for col in range(1, 20):
			st = board.stones[col - 1][row - 1]

			if st == Board.BLACK:
				stone(*inter(row, col), "black", mode="texture")
			elif st == Board.WHITE:
				stone(*inter(row, col), "white", mode="texture")

	## Mark last move
	if lmove:
		pla, col, row = lmove
		owner = "black" if pla == Board.BLACK else "white"
		circ_mark(*inter(row+1, col+1), owner)

	## Rendering Hints & variations & score diagram
	if SHOW_DEAD_STONES: draw_dead_stones(board)
	render_hints(pv, board=board, turn=history.getTurn(), coord=coord)
	if SHOW_WHITE_HINTS and SHOW_BLACK_HINTS:
		drawScoreList(history.getScoreSeq())

	SDL_RenderPresent(renderer)

## Init board, katago and history
# SDL_KATAGO - SDL event corresponding to KataGo's analysis
# path - optional parameter to import a sgf file
def init(SDL_KATAGO, path=None, skatago=True):

	## Standard
	if not path:
		board = Board(size=19)
		kata = KataGo(SDL_KATAGO, turnoff=not skatago)
		history = Node(kata)
		history._getRoot().setBoard(board, current=False)
		kata.setBoardsize(19)
		kata.setKomi(7.5)

	## Importing a sgf file
	else:
		print("Loading {} ...".format(path))
		gdata, setup, moves, rules = sgffiles.load_sgf_moves(path)

		board = Board(size=gdata.size)
		kata = KataGo(SDL_KATAGO, turnoff=not skatago)
		history = Node(kata)
		history._setCurrentBoard(board)
		kata.setBoardsize(gdata.size)
		kata.setKomi(gdata.komi)

		# Setting setup stones
		for pla, i, j in setup:
			history.playMove(board, i, j, pla, analyse=False)
		board = history.getCurrentBoard()
		history = history.setRootHere()

		for pla, i, j in moves:
			if (i, j) == (-1, -1): break
			history.playMove(board, i, j, pla, transmit=False)
		history.goToRoot()
		board = history.getCurrentBoard()
		print("{} loaded successfully.".format(path))

	return board, kata, history

# Treat an input
# - event : last SDL_event
# - board : game board
# - kata : KataGo object
# - history : Historic
# - input : Input object
# Output is a tuple (srun, srender)
# - srun : is set to True if the app has to be stoped
# - srender : is set to True if the app has to be re-rendered
# Caution: the board is not always modified. You MUST re-get it with a
# board = history.getCurrentBoard()
def treatInput(event, board, kata, history, inputs):

	global SHOW_BLACK_HINTS
	global SHOW_WHITE_HINTS
	global SHOW_HEAT_MAP
	global SHOW_DEAD_STONES
	global ltime

	srun, srender = True, False
	if event.type == SDL_QUIT:
		if DEBUG: print("EVENT: quit")
		srun = False

	## KATAGO
	elif event.type == SDL_KATAGO:
		if kata.lastAnalyse:
			if DEBUG: print("Event: katago")
			infos, heatInfos = kata.lastAnalyse

			heatInfos = Board.getSign(history.getTurn(current=True)) * heatInfos
			history.updPV(infos)
			board.loadHeatFromArray(heatInfos)
			srender = True

			if args.play:
				move = autoplay(history)
				if move != None:
					i, j = move
					history.setBoard(board, current=True) # save current board
					turn = history.getTurn()
					history.playMove(board, i, j, turn, transmit=True, analyse=True)
					board = history.getCurrentBoard()
					srender = True
					ltime = time.time()

	## KEYBOARD - always render
	elif event.type == SDL_KEYDOWN:
		if DEBUG: print("EVENT: KEY DOWN")
		srender = True
		if event.key.keysym.sym == SDLK_RIGHT:
			history.setBoard(board, current=True) # save current board
			board = history.goForward(transmit=True, analyse=True)
			if board == None:
				board = history.getCurrentBoard()
			ltime = time.time() + 1e6

		elif event.key.keysym.sym == SDLK_LEFT:
			history.setBoard(board, current=True) # save current board
			board = history.undo(transmit=True, analyse=True)
			ltime = time.time() + 1e6

		elif event.key.keysym.sym == SDLK_w:
			SHOW_WHITE_HINTS = not SHOW_WHITE_HINTS

		elif event.key.keysym.sym == SDLK_b:
			SHOW_BLACK_HINTS = not SHOW_BLACK_HINTS

		elif event.key.keysym.sym == SDLK_h:
			SHOW_HEAT_MAP = not SHOW_HEAT_MAP

		elif event.key.keysym.sym == SDLK_d:
			SHOW_DEAD_STONES = not SHOW_DEAD_STONES

		elif event.key.keysym.sym == SDLK_g:
			path = input("Write into: ")
			if path != "":
				try:
					txt = history.getSeqToCurrent()
					myfile = open(path, "a+")
					print(txt, file=myfile)
					myfile.close()
					print("Written description at {}".format(path))
					srender = False
				except:
					if DEBUG: print("Error wile trying to write informations in {}".format(path))
			else:
				print("Aborted.")

		elif event.key.keysym.sym == SDLK_l:
			path = input("Path to moves data: ")
			history.loadFileSequences(path)

		elif event.key.keysym.sym == SDLK_SPACE:
			history.loadNextSeq()

		elif event.key.keysym.sym == SDLK_BACKSPACE:
			history.loadPrevSeq()

		else: # If the key is not supported, do not render
			srender = False

	## MOUSE MOTION - do not always render
	elif event.type == SDL_MOUSEMOTION:
		lastCoord = inputs.getCoordinates()

		x, y = ctypes.c_int(0), ctypes.c_int(0)
		buttonState = SDL_GetMouseState(ctypes.byref(x), ctypes.byref(y))
		x, y = x.value, y.value

		inputs.setMouse(x, y)
		coord = inputs.getCoordinates()

		# Check if we need to render
		# Be cautious there otherwise we will rerender each time the mouse
		# moves
		if lastCoord == None: lastCoord = -2, -2
		if coord == None: coord = -2, -2
		lastmoveInfo = history.getMoveInfo(*lastCoord)
		moveInfo = history.getMoveInfo(*coord)
		if (lastmoveInfo != None or moveInfo != None) and lastCoord != coord:
			srender = True
			if DEBUG: print("EVENT: Taking account of motion")

	## MOUSE BUTTONS
	elif event.type == SDL_MOUSEBUTTONDOWN:
		if DEBUG: print("EVENT: mouse button")
		lastCoord = inputs.getCoordinates()
		if event.button.button == SDL_BUTTON_LEFT:
			if lastCoord != None:
				i, j = lastCoord
				history.setBoard(board, current=True) # save current board
				try:
					turn = history.getTurn()
					history.playMove(board, i, j, turn, transmit=True, analyse=True)
					board = history.getCurrentBoard()
					srender = True
					ltime = time.time()
				except:
					print("This move is illegal")
		elif event.button.button == SDL_BUTTON_RIGHT:
			srender = True
			history.setBoard(board, current=True) # save current board
			board = history.undo(transmit=True, analyse=True)
			ltime = time.time() + 1e6

	return srun, srender

def autoplay(history):
	"""
	If auto is on, return the best move according the history after a thinking
	time kttime. If KataGo is still supposed to thinking, return None.
	- param - auto
	"""

	auto = args.play
	kttime = args.kttime

	if ltime == None: return None

	kturn = Board.BLACK if auto == "black" else Board.WHITE
	cturn = history.getTurn(current=True)
	if kturn != cturn: return None

	t = time.time()
	if t - ltime < kttime: return None

	pv = history.getPV(current=True)
	visits, winrate, scoreMean, scoreStDev, moves = pv[0]
	bestmove = moves[0]
	return bestmove

# Main function
# run the katago-analyzer app.

def run():

	global args
	args = parser.parse_args()

	path = args.sgffile
	skatago = args.skatago
	silent = args.silent
	auto = args.play
	kttime = args.kttime

	if auto:
		print("KataGo playing {} thinking {} seconds".format(auto, kttime))
		global ltime
		ltime = time.time()

	if silent:
		global SHOW_BLACK_HINTS
		global SHOW_WHITE_HINTS
		global SHOW_HEAT_MAP
		global SHOW_VARIATION
		global SHOW_DEAD_STONES
		SHOW_BLACK_HINTS = False
		SHOW_WHITE_HINTS = False
		SHOW_HEAT_MAP = False
		SHOW_VARIATION = False
		SHOW_DEAD_STONES = False

	global font
	global smallfont
	global tinyfont
	global renderer
	global SDL_KATAGO

	SDL_Init(SDL_INIT_VIDEO)
	TTF_Init()

	window = SDL_CreateWindow("KataGo Analyzer".encode(),
		SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
		WIDTH, HEIGHT, SDL_WINDOW_SHOWN)
	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED)
	font = TTF_OpenFont(b"DejaVuSans.ttf", 13)
	smallfont = TTF_OpenFont(b"DejaVuSans.ttf", 10)
	tinyfont = TTF_OpenFont(b"DejaVuSans.ttf", 7)
	createStoneTexture("black")
	createStoneTexture("white")

	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b'1')

	# Creating SDL_Events for Katago
	SDL_KATAGO = SDL_RegisterEvents(1)

	# Initialise board & katago & inputs
	board, kata, history = init(SDL_KATAGO, path, skatago)
	inputs = Inputs()

	event = SDL_Event()
	render(board, history)
	t = None
	while True:
		if t != None:
			dt = time.time() - t
			fps = 1 / dt
			# print("FPS:", fps)
		# Event loop
		SDL_WaitEvent(event)
		t = time.time()
		srun, srender = treatInput(event, board, kata, history, inputs)
		if not srun: break
		if srender:
			if DEBUG: print("########### Rendering !")
			board = history.getCurrentBoard()
			render(board, history, inputs.getCoordinates())

	print("Closing KataGo")
	kata.close()
	print("Katago closed, closing everything else")
	SDL_DestroyRenderer(renderer)
	SDL_DestroyWindow(window)
	destroyStoneTexture("black")
	destroyStoneTexture("white")
	SDL_Quit()


# 第三版用来接鼠标控制器来实现自动点击.并且结合游戏运行

import shutil

#================
# bug 汇总:
#   1.  看到主要是cv的问题没有做完美, flood迷宫算法没问题
#       cv问题是, 第一个maze 的右下角坐标有时候识别不出来. 考虑调节参数
#                 第二个是切割完小图之后,比较不出来两个相同图片,跟之前担心的一样,差一个像素位置偏差时候很麻烦.
#  2.有兴趣的哥们可以试着优化一下这2点. 先分享版本到这.有时间继续优化.
#
#  3. 洗牌之后的算法需要重新修改. 需要判断黑块.
#
#
#
#
#
#
#
#
import win32con
import win32gui
#=============预处理:
#

if 1:#'初始化窗口:'
    # coding=utf-8
    import win32con
    import win32api
    import win32gui
    import win32ui
    from ctypes import *
    from ctypes import wintypes

    GetForegroundWindow = windll.user32.GetForegroundWindow
    GetWindowRect = windll.user32.GetWindowRect
    SetForegroundWindow = windll.user32.SetForegroundWindow
    GetWindowText = windll.user32.GetWindowTextA
    MoveWindow = windll.user32.MoveWindow
    EnumWindows = windll.user32.EnumWindows

    class RECT(Structure):
        _fields_ = [
            ('left', c_long),
            ('top', c_long),
            ('right', c_long),
            ('bottom', c_long)
        ]

    class POINT(Structure):
        _fields_ = [
            ('x', c_long),
            ('y', c_long),
        ]

    class FormControl(object):
        def __init__(self):
            self.win_hd = None
            self.win_title = ''

        def bindActiveWindow(self):
            """
            函数功能：获取当前焦点所在窗口
            """
            self.win_hd = GetForegroundWindow()

        def bindWindowByName(self, win_name):
            """
            函数功能：根据窗体名获取窗体句柄
            """
            self.win_title = win_name
            pro_fun_type = CFUNCTYPE(c_bool, c_int, c_long)
            pro_fun_p = pro_fun_type(self.EnumWindowsProc)
            EnumWindows(pro_fun_p, None)

        def getWinRect(self):
            """
            函数功能：获取窗体的位置和大小
            """
            if self.win_hd is None:
                return None
            rect = RECT()
            GetWindowRect(self.win_hd, byref(rect))
            return rect

        def toScreenPos(self, x, y):
            """
            函数功能：将窗体内部坐标转换为相对于显示屏的绝对坐标
            """
            # 未指定窗口，则结束函数
            if self.win_hd is None:
                return None
            rect = self.getWinRect()
            # 指定的坐标不在窗体内，则结束函数
            if x < 0 or y < 0 or x > rect.right or y > rect.bottom:
                return None
            pos = POINT()
            pos.x = x + rect.left
            pos.y = y + rect.top
            return pos

        def toWindowPos(self, x, y):
            """
            函数功能：将绝对坐标转换成相对于窗体内部坐标
            """
            if self.win_hd is None:
                return None
            rect = self.getWinRect()
            pos = POINT()
            pos.x = x - rect.left
            pos.y = y - rect.top
            # 指定的坐标不在窗体内，则结束函数
            if pos.x < 0 or pos.y < 0 or pos.x > rect.right or pos.y > rect.bottom:
                return None
            return pos

        def WindowActive(self):
            """
            函数功能：将窗体置前
            """
            if self.win_hd is None:
                return None
            SetForegroundWindow(self.win_hd)

        def getHWND(self):
            return self.win_hd

        def getWinTitle(self):
            """
            函数功能：获取窗体的标题
            """
            if self.win_hd is None:
                return None
            buffer = create_string_buffer(255, '\0')
            GetWindowText(self.win_hd, buffer, 255)
            value = buffer.value.decode('gbk')
            return value

        def MoveTo(self, x, y):
            """
            函数功能：移动窗体到指定坐标位置
            """
            if self.win_hd is None:
                return None
            rect = self.getWinRect()
            MoveWindow(self.win_hd, x, y, rect.right - rect.left, rect.bottom - rect.top, True)

        def WinCapture(self, path, x, y, w, h):
            """
            函数功能：抓取窗体截图，并保存到文件
            参    数：path 保存路径
                     x 截取起始x坐标（窗体内相对坐标）
                     y 截取起始y坐标（窗体内相对坐标）
                     w 截取宽度,为0则取窗体宽度
                     h 截取长度,为0则取窗体高度
            """
            if self.win_hd is None:
                return None
            rect = self.getWinRect()
            if w == 0:
                w = rect.right - rect.left
            if h == 0:
                h = rect.bottom - rect.top
            if x < 0 or y < 0 or (x + w) > rect.right or (y + h) > rect.bottom:
                return None
            self.Capture(self.win_hd, path, x, y, w, h, 0)

        def WinCapture_Mem(self, x, y, w, h):
            """
            函数功能：抓取窗体截图，并返回图像内存数据
            参    数：
                     x 截取起始x坐标（窗体内相对坐标）
                     y 截取起始y坐标（窗体内相对坐标）
                     w 截取宽度,为0则取窗体宽度
                     h 截取长度,为0则取窗体高度
            """
            if self.win_hd is None:
                return None
            rect = self.getWinRect()
            if w == 0:
                w = rect.right - rect.left
            if h == 0:
                h = rect.bottom - rect.top
            if x < 0 or y < 0 or (x + w) > rect.right or (y + h) > rect.bottom:
                return None
            return self.Capture(self.win_hd, '', x, y, w, h, 1)

        def Capture(self, hd, path, x, y, w, h, mode):
            """
            函数功能：截图
            参    数：hd 截取的窗口句柄
                    path 保存路径
                     x 截取起始x坐标（窗体内相对坐标）
                     y 截取起始y坐标（窗体内相对坐标）
                     w 截取宽度,为0则取窗体宽度
                     h 截取长度,为0则取窗体高度
                     mode 保存模式 0：保存为图片，1：返回图像字节数据
            """
            # 根据窗口句柄获取窗口的设备上下文
            hwndDC = win32gui.GetWindowDC(self.win_hd)
            # 根据窗口的DC获取memDC
            srcdc = win32ui.CreateDCFromHandle(hwndDC)
            # memDC创建可兼容的DC
            saveDC = srcdc.CreateCompatibleDC()
            # 创建bigmap准备保存图片
            saveBitMap = win32ui.CreateBitmap()
            # 为bitmap开辟空间
            saveBitMap.CreateCompatibleBitmap(srcdc, w, h)
            # 高度saveDC，将截图保存到saveBitmap中
            saveDC.SelectObject(saveBitMap)
            # 截取从左上角（0，0）长宽为（w，h）的图片
            saveDC.BitBlt((0, 0), (w, h), srcdc, (x, y), win32con.SRCCOPY)
            if mode == 0:
                saveBitMap.SaveBitmapFile(saveDC, path)
            else:
                signedIntsArray = saveBitMap.GetBitmapBits(True)
                return signedIntsArray
            # 释放内存
            srcdc.DeleteDC()
            saveDC.DeleteDC()
            win32gui.ReleaseDC(self.win_hd, hwndDC)
            win32gui.DeleteObject(saveBitMap.GetHandle())

        def EnumWindowsProc(self, hwnd, lParam):
            buffer = create_string_buffer(255, '\0')
            GetWindowText(hwnd, buffer, 255)
            value = buffer.value.decode('gbk')
            if value == self.win_title:
                self.win_hd = hwnd
                print(self.win_hd)
                return False
            return True

    hwnd = win32gui.FindWindow("#32770", u"连连看 v4.1")
    a = FormControl()
    a.win_hd = hwnd
    print(a.getWinRect())
    ttt=a.getWinRect()
    a.MoveTo(0, 0)
    a.WindowActive()
    print(1)
    rect = win32gui.GetWindowRect(hwnd)
    aaaa= (rect[2] - rect[0], rect[3] - rect[1])

    print(aaaa,3333333333333333333333)
    import win32gui as gui
    handle = gui.FindWindow(None, "连连看 v4.1")
    gui.SetForegroundWindow(handle)
    x0, y0, x1, y1 = gui.GetWindowRect(handle);
    print(x0,y0,x1,y1,9999999999999999999999)

print('置顶窗口完毕')
if 0:
    print('打印全部的窗口')#得到窗口的全部信息.
    import win32gui

    windows_list = []
    win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), windows_list)
    for window in windows_list:
        classname = win32gui.GetClassName(window)
        title = win32gui.GetWindowText(window)
        print(f'classname:{classname} title:{title}')

# classname:#32770 title:连连看 v4.1

#=======
test=0 # 切换用自己图片测试模式还是游戏模式

import cv2
import time
# time.sleep(3) # 启动代码3秒内切到连连看界面
littlepic=57,66  # 点击的方块大小. 单位像素.用画图软件可以自己量.
# 游戏在小游戏/llkwqb里面.
# 首先我们把练练看放到屏幕左上角位置.
print(littlepic)

import numpy as np
from PIL import ImageGrab
import cv2

# BOX = (0, 40, 880, 640)

# 左上角坐标和右下角坐标
# 调整box的值即可改变截取区域

if 0:
    screen = np.array(ImageGrab.grab())[:1200,:1300]  # 1200足够放连连看了.

#=========经过测试imageGrab的像素很低.质量太差了! 改为用下面这个方法来解平.
#==============截取屏幕.
# 下面是截取屏幕的算法.

zuoshangjiao, youxiajiao=None,None
debug_screen=[]
def getmaze():
    global zuoshangjiao
    global youxiajiao
    mouse.move(1,1)
    import pyautogui
    import cv2

    img = pyautogui.screenshot()  # x,y,w,h
    # img.save('screenshot.png')
    img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

    # screen=img[:aaaa[0],:aaaa[1]]
    screen=img
    # print(999999999999999999999999999,aaaa[0],aaaa[1])
    # screen=img[:ttt.bottom,:ttt.right]

    global debug_screen

    if 0: # 使用保存好的图片调试用
        screen=cv2.imread('3.png')[:ttt.right,:ttt.bottom]
        debug_screen = screen
    cv2.imwrite('tmp.png',screen) # 图像切割.
    # print(screen)
    #===========找到游戏操作的图版的左上和右下. 图版的含义是你一堆littlepic拼起来的区域.
    # littlepic是 那些小图片. 目标就是小图片一样的都点一下.

    # 找到第一个像素点他的 左边50全是黑.
    flag=0
    width,height,channel=screen.shape
    for i in range(width-littlepic[1]):
        if flag==1:
            break
        for j in range(50,height//2):
            tmp=screen[i,j-50:j]
            if np.all(tmp==0)  and tmp.shape[0]==50 and np.all(screen[i:i+7,j:j+7]!=0):
                print(i,j)
                flag=1
                cv2.imwrite('tmp2.png', screen[:i+10,:j+10])  # 图像切割.
                break

    zuoshangjiao=[i,j]
    print(1111111111)

    #==========同理找到右下角.

    # 找到第一个像素点他的 左边50全是黑.
    flag=0
    width,height,channel=screen.shape
    for i in range(width-littlepic[1],0,-1):
        if flag==1:
            break
        for j in range(height-littlepic[0],50,-1):
            tmp=screen[i,j:j+35]
            if np.all(tmp==0)  and tmp.shape[0]==35 and np.all(screen[i-7:i,j-7:j]!=0):
                print(i,j)
                flag=1
                cv2.imwrite('tmp3.png', screen[:i-10,:j-10])  # 图像切割.
                break

    youxiajiao=[i,j]
    print('zuojiao',zuoshangjiao,'youxiajiao',youxiajiao)
    print('左上角有下叫',zuoshangjiao,youxiajiao)

    tmp4=screen[zuoshangjiao[0]:youxiajiao[0],zuoshangjiao[1]:youxiajiao[1]]

    cv2.imwrite('tmp4.png',screen[zuoshangjiao[0]:youxiajiao[0],zuoshangjiao[1]:youxiajiao[1]])
    tmp4 = cv2.cvtColor(tmp4, cv2.COLOR_BGR2GRAY)
    #cv2.THRESH_OTSU 可以优化阈值的选择.
    ret,thresh1 = cv2.threshold(tmp4,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    cv2.imwrite('tmp5.png',thresh1)  # 图像切割.
    tmppic=thresh1
    #======找到所有的水平数值的切割.
    #============找到水平的横线. 都是0的一行

    hang=[]
    for i in range(thresh1.shape[0]):
        if np.all(thresh1[i,:]==0):
          hang.append(i)

    #=========进行像素合并. 找间断的,然后取中值

    hang_after_fix=[]

    savelist=[]

    for i,j in enumerate(hang):
        if i==0:
            savelist.append(j)
            continue
        if j-hang[i-1]==1:
            savelist.append(j)
        else:
            hang_after_fix.append(sum(savelist)/len(savelist))
            savelist=[]
            savelist.append(j)
        #======最后一行别忘了
        if i==len(hang)-1:
            hang_after_fix.append(sum(savelist) / len(savelist))
            savelist = []
    print(hang_after_fix)

    lie=[]
    for i in range(thresh1.shape[1]):
        if np.all(thresh1[:,i]==0):
          lie.append(i)

    #=========进行像素合并. 找间断的,然后取中值

    lie_after_fix=[]

    savelist=[]

    for i,j in enumerate(lie):
        if i==0:
            savelist.append(j)
            continue
        if j-lie[i-1]==1:
            savelist.append(j)
        else:
            lie_after_fix.append(sum(savelist)/len(savelist))
            savelist=[]
            savelist.append(j)
        #======最后一行别忘了
        if i==len(lie)-1:
            lie_after_fix.append(sum(savelist) / len(savelist))
            savelist = []
    print(lie_after_fix)

    #索引int化.
    hang_after_fix=[int(i) for i in hang_after_fix]
    lie_after_fix=[int(i) for i in lie_after_fix]

    #==============下面进行切割小图, 然后存下来,debug用

    dic_all_pic={}
    qipansize=len(hang_after_fix)-1,len(lie_after_fix)-1
    # tmppic 是 棋盘
    for i in range(len(hang_after_fix)-1):
        for j in range(len(lie_after_fix)-1):
            tmp=tmppic[hang_after_fix[i]:hang_after_fix[i+1],lie_after_fix[j]:lie_after_fix[j+1]]
            # cv2.imwrite(f'fordebug/{i}_{j}.png',tmp)
            dic_all_pic[f'{i}_{j}']=tmp

    #=======下面对切割完的进行比较和编码

    print(1)
    #========每个相同的小图之间可能差一些黑色框.所以把矩阵周围一圈黑色都去除.
    for i,j in dic_all_pic.items():
        hang_del,lie_del=[],[]
        for i1 in range(j.shape[0]):
            if np.all(j[i1,:]==0):
                hang_del.append(i1)
        for i1 in range(j.shape[1]):
            if np.all(j[:,i1]==0):
                lie_del.append(i1)
        j=np.delete(j,hang_del,0)
        j=np.delete(j,lie_del,1)
        dic_all_pic[i]=j

    #==========编码
    all_pic=list(dic_all_pic.values())

    # ==========修剪小图片的shape .有时候会差像素.可能因为二值化.都按照最小的切割试试
    xmin = min([i.shape[0] for i in all_pic])
    ymin = min([i.shape[1] for i in all_pic])

    all_pic=[i[1:xmin-1,1:ymin-1] for i in all_pic]

    #========save little pic for debug
    shutil.rmtree('fordebug')
    import os
    os.mkdir('fordebug')
    for i in dic_all_pic:
        cv2.imwrite(f'fordebug/{i}.png',dic_all_pic[i])

    all_pic_del_dup=[] # 记录的是索引
#================================这个地方需要优化比较算法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    for i in range(len(all_pic)):
        print(f'当前计算的索引是第{i//qipansize[1]}行图片,{i%qipansize[1]}列的图片',)
        dex=[i//qipansize[1],i%qipansize[1]]
        for j in ((all_pic_del_dup)):

            #  有时候切分会差几个像素!
            if np.sum(all_pic[i]!=all_pic[j])<5:#两个图片相同

                all_pic_del_dup.append(j)
                break
        else: # 如果不走break时候才走else. for-else结构非常实用.
          all_pic_del_dup.append(i)

    all_pic_del_dup=np.array(all_pic_del_dup)
    all_pic_del_dup.resize(qipansize)
    print(1)

    print(1)

    #==================下面解这个矩阵.

    # 首先最外一圈补 -1 ,对角线不用补. 不允许走斜线.但是矩阵缺位置更难描述.所以还是都补上

    #=========使用图片的maze
    maze=all_pic_del_dup+1

    # print(1)
    maze2=np.ones([maze.shape[0]+2,maze.shape[1]+2])*0
    maze2[1:-1,1:-1]=maze
    # print(1)
    maze=maze2
    maze=[list(i) for i in maze]
    print("下面打印图片给的maze",maze)          #========以上就是获取maze的代码
    with open('maze.txt','w') as f:
        for i in maze:
            f.write(str(i)+'\n')

    return maze, zuoshangjiao, youxiajiao,hang_after_fix,lie_after_fix

def getmaze2(a,b): # 非第一次的getmaze就不要再算zuoshangjiao,youxiajiao, 仍然使用第一次的zuoshangjia youxiajia

    global zuoshangjiao
    global youxiajiao
    print('getmaze2里面的',zuoshangjiao,youxiajiao)
    mouse.move(1,1)
    print()
    import pyautogui
    import cv2

    img = pyautogui.screenshot()  # x,y,w,h
    # img.save('screenshot.png')
    img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

    screen=img[:1200,:1300]
    global debug_screen

    # if test: # 使用保存好的图片调试用
    #     screen=cv2.imread('3.png')[:1200,:1300]
    #     debug_screen = screen
    cv2.imwrite('tmp.png',screen) # 图像切割.
    # print(screen)
    #===========找到游戏操作的图版的左上和右下. 图版的含义是你一堆littlepic拼起来的区域.
    # littlepic是 那些小图片. 目标就是小图片一样的都点一下.

    # # 找到第一个像素点他的 左边50全是黑.
    # flag=0
    # width,height,channel=screen.shape
    # for i in range(width-littlepic[1]):
    #     if flag==1:
    #         break
    #     for j in range(50,height//2):
    #         tmp=screen[i,j-50:j]
    #         if np.all(tmp==0)  and tmp.shape[0]==50 and np.all(screen[i:i+7,j:j+7]!=0):
    #             print(i,j)
    #             flag=1
    #             cv2.imwrite('tmp2.png', screen[:i+10,:j+10])  # 图像切割.
    #             break
    #
    #
    # print(1111111111)
    #
    # #==========同理找到右下角.
    #
    # # 找到第一个像素点他的 左边50全是黑.
    # flag=0
    # width,height,channel=screen.shape
    # for i in range(width-littlepic[1],0,-1):
    #     if flag==1:
    #         break
    #     for j in range(height-littlepic[0],50,-1):
    #         tmp=screen[i,j:j+35]
    #         if np.all(tmp==0)  and tmp.shape[0]==35 and np.all(screen[i-7:i,j-7:j]!=0):
    #             print(i,j)
    #             flag=1
    #             cv2.imwrite('tmp3.png', screen[:i-10,:j-10])  # 图像切割.
    #             break

    print('zuojiao',zuoshangjiao,'youxiajiao',youxiajiao)

    tmp4=screen[zuoshangjiao[0]:youxiajiao[0],zuoshangjiao[1]:youxiajiao[1]]

    cv2.imwrite('tmp4.png',screen[zuoshangjiao[0]:youxiajiao[0],zuoshangjiao[1]:youxiajiao[1]])
    tmp4 = cv2.cvtColor(tmp4, cv2.COLOR_BGR2GRAY)
    #cv2.THRESH_OTSU 可以优化阈值的选择.
    ret,thresh1 = cv2.threshold(tmp4,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    cv2.imwrite('tmp5.png',thresh1)  # 图像切割.
    tmppic=thresh1
    #======找到所有的水平数值的切割.
    #============找到水平的横线. 都是0的一行

    hang=[]
    for i in range(thresh1.shape[0]):
        if np.all(thresh1[i,:]==0):
          hang.append(i)

    #=========进行像素合并. 找间断的,然后取中值

    # hang_after_fix=[]

    # savelist=[]
    #
    # for i,j in enumerate(hang):
    #     if i==0:
    #         savelist.append(j)
    #         continue
    #     if j-hang[i-1]==1:
    #         savelist.append(j)
    #     else:
    #         hang_after_fix.append(sum(savelist)/len(savelist))
    #         savelist=[]
    #         savelist.append(j)
    #     #======最后一行别忘了
    #     if i==len(hang)-1:
    #         hang_after_fix.append(sum(savelist) / len(savelist))
    #         savelist = []
    # print(hang_after_fix)

    lie=[]
    for i in range(thresh1.shape[1]):
        if np.all(thresh1[:,i]==0):
          lie.append(i)

    #=========进行像素合并. 找间断的,然后取中值

    # lie_after_fix=[]

    # savelist=[]
    #
    # for i,j in enumerate(lie):
    #     if i==0:
    #         savelist.append(j)
    #         continue
    #     if j-lie[i-1]==1:
    #         savelist.append(j)
    #     else:
    #         lie_after_fix.append(sum(savelist)/len(savelist))
    #         savelist=[]
    #         savelist.append(j)
    #     #======最后一行别忘了
    #     if i==len(lie)-1:
    #         lie_after_fix.append(sum(savelist) / len(savelist))
    #         savelist = []
    # print(lie_after_fix)

    #索引int化.
    hang_after_fix=[int(i) for i in a]
    lie_after_fix=[int(i) for i in b]

    #==============下面进行切割小图, 然后存下来,debug用

    dic_all_pic={}
    qipansize=len(hang_after_fix)-1,len(lie_after_fix)-1
    # tmppic 是 棋盘
    for i in range(len(hang_after_fix)-1):
        for j in range(len(lie_after_fix)-1):
            tmp=tmppic[hang_after_fix[i]:hang_after_fix[i+1],lie_after_fix[j]:lie_after_fix[j+1]]
            # cv2.imwrite(f'fordebug/{i}_{j}.png',tmp)
            dic_all_pic[f'{i}_{j}']=tmp

    #=======下面对切割完的进行比较和编码

    print(1)
    # #========每个相同的小图之间可能差一些黑色框.所以把矩阵周围一圈黑色都去除.
    # for i,j in dic_all_pic.items():
    #     hang_del,lie_del=[],[]
    #     for i1 in range(j.shape[0]):
    #         if np.all(j[i1,:]==0):
    #             hang_del.append(i1)
    #     for i1 in range(j.shape[1]):
    #         if np.all(j[:,i1]==0):
    #             lie_del.append(i1)
    #     j=np.delete(j,hang_del,0)
    #     j=np.delete(j,lie_del,1)
    #     dic_all_pic[i]=j

    #==========编码
    all_pic=list(dic_all_pic.values())

    # ==========修剪小图片的shape .有时候会差像素.可能因为二值化.都按照最小的切割试试
    xmin = min([i.shape[0] for i in all_pic])
    ymin = min([i.shape[1] for i in all_pic])
#---------再减一个像素.
    all_pic=[i[1:xmin-1,1:ymin-1] for i in all_pic]

    #========save little pic for debug
    shutil.rmtree('fordebug')
    import os
    os.mkdir('fordebug',)
    # print(99999999,dic_all_pic)
    for i in dic_all_pic:
        cv2.imwrite(f'fordebug/{i}.png',dic_all_pic[i])
    print('保存好第二次小图片')

    all_pic_del_dup=[] # 记录的是索引============这个就把重复图片给笑出了,之后记录索引.
    all_pic_del_dup2=[] # 记录的是索引============这个就把重复图片给笑出了,之后记录索引.
#================================这个地方需要优化比较算法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    for i in range(len(all_pic)):
        print(f'当前计算的索引是第{i//qipansize[1]}行图片,{i%qipansize[1]}列的图片',)
        dex=[i//qipansize[1],i%qipansize[1]]
        if np.sum(all_pic[i]==np.zeros_like(all_pic[i]))>np.size(all_pic[i])*0.8:# 如果基本都黑色,那么就是崆不用管.
            all_pic_del_dup.append(-1.0) # 用-1表示.
            continue
        for j in ((all_pic_del_dup2)):

            #  有时候切分会差几个像素!
            if np.sum(all_pic[i]!=all_pic[j])<5:#两个图片相同==============判断图片!!!!!!!!!!!!!

                all_pic_del_dup2.append(j)
                all_pic_del_dup.append(j)
                break
        else: # 如果不走break时候才走else. for-else结构非常实用.
            all_pic_del_dup2.append(i)
            all_pic_del_dup.append(i)

    all_pic_del_dup=np.array(all_pic_del_dup)
    all_pic_del_dup.resize(qipansize)
    print(1)

    print(1)

    #==================下面解这个矩阵.

    # 首先最外一圈补 -1 ,对角线不用补. 不允许走斜线.但是矩阵缺位置更难描述.所以还是都补上

    #=========使用图片的maze
    maze=all_pic_del_dup+1

    # print(1)
    maze2=np.ones([maze.shape[0]+2,maze.shape[1]+2])*0
    maze2[1:-1,1:-1]=maze
    # print(1)
    maze=maze2
    maze=[list(i) for i in maze]
    print("下面打印图片给的maze",maze)          #========以上就是获取maze的代码
    with open('maze.txt','w') as f:
        for i in maze:
            f.write(str(i)+'\n')

    return maze

#========下面走 迷宫问题. 给定初始点和结束点. 给出所有的走法.

# print(1)

step=[(-1,0),(1,0),(0,-1),(0,1)]  # 上, 下, 左, 右 的顺序访问.

# ===========使用flood 算法. 先填充走一个方向的所有结果,再填充走第二个方向的所有结果.然后看是否有相同的即可.

def compare(a,b):  # a  一个numpy数组, b是numpy矩阵
    for i in a:
        if (i==b).all():
            return True
    return False

import copy
def search(maze, index1,index2):
    # 输入一个maze 和 位置(index1, index2) . 然后返回他flood算法走3个方向之后的所有覆盖点.
# 走3个折线.
    # 并且起点和终点之间只允许经过0 或者什么都不经过
    #

    dex=[index1,index2] # 起始索引
    result=[]  # 换了一个新项目环境, 之前的太坑了,乱跑代码.

    #=========经过0个0就可以到的点
    #走一步,如果到达的位置是0, 那么就可以继续按照这个方向走. 否则就只能停止直接写入result

    #使用bfs来处理=======使用queue,比list curd速度要快.

    a=[]
    a.append(dex)
    tmpway=[] # tmpway  是走一个直线的时候经过的位置.
    #==============走一个直线
    #向上走: 对a里面每一个点都一直向上走.
    result=[]
    result.append(dex)
    for k in range(3):
        for i in (a): # python for 循环数组时候不要在循环里面改变数组, 会无限循环下去. # a是上一轮走完4个方向经过的位置
            for j in step: # 4个方向.
                tmp = i
                while 1: # 一直向一个方向走.
                    tmp=[tmp[0]+j[0],tmp[1]+j[1]]
                    if tmp[0]>=0 and tmp[0]<= len(maze)-1 and tmp[1]>=0 and tmp[1]<=len(maze[0])-1: #判断不会走出maze
                        if maze[tmp[0]][tmp[1]]==0: # 如果遇到0了,就可以继续走,仍如a里面
                            if tmp not in tmpway: # tmp不在a中存在

                                 tmpway.append(tmp)
                            if tmp not in result:
                                 result.append(tmp)
                        else:
                            if tmp not in result:
                                result.append(tmp) # 碰到墙了不用走了.并且不允许继续走这个方向了.
                            break
                    else: # 超界了不用走了
                        break
        # a=copy.deepcopy(tmpway)
        a=tmpway.copy() #把可以继续拓展的位置交给下一轮拓展# 这个地方要用copy, 因为数组是传引用的!!!!!!!! 浅拷贝就足够了. 最高层级拷贝就足够用.
        # print('tmpway',tmpway)
        # print("debug",k,result)
                # print(result,999999)
    return result

#测试maze
# maze=np.array([[1,2,3],[1,1,3]])
# #补0
# maze2=np.ones([maze.shape[0]+2,maze.shape[1]+2])*0
# maze2[1:-1,1:-1]=maze
# # print(1)
# maze=maze2
# maze=[list(i) for i in maze]
# print(maze)

# numpy 好像debug有问题. 还是用list来算吧.
# a=search(maze,1,1)  # a就是3个连线后能到达的位置.

#=========然后我们只抽取里面结果位置符合要求的
def find(maze,a,b):
    # 返回值跟:索引a,b 能匹配上答案一样的图片的索引位置的列表
    candidate=search(maze,a,b)
    result=[]
    for i in candidate:
        if i[0]>0 and i[0]<len(maze)-1 and i[1]>0:
            if i[1] < len(maze[0])-1 and maze[i[0]][i[1]] == maze[a][b] :
                if i[0]==a and i[1]==b:#去除自己本身
                    continue
                else:
                  result.append(i)
    return result

# #=================find算法的输出:
# if 0:
#     print('find算法的输出:')
#     print(find(maze,1,10))

#==============下面进行playdemo.
#算法逻辑是玩这个矩阵. maze看做一维数组,优先消除索引小的块.

def play_one_step():
    for i in range(1,len(maze)-1):
        for j in range(1, len(maze[0]) - 1):
            if maze[i][j]!=0:
                a=find(maze,i,j)
                if a!=[]:
                    #=======进行消除运算.
                    maze[i][j]=0 #补0表示空白了.
                    maze[a[0][0]][a[0][1]]=0
                    print(f"消除了索引{i}_{j}和{a[0][0]}_{a[0][1]}")
                    print('打印消除后的maze用来debug',np.array(maze))
                    return True
    return False
#========玩10步:
# if 0:
#     for i in range(10):
#         play_one_step()

#========计算每个砖块的像素位置:

# print('zuoshangjiao',zuoshangjiao,'youxiajiao',youxiajiao)
dic_pix={}  # 把砖块索引和对应的像素坐标放到这个地方.
def computePix(): # 输入的a,b的索引是以1开始计数的.因为maze一周已经补0了.
    hang=len(maze)-2
    lie=len(maze[0])-2
    print('hang',hang,'lie',lie)
    # 算坐标.
    gaodu=(youxiajiao[0]-zuoshangjiao[0])/hang
    kuandu=(youxiajiao[1]-zuoshangjiao[1])/lie
    print('gaodu',gaodu,'kuandu',kuandu)

    for i in range(1,hang+1):
        for j in range(1,lie+1):
            dic_pix[f'{i}_{j}']=[zuoshangjiao[0]+(i-1+0.5)*gaodu,zuoshangjiao[1]+(j-1+0.5)*kuandu]
    #==========注意这个结果key是坐标, value是高和宽!!!!!!!!
# computePix()

#
if 0:#========下面代码画图6,用于debug看是否点击准确.
    print(dic_pix)

    point_size = 1
    point_color = (0, 0, 255) # BGR
    thickness = 4 # 可以为 0 、4、8
    kk=dic_pix.values()

    qq=[]
    for ii in kk:
        qq.append([int(i) for i in ii])

    qq=[i[::-1] for i in qq]
    kk=qq
    kk=[tuple(i) for i in kk]

    print('kk',kk)

    cv2.circle(debug_screen, (10,100), point_size, point_color, thickness)
    for point  in kk[:]:
        cv2.circle(debug_screen, point, point_size, point_color, thickness)

    cv2.imwrite('tmp6.png',debug_screen)

# print(a)

#==============下面书写游戏主逻辑

#================ 首先sleep 5秒. 在这个时间内,需要启动游戏并且选择一个难度开始游戏

time.sleep(3)
import mouse

#===========然后游戏获取maze地图.和点击像素的计算. 这些统一叫做预处理.

maze, zuoshangjiao, youxiajiao,hangafterfix,lieafterfix=getmaze()
computePix()

def play_one_step_trully(): # 在真是环境下玩一步
    for i in range(1,len(maze)-1):
        for j in range(1, len(maze[0]) - 1):
            if maze[i][j]!=0:
                a=find(maze,i,j)
                if a!=[]:
                    #=======进行消除运算.
                    maze[i][j]=0
                    maze[a[0][0]][a[0][1]]=0

                    #========这里进行鼠标点击.
                    mouse.move(dic_pix[f'{i}_{j}'][1],dic_pix[f'{i}_{j}'][0])
                    mouse.click()
                    mouse.move(dic_pix[f'{a[0][0]}_{a[0][1]}'][1],dic_pix[f'{a[0][0]}_{a[0][1]}'][0])
                    mouse.click()

                    print(f"消除了索引{i}_{j}和{a[0][0]}_{a[0][1]}")
                    print('打印消除后的maze用来debug',maze)
                    return True
    return False
play=not test

sleeptime=0.8
while play:
    a=play_one_step_trully()
    if a:
        time.sleep(sleeptime)
    else:#无解了
        time.sleep(2)
        #重新cv一遍即可.
        maze = getmaze2(hangafterfix,lieafterfix)
        computePix()


import torch
import torch.nn as nn

class RNN(nn.Module):
    def __init__(self, input_dim=500, hidden_dim=300, output_dim=100):
        super().__init__()
        self.hidden_layer_size = hidden_dim
        self.rnn = nn.RNN(input_dim, hidden_dim, num_layers=2)
        self.linear = nn.Linear(hidden_dim, output_dim)

    def forward(self, input_seq):
        rnn_out, _ = self.rnn(input_seq.view(len(input_seq), 1, -1))
        predictions = self.linear(rnn_out.view(len(input_seq), -1))
        return predictions


import os
import sys

os.environ['hadoop_home']="C:/hadoop"

print (os.environ.get('hadoop_home'))

from pyspark import SparkConf,SparkContext

def main(sc):
    file= sc.parallelize("a b c c",1)
    rdd=file.flatMap(lambda x:x.split(" "))
    rdd1=rdd.map(lambda w:(w,1))
    rdd2=rdd1.reduceByKey(lambda a,b:a+b)
    print (rdd2.collect())

if __name__ =="__main__":

    conf = SparkConf().setAppName("wordcount")\
        .setMaster("local")
    sc = SparkContext(conf=conf)
    main(sc)


import matplotlib.pyplot as plt
import numpy as np
import numpy.random as rng

ny, nx = 5, 6

y = np.arange(0, ny)
x = np.arange(0, nx)

# x-component of covariance
Cx = np.zeros((nx, nx))
for i in range(nx):
    for j in range(nx):
        Cx[i, j] = np.exp(-0.5*((x[i] - x[j])/1.0)**2)
    Cx[i, i] += 1E-6

# y-component of covariance
Cy = np.zeros((ny, ny))
for i in range(ny):
    for j in range(ny):
        Cy[i, j] = np.exp(-0.5*((y[i] - y[j])/1.0)**2)
    Cy[i, i] += 1E-6

# Eigen decompositions
Ex, Vx = np.linalg.eig(Cx)
Ey, Vy = np.linalg.eig(Cy)

# Explicit full covariance matrix
n = nx*ny
C = np.zeros((n, n))
for i in range(n):
    for j in range(n):
        dx = x[i % nx] - x[j % nx]
        dy = y[i // nx] - y[j // nx]
        rsq = dx**2 + dy**2
        C[i, j] = np.exp(-0.5*rsq/1.0**2)
    C[i, i] += 1E-6
# C would equal np.kron(Cy, Cx) if it weren't for the extra diagonal bit.

# Some data
L = np.linalg.cholesky(C)
ns = rng.randn(n)
ys = C @ ns
img = ys.reshape((ny, nx))
plt.imshow(img)
plt.show()

# Explicit solve
logl0 = -n*0.5*np.log(2*np.pi) - 0.5*np.log(np.linalg.det(C)) \
                - 0.5*np.dot(ys, np.linalg.solve(C, ys))

# Cholesky solve
logl1 = -n*0.5*np.log(2*np.pi) - np.sum(np.log(np.diag(L))) \
                - 0.5*np.dot(ys, ns)

# Kronecker solve
E = np.kron(Ey, Ex) # All eigenvalues of C

# Dot product of the data against eigenvectors of C
# = the data represented in that basis
coeffs = np.zeros(n)
k = 0
for i in range(ny):
    for j in range(nx):
        V = np.kron(Vy[:,i], Vx[:,j])
        coeffs[k] = np.dot(ys, V)
        k += 1

logl2 = -n*0.5*np.log(2*np.pi) - 0.5*np.sum(np.log(E)) \
                - 0.5*np.dot(coeffs, coeffs/E)

# Eigendecomposition: C = V @ diag(E) @ V.T since C is symmetric V.T = Vinv

# First two should be equal, third should be close.
print(logl0, logl1, logl2)
plt.plot(coeffs)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Tue Nov  7 13:36:36 2017

@author: sraghunath

Linear Regression using Gradient Descent

"""
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

class LinearRegressionGD(object) :

    def __init__(self,no_iters=20,learning_rate=0.001) :

        self.learning_rate = learning_rate
        self.no_iters = no_iters

    def fit(self,X,y) :

        self.w_ = np.zeros(X.shape[1] + 1)

        self.cost_ = []

        for i in range(self.no_iters) :

            errors = (y - self.get_output(X))

            self.w_[0] = self.learning_rate * errors.sum()

            self.w_[1:] = self.learning_rate * np.dot(X.T,errors)

            self.cost_.append(0.5 * (errors**2).sum())

        return self

    def get_output(self,X) :

        return self.w_[0] + np.dot(self.w_[1:],X.T)

    def predict(self,X_test) :

        return self.get_output(X_test)

df = pd.read_csv('https://raw.githubusercontent.com/rasbt/'
                 'python-machine-learning-book-2nd-edition'
                 '/master/code/ch10/housing.data.txt',
                 header=None,
                 sep='\s+')

df.columns = ['CRIM', 'ZN', 'INDUS', 'CHAS',
              'NOX', 'RM', 'AGE', 'DIS', 'RAD',
              'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']

X = df[['RM']].values

y = df['MEDV'].values

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()

X_std = sc.fit_transform(X)

y_std = sc.fit_transform(y[:,np.newaxis]).flatten()

lmodel = LinearRegressionGD()

lmodel.fit(X_std,y_std)

plt.plot(range(1,lmodel.no_iters+1),lmodel.cost_)

plt.xlabel("Number of epochs")
plt.ylabel("SSE Cost Function")
plt.show()

def linear_plot(X,y,model) :

    plt.scatter(X,y,c="steelblue")
    plt.plot(X,model.predict(X),color="black")


# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import zmq
import numpy as np
import math
from IPython.display import clear_output, display
from inputs import get_gamepad
import msgpack
import time
import threading

context = zmq.Context()

#  Socket to talk to server
print("Connecting to hello world server…")
socket = context.socket(zmq.PUB)
socket.bind("tcp://0.0.0.0:5555")
# socket.connect("tcp://192.168.50.234:5555")

#  Do 10 requests, waiting each time for a response
# for request in range(10):
#     print("Sending request %s …" % request)
#     socket.send(b"Hello")

#     #  Get the reply.
#     message = socket.recv()
#     print("Received reply %s [ %s ]" % (request, message))

# %%
def joystickToDiff(x, y, minSpeed, maxSpeed, minJoystick=-1.0, maxJoystick=1.0):    # If x and y are 0, then there is not much to calculate...
    if x == 0.0 and y == 0.0:
        return (0.0, 0.0)

    direction = np.arctan2(y, x)
    xmax = np.minimum(1.0, np.abs(np.cos(direction)))
    ymax = np.minimum(1.0, np.abs(np.sin(direction)))
    speed = np.hypot(x,y) / np.hypot(xmax,ymax)
    display("d {}  s {} ".format(direction, speed))

    straight_treshold = np.pi / 8
    if np.abs(direction - np.pi/2.0) < straight_treshold:
        return (speed, speed)
    elif np.abs(direction + np.pi/2.0) < straight_treshold:
        return (-speed, -speed)
    # elif y > 0:

    # First Compute the angle in deg
    # First hypotenuse
    z = math.sqrt(x * x + y * y)

    # angle in radians
    rad = math.acos(math.fabs(x) / z)

    # and in degrees
    angle = rad * 180 / math.pi

    # Now angle indicates the measure of turn
    # Along a straight line, with an angle o, the turn co-efficient is same
    # this applies for angles between 0-90, with angle 0 the coeff is -1
    # with angle 45, the co-efficient is 0 and with angle 90, it is 1

    tcoeff = -1 + (angle / 90) * 2
    turn = tcoeff * math.fabs(math.fabs(y) - math.fabs(x))
    turn = round(turn * 100, 0) / 100

    # And max of y or x is the movement
    mov = max(math.fabs(y), math.fabs(x))

    # First and third quadrant
    if (x >= 0 and y >= 0) or (x < 0 and y < 0):
        rawLeft = mov
        rawRight = turn
    else:
        rawRight = mov
        rawLeft = turn

    # Reverse polarity
    if y < 0:
        rawLeft = 0 - rawLeft
        rawRight = 0 - rawRight

    # minJoystick, maxJoystick, minSpeed, maxSpeed
    # Map the values onto the defined rang
    rightOut = limit(rawRight, minJoystick, maxJoystick, minSpeed, maxSpeed)
    leftOut = limit(rawLeft, minJoystick, maxJoystick, minSpeed, maxSpeed)

    return (rightOut, leftOut)

def limit(v, in_min, in_max, out_min, out_max):
    # Check that the value is at least in_min
    if v < in_min:
        v = in_min
    # Check that the value is at most in_max
    if v > in_max:
        v = in_max
    return (v - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

print(1,1,joystickToDiff(1,1,-100,100))
print(0,0,joystickToDiff(0,0,-100,100))
print(-1,-1,joystickToDiff(-1,-1,-100,100))

# %%
class Messages:
    def control(lr):
        return msgpack.packb({
            "type": "control-wheels",
            "left": lr[0],
            "right": lr[1]
        })

class Control():
    MODE_TRIGGER = "trigger"
    MODE_STICK = "stick"
    mode = 'trigger'

    rx = 0
    ry = 0
    trigger = [0.0, 0.0]
    bumper = [False, False]

    th_read_joy = None
    th_send_control = None

    @staticmethod
    def norm(value):
        low, high = 129.0, 32768.0
        return (np.maximum(0.0,np.abs(value)-low) / (high - low)) * np.sign(value)

    @classmethod
    def start(cls):
        cls.th_read_joy = threading.Thread(target=cls.read_joy)
        cls.th_read_joy.start()
        cls.th_send_control = threading.Thread(target=cls.send_control)
        cls.th_send_control.start()

    @classmethod
    def read_joy(cls):
        while True:
            events = get_gamepad()
            if len(events) == 0:
                print('sleep')
                time.sleep(0.1)
            else:
                for event in events:
                    # print(event.ev_type, event.code, event.state)
                    if event.ev_type == "Absolute":
                        if event.code == "ABS_RX":
                            cls.rx = cls.norm(event.state)
                        elif event.code == "ABS_RY":
                            cls.ry = -cls.norm(event.state)
                        elif event.code == "ABS_Z":
                            cls.trigger[0] = event.state / 255.0
                        elif event.code == "ABS_RZ":
                            cls.trigger[1] = event.state / 255.0
                    if event.ev_type == "Key":
                        if event.code == "BTN_TL":
                            cls.bumper[0] = event.state == 1
                        if event.code == "BTN_TR":
                            cls.bumper[1] = event.state == 1
    @classmethod
    def send_control(cls):
        while True:
            if cls.mode == cls.MODE_STICK:
                left, right = joystickToDiff(cls.rx, cls.ry, -1.0, 1.0)

            elif cls.mode == cls.MODE_TRIGGER:
                left = cls.trigger[0]
                if cls.bumper[0]:
                    left *= -1
                right = cls.trigger[1]
                if cls.bumper[1]:
                    right *= -1

            # display((cls.rx, cls.ry))
            print(cls.mode, left, right)
            socket.send(Messages.control((left, right)))
            # socket.recv()
            time.sleep(0.1)

Control.start()
Control.th_read_joy.join()
# while True:
#     clear_output(wait=True)
#     c.read_joy()
#     c.send_controll()
#     time.sleep(0.01)


import os
import pickle
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# load MCMC output and thin
species_codes = {1: 'Pst', 2: 'Pst', 3: 'Pgr', 4: 'Pgr', 6: 'Pst',
                 7: 'Aru', 9: 'Aru', 11: 'Aru', 12: 'Pst', 14: 'Pst',
                 17: 'Pst', 19: 'Pst', 21: 'Aru', 22: 'Pgr', 24: 'Aru',
                 26: 'Aru', 27: 'Pgr', 28: 'Aru', 29: 'Aru', 31: 'Bpa',
                 32: 'Bpa', 34: 'Pgr', 35: 'Bpa', 38: 'Bpa', 41: 'Bpa'}

# helper functions
def hide_current_axis(*args, **kwds):
    plt.gca().set_visible(False)

def f(species_code, plot=False, save=False):
    ts = pickle.load(open('../../Data/UMB_trace/Gil_v2/{}.pickle'\
                          .format(species_code), 'rb'))
    ts['kxmax/g1'] = 10**ts['kxmax_log10']/10**ts['g1_log10']
    ts['kxmax/alpha'] = 10**ts['kxmax_log10']/10**ts['alpha_log10']
    latex = [r'$c$', '$\\psi_{x50}$', '$k_{xmax}/L/g_1$', r'$k_{xmax}/\alpha$']
    df_thinned = {}
    for key in ts:
        #print(np.median(ts[key]))
        df_thinned[key] = [item for index, item in enumerate(ts[key])\
                           if index % 10 == 0]
    df_thinned = pd.DataFrame.from_dict(data=df_thinned, orient='columns')
    df_thinned = df_thinned.drop(['g1_log10', 'kxmax_log10', 'alpha_log10'],\
                                 axis=1)
    df_thinned.rename(columns=dict(zip(df_thinned.columns, latex)),\
                      inplace=True)
    #print(df_thinned[latex[-1]].describe(percentiles=[0.01]))
    # figure
    if plot:
        #sns.set(font_scale=2)
        sns.set_context('paper', rc={'axes.labelsize': 24})
        plt.ioff()
        g = sns.PairGrid(data=df_thinned, vars=latex, diag_sharey=False)
        g.fig.set_size_inches(12, 12)
        g = g.map_lower(plt.scatter, s=1)
        g = g.map_diag(plt.hist)
        #g = g.map_diag(sns.kdeplot, lw=3, legend=False)
        #g = g.map_upper(plt.scatter, s=1)
        g.map_upper(hide_current_axis)
        if save:
            g.savefig('../../Figures/Figure_correlation_Gil_v2_{}.png'\
                      .format(species_code), bbox_inches="tight")

# figures
#species_codes = [y+'_'+str(x) for x, y in species_codes.items()]
species_codes = ['Aru_24']
for species_code in species_codes:
    if not os.path.exists('../../Data/UMB_trace/Gil_v2/{}.pickle'\
                          .format(species_code)):
        continue
    f(species_code, plot=True, save=True)
    print(species_code)


from boto3 import Session
from botocore.exceptions import BotoCoreError, ClientError
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image
from pydub import AudioSegment
from pydub.playback import play
import openai
import json
import time
import threading
import io
import pyaudio
import numpy as np
import wave
import os

openai.api_key = os.environ["OPENAI_API_KEY"]
messages = [
        {"role": "system", "content": "Your name is Kevin, and you are a conversational chatbot that a companion for a lonely person. If innaprporiate language is used, do not mention it. Respond accordingly and briefly as if you were in a casual dinner seting. Don't say numbered lists and avoid making responses longer than 3-4 sentences."}
]

face1 = 'faces/face1.jpg'
face2 = 'faces/face2.jpg'
face3 = 'faces/face3.jpg'
face4 = 'faces/face4.jpg'
face5 = 'faces/face5.jpg'
face6 = 'faces/face6.jpg'

mappings = {
        'k': face1,
        'E': face1,
        'i': face2,
        '@': face2,
        'a': face2,
        'e': face2,
        'O': face2,
        't': face3,
        's': face3,
        'S': face3,
        'T': face3,
        'f': face4,
        'sil': face5,
        'p': face5,
        'r': face6,
        'u': face6,
        'o': face6,
}

options = RGBMatrixOptions()
options.rows = 32
options.chain_length = 1
options.parallel = 1
options.hardware_mapping = 'adafruit-hat'

matrix = RGBMatrix(options = options)

session = Session(profile_name="default")
polly = session.client("polly")

FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
THRESHOLD = 100
SILENCE_TIMEOUT = 2

def gpt_response(prompt):
        messages.append({"role": "user", "content": prompt})
        response = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=messages
        )
        content = response['choices'][0]['message']['content']
        messages.append({"role": "assistant", "content": content})
        return content

def tts_lipsync(text):
        audio_response = polly.synthesize_speech(
                Engine="neural",
                Text=text,
                OutputFormat="mp3",
                VoiceId="Stephen")

        visemes_response = polly.synthesize_speech(
                Engine="neural",
                Text=text,
                OutputFormat="json",
                SpeechMarkTypes=["viseme"],
                VoiceId="Stephen")

        audio = audio_response["AudioStream"].read()
        visemes = [json.loads(v) for v in visemes_response["AudioStream"].read().decode().split() if v]

        audio_thread = threading.Thread(target=lambda: play(AudioSegment.from_mp3(io.BytesIO(audio))))
        audio_thread.start()

        time.sleep(1)
        start_time = time.time() * 1000

        for viseme in visemes:
                while time.time() * 1000 - start_time < viseme["time"]:
                        time.sleep(1/1000)
                matrix.SetImage(Image.open(mappings.get(viseme["value"])).rotate(-90))

        matrix.SetImage(Image.open(face5).rotate(-90))
        time.sleep(1)

def main():
        response = gpt_response("Address the user as Ryan. Make an upbeat conversation starter question.")
        print(response)
        tts_lipsync(response)

        audio = pyaudio.PyAudio()
        stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)

        recording = False
        recording_data = []

        silence_timer = None

        print("Listening...")

        while True:
                data = stream.read(CHUNK)
                audio_signal = np.frombuffer(data, dtype=np.int16)

                if max(audio_signal) > THRESHOLD and not recording:
                        recording = True
                        silence_timer = None
                        print("Recording...")

                if recording:
                        recording_data.append(data)

                        if max(audio_signal) <= THRESHOLD:
                                if silence_timer is None:
                                        silence_timer = time.time()
                                elif time.time() - silence_timer >= SILENCE_TIMEOUT:
                                        print("Silence detected, stopping recording")

                                        stream.stop_stream()
                                        stream.close()

                                        with wave.open('recording.wav', "wb") as wf:
                                                wf.setnchannels(CHANNELS)
                                                wf.setsampwidth(audio.get_sample_size(FORMAT))
                                                wf.setframerate(RATE)
                                                wf.writeframes(b"".join(recording_data))

                                        transcript = openai.Audio.transcribe("whisper-1", open('recording.wav', 'rb'))["text"]
                                        print("Transcription:", transcript)
                                        os.remove('recording.wav')

                                        tts_lipsync(gpt_response(transcript))

                                        stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)
                                        recording = False
                                        recording_data = []
                                        print("Listening...")
                        else:
                                silence_timer = None

if __name__ == '__main__':
        main()


import os, shutil
import matplotlib.pyplot as plt

def set_details(path_raw='', path_proc='', sub='', trial='', signal=''):
    """
    Initialise dictionary of testing details.

	Example:
        path_raw = '/home/joanna/Dropbox/Projects/thisProject/data/raw/'
		path_proc = '/home/joanna/Dropbox/Projects/thisProject/data/proc/'
		sub = 'sub01'
		trial = 'NV_UL'
		signal = 'force'
		type = 'filt' # OR 'raw' OR 'powerspec'

    :param path_raw: file path to raw data
    :type path_raw: str
    :param path_proc: file path to processed data
    :type path_proc: str
    :param sub: subject ID
    :type sub: str
    :param trial: trial
    :type trial: str
	:param signal: signal
    :type signal: str
    :return: file paths to raw and processed data, subject ID, trial, condition type
    :rtype: dict
    """
    try:
        if not os.path.exists(path_proc + sub):
            os.mkdir(path_proc + sub)
    except FileNotFoundError:
        print('Error: Not enough information to create directory for processed data')
    details = {'path_raw': path_raw, 'path_proc': path_proc, 'sub': sub, 'trial': trial, 'signal': signal}
    return details

def save_plot(details=None, cond_type=''):
    """
    If testing details are passed, save plot in directory for processed data.
    Otherwise save plot in local directory.

    :param details: file paths to raw and processed data, subject ID, trial, signal
    :type details: dict
	:param cond_type: condition type (eg. raw, filt, powerspec)
    :type cond_type: str
    :return:
    :rtype:
    """
    if details and cond_type:
        # save figure to processed data directory
        plt.savefig(details['trial'] + '_' + details['signal'] + '_' + cond_type + '.png')
        plt.close()
        shutil.move(details['path_raw'] + details['sub'] + '/' + details['trial'] + '_'  + details['signal'] + '_'  + cond_type + '.png',
                    details['path_proc'] + details['sub'] + '/' + details['trial'] + '_'  + details['signal'] + '_'  + cond_type + '.png')
    else:
        # save figure to local directory
        plt.savefig('figure.png')
        plt.close()

def plot_raw(data, details=None, cond_type='raw'):
    """
    Plot raw data.
    If testing details are passed, plot is saved in directory for processed data.
    Otherwise plot is saved in local directory.

    :param data: data
    :type data: ndarray
    :param details: file paths to raw and processed data, subject ID, trial, signal
    :type details: dict
	:param cond_type: condition type
    :type cond_type: str
    :return:
    :rtype:
    """
    plt.clf()
    plt.close()
    fig = plt.figure()
    plt.plot(data)
    plt.xlabel('Sample')
    plt.ylabel('EMG (a.u.)')
    plt.tight_layout()
    save_plot(details, cond_type)

def plot_filt(data, data_filt, freq, details=None, cond_type='filt'):
    """
    Plot filtered data: short section and full trial.
    If testing details are passed, plot is saved in directory for processed data.
    Otherwise plot is saved in local directory.

    :param data: data
    :type data: ndarray
    :param data_filt: filtered data
    :type data_filt: ndarray
    :param freq: sampling rate (Hz)
    :type freq: int
    :param details: file paths to raw and processed data, subject ID, trial, condition type
    :type details: dict
	:param cond_type: condition type
    :type cond_type: str
    :return:
    :rtype:
    """
    plt.clf()
    fig = plt.figure()
    axes1 = fig.add_subplot(2, 1, 1)
    axes1.set_ylabel('EMG (a.u.), 1 sec')
    axes1.plot(data[0 : 1 * freq], label='raw')
    axes1.plot(data_filt[0 : 1 * freq], label='filtered')
    plt.locator_params(axis='x', nbins=5)
    plt.locator_params(axis='y', nbins=5)
    plt.axis('tight')
    plt.legend(loc='best')
    axes2 = fig.add_subplot(2, 1, 2)
    axes2.set_ylabel('EMG (a.u.), full')
    axes2.set_xlabel('Sample')
    axes2.plot(data)
    axes2.plot(data_filt)
    plt.locator_params(axis='x', nbins=5)
    plt.locator_params(axis='y', nbins=5)
    plt.axis('tight')
    plt.tight_layout()
    save_plot(details, cond_type)
    plt.close()

def plot_powerspec(f, Pxx_den, lowpass, details=None, cond_type='powerspec'):
    """
    Plot power spectrum of data.
    This function requires a power spectral density analysis is first performed on the recorded signal.
    If testing details are passed, plot is saved in directory for processed data.
    Otherwise plot is saved in local directory.

    Example:
        import numpy as np
        import matplotlib.pyplot as plt
        from scipy import signal

        x = np.random.uniform(-1, 1, size=1000)
        x = filter_lowpass(x, 1000, 40)
        f, Pxx_den = signal.welch(x, 1000, nperseg=250)
        plot_powerspec(f, Pxx_den, lowpass=500, details=None, cond_type='powerspec')

    :param f: sample frequencies
    :type f: ndarray
    :param Pxx_den: power spectrum of data
    :type Pxx_den: ndarray
    :param lowpass: lowpass cut-off (Hz)
    :type lowpass: int
    :param details: file paths to raw and processed data, subject ID, trial, condition type
    :type details: dict
	:param cond_type: condition type
    :type cond_type: str
    :return:
    :rtype:
    """
    plt.clf()
    fig = plt.figure(figsize=(11, 7))
    plt.plot(f, Pxx_den, 'k-o')
    plt.xlabel('Frequency [Hz]')
    plt.ylabel('PSD [V**2/Hz]')
    plt.xlim((1, lowpass / 2))
    plt.ylim(0, Pxx_den.max())  # print(Pxx_den.max())
    plt.locator_params(axis='x', nbins=5)
    plt.locator_params(axis='y', nbins=5)
    plt.tight_layout()
    save_plot(details, cond_type)
    plt.close()

def plot_spectrogram(f, t, Sxx, ylim_ul, xmargin, toffset=0, details=None, cond_type='specgram'):
    """
    Plot spectrogram of data.
    This function requires a spectrogram analysis is first performed on the recorded signal.
    If testing details are passed, plot is saved in directory for processed data.
    Otherwise plot is saved in local directory.

    Example:
        import numpy as np
        import matplotlib.pyplot as plt
        from scipy import signal

        x = np.random.uniform(-1, 1, size=1000)
        x = filter_lowpass(x, 1000, 40)
        f, t, Sxx = signal.spectrogram(x, fs=1000, window='hamming', nperseg=256)
        plot_spectrogram(f, t, Sxx, ylim_ul=50, xmargin=2, toffset=2, details=None, cond_type='specgram')

    :param f: sample frequencies
    :type f: ndarray
    :param t: segment times
    :type t: ndarray
    :param Sxx: spectrogram of x. By default, the last axis of Sxx corresponds to the segment times.
    :type Sxx: ndarray
    :param ylim_ul: frequencies upper limit
    :type ylim_ul: int
    :param xmargin: time margin between event and upper or lower limit
    :type xmargin: int
    :param toffset: time offset from event
    :type toffset: int
    :param details: file paths to raw and processed data, subject ID, trial, condition type
    :type details: dict
    :param cond_type: condition type
    :type cond_type: str
    :return:
    :rtype:
    """
    plt.clf()
    fig, ax = plt.subplots(1, figsize=(11,7))
    t = t - toffset
    p = ax.pcolormesh(t, f, Sxx, cmap='RdBu', shading='gouraud')
    fig.colorbar(p)
    plt.ylim((0, ylim_ul))
    plt.xlim((t - xmargin, t + xmargin))
    plt.ylabel('Frequency [Hz]')
    plt.xlabel('Time [sec]')
    plt.locator_params(axis='x', nbins=5)
    plt.locator_params(axis='y', nbins=5)
    plt.tight_layout()
    save_plot(details, cond_type)
    plt.close()


from dataclasses import dataclass

import dataclasses
from typing import Iterable, Self

import json
import logging
import pickle

from utils import verify, sign

@dataclass
class TransInput:
    public_key: str  # PEM
    amount: int

@dataclass
class TransOutput:
    public_key: str  # PEM
    amount: int

@dataclass(frozen=True, slots=True)
class Transaction:
    input: TransInput | None
    output: TransOutput
    number_in_chain: int

    def serialized_input_output(self) -> str:
        inp = self.input
        ou = self.output
        separators = (",", ":")
        output_json_dump = json.dumps(
            obj=dataclasses.asdict(ou), sort_keys=True, separators=separators
        )

        if inp:
            inp_json_dump = json.dumps(
                obj=dataclasses.asdict(inp), sort_keys=True, separators=separators
            )
            return inp_json_dump + "|" + output_json_dump
        else:
            return "|" + output_json_dump

@dataclass(frozen=True, slots=True)
class TransactionSigned(Transaction):
    signature: str

    @classmethod
    def from_transaction(cls, t: Transaction, private_key):
        serialized = t.serialized_input_output().encode()
        signature = sign(serialized, private_key).hex()
        return cls(t.input, t.output, t.number_in_chain, signature)

    def is_valid(self) -> bool:
        serialized = self.serialized_input_output()
        key = self.input.public_key if self.input else self.output.public_key
        if self.output.amount < 0:
            return False
        if self.input and self.input.amount != self.output.amount:
            return False
        try:
            verify(serialized, self.signature.encode(), key)
            return True
        except Exception:
            return False

    # SERIALIZATION
    def pack_transaction(self) -> str:
        return pickle.dumps(self).decode("latin-1")

    # DESERIALIZATION
    @classmethod
    def unpack_transaction(cls, packed_transaction: str):
        new_obj = pickle.loads(packed_transaction.encode("latin-1"))
        if isinstance(new_obj, Self):
            return new_obj
        else:
            raise Exception("Invalid object instance unpickled")

def eval_balance(
    transactions: Iterable[TransactionSigned], accounts: dict[str, int], miner_fee: int
):
    for trans in transactions:
        input = trans.input
        output = trans.output
        # check signaged
        # check if amount is the same as fee
        if not trans.is_valid():
            raise Exception("Transaction not valid")

        if not input:
            # mined
            if output.amount == miner_fee:
                if not accounts.get(output.public_key):
                    accounts[output.public_key] = miner_fee
                else:
                    accounts[output.public_key] += miner_fee
            else:
                raise Exception("Fee different than miner fee")
        else:
            payer_address = input.public_key
            payee_address = output.public_key
            expense = input.amount
            if expense < 0:
                raise Exception("Input amount cant be a negative number")
            payer_account_balance = accounts.get(payer_address)
            if not payer_account_balance:
                raise Exception("Non existing payer attempting to transfer")
            else:
                if payer_account_balance < expense:
                    raise Exception("Spending above amount")
                else:
                    if not accounts.get(output.public_key):
                        # logging.warning("New or dead wallet")
                        accounts[payee_address] = 0
                    accounts[payee_address] += expense
                    accounts[payer_address] -= expense
    return accounts


"""
A* grid planning has been improved
Author: Grizi-ju
The tutorials at bilibili, ID:小巨同学zz https://www.bilibili.com/video/BV1FL4y1M7PM?spm_id_from=333.999.0.0
See Wikipedia article (https://en.wikipedia.org/wiki/A*_search_algorithm)
For reference only, it is forbidden to sell codes in a commercial manner
Please give me a Star or Fork, thank u
"""

import math
import numpy as np
from math import factorial
import matplotlib.pyplot as plt

show_animation = True

class AStarPlanner:

    def __init__(self, ox, oy, reso, rr):

        self.reso = reso   # grid resolution [m]
        self.rr = rr       # robot radius [m]
        self.calc_obstacle_map(ox, oy)
        self.motion = self.get_motion_model()

    # 创建一个节点类，节点的信息包括：xy坐标，cost代价, parent_index
    class Node:
        def __init__(self, x, y , cost, parent_index):
            self.x = x
            self.y = y
            self.cost = cost  # g(n)
            self.parent_index = parent_index

        def __str__(self):     # 返回字符串类型
            return str(self.x) + "," + str(self.y) + "," + str(
                self.cost) + "," + str(self.parent_index)

    # planning函数，传入起始点坐标sx,sy,gx,gy, 返回pathx,pathy(最终的路径)
    def planning(self, sx, sy, gx, gy):
        """
        1、sx = nstart  sy = ngoal
        2、open_set  closed_set
        3、open_set = nstart
        4、将open表中代价最小的子节点=当前节点，并在plot上动态显示，按esc退出
        5、如果当前节点等于ngoal，提示找到目标点
        6、删除open表中的内容，添加到closed表中
        7、基于运动模型定义搜索方式
        8、pathx,pathy = 最终路径(传入ngoal,closed_set)，返回pathx,pathy
        """
        # 1、sx = nstart  sy = ngoal  初始化nstart、ngoal坐标作为一个节点，传入节点全部信息
        nstart = self.Node(self.calc_xyindex(sx, self.minx),  # position min_pos
                           self.calc_xyindex(sy, self.miny),
                           0.0,
                           -1)
        ngoal = self.Node(self.calc_xyindex(gx, self.minx),
                           self.calc_xyindex(gy, self.miny),
                           0.0,
                           -1)

        # 2、open表、closed表
        # 3、起点加入open表
        open_set, closed_set = dict(), dict()
        open_set[self.calc_grid_index(nstart)] = nstart

        while 1:
            if len(open_set) == 0:
                print("Open set is empty...")
                break

        # 4、将open表中代价最小的子节点 = 当前节点，并在plot上动态显示，按esc退出
            # f(n)=g(n)+h(n)  实际代价+预估代价
            # 第几个子节点 child_node_id
            c_id = min(
                open_set,
                key=lambda o: open_set[o].cost + self.calc_heuristic(ngoal, open_set[o]))
            current = open_set[c_id]   # open表中当前节点 = 当前节点

            # 将当前节点显示出来
            if show_animation:
                plt.plot(self.calc_grid_position(current.x, self.minx),
                         self.calc_grid_position(current.y, self.miny),
                         "xc")   # 青色x 搜索点
                # 按esc退出
                plt.gcf().canvas.mpl_connect('key_release_event',
                                              lambda event: [exit(0) if event.key == 'escape' else None]
                                            )
                if len(closed_set.keys()) % 10 == 0:
                    plt.pause(0.001)

            if current.x == ngoal.x and current.y == ngoal.y:
                print("Find goal!")
                ngoal.parent_index = current.parent_index
                ngoal.cost = current.cost
                break

            # 删除open表中的节点,并把它添加到closed表
            del open_set[c_id]
            closed_set[c_id] = current

            # 基于motion model做栅格扩展，也就是搜索方式，可进行改进
            for i, _ in enumerate(self.motion):
                node = self.Node(current.x + self.motion[i][0],    # 当前x+motion列表中第0个元素dx
                                 current.y + self.motion[i][1],
                                 current.cost + self.motion[i][2],c_id)
                n_id = self.calc_grid_index(node)   # 返回该节点位置index

                # If the node is not safe, do nothing
                if not self.verify_node(node):
                    continue

                if n_id in closed_set:
                    continue

                if n_id not in open_set:
                    open_set[n_id] = node
                else:
                    if open_set[n_id].cost > node.cost:
                        open_set[n_id] = node    # 搜索的节点代价更小，替代open表中的节点

        pathx, pathy = self.calc_final_path(ngoal, closed_set)
        return pathx, pathy

    def calc_final_path(self, ngoal, closedset):    # 传入目标点和close表，经过函数处理得到最终所有的xy列表
        pathx, pathy = [self.calc_grid_position(ngoal.x, self.minx)], [
                        self.calc_grid_position(ngoal.y, self.miny)]
        parent_index = ngoal.parent_index
        while parent_index != -1:
            n = closedset[parent_index]
            pathx.append(self.calc_grid_position(n.x, self.minx))
            pathy.append(self.calc_grid_position(n.y, self.miny))
            parent_index = n.parent_index

        return pathx, pathy

    @staticmethod  # 静态方法，calc_heuristic函数不用传入self，经常改进启发函数，目的是提高阅读性
    def calc_heuristic(n1, n2):  # n1传入ngoal，n2传入open表要计算的那个点
        """
        改进点1：启发函数距离公式 曼哈顿距离、欧式距离、对角线距离 默认使用对角线距离
        """
        # d = np.abs(n1.x - n2.x) + np.abs(n1.y - n2.y)     #  Manhattan distance

        # dx = np.abs(n1.x - n2.x)                          #  Diagnol distance
        # dy = np.abs(n1.y - n2.y)
        # min_xy = min(dx,dy)
        # d = dx + dy + (math.sqrt(2) - 2) * min_xy

        d = math.hypot(n1.x - n2.x, n1.y - n2.y)            #  Euclidean distance
        print(d)

        """
        改进点2：f(n)=g(n)+h(n)*w(n)
        动态加权：在放弃搜索最优路径的情况下，使用动态加权来缩短A*搜索的时间
        其原则为，在搜索开始时，快速到达目的地所在区域更重要；在搜索结束时，得到到达目标的最佳路径更重要
        当h较大时，权重系数w也应该较大，此时A*算法会尽快向终点扩展，搜索速度很快但会错过最优路径；当h较小时，w也应该较小，此时A*算法会倾向于搜索最优路径而减慢搜索速度。
        如何找到最优选择，需要根据地图情况，多次调节参数
        """
        if d > 18:
            w = 3.0
        else:
            w = 1.0
        h = w * d

        #return d
        return h    # 传入2个点，返回h值

    # 函数功能：传入地图中最小处障碍物的坐标和index，得到全局范围下某个坐标
    def calc_grid_position(self, index, minpos):
        pos = index * self.reso + minpos
        return pos

    # 函数功能：计算xy坐标函数，传入position,min_pos，返回x/y坐标
    def calc_xyindex(self, position, min_pos):
        return round((position - min_pos) / self.reso)  # (当前节点-最小处的坐标)/分辨率=pos_index

    # 函数功能：计算栅格地图index，传入某个节点，返回当前节点的栅格index
    def calc_grid_index(self, node):
        return (node.y - self.miny) * self.xwidth + (node.x - self.minx)

    # 函数功能：验证是否为安全节点函数
    def verify_node(self, node):
        posx = self.calc_grid_position(node.x, self.minx)
        posy = self.calc_grid_position(node.y, self.miny)
        if posx < self.minx:
            return False
        elif posy < self.miny:
            return False
        elif posx >= self.maxx:
            return False
        elif posy >= self.maxy:
            return False

        if self.obmap[int(node.x)][int(node.y)]:
            return False
        return True

    # 函数功能：计算障碍物地图
    def calc_obstacle_map(self, ox, oy):
        self.minx = round(min(ox))    # 地图中的临界值
        self.miny = round(min(oy))
        self.maxx = round(max(ox))
        self.maxy = round(max(oy))
        print("minx:", self.minx)
        print("miny:", self.miny)
        print("maxx:", self.maxx)
        print("maxy:", self.maxy)

        self.xwidth = round((self.maxx - self.minx) / self.reso)
        self.ywidth = round((self.maxy - self.miny) / self.reso)
        print("xwidth:", self.xwidth)
        print("ywidth:", self.ywidth)

        self.obmap = [[False for i in range(int(self.ywidth))]
                       for i in range(int(self.xwidth))]
        for ix in range(int(self.xwidth)):
            x = self.calc_grid_position(ix, self.minx)
            for iy in range(int(self.ywidth)):
                y = self.calc_grid_position(iy, self.miny)
                for iox, ioy in zip(ox, oy):  #将ox,oy打包成元组，返回列表，并遍历
                    d = math.hypot(iox - x, ioy - y)
                    if d <= self.rr:          #代价小于车辆半径，可正常通过，不会穿越障碍物
                        self.obmap[ix][iy] = True
                        break

    @staticmethod
    def get_motion_model():
        # dx, dy, cost
        """
        改进点3：算法搜索的8个邻域，可改进为：5个、16个、32个、48个等(参考论文)
        """
        motion = [
                    [1, 0, 1],
                    [0, 1, 1],
                    [-1, 0, 1],
                    [0, -1, 1],
                    [1, 1, math.sqrt(2)],
                    [1, -1, math.sqrt(2)],
                    [-1, 1, math.sqrt(2)],
                    [-1, -1, math.sqrt(2)]

                    # [2,1,math.sqrt(5)],
                    # [1,2,math.sqrt(5)],
                    # [-2,1,math.sqrt(5)],
                    # [-1,2,math.sqrt(5)],
                    # [-2,-1,math.sqrt(5)],
                    # [-1,-2,math.sqrt(5)],
                    # [1,-2,math.sqrt(5)],
                    # [2,-1,math.sqrt(5)]

                    # [3,1,math.sqrt(10)],
                    # [1,3,math.sqrt(10)],
                    # [-3,1,math.sqrt(10)],
                    # [-1,3,math.sqrt(10)],
                    # [-3,-1,math.sqrt(10)],
                    # [-1,-3,math.sqrt(10)],
                    # [1,-3,math.sqrt(10)],
                    # [3,-1,math.sqrt(10)]

                    # [3,2,math.sqrt(13)],
                    # [2,3,math.sqrt(13)],
                    # [-3,2,math.sqrt(13)],
                    # [-2,3,math.sqrt(13)],
                    # [-3,-2,math.sqrt(13)],
                    # [-2,-3,math.sqrt(13)],
                    # [2,-3,math.sqrt(13)],
                    # [3,-2,math.sqrt(13)]
                  ]
        return motion
"""
改进点4：贝塞尔曲线平滑处理，对planning函数返回的path容器进行处理，可替换使用其他曲线方法
"""
#  贝塞尔曲线部分的三个函数
def comb(n, k):
    return factorial(n) // (factorial(k) * factorial(n-k))

def get_bezier_curve(points):
    n = len(points) - 1
    return lambda t: sum(comb(n, i)*t**i * (1-t)**(n-i)*points[i] for i in range(n+1))

def evaluate_bezier(points, total):
    bezier = get_bezier_curve(points)
    new_points = np.array([bezier(t) for t in np.linspace(0, 1, total)])
    return new_points[:, 0], new_points[:, 1]

# 主函数
def main():
    print(__file__ + '  start!')
    plt.title("improved astar by xiaoju")

    # start and goal position  [m]
    sx = -5.0
    sy = -5.0
    gx = 50
    gy = 50
    grid_size = 2.0
    robot_radius = 1.0

    # obstacle positions   在此更改地图
    ox, oy = [],[]
    for i in range(-10, 60):
        ox.append(i)
        oy.append(-10)      # y坐标-10的一行-10~60的坐标添加到列表并显示为黑色障碍物
    for i in range(-10, 60):
        ox.append(i)
        oy.append(60)       # y坐标60的一行-10~60的坐标添加到列表并显示为黑色障碍物
    for i in range(-10, 61):
        ox.append(-10)
        oy.append(i)        # x坐标-10的一列-10~61的坐标添加到列表并显示为黑色障碍物
    for i in range(-10, 61):
        ox.append(60)
        oy.append(i)        # x坐标60的一列-10~61的坐标添加到列表并显示为黑色障碍物
    for i in range(-10, 40):
        ox.append(20)
        oy.append(i)        # x坐标20的一列-10~40的坐标添加到列表并显示为黑色障碍物
    for i in range(0, 40):
        ox.append(40)
        oy.append(60 - i)   # x坐标40的一列20~60的坐标添加到列表并显示为黑色障碍物

    if show_animation:
        plt.plot(ox,oy,".k")  # 黑色.       障碍物
        plt.plot(sx,sy,"og")  # 绿色o       开始坐标
        plt.plot(gx,gy,"xb")  # 蓝色x       目标点
        plt.grid(True)
        plt.axis('equal')  # 保持栅格的横纵坐标刻度一致

    a_star = AStarPlanner(ox, oy, grid_size ,robot_radius)   # grid_size=reso
    pathx, pathy = a_star.planning(sx, sy, gx, gy)           # 开始与结束的坐标传入函数进行处理后，得到pathx,pathy：最终规划出的路径坐标

    # 贝塞尔曲线的控制点，为了方便更改，可根据出图效果调整
    points = np.array([[pathx[0], pathy[0]], [pathx[1], pathy[1]], [pathx[2], pathy[2]],
                        [pathx[3], pathy[3]],[pathx[4], pathy[4]],[pathx[5], pathy[5]],
                        [pathx[6], pathy[6]],[pathx[7], pathy[7]],[pathx[8], pathy[8]],
                        [pathx[9], pathy[9]],[pathx[10], pathy[10]],[pathx[11], pathy[11]],
                        [pathx[12], pathy[12]],[pathx[13], pathy[13]],[pathx[14], pathy[14]],
                        [pathx[15], pathy[15]]])
    points1 = np.array([[pathx[15], pathy[15]], [pathx[16], pathy[16]],[pathx[17], pathy[17]]])
    points2 = np.array([[pathx[17], pathy[17]],[pathx[18], pathy[18]], [pathx[19], pathy[19]], [pathx[20], pathy[20]],
                        [pathx[21], pathy[21]],[pathx[22], pathy[22]],[pathx[23], pathy[23]],
                        [pathx[24], pathy[24]],[pathx[25], pathy[25]],[pathx[26], pathy[26]],
                        [pathx[27], pathy[27]]])
    points3 = np.array([[pathx[27], pathy[27]],[pathx[28], pathy[28]],[pathx[29], pathy[29]]])
    points4 = np.array([[pathx[29], pathy[29]],[pathx[30], pathy[30]], [pathx[31], pathy[31]], [pathx[32], pathy[32]],
                        [pathx[33], pathy[33]],[pathx[34], pathy[34]],[pathx[35], pathy[35]],
                        [pathx[36], pathy[36]],[pathx[37], pathy[37]],[pathx[38], pathy[38]],
                        [pathx[39], pathy[39]],[pathx[40], pathy[40]],[pathx[41], pathy[41]],
                        [pathx[42], pathy[42]],[pathx[43], pathy[43]],[pathx[44], pathy[44]],
                        [pathx[45], pathy[45]],[pathx[46], pathy[46]],[pathx[47], pathy[47]],
                        [pathx[48], pathy[48]],[pathx[49], pathy[49]],[pathx[50], pathy[50]],
                        [pathx[51], pathy[51]],[pathx[52], pathy[52]]])

    bx, by = evaluate_bezier(points, 50)
    bx1, by1 = evaluate_bezier(points1, 50)
    bx2, by2 = evaluate_bezier(points2, 50)
    bx3, by3 = evaluate_bezier(points3, 50)
    bx4, by4 = evaluate_bezier(points4, 50)

    if show_animation:
        plt.plot(pathx, pathy, "-r")  # 红色线 最终路径
        plt.plot(bx, by, 'b-')        # 蓝色线 贝塞尔曲线
        plt.plot(bx1, by1, 'b-')
        plt.plot(bx2, by2, 'b-')
        plt.plot(bx3, by3, 'b-')
        plt.plot(bx4, by4, 'b-')

        plt.show()
        plt.pause(0.001)   # 动态显示

if __name__ == '__main__':
    main()


import requests

from restapitest.Routes import Routes

path = "C:\\Users\\Darshan\\Desktop\\questionpapers\\DRDO\\TIER 1\\Arithmetic.pdf"

files = {'file': open(path, 'rb')}

resps=requests.post(Routes.url3, files=files)
print(resps.status_code)
print(resps.text)


import numpy as np
import torch

import string
from torch.utils import data
from random import choice, randrange
from itertools import zip_longest

import pdb

def batch(iterable, n=1):
    args = [iter(iterable)] * n
    return zip_longest(*args)

def pad_tensor(vec, pad, value=0, dim=0):
    """
    args:
        vec - tensor to pad
        pad - the size to pad to
        dim - dimension to pad

    return:
        a new tensor padded to 'pad' in dimension 'dim'
    """
    pad_size = pad - vec.shape[0]

    if len(vec.shape) == 2:
        zeros = torch.ones((pad_size, vec.shape[-1])) * value
    elif len(vec.shape) == 1:
        zeros = torch.ones((pad_size,)) * value
    else:
        raise NotImplementedError
    return torch.cat([torch.Tensor(vec), zeros], dim=dim)

def pad_collate(batch, values=(0, 0), dim=0):
    """
    args:
        batch - list of (tensor, label)

    reutrn:
        xs - a tensor of all examples in 'batch' after padding
        ys - a LongTensor of all labels in batch
        ws - a tensor of sequence lengths
    """

    sequence_lengths = torch.Tensor([int(x[0].shape[dim]) for x in batch])
    sequence_lengths, xids = sequence_lengths.sort(descending=True)
    target_lengths = torch.Tensor([int(x[1].shape[dim]) for x in batch])
    target_lengths, yids = target_lengths.sort(descending=True)
    # find longest sequence
    src_max_len = max(map(lambda x: x[0].shape[dim], batch))
    tgt_max_len = max(map(lambda x: x[1].shape[dim], batch))
    # pad according to max_len
    batch = [(pad_tensor(x, pad=src_max_len, dim=dim), pad_tensor(y, pad=tgt_max_len, dim=dim)) for (x, y) in batch]

    # stack all
    xs = torch.stack([x[0] for x in batch], dim=0)
    ys = torch.stack([x[1] for x in batch]).int()
    xs = xs[xids]
    ys = ys[yids]
    return xs, ys, sequence_lengths.int(), target_lengths.int()

class PolyDataset(data.Dataset):
    """
    https://talbaumel.github.io/blog/attention/
    """
    def __init__(self, filepath='train_small.txt'):
        self.SOS = "<s>"  # all strings will end with the End Of String token
        self.EOS = "</s>"  # all strings will end with the End Of String token

        self.char2int = {
            'c': 10, 's': 11, 't': 12,
            '+': 13, '-': 14, '/': 15, '*': 16, 'p': 17,
            '(': 18, ')': 19,
            'x': 20
        }

        for i in range(10):
            self.char2int[str(i)] = i

        self.VOCAB_SIZE = len(self.char2int)

        with open(filepath) as f:
            content = f.readlines()

        content = [x.strip() for x in content]

        self.set = self.parse(content)

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

    def __getitem__(self, item):
        return self.set[item]

    def translate_str(self, exp):
        d = {
            'cos': 'COS',
            'sin': 'SIN',
            'tan': 'TAN'
        }
        for fr, to in d.items():
            exp = exp.replace(fr, to)

        # translate all variables to x
        alpha = string.ascii_lowercase
        exp = exp.translate({ord(c): 'x' for c in alpha})

        # translate expressions to single character variables
        d = {
            '**': 'p',
            'COS(x)': 'c',
            'SIN(x)': 's',
            'TAN(x)': 't'
        }

        for fr, to in d.items():
            exp = exp.replace(fr, to)

        return exp.replace(" ", "")

    def str_to_int(self, str):
        output = []
        for char in str:
            output.append(self.char2int[char])
        return output

    def parse(self, content):
        pairs = []

        for eq in content:
            left, right = eq.split("=")
            left = self.translate_str(left)
            right = self.translate_str(right)

            left_ints = self.str_to_int(left)
            right_ints = self.str_to_int(right) + [21]
            x = np.zeros((len(left_ints), self.VOCAB_SIZE))

            x[np.arange(len(left_ints)), left_ints] = 1

            pairs.append((x, np.array(right_ints)))

        return pairs


import json
import time

def text_processing(text):
    res = ['\ufeff', '\xa0', '\u3000']
    text = text.strip()
    for ch in res:
        text = text.replace(ch, '')
    return text

def read_txt(input_file):
    with open(input_file, encoding="utf-8") as f:
        reader = f.readlines()
        return [tuple(line.split('\t')) for line in reader]

def read_json(input_file):
    """Reads a json list file."""
    with open(input_file, "r") as f:
        reader = f.readlines()
    return [json.loads(line.strip()) for line in reader]

def save_json(input_json):
    VERSION = time.strftime("%Y%m%d-%H%M%S")
    with open(f'evaluate/results/result-{VERSION}.json', 'w', encoding='utf-8') as f:
        json.dump(input_json, f, ensure_ascii=False, indent=4)
    print(f'result-{VERSION}.json save completely..')