/* Pepito v1.59  -- file: mueve.c --
   Copyright (c) 2000-02 Carlos del Cacho

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact the author: guggenjaimito@yahoo.com
*/

#ifndef UNIQUE_SOURCE_FILE
#  include "datos.h"
#endif

void            Mueve(register entero32 jug)
{
    register int    hacia = (jug >> 6) & 0x3f;
    register int    desde = jug & 0x3f;
    register int    captura = (jug >> 12) & 0xf;
    register int    pieza = Tablero[desde];
    register historial *hist = Historial+NumJug;

    hist->hash = HashGlob;
    HashGlob = ~HashGlob;
    hist->hash_p = HashPeones;
    hist->jug = jug;
    hist->status = PosStatus;
    hist->alpaso = AlPaso;
    (hist+1)->last_cap = hist->last_cap;

    if (AlPaso != 64) {
        if (AlPaso > 32)        // TurnoBlancas
            HashGlob ^= Zobrist(PEON_B, 56 + (AlPaso & 7));
        else
            HashGlob ^= Zobrist(PEON_N, (AlPaso & 7));
        AlPaso = 64;
    }
    if (pieza == (Turno ? PEON_B : PEON_N)) {   // si es un peon
        (hist+1)->last_cap = NumJug;
        if (jug & PROMOCION) {
            register int    promocion = (jug >> 16) & 0xf;
            Elimina_Ficha(pieza, desde);
            Elimina_Ficha(promocion, hacia);
            PiezasColor(Turno) ^= MASK[desde] ^ MASK[hacia];
            Material(Turno) += VALOR[promocion] - PEON_VAL;
            HashPeones ^= Zobrist(pieza, desde);
            Tablero[hacia] = promocion;
            Tablero[desde] = VACIA;
            NumPeones[Turno]--;
            Piezas[Turno]++;
            goto FASE_2;
        }
        HashPeones ^= Zobrist(pieza, desde) ^ Zobrist(pieza, hacia);
        if (jug & PEON_DE_2 && ATAQUES_TORRE_FIL[hacia][255] & (Turno ? PeonesN : PeonesB)) {
            AlPaso = hacia - 8 + 16 * Turno;
            if (AlPaso > 32)
                HashGlob ^= Zobrist(PEON_B, 56 + (AlPaso & 7));
            else
                HashGlob ^= Zobrist(PEON_N, (AlPaso & 7));
        }
        if (captura) {
            int             cas_elim = hacia;

            if (jug & AL_PASO)
                cas_elim = (Turno ? hacia + 8 : hacia - 8);
            else if (captura == TORRE_B) {
                if (cas_elim == H1 && Enroque & E_CORTO_B) {
                    Enroque &= ~E_CORTO_B;
                    HashGlob ^= HASH_E_CORTO_B;
                } else if (cas_elim == A1 && Enroque & E_LARGO_B) {
                    Enroque &= ~E_LARGO_B;
                    HashGlob ^= HASH_E_LARGO_B;
                }
            } else if (captura == TORRE_N) {
                if (cas_elim == H8 && Enroque & E_CORTO_N) {
                    Enroque &= ~E_CORTO_N;
                    HashGlob ^= HASH_E_CORTO_N;
                } else if (cas_elim == A8 && Enroque & E_LARGO_N) {
                    Enroque &= ~E_LARGO_N;
                    HashGlob ^= HASH_E_LARGO_N;
                }
            }
            if (captura == (Turno ? PEON_N : PEON_B)) { // si es un peon
                NumPeones[Turno ^ 1]--;
                HashPeones ^= Zobrist(captura, cas_elim);
            } else
                Piezas[Turno ^ 1]--;

            Tablero[cas_elim] = VACIA;
            Elimina_Ficha(captura, cas_elim);
            PiezasColor(Turno ^ 1) ^= MASK[cas_elim];
            Material(Turno ^ 1) -= VALOR[captura];
        }
        Mueve_Ficha(pieza, desde, hacia);
        NumJug++;
        return;
    }
    if (jug & (ENROQUE_CORTO | ENROQUE_LARGO)) {
        if (jug & ENROQUE_CORTO) {
            if (Turno) {
                Mueve_Ficha(TORRE_B, H1, F1);
                PosReyB = G1;
                PosStatus |= REY_B_ENRQ_CORTO;
                HashGlob ^= HASH_E_CORTO_B ^ (Enroque & E_LARGO_B ? HASH_E_LARGO_B : 0);
                Enroque &= ~(E_CORTO_B + E_LARGO_B);
            } else {
                Mueve_Ficha(TORRE_N, H8, F8);
                PosReyN = G8;
                PosStatus |= REY_N_ENRQ_CORTO;
                HashGlob ^= HASH_E_CORTO_N ^ (Enroque & E_LARGO_N ? HASH_E_LARGO_N : 0);
                Enroque &= ~(E_CORTO_N + E_LARGO_N);
            }
        } else {
            if (Turno) {
                Mueve_Ficha(TORRE_B, A1, D1);
                PosReyB = C1;
                PosStatus |= REY_B_ENRQ_LARGO;
                HashGlob ^= HASH_E_LARGO_B ^ (Enroque & E_CORTO_B ? HASH_E_CORTO_B : 0);
                Enroque &= ~(E_CORTO_B + E_LARGO_B);
            } else {
                Mueve_Ficha(TORRE_N, A8, D8);
                PosReyN = C8;
                PosStatus |= REY_N_ENRQ_LARGO;
                HashGlob ^= HASH_E_LARGO_N ^ (Enroque & E_CORTO_N ? HASH_E_CORTO_N : 0);
                Enroque &= ~(E_CORTO_N + E_LARGO_N);
            }
        }
        Mueve_Ficha(pieza, desde, hacia);
        NumJug++;
        return;
    }
    if (desde == PosReyB) {
        if (Enroque & E_CORTO_B) {
            Enroque &= ~E_CORTO_B;
            HashGlob ^= HASH_E_CORTO_B;
        }
        if (Enroque & E_LARGO_B) {
            Enroque &= ~E_LARGO_B;
            HashGlob ^= HASH_E_LARGO_B;
        }
        PosReyB = hacia;
    } else if (desde == PosReyN) {
        if (Enroque & E_CORTO_N) {
            Enroque &= ~E_CORTO_N;
            HashGlob ^= HASH_E_CORTO_N;
        }
        if (Enroque & E_LARGO_N) {
            Enroque &= ~E_LARGO_N;
            HashGlob ^= HASH_E_LARGO_N;
        }
        PosReyN = hacia;
    } else
        switch (desde) {
        case H8:
            if (Enroque & E_CORTO_N) {
                Enroque &= ~E_CORTO_N;
                HashGlob ^= HASH_E_CORTO_N;
            } break;
        case A8:
            if (Enroque & E_LARGO_N) {
                Enroque &= ~E_LARGO_N;
                HashGlob ^= HASH_E_LARGO_N;
            } break;
        case H1:
            if (Enroque & E_CORTO_B) {
                Enroque &= ~E_CORTO_B;
                HashGlob ^= HASH_E_CORTO_B;
            } break;
        case A1:
            if (Enroque & E_LARGO_B) {
                Enroque &= ~E_LARGO_B;
                HashGlob ^= HASH_E_LARGO_B;
            } break;
        }

    Mueve_Ficha(pieza, desde, hacia);

FASE_2:

    if (captura) {
        register int    cas_elim = hacia;

        (hist+1)->last_cap = NumJug;
        Elimina_Ficha(captura, cas_elim);
        PiezasColor(Turno ^ 1) ^= MASK[cas_elim];
        Material(Turno ^ 1) -= VALOR[captura];
        if (captura == (Turno ? PEON_N : PEON_B)) {     // si es un peon
            NumPeones[Turno ^ 1]--;
            HashPeones ^= Zobrist(captura, cas_elim);
        } else
            Piezas[Turno ^ 1]--;

        if (captura == TORRE_B && Enroque & (E_CORTO_B | E_LARGO_B)) {
            if (cas_elim == H1 && Enroque & E_CORTO_B) {
                Enroque &= ~E_CORTO_B;
                HashGlob ^= HASH_E_CORTO_B;
            } else if (cas_elim == A1 && Enroque & E_LARGO_B) {
                Enroque &= ~E_LARGO_B;
                HashGlob ^= HASH_E_LARGO_B;
            }
        } else if (captura == TORRE_N && Enroque & (E_CORTO_N | E_LARGO_N)) {
            if (cas_elim == H8 && Enroque & E_CORTO_N) {
                Enroque &= ~E_CORTO_N;
                HashGlob ^= HASH_E_CORTO_N;
            } else if (cas_elim == A8 && Enroque & E_LARGO_N) {
                Enroque &= ~E_LARGO_N;
                HashGlob ^= HASH_E_LARGO_N;
            }
        }
    }
    NumJug++;
    return;
}

