Package prop.hex.domini.controladors.IA

Source Code of prop.hex.domini.controladors.IA.IAHexNegaMonteScoutCtrl

package prop.hex.domini.controladors.IA;

import prop.cluster.domini.models.Tauler;
import prop.cluster.domini.models.estats.EstatCasella;
import prop.cluster.domini.models.estats.EstatPartida;
import prop.hex.domini.controladors.IA.auxiliars.ElementTaulaTransposicions;
import prop.hex.domini.controladors.IA.auxiliars.FitesDePoda;
import prop.hex.domini.controladors.IA.auxiliars.TwoDistance;
import prop.hex.domini.models.Casella;
import prop.hex.domini.models.PartidaHex;
import prop.hex.domini.models.TaulerHex;

import java.util.HashMap;
import java.util.Random;

/**
* Intel·ligència artificial per al joc Hex que utilitza un mètode negaScout amb Monte Carlo.
* <p/>
* Utilitza un mètode NegaScout amb elecció de moviments a avaluar amb un mètode de
* Monte Carlo equiprobable. A més, guarda els valors de les avaluacions de la partida en joc en una taula de
* transposicions, on els resums (<em>hash</em> en anglès) de les avaluacions vénen donats per <a
* href="http://en.wikipedia.org/wiki/Zobrist_hashing">claus Zobrist</a>.
* <p/>
* Les avaluacions estàtiques del tauler fan servir la funció d'avaluació
* <a href="http://javhar.net/AreBeesBetterThanFruitfliesThesis.pdf">QueenBee</a>,
* de <a href="http://javhar.net/">Jack van Rijswijck</a>.
*
* @author Isaac Sánchez Barrera (Grup 7.3, Hex)
*/
public final class IAHexNegaMonteScoutCtrl extends InteligenciaArtificialHexCtrl
{

  /**
   * Tauler on s'està desenvolupant la partida.
   */
  private TaulerHex tauler;

  /**
   * Profunditat màxima de l'arbre de cerca.
   */
  private int profunditat_maxima = 3;

  /**
   * Taula de transposicions. No té en compte possibles col·lisions.
   *
   * @see TaulerHex#hashCode()
   */
  private HashMap<Integer, ElementTaulaTransposicions> taula_transposicions;

  /**
   * Generador de nombres pseudo-aleatoris per a l'elecció dels moviments a avaluar.
   */
  private Random generador;

  /**
   * Constructor per defecte. Inicialitza la taula de transposicions i el generador dels nombres pseudo-aleatoris.
   */
  public IAHexNegaMonteScoutCtrl()
  {
    taula_transposicions = new HashMap<Integer, ElementTaulaTransposicions>();
    generador = new Random();
  }

  /**
   * Consulta la fitxa de l'oponent
   *
   * @param original Fitxa de qui es vol consultar l'oponent
   * @return La fitxa de l'oponent
   */
  private static EstatCasella fitxaContraria( EstatCasella original )
  {
    if ( original == EstatCasella.JUGADOR_A )
    {
      return EstatCasella.JUGADOR_B;
    }
    else
    {
      return EstatCasella.JUGADOR_A;
    }
  }

