Android Open Source - it.alcacoop.fourinaline Alpha Beta






From Project

Back to project page it.alcacoop.fourinaline.

License

The source code is released under:

GNU General Public License

If you think the Android project it.alcacoop.fourinaline listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.gojul.fourinaline.model;
// w  ww . ja v  a2s.com
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.WeakHashMap;

import org.gojul.fourinaline.model.GameModel.GameStatus;
import org.gojul.fourinaline.model.GameModel.PlayerMark;


/**
 * An implementation of the alpha-beta algorithm for our purpose.
 * This implementation makes it possible to use any user-developed evaluation
 * algorithm that can be better than the one provided.<br/>
 * This algorithm implements a caching mechanism to improve the performance
 * of the AI player.
 * 
 * @author Julien Aubin
 */
public class AlphaBeta implements Serializable {
  /**
   * The serial version UID.
   */
  final static long serialVersionUID = 1;

  /**
   * The cache initial capacity.
   */
  private final static int CACHE_INITIAL_CAPACITY = 5000;

  /**
   * The random which determines which column is to be played
   * when two columns have the same score.
   */
  private final static Random random = new SecureRandom();

  /**
   * The evaluation function.
   */
  private EvalScore evalScore;

  /**
   * The search deepness.
   */
  private int deepness;

  /**
   * AI noise, one per level
   */
  private float[] aiNoise = { 2f, 1.3f, 1.0f, 0.55f, 0f };
  private int aiLevel;
  public static double[] ratings = { 650, 900, 1500, 1700, 1900 };

  /**
   * The random factor.
   */
  private float randFactor;

  /**
   * The score cache.
   */
  private transient Map<String, Integer> scoreCache;


  /**
   * Constructor.
   * 
   * @param evalScoreFunction
   *          the evaluation function used.
   * @param deepnessSearch
   *          the search deepness.
   * @param randomFactor
   *          the random factor used when two possible plays
   *          have the same score.
   * @throws NullPointerException
   *           if any of the method parameter is null.
   * @throws IllegalArgumentException
   *           if <code>deepnessSearch</code> is
   *           inferior or equal to 0, or if <code>randomFactor</code> is not in the
   *           [0, 1] range.
   */
  public AlphaBeta(final EvalScore evalScoreFunction, final int deepnessSearch, final float randomFactor, final int aiNoiseLevel) throws NullPointerException,
      IllegalArgumentException {
    if (evalScoreFunction == null)
      throw new NullPointerException();

    if (deepnessSearch <= 0)
      throw new IllegalArgumentException("deepnessSearch");

    if (randomFactor < 0.0f || randomFactor > 1.0f)
      throw new IllegalArgumentException("randomFactor");

    evalScore = evalScoreFunction;
    deepness = deepnessSearch;
    randFactor = randomFactor;
    aiLevel = aiNoiseLevel;
    scoreCache = new WeakHashMap<String, Integer>(CACHE_INITIAL_CAPACITY);
  }

