Android Open Source - blokish A I






From Project

Back to project page blokish.

License

The source code is released under:

GNU General Public License

If you think the Android project blokish 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

/*
* Copyright (C) 2011- stephane coutant/* w  ww.  ja va 2  s .c  o  m*/
*
* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>
*/

package org.scoutant.blokish.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;

import android.util.Log;

public class AI  {

  public static final String tag = "sc";
  private static final int SIZE_WEIGHT = 5;
  private static final int CENTER_WEIGHT = 1;
  private static final int SEEDS_WEIGHT = 3;
  private static final int ENEMY_SEEDS_WEIGHT = 1;
  private static final int CHAINING_WEIGHT = 3;
  public Game game;
  private Random random = new Random();
  
  private int[] maxMoves = { 40, 100, 250, 10000 };

  public int adaptedLevel = 3;

  public AI(Game game) {
    this.game = game;
  }

  public boolean hasMove(int color) {
    Board board = game.boards.get(color);
    for (Square seed : board.seeds()) {
      for (int p=0; p<board.pieces.size(); p++) {
        Piece piece = board.pieces.get(p);
        // Fixing issue #3, changing order rotate/flip
        for( int f=0; f<piece.flips; f++, piece.flip()) {
          for (int r=0; r<piece.rotations; r++, piece.rotate(1)) {
            for (Square s : piece.squares()) {
              int i = seed.i - s.i;
              int j = seed.j - s.j;
              if ( !board.outside(s, i, j) && game.fits(piece, i, j)) {
                Log.d(tag, "possible move : " + new Move(piece, i, j));
                game.boards.get(color).over = false;
                return true;
              }
            }
          }
        }
      }
    }
    game.boards.get(color).over = true;
    return false;
  }
  
  public Move think(int color, int level) {
    if (game.boards.get(color).pieces.isEmpty()) {
      Log.d(tag, "no more pieces for player : " + color);
      // no big deal, AI will continue for the other players. At the very end current player will be granted the winning message.
      game.boards.get(color).over = true;
      return null;
    }
    Log.d(tag, "--------------------------------------------------------------------------------");
    level = Math.min(level, adaptedLevel);
    // reinforce player 1 compared to player 2 and 3
    if (level>1 && color!=1 ) level--;
    Log.d(tag, "thinking for player : " + color + ", upto # moves : " + maxMoves[level]);
    List<Move> moves = thinkUpToNMoves(color, level);
    Log.d(tag, "# moves : " + moves.size());
    if (moves.size()==0) {
      game.boards.get(color).over = true;
      return null;
    }
    Collections.sort(moves);
    Collections.reverse(moves);
    Move move = moves.get( 0);
    Log.d(tag, "best move actually is : " + move);
    // TODO may be many moves with O1 and I2. But only 1 or 2 nice moves!!
    if (moves.size()>20) {
      // let's prune trivial moves
      for (int k=moves.size()-1; k>=2; k--) {
        if (moves.get(k).piece.count<=2) moves.remove(k);
      }
      // Now if we do have a significant # of moves, lets randomized among the very best ones.
      if (moves.size()> 10) {
        move = moves.get( random.nextInt(3));
      } else {
        Log.d(tag, "keeping best move!");
      }
    }
    move.piece.reset(move.ghost);
    return move;
  }
  protected List<Move> thinkUpToNMoves(int color, int level) {
    List<Move> moves = new ArrayList<Move>();
    Board board = game.boards.get(color);
    // Most of time , in the middle of the game, any player has about 10 to 20 seeds.
    int nbSeeds = board.seeds().size();
    Log.d(tag, "# of seeds : " + nbSeeds);
    if (nbSeeds==0) return moves;
    long startedAt = new Date().getTime();
    List<Square> seeds = board.seeds();
    Collections.sort(seeds);
    if (board.pieces.size()> board.nbPieces - 4) {
      seeds = seeds.subList(0, Math.min(seeds.size(), 2));
    }
    for (Square seed : seeds) {
      int movesAgainstSeed=0;
      Log.d(tag, "---- seed : " + seed);
      int maxMovesAgainstSeed = maxMoves[level] / nbSeeds;
//      Log.d(tag, "considering # of moves : " + maxMovesAgainstSeed );
      for (int p=0; p<board.pieces.size() && movesAgainstSeed<maxMovesAgainstSeed; p++) {
        Piece piece = board.pieces.get(p);
        for (int r=0; r<piece.rotations; r++, piece.rotate(1)) {
          for( int f=0; f<piece.flips; f++, piece.flip()) {
            for (Square s : piece.squares()) {
              int i = seed.i - s.i;
              int j = seed.j - s.j;
              if ( !board.outside(s, i, j) && game.fits(piece, i, j)) {
                Move move = new Move(piece, i, j);
                if (!game.valid(move)) {
                  Log.e(tag, "Inconsistant ! "+move);
                }
                int score = SIZE_WEIGHT * piece.count;
                if (board.pieces.size()> board.nbPieces-5) {
                  // encourage moving to the center, this extra bonus only for pentaminos
                  int io = game.size/2 - i;
                  int jo = game.size/2 - j;
                  score -= CENTER_WEIGHT * (io*io + jo*jo);
                }
                int seedsIfAdding = board.scoreSeedsIfAdding(piece, i, j);
                score += SEEDS_WEIGHT * seedsIfAdding ;
                int enemyscore = game.scoreEnemySeedsIfAdding(board.color, piece, i, j);
                score -= ENEMY_SEEDS_WEIGHT * enemyscore;
                // Endgame deep thinking
                if (board.pieces.size() < 9) {
                  score += CHAINING_WEIGHT*chainingScore(color, move);
                }
                move.score = score;
//                Log.d(tag, ""+move);
                if (board.pieces.size()<= board.nbPieces-4 || piece.count>=5) {
                  moves.add(move);
                }
                
                movesAgainstSeed++;
                if (moves.size()>= maxMoves[level]) {
                  autoAdaptLevel(startedAt);
                  return moves;
                }
              }
            }
          }
        }
      }
    }
    autoAdaptLevel(startedAt);
    return moves;
  }

