import socket
import this 
import requests
import sys
import json
from threading import Thread
from queue import Queue
#multithreading?


class Move:
    def __init__(self,start_loc,end_loc,board):
        self.start_row = start_loc[0]
        self.start_col = start_loc[1]
        self.end_row = end_loc[0]
        self.end_col = end_loc[1]
        self.nxt_piece = board[self.end_row][self.end_col]
        self.cur_piece = board[self.start_row][self.start_col]
        self.attack = self.nxt_piece != '00'
        self.moveID = self.start_row + self.start_col*10 + self.end_row *100 +self.end_col * 1000 #Hash
    def __eq__(self, __o: object) -> bool:
        if isinstance(__o, Move):
            return self.moveID == __o.moveID
        return False
    def __str__(self):
        return f"{self.start_row=}, "+f"{self.start_col=}, "+f"{self.end_row=}, "+f"{self.end_col=}"
    def get_goal(self):
        return (self.end_row, self.end_col)
    def get_start(self):
        return (self.start_row, self.start_col)
    def get_goal_chess_comb(self):
        return (self.end_row, self.end_col, self.cur_piece)
    def get_turn(self):
        return self.cur_piece[0]
    def get_piece(self):
        return self.cur_piece

def startNewThread(target):
    thread = Thread(target=target)
    thread.daemon = True
    thread.start()
    