//----------------------------------------------------------------------------------------------
void            CambiaTurno()
{
    Turno ^= 1;
}
//----------------------------------------------------------------------------------------------
void            DesMueve()
{
    int        jug, hacia, desde, captura;
    register int    pieza;

    HashGlob = Historial[--NumJug].hash;
    HashPeones = Historial[NumJug].hash_p;
    jug = Historial[NumJug].jug;
    PosStatus = Historial[NumJug].status;
    AlPaso = Historial[NumJug].alpaso;
    hacia = (jug >> 6) & 0x3f;
    desde = jug & 0x3f;
    captura = (jug >> 12) & 0xf;
    pieza = Tablero[hacia];

    if (jug & PROMOCION) {
        register int    promocion = (jug >> 16) & 0xf;
        Elimina_NH_Ficha(Turno ? PEON_B : PEON_N, desde);
        Elimina_NH_Ficha(promocion, hacia);
        PiezasColor(Turno) ^= MASK[desde] ^ MASK[hacia];
        Material(Turno) += PEON_VAL - VALOR[promocion];
        Tablero[hacia] = captura;
        Tablero[desde] = Turno ? PEON_B : PEON_N;
        NumPeones[Turno]++;
        Piezas[Turno]--;
        goto UNDO_FASE_2;
    }
    if (jug & (ENROQUE_CORTO | ENROQUE_LARGO)) {
        if (jug & ENROQUE_CORTO) {
            if (Turno) {
                Mueve_NH_Ficha(TORRE_B, F1, H1);
                PosReyB = E1;
            } else {
                Mueve_NH_Ficha(TORRE_N, F8, H8);
                PosReyN = E8;
            }
        } else {
            if (Turno) {
                Mueve_NH_Ficha(TORRE_B, D1, A1);
                PosReyB = E1;
            } else {
                Mueve_NH_Ficha(TORRE_N, D8, A8);
                PosReyN = E8;
            }
        }
        Mueve_NH_Ficha(pieza, hacia, desde);
        return;
    }

    if (hacia == PosReyB)
        PosReyB = desde;
    else if (hacia == PosReyN)
        PosReyN = desde;

    Mueve_NH_Ficha(pieza, hacia, desde);

UNDO_FASE_2:

    if (captura) {
        register int    cas_elim = hacia;

        if (captura != (PEON_B + 6 * Turno))
            Piezas[Turno ^ 1]++;
        else
            NumPeones[Turno ^ 1]++;

        if (jug & AL_PASO)
            cas_elim = (Turno ? hacia + 8 : hacia - 8);

        Elimina_NH_Ficha(captura, cas_elim);
        Tablero[cas_elim] = captura;
        Material(Turno ^ 1) += VALOR[captura];
        PiezasColor(Turno ^ 1) ^= MASK[cas_elim];
    }
    return;
}

