Android Open Source - ChineseChess4Android A I






From Project

Back to project page ChineseChess4Android.

License

The source code is released under:

MIT License

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

/*
 * ChineseChess4Android/*w w w  .j  ava2  s.  c  om*/
 *
 * Copyright (c) 2012 Zhijie Lee
 *
 * The MIT License (MIT)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.onezeros.chinesechess;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;

import android.R.integer;
import android.bluetooth.BluetoothA2dp;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * algorithm from feiyan
 * @author onezeros
 *
 */

public class AI {
  private int MAX_PLY = 4;
  public static final int SIZE_X = 9;
  public static final int SIZE_Y = 10;
  public static final int BOARD_SIZE = SIZE_X*SIZE_Y;

  public static final int MOVE_STACK = 4096;
  public static final int HIST_STACK = 50;

  public static final int EMPTY = 7;
  public static final int DARK = 0;
  public static final int LIGHT = 1;

  public static final int PAWN = 0 ;
  public static final int BISHOP = 1;
  public static final int ELEPHANT = 2;
  public static final int KNIGHT = 3;
  public static final int CANNON = 4;
  public static final int ROOK = 5;
  public static final int KING = 6;

  public static final int INFINITY = 20000;
  public static final int NORMAL = 0;
  public static final int SELECT = 1;

  // result of a move
  public static final int MOVE_WIN = 1;
  public static final int MOVE_INVALID = 2;
  public static final int MOVE_OK = 3;




  
  /* the board representation && the initial board state */
  // 0, 1,represent for both sides
  public int[] color = new int[BOARD_SIZE];

  public int[] piece = new int[BOARD_SIZE];

  /* For getting information */
  int nodecount, brandtotal = 0, gencount = 0;
  int ply, side, xside, computerside;
  Move newmove = new Move();
  Recorder[] gen_dat ;//record moved steps
  //store possible moves indexs in gen_data for  current situation
  int[] gen_begin = new int[HIST_STACK];
  int[] gen_end = new int[HIST_STACK];
  History[] hist_dat ;//history data
  int hdp;

  public void setSearchDepth(int depth) {    
    MAX_PLY = depth > 4 ? 4:(depth < 2 ? 2 :depth);
  }
  
  public void saveStatus(DataOutputStream dos) throws IOException {
    for (int i = 0; i < color.length; i++) {
      dos.writeInt(color[i]);
    }
    for (int i = 0; i < piece.length; i++) {
      dos.writeInt(piece[i]);
    }
    dos.writeInt(MAX_PLY);
    dos.writeInt(nodecount);
    dos.writeInt(brandtotal);
    dos.writeInt(gencount);
    dos.writeInt(ply);
    dos.writeInt(side);
    dos.writeInt(xside);
    dos.writeInt(computerside);
    
    dos.writeInt(newmove.from);
    dos.writeInt(newmove.dest);
    
    for (int i = 0; i < gen_dat.length; i++) {
      dos.writeInt(gen_dat[i].m.from);
      dos.writeInt(gen_dat[i].m.dest);
    }
    for (int i = 0; i < gen_begin.length; i++) {
      dos.writeInt(gen_begin[i]);
    }
    for (int i = 0; i < gen_end.length; i++) {
      dos.writeInt(gen_end[i]);
    }
    for (int i = 0; i < hist_dat.length; i++) {
      dos.writeInt(hist_dat[i].m.from);
      dos.writeInt(hist_dat[i].m.dest);
      dos.writeInt(hist_dat[i].capture);
    }

    dos.writeInt(hdp);
  }
  
