zoukankan      html  css  js  c++  java
  • Java中国象棋博弈程序探秘[5]——搜索算法

    搜索算法

    转载请保留作者信息:

    作者:88250

    Bloghttp:/blog.csdn.net/DL88250

    MSN & Gmail & QQDL88250@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里看一下,那样比较清晰 : )

  • 相关阅读:
    spring jdbcTemplate使用queryForList示例
    凡事预则立,不立则废:好计划是成功的开始——布利斯定理
    传世智库:初识IPD-集成产品开发
    骑手送外卖获奖1500多万元又被撤销 法律专家:不能一扣了之
    leetcode-----129. 求根到叶子节点数字之和
    leetcode-----128. 最长连续序列
    leetcode-----127. 单词接龙
    leetcode-----126. 单词接龙 II
    leetcode-----125. 验证回文串
    leetcode-----124. 二叉树中的最大路径和
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6470221.html
Copyright © 2011-2022 走看看