  /**
   * Returns the index of the column to play, or -1 if there's no
   * more playable column.
   * 
   * @param gameModel
   *          the game model to consider.
   * @param playerMark
   *          the player mark to consider.
   * @return the index of the column to play, or -1 if there's no
   *         more playable column.
   */
  public int getColumnIndex(final GameModel gameModel, final PlayerMark playerMark) {
    Collection<Integer> possiblePlays = gameModel.getListOfPlayableColumns();

    int bestColumn = -1;
    int bestScore = -Integer.MAX_VALUE;

    GameModel tempModel = new GameModel(gameModel);
    // We iterate over the columns from the center
    // as this is the most interesting order for us.
    // This quirk improves greatly speed as the best
    // scores of the alpha beta algorithm are in
    // the middle columns.
    List<Integer> playOrder = new ArrayList<Integer>();
    int column = (tempModel.getColCount() - 1) / 2;
    for (int i = 1, len = tempModel.getColCount(); i <= len; i++) {
      playOrder.add(column);
      column += (i % 2 == 1) ? i : -i;
    }
    // for (int i=0; i<tempModel.getColCount();i++)
    // playOrder.add(i);

    List<Integer> iterationOrder = new ArrayList<Integer>(playOrder);
    iterationOrder.retainAll(possiblePlays);

    HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
    List<Integer> scoresList = new ArrayList<Integer>();
    for (Integer colIndex : iterationOrder) {
      tempModel.play(colIndex.intValue(), playerMark);
      String key = tempModel.toUniqueKey();
      int currentScore = 0;

      Integer currentScoreInt = scoreCache.get(key);

      if (currentScoreInt != null) {
        currentScore = currentScoreInt.intValue();
      } else {
        // We build the key before performing the alpha-beta evaluation
        // becuase tempModel is mutable.
        currentScore = alphaBeta(playOrder, tempModel, playerMark, Integer.MIN_VALUE, -bestScore, 0);
        scoreCache.put(key, Integer.valueOf(currentScore));
      }

      m.put(colIndex, currentScore);
      System.out.println("SCORE " + colIndex + ": " + currentScore);
      tempModel.cancelLastPlay();

      if (currentScore > bestScore) {
        bestScore = currentScore;
        bestColumn = colIndex;
      } else if (currentScore == bestScore) {
        if (random.nextFloat() >= randFactor) {
          bestColumn = colIndex;
        }
      }
    }
    System.out.println("OLD BEST COLUMN: " + bestColumn);

    // Taglio via i piu bassi
    Iterator<Entry<Integer, Integer>> it = m.entrySet().iterator();
    while (it.hasNext()) {
      Map.Entry<Integer, Integer> pairs = (Map.Entry<Integer, Integer>)it.next();
      if ((Integer)pairs.getValue() < -1000)
        it.remove();
      else
        scoresList.add((Integer)pairs.getValue());
    }

    if ((m.size() == 0) || aiLevel == 5)
      return bestColumn;

    // Ordinamento
    Collections.sort(scoresList);

    // Max/min
    int max = scoresList.get(scoresList.size() - 1);
    int min = scoresList.get(0);

    float window = Math.abs(max - min);
    float unit = window / m.size();

    // Soglia al di sotto della quale scartare i valori
    float threshold = aiNoise[aiLevel - 1] * unit;

    float minToPick = max - threshold;
    // System.out.println("MINTOPICK:" + minToPick);

    // Elimino i valori al di sotto di minToPick
    for (Iterator<Integer> iter = scoresList.iterator(); iter.hasNext();) {
      Integer score = (Integer)iter.next();
      if (score < minToPick) {
        iter.remove();
      }
    }

    // Scelgo random tra quelli rimasti
    Integer value = scoresList.get(random.nextInt(scoresList.size()));

    // Prendo colIndex dalla mappa
    Entry<Integer, Integer> item = null;
    for (Entry<Integer, Integer> entry : m.entrySet()) {
      if (entry.getValue() == value) {
        item = entry;
        break;
      }
    }
    bestColumn = item.getKey();
    System.out.println("NEW BEST COLUMN:" + bestColumn);

    return bestColumn;
  }

  /**
   * Deserializes the AI game client in case of serialization.
   * 
   * @param in
   *          the input stream responsible of deserialization.
   * @throws IOException
   *           if an I/O error occurs while deserializing.
   * @throws ClassNotFoundException
   *           in case a class to be deserialized
   *           is not found.
   */
  private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    scoreCache = new WeakHashMap<String, Integer>(CACHE_INITIAL_CAPACITY);
  }


  /**
   * Performs an alpha-beta algorithm over the game model <code>gameModel</code>,
   * with current player <code>playerMark</code>.
   * 
   * @param playOrder
   *          the play order in which we iterate over the board.
   * @param gameModel
   *          the game model to consider.
   * @param playerMark
   *          the player mark to consider.
   * @param alpha
   *          the alpha value.
   * @param beta
   *          the beta value.
   * @param currentDeepness
   *          the deepness in the alpha-beta tree.
   * @return the score of each possibility of the alpha beta model.
   */
  private int alphaBeta(final List<Integer> playOrder, final GameModel gameModel, final PlayerMark playerMark, final int alpha, final int beta, final int currentDeepness) {
    // Game won by the player.
    if (gameModel.getGameStatus() == GameStatus.WON_STATUS) {
      return Integer.MAX_VALUE - currentDeepness;
    }
    // Tie game.
    else if (gameModel.getGameStatus() == GameStatus.TIE_STATUS)
      return 0;
    // Maximum deepness.
    else if (currentDeepness >= deepness)
      return evalScore.evaluate(gameModel, playerMark);
    else {
      int bestScore = Integer.MIN_VALUE;

      PlayerMark tempMark = PlayerMark.getNextMark(playerMark);

      int alphaEval = alpha;

      Collection<Integer> possiblePlays = gameModel.getListOfPlayableColumns();
      List<Integer> iterationOrder = new ArrayList<Integer>(playOrder);
      iterationOrder.retainAll(possiblePlays);

      for (Integer colIndex : iterationOrder) {
        // We avoid there multiple copies of the game model
        // which are unuseful in our case...
        gameModel.play(colIndex.intValue(), tempMark);

        // We cannot use the cache there since it would bring
        // erroneous results.
        int currentScore = alphaBeta(playOrder, gameModel, tempMark, -beta, -alphaEval, currentDeepness + 1);

        gameModel.cancelLastPlay();

        if (currentScore > bestScore) {
          bestScore = currentScore;

          if (bestScore > alphaEval) {
            alphaEval = bestScore;

            if (alphaEval > beta) {
              // What is good for the other player is bad for this one.
              return -bestScore;
            }
          }
        }
      }

      // What is good for the other player is bad for this one.
      return -bestScore;
    }
  }
}