  /**
   * Realitza un seguit d'iteracions de l'algorisme negaScout amb nodes aleatoris fent servir la funció d'avaluació
   * de QueensBee.
   * <p/>
   * La quantitat de nodes que s'avaluen depèn de la mida del tauler i les caselles buides. Les caselles que
   * s'acaben avaluant són escollides de manera aleatòria d'entre totes les buides. L'avaluació final s'acaba
   * afegint a la taula de transposicions, amb la fita corresponent.
   *
   * @param jugador        Fitxa del jugador que ha efectuat el darrer moviment
   * @param contrincant    Fitxa del jugador contrincant
   * @param alfa           Valor del paràmetre alfa a maximitzar en la darrera iteració recursiva
   * @param beta           Valor del paràmetre alfa a minimitzar en la darrera iteració recursiva
   * @param profunditat    Profunditat a què s'ha arribat
   * @param estat_iteracio Estat de la partida en la darrera iteració
   * @return El valor avaluat del moviment a la casella per al jugador que efectua el moviment
   * @see ElementTaulaTransposicions
   * @see FitesDePoda
   * @see PartidaHex#comprovaEstatPartida(int, int)
   */
  private int negaMonteScout( EstatCasella jugador, EstatCasella contrincant, int alfa, int beta, int profunditat,
                              EstatPartida estat_iteracio )
  {
    int beta_2, puntuacio;
    if ( profunditat == profunditat_maxima || estat_iteracio != EstatPartida.NO_FINALITZADA )
    {
      puntuacio = funcioAvaluacio( tauler, estat_iteracio, profunditat, jugador );
      taula_transposicions.put( tauler.hashCode(),
          new ElementTaulaTransposicions( partida.getTornsJugats() + profunditat, FitesDePoda.VALOR_EXACTE,
              puntuacio, jugador ) );
      return puntuacio;
    }

    if ( taula_transposicions.containsKey( tauler.hashCode() ) )
    {
      ElementTaulaTransposicions element = taula_transposicions.get( tauler.hashCode() );
      if ( element.getProfunditat() >= partida.getTornsJugats() + profunditat )
      {
        switch ( element.getFita( jugador ) )
        {
          case FITA_INFERIOR:
            alfa = Math.max( alfa, element.getPuntuacio( jugador ) );
            break;
          case FITA_SUPERIOR:
            beta = Math.min( beta, element.getPuntuacio( jugador ) );
            break;
          case VALOR_EXACTE:
            return element.getPuntuacio( jugador );
        }

        if ( alfa >= beta )
        {
          return alfa;
        }
      }
    }

    int caselles_restants = tauler.getMida() * tauler.getMida() - tauler.getTotalFitxes();
    int max_moviments = Math.max( caselles_restants / ( int ) ( Math.sqrt( tauler.getMida() ) * 0.85 ), 7 );

    beta_2 = beta;
    boolean primer_fill = true;
    FitesDePoda fita = FitesDePoda.FITA_SUPERIOR;
    boolean[][] explorades = new boolean[tauler.getMida()][tauler.getMida()];
    int num_explorades = 0;
    while ( num_explorades < max_moviments && num_explorades < caselles_restants )
    {
      Casella actual =
          new Casella( generador.nextInt( tauler.getMida() ), generador.nextInt( tauler.getMida() ) );
      if ( tauler.esMovimentValid( contrincant, actual ) && !explorades[actual.getFila()][actual.getColumna()] )
      {
        explorades[actual.getFila()][actual.getColumna()] = true;
        num_explorades++;
        tauler.mouFitxa( contrincant, actual );
        estat_iteracio = partida.comprovaEstatPartida( actual.getFila(), actual.getColumna() );
        puntuacio = -negaMonteScout( contrincant, jugador, -beta_2, -alfa, profunditat + 1, estat_iteracio );

        if ( alfa < puntuacio && puntuacio < beta && !primer_fill )
        {
          fita = FitesDePoda.VALOR_EXACTE;
          puntuacio = -negaMonteScout( contrincant, jugador, -beta, -alfa, profunditat + 1, estat_iteracio );
        }

        tauler.treuFitxa( actual );

        if ( alfa < puntuacio )
        {
          fita = FitesDePoda.VALOR_EXACTE;
          alfa = puntuacio;
        }

        if ( alfa >= beta )
        {
          taula_transposicions.put( tauler.hashCode(),
              new ElementTaulaTransposicions( partida.getTornsJugats() + profunditat,
                  FitesDePoda.FITA_INFERIOR, alfa, jugador ) );
          return alfa;
        }

        beta_2 = alfa + 1;
        primer_fill = false;
      }
    }

    taula_transposicions.put( tauler.hashCode(),
        new ElementTaulaTransposicions( partida.getTornsJugats() + profunditat, fita, alfa, jugador ) );

    return alfa;
  }

