import java.util.List;

/**
 * Un objeto del tipo Posicion representa una posicion del juego en cualquier
 * juego de dos jugadores. Como esta clase incluye el algoritmo mini-max con poda alpha-beta, se puede usar para
 * desarrollar cualquier juego de dos jugadores donde la maquina use Inteligencia Artificial para encontrar
 * el mejor movimiento en el juego.
 *
 */
public abstract class Posicion {
    /**
     * Evalua el valor de la posicion/estado del juego actual.
     *
     * @return Devuelve un valor entero de la posicion/estado del juego actual.
     */
    protected abstract int valor();

    /**
     * Realiza un movimiento dado en un juego.
     *
     * @param m el movimiento a realizar.
     */
    protected abstract void Mover(Mover m);

    /**
     * Copia la posicion/estado del juego actual.
     * Este metodo es importante para el metodo de busqueda AlphaBeta ya que este metodo recuerda
     * la posicion/estado del juego antes de realizar el movimiento.
     *
     * @return Devuelve la posicion/estado del juego.
     */
    protected abstract Posicion copiar();

    /**
     * Genera los posibles proximos movimientos segun la posicion/estado del juego y el turno.
     *
     * @return Devuelve una lista con todos los posibles movimientos para la posicion/estado actual del juego.
     */
    protected abstract List<Mover> MovimientosPosibles();

    private Mover mejormov;

    /**
     * Encuentra el mejor movimiento para la posicion/estado actual del juego usando el algoritmo alpha-beta.
     *
     * @param depth la profundidad maxima de busqueda del algoritmo alpha-beta.
     * @return Devuelve el mejor movimiento,encontrado por el algoritmo alpha-beta.
     */
    public Mover getmejormov(int depth) {
        mejormov= null;
        busquedaAlphaBeta(Integer.MIN_VALUE, Integer.MAX_VALUE, depth);
        return mejormov;
    }

    /**
     * Indica de quien es el turno. Si maxTurn es true, entonces es el turno de max,
     * en otro caso, es el turno del otro jugador (min).
     */
    public boolean maxTurn;

    /**
     * Este metodo permire al algoritmo alpha-beta buscar mas lejos en caso de que la posicion/estado actual 
     * del juego no se pueda evaluar. Si este metodo tiene que ser usado, tiene que ser superpuesto en la clase 
     * Posicion especifica que hereda esta clase.
     *
     * @return Devuelve true si la posicion/estado actual ha sido evaluada con mayor profundidad,en otro caso devuelve
     * falso.
     */
    public boolean unclear() {
        return false;
    }

    /**
     * Comprueba si el juego ha finalizado o no.
     *
     * @return Devuelve true si el juego ha finalizado, en otro caso devuelve falso.
     */
    protected abstract boolean gameOver();

    /**
     * Algoritmo de busqueda, que busca el mejor movimiento para la maquina realizando 
     * movimientos de forma inteligente.
     *
     * @param alpha hasta ahora el mejor valor para max.
     * @param beta  hasta ahora el mejor valor para min.
     * @param prof maxima profundidad acerca de cuantas posiciones/estados tiene que pensar la maquina.
     * @return Devuelve el valor de una posicion/estado,el mejor movimiento encontrado .
     */
    public int busquedaAlphaBeta(int alpha, int beta, int prof) {
        List<Mover> npmList = MovimientosPosibles();
        if ((prof <= 0 && !unclear()) || npmList == null || npmList.isEmpty()) {
            return valor();
        }
        for (Mover gm : npmList) {
            Posicion gp = (Posicion) copiar();
            gp.Mover(gm);
            int valor = gp.busquedaAlphaBeta(alpha, beta, prof - 1); //Llamada recursiva.
            if (maxTurn && valor > alpha) {
                alpha = valor;
                if (alpha >= beta) {
                    return alpha;
                }
                mejormov= gm;
            } else if (!maxTurn && valor < beta) {
                beta = valor;
				if (alpha >= beta)
				{ return beta; }
				mejormov= gm;
			}	
		}
		return maxTurn ? alpha : beta;
	}
}
