您的位置:首页 > 编程语言 > Java开发

Java中国象棋博弈程序探秘[5]——搜索算法

2008-07-07 12:22 483 查看

搜索算法

转载请保留作者信息:

作者:88250

Blog:http:/blog.csdn.net/DL88250

MSN & Gmail & QQ:DL88250@gmail.com

搜索是电脑棋手AI的核心,有效的搜索算法很关键。本文给出了一些常用的搜索算法代码,以及这些算法的改进。例如配合置换表,历史启发表,开局库。算法的深入学习可以参考注释里给出的地址 : )

/*

* @(#)SearchEngine.java

* Author: 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* Created on May 24, 2008, 10:51:52 AM

*

* 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 Library General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

package cn.edu.ynu.sei.chinesechess.infrastructure.search;

import cn.edu.ynu.sei.chinesechess.infrastructure.common.Motion;

import cn.edu.ynu.sei.chinesechess.infrastructure.common.Situation;

import cn.edu.ynu.sei.chinesechess.infrastructure.search.TranspositionTable.NodeType;

import java.util.Collections;

import java.util.List;

/**

* This class descripts some search algorithms of game tree.

* @author 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* @version 1.1.2.6, Jun 7, 2008

*/

public class SearchEngine {

/**

* value while win a game

*/

public static final int WIN = 54;

/**

* chessboard situation

*/

public Situation situation;

/**

* the best move

*/

public Motion bestMotion;

/**

* situation libaray

*/

private Book book = new Book();

/**

* default search depth

*/

public static int SEARCH_DEPTH = 5;

/**

* For Performance Test.

* @param args should be <code>null</code>

*/

public static void main(String[] args) {

SearchEngine instance;

instance = new SearchEngine(new Situation());

System.out.println("Getting start search!");

long startTime = System.nanoTime();

//instance.basicAlphaBetaSearch(SEARCH_DEPTH, -WIN, WIN);

//instance.alphaBetaWithHistoryHeuristicSearch(SEARCH_DEPTH, -WIN, WIN);

//instance.alphaBetaWithTranspositonSearch(SEARCH_DEPTH, -WIN, WIN);

//instance.principalVariationSearch(SEARCH_DEPTH, -WIN, WIN);

//instance.principalVariationWithHHSearch(SEARCH_DEPTH, -WIN, WIN);

instance.negaScoutWithHHTTSearch(SEARCH_DEPTH, -WIN, WIN);

long estimatedTime = System.nanoTime() - startTime;

System.out.println("Evaluated node count: " + Situation.nodeEvaluatedCount);

System.out.println("TT hit count: " + TranspositionTable.hashHitCount);

System.out.println("Best motion: " + instance.bestMotion.toString());

System.out.println("Elapsed Time: " + estimatedTime / 1000000000.0 + "s");

System.out.println("");

}

/**

* Finds the best move on the specified chessboard situation.

* @param boardSituation the specified chessboard situation

* @return the evaluate value

*/

public int findTheBestMove(int[][] boardSituation) {

TranspositionTable.initHashCode(boardSituation);

return negaScoutWithHHTTSearch(SEARCH_DEPTH, -WIN, WIN);

}

/**

* Search the FEN book for a good move.

* @return if find a move in the book, retusns <code>true</code>,

* otherwise, returns <code>false</code>

*/

public boolean bookSearch() {

List<Motion> motions = situation.generatePossibleMoves();

for (Motion motion : motions) {

situation.makeMove(motion.fromX, motion.fromY, motion.toX, motion.toY);

if (book.exists(situation.chessboard)) {

// this situation exists in book!

bestMotion = motion;

situation.unMove();

return true;

}

situation.unMove();

}

return false;

}

/**

* Basic Alpha-Beta search method.

* @param depth depth of search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

final int basicAlphaBetaSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

if (depth <= 0) {

return situation.evaluate();

}

List<Motion> motions = situation.generatePossibleMoves();

for (Motion motion : motions) {

situation.makeMove(motion.fromX, motion.fromY, motion.toX, motion.toY);

int score = -basicAlphaBetaSearch(depth - 1, -beta, -alpha);

situation.unMove();

if (score > alpha) {

alpha = score;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

if (alpha >= beta) {

return beta;

}

}

}

return alpha;

}

/**

* Alpha-Beta with History Heuristic search method.

* @param depth depth of search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

@SuppressWarnings("unchecked")

final int alphaBetaWithHistoryHeuristicSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

if (depth <= 0) {

return situation.evaluate();

}

List<Motion> motions = situation.generatePossibleMoves();

// History heuristic

if (depth < SEARCH_DEPTH) {

for (Motion motion : motions) {

motion.value = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX, motion.toY);

}

// XXX do sort algorithm by myself for performance?

Collections.sort(motions);

}

for (Motion motion : motions) {

situation.makeMove(motion.fromX, motion.fromY, motion.toX, motion.toY);

int score = -alphaBetaWithHistoryHeuristicSearch(depth - 1, -beta, -alpha);

situation.unMove();

if (score > alpha) {

alpha = score;

HistoryHeuristicTable.setValue(motion.fromX, motion.fromY,

motion.toX, motion.toY,

(HistoryHeuristicTable.getValue(

motion.fromX,

motion.fromY,

motion.toX,

motion.toY) +

2 << depth));

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

if (alpha >= beta) {

return beta;

}

}

}

return alpha;

}

/**

* Principal Variation SearchEngine method.

* <p>Probably the best of the alpha-beta variants, this goes by

* several names: <em><b>NegaScout</b></em>, <em>Principal Variation SearchEngine</em>,

* or <em>PVS</em> for short. The idea is that alpha-beta search works

* best if the first recursive search is likely to be the one

* with the best score. Techniques such as sorting the move list

* or using a best move stored in the hash table make it especially

* likely that the first move is best. If it is, we can search

* the other moves more quickly by using the assumption that

* they are not likely to be as good. So PVS performs that first

* search with a normal window, but on subsequent searches uses a

* zero-width window to test each successive move against the first

* move. Only if the zero-width search fails does it do a normal search.

* </p>

* <p>

* More detalis, please visits:<br>

* <a href="http://www.ics.uci.edu/~eppstein/180a/index.html">

* ICS 180, Winter 1999: Strategy and board game programming</a><br>

* or Read this paper:<br>

* Alexander Reinefild, AN IMPROVEMENT TO THE SCOUT TREE SEARCH ALGORITHM,

* 1983

* </p>

* @param depth depth search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

final int principalVariationSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

if (depth <= 0) {

return situation.evaluate();

}

List<Motion> motions = situation.generatePossibleMoves();

situation.makeMove(motions.get(0).fromX, motions.get(0).fromY,

motions.get(0).toX, motions.get(0).toY);

int best = -principalVariationSearch(depth - 1, -beta, -alpha);

situation.unMove();

if (depth == SEARCH_DEPTH) {

bestMotion = motions.get(0);

}

for (int i = 1; i < motions.size(); i++) {

if (best < beta) {

if (best > alpha) {

alpha = best;

}

Motion motion = motions.get(i);

situation.makeMove(motion.fromX, motion.fromY, motion.toX, motion.toY);

int score = -principalVariationSearch(depth - 1, -alpha - 1, -alpha);

if (score > alpha && score < beta) {

// fail high, re-search

best = -principalVariationSearch(depth - 1, -beta, -score);

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

} else if (score > best) {

best = score;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

}

situation.unMove();

}

}

return best;

}

/**

* Principal Variation with History Heuristic SearchEngine method(fail-soft version).

* @param depth depth search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

@SuppressWarnings("unchecked")

final int principalVariationWithHHSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

if (depth <= 0) {

return situation.evaluate();

}

List<Motion> motions = situation.generatePossibleMoves();

// History heuristic

if (depth < SEARCH_DEPTH) {

for (Motion motion : motions) {

motion.value = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX, motion.toY);

}

Collections.sort(motions);

}

Motion motion = motions.get(0);

situation.makeMove(motion.fromX, motion.fromY,

motion.toX, motion.toY);

int best = -principalVariationWithHHSearch(depth - 1, -beta, -alpha);

situation.unMove();

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

int oldValue = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX,

motion.toY);

HistoryHeuristicTable.setValue(motions.get(0).fromX,

motions.get(0).fromY,

motions.get(0).toX,

motions.get(0).toY,

(oldValue + 2 << depth));

}

for (int i = 1; i < motions.size(); i++) {

if (best < beta) {

if (best > alpha) {

alpha = best;

}

motion = motions.get(i);

situation.makeMove(motion.fromX, motion.fromY, motion.toX, motion.toY);

int score = -principalVariationWithHHSearch(depth - 1, -alpha - 1,

-alpha);

if (score > alpha && score < beta) {

best = -principalVariationWithHHSearch(depth - 1, -beta, -score);

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

} else if (score > best) {

int oldValue = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX,

motion.toY);

HistoryHeuristicTable.setValue(motion.fromX,

motion.fromY,

motion.toX,

motion.toY,

(oldValue + 2 << depth));

best = score;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

}

situation.unMove();

}

}

return best;

}

/**

* Alpha-Beta with Transposition Table search method.

* @param depth depth search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

final int alphaBetaWithTranspositonSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

// lookup transposition table

int score = TranspositionTable.lookup(depth, alpha, beta);

if (score != 88250) {

// hit the target!

return score;

}

if (depth <= 0) {

score = situation.evaluate();

// save the node

TranspositionTable.save(NodeType.exact, depth, score);

return score;

}

NodeType hashItemType = NodeType.unknown;

List<Motion> motions = situation.generatePossibleMoves();

for (Motion motion : motions) {

int toId = situation.makeMove(motion.fromX, motion.fromY, motion.toX,

motion.toY);

score = -alphaBetaWithTranspositonSearch(depth - 1, -beta, -alpha);

situation.unMove();

if (score > alpha) {

alpha = score;

hashItemType = NodeType.exact;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

if (alpha >= beta) {

TranspositionTable.save(NodeType.lowerBound, depth, alpha);

return beta;

}

}

}

if (hashItemType != NodeType.unknown) {

TranspositionTable.save(NodeType.exact, depth, alpha);

} else {

TranspositionTable.save(NodeType.upperBound, depth, alpha);

}

return alpha;

}

/**

* NegaScout with History Heuristic and Transposition Table search.

* @param depth depth search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if game is over, returns <code>-WIN</code>, if depth arrived,

* returns evaluat value(leaf node value), otherwise, returns bound(

* determined by cut-off)

*/

@SuppressWarnings("unchecked")

final int negaScoutWithHHTTSearch(int depth, int alpha, int beta) {

if (situation.gameOver() != 0) {

return -WIN;

}

// lookup transpositiont table

int score = TranspositionTable.lookup(depth, alpha, beta);

if (score != 88250) {

// hit the target!

return score;

}

if (depth <= 0) {

score = situation.evaluate();

TranspositionTable.save(NodeType.exact, depth, score);

return score;

}

List<Motion> motions = situation.generatePossibleMoves();

// History heuristic

if (depth < SEARCH_DEPTH) {

for (Motion motion : motions) {

motion.value = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX, motion.toY);

}

Collections.sort(motions);

}

int bestmove = 0;

int a = alpha;

int b = beta;

int t;

int oldValue;

NodeType hashItemType = NodeType.unknown;

for (int i = 0; i < motions.size(); i++) {

Motion motion = motions.get(i);

int toId = situation.makeMove(motion.fromX, motion.fromY, motion.toX,

motion.toY);

t = -negaScoutWithHHTTSearch(depth - 1, -b, -a);

if (t > a && t < beta && i > 0) {

a = -negaScoutWithHHTTSearch(depth - 1, -beta, -t);

hashItemType = NodeType.exact;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

bestmove = i;

}

situation.unMove();

if (a < t) {

hashItemType = NodeType.exact;

a = t;

if (depth == SEARCH_DEPTH) {

bestMotion = motion;

}

}

if (a >= beta) {

TranspositionTable.save(NodeType.lowerBound, depth, a);

oldValue = HistoryHeuristicTable.getValue(motion.fromX,

motion.fromY,

motion.toX,

motion.toY);

HistoryHeuristicTable.setValue(motion.fromX,

motion.fromY,

motion.toX,

motion.toY,

(oldValue + 2 << depth));

return a;

}

b = a + 1;  // set a new numm window

}

oldValue = HistoryHeuristicTable.getValue(motions.get(bestmove).fromX,

motions.get(bestmove).fromY,

motions.get(bestmove).toX,

motions.get(bestmove).toY);

HistoryHeuristicTable.setValue(motions.get(bestmove).fromX,

motions.get(bestmove).fromY,

motions.get(bestmove).toX,

motions.get(bestmove).toY,

(oldValue + 2 << depth));

if (hashItemType != NodeType.unknown) {

TranspositionTable.save(NodeType.exact, depth, alpha);

} else {

TranspositionTable.save(NodeType.upperBound, depth, alpha);

}

return a;

}