class GameState:   
    
    def __init__(self): 
        '''
        有关信息：
        1. 棋盘尺寸为7*9（横向）
        2. 按照以下命名格式对棋盘中的棋子进行标注：
            (b|r)(1|2|3|4|5|6|7)
        3. 左方为蓝方，对应的网络服务器中的side项目编号为0
           右方为红方，对应的网络服务器中的side项目编号为1
        4. 网络版需要额外的类变量：type, game_id, side, chessman(piece), src, dst
        '''
        self.board = [
            ['00','00','b7','00','00','00','r1','00','00'],
            ['00','b2','00','00','00','00','00','r3','00'],
            ['00','00','b4','00','00','00','r5','00','00'],
            ['00','00','b6','00','00','00','r6','00','00'],
            ['00','00','b5','00','00','00','r4','00','00'],
            ['00','b3','00','00','00','00','00','r2','00'],
            ['00','00','b1','00','00','00','r7','00','00'],
        ]
        #用数字1-7代替棋子的强弱关系
        self.moveFunctions = {
            "1": self.getMseMoves,
            "2": self.getEagMoves,
            "3": self.getFoxMoves,
            "4": self.getStdMoves,
            "5": self.getStdMoves,
            "6": self.getLionMoves,
            "7": self.getStdMoves,
        }
        self.blue_trap_loc=[(2,0),(3,1),(4,0)]
        self.red_trap_loc=[(3,7),(2,8),(4,8)]
        self.blue_home=(3,0)
        self.red_home=(3,8)
        self.blue_pieces=[7,6,5,4,3,2,1]
        self.red_pieces=[7,6,5,4,3,2,1]
        #红方(右)先行
        self.red_to_move=True
        self.conquered=False
        self.win_person=''
        self.MASSACRE=False
        self.isStarted = False
        self.move_log = []
        
    def color(self):
        return 'r' if self.red_to_move else 'b'
    def exchange(self):
        self.red_to_move = not self.red_to_move

    def seventy_three_violation(self, goal, color, showTrace = False):
        #输入：goal为三元组(格坐标x，格坐标y，棋子)
        #     color为颜色
        #     showTrace为是否查看轨迹
        past7Moves = []
        #寻找该动物的移动轨迹
        for i in range(len(self.move_log)-1, -1, -1):
            if self.move_log[i].get_turn() == color:
                past7Moves.append(self.move_log[i])
            if len(past7Moves) > 7:
                break
        # print("past7Moves= ",[move.get_goal_chess_comb() for move in past7Moves])
        if len(past7Moves) > 7:
            past7Moves = past7Moves[-7:]
        visit_counts = dict()
        for move in past7Moves:
            if move.get_goal() in (self.blue_trap_loc + self.red_trap_loc):
                return True
            try:
                visit_counts[move.get_goal_chess_comb()] += 1
            except KeyError:
                visit_counts[move.get_goal_chess_comb()] = 1
        try:
            if visit_counts[goal] >= 3:
                if showTrace:
                    return (goal, [move.get_goal() for move in past7Moves])
                else:
                    return False
            else:
                return True
        except:
            return True
    def seventeen_five_violation(self, goal, color, showTrace = False):
        # 输入：goal为三元组(格坐标x，格坐标y，棋子)
        #     color为颜色
        #     showTrace为是否查看轨迹

        #获取近17步操作
        RECENT_COUNT = 17
        PERMIT_RANGE = 5
        # print("using params: {}, {}, {}".format(goal, color, showTrace))
        past17Moves = []
        for i in range(len(self.move_log) - 1, -1, -1):
            if self.move_log[i].get_turn() == color:
                past17Moves.append(self.move_log[i])
            if len(past17Moves) > RECENT_COUNT:
                break
        if len(past17Moves) >= RECENT_COUNT:
            past17Moves = past17Moves[-RECENT_COUNT:]
        else:
            return True
        #print("past17Movesloc: {}".format([move.get_goal() for move in past17Moves]))
        #判断是否为同一棋子的移动
        piece = past17Moves[0].get_piece()
        for i in range(len(past17Moves)):
            if past17Moves[i].get_piece() != piece:
                return True
        #当为同一棋子时……
        visit_locs = []
        for move in past17Moves:
            if move.get_goal() in (self.blue_trap_loc + self.red_trap_loc):
                return True
            this_loc = move.get_goal()
            if this_loc not in visit_locs:
                visit_locs.append(this_loc)

        try:
            if showTrace and len(visit_locs) <= PERMIT_RANGE:
                if goal in visit_locs:
                    return (goal,visit_locs)
                else:
                    return True
            elif len(visit_locs)<=PERMIT_RANGE:
                if goal in visit_locs:
                    return False
                else:
                    return True
            else:
                return True
        except:
            return True

    # 判断特殊位置
    def inHome(self,row,col,color):
        if color=="b":
            if (row,col)==self.blue_home:
                return True
        else:
            if (row,col)==self.red_home:
                return True
        return False
    
    def inWater(self,row,col):
        if row in [1,2,4,5]:
            if col in [3,4,5]:
                return True
        return False
    
    def inTrap(self,row,col,color): #是否在color方的陷阱中
        if color=="b":
            if (row,col) in self.blue_trap_loc:
                return True
        else:
            if (row,col) in self.red_trap_loc:
                return True
        return False
    
    def Eliminate(self,row,col,nxt_row,nxt_col):#下一步可吃对手的棋
        next_blank = self.board[nxt_row][nxt_col] == '00'
        attack = self.board[nxt_row][nxt_col][0] != self.board[row][col][0] and (eval(self.board[row][col][1]) >= eval(self.board[nxt_row][nxt_col][1]) and not (eval(self.board[row][col][1]) == 7 and eval(self.board[nxt_row][nxt_col][1]) == 1 ) or eval(self.board[row][col][1])==1 and eval(self.board[nxt_row][nxt_col][1])==7)
        other_in_trap = self.board[row][col][0] != self.board[nxt_row][nxt_col][0] and (self.board[nxt_row][nxt_col][0] == 'r' and self.inTrap(nxt_row,nxt_col,'b') or self.board[nxt_row][nxt_col][0] == 'b' and self.inTrap(nxt_row,nxt_col,'r'))        
        return  next_blank or attack or other_in_trap 
    
    # 定义移动方式
    #----------------------------------
    #   以下为移动方式的相关规定：
    #       1.下一步的可行位置以二维数组的格式进行记录。
    #       2.getValidMoves返回值为下一步可行位置构成的集合。
    #----------------------------------    
    def getAllMoves(self): #全部合法移动的集合
        moves=[]
        for row in range(len(self.board)):
            for col in range(len(self.board[row])):
                player = self.board[row][col][0]
                if (player == 'r' and self.red_to_move or player == 'b' and not self.red_to_move):
                    #print('what color is the valid move? ',player)
                    self.moveFunctions[self.board[row][col][1]](row,col,moves)
        valid_moves = []
        for move in moves: # 己方躲避17-5和7-3
            if self.seventy_three_violation(goal=move.get_goal_chess_comb(), color=self.color()) and self.seventeen_five_violation(goal=move.get_goal_chess_comb(), color=self.color()):
                valid_moves.append(move)
        if len(valid_moves) == 0:
            print('warning: no available moves at side {}'.format('b' if not self.red_to_move else 'r'))
        return valid_moves
                
            
    def getStdMoves(self,row,col,moves):#输入当前的位置，将可行路径输出给moves
        directions = [(1,0),(0,1),(-1,0),(0,-1)]
        enemy_color = 'b' if self.red_to_move else 'r'
        for direction in directions:
            new_row = row + direction[0]*1
            new_col = col + direction[1]*1
            if 0<=new_row<=6 and 0<=new_col<=8 :
                nxt_piece = self.board[new_row][new_col]
                if nxt_piece == '00' and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()):#如果下一个位置是空的，则可以移动
                    moves.append(Move((row,col),(new_row,new_col),self.board))
                elif nxt_piece[0]==enemy_color and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()) and self.Eliminate(row,col,new_row,new_col):#如果是敌方棋子，且不在水里，且可以消除，则添加到可行路径中
                    moves.append(Move((row,col),(new_row,new_col),self.board))
        return moves
    
    def getMseMoves(self,row,col,moves):
        directions = [(1,0),(0,1),(-1,0),(0,-1)]
        enemy_color = 'b' if self.red_to_move else 'r'
        for direction in directions:
            new_row = row + direction[0]*1
            new_col = col + direction[1]*1
            if 0<=new_row<=6 and 0<=new_col<=8 :
                nxt_piece = self.board[new_row][new_col]
                if self.inHome(new_row,new_col,self.color()): continue
                if nxt_piece == '00':
                    moves.append(Move((row,col),(new_row,new_col),self.board))
                elif nxt_piece[0]==enemy_color and self.Eliminate(row,col,new_row,new_col) and not self.inWater(row,col):
                    moves.append(Move((row,col),(new_row,new_col),self.board))
        return moves
    
    def getEagMoves(self,row,col,moves):
        #可能存在一些问题，主要是移动方式的判断以及在陷阱的判断
        enemy_color = 'b' if self.red_to_move else 'r'
        new_row = row
        new_col = col
        
        #U&D direction
        while new_row >= 0:
            nxt_piece = self.board[new_row][col]
            if nxt_piece == '00' and not self.inTrap(new_row,col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
                if not self.inHome(new_row,col,self.color()) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
            else:
                if self.Eliminate(row,col,new_row,col) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
                if new_row != row: break
            new_row -= 1
        new_row = row
        while new_row <= 6:
            nxt_piece = self.board[new_row][col]
            if nxt_piece == '00' and not self.inTrap(new_row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
                if not self.inHome(new_row,col,self.color()) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
            else:
                if self.Eliminate(row,col,new_row,col) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
                if new_row != row: break
            new_row += 1
        #L&R Direction
        while new_col >= 0:
            nxt_piece = self.board[row][new_col]
            if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
                if not self.inHome(row,new_col,self.color()) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
            else:
                if self.Eliminate(row,col,row,new_col) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
                if new_col != col: break
            new_col -= 1
        new_col = col
        while new_col <= 8:
            nxt_piece = self.board[row][new_col]
            if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
                if not self.inHome(row,new_col,self.color()) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
            else:
                if self.Eliminate(row,col,row,new_col) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
                if new_col != col: break
            new_col += 1
        return moves
            
         
    
    def getFoxMoves(self,row,col,moves):
        directions = [(1,0),(0,1),(-1,0),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)]
        enemy_color = 'b' if self.red_to_move else 'r'
        for direction in directions:
            new_row = row + direction[0]*1
            new_col = col + direction[1]*1
            if 0<=new_row<=6 and 0<=new_col<=8 :
                nxt_piece = self.board[new_row][new_col]
                if nxt_piece == '00' and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()):#如果下一个位置是空的，则可以移动
                    moves.append(Move((row,col),(new_row,new_col),self.board))
                elif nxt_piece[0]==enemy_color and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()) and self.Eliminate(row,col,new_row,new_col):#如果是敌方棋子，且不在水里，且可以消除，则添加到可行路径中
                    moves.append(Move((row,col),(new_row,new_col),self.board))
        return moves
    
    def getLionMoves(self,row,col,moves):
        directions = [(1,0),(0,1),(-1,0),(0,-1)]
        enemy_color = 'b' if self.red_to_move else 'r'
        for direction in directions:
            new_row = row + direction[0]*1
            new_col = col + direction[1]*1
            if 0<=new_row<=6 and 0<=new_col<=8 :
                nxt_piece = self.board[new_row][new_col]
                if nxt_piece == '00' and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()):#如果下一个位置是空的，则可以移动
                    moves.append(Move((row,col),(new_row,new_col),self.board))
                elif nxt_piece[0]==enemy_color and not self.inWater(new_row,new_col) and not self.inHome(new_row,new_col,self.color()) and self.Eliminate(row,col,new_row,new_col):#如果是敌方棋子，且不在水里，且可以消除，则添加到可行路径中
                    moves.append(Move((row,col),(new_row,new_col),self.board))
        jump_vert_loc=[(0,3),(0,4),(0,5),(3,3),(3,4),(3,5),(6,3),(6,4),(6,5)]
        jump_hori_loc=[(1,2),(1,6),(2,2),(2,6),(4,2),(4,6),(5,2),(5,6)]
        enemy_mouse = enemy_color + '1'
        if (row,col) in jump_vert_loc:
            if row == 0:
                if self.board[1][col] != enemy_mouse and self.board[2][col] != enemy_mouse:
                    if self.Eliminate(row,col,3,col): moves.append(Move((row,col),(3,col),self.board))
            elif row == 3:
                if self.board[1][col] != enemy_mouse and self.board[2][col] != enemy_mouse:
                    if self.Eliminate(row,col,0,col): moves.append(Move((row,col),(0,col),self.board))
                if self.board[4][col] != enemy_mouse and self.board[5][col] != enemy_mouse:
                    if self.Eliminate(row,col,6,col): moves.append(Move((row,col),(6,col),self.board))
            elif row == 6:
                if self.board[4][col] != enemy_mouse and self.board[5][col] != enemy_mouse:
                    if self.Eliminate(row,col,3,col): moves.append(Move((row,col),(3,col),self.board))
        elif (row,col) in jump_hori_loc:
            if col == 2:
                lea = True
                for i in range(3,6):
                    if self.board[row][i] == enemy_mouse: lea = False
                if lea and self.Eliminate(row,col,row,6): moves.append(Move((row,col),(row,6),self.board))
            elif col == 6:
                lea = True
                for i in range(3,6):
                    if self.board[row][i] == enemy_mouse: lea = False
                if lea and self.Eliminate(row,col,row,2): moves.append(Move((row,col),(row,2),self.board))
        return moves
    
    # 判断是否胜利
    def conquer(self):
        if self.board[3][0][0] == 'r' or self.board[3][8][0] == 'b':
            self.conquered = True
            if self.board[3][0][0] == 'r':
                self.win_person = 'r'
            else: self.win_person = 'b'
        elif len(self.blue_pieces) == 0:
            self.conquered = True
            self.win_person = 'r'
        elif len(self.red_pieces) == 0:
            self.conquered = True
            self.win_person = 'b'
        elif len(self.getAllMoves()) == 0:
            self.conquered = True
            self.win_person = 'r' if self.color() == 'b' else 'b' 
        return self.conquered
    
    #移动操作
    def makeMove(self,move):#cur是当前位置，nxt是下一个位置,参数传入为元组
        # makeMove假设这个move一定是合法的
        # 为什么添加toUpload？防止使用makeMove更新己方棋盘时，把敌方棋盘错误上传，导致“棋子消失”。
        if self.board[move.end_row][move.end_col] != '00':
            nxt_piece = self.board[move.end_row][move.end_col]
            if nxt_piece[0] == 'r':
                self.red_pieces.remove(int(nxt_piece[1]))
            else:
                self.blue_pieces.remove(int(nxt_piece[1]))
        self.board[move.end_row][move.end_col] = self.board[move.start_row][move.start_col]
        self.board[move.start_row][move.start_col] = '00'
        self.red_to_move = not self.red_to_move
        self.move_log.append(move)
        
        #由于使用多线程，考虑让另一个线程去更新red_to_move
            
    def undoMove(self):
        if len(self.move_log) != 0:
            move = self.move_log.pop()
            self.board[move.start_row][move.start_col] = move.cur_piece
            self.board[move.end_row][move.end_col] = move.nxt_piece
            if move.nxt_piece != '00' and move.nxt_piece not in (self.blue_pieces if move.nxt_piece[0]=='b' else self.red_pieces):
                (self.blue_pieces if move.nxt_piece[0]=='b' else self.red_pieces).append(int(move.nxt_piece[1]))
            self.red_to_move = not self.red_to_move
            