  public void restoreStatus(DataInputStream dis) throws IOException {
    for (int i = 0; i < color.length; i++) {
      color[i] = dis.readInt();
    }
    for (int i = 0; i < piece.length; i++) {
      piece[i] = dis.readInt();
    }
    MAX_PLY = dis.readInt();
    nodecount = dis.readInt();
    brandtotal = dis.readInt();
    gencount = dis.readInt();
    ply = dis.readInt();
    side = dis.readInt();
    xside = dis.readInt();
    computerside = dis.readInt();
    
    newmove.from = dis.readInt();
    newmove.dest = dis.readInt();
    
    for (int i = 0; i < gen_dat.length; i++) {
      gen_dat[i].m.from = dis.readInt();
      gen_dat[i].m.dest = dis.readInt();
    }
    for (int i = 0; i < gen_begin.length; i++) {
      gen_begin[i] = dis.readInt();
    }
    for (int i = 0; i < gen_end.length; i++) {
      gen_end[i] = dis.readInt();
    }
    for (int i = 0; i < hist_dat.length; i++) {
      hist_dat[i].m.from = dis.readInt();
      hist_dat[i].m.dest = dis.readInt();
      hist_dat[i].capture = dis.readInt();
    }

    hdp = dis.readInt();
  }
  /**** MOVE GENERATE ****/
  //[7][8] possible positions offset
  final int[][] offset = {
      {-1, 1,13, 0, 0, 0, 0, 0}, /* PAWN {for DARK side} */
      {-12,-14,12,14,0,0,0,0}, /* BISHOP */
      {-28,-24,24,28, 0, 0, 0, 0 }, /* ELEPHAN */
      {-11,-15,-25,-27,11,15,25,27}, /* KNIGHT */
      {-1, 1,-13,13, 0, 0, 0, 0}, /* CANNON */
      {-1, 1,-13,13, 0, 0, 0, 0}, /* ROOK */
      {-1, 1,-13,13, 0, 0, 0, 0}/* KING */
  }; 

  //14*13,10*9
  final int[] mailbox182 = {
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,
      -1,-1, 9,10,11,12,13,14,15,16,17,-1,-1,
      -1,-1,18,19,20,21,22,23,24,25,26,-1,-1,
      -1,-1,27,28,29,30,31,32,33,34,35,-1,-1,
      -1,-1,36,37,38,39,40,41,42,43,44,-1,-1,
      -1,-1,45,46,47,48,49,50,51,52,53,-1,-1,
      -1,-1,54,55,56,57,58,59,60,61,62,-1,-1,
      -1,-1,63,64,65,66,67,68,69,70,71,-1,-1,
      -1,-1,72,73,74,75,76,77,78,79,80,-1,-1,
      -1,-1,81,82,83,84,85,86,87,88,89,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  };

  //positions in mailbox182 10*9
  final int[] mailbox90 = {
      28, 29, 30, 31, 32, 33, 34, 35, 36,//+5
      41, 42, 43, 44, 45, 46, 47, 48, 49,
      54, 55, 56, 57, 58, 59, 60, 61, 62,
      67, 68, 69, 70, 71, 72, 73, 74, 75,
      80, 81, 82, 83, 84, 85, 86, 87, 88,
      93, 94, 95, 96, 97, 98, 99,100,101,
      106, 107,108,109,110,111,112,113,114,
      119, 120,121,122,123,124,125,126,127,
      132, 133,134,135,136,137,138,139,140,
      145, 146,147,148,149,150,151,152,153
  };

  final int[] legalposition = {
      1, 1, 5, 3, 3, 3, 5, 1, 1,
      1, 1, 1, 3, 3, 3, 1, 1, 1,
      5, 1, 1, 3, 7, 3, 1, 1, 5,
      1, 1, 1, 1, 1, 1, 1, 1, 1,
      9, 1,13, 1, 9, 1,13, 1, 9,
      9, 9, 9, 9, 9, 9, 9, 9, 9,
      9, 9, 9, 9, 9, 9, 9, 9, 9,
      9, 9, 9, 9, 9, 9, 9, 9, 9,
      9, 9, 9, 9, 9, 9, 9, 9, 9,
      9, 9, 9, 9, 9, 9, 9, 9, 9
  };