/**

* Constructor with parameters.

* @param situation the specified situation

*/

public SearchEngine(Situation situation) {

this.situation = situation;

}

}

/*

* @(#)Book.java

* Author: 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* Created on Jun 5, 2008, 4:45:31 PM

*

* 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 Library General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

package cn.edu.ynu.sei.chinesechess.infrastructure.search;

import cn.edu.ynu.sei.chinesechess.infrastructure.common.Situation;

import cn.edu.ynu.sei.chinesechess.infrastructure.fen.FEN;

import cn.edu.ynu.sei.chinesechess.infrastructure.fen.FENAccessor;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* Opening book, endgame book, or any chessboard situation book.

* Currently, this class ONLY can read FEN file.

* @author 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* @version 1.0.0.0, Jun 5, 2008

*/

final public class Book {

/**

* book situation hash map

*/

public Map<Integer, FEN> hashMap = new HashMap<Integer, FEN>();

/**

* Packaged default constructor.

*/

Book() {

List<FEN> fens = FENAccessor.readFile("book_final");

Integer hashCode = 0;

for (FEN fen : fens) {

hashCode = TranspositionTable.calcCurHashCode(fen.chessboard);

hashMap.put(hashCode, fen);

}

}

/**

* This book exists the specified chessboard situation?

* @param chessboard the specified chessboard

* @return if exists, returns <code>true</code>, otherwise,

* returns <code>false</code>

*/

final public boolean exists(int[][] chessboard) {

FEN f = hashMap.get(TranspositionTable.calcCurHashCode(chessboard));

if (f != null && f.isBlackDone == false) {

return true;

}

return false;

}

}