Java Source Code List

android.UnusedStub.java
it.alcacoop.fourinaline.BaseGServiceApplication.java
it.alcacoop.fourinaline.FourInALineDesktop.java
it.alcacoop.fourinaline.FourInALine.java
it.alcacoop.fourinaline.GServiceApplication.java
it.alcacoop.fourinaline.GServiceInterface.java
it.alcacoop.fourinaline.MainActivity.java
it.alcacoop.fourinaline.NativeFunctions.java
it.alcacoop.fourinaline.PrivateDataManager.java
it.alcacoop.fourinaline.PurchaseActivity.java
it.alcacoop.fourinaline.SoundManager.java
it.alcacoop.fourinaline.actors.BoardImage.java
it.alcacoop.fourinaline.actors.Board.java
it.alcacoop.fourinaline.actors.ChatBox.java
it.alcacoop.fourinaline.actors.Checker.java
it.alcacoop.fourinaline.actors.FixedButtonGroup.java
it.alcacoop.fourinaline.actors.IconButton.java
it.alcacoop.fourinaline.actors.ParticleEffectActor.java
it.alcacoop.fourinaline.actors.PlayerBlock.java
it.alcacoop.fourinaline.actors.UIDialog.java
it.alcacoop.fourinaline.billingutils.Base64DecoderException.java
it.alcacoop.fourinaline.billingutils.Base64.java
it.alcacoop.fourinaline.billingutils.IabException.java
it.alcacoop.fourinaline.billingutils.IabHelper.java
it.alcacoop.fourinaline.billingutils.IabResult.java
it.alcacoop.fourinaline.billingutils.Inventory.java
it.alcacoop.fourinaline.billingutils.Purchase.java
it.alcacoop.fourinaline.billingutils.Security.java
it.alcacoop.fourinaline.billingutils.SkuDetails.java
it.alcacoop.fourinaline.client.GwtLauncher.java
it.alcacoop.fourinaline.fsm.FSM.java
it.alcacoop.fourinaline.gservice.GServiceClient.java
it.alcacoop.fourinaline.gservice.GServiceCookieMonster.java
it.alcacoop.fourinaline.gservice.GServiceMessages.java
it.alcacoop.fourinaline.gservice.GServiceNetHandler.java
it.alcacoop.fourinaline.layers.BaseScreen.java
it.alcacoop.fourinaline.layers.GameScreen.java
it.alcacoop.fourinaline.layers.MatchOptionsScreen.java
it.alcacoop.fourinaline.layers.MenuScreen.java
it.alcacoop.fourinaline.layers.OptionsScreen.java
it.alcacoop.fourinaline.layers.SplashScreen.java
it.alcacoop.fourinaline.logic.AIExecutor.java
it.alcacoop.fourinaline.logic.MatchState.java
it.alcacoop.fourinaline.util.Base64DecoderException.java
it.alcacoop.fourinaline.util.Base64.java
it.alcacoop.fourinaline.util.GServiceGameHelper.java
it.alcacoop.fourinaline.util.GameHelperUtils.java
it.alcacoop.fourinaline.utils.AchievementsManager.java
it.alcacoop.fourinaline.utils.AppDataManager.java
it.alcacoop.fourinaline.utils.ELORatingManager.java
org.gojul.fourinaline.model.AlphaBeta.java
org.gojul.fourinaline.model.DefaultEvalScore.java
org.gojul.fourinaline.model.EvalScore.java
org.gojul.fourinaline.model.GameModel.java