  private int[][] ij = new int [20][20];
  
  /**
   * Considering we play given @param move. 
   * @return true if we may place a piece on newly created seeds.
   */
  protected int chainingScore(int color, Move move) {
    Board board = game.boards.get(color);
    Piece played = move.piece;
    List<Piece> pieces = new ArrayList<Piece>();
    for (Piece piece : board.pieces) {
      if (!piece.equals(played)) {
        pieces.add( piece.clone());
      }
    }
    
    for (int j=0;j<20;j++) {
      for (int i=0;i<20;i++) {
        ij[i][j] = board.ij[i][j];
      }
    }
    // let's place 'played' onto board.
    for(Square s : played.squares(color)) {
      int I = move.i+s.i;
      int J = move.j+s.j;
      if (I>=0 && I<20 && J>=0 && J<20) ij[I][J] = s.value;
    }

    int score = 0;
    Move second = null;
    Log.d(tag, "considering # of pieces : " + pieces.size());
    for (Square seed : played.seeds()) {
      for (Piece piece : pieces) {
        for (int r=0; r<piece.rotations; r++, piece.rotate(1)) {
          for( int f=0; f<piece.flips; f++, piece.flip()) {
            for (Square s : piece.squares()) {
              int i = move.i + seed.i - s.i;
              int j = move.j + seed.j - s.j;
              boolean overlaps = overlaps(color, piece, i, j);
              boolean outside = board.outside(s, i, j); 
              boolean fits =  game.fits(piece, i, j);
              if ( !overlaps && !outside && fits ){
                if (piece.count > score) {
                  second = new Move(piece, i, j);
                  score = piece.count;
                }
                // TODO also include # of promissing seeds?
              }
            }
          }
        }
      }
    }
    if (score>1) Log.d(tag, "may CHAIN with : " + second);
    return score;
  }
  
  public boolean overlaps( int color, Piece piece, int i, int j) {
    for(Square s : piece.squares()) {
      int I = i+s.i;
      int J = j+s.j;
      if ( I>=0&&I<20&&J>=0&&J<20 &&  ij[I][J] > 1 ) return true;
    }
    return false;
  }

  
  private void autoAdaptLevel(long startedAt) {
    long duration = new Date().getTime()- startedAt;
    Log.d(tag, "lasted : " + duration );
    if (duration > 2500 && adaptedLevel>0) {
      Log.i(tag, "Decreasing AI level! * * * * * * * * * * * * * * * * * * * *");
      adaptedLevel --;
    }
  }
}




Java Source Code List

org.scoutant.blokish.AppRater.java
org.scoutant.blokish.BusyIndicator.java
org.scoutant.blokish.ButtonsView.java
org.scoutant.blokish.EndGameDialog.java
org.scoutant.blokish.GameView.java
org.scoutant.blokish.Help.java
org.scoutant.blokish.PieceUI.java
org.scoutant.blokish.RateDialog.java
org.scoutant.blokish.Settings.java
org.scoutant.blokish.SplashScreen.java
org.scoutant.blokish.UI.java
org.scoutant.blokish.model.AI.java
org.scoutant.blokish.model.Board.java
org.scoutant.blokish.model.Game.java
org.scoutant.blokish.model.Move.java
org.scoutant.blokish.model.Piece.java
org.scoutant.blokish.model.Square.java