  final int[] maskpiece = {8, 2, 4, 1, 1, 1, 2};
  final int[] knightcheck = {1,-1,-9,-9,-1,1,9,9};
  final int[] elephancheck = {-10,-8,8,10,0,0,0,0};
  final int[] kingpalace = {3,4,5,12,13,14,21,22,23};//possible positions for computer side

  
  public AI() {
    gen_dat = new Recorder[MOVE_STACK];
    for (int i = 0; i < gen_dat.length; i++) {
      gen_dat[i] = new Recorder();
    }
    hist_dat = new History[HIST_STACK];
    for (int i = 0; i < hist_dat.length; i++) {
      hist_dat[i] = new History(); 
    }
  }
  public void init() {
    gen_begin[0] = 0; 
    ply = 0; 
    hdp = 0;
    side = LIGHT; 
    xside = DARK; 
    computerside = DARK;

    int[] clr = {
      0, 0, 0, 0, 0, 0, 0, 0, 0,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      7, 0, 7, 7, 7, 7, 7, 0, 7,
      0, 7, 0, 7, 0, 7, 0, 7, 0,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      1, 7, 1, 7, 1, 7, 1, 7, 1,
      7, 1, 7, 7, 7, 7, 7, 1, 7,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      1, 1, 1, 1, 1, 1, 1, 1, 1
    };
    int[] pc =   {
      5, 3, 2, 1, 6, 1, 2, 3, 5,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      7, 4, 7, 7, 7, 7, 7, 4, 7,
      0, 7, 0, 7, 0, 7, 0, 7, 0,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      0, 7, 0, 7, 0, 7, 0, 7, 0,
      7, 4, 7, 7, 7, 7, 7, 4, 7,
      7, 7, 7, 7, 7, 7, 7, 7, 7,
      5, 3, 2, 1, 6, 1, 2, 3, 5
    };
    System.arraycopy(clr, 0, color, 0, clr.length);
    System.arraycopy(pc, 0, piece, 0, pc.length);
  }
  //check whether computer's King will be killed by opponent's King directly
  // after computer moves King,
  int kingFace(int from, int dest)
  {
    int i, k, t, r = 0;
    i = from % SIZE_X;
    if (i>=3 && i<=5 && piece[dest]!=KING)
    {
      t = piece[dest]; piece[dest] = piece[from]; piece[from] = EMPTY;//make the move
      i = 0;
      for (k=kingpalace[i]; piece[k]!=KING; k++) ;
      for (k += SIZE_X; k<BOARD_SIZE && piece[k]==EMPTY; k += SIZE_X);
      if ( k<BOARD_SIZE && piece[k]==KING) r = 1;
      piece[from] = piece[dest]; piece[dest] = t;//unmove
    }
    return r;
  }
  //save a possible move
  public void pushGeneratedMove(int from, int dest)
  {
    if (kingFace(from, dest) == 0)
    {
      gen_dat[gen_end[ply]].m.from = from;
      gen_dat[gen_end[ply]].m.dest = dest;
      gen_end[ply]++;
    }
  }

  //generate all possible moves
  public void generateMoves()
  {
    int i, j, k, n, p, x, y, t, fcannon;

    gen_end[ply] = gen_begin[ply];

    for (i=0; i < BOARD_SIZE; i++){
      if (color[i]==side)
      {
        p = piece[i];//piece kind
        for (j=0; j<8; j++)
        {
          if (offset[p][j] == 0) break;//find possible next position
          x = mailbox90[i]; //offset in mailbox128
          fcannon = 0;
          if (p==ROOK || p==CANNON) n = 9; else n = 1;//
          for (k=0; k<n; k++)
          {
            //  get offset result for (p==PAWN && side==LIGHT)
            //there is no offset table for it
            if (p==PAWN && side==LIGHT) x -= offset[p][j]; else x += offset[p][j];

            y = mailbox182[x];
            //  t for the position in the board of this piece ,
            //according which side the piece is 
            if (side == DARK) t = y; else t = 89-y;
            if (y==-1 || (legalposition[t] & maskpiece[p])==0) break;
            if (fcannon == 0)
            {
              if (color[y]!=side)
                switch (p)
              {
                case KNIGHT: if (color[i+knightcheck[j]]==EMPTY) pushGeneratedMove(i, y); break;
                case ELEPHANT:if (color[i+elephancheck[j]]==EMPTY) pushGeneratedMove(i, y); break;
                case CANNON: if (color[y]==EMPTY) pushGeneratedMove(i, y); break;
                default: pushGeneratedMove(i, y);
              }
              if (color[y]!=EMPTY) { if (p==CANNON) fcannon++; else break; }
            }
            else   /* CANNON switch */
            {
              if (color[y] != EMPTY)
              {
                if (color[y]==xside) pushGeneratedMove(i, y);
                break;
              }
            }
          } /* for k */
        } /* for j */
      }
    }
    gen_end[ply+1] = gen_end[ply]; gen_begin[ply+1] = gen_end[ply];
    brandtotal += gen_end[ply] - gen_begin[ply]; gencount++;
  }

  /***** MOVE *****/
  public boolean move(Move m)
  {
    int from, dest, p;
    nodecount++;
    from = m.from;
    dest = m.dest;
    hist_dat[hdp].m.from = m.from;
    hist_dat[hdp].m.dest = m.dest;
    hist_dat[hdp].capture = p = piece[dest];
    piece[dest] = piece[from]; piece[from] = EMPTY;
    color[dest] = color[from]; color[from] = EMPTY;
    hdp++; ply++; side = xside; xside = 1-xside;
    return p == KING;
  }


  public void unmove()
  {
    int from, dest;
    hdp--; ply--; side = xside; xside = 1-xside;
    from = hist_dat[hdp].m.from; dest = hist_dat[hdp].m.dest;
    piece[from] = piece[dest]; color[from] = color[dest];
    piece[dest] = hist_dat[hdp].capture;
    if (piece[dest] == EMPTY) color[dest] = EMPTY; else color[dest] = xside;
  }

  /***** EVALUATE *****/
  //  evaluate for current board simply by counting how many and 
  //what kind of pieces left on the board
  public int eval()
  {
    //values for every kind of pieces
    int[] piecevalue = {10, 20, 20, 40, 45, 90, 1000};
    int i, s = 0;
    for (i=0; i<BOARD_SIZE; i++)
      if (color[i]==side) s += piecevalue[piece[i]];
      else if (color[i]==xside) s -= piecevalue[piece[i]];
    return s;
  }


  /***** SEARCH *****/
  /* Search game tree by alpha-beta algorithm */
  public int alphabeta(int alpha, int beta, int depth)
  {
    int i, value, best;

    if (depth == 0) return eval();

    generateMoves();
    best = -INFINITY;

    for (i=gen_begin[ply]; i<gen_end[ply] && best<beta; i++)
    {
      if (best > alpha) alpha = best;

      if (move(gen_dat[i].m)) value = 1000-ply;
      else value = -alphabeta(-beta, -alpha, depth-1);
      unmove();

      if (value > best)
      {
        best = value; 
        if (ply == 0) {
          newmove.from = gen_dat[i].m.from;
          newmove.dest = gen_dat[i].m.dest;
        }
      }
    }

    return best;
  }
  //real move
  public boolean updateNewMove()
  {
    int from, dest, p;
    from = newmove.from; dest = newmove.dest; p = piece[dest];
    piece[dest] = piece[from]; piece[from] = EMPTY;
    color[dest] = color[from]; color[from] = EMPTY;  
    return p == KING;
  }
  
  // 
  public int takeAMove(int from , int to) {
    generateMoves();
    newmove.from = from; 
    newmove.dest = to;
    int ret = MOVE_INVALID;
    for (int i=gen_begin[ply]; i<gen_end[ply]; i++){
      if (gen_dat[i].m.from==newmove.from && gen_dat[i].m.dest==newmove.dest){
        if(updateNewMove()){
          return MOVE_WIN;
        }
        ret = MOVE_OK;
        side = xside; xside = 1-xside;

        break;
      }
    }
    return ret;
  }

  public int computerMove() {
    //computer move
    alphabeta(-INFINITY, INFINITY, MAX_PLY);
    if(updateNewMove()){
      return MOVE_WIN;
    }
    side = xside; xside = 1-xside;
    return MOVE_OK;
  }
  public Move getComputerMove() {
    return newmove;
  }
}

class Move{
  public int from;
  public int dest;
};

class Recorder{
  public Move m ;
  public Recorder() {
    m = new Move();
  }
};

class History{
  public Move m ;
  public int capture;

  public History() {
    m = new Move();
  }
} ;




Java Source Code List

com.onezeros.chinesechess.AI.java
com.onezeros.chinesechess.ChessboardView.java
com.onezeros.chinesechess.ChineseChessActivity.java