  /**
   * Funció d'avaluació del MiniMax, si estem en un estat termianl on ja ha guanyat un jugador retornem 1000000 o
   * -1000000, que son valors prou grans, sinó, apliquem l'estratègia agresiva o la passiva en funció del valor de
   * la variable tactica_agresiva.
   *
   * @param tauler         Objecte de la classe <code>Tauler</code> sobre el qual es disputa una partida.
   * @param estat_moviment Descriu en quin estat ha quedat <em>tauler</em> en funció de l'últim moviment efectuat
   *                       sobre aquest.
   * @param profunditat    És la profunditat a la que s'ha arribat durant l'exploració de les diferents possibilitats de
   *                       moviment. Cada unitat de <em>profunditat</em> representa un torn jugat de la partida.
   * @param fitxa_jugador  Indica el jugador de la partida a partir del qual avaluar <em>tauler</em>.
   * @return La puntuació de l'evaluació
   */
  public int funcioAvaluacio( Tauler tauler, EstatPartida estat_moviment, int profunditat,
                              EstatCasella fitxa_jugador )
  {
    int retorn;

    if ( estat_moviment == EstatPartida.GUANYA_JUGADOR_A )
    {
      if ( fitxa_jugador == EstatCasella.JUGADOR_A )
      {
        return 1000000;
      }
      else
      {
        return -1000000;
      }
    }
    else if ( estat_moviment == EstatPartida.GUANYA_JUGADOR_B )
    {
      if ( fitxa_jugador == EstatCasella.JUGADOR_B )
      {
        return 1000000;
      }
      else
      {
        return -1000000;
      }
    }

    TwoDistance distancia_a = new TwoDistance( ( TaulerHex ) tauler, EstatCasella.JUGADOR_A );
    TwoDistance distancia_b = new TwoDistance( ( TaulerHex ) tauler, EstatCasella.JUGADOR_B );

    int potencial_a = distancia_a.getPotencial();
    int potencial_b = distancia_b.getPotencial();
    int desempat_a = distancia_a.getNombrePotencialsMinims();
    int desempat_b = distancia_b.getNombrePotencialsMinims();

    if ( fitxa_jugador == EstatCasella.JUGADOR_A )
    {
      retorn = 100 * ( potencial_b - potencial_a ) + desempat_b - desempat_a;
    }
    else
    {
      retorn = 100 * ( potencial_a - potencial_b ) + desempat_a - desempat_b;
    }

    return retorn;
  }

  /**
   * Retorna un moviment adequat al tauler actual per al jugador indicat per fitxa.
   * <p/>
   * Fa servir un algorisme negaScout amb taules de transposició i elecció d'un subconjunt de nodes aleatoris
   * (Monte Carlo amb elecció equiprobable).
   *
   * @param fitxa Fitxa que vol col·locar-se al tauler de la partida del paràmetre implícit.
   * @return La casella on es mouria la fitxa.
   * @see #negaMonteScout(EstatCasella, EstatCasella, int, int, int, EstatPartida)
   * @see InteligenciaArtificialHexCtrl
   */
  public Casella obteMoviment( EstatCasella fitxa )
  {
    if ( partida.getTornsJugats() <= 1 )
    {
      Casella obertura = obertura();
      if ( obertura != null )
      {
        return obertura;
      }
    }

    tauler = partida.getTauler();

    int puntuacio_millor = Integer.MIN_VALUE + 1;
    Casella millor_moviment = null;

    int caselles_restants = tauler.getMida() * tauler.getMida() - tauler.getTotalFitxes();
    if ( partida.getTornsJugats() < 2 )
    {
      caselles_restants--;
    }
    else if ( tauler.getTotalFitxes() % ( ( int ) ( 2.3 * tauler.getMida() ) ) == 0 &&
              partida.getTornsJugats() != 0 )
    {
      profunditat_maxima++;
    }

    int max_moviments = Math.max( caselles_restants / ( int ) ( Math.sqrt( tauler.getMida() ) * 0.6 ), 7 );
    boolean[][] explorades = new boolean[tauler.getMida()][tauler.getMida()];
    int num_explorades = 0;
    while ( num_explorades < max_moviments && num_explorades < caselles_restants )
    {
      Casella actual =
          new Casella( generador.nextInt( tauler.getMida() ), generador.nextInt( tauler.getMida() ) );
      if ( tauler.esMovimentValid( fitxa, actual ) && !explorades[actual.getFila()][actual.getColumna()] )
      {
        explorades[actual.getFila()][actual.getColumna()] = true;
        num_explorades++;

        tauler.mouFitxa( fitxa, actual );
        int puntuacio_actual =
            negaMonteScout( fitxa, fitxaContraria( fitxa ), Integer.MIN_VALUE + 1, Integer.MAX_VALUE - 1, 1,
                partida.comprovaEstatPartida( actual.getFila(), actual.getColumna() ) );

        tauler.treuFitxa( actual );

        if ( puntuacio_actual > puntuacio_millor )
        {
          puntuacio_millor = puntuacio_actual;
          millor_moviment = actual;
        }
      }
    }

    return millor_moviment;
  }
}
TOP

Related Classes of prop.hex.domini.controladors.IA.IAHexNegaMonteScoutCtrl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.