/*

* @(#)HistoryHeuristicTable.java

* Author: 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* Created on May 29, 2008, 11:51:10 PM

*

* 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 Library General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

package cn.edu.ynu.sei.chinesechess.infrastructure.search;

/**

* A <em>History Heuristic Table</em> maintains all best motion in

* the past.

* ({@link cn.edu.ynu.sei.chinesechess.common.Motion#value})

* @author 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* @version 1.0.1.4, Jun 7, 2008

*/

final class HistoryHeuristicTable {

/**

* hold all best moves

*/

static int[][][][] holds = new int[9][10][9][10];

/**

* singleton

*/

private static HistoryHeuristicTable instance = new HistoryHeuristicTable();

/**

* Gets the single instance of the class.

* @return history heuristic instance

*/

final public static HistoryHeuristicTable getInstance() {

if (instance == null) {

instance = new HistoryHeuristicTable();

}

return instance;

}

/**

* Private default constructor.

*/

private HistoryHeuristicTable() {

}

/**

* Returns the history motion.

* @param fromX x coordinate of which chessman do this move

* @param fromY y coordinate of which chessman do this move

* @param toX x coordinate of which chessman's destination

* @param toY y coordinate of which chessman's destination

* @return this move's value

*/

final static int getValue(int fromX, int fromY, int toX, int toY) {

return holds[fromX][fromY][toX][toY];

}

/**

* Sets the history motion.

* @param fromX x coordinate of which chessman do this move

* @param fromY y coordinate of which chessman do this move

* @param toX x coordinate of which chessman's destination

* @param toY y coordinate of which chessman's destination

* @param newValue the new value

*/

final static void setValue(int fromX, int fromY, int toX, int toY, int newValue) {

holds[fromX][fromY][toX][toY] = newValue;

}

}

