# -*- coding: latin-1 -*-

from Code.Traducir import tr

class ControlPosicion :
    """
    Lleva el control del tablero, piezas en cada casilla, posibilidad de mover de una a otra, así como el turno....
    Variables :
        self.casillas - diccionario a partir de una coordenada algebraica tiene la pieza o None, las piezas en inglés = negras:rnbqkp y en mayúsculas para las blancas.
        self.siBlancas - lógica
        self.jugadas - número de jugadas completas, empieza por 1 y se incrementa tras mover las negras
        self.movPeonCap - número de medios movimientos tras mover un peón o una captura
        self.alPaso - si se puede capturar al paso, la casilla en notación algebraica
        self.enroques - si se puede enrocar

        Siguiendo la definición FEN :

            A FEN "record" defines a particular game position, all in one text line and using only the ASCII character set. A text file with only FEN data records should have the file extension ".fen".

            A FEN record contains six fields. The separator between fields is a space. The fields are:

               1. Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending with rank 1; within each rank, the contents of each square are described from file a through file h. Following the Standard Algebraic Notation (SAN), each piece is identified by a single letter taken from the standard English names (pawn = "P", knight = "N", bishop = "B", rook = "R", queen = "Q" and king = "K").[1] White pieces are designated using upper-case letters ("PNBRQK") while Black take lowercase ("pnbrqk"). Blank squares are noted using digits 1 through 8 (the number of blank squares), and "/" separate ranks.
               2. Active color. "w" means white moves next, "b" means black.
               3. Castling availability. If neither side can castle, this is "–". Otherwise, this has one or more letters: "K" (White can castle kingside), "Q" (White can castle queenside), "k" (Black can castle kingside), and/or "q" (Black can castle queenside).
               4. En passant target square in algebraic notation. If there's no en passant target square, this is "–". If a pawn has just made a 2-square move, this is the position "behind" the pawn. This is recorded regardless of whether there is a pawn in position to make an en passant capture.
               5. Halfmove clock: This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the fifty-move rule.
               6. Fullmove number: The number of the full move. It starts at 1, and is incremented after Black's move.
    """

    def __init__( self ) :
        self.fenInicial = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
        self.liExtras = []
        self.dfun = {}
        self.dfun["mover"] = { "P":self.moverP, "Q":self.moverQ, "K":self.moverK, "R":self.moverR, "B":self.moverB, "N":self.moverN }
        self.dfun["celdasAtacadasPor"] = { "P":self.celdasAtacadasPorP, "Q":self.celdasAtacadasPorQ, "K":self.celdasAtacadasPorK, "R":self.celdasAtacadasPorR, "B":self.celdasAtacadasPorB, "N":self.celdasAtacadasPorN }

    def posInicial( self ) :
        """
        Se situa en el comienzo normal.
        """
        self.leeFen( self.fenInicial )
        # self.leeFen( "r1bqkbnr/pp1ppppp/2n5/8/3QP3/8/PPP2PPP/RNB1KBNR w KQkq - 1 4" )
        #~ self.leeFen( "2kn1b1r/pp3ppp/5n2/1Np2pN1/5B2/4P3/PPP2PPP/2KR4 w - - 0 1" )
        # self.leeFen( "7k/4Q3/8/6K1/8/8/8/8 w - - 0 1" ) # Mate en 2/3 para blancas
        # self.leeFen( "7K/4q3/8/6k1/8/8/8/8 w - - 0 1" ) # Mate en 2/3 para negras
        #~ self.leeFen( "7K/4q3/Q7/6k1/8/8/8/8 w - - 0 1" ) # Mate en 2/3 para negras
        #~ self.leeFen( "8/3P3k/8/8/8/8/7K/8 w - - 0 1" ) # Peon a punto de coronar -> promoción
        #~ self.leeFen( "7K/8/7k/4Q3/8/8/8/8 w - - 0 1" ) # Tablas
        #~ self.leeFen( "8/8/8/4k1P1/8/8/6P1/2K5 w - - 0 1" )
        #~ self.leeFen( "r3k1nr/pp1n1ppp/4p3/1N6/3N4/8/PP2KP1q/R1B1b3 w - - 0 1" ) # Mate a las blancas
        #~ self.leeFen( "r1b1kbnr/pp3ppp/4p3/4n3/3N4/N7/PP2BPqP/R1BQK2R w - - 0 1" )
        #~ self.leeFen( "2kr1b1r/2p1pppp/p7/3pPb2/1q3P2/2N1P3/PPP3PP/R1BQK2R w KQ - 0 1" )
        #~ self.leeFen( "4rk2/1bpq1rpQ/1pn1p2p/p2pP3/3P1N2/P1PB4/2P2PPP/R5K1 w  - 0 1" )
        #~ self.leeFen( "R3R2K/P6P/2PPq12/1PB1PpQ1/8/p1pp1p1B/bp3p1p/r5rk w - - 6 21" )
        #~ self.leeFen( "3k4/1p3ppp/2q1p3/1p6/8/N1P3Pb/P2n3P/6K1 w - - 2 40" )
        #~ self.leeFen( "2bq1b2/p1ppp3/7p/1rPP1Q2/3k3P/P4R2/4PPP1/2R1KB2 w - - 0 22" ) # Mate de mas de una forma
        #~ self.leeFen( "1rbq1b2/p1pppk2/7p/1PPP4/4Q2P/P6R/4PPP1/R3KB2 w Q - 0 18" )
        #~ r2qkb1r/1pP1pppp/p2p4/1B6/4P1n1/2N5/PPP2PPP/R1B1K2R b KQkq - 0 10

    def logo( self ) :
        #~ self.leeFen( "8/4q1k1/1R2bpn1/1N2n1b1/1B2r1r1/1Q6/1PKNBR2/8 w - - 0 1" )
        self.leeFen( "8/4Q1K1/1r2BPN1/1n2N1B1/1b2R1R1/1q6/1pknbr2/8 w - - 0 1" )

    def copia( self ) :
        """

        Crea una copia de la situación actual.
        """
        p = ControlPosicion( )
        p.leeFen( self.fen() )
        return p

    # -----------------------------------------------------------------------------------------------
    def muestra( self ) :
        import MotorInterno
        mi = MotorInterno.MotorInterno()
        mi.ponFen( self.fen() )
        return mi.muestraC()

    # -----------------------------------------------------------------------------------------------
    def leeFen( self, fen ) :
        """
        Lee una definición FEN y lo pone en variables
        """
        fen = fen.strip()
        if "/" not in fen :
            fen = self.fenInicial

        d = {}
        for i in range(8) :
            for j in range(8) :
                cCol = chr(i+97)
                cFil = chr(j+49)
                d[cCol+cFil] = None
        self.casillas = d

        li = fen.split( " " )
        if len(li) == 4 :
            li.append( "0" )
            li.append( "1" )
        posicion, color, self.enroques, self.alPaso, mp, jg = li

        self.siBlancas = color == "w"
        self.jugadas = int(jg)
        self.movPeonCap = int(mp)

        for x,linea in enumerate(posicion.split("/")) :
            cFil = chr( 48+8-x)
            nc = 0
            for c in linea :
                if c.isdigit() :
                    nc += int(c)
                else :
                    cCol = chr( nc+97 )
                    self.casillas[cCol+cFil] = c
                    nc += 1
        return self

    # -----------------------------------------------------------------------------------------------
    def fenBase( self ) :
        nSin = 0
        posicion = ""
        for i in range(8,0,-1) :
            cFil = chr(i+48)
            fila = ""
            for j in range(8) :
                cCol = chr(j+97)
                clave = cCol+cFil
                v = self.casillas[clave]
                if v is None :
                    nSin += 1
                else :
                    if nSin :
                        fila += "%d"%nSin
                        nSin = 0
                    fila += v
            if nSin :
                fila += "%d"%nSin
                nSin = 0

            posicion += fila
            if i > 1 :
                posicion += "/"
        color = "w" if self.siBlancas else "b"
        return posicion + " " + color

    # -----------------------------------------------------------------------------------------------
    def fenDGT( self ) :
        nSin = 0
        resp = ""
        for i in range(8,0,-1) :
            cFil = chr(i+48)
            for j in range(8) :
                cCol = chr(j+97)
                clave = cCol+cFil
                v = self.casillas[clave]
                if v is None :
                    nSin += 1
                else :
                    if nSin :
                        resp += "%d"%nSin
                        nSin = 0
                    resp += v
        return resp

    # -----------------------------------------------------------------------------------------------
    def fen( self ) :
        """
        Devuelve el FEN de la situación actual.
        """

        posicion = self.fenBase()

        if len(self.alPaso) == 2 :
            letra = self.alPaso[0]
            numero = self.alPaso[1]
            #deben existir
            num = "5" if numero == "6" else "4"
            peon = "P" if numero == "6" else "p"
            let = chr( ord(letra)-1 )
            si = False
            if let >= 'a' :
                si = self.siExistePieza( peon, let + num )
            if not si :
                let = chr( ord(letra)+1 )
                if let <= 'h' :
                    si = self.siExistePieza( peon, let + num )
            if not si :
                self.alPaso = "-"

        if self.enroques != '-' :
            dic = { "K":( "K", "R", "e1", "h1" ), "k":( "k", "r", "e8", "h8" ), "Q":( "K", "R", "e1", "a1" ), "q":( "k", "r", "e8", "a8" ) }
            enr = ""
            for tipo in self.enroques :
                rey, torre, posRey, posTorre = dic[tipo]
                if self.siExistePieza( rey, posRey ) and self.siExistePieza( torre, posTorre ) :
                    enr += tipo
            self.enroques = enr

        return "%s %s %s %d %d"%(posicion, self.enroques, self.alPaso, self.movPeonCap, self.jugadas)

    # -----------------------------------------------------------------------------------------------
    def fenM2( self ) :
        """
        Se utiliza en análisis
        """
        fen = self.fen()
        li = fen.split( " " )
        return " ".join(li[:4])

    # -----------------------------------------------------------------------------------------------
    def capturas( self ) :
        """
        Devuelve las piezas capturadas, liNuestro, liOponente. ( pieza, numero )
        """

        dic = {}
        for pieza, num in ( ("P",8), ("R",2), ("N",2), ("B",2), ("Q",1), ("K",1) ) :
            dic[pieza] = num
            dic[pieza.lower()] = num

        for pieza in self.casillas.itervalues() :
            if pieza and dic[pieza] > 0 :
                dic[pieza] -= 1

        return dic, self.siBlancas

    # -----------------------------------------------------------------------------------------------
    def coordenadasConPieza( self, xyDesde, siDesdeIncluida, xyHasta, siHastaIncluida ) :
        dx,dy = xyDesde.tupla()
        hx,hy = xyHasta.tupla()
        difx = hx-dx
        dify = hy-dy

        sx = 0 if dx == hx else 1 if hx > dx else -1
        sy = 0 if dy == hy else 1 if hy > dy else -1

        elem = max( abs(difx), abs(dify) )
        if not siHastaIncluida :
            elem -= 1

        liResp = []
        if siDesdeIncluida :
            liResp.append( xyDesde )

        xy = xyDesde.clona()
        for z in range( elem ) :
            xy1 = xy.clona( )
            xy1.suma( sx, sy )
            if xy1.valido() :
                liResp.append( xy1 )
                xy = xy1

        return liResp

    # -----------------------------------------------------------------------------------------------
    def piezaEnCoordenadas( self, xy ) :
        return self.casillas[xy.a1h8]

    # -----------------------------------------------------------------------------------------------
    def siPiezasEnCoordenadas( self, listaCoordenadas ) :
        """
        Comprueba si hay alguna pieza en la lista ( x, y )
        """
        for xy in listaCoordenadas :
            if self.casillas[xy.a1h8] :
                return True
        return False

    # -----------------------------------------------------------------------------------------------
    def siPeonCoronando( self, desdeA1H8, hastaA1H8 ) :
        pieza = self.casillas[desdeA1H8]
        if pieza.upper() != "P" : #or self.casillas[hastaA1H8] is not None :
            return False
        desde = Coordenadas( a1h8=desdeA1H8 )
        hasta = Coordenadas( a1h8=hastaA1H8 )
        if pieza == "P" :
            ori = 7
            dest = 8
        else :
            ori = 2
            dest = 1

        #~ if desde.x != hasta.x :
            #~ return False

        if not (desde.y == ori and hasta.y == dest ) :
            return False

        return True

    # -----------------------------------------------------------------------------------------------
    def mover( self, desdeA1H8, hastaA1H8, coronacion=None ) :
        """
        desde y hasta en notación a1h8
        para los peones que coronen, coronacion = pieza en que se convierte

        Devuelve :
            siCorrecto :
                False + mensaje error
                True + liExtras, movimientos adicionales, para tenerlos en cuenta en el tablero (por ejemplo en el enroque o captura al paso)
                        liExtras = lista comando(b=borrar,m=mover, casilla/s)

        """

        self.liExtras = []

        self.coronacion = coronacion
        if coronacion :
            if self.siBlancas :
                self.coronacion = coronacion.upper()
            else :
                self.coronacion = coronacion.lower()

        pieza = self.casillas[desdeA1H8]
        if pieza is None :
            return ( False, "No hay ninguna pieza en %s"%desdeA1H8 )

        if self.siBlancas :
            if pieza.islower() :
                return ( False, "Corresponde mover a las blancas" )
        elif not pieza.islower() :
            return ( False, "Corresponde mover a las negras" )

        piezaDest = self.casillas[hastaA1H8]
        if piezaDest :
            siDestB = piezaDest.isupper()
            if siDestB == self.siBlancas :
                return ( False, "No puede comer una pieza del mismo color" )

        xyDesde = Coordenadas( a1h8=desdeA1H8 )
        xyHasta = Coordenadas( a1h8=hastaA1H8 )

        self.alPasoNuevo = "-"

        self.error = ""
        self.liExtras = []

        resp = self.dfun["mover"][pieza.upper()]( xyDesde, xyHasta )

        if resp :
            if self.siAtacadoElRey() :
                self.error = "El rey está en jaque mate."
                resp = False

        if resp :
            self.alPaso = self.alPasoNuevo
            if not self.siBlancas :
                self.jugadas += 1
            self.siBlancas = not self.siBlancas
            if pieza.upper() == "P" or piezaDest :
                self.movPeonCap = 0
            else :
                if self.siBlancas :
                    self.movPeonCap += 1

            #Enroques
            if piezaDest and piezaDest in "Rr" :
                dic = { (1,1):"Q", (8,1):"K", (1,8):"q", (8,8):"k" }
                tp = (xyHasta.x, xyHasta.y)
                if tp in dic :
                    self.enroques = self.enroques.replace( dic[tp], "" )
            if self.enroques == "" :
                self.enroques = "-"

        if self.error :
            return ( False, self.error )

        else :
            return ( True, self.liExtras )

    # -----------------------------------------------------------------------------------------------
    def moverP( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y
        salto = abs(dy-hy)

        xyTmp = Coordenadas(  )

        siB = self.siBlancas
        siN = not self.siBlancas

        pieza = self.casillas[xyDesde.a1h8]

        # Imposibles básicos
        if abs(hx-dx) not in [ 0, 1 ] :
            self.error = "No puede saltar a esa columna"
            return False

        if ( siB and hy < dy ) or ( siN and hy > dy ) :
            self.error = "No puede volver hacia atrás"
            return False

        # Si es movimiento
        if dx == hx :
            if salto == 2 :
                if ( siB and dy != 2 ) or ( siN and dy != 7 ) :
                    self.error = "Sólo se pueden saltar dos posiciones desde la casilla inicial"
                    return False

                xyTmp.copia( xyDesde ).adelante( 1 if siB else -1 )

                if self.piezaEnCoordenadas( xyTmp ) :
                    self.error = "Hay una pieza en medio"
                    return False

                xyTmp.adelante( 1 if siB else -1 )

                if self.piezaEnCoordenadas( xyTmp ) :
                    self.error = "Hay una pieza ocupando el lugar"
                    return False

                xyTmp.copia( xyDesde ).adelante( 1 if siB else -1 )
                self.alPasoNuevo = xyTmp.a1h8

            elif salto == 1 :
                xyTmp.copia( xyDesde ).adelante( 1 if siB else -1 )
                if self.piezaEnCoordenadas( xyTmp ) :
                    self.error = "Hay una pieza ocupando el lugar"
                    return False

            else :
                self.error = "No puede hacer este salto %d"%salto
                return False

        # No es movimiento es captura
        else :
            if salto != 1 or abs(dx-hx) != 1 :
                self.error = "No puede hacer este salto"
                return False

            siAlPaso = xyHasta.a1h8 == self.alPaso
            if not ( self.piezaEnCoordenadas( xyHasta ) or siAlPaso ) :
                self.error = "No puede hacer este movimiento"
                return False

            if siAlPaso :
                xyTmp.copia( xyHasta ).adelante( -1 if siB else 1 )
                self.casillas[xyTmp.a1h8] = None

                self.liExtras.append( ("b",xyTmp.a1h8) )

        if xyHasta.y == 8 or xyHasta.y == 1 :
            pieza = self.coronacion
            self.liExtras.append( ("c",xyHasta.a1h8, self.coronacion) )

        self.casillas[xyHasta.a1h8] = pieza
        self.casillas[xyDesde.a1h8] = None

        return True

    # -----------------------------------------------------------------------------------------------
    def moverN( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y

        difx = abs(dx-hx)
        dify = abs(dy-hy)

        if not ( (difx == 2 and dify == 1) or (difx == 1 and dify == 2) ) :
            self.error = "No puede hacer este movimiento"
            return False

        self.casillas[xyHasta.a1h8] = self.casillas[xyDesde.a1h8]
        self.casillas[xyDesde.a1h8] = None

        return True

    # -----------------------------------------------------------------------------------------------
    def moverB( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y

        difx = abs(dx-hx)
        dify = abs(dy-hy)

        if not ( difx == dify ) :
            self.error = "No puede hacer este movimiento"
            return False

        # No puede haber ninguna pieza en medio
        liPiezas = self.coordenadasConPieza( xyDesde, False, xyHasta, False )
        if self.siPiezasEnCoordenadas( liPiezas ) :
            self.error = "Hay piezas en medio, no se puede mover a ese lugar"
            return False

        self.casillas[xyHasta.a1h8] = self.casillas[xyDesde.a1h8]
        self.casillas[xyDesde.a1h8] = None

        return True

    # -----------------------------------------------------------------------------------------------
    def moverR( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y

        difx = abs(dx-hx)
        dify = abs(dy-hy)

        if not ( ( difx > 0 and dify == 0 ) or ( difx == 0 and dify > 0 ) ) :
            self.error = "No puede hacer este movimiento"
            return False

        # No puede haber ninguna pieza en medio
        liPiezas = self.coordenadasConPieza( xyDesde, False, xyHasta, False )
        if self.siPiezasEnCoordenadas( liPiezas ) :
            self.error = "Hay piezas en medio, no se puede mover a ese lugar"
            return False

        # Quitamos la posibilidad de enrocar
        cDesde = xyDesde.a1h8
        if self.siBlancas :
            if cDesde == "a1" and "Q" in self.enroques :
                self.enroques = self.enroques.replace( "Q", "" )
            elif cDesde == "h1" and "K" in self.enroques :
                self.enroques = self.enroques.replace( "K", "" )
        else :
            if cDesde == "a8" and "q" in self.enroques :
                self.enroques = self.enroques.replace( "q", "" )
            elif cDesde == "h8" and "k" in self.enroques :
                self.enroques = self.enroques.replace( "k", "" )

        self.casillas[xyHasta.a1h8] = self.casillas[xyDesde.a1h8]
        self.casillas[xyDesde.a1h8] = None

        return True

    # -----------------------------------------------------------------------------------------------
    def moverQ( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y

        difx = abs(dx-hx)
        dify = abs(dy-hy)

        if not ( ( difx > 0 and dify == 0 ) or ( difx == 0 and dify > 0 ) or (difx == dify )) :
            self.error = "No puede hacer este movimiento"
            return False

        # No puede haber ninguna pieza en medio
        liPiezas = self.coordenadasConPieza( xyDesde, False, xyHasta, False )
        if self.siPiezasEnCoordenadas( liPiezas ) :
            self.error = "Hay piezas en medio, no se puede mover a ese lugar"
            return False

        self.casillas[xyHasta.a1h8] = self.casillas[xyDesde.a1h8]
        self.casillas[xyDesde.a1h8] = None

        return True

    # -----------------------------------------------------------------------------------------------
    def moverK( self, xyDesde, xyHasta ) :
        dx = xyDesde.x
        dy = xyDesde.y
        hx = xyHasta.x
        hy = xyHasta.y

        difx = abs(dx-hx)
        dify = abs(dy-hy)

        bien = dify <= 1 and difx <= 2
        if bien :
            if difx == 2 :
                bien = dify == 0 and ( (dy==1 and self.siBlancas) or (dy==8 and not self.siBlancas))
        if not bien :
            self.error = "No puede hacer este movimiento"
            return False

        # Enroque
        if difx == 2 :
            self.error = "No se puede enrocar"
            # Que se pueda enrocar
            # Que esté la torre
            # Que no haya ninguna pieza en medio
            # Que ni el cuadro al que se mueve el rey al final del enroque, ni los cuadros por los que ha de pasar el rey, estén atacados por piezas contrarias. Ni esté en jaque
            torre = "R" if self.siBlancas else "r"
            rey = "K" if self.siBlancas else "k"
            dama = "Q" if self.siBlancas else "q"

            # Que sea posible el enroque
            if hx == 3 :
                if dama not in self.enroques :
                    self.error += ", por el lado de la dama"
                    return False
                colTorre = 1
                sx = -1

            else :
                if rey not in self.enroques :
                    self.error += ", por el lado del rey"
                    return False
                colTorre = 8
                sx = 1

            # Que haya una torre
            xyTorre = Coordenadas( colTorre, hy )
            if self.piezaEnCoordenadas( xyTorre ) != torre :
                self.error += ", no está la torre en posición"
                return False

            # Que no haya nada en medio
            liPiezas = self.coordenadasConPieza( xyDesde, False, xyHasta, False )
            if self.siPiezasEnCoordenadas( liPiezas ) :
                self.error += ", hay piezas en medio"
                return False

            # Que no estén atacadas, las celdas = desde el rey incluido (jaque) hasta el hx
            liComprobar = [ Coordenadas(dx,dy), Coordenadas(dx+sx,dy), Coordenadas(dx+2*sx,dy) ]
            listaAtaques = self.celdasAtacadasPorBando( not self.siBlancas )

            for xyCasilla, xyLista in listaAtaques :
                for xComprobar in liComprobar :
                    if xComprobar.estaEnLista( xyLista ) :
                        self.error += ", la posición %s atacada por la pieza en %s"%(xComprobar.a1h8, xyCasilla.a1h8)
                        return False

            self.error = ""

            xyTorreMov = xyDesde.clona().suma( sx,0 )
            self.casillas[xyTorreMov.a1h8] = self.casillas[xyTorre.a1h8]
            self.casillas[xyTorre.a1h8] = None

            self.liExtras.append( ( "m", xyTorre.a1h8, xyTorreMov.a1h8 ) )

        # Movemos
        self.casillas[xyHasta.a1h8] = self.casillas[xyDesde.a1h8]
        self.casillas[xyDesde.a1h8] = None

        # Quitamos la posibilidad de enrocar
        for c in "KQ" if self.siBlancas else "kq" :
            self.enroques = self.enroques.replace( c, "" )

        return True

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorBando( self, siBlancas ) :
        lista = []
        for a1h8, pieza in self.casillas.iteritems() :
            if pieza :
                if (siBlancas and pieza.isupper() ) or ( not siBlancas and pieza.islower() ) :
                    pzlista = self.celdasAtacadasPor( Coordenadas( a1h8=a1h8 ) )
                    if pzlista :
                        lista.append( pzlista )

        return lista

    # -----------------------------------------------------------------------------------------------
    def siAtacadaPosicion( self, xy ) :
        li = self.celdasAtacadasPorBando( not self.siBlancas )
        for pieza, lista in li :
            for xyPos in lista :
                if xyPos.a1h8 == xy.a1h8 :
                    return True
        return False

    # -----------------------------------------------------------------------------------------------
    def siAtacadoElRey( self ) :
        # Donde está el rey ?
        buscamos = "K" if self.siBlancas else "k"
        for a1h8, pieza in self.casillas.iteritems() :
            if pieza == buscamos :
                return self.siAtacadaPosicion( Coordenadas( a1h8=a1h8 ) )
        return True

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPor( self, xy ) :
        pieza = self.piezaEnCoordenadas( xy )
        if not pieza :
            return None
        liResp = self.dfun["celdasAtacadasPor"][pieza.upper()]( xy )

        siBlanca = pieza.isupper()
        for n in range( len(liResp)-1, -1, -1 ) :
            ata = self.piezaEnCoordenadas( liResp[n] )
            if ata :
                if siBlanca :
                    if ata.isupper() :
                        del liResp[n]
                else :
                    if ata.islower() :
                        del liResp[n]
        return (xy, liResp)

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorP( self, xy ) :
        pieza = self.piezaEnCoordenadas( xy )
        direccion = 1 if pieza.isupper() else -1

        liResp = []
        if xy.x < 8 :
            z = xy.clona()
            z.suma( 1, direccion )
            liResp.append( z )
        if xy.x > 1 :
            z = xy.clona()
            z.suma( -1, direccion )
            liResp.append( z )

        return liResp

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorN( self, xy ) :

        li = ( (1,2),(1,-2),(2,1),(2,-1),(-1,2),(-1,-2),(-2,1),(-2,-1) )

        liResp = []
        for sx, sy in li :
            cop = xy.clona()
            cop.suma( sx, sy )
            if cop.valido() :
                liResp.append( cop )

        return liResp

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorB( self, xy ) :

        liResp = []

        for factorx, factory in ( (1,1),(1,-1),(-1,1),(-1,-1) ) :
            xyHasta = xy.clona().suma(8*factorx,8*factory)
            for xyt in self.coordenadasConPieza( xy, False, xyHasta, True ) :
                liResp.append( xyt )
                if self.piezaEnCoordenadas( xyt ) :
                    break

        return liResp

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorR( self, xy ) :
        liResp = []

        for factorx, factory in ( (1,0),(-1,0),(0,1),(0,-1) ) :
            xyHasta = xy.clona().suma(8*factorx,8*factory)
            for xyt in self.coordenadasConPieza( xy, False, xyHasta, True ) :
                liResp.append( xyt )
                if self.piezaEnCoordenadas( xyt ) :
                    break

        return liResp

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorQ( self, xy ) :
        liResp = []
        for factorx, factory in ( (1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1) ) :
            xyHasta = xy.clona().suma(8*factorx,8*factory)
            for xyt in self.coordenadasConPieza( xy, False, xyHasta, True ) :
                liResp.append( xyt )
                if self.piezaEnCoordenadas( xyt ) :
                    break

        return liResp

    # -----------------------------------------------------------------------------------------------
    def celdasAtacadasPorK( self, xy ) :
        li = ( (1,1),(1,-1),(0,1),(0,-1),(-1,0),(-1,1),(-1,-1) )

        liResp = []
        for sx, sy in li :
            cop = xy.clona()
            cop.suma( sx, sy )
            if cop.valido() :
                liResp.append( cop )

        return liResp

    # -----------------------------------------------------------------------------------------------
    def tablero( self ) :
        resp = "   " + "+---"*8+"+" + "\n"
        for fila in "87654321" :
            resp += " " + fila + " |"
            for columna in "abcdefgh" :
                pieza = self.casillas[columna+fila]
                if pieza is None :
                    resp += "   |"
                    #~ resp += "-"+columna+fila + "|"
                else :
                    resp += " " + pieza + " |"
            resp += " " + fila + "\n"
            resp +=  "   " + "+---"*8+"+" + "\n"
        resp +=  "    "
        for columna in "abcdefgh" :
            resp += " " + columna + "  "

        return resp

    # -----------------------------------------------------------------------------------------------
    def pgn( self, desde, hasta, coronacion = None ) :
        pieza = self.casillas[desde]
        if pieza is None :
            return ""
        pOri = pieza.upper()
        pDest = self.casillas[hasta]

        # Enroque
        if pOri == "K" :
            n = ord( desde[0] ) - ord( hasta[0] )
            if abs(n) == 2 :
                cResp = "O-O-O" if n == 2 else "O-O"
                return cResp

        cRespFinal = hasta

        if coronacion :
            cRespFinal += "=" + coronacion.upper()

        siCaptura = pDest is not None
        siPeon = pOri == "P"
        if siPeon :
            siAlPaso = hasta == self.alPaso
            if siAlPaso :
                siCaptura = True
                # cRespFinal += "e.p."
            cRespInicio = desde[0] if siCaptura else ""
        else :
            cRespInicio = pOri
            # Veamos que no haya otra pieza igual que pueda atacar
            li = []
            for pos, lista in self.celdasAtacadasPorBando( self.siBlancas ) :
                if self.casillas[pos.a1h8] == pieza :
                    for dest in lista :
                        if dest.a1h8 == hasta :
                            li.append( pos.a1h8 )

            if len(li) > 1 :
                columna = desde[0]
                n = 0
                for cpos in li :
                    if columna == cpos[0] :
                        n += 1
                if n == 1 :
                    cRespInicio += columna
                fila = desde[1]
                n = 0
                for cpos in li :
                    if fila == cpos[1] :
                        n += 1
                if n == 1 :
                    cRespInicio += fila

        cRespCaptura = "x" if siCaptura else ""

        return cRespInicio + cRespCaptura + cRespFinal

    # -----------------------------------------------------------------------------------------------
    def pv2dgt( self, desde, hasta, coronacion = None ) :
        pOri = self.casillas[desde]

        # Enroque
        if pOri in "Kk" :
            n = ord( desde[0] ) - ord( hasta[0] )
            if abs(n) == 2 :
                orden = "ke8kc8ra8rd8" if n == 2 else "ke8kg8rh8rf8"
                if pOri == "k" :
                    return orden
                else :
                    return orden.replace( "k", "K" ).replace( "8", "1" )
        # Promotion
        if coronacion :
            coronacion = coronacion.upper() if self.siBlancas else coronacion.lower()
            return pOri + desde + coronacion +hasta

        # Al paso
        if pOri in "Pp" and hasta == self.alPaso :
            if self.siBlancas :
                otro = "p"
                dif = -1
            else :
                otro = "P"
                dif = +1
            casilla = "%s%d"%(hasta[0],int(hasta[1])+dif)
            return pOri +desde + pOri + hasta + otro + casilla + "." + casilla

        return pOri + desde + pOri + hasta
    # -----------------------------------------------------------------------------------------------
    def pgnSP( self, desde, hasta, coronacion = None ) :
        dConv = { "K":tr("PGN_K"), "N":tr("PGN_N"), "B":tr("PGN_B"), "Q":tr("PGN_Q"), "R":tr("PGN_R") }
        resp = self.pgn(desde, hasta, coronacion )
        for k in dConv.keys() :
            if k in resp :
                resp = resp.replace( k, dConv[k] )
        return resp

    # -----------------------------------------------------------------------------------------------
    def siJaque( self ) :

        rey = "K" if self.siBlancas else "k"
        for pos, lista in self.celdasAtacadasPorBando( not self.siBlancas ) : # Es no siBlancas ya que se mira a posteriori, tras mover siBlancas
            for dest in lista :
                if self.casillas[dest.a1h8] == rey :
                    return True

        return False

    # -----------------------------------------------------------------------------------------------
    def siExistePieza( self, pieza, a1h8=None ) :
        if a1h8 :
            return self.casillas[a1h8] == pieza
        else :
            n = 0
            for v in self.casillas.itervalues() :
                if v == pieza :
                    n += 1
            return n

    # -----------------------------------------------------------------------------------------------
    def siFaltaMaterial( self ) :
        # Rey y Rey
        # Rey + Caballo y Rey
        # Rey + Caballo y Rey y Caballo
        # Rey + alfil y Rey
        # Rey + alfil y Rey + alfil
        negras = ""
        blancas = ""
        for v in self.casillas.itervalues() :
            if v :
                if v in "RrQqPp" :
                    return False
                if v in "kK" :
                    continue
                if v.isupper() :
                    blancas += v
                else :
                    negras += v
        lb = len(blancas)
        ln = len(negras)
        if lb > 1 or ln > 1 :
            return False

        if lb == 0 and ln == 0 :
            return True

        todas = blancas.lower() + negras
        if todas in [ "b", "n", "bn", "nb" ]  :
            return True

        return False

    # -----------------------------------------------------------------------------------------------
    def siReyEnJaque( self, siBlancas ) :

        rey = "K" if siBlancas else "k"
        for pos, lista in self.celdasAtacadasPorBando( not siBlancas ) :
            for dest in lista :
                if self.casillas[dest.a1h8] == rey :
                    return True

        return False

    # -----------------------------------------------------------------------------------------------
    def numPiezas( self, pieza ) :
        if not self.siBlancas :
            pieza = pieza.lower()
        num = 0
        for i in range(8) :
            for j in range(8) :
                cCol = chr(i+97)
                cFil = chr(j+49)
                if self.casillas[cCol+cFil] == pieza :
                    num += 1
        return num
    # -----------------------------------------------------------------------------------------------
    def totalPiezas( self ) :
        num = 0
        for i in range(8) :
            for j in range(8) :
                cCol = chr(i+97)
                cFil = chr(j+49)
                if self.casillas[cCol+cFil] :
                    num += 1
        return num

    # -----------------------------------------------------------------------------------------------
    def determinaDesde( self, hasta, pieza, desde ) : #Para leer PGN
        col = None
        fil = None
        self.coronacion = ""
        if desde :
            if len(desde) == 2 :
                return desde
            if desde.isdigit() :
                fil = desde
            else :
                col = desde

        fmover = self.dfun["mover"][pieza.upper()]
        xyhasta = Coordenadas( a1h8 = hasta )

        if not self.siBlancas :
            pieza = pieza.lower()

        siBlancas = self.siBlancas # Para comprobar el jaque tras mover

        for i in range(8) :
            for j in range(8) :
                cCol = chr(i+97)
                cFil = chr(j+49)
                if self.casillas[cCol+cFil] == pieza :
                    if col and col != cCol :
                        continue
                    if fil and fil != cFil :
                        continue
                    xydesde = Coordenadas( x = i+1, y = j+1 )
                    fen = self.fen()
                    if fmover( xydesde, xyhasta ) :
                        if not self.siReyEnJaque(siBlancas) :
                            return cCol+cFil
                        self.leeFen( fen )
        return None

    # -----------------------------------------------------------------------------------------------
    def pesoWB( self ) :
        dpesos = { "Q":110, "N":30, "B":32, "R":50, "P":10 }
        peso = 0

        dposk = {True:[0,0], False:[0,0]}
        for i in range(8) :
            for j in range(8) :
                pieza = self.casillas[chr(i+97)+chr(j+49)]
                if pieza == "K" :
                    dposk[True] = i, j
                elif pieza == "k" :
                    dposk[False] = i, j

        for i in range(8) :
            for j in range(8) :
                pieza = self.casillas[chr(i+97)+chr(j+49)]
                if pieza and pieza.upper() != "K" :
                    siW = pieza.isupper()
                    ck,fk = dposk[not siW]
                    d = 14-(abs(i-ck)+abs(j-fk))
                    valor = d*dpesos[pieza.upper()]
                    if not siW :
                        valor = -valor
                    peso += valor
        return peso

    # -----------------------------------------------------------------------------------------------
    def distanciaPiezaKenemigo( self, a1 ) :

        pieza = self.casillas[a1]
        if pieza is None :
            return 15
        k = "k" if pieza.isupper() else "K"
        for i in range(8) :
            for j in range(8) :
                if self.casillas[chr(i+97)+chr(j+49)] == k :
                    c = ord(a1[0])-97
                    f = int(a1[1])-1
                    return abs(i-c)+abs(j-f)-1

    #~ def siJaqueMate( self ) :
        #~ # Sabemos que está en jaque, ya que sólo se llama desde jugada
        #~ # Hay que averiguar si haciendo todos los movimientos permitidos, siempre está en jaque

        #~ for pos, lista in self.celdasAtacadasPorBando( self.siBlancas ) : # Es no no siBlancas ya que se mira a posteriori, tras mover siBlancas-no
            #~ for dest in lista :
                #~ posicion = self.copia()
                #~ siBien, resp = posicion.mover( pos.a1h8, dest.a1h8 )
                #~ if siBien and not posicion.siJaque() :
                    #~ return False
        #~ return True

class Coordenadas :
    """
    Permite pasar de las coordenadas de las casillas de notación algebraica ( a..h,1..8 ) a una notación numérica, 1..8, 1..8
    """
    def __init__( self, x=0, y= 0, a1h8=None ) :
        if a1h8 :
            self.leeA1h8( a1h8 )
        else :
            self.ponxy( x, y )

    def leeA1h8( self, a1h8 ) :
        self.a1h8 = a1h8
        col = a1h8[0]
        fil = a1h8[1]

        self.x = ord(a1h8[0])-96 if col in "abcdefgh" else 0
        self.y = int(a1h8[1]) if fil in "12345678" else 0

    def clona( self ) :
        return Coordenadas( a1h8=self.a1h8 )

    def copia( self, coord ) :
        self.leeA1h8( coord.a1h8 )
        return self

    def adelante( self, num ) :
        self.y += num
        self.rehazA1h8()
        return self

    def aUnLado( self, num ) :
        self.x += num
        self.rehazA1h8()
        return self

    def suma( self, sx, sy ) :
        self.x += sx
        self.y += sy
        self.rehazA1h8()
        return self

    def rehazA1h8( self ) :
        self.a1h8 = chr(self.x+96) + chr(self.y+48)

    def ponxy( self, x, y ) :
        self.x = x
        self.y = y
        self.rehazA1h8()
        return self

    def tupla( self ) :
        return self.x, self.y

    def valido( self ) :
        return self.x >= 1 and self.x <= 8 and self.y >= 1 and self.y <= 8

    def __str__( self ) :
        return "%s(%d,%d)"%(self.a1h8,self.x,self.y)

    def estaEnLista( self, lista ) :
        for una in lista :
            if una.x == self.x and una.y == self.y :
                return True
        return False

#~ cp = ControlPosicion()

#~ li="""e2e4 g8f6
#~ d2d4 g8f6 c2c4 c7c5 d4d5 b7b5
#~ d2d4 g8f6 c2c4 c7c5
#~ e2e4 e7e5 d2d4 e5d4
#~ e2e4 e7e5 d2d4 e5d4 c2c3 d4c3 f1c4 c3b2 c1b2
#~ c2c4
#~ e2e4 e7e6
#~ e2e4 e7e6 d2d4 d7d5 b1d2
#~ e2e4 e7e6 d2d4 d7d5 e4e5
#~ e2e4 e7e5 g1f3 b8c6 f1c4 f8c5
#~ e2e4 e7e5 g1f3 b8c6 f1c4 f8c5 b1c3 g8f6
#~ d2d4 g8f6 c2c4 g7g6 b1c3 d7d5
#~ e2e4 e7e5 g1f3 b8c6 f1c4
#~ d2d4 g8f6 c2c4 g7g6
#~ d2d4 g8f6 c2c4 g7g6 b1c3
#~ e2e4 e7e5 g1f3
#~ e2e4 e7e5
#~ e2e4
#~ d2d4 g8f6 c2c4 e7e6 b1c3 f8b4
#~ e2e4 e7e5 g1f3 g8f6
#~ e2e4 e7e5 g1f3 d7d6
#~ e2e4 d7d6 d2d4 g8f6 b1c3
#~ d2d4 d7d5 c2c4 c7c6
#~ d2d4 d7d5 c2c4
#~ d2d4 g8f6 c2c4 e7e6 g1f3 b7b6
#~ d2d4 d7d5 c2c4 d5c4
#~ d2d4 d7d5 c2c4 e7e6
#~ d2d4
#~ d2d4 g8f6
#~ g1f3
#~ g1f3 d7d5 g2g3
#~ e2e4 g7g6
#~ e2e4 e7e5 g1f3 b8c6 f1b5 a7a6 b5a4
#~ e2e4 e7e5 g1f3 b8c6 f1b5
#~ e2e4 e7e5 g1f3 b8c6 f1b5 a7a6 b5a4 g8f6 e1g1
#~ e2e4 e7e5 g1f3 b8c6 f1b5 a7a6 b5a4 g8f6
#~ e2e4 d7d5
#~ e2e4 d7d5 e4d5 d8d5 b1c3 d5a5 d2d4 g8f6 g1f3 c8g4 h2h3
#~ e2e4 c7c5
#~ e2e4 c7c5 g1f3 b8c6 d2d4 c5d4 f3d4 g8f6 b1c3 d7d6 f1e2"""

#~ for linea in li.split("\n") :
    #~ linea = linea.strip()
    #~ lih = linea.split( " " )
    #~ cp.posInicial()
    #~ for uno in lih :
        #~ cp.mover( uno[:2], uno[2:] )
    #~ prlk( cp.fen() )