//--------------------------------------------------------------------------------------
void            MueveHash(register entero32 jug)
{
    register int    hacia = (jug >> 6) & 0x3f;
    register int    desde = jug & 0x3f;
    register int    captura = (jug >> 12) & 0xf;
    register int    pieza = Tablero[desde];

    Historial[NumJug++].hash = HashGlob;

    HashGlob = ~HashGlob;
    if (AlPaso != 64) {
        if (AlPaso > 32)        // TurnoBlancas
            HashGlob ^= Zobrist(PEON_B, 56 + (AlPaso & 7));
        else
            HashGlob ^= Zobrist(PEON_N, (AlPaso & 7));
    }
    if (pieza == (Turno ? PEON_B : PEON_N)) {   // si es un peon
        if (jug & PROMOCION)
            HashGlob = HashGlob ^ Zobrist(pieza, desde) ^ Zobrist(((jug >> 16) & 0xf), hacia);
        else
            HashGlob ^= Zobrist(pieza, desde) ^ Zobrist(pieza, hacia);

        if (captura) {
            int             cas_elim = hacia;

            if (jug & AL_PASO)
                cas_elim = (Turno ? hacia + 8 : hacia - 8);
            else if (captura == TORRE_B) {
                if (cas_elim == H1 && Enroque & E_CORTO_B)
                    HashGlob ^= HASH_E_CORTO_B;
                else if (cas_elim == A1 && Enroque & E_LARGO_B)
                    HashGlob ^= HASH_E_LARGO_B;
            } else if (captura == TORRE_N) {
                if (cas_elim == H8 && Enroque & E_CORTO_N)
                    HashGlob ^= HASH_E_CORTO_N;
                else if (cas_elim == A8 && Enroque & E_LARGO_N)
                    HashGlob ^= HASH_E_LARGO_N;
            }
            HashGlob = HashGlob ^ Zobrist(captura, cas_elim);
        }
        return;
    }
    if (jug & (ENROQUE_CORTO | ENROQUE_LARGO)) {
        if (jug & ENROQUE_CORTO) {
            if (Turno) {
                HashGlob ^= Zobrist(TORRE_B, H1) ^ Zobrist(TORRE_B, F1) ^
                    HASH_E_CORTO_B ^ (Enroque & E_LARGO_B ? HASH_E_LARGO_B : 0);
            } else {
                HashGlob ^= Zobrist(TORRE_N, H8) ^ Zobrist(TORRE_N, F8) ^
                    HASH_E_CORTO_N ^ (Enroque & E_LARGO_N ? HASH_E_LARGO_N : 0);
            }
        } else {
            if (Turno) {
                HashGlob ^= Zobrist(TORRE_B, A1) ^ Zobrist(TORRE_B, D1) ^
                    HASH_E_LARGO_B ^ (Enroque & E_CORTO_B ? HASH_E_CORTO_B : 0);
            } else {
                HashGlob ^= Zobrist(TORRE_N, A8) ^ Zobrist(TORRE_N, D8) ^
                    HASH_E_LARGO_N ^ (Enroque & E_CORTO_N ? HASH_E_CORTO_N : 0);
            }
        }
        HashGlob ^= Zobrist(pieza, desde) ^ Zobrist(pieza, hacia);
        return;
    }
    if (desde == PosReyB) {
        if (Enroque & E_CORTO_B)
            HashGlob ^= HASH_E_CORTO_B;
        if (Enroque & E_LARGO_B)
            HashGlob ^= HASH_E_LARGO_B;
    } else if (desde == PosReyN) {
        if (Enroque & E_CORTO_N)
            HashGlob ^= HASH_E_CORTO_N;
        if (Enroque & E_LARGO_N)
            HashGlob ^= HASH_E_LARGO_N;
    } else
        switch (desde) {
        case H8:
            if (Enroque & E_CORTO_N)
                HashGlob ^= HASH_E_CORTO_N;
            break;
        case A8:
            if (Enroque & E_LARGO_N)
                HashGlob ^= HASH_E_LARGO_N;
            break;
        case H1:
            if (Enroque & E_CORTO_B)
                HashGlob ^= HASH_E_CORTO_B;
            break;
        case A1:
            if (Enroque & E_LARGO_B)
                HashGlob ^= HASH_E_LARGO_B;
            break;
        }

    HashGlob ^= Zobrist(pieza, desde) ^ Zobrist(pieza, hacia);

    if (captura) {
        register int    cas_elim = hacia;

        HashGlob ^= Zobrist(captura, cas_elim);
        if (captura == TORRE_B && Enroque & (E_CORTO_B | E_LARGO_B)) {
            if (cas_elim == H1 && Enroque & E_CORTO_B)
                HashGlob ^= HASH_E_CORTO_B;
            else if (cas_elim == A1 && Enroque & E_LARGO_B)
                HashGlob ^= HASH_E_LARGO_B;
        } else if (captura == TORRE_N && Enroque & (E_CORTO_N | E_LARGO_N)) {
            if (cas_elim == H8 && Enroque & E_CORTO_N)
                HashGlob ^= HASH_E_CORTO_N;
            else if (cas_elim == A8 && Enroque & E_LARGO_N)
                HashGlob ^= HASH_E_LARGO_N;
        }
    }
}
//----------------------------------------------------------------------------------------------
void            DesMueveHash()
{
    HashGlob = Historial[--NumJug].hash;
}