/*

* @(#)TranspositionTable.java

* Author: 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* Created on Jun 1, 2008, 11:04:57 AM

*

* 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 Library General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

package cn.edu.ynu.sei.chinesechess.infrastructure.search;

import java.util.Random;

/**

* A <em>Transpositon Table</em> maintains a mass of node had evaluated.

* In the table, we use <code>HashTable</code> to store each game tree node.

* @author 88250 <DL88250@gmail.com>, http://blog.csdn.net/DL88250 
* @version 1.0.0.5, Jun 7, 2008

*/

final public class TranspositionTable {

// XXX currently, using 32-bits hash code

/**

* transposition table's size

*/

final static int SIZE = 1024 * 32 * 8;

/**

* holds the chessboard condition<br>

* <ul>

* <li>15: 14 kinds of chessman, from 1 to 14</li>

* </li>9, 10: 9 * 10 matrics form chessboard

* </ul>

* @see cn.edu.ynu.sei.chinesechess.infrastructure.common.Constants

* @see cn.edu.ynu.sei.chinesechess.infrastructure.common.Situation#chessboard

*/

static int[][][] hashCodes = new int[15][9][10];

/**

* the holds, [0, 1] for min value and max value

*/

static HashNode[][] items = new HashNode[2][SIZE];

/**

* a 32-bits integer, acts for current hash code

*/

static int curHashCode;

/**

* Transposition table hit count

*/

public static int hashHitCount = 0;

/**

* singleton

*/

private static TranspositionTable instance = new TranspositionTable();

/**

* Gets the single instance.

* @return transposition table single instance

*/

public static TranspositionTable getInstance() {

if (instance == null) {

instance = new TranspositionTable();

}

return instance;

}

/**

* Initializes the hash code of the specified chessboard situation.

* @param chessboard the specified chessboard situation

*/

final static void initHashCode(int[][] chessboard) {

curHashCode = calcCurHashCode(chessboard);

}

/**

* Private default constructor.

*/

private TranspositionTable() {

Random random = new Random();

for (int i = 0; i < 15; i++) {

for (int j = 0; j < 9; j++) {

for (int k = 0; k < 10; k++) {

hashCodes[i][j][k] = Math.abs(random.nextInt());

}

}

}

for (int i = 0; i < 2; i++) {

for (int j = 0; j < SIZE; j++) {

items[i][j] = new HashNode();

}

}

}

/**

* Calculates the hash code for the specified chessboard situation.

* @param chessboard the specified chessboar situation

* @return 32-bits hash code

*/

final public static int calcCurHashCode(int[][] chessboard) {

int ret = 0;

for (int i = 0; i < 9; i++) {

for (int j = 0; j < 10; j++) {

ret ^= hashCodes[chessboard[i][j]][i][j];

}

}

return ret;

}

/**

* Save a chessboard situation into transposition table.

* @param type type of this hash item

* @param depth depth depth of search

* @param value value of this hash value

*/

final public static void save(NodeType type, int depth, int value) {

// depth % 2: 0 for max, 1 for min

HashNode item = items[depth % 2][curHashCode % SIZE];

item.depth = depth;

item.hashCode = curHashCode;

item.type = type;

item.value = value;

}

/**

* Lookup a chessboard situation in transposition table.

* @param depth depth of search

* @param alpha min value to max value, the "floor"

* @param beta  max value to min value, the "ceiling"

* @return if find the result, returns value, otherwise,

* returns <code>88250</code>

*/

final public static int lookup(int depth, int alpha, int beta) {

// depth % 2: 0 for max, 1 for min

HashNode item = items[depth % 2][curHashCode % SIZE];

if (item.depth == depth && item.hashCode == curHashCode) {

hashHitCount++;

switch (item.type) {

case exact:

return item.value;

case lowerBound:

if (item.value >= beta) {

return item.value;

} else {

break;

}

case upperBound:

if (item.value <= alpha) {

return item.value;

} else {

break;

}

}

}

// doesn't hit the target

return 88250;

}

/**

* Recovery the hash value of a motion had done.

* @param fromX x coordinate of which chessman do this move

* @param fromY y coordinate of which chessman do this move

* @param toX x coordinate of which chessman's destination

* @param toY y coordinate of which chessman's destination

* @param chessmanId the target position's chessman

* @param chessboard current chessboard situation

*/

final public static void unMoveHash(int fromX, int fromY, int toX, int toY,

int chessmanId, int[][] chessboard) {

int toId = chessboard[toX][toY];

// retrieves the random number before the motion done

curHashCode ^= hashCodes[toId][fromX][fromY];

// removes chessman which position is toId

curHashCode ^= hashCodes[toId][toX][toY];

if (chessmanId != 0) {

// recovery hash value chessman be eaten

curHashCode ^= hashCodes[chessmanId][toX][toY];

}

}

/**

* Generates the hash value of a motion on the current situation.

* @param fromX x coordinate of which chessman do this move

* @param fromY y coordinate of which chessman do this move

* @param toX x coordinate of which chessman's destination

* @param toY y coordinate of which chessman's destination

* @param chessboard current chessboard situation

*/

final public static void moveHash(int fromX, int fromY, int toX, int toY,

int[][] chessboard) {

int fromId, toId;

fromId = chessboard[fromX][fromY];

toId = chessboard[toX][toY];

// removes chessman which position is fromId

curHashCode ^= hashCodes[fromId][fromX][fromY];

if (toId != 0) {

// if toId position has a chessman, removes it

curHashCode ^= hashCodes[toId][toX][toY];

}

// retrieves the random number at toId

curHashCode ^= hashCodes[fromId][toX][toY];

}

/**

* Hash item type description.

*/

enum NodeType {

/**

* the hash item's value had evaluated

*/

exact,

/**

* the hash item's value is low bound

*/

lowerBound,

/**

* the hash item's value is upper bound

*/

upperBound,

/**

* the hash item's value is unknown

*/

unknown

};

/**

* Hash item description.

*/

final class HashNode {

/**

* 32-bits hash code

*/

int hashCode;

/**

* item's type

*/

NodeType type = NodeType.unknown;

/**

* search depth

*/

int depth;

/**

* item's value

*/

int value;

}

}


可以把代码贴到带Javadoc查看的IDE里看一下,那样比较清晰 : )
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: