dfpn.cc
Go to the documentation of this file.
00001 /* dfpn.cc
00002  */
00003 #include "osl/checkmate/dfpn.h"
00004 #include "osl/checkmate/dfpnParallel.h"
00005 #include "osl/checkmate/dfpnRecord.h"
00006 #include "osl/checkmate/immediateCheckmate.h"
00007 #include "osl/checkmate/fixedDepthSearcher.h"
00008 #include "osl/checkmate/fixedDepthSearcher.tcc"
00009 #include "osl/checkmate/libertyEstimator.h"
00010 #include "osl/checkmate/pieceCost.h"
00011 #include "osl/checkmate/disproofPieces.h"
00012 #include "osl/checkmate/oracleAdjust.h"
00013 #include "osl/checkmate/pawnCheckmateMoves.h"
00014 #include "osl/checkmate/proofTreeDepthDfpn.h"
00015 #include "osl/move_generator/escape_.h"
00016 #include "osl/move_generator/addEffectWithEffect.h"
00017 #include "osl/move_action/store.h"
00018 #include "osl/move_classifier/check_.h"
00019 #include "osl/move_classifier/moveAdaptor.h"
00020 #include "osl/move_classifier/pawnDropCheckmate.h"
00021 #include "osl/record/csa.h"
00022 #include "osl/container/moveVector.h"
00023 #ifdef USE_TBB_HASH
00024 #  include <cstring>
00025 #  include <tbb/concurrent_hash_map.h>
00026 #endif
00027 #include "osl/stl/hash_map.h"
00028 #include "osl/stl/vector.h"
00029 #include "osl/stl/slist.h"
00030 #include "osl/stat/ratio.h"
00031 #include "osl/hash/hashRandomPair.h"
00032 #include "osl/misc/align16New.h"
00033 #include "osl/oslConfig.h"
00034 #include <boost/tuple/tuple.hpp>
00035 #include <boost/tuple/tuple_comparison.hpp>
00036 #include <iostream>
00037 #include <iomanip>
00038 #include <bitset>
00039 
00040 // see dfpnRecord.h for #defined NAGAI_DAG_TEST
00041 
00042 #define GRAND_PARENT_SIMULATION
00043 #define GRAND_PARENT_DELAY
00044 
00045 #define INITIAL_DOMINANCE
00046 
00047 #define ROOT_PROOF_TOL 65536ul*1024
00048 
00049 #define ROOT_DISPROOF_TOL 65536ul*1024
00050 // #define DELAY_UPWARD
00051 // #define NO_IMMEDIATE_CHECKMATE
00052 #define CHECKMATE_D2
00053 // #define CHECKMATE_A3
00054 #define PROOF_AVERAGE
00055 #define DISPROOF_AVERAGE
00056 
00057 #define KAKINOKI_FALSE_BRANCH_SEARCH
00058 #define IGNORE_MONSTER_CHILD
00059 #define KISHIMOTO_WIDEN_THRESHOLD
00060 #define ADHOC_SUM_RESTRICTION
00061 #define AGGRESSIVE_FIND_DAG
00062 #define AGGRESSIVE_FIND_DAG2
00063 #define CHECKMATE_A3_GOLD
00064 #define CHECKMATE_A3_SIMULLATION
00065 
00066 // 何番目に生成された指手が解決済みかを記録。生成順序は駒番号に依存するので注意。
00067 #define MEMORIZE_SOLVED_IN_BITSET
00068 
00069 // #define DFPN_STAT
00070 
00071 static const int UpwardWeight = 2, SacrificeBlockCount = 0, LongDropCount = 1;
00072 #ifdef MINIMAL
00073 static const int MaxDagTraceDepth = 64;
00074 #else
00075 static const int MaxDagTraceDepth = 1600;
00076 #endif
00077 static const unsigned int NoPromoeIgnoreProofThreshold = 100;
00078 static const unsigned int NoPromoeIgnoreDisproofThreshold = 200;
00079 static const unsigned int IgnoreUpwardProofThreshold = 100;
00080 static const unsigned int IgnoreUpwardDisproofThreshold = 100;
00081 #ifdef MEMORIZE_SOLVED_IN_BITSET
00082 static const unsigned int InitialDominanceProofMax = 35;
00083 #else
00084 static const unsigned int InitialDominanceProofMax = 20;
00085 #endif
00086 static const unsigned int InitialDominanceDisproofMax = 110;
00087 static const unsigned int DagFindThreshold = 64;
00088 static const unsigned int DagFindThreshold2 = 256;
00089 static const int EnableGCDepth = 512;
00090 static const int AdHocSumScale = 128;
00091 static const size_t GrowthLimitInfty = std::numeric_limits<size_t>::max();
00092 const int ProofSimulationTolerance = 1024;
00093 
00094 // #define DFPN_DEBUG
00095 
00096 #ifndef NDEBUG
00097 static size_t timer = 0;
00098 const size_t debug_time_start = 3851080;
00099 #endif
00100 /* ------------------------------------------------------------------------- */
00101 
00102 namespace osl
00103 {
00104   namespace checkmate
00105   {
00106     inline int log2(uint32_t n) 
00107     {
00108       return (n <= 1) ? 1 : 32 - __builtin_clz(n);
00109     }
00110     inline int slow_increase(uint32_t n)
00111     {
00112       return log2(n);
00113     }
00114 #ifdef DFPN_DEBUG
00115     struct NodeIDTable : public hash_map<HashKey, int> 
00116     {
00117       size_t cur;
00118       NodeIDTable() : cur(0) {}
00119       int id(const HashKey& key) 
00120       {
00121         int& ret = (*this)[key];
00122         if (ret == 0)
00123           ret = ++cur;
00124         return ret;
00125       }
00126     } node_id_table;
00127     CArray<int,3> debug_node = {{
00128       }};
00130     struct NodeCountTable : public hash_map<int, std::pair<int,vector<Move> > >
00131     {
00132       typedef std::pair<int,vector<Move> > pair_t;
00133       ~NodeCountTable()
00134       {
00135         std::cerr << "timer " << timer << "\n";
00136         vector<std::pair<int,int> > all; 
00137         all.reserve(size());
00138         BOOST_FOREACH(const value_type& v, *this)
00139           all.push_back(std::make_pair(-v.second.first, v.first));
00140         std::sort(all.begin(), all.end());
00141         for (size_t i=0; i<std::min((size_t)10, size()); ++i){
00142           std::cerr << "freq " << -all[i].first << " id " << std::setw(5) << all[i].second << ' ';
00143           BOOST_FOREACH(Move m, (*this)[all[i].second].second)
00144             std::cerr << record::csa::show(m);
00145           std::cerr << "\n";
00146         }
00147       }
00148     } node_count_table;
00149 #endif
00150 
00151     struct SimpleTwinList : slist<PathEncoding
00152 #ifdef USE_BOOST_POOL_ALLOCATOR
00153                                   , osl::stl::fast_pool_allocator<PathEncoding>
00154 #endif
00155                                   >
00156     {
00157     };
00158 
00159     struct DfpnPathRecord
00160     {
00161       static const int MaxDistance = 1024*128;
00162       SimpleTwinList twin_list;
00164       int distance;
00165       bool visiting;
00166       size_t node_count;
00167       DfpnPathRecord() 
00168         : distance(MaxDistance), visiting(false), node_count(0)
00169       {
00170       }
00171     };
00172     template <bool Enabled=true>
00173     struct DfpnVisitLock : boost::noncopyable
00174     {
00175       DfpnPathRecord *record;
00176       DfpnVisitLock(DfpnPathRecord *r) : record(r)
00177       {
00178         if (! Enabled) return;
00179         assert(! record->visiting);
00180         record->visiting = true;
00181       }
00182       ~DfpnVisitLock()
00183       {
00184         if (! Enabled) return;
00185         assert(record->visiting);
00186         record->visiting = false;
00187       }
00188     };
00189     enum LoopToDominance { NoLoop=0, BadAttackLoop };
00190     struct DfpnPathList : public slist<std::pair<PieceStand, DfpnPathRecord>
00191 #ifdef USE_BOOST_POOL_ALLOCATOR
00192                  , osl::stl::fast_pool_allocator<std::pair<PieceStand,DfpnPathRecord> >
00193 #endif
00194                                        >
00195     {
00196       typedef slist<std::pair<PieceStand, DfpnPathRecord> > list_t;
00197     private:
00198       template <Player Attack>
00199       iterator find(PieceStand black, LoopToDominance& loop)
00200       {
00201         loop = NoLoop;
00202         iterator ret = end();
00203         for (iterator p=begin(); p!=end(); ++p) {
00204           if (p->first == black) {
00205             assert(p->second.distance != DfpnPathRecord::MaxDistance);
00206             ret = p;
00207             if (loop || p->second.visiting) break;
00208           } 
00209           if (! p->second.visiting)
00210             continue;
00211           if (p->first.isSuperiorOrEqualTo(black)) {
00212             if (Attack == BLACK) {
00213               loop = BadAttackLoop;
00214               if (ret != end()) break;
00215             } 
00216           } 
00217           else if (black.isSuperiorOrEqualTo(p->first)) {  
00218             if (Attack == WHITE) {
00219               loop = BadAttackLoop;
00220               if (ret != end()) break;
00221             }       
00222           }
00223         }
00224         return ret;
00225       }
00226     public:
00227       template <Player Attack>
00228       DfpnPathRecord *allocate(PieceStand black, int depth, LoopToDominance& loop,
00229                                size_t& size)
00230       {
00231         iterator ret = find<Attack>(black, loop);
00232         if (ret != end()) {
00233           ret->second.distance = std::min(depth, ret->second.distance);
00234           return &(ret->second);
00235         }
00236         ++size;
00237         push_front(std::make_pair(black, DfpnPathRecord()));
00238         DfpnPathRecord *record = &(begin()->second);
00239         assert(record->distance == DfpnPathRecord::MaxDistance);
00240         record->distance = depth;
00241         return record;
00242       }
00243       const DfpnPathRecord *probe(PieceStand black) const
00244       {
00245         BOOST_FOREACH(const value_type& v, *this) {
00246           if (v.first == black)
00247             return &(v.second);
00248         }
00249         return 0;
00250       }
00251       static bool precious(const DfpnPathRecord& record, size_t threshold)
00252       {
00253         return record.visiting 
00254           || record.node_count > threshold
00255           || (! record.twin_list.empty() && record.node_count > threshold - 10);
00256       }
00257       size_t runGC(size_t threshold)
00258       {
00259         size_t removed = 0;
00260         list_t::iterator p=begin();
00261         while (p!=end()) {
00262           list_t::iterator q=p;
00263           ++q;
00264           if (q == end())
00265             break;
00266           if (! precious(q->second, threshold)) {
00267             erase_after(p);
00268             ++removed;
00269             continue;
00270           }
00271           p = q;
00272         }
00273         if (! empty() && ! precious(begin()->second, threshold)) {
00274           erase(begin());
00275           ++removed;
00276         }
00277         return removed;
00278       }
00279     };
00280     class DfpnPathTable 
00281     {
00282       typedef hash_map<BoardKey, DfpnPathList
00283 #  ifdef USE_BOOST_POOL_ALLOCATOR
00284                    , osl::stl::hash<BoardKey>
00285                    , std::equal_to<BoardKey>
00286                    , osl::stl::fast_pool_allocator<std::pair<const BoardKey,DfpnPathList> >
00287 #  endif
00288                        > table_t;
00289       table_t table;
00290       size_t total_size;
00291       size_t gc_threshold;
00292     public:
00293       DfpnPathTable() : total_size(0), gc_threshold(10)
00294       {
00295       }
00296       template <Player Attack>
00297       DfpnPathRecord *allocate(const HashKey& key, int depth, LoopToDominance& loop) 
00298       {
00299         DfpnPathList& l = table[key.boardKey()];
00300         return l.allocate<Attack>(key.blackStand(), depth, loop,
00301                                   total_size);
00302       }
00303       const DfpnPathRecord *probe(const HashKey& key) const
00304       {
00305         table_t::const_iterator p = table.find(key.boardKey());
00306         if (p == table.end()) 
00307           return 0;
00308         return p->second.probe(key.blackStand());
00309       }
00310       void clear() { table.clear(); }
00311       size_t runGC()
00312       {
00313         size_t removed = 0;
00314         for (table_t::iterator p=table.begin(); p!=table.end(); ++p)
00315           removed += p->second.runGC(gc_threshold);
00316         total_size -= removed;
00317         gc_threshold += 15;
00318         static double memory_threshold = 0.8;
00319         double memory = OslConfig::memoryUseRatio();
00320         if (memory > memory_threshold) {
00321           gc_threshold += 15;
00322           memory_threshold += 1.0/128;
00323         }
00324         return removed;
00325       }
00326       size_t size() const { return total_size; }
00327       void rehash(size_t bucket_size) { table.rehash(bucket_size); }
00328     };
00329 
00330     int attackProofCost(Player attacker, const NumEffectState& state, Move move)
00331     {
00332       int proof = 0;
00333       if (! move.isCapture())
00334       {
00335         const Square from=move.from(), to=move.to();
00336         const int a = (state.countEffect(attacker,to) 
00337                        + (from.isPieceStand() ? 1 : 0));
00338         int d = state.countEffect(alt(attacker),to);
00339         if (a <= d)
00340         {
00341           const Ptype ptype = move.ptype();
00342           proof = PieceCost::attack_sacrifice_cost[ptype];
00343           if ((d >= 2) && (a == d))     // 追加利きとか利きがずれたりとか
00344             proof /= 2;
00345         }
00346       }
00347       return proof;
00348     }
00349   }
00350 }
00351 
00352 /* ------------------------------------------------------------------------- */
00353 struct osl::checkmate::Dfpn::NodeBase
00354 {
00355   // input
00356   HashKey hash_key;
00357   PathEncoding path;
00358   ProofDisproof threshold;
00359   Move moved;
00360   PieceStand white_stand;
00361   // work or output
00362   DfpnRecord record;
00363   DfpnPathRecord *path_record;
00364 };
00365 
00366 struct osl::checkmate::Dfpn::Node : NodeBase
00367 {
00368   DfpnMoveVector moves;
00369   FixedCapacityVector<DfpnRecord,DfpnMaxUniqMoves> children;
00370   FixedCapacityVector<const DfpnPathRecord*,DfpnMaxUniqMoves> children_path;
00371   CArray<HashKey,DfpnMaxUniqMoves> hashes;
00372   FixedCapacityVector<int8_t,DfpnMaxUniqMoves> proof_cost; // only attack
00373   size_t visit_time;
00374 
00375   const PieceStand nextWhiteStand(Player P, Move move) const
00376   {
00377     assert(move.player() == P);
00378     return (P == WHITE) ? white_stand.nextStand(P, move) : white_stand;
00379   }
00380   void clear()
00381   {
00382     moves.clear();
00383     proof_cost.clear();
00384     children.clear();
00385     children_path.clear();
00386   }
00387   void allocate(int n)
00388   {
00389     while (n--) {
00390       proof_cost.push_back(0);
00391       children.push_back(DfpnRecord());
00392       children_path.push_back(0);
00393     }
00394   }
00395   void setLoopDetection()
00396   {
00397     assert(! (record.proof_disproof.isFinal()
00398               && ! record.proof_disproof.isLoopDetection()));
00399     record.proof_disproof = ProofDisproof(1,1);
00400     path_record->twin_list.push_front(path);
00401   }
00402   const PathEncoding newPath(int c) const
00403   {
00404     PathEncoding n = path;
00405     n.pushMove(moves[c]);
00406     return n;
00407   }
00408   bool isLoop(int c) const
00409   {
00410     if (! children_path[c] || children[c].proof_disproof.isFinal())
00411       return false;
00412     if (children_path[c]->visiting)
00413       return true;
00414     const PathEncoding p = newPath(c);
00415     const SimpleTwinList& tl = children_path[c]->twin_list;
00416     return std::find(tl.begin(), tl.end(), p) != tl.end();
00417   }
00418   void setCheckmateAttack(Player attack, int best_i)
00419   {
00420     DfpnRecord& child = children[best_i];
00421     assert(child.proof_disproof.isCheckmateSuccess());
00422     record.proof_disproof = child.proof_disproof;
00423     record.best_move = moves[best_i];
00424     const PieceStand proof_pieces 
00425       = ProofPieces::attack(child.proofPieces(), record.best_move,
00426                             record.stands[attack]);
00427     record.setProofPieces(proof_pieces);
00428   }
00429   void setNoCheckmateDefense(Player attack, int best_i)
00430   {
00431     DfpnRecord& child = children[best_i];
00432     assert(child.proof_disproof.isCheckmateFail());
00433     assert(! child.proof_disproof.isLoopDetection());
00434     record.proof_disproof = child.proof_disproof;
00435     record.best_move = moves[best_i];
00436     const PieceStand disproof_pieces 
00437       = DisproofPieces::defense(child.disproofPieces(), record.best_move,
00438                                 record.stands[alt(attack)]);
00439     record.setDisproofPieces(disproof_pieces);
00440   }
00441   void setCheckmateDefense(Player attack, const NumEffectState& state) 
00442   {
00443     assert(moves.size());
00444     assert(record.proof_disproof.isCheckmateSuccess());
00445     record.proof_disproof = ProofDisproof::Checkmate(); // prevent backup of NoEscape
00446     PieceStand result = record.proof_pieces_candidate;
00447     const Player defender = alt(attack);
00448     if (! effect_util::UnblockableCheck::isMember(defender, state))
00449       ProofPiecesUtil::addMonopolizedPieces(state, attack, record.stands[attack],
00450                                             result);
00451     record.setProofPieces(result);
00452   }
00453   void setNoCheckmateAttack(Player attack, const NumEffectState& state)
00454   {
00455     assert(moves.size());
00456     assert(record.proof_disproof.isCheckmateFail());
00457     assert(! record.proof_disproof.isLoopDetection());
00458     PieceStand result = record.proof_pieces_candidate;
00459     ProofPiecesUtil::addMonopolizedPieces(state, alt(attack), record.stands[alt(attack)],
00460                                           result);
00461     record.setDisproofPieces(result);
00462   }
00463   void setCheckmateChildInDefense(size_t i) 
00464   {
00465     assert(children[i].proof_disproof.isCheckmateSuccess());
00466 #ifdef MEMORIZE_SOLVED_IN_BITSET
00467     record.solved |= (1ull<<i);
00468 #endif
00469     record.min_pdp = std::min(record.min_pdp, children[i].proof_disproof.disproof());
00470     record.proof_pieces_candidate
00471       = record.proof_pieces_candidate.max(children[i].proofPieces());
00472   }
00473   void setNoCheckmateChildInAttack(size_t i) 
00474   {
00475     assert(children[i].proof_disproof.isCheckmateFail());
00476 #ifdef MEMORIZE_SOLVED_IN_BITSET
00477     record.solved |= (1ull<<i);
00478 #endif
00479     record.min_pdp = std::min(record.min_pdp, children[i].proof_disproof.proof());
00480     record.proof_pieces_candidate
00481       = record.proof_pieces_candidate.max(children[i].disproofPieces());
00482   }
00483 };
00484 
00485 struct osl::checkmate::Dfpn::Tree
00486 #if OSL_WORDSIZE == 32
00487   : public misc::Align16New
00488 #endif
00489 {
00490   NumEffectState state;
00491   int depth;
00492 #ifdef MINIMAL
00493   enum { MinimalMaxDepth = 256 };
00494   Node node[MinimalMaxDepth];
00495 #else
00496   boost::scoped_array<Node> node;
00497 #endif
00498   const int MaxDepth;
00499   Tree(int
00500 #ifndef MINIMAL
00501        max_depth
00502 #endif
00503     ) : state(SimpleState(HIRATE)),
00504         MaxDepth(
00505 #ifndef MINIMAL
00506           max_depth
00507 #else
00508           MinimalMaxDepth
00509 #endif
00510           )
00511   {
00512 #ifndef MINIMAL
00513     node.reset(new Node[max_depth]);
00514 #endif
00515   }
00516   bool inCheck(Player P) const
00517   {
00518     return state.inCheck(P);
00519   }
00520   const Piece king(Player P) const { return state.kingPiece(P); }
00521   void newVisit(Player P, Move move, const HashKey& next_hash)
00522   {
00523     assert(P == move.player());
00524     const Node& node = this->node[depth];
00525     assert(next_hash == node.hash_key.newHashWithMove(move));
00526     Node& next = this->node[depth+1];
00527     next.moved = move;
00528     next.white_stand = node.nextWhiteStand(P, move);
00529     next.path = node.path;
00530     next.clear();
00531     next.hash_key = next_hash;
00532   }
00533   void setNoCheckmateChildInAttack(size_t best_i) 
00534   {
00535     Node &node = this->node[depth];
00536     node.setNoCheckmateChildInAttack(best_i);
00537   }
00538   void setNoCheckmateDefense(Player attack, int best_i)
00539   {
00540     Node &node = this->node[depth];
00541     node.setNoCheckmateDefense(attack, best_i);
00542   }
00543   void dump(int lines, int depth=0) const
00544   {
00545 #ifndef NDEBUG
00546     if (depth == 0)
00547       depth = this->depth;
00548     for (int i=0; i<=depth; ++i) {
00549       std::cerr << "history " << i << " " << node[i].moved << " ";
00550       node[i].hash_key.dumpContentsCerr();
00551       std::cerr << "\n";
00552     }
00553     const int my_distance = node[depth].path_record ? node[depth].path_record->distance : -1;
00554     const Node &node = this->node[depth];
00555     std::cerr << "time " << node.visit_time << " (" << timer << ") here " << lines << "\n" << state;
00556     std::cerr << " false-branch? " << (bool)node.record.false_branch << "\n";
00557 #ifdef MEMORIZE_SOLVED_IN_BITSET
00558     std::cerr << " solved " << std::bitset<32>(node.record.solved) << "\n";
00559 #endif
00560     std::cerr << " dags   " << std::bitset<32>(node.record.solved) << "\n";
00561     std::cerr << " last_to " << node.record.last_to
00562               << " threshold " << node.threshold
00563               << " my_distance " << my_distance << "\n";
00564     for (size_t i=0; i<node.moves.size(); ++i) {
00565       std::cerr << "  " << i << " " << node.moves[i]
00566                 << " " << node.children[i].proof_disproof
00567                 << " " << (int)node.proof_cost[i]
00568                 << " " << node.children[i].best_move
00569                 << " depth " << (node.children_path[i] ? node.children_path[i]->distance : -1)
00570                 << " count " << node.children[i].node_count
00571                 << "\n";
00572     }
00573     std::cerr << node.record.proof_disproof << " " << node.record.best_move << "\n";
00574     std::cerr << "path " << node.path << " twins ";
00575     if (node.path_record) {
00576       BOOST_FOREACH(const PathEncoding& path, node.path_record->twin_list)
00577         std::cerr << path << " ";
00578     }
00579     std::cerr << "\n";
00580 #endif
00581   }
00582 #ifdef DFPN_DEBUG
00583   void showPath(const char *message, size_t table_size) const
00584   {
00585     std::cerr << message << " depth " << depth << " node " << node_id_table.id(node[depth].hash_key)
00586               << " time " << timer << " table " << table_size << ' ';
00587     for (int i=0; i<=depth; ++i)
00588       std::cerr << record::csa::show(node[i].moved);
00589     std::cerr << "\n";
00590   }
00591   struct Logging
00592   {
00593     const Tree *tree;
00594     const DfpnTable *table;
00595     const size_t old_table_size;
00596     Logging(Tree *tr, DfpnTable *tb, const char *message)
00597       : tree(tr), table(tb), old_table_size(table->size())
00598     {
00599       if (timer < debug_time_start)
00600         return;
00601       tree->showPath(message, old_table_size);
00602     }
00603     ~Logging()
00604     {
00605       if (timer < debug_time_start)
00606         return;
00607       const Node& node = tree->node[tree->depth];
00608       const int id = node_id_table.id(node.hash_key);
00609       std::cerr << " node " << id << " " << node.threshold
00610                 << " " << node.record.proof_disproof << "\n";
00611       if (std::find(debug_node.begin(), debug_node.end(), id)
00612           != debug_node.end() && timer > debug_time_start)
00613         tree->dump(__LINE__);
00614       if (table->size() == old_table_size)
00615         countImmediateReturns(id);
00616     }
00617     void countImmediateReturns(int id)
00618     {
00619       NodeCountTable::pair_t& p = node_count_table[id];
00620       if (p.first == 0) {
00621         for (int i=0; i<=tree->depth; ++i)
00622           p.second.push_back(tree->node[i].moved);
00623       }
00624       ++(p.first);
00625     }
00626   };
00627 #endif
00628 };
00629 
00630 /* ------------------------------------------------------------------------- */
00631 #ifdef DFPN_STAT
00632 osl::CArray<osl::CArray<int,64>,2> count2proof, count2disproof, count2unknown;
00633 #endif
00634 
00635 struct osl::checkmate::DfpnTable::List : public slist<DfpnRecord
00636 #ifdef USE_BOOST_POOL_ALLOCATOR
00637                                                       , osl::stl::fast_pool_allocator<DfpnRecord>
00638 #endif
00639                                                       >
00640 {
00641   typedef slist<DfpnRecord
00642 #ifdef USE_BOOST_POOL_ALLOCATOR
00643                 , osl::stl::fast_pool_allocator<DfpnRecord>
00644 #endif
00645                 > list_t;
00646 #ifdef OSL_DFPN_SMP
00647   mutable Mutex mutex;
00648 #endif
00649   List() {}
00650   List(const List& src) : list_t(src) {}
00651 
00652   template <Player Attack>
00653   const DfpnRecord probe(const HashKey& key, PieceStand white_stand) const;
00654   template <Player Attack>
00655   const DfpnRecord findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const;
00656   template <Player Attack>
00657   void showProofOracles(const HashKey& key, PieceStand white_stand, Move last_move) const;
00658   bool store(DfpnRecord& value, int leaving_thread_id)
00659   {
00660 #ifdef USE_TBB_HASH
00661     SCOPED_LOCK(lk,mutex);
00662 #endif
00663     BOOST_FOREACH(DfpnRecord& record, *this) {
00664       if (record.stands[BLACK] == value.stands[BLACK]) {
00665 #ifdef OSL_DFPN_SMP
00666         if (record.proof_disproof.isFinal()) {
00667           value = record;
00668           record.working_threads &= ~(1u << leaving_thread_id);
00669           return false;
00670         }
00671         if (! value.proof_disproof.isFinal()) {
00672           value.min_pdp = std::min(value.min_pdp, record.min_pdp);
00673           value.proof_pieces_candidate 
00674             = value.proof_pieces_candidate.max(record.proof_pieces_candidate);
00675           value.dag_moves |= record.dag_moves;
00676           value.solved |= record.solved;
00677           value.false_branch |= record.false_branch;
00678         }
00679         value.working_threads = record.working_threads;
00680         if (leaving_thread_id >= 0) {
00681           assert(value.working_threads & (1u << leaving_thread_id));
00682           value.working_threads &= ~(1u << leaving_thread_id);
00683         }
00684 #endif
00685         record = value;
00686         return false;
00687       }
00688     }
00689     value.working_threads &= ~(1u << leaving_thread_id);
00690     push_front(value);
00691     return true;
00692   }
00693   void addDag(DfpnRecord& value)
00694   {
00695 #ifdef USE_TBB_HASH
00696     SCOPED_LOCK(lk,mutex);
00697 #endif
00698     BOOST_FOREACH(DfpnRecord& record, *this) {
00699       if (record.stands[BLACK] == value.stands[BLACK]) {
00700 #ifdef OSL_DFPN_SMP
00701         value.min_pdp = std::min(value.min_pdp, record.min_pdp);
00702         value.proof_pieces_candidate
00703           = value.proof_pieces_candidate.max(record.proof_pieces_candidate);
00704         value.dag_moves |= record.dag_moves;
00705         value.solved |= record.solved;
00706         value.false_branch |= record.false_branch;
00707         value.working_threads = record.working_threads;
00708 #endif
00709         record.dag_moves = value.dag_moves;
00710         return;
00711       }
00712     }
00713   }
00714   bool setWorking(const DfpnRecord& value, int thread_id)
00715   {
00716 #ifdef USE_TBB_HASH
00717     SCOPED_LOCK(lk,mutex);
00718 #endif
00719     BOOST_FOREACH(DfpnRecord& record, *this) {
00720       if (record.stands[BLACK] == value.stands[BLACK]) {
00721         assert(! (value.working_threads & (1u << thread_id)));
00722         record.working_threads |= 1u << thread_id;
00723         return false;
00724       }
00725     }
00726     push_front(value);
00727     front().working_threads |= 1u << thread_id;
00728     return true;
00729   }
00730   void leaveWorking(PieceStand black, int thread_id)
00731   {
00732 #ifdef USE_TBB_HASH
00733     SCOPED_LOCK(lk,mutex);
00734 #endif
00735     BOOST_FOREACH(DfpnRecord& record, *this) {
00736       if (record.stands[BLACK] == black) {
00737         // assert(p->working_threads & (1u << thread_id)); // fail when stop_all
00738         record.working_threads &= ~(1u << thread_id);
00739         return;
00740       }
00741     }
00742     // assert(0); // fail when stop_all
00743   }
00744   void testTable(const BoardKey& /*key*/) const
00745   {
00746 #ifdef USE_TBB_HASH
00747     SCOPED_LOCK(lk,mutex);
00748 #endif
00749     BOOST_FOREACH(const DfpnRecord& record, *this) {
00750       if (record.working_threads)
00751         std::cerr << std::bitset<16>(record.working_threads) << "\n";
00752       assert(record.working_threads == 0);
00753 #ifdef DFPN_STAT
00754       const int count = misc::BitOp::countBit(record.solved);
00755       if (record.proof_disproof.isCheckmateSuccess())
00756         count2proof[key.turn()][count]++;
00757       else if (record.proof_disproof.isCheckmateFail())
00758         count2disproof[key.turn()][count]++;
00759       else
00760         count2unknown[key.turn()][count]++;
00761 #endif
00762     }
00763   }
00764   size_t smallTreeGC(size_t threshold)
00765   {
00766     size_t removed = 0;
00767 #ifdef USE_TBB_HASH
00768     SCOPED_LOCK(lk,mutex);
00769 #endif
00770     list_t::iterator p=begin();
00771     while (p!=end()) {
00772       list_t::iterator q=p;
00773       ++q;
00774       if (q == end())
00775         break;
00776       if (! q->proof_disproof.isFinal()
00777 #ifdef OSL_DFPN_SMP
00778           && q->working_threads == 0
00779 #endif
00780           && q->node_count < threshold) {
00781         erase_after(p);
00782         ++removed;
00783         continue;
00784       }
00785       p = q;
00786     }
00787     if (! empty() && ! begin()->proof_disproof.isFinal()
00788 #ifdef OSL_DFPN_SMP
00789         && begin()->working_threads == 0
00790 #endif
00791         && begin()->node_count < threshold) {
00792       erase(begin());
00793       ++removed;
00794     }
00795     return removed;
00796   }
00797   size_t estimateNodeCount(const HashKey& key, bool dominance_max) const;
00798 };
00799 template <osl::Player A>
00800 const osl::checkmate::DfpnRecord osl::checkmate::DfpnTable::
00801 List::probe(const HashKey& key, PieceStand white_stand) const
00802 {
00803 #ifdef USE_TBB_HASH
00804     SCOPED_LOCK(lk,mutex);
00805 #endif
00806   DfpnRecord result(key.blackStand(), white_stand);
00807   const PieceStand attack_stand = (A == BLACK) ? key.blackStand() : white_stand;
00808   const PieceStand defense_stand = (A == BLACK) ? white_stand : key.blackStand();
00809 #ifdef INITIAL_DOMINANCE
00810   unsigned int proof_ll = 1, disproof_ll = 1;
00811 #endif
00812   BOOST_FOREACH(const DfpnRecord& record, *this) {
00813     if (record.stands[BLACK] == key.blackStand()) {
00814       result = record;
00815       if (result.proof_disproof.isFinal())
00816         break;
00817       continue;
00818     }
00819     if (record.proof_disproof.isCheckmateSuccess()) {
00820       if (attack_stand.isSuperiorOrEqualTo(record.proofPieces())) {
00821         result.setFrom(record);
00822         break;
00823       }
00824     }
00825     else if (record.proof_disproof.isCheckmateFail()) {
00826       if (defense_stand.isSuperiorOrEqualTo(record.disproofPieces())) {
00827         result.setFrom(record);
00828         break;
00829       }
00830     }
00831 #ifdef INITIAL_DOMINANCE
00832     else {
00833       if (record.stands[A].isSuperiorOrEqualTo(attack_stand)) {
00834         proof_ll = std::max(proof_ll, record.proof());
00835       }
00836       else if (attack_stand.isSuperiorOrEqualTo(record.stands[A])) {
00837         disproof_ll = std::max(disproof_ll, record.disproof());
00838       }
00839     }
00840 #endif
00841   }
00842 #ifdef INITIAL_DOMINANCE
00843   if (result.proof_disproof == ProofDisproof(1,1)) {
00844       result.proof_disproof = ProofDisproof(std::min(proof_ll, InitialDominanceProofMax), 
00845                                             std::min(disproof_ll, InitialDominanceDisproofMax));
00846       result.node_count++;      // not suitable for proof_average
00847   }
00848 #endif
00849   return result;
00850 }
00851 
00852 size_t osl::checkmate::DfpnTable::
00853 List::estimateNodeCount(const HashKey& key, bool dominance_max) const
00854 {
00855 #ifdef USE_TBB_HASH
00856   SCOPED_LOCK(lk,mutex);
00857 #endif
00858   size_t node_count = 0, exact = 0;
00859   BOOST_FOREACH(const DfpnRecord& record, *this) {
00860     if (node_count < record.node_count)
00861       node_count = record.node_count;
00862     if (key.blackStand() == record.stands[BLACK])
00863       exact = record.node_count;
00864   }
00865   return dominance_max ? node_count : exact;
00866 }
00867 
00868 template <osl::Player A>
00869 const osl::checkmate::DfpnRecord osl::checkmate::DfpnTable::
00870 List::findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const
00871 {
00872 #ifdef USE_TBB_HASH
00873   SCOPED_LOCK(lk,mutex);
00874 #endif
00875   const PieceStand attack_stand = (A == BLACK) ? key.blackStand() : white_stand;
00876   DfpnRecord result(key.blackStand(), white_stand);
00877   BOOST_FOREACH(const DfpnRecord& record, *this) {
00878     if (! record.proof_disproof.isCheckmateSuccess()) 
00879       continue;
00880     if (attack_stand.isSuperiorOrEqualTo(record.proofPieces())) {
00881       result.setFrom(record);
00882       ++record.node_count;
00883       if (record.last_move == last_move)
00884         break;
00885     }
00886   }
00887   return result;
00888 }
00889 
00890 #ifndef MINIMAL
00891 template <osl::Player A>
00892 void osl::checkmate::DfpnTable::
00893 List::showProofOracles(const HashKey& key, PieceStand white_stand, Move last_move) const
00894 {
00895   std::cerr << "search proof oracles after " << last_move << " size " << size() << "\n";
00896 #ifdef USE_TBB_HASH
00897   SCOPED_LOCK(lk,mutex);
00898 #endif
00899   const PieceStand attack_stand = (A == BLACK) ? key.blackStand() : white_stand;
00900   BOOST_FOREACH(const DfpnRecord& record, *this) {
00901     if (! record.proof_disproof.isCheckmateSuccess()) 
00902       continue;
00903     if (attack_stand.isSuperiorOrEqualTo(record.proofPieces())) {
00904       std::cerr << record.last_move << " " << record.best_move << " " << record.node_count << " " << record.proofPieces()
00905                 << " " << record.stands[BLACK] << " " << record.stands[WHITE] << "\n";
00906     }
00907   }
00908 }
00909 #endif
00910 
00911 #ifdef USE_TBB_HASH
00912 struct osl::checkmate::DfpnTable::Table : public tbb::concurrent_hash_map<BoardKey, List, TBBSignatureCompare> 
00913 {
00914   Player attack;
00915   explicit Table(Player a=BLACK) : attack(a) {}
00916 };
00917 #else
00918 struct osl::checkmate::DfpnTable::Table : public hash_map
00919 <BoardKey, List
00920 #  ifdef USE_BOOST_POOL_ALLOCATOR
00921  , osl::stl::hash<BoardKey>
00922  , std::equal_to<BoardKey>
00923  , osl::stl::fast_pool_allocator<std::pair<const BoardKey,List> >
00924 #  endif
00925 > 
00926 {
00927   Player attack;
00928   explicit Table(Player a=BLACK) : attack(a) {}
00929 };
00930 #endif
00931 
00932 osl::checkmate::
00933 DfpnTable::DfpnTable(Player attack) 
00934   : table(new Table[DIVSIZE]), total_size(0), dfpn_max_depth(0),
00935     growth_limit(GrowthLimitInfty), 
00936     gc_threshold(10)    
00937 {
00938   setAttack(attack);
00939 }
00940 
00941 osl::checkmate::
00942 DfpnTable::DfpnTable() 
00943   : table(new Table[DIVSIZE]), total_size(0), dfpn_max_depth(0)
00944 {
00945 }
00946 osl::checkmate::
00947 DfpnTable::~DfpnTable()
00948 {
00949 }
00950 
00951 void osl::checkmate::
00952 DfpnTable::setGrowthLimit(size_t new_limit)
00953 {
00954   growth_limit = new_limit; 
00955   for (int i=0; i<DIVSIZE; ++i)
00956     table[i].rehash(new_limit/DIVSIZE+new_limit/DIVSIZE/128+1);
00957 }
00958 
00959 void osl::checkmate::
00960 DfpnTable::showStats() const
00961 {
00962   if (size()) {
00963     std::cerr << "total " << total_size << "\n";
00964     for (int i=0; i<DIVSIZE; ++i)
00965       std::cerr << "DfpnTable " << i << " " << table[i].size() << "\n";
00966   }
00967 }
00968 
00969 void osl::checkmate::
00970 DfpnTable::setMaxDepth(int new_depth)
00971 {
00972   dfpn_max_depth = new_depth;
00973 }
00974 int osl::checkmate::
00975 DfpnTable::maxDepth() const
00976 {
00977   return dfpn_max_depth;
00978 }
00979 
00980 void osl::checkmate::
00981 DfpnTable::setAttack(Player a) 
00982 {
00983   assert(size() == 0);
00984   for (int i=0; i<DIVSIZE; ++i)
00985     table[i].attack = a;
00986 }
00987 
00988 osl::Player osl::checkmate::
00989 DfpnTable::attack() const
00990 {
00991   return table[0].attack;
00992 }
00993 
00994 template <osl::Player Attack>
00995 osl::checkmate::DfpnTable::List *
00996 osl::checkmate::
00997 DfpnTable::find(const HashKey& key, int subindex)
00998 {
00999   assert(table[subindex].attack == Attack);
01000 #ifdef USE_TBB_HASH
01001   Table::accessor it;
01002   if(!table[subindex].find(it,key.boardKey()))
01003     return 0;
01004   return &it->second;
01005 #else
01006   Table::iterator p = table[subindex].find(key.boardKey());
01007   if (p == table[subindex].end())
01008     return 0;
01009   return &p->second;
01010 #endif
01011 }
01012 
01013 template <osl::Player Attack>
01014 const osl::checkmate::DfpnTable::List *
01015 osl::checkmate::
01016 DfpnTable::find(const HashKey& key, int subindex) const
01017 {
01018   assert(table[subindex].attack == Attack);
01019   return find(key, subindex);
01020 }
01021 
01022 const osl::checkmate::DfpnTable::List *
01023 osl::checkmate::
01024 DfpnTable::find(const HashKey& key, int subindex) const
01025 {
01026 #ifdef USE_TBB_HASH
01027   Table::accessor it;
01028   if(!table[subindex].find(it,key.boardKey()))
01029     return 0;
01030   return &it->second;
01031 #else
01032   Table::const_iterator p = table[subindex].find(key.boardKey());
01033   if (p == table[subindex].end())
01034     return 0;
01035   return &p->second;
01036 #endif
01037 }
01038 
01039 template <osl::Player Attack>
01040 const osl::checkmate::DfpnRecord osl::checkmate::
01041 DfpnTable::probe(const HashKey& key, PieceStand white_stand) const
01042 {
01043   const int i=keyToIndex(key);
01044 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01045   SCOPED_LOCK(lk,mutex[i]);
01046 #endif
01047   const List *l = find<Attack>(key, i);
01048   if (l == 0) 
01049     return DfpnRecord(key.blackStand(), white_stand);
01050   return l->probe<Attack>(key, white_stand);
01051 }
01052 
01053 const osl::checkmate::DfpnRecord osl::checkmate::
01054 DfpnTable::probe(const HashKey& key, PieceStand white_stand) const
01055 {
01056   if (table[0].attack == BLACK)
01057     return probe<BLACK>(key, white_stand);
01058   else
01059     return probe<WHITE>(key, white_stand);
01060 }
01061 template <osl::Player Attack>
01062 const osl::checkmate::DfpnRecord osl::checkmate::
01063 DfpnTable::findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const
01064 {
01065   const int i=keyToIndex(key);
01066 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01067   SCOPED_LOCK(lk,mutex[i]);
01068 #endif
01069   const List *l = find<Attack>(key, i);
01070   if (l == 0)
01071     return DfpnRecord(key.blackStand(), white_stand);
01072   return l->findProofOracle<Attack>(key, white_stand, last_move);
01073 }
01074 const osl::checkmate::DfpnRecord osl::checkmate::
01075 DfpnTable::findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const
01076 {
01077   if (table[0].attack == BLACK)
01078     return findProofOracle<BLACK>(key, white_stand, last_move);
01079   else
01080     return findProofOracle<WHITE>(key, white_stand, last_move);
01081 }
01082 
01083 #ifndef MINIMAL
01084 template <osl::Player Attack>
01085 void osl::checkmate::
01086 DfpnTable::showProofOracles(const HashKey& key, PieceStand white_stand, Move last_move) const
01087 {
01088   const int i=keyToIndex(key);
01089 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01090   SCOPED_LOCK(lk,mutex[i]);
01091 #endif
01092   const List *l = find<Attack>(key, i);
01093   if (l == 0) 
01094     return;
01095   return l->showProofOracles<Attack>(key, white_stand, last_move);
01096 }
01097 #endif
01098 
01099 size_t osl::checkmate::
01100 DfpnTable::estimateNodeCount(const HashKey& key, bool dominance_max) const
01101 {
01102   const int i=keyToIndex(key);
01103 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01104   SCOPED_LOCK(lk,mutex[i]);
01105 #endif
01106   const List *l = find(key, i);
01107   if (l == 0) 
01108     return 0;
01109   return l->estimateNodeCount(key, dominance_max);
01110 }
01111 
01112 void osl::checkmate::
01113 DfpnTable::store(const HashKey& key, DfpnRecord& value, int leaving_thread_id)
01114 {
01115   assert(key.blackStand() == value.stands[BLACK]);
01116   assert(! value.proof_disproof.isLoopDetection());
01117   const int i=keyToIndex(key);
01118 #ifdef USE_TBB_HASH
01119   Table::accessor it;
01120   table[i].insert(it,key.boardKey());
01121   List& l = it->second;
01122 #else
01123 #  ifdef OSL_DFPN_SMP
01124   SCOPED_LOCK(lk,mutex[i]);
01125 #  endif
01126   List& l = table[i][key.boardKey()];
01127 #endif
01128   if (l.store(value, leaving_thread_id)) {
01129 #ifdef OSL_USE_RACE_DETECTOR
01130     SCOPED_LOCK(lk,root_mutex);
01131     // __sync_fetch_and_add() ?
01132 #endif
01133     total_size += 1;
01134   }
01135 }
01136 void osl::checkmate::
01137 DfpnTable::addDag(const HashKey& key, DfpnRecord& value)
01138 {
01139   assert(key.blackStand() == value.stands[BLACK]);
01140   assert(! value.proof_disproof.isLoopDetection());
01141   const int i=keyToIndex(key);
01142 #ifdef USE_TBB_HASH
01143   Table::accessor it;
01144   table[i].insert(it,key.boardKey());
01145   List& l = it->second;
01146 #else
01147 #  ifdef OSL_DFPN_SMP
01148   SCOPED_LOCK(lk,mutex[i]);
01149 #  endif
01150   List& l = table[i][key.boardKey()];
01151 #endif
01152   l.addDag(value);
01153 }
01154 
01155 void osl::checkmate::
01156 DfpnTable::setWorking(const HashKey& key, const DfpnRecord& value, int thread_id)
01157 {
01158   assert(key.blackStand() == value.stands[BLACK]);
01159   const int i=keyToIndex(key);
01160 #ifdef USE_TBB_HASH
01161   Table::accessor it;
01162   table[i].insert(it,key.boardKey());
01163   List& l = it->second;
01164 #else
01165 #  ifdef OSL_DFPN_SMP
01166   SCOPED_LOCK(lk,mutex[i]);
01167 #  endif
01168   List& l = table[i][key.boardKey()];
01169 #endif
01170   if (l.setWorking(value, thread_id)) {
01171 #ifdef OSL_USE_RACE_DETECTOR
01172     SCOPED_LOCK(lk,root_mutex);
01173     // __sync_fetch_and_add() ?
01174 #endif
01175     total_size += 1;
01176   }
01177 }
01178 void osl::checkmate::
01179 DfpnTable::leaveWorking(const HashKey& key, int thread_id)
01180 {
01181   const int i=keyToIndex(key);
01182 #ifdef USE_TBB_HASH
01183   Table::accessor it;
01184   table[i].insert(it,key.boardKey());
01185   List& l = it->second;
01186 #else
01187 #  ifdef OSL_DFPN_SMP
01188   SCOPED_LOCK(lk,mutex[i]);
01189 #  endif
01190   List& l = table[i][key.boardKey()];
01191 #endif
01192   l.leaveWorking(key.blackStand(), thread_id);
01193 }
01194 
01195 void osl::checkmate::
01196 DfpnTable::clear()
01197 {
01198   total_size = 0;
01199   for (int i=0; i<DIVSIZE; ++i) {
01200 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01201     SCOPED_LOCK(lk,mutex[i]);
01202 #endif
01203     table[i].clear();
01204   }
01205 }
01206 
01207 void osl::checkmate::
01208 DfpnTable::testTable()
01209 {
01210   for (int i=0; i<DIVSIZE; ++i) {
01211 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01212     SCOPED_LOCK(lk,mutex[i]);
01213 #endif
01214     BOOST_FOREACH(Table::value_type& v, table[i])
01215       v.second.testTable(v.first);
01216   }
01217 #ifdef DFPN_STAT
01218   for (int i=0; i<16; ++i) {
01219     for (int j=0; j<2; ++j)
01220       std::cout << std::setw(9) << count2proof[j][i]
01221                 << std::setw(9) << count2disproof[j][i]
01222                 << std::setw(9) << count2unknown[j][i]
01223                 << "   ";
01224     std::cout << "\n";
01225   }
01226 #endif
01227 }
01228 
01229 size_t osl::checkmate::
01230 DfpnTable::smallTreeGC(size_t threshold)
01231 {
01232   size_t removed = 0;
01233   for (int i=0; i<DIVSIZE; ++i) {
01234 #if (defined OSL_DFPN_SMP) && (! defined USE_TBB_HASH)
01235     SCOPED_LOCK(lk,mutex[i]);
01236 #endif
01237     Table::iterator p=table[i].begin();
01238     while (p!=table[i].end()) {
01239       removed += p->second.smallTreeGC(threshold);
01240       Table::iterator q = p;
01241       ++p;
01242       if (q->second.empty()) {
01243 #ifdef USE_TBB_HASH
01244         table[i].erase(q->first);
01245 #else
01246         table[i].erase(q);
01247 #endif
01248       }
01249     }
01250   }
01251   total_size -= removed;
01252   return removed;
01253 }
01254 
01255 bool osl::checkmate::
01256 DfpnTable::runGC()
01257 {
01258   const size_t before = total_size;
01259   if (! (before >= growth_limit || (growth_limit - before) < growth_limit/8))
01260     return false;
01261 
01262   std::cerr << "run GC " << before << ' ' << gc_threshold << "\n";
01263   const size_t removed = smallTreeGC(gc_threshold);
01264   double memory = OslConfig::memoryUseRatio();
01265   std::cerr << " GC " << removed
01266             << " entries "
01267             << "collected " << std::setprecision(3)
01268             << ((sizeof(HashKey)+sizeof(DfpnRecord)+sizeof(char*)*2)
01269                 * removed / (1<<20)) << "MB "
01270             << 100.0*removed/before << "%"
01271             << " real " << memory*100 << "%"
01272     // << " (" << elapsed << " s)"
01273             << "\n";
01274   gc_threshold += 15;
01275   static double memory_limit = 0.75;
01276   if (memory > memory_limit) {
01277     growth_limit -= growth_limit/8;
01278     gc_threshold += 15 + gc_threshold/4;
01279     memory_limit += 0.01;
01280   }
01281   if (removed < before*2/3)
01282     gc_threshold += 15 + gc_threshold/2;
01283   if ((removed < before*3/5 && memory > 0.75) || removed < before/2)
01284     throw Dfpn::DepthLimitReached();
01285   return true;
01286 }
01287 
01288 
01289 size_t osl::checkmate::
01290 DfpnTable::size() const
01291 {
01292   return total_size;
01293 }
01294 
01295 /* ------------------------------------------------------------------------- */
01296 
01297 template <osl::Player P>
01298 struct osl::checkmate::Dfpn::CallAttack
01299 {
01300   Dfpn *search;
01301   CallAttack(Dfpn *s) : search(s)
01302   {
01303   }
01304   void operator()(Square) const 
01305   {
01306     search->attack<P>();
01307   }
01308 };
01309 
01310 template <osl::Player P>
01311 struct osl::checkmate::Dfpn::CallDefense
01312 {
01313   Dfpn *search;
01314   CallDefense(Dfpn *s) : search(s)
01315   {
01316   }
01317   void operator()(Square) const 
01318   {
01319     search->defense<P>();
01320   }
01321 };
01322 
01323 /* ------------------------------------------------------------------------- */
01324 
01325 
01326 osl::checkmate::Dfpn::Dfpn()
01327   : table(0), tree(new Tree(OslConfig::dfpnMaxDepth())), path_table(new DfpnPathTable), parallel_shared(0),
01328     thread_id(-1), blocking_verify(true)
01329 {
01330 }
01331 osl::checkmate::Dfpn::~Dfpn()
01332 {
01333 }
01334 
01335 void osl::checkmate::Dfpn::setTable(DfpnTable *new_table)
01336 {
01337   table = new_table; 
01338   table->setMaxDepth(tree->MaxDepth);
01339   if (tree->MaxDepth > EnableGCDepth
01340       && table->growthLimit() < GrowthLimitInfty)
01341     path_table->rehash(parallel_shared ? table->growthLimit()/4 : table->growthLimit());
01342 }
01343 
01344 void osl::checkmate::Dfpn::clear()
01345 {
01346   path_table->clear();
01347 }
01348 
01349 
01350 void osl::checkmate::Dfpn::setIllegal(const HashKey& key, PieceStand white_stand)
01351 {
01352   // path_table はDualDfpnでクリアされるのでこちらは現状ではおまじない
01353   LoopToDominance dummy;
01354   DfpnPathRecord *record = (table->attack() == BLACK)
01355     ? path_table->allocate<BLACK>(key, 0, dummy)
01356     : path_table->allocate<WHITE>(key, 0, dummy);
01357   record->visiting = true;
01358 
01359   // こちらが重要
01360   DfpnRecord result(key.blackStand(), white_stand);
01361   result.proof_disproof = ProofDisproof::NoCheckmate();
01362   result.setDisproofPieces((table->attack() == WHITE) ? key.blackStand() : white_stand);
01363   table->store(key, result);
01364 }
01365 
01366 const osl::checkmate::ProofDisproof 
01367 osl::checkmate::
01368 Dfpn::hasCheckmateMove(const NumEffectState& state, const HashKey& key,
01369                        const PathEncoding& path, size_t limit, Move& best_move, Move last_move,
01370                        vector<Move> *pv)
01371 {
01372   PieceStand dummy;
01373   return hasCheckmateMove(state, key, path, limit, best_move, dummy, last_move, pv);
01374 }
01375 
01376 const osl::checkmate::ProofDisproof 
01377 osl::checkmate::
01378 Dfpn::hasCheckmateMove(const NumEffectState& state, const HashKey& key,
01379                        const PathEncoding& path, size_t limit, Move& best_move, PieceStand& proof_pieces,
01380                        Move last_move, vector<Move> *pv)
01381 {
01382   assert(table);
01383   if (! table)
01384     return ProofDisproof();
01385   path_table->clear();
01386   
01387   node_count = 0;
01388   node_count_limit = limit;
01389 
01390   Node& root = tree->node[0];
01391   try {
01392     tree->state.copyFrom(state);
01393     tree->depth = 0;
01394     root.hash_key = key;
01395     root.path = path;
01396     root.clear();
01397     root.threshold = ProofDisproof(ROOT_PROOF_TOL, ROOT_DISPROOF_TOL);
01398     root.white_stand = PieceStand(WHITE, state);
01399     root.moved = last_move;
01400     if (state.turn() == BLACK)
01401       attack<BLACK>();
01402     else
01403       attack<WHITE>();
01404   }
01405   catch (DepthLimitReached&) {
01406     for (int i=0; i<=tree->depth; ++i)
01407       table->leaveWorking(tree->node[i].hash_key, thread_id);
01408     return ProofDisproof();
01409   }
01410   if (root.path_record
01411       && (std::find(root.path_record->twin_list.begin(), root.path_record->twin_list.end(), path)
01412           != root.path_record->twin_list.end())) {
01413     if (parallel_shared)
01414       parallel_shared->stop_all = true;
01415     return ProofDisproof::LoopDetection();
01416   }
01417   if (parallel_shared && root.record.proof_disproof.isFinal())
01418     parallel_shared->stop_all = true;
01419   best_move = root.record.best_move;
01420   if (root.record.proof_disproof.isCheckmateSuccess())
01421     proof_pieces = root.record.proofPieces();
01422   // retrieve pv
01423   if (pv && root.record.proof_disproof.isCheckmateSuccess()) {
01424     ProofTreeDepthDfpn analyzer(*table);
01425     analyzer.retrievePV(state, true, *pv);
01426   }
01427   return root.record.proof_disproof;
01428 }
01429 
01430 const osl::checkmate::ProofDisproof 
01431 osl::checkmate::
01432 Dfpn::tryProof(const NumEffectState& state, const HashKey& key,
01433                const PathEncoding& path, const ProofOracle& oracle, size_t oracle_id, Move& best_move,
01434                Move last_move)
01435 {
01436   return tryProofMain<true>(state, key, path, oracle, oracle_id, best_move, last_move);
01437 }
01438 const osl::checkmate::ProofDisproof 
01439 osl::checkmate::
01440 Dfpn::tryProofLight(const NumEffectState& state, const HashKey& key,
01441                     const PathEncoding& path, const ProofOracle& oracle, size_t oracle_id, Move& best_move,
01442                     Move last_move)
01443 {
01444   return tryProofMain<false>(state, key, path, oracle, oracle_id, best_move, last_move);
01445 }
01446 
01447 static const size_t root_proof_simulation_limit = 999999999;// large enough
01448 
01449 template <bool UseTable>
01450 const osl::checkmate::ProofDisproof 
01451 osl::checkmate::
01452 Dfpn::tryProofMain(const NumEffectState& state, const HashKey& key,
01453                    const PathEncoding& path, const ProofOracle& oracle, size_t oracle_id, Move& best_move,
01454                    Move last_move)
01455 {
01456   assert(table);
01457   if (! table)
01458     return ProofDisproof();
01459   path_table->clear();
01460   
01461   tree->state.copyFrom(state);
01462   node_count = tree->depth = 0;
01463   node_count_limit = root_proof_simulation_limit;
01464 
01465   Node& root = tree->node[0];
01466   root.hash_key = key;
01467   root.path = path;
01468   root.clear();
01469   root.threshold = ProofDisproof(ROOT_PROOF_TOL, ROOT_DISPROOF_TOL);
01470   root.white_stand = PieceStand(WHITE, state);
01471   root.moved = last_move;
01472 
01473   root.record = (state.turn() == BLACK) 
01474     ? table->probe<BLACK>(root.hash_key, root.white_stand)
01475     : table->probe<WHITE>(root.hash_key, root.white_stand);
01476   if (root.record.proof_disproof.isFinal() || root.record.tried_oracle > oracle_id) {
01477     best_move = root.record.best_move;
01478     return root.record.proof_disproof;
01479   }
01480 
01481   try {
01482     if (state.turn() == BLACK)
01483       proofOracleAttack<BLACK,UseTable>(oracle, ProofSimulationTolerance);
01484     else
01485       proofOracleAttack<WHITE,UseTable>(oracle, ProofSimulationTolerance);
01486   }
01487   catch (DepthLimitReached&) {
01488     for (int i=0; i<=tree->depth; ++i)
01489       table->leaveWorking(tree->node[i].hash_key, thread_id);
01490     return ProofDisproof();
01491   }
01492   if (UseTable && root.path_record
01493       && (std::find(root.path_record->twin_list.begin(), root.path_record->twin_list.end(), path)
01494           != root.path_record->twin_list.end()))
01495     return ProofDisproof::LoopDetection();
01496   if (UseTable) {
01497     root.record.last_move = last_move;
01498     table->store(root.hash_key, root.record);
01499   }
01500   best_move = root.record.best_move;
01501   root.record.tried_oracle = oracle_id+1;
01502   return root.record.proof_disproof;
01503 }
01504 
01505 
01506 const osl::checkmate::ProofDisproof
01507 osl::checkmate::
01508 Dfpn::hasEscapeMove(const NumEffectState& state, 
01509                     const HashKey& key, const PathEncoding& path, 
01510                     size_t limit, Move last_move)
01511 {
01512   assert(table);
01513   if (! state.hasEffectAt(alt(state.turn()), state.kingSquare(state.turn())))
01514     return ProofDisproof::NoCheckmate();
01515   if (! table)
01516     return ProofDisproof();
01517   path_table->clear();
01518   node_count = tree->depth = 0;
01519   node_count_limit = limit;
01520 
01521   Node& root = tree->node[0];  
01522   try {
01523     tree->state.copyFrom(state);
01524     tree->depth = 0;
01525     root.hash_key = key;
01526     root.path = path;
01527     root.moved = last_move;
01528     root.clear();
01529     root.threshold = ProofDisproof(ROOT_PROOF_TOL, ROOT_DISPROOF_TOL);
01530     root.white_stand = PieceStand(WHITE, state);
01531     if (state.turn() == BLACK)
01532       defense<WHITE>();
01533     else
01534       defense<BLACK>();
01535 
01536     if (root.record.need_full_width) {
01537       root.clear();
01538       if (state.turn() == BLACK)
01539         defense<WHITE>();
01540       else
01541         defense<BLACK>();
01542     }
01543   }
01544   catch (DepthLimitReached&) {
01545     return ProofDisproof();
01546   }
01547   if (root.record.proof_disproof == ProofDisproof::NoEscape()
01548       && last_move.isNormal() && last_move.isDrop() && last_move.ptype() == PAWN)
01549     return ProofDisproof::PawnCheckmate();
01550   if (root.path_record) {
01551     const SimpleTwinList& tl = root.path_record->twin_list;
01552     if (std::find(tl.begin(), tl.end(), root.path) != tl.end())
01553       return ProofDisproof::LoopDetection();
01554   }
01555   return root.record.proof_disproof;
01556 }
01557 
01558 namespace osl
01559 {
01560   namespace 
01561   {
01562     typedef boost::tuple<int,int,int> tuple_t;
01563     template <Player Turn>
01564     struct move_compare
01565     {
01566       const NumEffectState *state;
01567       move_compare(const NumEffectState& s) : state(&s)
01568       {
01569         assert(Turn == state->turn());
01570       }
01571       tuple_t convert(Move m) const
01572       {
01573         const int a = state->countEffect(Turn, m.to()) + m.isDrop();
01574         const int d = state->countEffect(alt(Turn), m.to());
01575         const int to_y = playerToMul(Turn)*m.to().y();
01576         const int to_x = (5 - abs(5-m.to().x()))*2 + (m.to().x() > 5);
01577         int from_to = (to_y*16+to_x)*256;
01578         if (m.isDrop())
01579           from_to += m.ptype();
01580         else
01581           from_to += m.from().index();
01582         return boost::make_tuple(a > d, from_to, m.isPromotion());
01583       }
01584       bool operator()(Move l, Move r) const
01585       {
01586         return convert(l) > convert(r);
01587       }
01588     };
01589   }
01590 }
01591 
01592 template <osl::Player Turn>
01593 void osl::checkmate::
01594 Dfpn::sort(const NumEffectState& state, DfpnMoveVector& moves)
01595 {
01596 #ifdef MEMORIZE_SOLVED_IN_BITSET
01597   int last_sorted = 0, cur = 0; 
01598   Ptype last_ptype = PTYPE_EMPTY;
01599   for (;(size_t)cur < moves.size(); ++cur) {
01600     if (moves[cur].isDrop() || moves[cur].oldPtype() == last_ptype)
01601       continue;
01602     std::sort(moves.begin()+last_sorted, moves.begin()+cur, move_compare<Turn>(state));
01603     last_sorted = cur;
01604     last_ptype = moves[cur].oldPtype();
01605   }
01606   std::sort(moves.begin()+last_sorted, moves.begin()+cur, move_compare<Turn>(state));
01607 #endif
01608 }
01609 
01610 template <osl::Player P> 
01611 void osl::checkmate::
01612 Dfpn::generateCheck(const NumEffectState& state, DfpnMoveVector& moves, bool &has_pawn_checkmate)
01613 {
01614   assert(moves.empty());
01615   if (state.inCheck(P))
01616   {
01617     using namespace osl::move_classifier;
01618     DfpnMoveVector escape;
01619     move_generator::GenerateEscape<P>::generateKingEscape(state, escape);
01620     BOOST_FOREACH(Move move, escape)
01621     {
01622       if (MoveAdaptor<Check<P> >::isMember(state, move))
01623         moves.push_back(move);
01624     }
01625   }
01626   else
01627   {
01628     move_action::Store store(moves);
01629     move_generator::AddEffectWithEffect<move_action::Store>::template generate<P,true>
01630       (state,state.kingPiece(alt(P)).square(),store,has_pawn_checkmate);
01631   }
01632   BOOST_FOREACH(Move move, moves)
01633   {
01634     if(move.hasIgnoredUnpromote<P>()){
01635       if(Ptype_Table.getEffect(unpromote(move.ptypeO()),move.to(),
01636                                state.kingSquare(alt(P))).hasEffect()
01637          || (state.pinOrOpen(alt(P)).test
01638              (state.pieceAt(move.from()).number())))
01639         moves.push_back(move.unpromote());
01640     }
01641   }
01642   sort<P>(state, moves);
01643 }
01644 
01645 void osl::checkmate::
01646 Dfpn::findDagSource(const HashKey& terminal_key,
01647                     DfpnRecord& terminal_record,
01648                     PieceStand terminal_stand, int offset)
01649 {
01650 #ifdef NAGAI_DAG_TEST
01651   PieceStand white_stand = terminal_stand;
01652   HashKey key = terminal_key;
01653   DfpnRecord cur = terminal_record;
01654   
01655   for (int d=offset; d<std::min(tree->MaxDepth,MaxDagTraceDepth); ++d) {
01656     if (! cur.last_move.isNormal())
01657       return;
01658     assert(key.turn() == alt(cur.last_move.player()));
01659     HashKey parent_key = key.newUnmakeMove(cur.last_move);
01660     white_stand = white_stand.previousStand(WHITE, cur.last_move);
01661     DfpnRecord parent = table->probe(parent_key, white_stand);
01662     // loop invariants
01663     // { parent, parent_key, white_stand } --(cur.last_move)-> { cur, key }
01664 
01665     // some implementation test (parent.best_move == cur.last_move) here
01666     // but it seems to be not suitable for gpsshogi
01667     for (int i=tree->depth - 4 - (d%2); i>=0; i-=2) {
01668       if (parent_key == tree->node[i].hash_key) {
01669         for (size_t m=0; m<std::min(tree->node[i].moves.size(), (size_t)64); ++m) {
01670           if (tree->node[i].moves[m] == tree->node[i+1].moved
01671               || tree->node[i].moves[m] == cur.last_move)
01672             tree->node[i].record.dag_moves |= (1ull << m);
01673         }
01674         if (parallel_shared)
01675           table->addDag(tree->node[i].hash_key, tree->node[i].record);
01676         terminal_record.dag_terminal = true;
01677         return;
01678       }
01679     }
01680     key = parent_key;
01681     cur = parent;
01682   }
01683 #endif
01684 }
01685 
01686 void osl::checkmate::
01687 Dfpn::findDagSource()
01688 {
01689   findDagSource(tree->node[tree->depth].hash_key, tree->node[tree->depth].record, 
01690                 tree->node[tree->depth].white_stand, 1);
01691 }
01692 
01693 // P は攻撃側
01694 template <osl::Player P> 
01695 void osl::checkmate::
01696 Dfpn::attack()
01697 {
01698   assert(! tree->inCheck(alt(P)));
01699   Node& node = tree->node[tree->depth];
01700 #if (! defined NDEBUG) && (! defined OSL_USE_RACE_DETECTOR)
01701   node.visit_time = ++timer;
01702 #endif
01703 #ifdef DFPN_DEBUG
01704   Tree::Logging logging(tree.get(), table, "attack");
01705 #endif
01706   const int my_distance = tree->depth ? tree->node[tree->depth-1].path_record->distance+1 : node.path.getDepth();
01707   LoopToDominance loop;
01708   DfpnVisitLock<> lk(node.path_record = path_table->allocate<P>(node.hash_key, my_distance, loop));
01709   DfpnRecord& record = node.record;
01710   record = DfpnRecord();
01711   if (loop == BadAttackLoop) {
01712     node.setLoopDetection();
01713     return;
01714   }
01715   assert(node.white_stand == PieceStand(WHITE, tree->state));
01716   const size_t node_count_org = node_count++;
01717 #if (! defined CHECKMATE_D2) && (! defined NO_IMMEDIATE_CHECKMATE)
01718   if (! tree->inCheck(P)
01719       && ImmediateCheckmate::hasCheckmateMove<P>(tree->state, record.best_move)) {
01720     PieceStand proof_pieces;    // Note: ImmediateCheckmate が合駒が必要な王手を使わないことに依存
01721     if (record.best_move.isDrop())
01722       proof_pieces.add(record.best_move.ptype());
01723     record.setProofPieces(proof_pieces);
01724     record.proof_disproof = ProofDisproof::Checkmate();
01725     return;
01726   }
01727 #endif
01728   if (tree->depth + 2 >= tree->MaxDepth) {
01729     std::cerr << "throw " << thread_id << "\n";
01730     throw DepthLimitReached();
01731   }
01732   assert(tree->depth + 2 < tree->MaxDepth);
01733   record = table->probe<P>(node.hash_key, node.white_stand);
01734   assert(record.stands[BLACK] == node.hash_key.blackStand());
01735   assert(record.stands[WHITE] == node.white_stand);
01736   if (record.proof_disproof.isFinal())
01737     return;
01738   if (tree->depth == 0 && node_count_limit <= 50 && record.node_count >= node_count_limit)
01739     return;
01740   if (tree->depth == 0
01741 #ifdef CHECKMATE_A3
01742       || true
01743 #endif
01744 #ifdef CHECKMATE_A3_GOLD
01745       || (record.proof_disproof == ProofDisproof(1,1) && tree->state.hasPieceOnStand<GOLD>(P)
01746           && (tree->king(alt(P)).square().x() <= 3 || tree->king(alt(P)).square().x() >= 7
01747               || tree->king(alt(P)).square().template squareForBlack<P>().y() <= 3))
01748 #endif
01749     )
01750   {
01751 #ifdef DFPN_STAT
01752     static stat::Ratio oracle_success("a3-gold");
01753 #endif
01754     FixedDepthSearcher fixed_searcher(tree->state);
01755     PieceStand proof_pieces;
01756     ProofDisproof pdp = fixed_searcher.hasCheckmateMove<P>(2, record.best_move, proof_pieces);
01757     ++node_count;
01758 #ifdef DFPN_STAT
01759     oracle_success.add(pdp.isCheckmateSuccess());
01760 #endif
01761     if (pdp.isCheckmateSuccess()) {
01762       record.node_count++;
01763       record.proof_disproof = pdp;
01764       record.setProofPieces(proof_pieces);
01765       record.last_move = node.moved;
01766       table->store(node.hash_key, record);
01767       return;
01768     }
01769   }
01770 #ifndef MINIMAL
01771   if (tree->MaxDepth > EnableGCDepth && thread_id <= 0) {
01772     try {
01773       const size_t removed = table->runGC();
01774       if (removed > 0) {
01775 #ifdef DFPN_DEBUG
01776         for (int i=1; i<tree->depth; ++i)
01777           std::cerr << tree->node[i].threshold.proof() << ' '
01778                     << record::csa::show(tree->node[i].moved) << ' ';
01779         std::cerr << "\n";
01780 #endif
01781       }
01782     }
01783     catch (...) { //  fail
01784       if (parallel_shared)
01785         parallel_shared->stop_all = true;
01786       throw;
01787     }
01788   }
01789   if (tree->MaxDepth > EnableGCDepth
01790       && (path_table->size() > table->growthLimit()
01791 #ifdef OSL_DFPN_SMP
01792           || (parallel_shared
01793               && path_table->size() > table->growthLimit()/4)
01794 #endif
01795         )) {
01796     const size_t before = path_table->size();
01797     const size_t removed = path_table->runGC();
01798     if (removed > 0) {
01799       if (thread_id <= 0)
01800         std::cerr << " GC-path collected "
01801                   << std::setprecision(3)
01802                   << ((sizeof(HashKey)+sizeof(DfpnPathRecord)+sizeof(char*)*2)
01803                       * removed / (1<<20)) << "MB "
01804                   << 100.0*removed/before << "%\n";
01805       for (int i=0; i<tree->depth; ++i) {
01806         for (size_t j=0; j<tree->node[i].moves.size(); ++j) {
01807           tree->node[i].children_path[j] = 0;
01808         }
01809       }
01810     }
01811   }
01812 #endif
01813   if (parallel_shared) {
01814     if (parallel_shared->stop_all) {
01815       // std::cerr << "throw " << thread_id << "\n";
01816       throw DepthLimitReached();
01817     }
01818     if (parallel_shared->data[thread_id].restart) {
01819       for (int i=0; i<tree->depth; ++i) {
01820         if (tree->node[i].hash_key
01821             == parallel_shared->data[thread_id].restart_key)
01822           return;
01823 #if 0
01824         if (tree->node[i].record.dag_terminal)
01825           break;                // ignore
01826 #endif
01827       }
01828       // false alert
01829       parallel_shared->data[thread_id].clear();
01830     }
01831   }
01832 
01833   // move generation
01834   bool has_pawn_checkmate=false;
01835   generateCheck<P>(tree->state, node.moves,has_pawn_checkmate);
01836   if (node.moves.empty()) {
01837     record.setDisproofPieces(DisproofPieces::leaf(tree->state, alt(P), 
01838                                                   record.stands[alt(P)]));
01839     if(has_pawn_checkmate)
01840       record.proof_disproof = ProofDisproof::PawnCheckmate();
01841     else
01842       record.proof_disproof = ProofDisproof::NoCheckmate();
01843     return;
01844   }
01845   // probe all
01846 #ifdef PROOF_AVERAGE
01847   int frontier_count = 0, sum_frontier_proof = 0;
01848 #endif
01849   assert(node.children.empty());
01850   {
01851     node.allocate(node.moves.size());
01852     const King8Info info_modified 
01853       = Edge_Table.resetEdgeFromLiberty(alt(P), tree->king(alt(P)).square(), King8Info(tree->state.Iking8Info(alt(P))));
01854     for (size_t i=0; i<node.moves.size(); ++i) {
01855 #ifdef MEMORIZE_SOLVED_IN_BITSET
01856       if (record.solved & (1ull << i))
01857         continue;
01858 #endif
01859       const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
01860       node.children[i] = table->probe<P>(new_key, node.nextWhiteStand(P, node.moves[i]));
01861       if (node.children[i].proof_disproof == ProofDisproof(1,1)) {
01862         unsigned int proof, disproof;
01863         LibertyEstimator::attackH(P, tree->state, info_modified, 
01864                                   node.moves[i], proof, disproof);
01865 #ifndef MINIMAL
01866         if (HashRandomPair::initialized()) {
01867           // randomness presented by Hoki2011 (zero by default)
01868           std::pair<char,char> randomness = HashRandomPair::value(new_key);
01869           proof    += randomness.first;
01870           disproof += randomness.second;
01871         }
01872 #endif
01873         node.children[i].proof_disproof = ProofDisproof(proof, disproof);
01874       }
01875       if (node.children[i].proof_disproof == ProofDisproof::NoEscape()
01876           && node.moves[i].isDrop() && node.moves[i].ptype() == PAWN) {
01877         node.children[i].proof_disproof = ProofDisproof::PawnCheckmate();
01878 #ifdef MEMORIZE_SOLVED_IN_BITSET
01879         record.solved |= (1ull << i);
01880 #endif
01881         record.min_pdp = std::min(record.min_pdp, (unsigned int)ProofDisproof::PAWN_CHECK_MATE_PROOF);
01882       }
01883       else if (node.children[i].proof_disproof.isCheckmateFail())
01884         tree->setNoCheckmateChildInAttack(i);
01885       else if (node.children[i].proof_disproof.isCheckmateSuccess()) {
01886         record.node_count += node_count - node_count_org;
01887         node.setCheckmateAttack(P,i);
01888         record.last_move = node.moved;
01889         table->store(node.hash_key, record);
01890         node.path_record->node_count = 0;
01891         return;
01892       }
01893 #ifdef PROOF_AVERAGE
01894       else if (node.children[i].node_count == 0) {
01895         ++frontier_count;
01896         sum_frontier_proof += node.children[i].proof();
01897         assert(node.children[i].proof() < 128);
01898       }
01899 #endif
01900 #ifdef AGGRESSIVE_FIND_DAG2
01901       else if (!node.children[i].proof_disproof.isFinal()
01902                && std::max(node.children[i].proof(), node.children[i].disproof()) >= DagFindThreshold2
01903                && node.children[i].last_move.isNormal()
01904                && node.children[i].last_move != node.moves[i]) {
01905         findDagSource(node.hashes[i], node.children[i],
01906                       node.nextWhiteStand(P, node.moves[i]));
01907       }
01908 #endif
01909       node.children_path[i] = path_table->probe(new_key);
01910       node.proof_cost[i] = attackProofCost(P, tree->state, node.moves[i]);
01911     }
01912   }
01913 
01914   // hereafter, call leaveWorking before returning
01915   if (parallel_shared)
01916     table->setWorking(node.hash_key, record, thread_id);
01917 
01918   const Move recorded_last_move = record.last_move;
01919   record.last_move = node.moved;
01920 
01921   assert(node.children.size() == node.moves.size());
01922 #ifdef PROOF_AVERAGE
01923   const size_t proof_average = frontier_count ? sum_frontier_proof/frontier_count : 1;
01924 #else
01925   const size_t proof_average = 1;
01926 #endif
01927   // main loop
01928 #ifdef DFPN_DEBUG
01929   if (std::find(debug_node.begin(), debug_node.end(), node_id_table.id(node.hash_key))
01930       != debug_node.end() && timer > debug_time_start)
01931     tree->dump(__LINE__);
01932 #endif
01933   for (int loop=0; true; ++loop) {
01934     unsigned int min_proof=record.min_pdp, min_proof2=record.min_pdp;
01935     size_t sum_disproof = 0, max_disproof = 0, max_disproof_dag = 0, next_i=node.children.size();
01936     size_t max_drop_disproof_rook = 0, max_drop_disproof_bishop = 0, max_drop_disproof_lance = 0;
01937     int max_children_depth = 0, upward_count = 0;
01938     for (size_t i=0; i<node.children.size(); ++i) {
01939 #ifdef MEMORIZE_SOLVED_IN_BITSET
01940       if (record.solved & (1ull << i))
01941         continue;
01942 #endif
01943       if (i > 0 && min_proof < ProofDisproof::PROOF_LIMIT
01944           && node.moves[i].fromTo() == node.moves[i-1].fromTo()
01945           && ! node.moves[i].isDrop()) {
01946         // ignore a no-promote move until it becomes the last one, if there is the corresponding promote move  
01947         assert(node.moves[i].ptype() == node.moves[i-1].oldPtype());
01948         record.dag_moves |= ((1ull << i) | (1ull << (i-1)));
01949         if (node.threshold.proof() < NoPromoeIgnoreProofThreshold
01950             && node.threshold.disproof() < NoPromoeIgnoreDisproofThreshold)
01951           continue;
01952         // fall through
01953       }
01954       size_t proof = node.children[i].proof();
01955       size_t disproof = node.children[i].disproof();
01956       if (proof && disproof) {
01957         proof += node.proof_cost[i];
01958 #ifdef OSL_DFPN_SMP
01959         if (parallel_shared && node.children[i].working_threads) {
01960           // proof += misc::BitOp::countBit(node.children[i].working_threads)/2+1;
01961           proof += misc::BitOp::countBit(node.children[i].working_threads);
01962         }
01963 #endif
01964       }
01965       if (node.children_path[i]) {      
01966         if (node.isLoop(i)) {
01967           node.children[i].proof_disproof = ProofDisproof::LoopDetection();
01968           assert(proof < ProofDisproof::LOOP_DETECTION_PROOF);
01969           proof = ProofDisproof::LOOP_DETECTION_PROOF;
01970           disproof = 0;
01971         } 
01972         else if (! node.children[i].proof_disproof.isFinal()) {
01973           max_children_depth = std::max(max_children_depth, node.children_path[i]->distance);
01974 #ifdef NAGAI_DAG_TEST
01975           if (record.dag_moves & (1ull<<i)) {
01976             max_disproof_dag = std::max(max_disproof_dag, disproof);
01977             disproof = 0;
01978           }
01979           else
01980 #endif
01981 #ifdef DELAY_UPWARD
01982             if (node.children_path[i]->distance <= node.path_record->distance) {
01983               max_disproof = std::max(max_disproof, disproof);
01984               ++upward_count;
01985               disproof = UpwardWeight;
01986             }
01987             else
01988 #endif
01989             if (node.moves[i].isDrop()
01990                 || (isMajor(node.moves[i].ptype())
01991                     && ! node.moves[i].isCapture()
01992                     && ! node.moves[i].isPromotion() && isPromoted(node.moves[i].ptype())
01993                     && ! tree->state.hasEffectAt(alt(P), node.moves[i].to()))) {
01994               const EffectContent e
01995                 = Ptype_Table.getEffect(node.moves[i].ptypeO(),
01996                                         Offset32(tree->king(alt(P)).square(), node.moves[i].to()));
01997               if (! e.hasUnblockableEffect()) {
01998                 size_t *target = &max_drop_disproof_lance;
01999                 if (unpromote(node.moves[i].ptype()) == ROOK)
02000                   target = &max_drop_disproof_rook;
02001                 else if (unpromote(node.moves[i].ptype()) == BISHOP)
02002                   target = &max_drop_disproof_bishop;
02003                 *target = std::max(*target, disproof);
02004                 disproof = LongDropCount;
02005               }
02006             }
02007         } // ! isFinal
02008       }
02009       else {
02010         max_children_depth = node.path_record->distance+1;
02011       }
02012       if (proof < min_proof || (proof == min_proof && disproof && disproof < node.children[next_i].disproof())) {
02013         min_proof2 = min_proof;
02014         min_proof = proof;
02015         next_i = i;
02016       } else if (proof < min_proof2) {
02017         min_proof2 = proof;
02018       }
02019       sum_disproof += disproof;
02020     }
02021     sum_disproof += max_drop_disproof_rook + max_drop_disproof_bishop + max_drop_disproof_lance
02022       + max_disproof_dag;
02023     if (LongDropCount) {
02024       if (max_drop_disproof_rook)   sum_disproof -= LongDropCount;
02025       if (max_drop_disproof_bishop) sum_disproof -= LongDropCount;
02026       if (max_drop_disproof_lance)  sum_disproof -= LongDropCount;
02027     }
02028     if (upward_count) {
02029       if (sum_disproof == 0)
02030         sum_disproof = max_disproof;
02031     }
02032     if (node.path_record->distance >= max_children_depth) {
02033       node.path_record->distance = max_children_depth-1;
02034     }
02035 #ifdef KISHIMOTO_WIDEN_THRESHOLD
02036     if (loop == 0 && sum_disproof >= node.threshold.disproof() && sum_disproof > IgnoreUpwardDisproofThreshold)
02037       node.threshold = ProofDisproof(node.threshold.proof(), sum_disproof+1);
02038 #endif
02039 #ifdef ADHOC_SUM_RESTRICTION
02040     if (sum_disproof < ROOT_DISPROOF_TOL && min_proof > 0 && sum_disproof > min_proof*AdHocSumScale) {
02041       sum_disproof = min_proof*AdHocSumScale
02042         + slow_increase(sum_disproof-min_proof*AdHocSumScale);
02043     }
02044 #endif
02045     if (min_proof >= node.threshold.proof()
02046         || sum_disproof >= node.threshold.disproof()
02047         || next_i >= node.children.size()
02048         || node_count + min_proof >= node_count_limit) {
02049       record.proof_disproof = ProofDisproof(min_proof, sum_disproof);
02050       if (record.proof_disproof.isLoopDetection())
02051         node.setLoopDetection();
02052       else if (record.proof_disproof.isCheckmateFail()) {
02053         node.setNoCheckmateAttack(P, tree->state);
02054       } else if (! record.proof_disproof.isFinal()) {
02055         if (recorded_last_move.isNormal() && recorded_last_move != node.moved
02056             && std::max(record.proof(), record.disproof()) >= DagFindThreshold)
02057           findDagSource();
02058 #ifdef AGGRESSIVE_FIND_DAG
02059         if (std::max(node.children[next_i].proof(), node.children[next_i].disproof()) >= DagFindThreshold
02060             && node.children[next_i].last_move.isNormal()
02061             && node.children[next_i].last_move != node.moves[next_i]) {
02062           findDagSource(node.hashes[next_i], node.children[next_i],
02063                         node.nextWhiteStand(P, node.moves[next_i]));
02064           node.children[next_i].last_move = node.moves[next_i];
02065           table->store(node.hashes[next_i], node.children[next_i]);
02066         }
02067 #endif
02068       }
02069       record.node_count += node_count - node_count_org;
02070       table->store(node.hash_key, record, thread_id);
02071       node.path_record->node_count = record.node_count;
02072       if (parallel_shared && record.proof_disproof.isFinal())
02073         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02074       return;
02075     }
02076 #ifdef MEMORIZE_SOLVED_IN_BITSET
02077     assert(! (record.solved & (1ull << next_i)));
02078 #endif
02079     record.best_move = node.moves[next_i];
02080     tree->newVisit(P, node.moves[next_i], node.hashes[next_i]);
02081     Node& next = tree->node[tree->depth+1];
02082     unsigned int disproof_c = node.threshold.disproof()
02083       - (sum_disproof - node.children[next_i].disproof());
02084 #ifdef ADHOC_SUM_RESTRICTION
02085     if (disproof_c > node.threshold.disproof())
02086       disproof_c = node.children[next_i].disproof()
02087         + (node.threshold.disproof() - sum_disproof);
02088 #endif    
02089     next.threshold = ProofDisproof(std::min(min_proof2+proof_average, (size_t)node.threshold.proof())
02090                                    - node.proof_cost[next_i], 
02091                                    disproof_c);
02092     CallDefense<P> helper(this);
02093     tree->depth += 1;
02094     next.path.pushMove(next.moved);
02095     tree->state.makeUnmakeMove(Player2Type<P>(), next.moved, helper);
02096     tree->depth -= 1;
02097     node.children[next_i] = next.record;
02098     node.children_path[next_i] = next.path_record;
02099     if (next.record.proof_disproof == ProofDisproof::NoEscape()
02100         && next.moved.isDrop() && next.moved.ptype() == PAWN)
02101       node.children[next_i].proof_disproof = ProofDisproof::PawnCheckmate();
02102     if (node.children[next_i].proof_disproof.isCheckmateSuccess()) {
02103       node.setCheckmateAttack(P,next_i);
02104       record.node_count += node_count - node_count_org;
02105       record.last_move = node.moved;
02106       table->store(node.hash_key, record, thread_id);
02107       node.path_record->node_count = 0;
02108       if (parallel_shared)
02109         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02110       return;
02111     }      
02112     else if (next.record.proof_disproof.isCheckmateFail()
02113              && ! next.record.proof_disproof.isLoopDetection())
02114       tree->setNoCheckmateChildInAttack(next_i);
02115     min_proof = std::min(min_proof2, node.children[next_i].proof());
02116     if (min_proof < ProofDisproof::PROOF_LIMIT
02117         && node_count + min_proof >= node_count_limit) {
02118       record.proof_disproof = ProofDisproof(min_proof, sum_disproof);
02119       record.node_count += node_count - node_count_org;
02120       table->store(node.hash_key, record, thread_id);
02121       node.path_record->node_count = record.node_count;
02122       if (parallel_shared)
02123         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02124       return;
02125     }
02126     if (parallel_shared && parallel_shared->data[thread_id].restart) {
02127       if (tree->depth == 0)
02128         parallel_shared->data[thread_id].clear();
02129       else {
02130         if (parallel_shared->data[thread_id].restart_key == node.hash_key) {
02131           record = table->probe<P>(node.hash_key, node.white_stand);
02132           if (! record.proof_disproof.isFinal()) 
02133             continue;
02134           parallel_shared->data[thread_id].clear();
02135         }
02136         table->leaveWorking(node.hash_key, thread_id);
02137         return;
02138       }
02139     } 
02140   }
02141 }
02142 
02143 template <osl::Player P> 
02144 void osl::checkmate::
02145 Dfpn::generateEscape(const NumEffectState& state, bool need_full_width, 
02146                      Square last_to, DfpnMoveVector& moves)
02147 {
02148   assert(moves.empty());
02149   const Player AltP=PlayerTraits<P>::opponent;
02150 #ifdef GRAND_PARENT_DELAY
02151   const bool delay_node = last_to != Square() 
02152       && state.hasEffectAt(alt(P), last_to)
02153       && (state.hasEffectNotBy(alt(P), state.kingPiece(alt(P)), last_to)
02154           || ! state.hasEffectAt(P, last_to));
02155   if (delay_node)
02156   {
02157     DfpnMoveVector all;
02158     move_generator::GenerateEscape<AltP>::
02159 	generateCheapKingEscape(state, all);
02160 
02161     BOOST_FOREACH(Move move, all) {
02162       if (move.to() == last_to) {
02163         moves.push_back(move);
02164       }
02165     }
02166 #ifdef MEMORIZE_SOLVED_IN_BITSET
02167     sort<AltP>(state, moves);
02168 #endif
02169   }
02170   else 
02171 #endif
02172   {
02173     move_generator::GenerateEscape<AltP>::
02174 	generateCheapKingEscape(state, moves);
02175 #ifdef MEMORIZE_SOLVED_IN_BITSET
02176     sort<AltP>(state, moves);
02177 #endif
02178   }
02179 
02180   if (need_full_width) {
02181     DfpnMoveVector others;
02182     move_generator::GenerateEscape<AltP>::
02183       generateKingEscape(state, others);
02184 #ifdef MEMORIZE_SOLVED_IN_BITSET
02185     sort<AltP>(state, others);
02186 #endif
02187     const int org_size = moves.size();
02188     BOOST_FOREACH(Move move, others) {
02189       if (std::find(moves.begin(), moves.begin()+org_size, move) == moves.begin()+org_size)
02190         moves.push_back(move);
02191     }
02192     BOOST_FOREACH(Move move, moves)
02193     {
02194       if(move.hasIgnoredUnpromote<AltP>())
02195         moves.push_back(move.unpromote());
02196     }
02197   }
02198   // TODO: 受け方の打歩詰め王手
02199 }
02200 
02201 bool osl::checkmate::
02202 Dfpn::grandParentSimulationSuitable() const
02203 {
02204 #ifdef GRAND_PARENT_SIMULATION
02205   Node& node = tree->node[tree->depth];
02206   if (tree->depth >= 2) {
02207     const Node& parent = tree->node[tree->depth-1];
02208     const Node& gparent = tree->node[tree->depth-2];
02209     const Move alm = node.moved; // attacker's last move
02210     const Move dlm = parent.moved; // defense's last move
02211     const Move alm2 = gparent.moved; // attacker's second-last move
02212     if (dlm.isNormal() && alm.to() == dlm.to() && ! dlm.isCapture()
02213         && alm2.isNormal() && alm2.to() == alm.from()) {
02214       return true;
02215     }
02216   }
02217 #endif
02218   return false;
02219 }
02220 
02221 template <osl::Player P> 
02222 void osl::checkmate::
02223 Dfpn::defense()
02224 {
02225 #if 0
02226   if (parallel_shared) {
02227     if (parallel_shared->stop_all)
02228       throw DepthLimitReached();
02229     if (parallel_shared->data[thread_id].restart) {
02230       for (int i=0; i<tree->depth; ++i) {
02231         if (tree->node[i].hash_key == parallel_shared->data[thread_id].restart_key)
02232           return;
02233 #if 0
02234         if (tree->node[i].record.dag_terminal)
02235           break;
02236 #endif
02237       }
02238       // false alert
02239       parallel_shared->data[thread_id].clear();
02240     }
02241   }
02242 #endif
02243   Node& node = tree->node[tree->depth];
02244 #if (! defined NDEBUG) && (! defined OSL_USE_RACE_DETECTOR)
02245   node.visit_time = ++timer;
02246 #endif
02247 #ifdef DFPN_DEBUG
02248   Tree::Logging logging(tree.get(), table, "defens");
02249 #endif
02250   const int my_distance = tree->depth ? tree->node[tree->depth-1].path_record->distance+1 : node.path.getDepth();
02251   LoopToDominance loop;
02252   DfpnVisitLock<> lk(node.path_record = path_table->allocate<P>(node.hash_key, my_distance, loop));
02253   DfpnRecord& record = node.record;
02254   if (loop == BadAttackLoop) {
02255     record = DfpnRecord();
02256     node.setLoopDetection();
02257     return;
02258   }
02259   const size_t node_count_org = node_count++;
02260   assert(tree->inCheck(alt(P)));
02261   assert(node.white_stand == PieceStand(WHITE, tree->state));
02262 
02263   record = table->probe<P>(node.hash_key, node.white_stand);
02264   assert(record.stands[BLACK] == node.hash_key.blackStand());
02265   assert(record.stands[WHITE] == node.white_stand);
02266   if (record.proof_disproof.isFinal())
02267     return;
02268   const bool grand_parent_simulation = grandParentSimulationSuitable();
02269   if (record.last_to == Square())
02270     record.last_to = grand_parent_simulation ? node.moved.to() : tree->king(alt(P)).square();
02271   const Square grand_parent_delay_last_to
02272     = (record.last_to != tree->king(alt(P)).square()) ? record.last_to : Square();
02273 
02274   generateEscape<P>(tree->state, record.need_full_width, grand_parent_delay_last_to, node.moves);
02275   if (node.moves.empty() && ! record.need_full_width) {
02276     record.need_full_width = true;
02277     generateEscape<P>(tree->state, record.need_full_width, grand_parent_delay_last_to, node.moves);
02278   }
02279   if (node.moves.empty()) {
02280     record.setProofPieces(ProofPieces::leaf(tree->state, P, record.stands[P]));
02281     record.proof_disproof = ProofDisproof::NoEscape();
02282     return;
02283   }
02284   // probe all
02285 #ifdef DISPROOF_AVERAGE
02286   int frontier_count = 0, sum_frontier_disproof = 0;
02287 #endif
02288   assert(node.children.empty());
02289   {
02290     node.allocate(node.moves.size());
02291     for (size_t i=0;i <node.moves.size(); ++i) {
02292 #ifdef MEMORIZE_SOLVED_IN_BITSET
02293       if (record.solved & (1ull << i))
02294         continue;
02295 #endif
02296       const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
02297       node.children[i] = table->probe<P>(new_key, node.nextWhiteStand(alt(P), node.moves[i]));
02298       if (node.children[i].proof_disproof.isCheckmateSuccess()) {
02299         node.setCheckmateChildInDefense(i);     
02300       }
02301 #ifdef CHECKMATE_D2
02302       else if (node.children[i].proof_disproof == ProofDisproof(1,1)) {
02303         FixedDepthSearcher fixed_searcher(tree->state);
02304         PieceStand proof_pieces;
02305         Move check_move;
02306         node.children[i].proof_disproof
02307           = fixed_searcher.hasEscapeByMove<P>(node.moves[i], 0, check_move, proof_pieces);
02308         ++node_count;
02309         if (node.children[i].proof_disproof.isCheckmateSuccess()) {       
02310           node.children[i].best_move = check_move;
02311           node.children[i].setProofPieces(proof_pieces);
02312           node.children[i].node_count++;
02313           node.setCheckmateChildInDefense(i);
02314         }
02315         else {
02316           if (node.children[i].proof_disproof.isCheckmateFail()) {
02317             node.children[i].proof_disproof = ProofDisproof(1,1);
02318             if (i) {
02319               node.moves[0] = node.moves[i];
02320               node.children[0] = node.children[i];
02321               node.children_path[0] = node.children_path[i];
02322               const int old_size = (int)node.moves.size();
02323               for (int j=1; j<old_size; ++j) {
02324                 node.moves.pop_back();
02325                 node.children.pop_back();
02326                 node.children_path.pop_back();
02327               }
02328             }
02329             break;
02330           }
02331           else {
02332 #ifndef MINIMAL
02333             if (HashRandomPair::initialized()) {
02334               // randomness presented by Hoki2011 (zero by default)
02335               std::pair<char,char> randomness = HashRandomPair::value(new_key);
02336               if (randomness.first || randomness.second) {
02337                 unsigned int proof    = node.children[i].proof();
02338                 unsigned int disproof = node.children[i].disproof();
02339                 proof    += randomness.first;
02340                 disproof += randomness.second;
02341                 node.children[i].proof_disproof = ProofDisproof( proof, disproof );
02342               }
02343             }
02344 #endif
02345           }       
02346 #ifdef DISPROOF_AVERAGE
02347           ++frontier_count;
02348           sum_frontier_disproof += node.children[i].proof_disproof.disproof();
02349 #endif
02350         }
02351         // ++node_count;
02352       }
02353 #endif
02354       if (! node.children[i].proof_disproof.isCheckmateFail()) {
02355         node.children_path[i] = path_table->probe(new_key);
02356         if (node.isLoop(i)) {
02357           node.setLoopDetection();
02358           return;
02359         }
02360 #ifdef GRAND_PARENT_SIMULATION
02361         if (grand_parent_simulation && node.children[i].proof_disproof == ProofDisproof(1,1)) {
02362           const Node& gparent = tree->node[tree->depth-2];
02363           size_t gi=std::find(gparent.moves.begin(), gparent.moves.end(), node.moves[i]) - gparent.moves.begin();
02364           if (gi < gparent.moves.size() 
02365               && (
02366 #ifdef MEMORIZE_SOLVED_IN_BITSET
02367                 (gparent.record.solved & (1ull<<gi))
02368                 || 
02369 #endif
02370                 gparent.children[gi].proof_disproof.isCheckmateSuccess())) {
02371             grandParentSimulation<P>(i, gparent, gi);
02372             if (node.children[i].proof_disproof.isCheckmateSuccess())
02373               node.setCheckmateChildInDefense(i);
02374           }
02375         }
02376 #endif
02377       }
02378       if (node.children[i].proof_disproof.isCheckmateFail()) {
02379         tree->setNoCheckmateDefense(P, i);
02380         table->store(node.hash_key, record);
02381         return;
02382       }
02383 #ifdef AGGRESSIVE_FIND_DAG2
02384       if (!node.children[i].proof_disproof.isFinal()
02385           && std::max(node.children[i].proof(),node.children[i].disproof()) >= DagFindThreshold2
02386           && node.children[i].last_move.isNormal()
02387           && node.children[i].last_move != node.moves[i]) {
02388         findDagSource(node.hashes[i], node.children[i],
02389                       node.nextWhiteStand(alt(P), node.moves[i]));
02390       }
02391 #endif
02392     }
02393     if (record.need_full_width==1) {
02394       record.need_full_width++;
02395       for (size_t i=0;i <node.moves.size(); ++i) {
02396         if (
02397 #ifdef MEMORIZE_SOLVED_IN_BITSET
02398           ((record.solved & (1ull<<i))
02399            || (i >= 64 && node.children[i].proof_disproof.isCheckmateSuccess()))
02400 #else
02401           node.children[i].proof_disproof.isCheckmateSuccess()
02402 #endif
02403             
02404             && node.moves[i].isDrop()) {
02405           blockingSimulation<P>(i, ProofOracle(node.hash_key.newHashWithMove(node.moves[i]), 
02406                                                node.nextWhiteStand(alt(P), node.moves[i])));
02407         }
02408       }
02409     }
02410   }
02411   assert(node.children.size() == node.moves.size());
02412 
02413   // hereafter, call leaveWorking before return
02414   if (parallel_shared)
02415     table->setWorking(node.hash_key, record, thread_id);
02416 
02417   // for dag analyses
02418   const Move recorded_last_move = node.moved;
02419   record.last_move = node.moved;
02420 
02421 #ifdef DISPROOF_AVERAGE
02422   const size_t disproof_average = frontier_count ? sum_frontier_disproof/frontier_count : 1;
02423 #else
02424   const size_t disproof_average = 1;
02425 #endif
02426   // main loop
02427 #ifdef DFPN_DEBUG
02428   if (std::find(debug_node.begin(), debug_node.end(), node_id_table.id(node.hash_key))
02429       != debug_node.end() && timer > debug_time_start)
02430     tree->dump(__LINE__);
02431 #endif
02432   CArray<char,DfpnMaxUniqMoves> target;
02433   for (int loop=0; true; ++loop) {
02434     std::fill(target.begin(), target.begin()+(int)node.moves.size(), false);
02435     unsigned int min_disproof=record.min_pdp, min_disproof2=record.min_pdp;
02436     size_t sum_proof = 0, max_upward_proof = 0, max_drop_proof = 0, next_i=node.children.size();
02437     size_t max_proof_dag = 0;
02438     int max_children_depth = 0, upward_count = 0;
02439 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02440     size_t max_proof = 0;
02441     bool false_branch_candidate = !record.false_branch;
02442 #endif
02443     for (size_t i=0; i<node.children.size(); ++i) {
02444 #ifdef MEMORIZE_SOLVED_IN_BITSET
02445       if (record.solved & (1ull << i))
02446         continue;
02447 #endif
02448       if (i > 0 && min_disproof < ProofDisproof::DISPROOF_LIMIT
02449           && node.moves[i].fromTo() == node.moves[i-1].fromTo()
02450           && ! node.moves[i].isDrop()) {
02451         // ignore a no-promote move until it becomes the last one, if there is the corresponding promote move  
02452         assert(node.moves[i].ptype() == node.moves[i-1].oldPtype());
02453         continue;
02454       }
02455       size_t disproof = node.children[i].disproof();
02456       size_t proof = node.children[i].proof();
02457       if (node.children[i].proof_disproof.isCheckmateFail()) {
02458         // simulation で表を読んだら更新されていた等
02459         assert(! node.children[i].proof_disproof.isLoopDetection());
02460         tree->setNoCheckmateDefense(P, i);
02461         table->store(node.hash_key, record, thread_id);
02462         if (parallel_shared)
02463           parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02464         return;
02465       }      
02466 #ifdef OSL_DFPN_SMP
02467       if (proof && disproof) {
02468         if (parallel_shared && node.children[i].working_threads) {
02469           // disproof += misc::BitOp::countBit(node.children[i].working_threads)/2+1;
02470           disproof += misc::BitOp::countBit(node.children[i].working_threads);
02471         }
02472       }
02473 #endif
02474       if (node.children_path[i]) {
02475         if (node.isLoop(i)) {
02476           node.setLoopDetection();
02477           if (parallel_shared)
02478             table->leaveWorking(node.hash_key, thread_id);
02479           return;
02480         }
02481         if (! node.children[i].proof_disproof.isFinal()) {
02482           max_children_depth = std::max(max_children_depth, node.children_path[i]->distance);
02483 #ifdef IGNORE_MONSTER_CHILD
02484             if (node.children_path[i]->distance <= node.path_record->distance
02485                 && (! record.need_full_width || min_disproof < ProofDisproof::DISPROOF_LIMIT) // todo: this condition is not accurate 
02486                 && node.children[i].proof_disproof.proof() >= node.threshold.proof()
02487                 && node.threshold.proof() > IgnoreUpwardProofThreshold) {
02488               false_branch_candidate = false;
02489               continue;         // ignore upward move with too much pdp, untill it becomes the last one
02490             }
02491             else
02492 #endif
02493 #ifdef NAGAI_DAG_TEST
02494           if (record.dag_moves & (1ull << i)) {
02495             max_proof_dag = std::max(max_proof_dag, proof);
02496             proof = 0;
02497           }
02498           else
02499 #endif
02500 #ifdef DELAY_UPWARD
02501             if (node.children_path[i]->distance <= node.path_record->distance) {
02502               max_upward_proof = std::max(max_upward_proof , proof);
02503               ++upward_count;
02504               proof = UpwardWeight;
02505             }
02506             else
02507 #endif
02508             if (node.moves[i].isDrop() && !tree->state.hasEffectAt(alt(P), node.moves[i].to())) {
02509               max_drop_proof = std::max(max_drop_proof, proof);
02510               proof = SacrificeBlockCount;
02511             }
02512         }
02513       }
02514       else {
02515         max_children_depth = node.path_record->distance+1;
02516       }
02517       target[i] = true;
02518       if (disproof < min_disproof
02519           || (disproof == min_disproof && proof && proof < node.children[next_i].proof())) {
02520         min_disproof2 = min_disproof;
02521         min_disproof = disproof;
02522         next_i = i;
02523       } else if (disproof < min_disproof2) {
02524         min_disproof2 = disproof;
02525       }
02526 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02527       if (false_branch_candidate && ! node.children[i].proof_disproof.isFinal()
02528           && (node.children[i].node_count == 0
02529               || ! node.children[i].best_move.isNormal()
02530               || ! (node.moves[i].ptype() == KING && ! node.moves[i].isCapture())))
02531         false_branch_candidate = false;
02532       max_proof = std::max(max_proof, proof);
02533 #endif
02534       sum_proof += proof;
02535     }
02536 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02537     if (false_branch_candidate) {
02538       record.false_branch = true;
02539       HashKey goal;
02540       for (size_t i=0; i<node.children.size(); ++i) {
02541         if (! target[i])
02542           continue;
02543         HashKey key = node.hashes[i];
02544         key = key.newHashWithMove(node.children[i].best_move);
02545         if (goal == HashKey()) {
02546           goal = key;
02547           continue;
02548         }
02549         if (goal != key) {
02550           record.false_branch = false;
02551           break;
02552         }
02553       }
02554     }
02555     if (record.false_branch)
02556       sum_proof = max_proof;
02557 #endif
02558     sum_proof += max_drop_proof + max_proof_dag;
02559     if (SacrificeBlockCount && max_drop_proof) 
02560       sum_proof -= SacrificeBlockCount;
02561     if (upward_count) {
02562       if (sum_proof == 0)
02563         sum_proof = std::max(sum_proof, max_upward_proof);
02564     }
02565     if (node.path_record->distance >= max_children_depth) {
02566       node.path_record->distance = max_children_depth-1;
02567     }
02568     if (min_disproof >= ProofDisproof::DISPROOF_MAX) {
02569       assert(! record.need_full_width);
02570       record.proof_disproof = ProofDisproof(1,1);
02571       record.need_full_width = 1;
02572       table->store(node.hash_key, record, thread_id);
02573       return;
02574     }
02575 #ifdef KISHIMOTO_WIDEN_THRESHOLD
02576     if (loop == 0 && sum_proof >= node.threshold.proof() && sum_proof > IgnoreUpwardProofThreshold)
02577       node.threshold = ProofDisproof(sum_proof+1, node.threshold.disproof());
02578 #endif
02579 #ifdef ADHOC_SUM_RESTRICTION
02580     if (sum_proof < ROOT_PROOF_TOL && min_disproof > 0 && sum_proof > min_disproof*AdHocSumScale) {
02581       sum_proof = min_disproof*AdHocSumScale
02582         + slow_increase(sum_proof-min_disproof*AdHocSumScale);
02583     }
02584 #endif
02585     if (min_disproof >= node.threshold.disproof()
02586         || sum_proof >= node.threshold.proof()
02587         || next_i >= node.children.size()
02588         || node_count + sum_proof >= node_count_limit) {
02589       record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
02590       if (record.proof_disproof.isLoopDetection())
02591         node.setLoopDetection();
02592       else if (record.proof_disproof.isCheckmateSuccess()) {
02593         if (blocking_verify && ! record.need_full_width) {
02594           // read again with full move generation
02595           record.need_full_width = 1;
02596           record.proof_disproof = ProofDisproof(1,1);
02597           table->store(node.hash_key, record, thread_id);
02598           return;
02599         }
02600         node.setCheckmateDefense(P, tree->state);
02601       } else if (! record.proof_disproof.isFinal()) {
02602         if (recorded_last_move.isNormal() && recorded_last_move != node.moved
02603             && std::max(record.proof(), record.disproof()) >= DagFindThreshold)
02604           findDagSource();
02605 #ifdef AGGRESSIVE_FIND_DAG
02606         if (std::max(node.children[next_i].proof(), node.children[next_i].disproof()) >= DagFindThreshold
02607             && node.children[next_i].last_move.isNormal()
02608             && node.children[next_i].last_move != node.moves[next_i]) {
02609           findDagSource(node.hashes[next_i], node.children[next_i],
02610                         node.nextWhiteStand(alt(P), node.moves[next_i]));
02611           node.children[next_i].last_move = node.moves[next_i];
02612           table->store(node.hashes[next_i], node.children[next_i]);
02613         }
02614 #endif
02615       }
02616       record.node_count += node_count - node_count_org;
02617       table->store(node.hash_key, record, thread_id);
02618       node.path_record->node_count = record.node_count;
02619       if (parallel_shared && record.proof_disproof.isFinal())
02620         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02621       return;
02622     }
02623 #ifdef MEMORIZE_SOLVED_IN_BITSET
02624     assert(! (record.solved & (1ull << next_i)));
02625 #endif
02626     record.best_move = node.moves[next_i];
02627     tree->newVisit(alt(P), node.moves[next_i], node.hashes[next_i]);
02628     Node& next = tree->node[tree->depth+1];
02629     unsigned int proof_c = node.threshold.proof()
02630       - (sum_proof - node.children[next_i].proof());
02631 #ifdef ADHOC_SUM_RESTRICTION
02632     if (proof_c > node.threshold.proof())
02633       proof_c = node.children[next_i].proof()
02634         + (node.threshold.proof() - sum_proof);
02635 #endif
02636     next.threshold = ProofDisproof(proof_c,
02637                                    std::min(min_disproof2+disproof_average,
02638                                             (size_t)node.threshold.disproof()));
02639     CallAttack<P> helper(this);
02640     tree->depth += 1;
02641     next.path.pushMove(node.moves[next_i]);
02642     tree->state.makeUnmakeMove(Player2Type<PlayerTraits<P>::opponent>(), node.moves[next_i], helper);
02643     tree->depth -= 1;
02644     if (parallel_shared && parallel_shared->data[thread_id].restart) {
02645       if (tree->depth == 0)
02646         parallel_shared->data[thread_id].clear();
02647       else {
02648         if (parallel_shared->data[thread_id].restart_key == node.hash_key) {
02649           record = table->probe<P>(node.hash_key, node.white_stand);
02650           assert(record.proof_disproof.isFinal());
02651           parallel_shared->data[thread_id].clear();
02652         }
02653         table->leaveWorking(node.hash_key, thread_id);
02654         return;
02655       }
02656     } 
02657 
02658     node.children[next_i] = next.record;
02659     node.children_path[next_i] = next.path_record;
02660     if (next.record.proof_disproof.isCheckmateFail()) {
02661       if (record.proof_disproof.isLoopDetection())
02662         node.setLoopDetection();
02663       else
02664         tree->setNoCheckmateDefense(P, next_i);
02665       record.node_count += node_count - node_count_org;
02666       table->store(node.hash_key, record, thread_id);
02667       node.path_record->node_count = record.node_count;
02668       if (parallel_shared && record.proof_disproof.isFinal())
02669         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02670       return;
02671     }      
02672     if (next.record.proof_disproof.isCheckmateSuccess())
02673       node.setCheckmateChildInDefense(next_i);
02674     if (node_count >= node_count_limit) {
02675       record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
02676       record.node_count += node_count - node_count_org;
02677       table->store(node.hash_key, record, thread_id);
02678       node.path_record->node_count = record.node_count;
02679       if (parallel_shared && record.proof_disproof.isFinal())
02680         parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02681       return;
02682     }
02683     if (next.moved.isDrop() && next.record.proof_disproof.isCheckmateSuccess()) {
02684       blockingSimulation<P>(next_i, ProofOracle(next.hash_key, next.white_stand));
02685     }
02686   }
02687 }
02688 
02689 #if (!defined MINIMAL) || (defined DFPNSTATONE)
02690 void osl::checkmate::
02691 Dfpn::analyze(const PathEncoding& path_src, 
02692               const NumEffectState& src, const vector<Move>& moves) const
02693 {
02694   NumEffectState state(src);
02695   HashKey key(state);
02696   PathEncoding path(path_src);
02697   for (size_t i=0; i<moves.size(); ++i) {
02698     if (! state.isAlmostValidMove(moves[i]))
02699       break;
02700     state.makeMove(moves[i]);
02701     key = key.newMakeMove(moves[i]);
02702     path.pushMove(moves[i]);
02703     DfpnRecord record = table->probe(key, PieceStand(WHITE, state));
02704     const DfpnPathRecord *path_record = path_table->probe(key);
02705     std::cerr << i << ' ' << moves[i] << " " << path
02706               << ' ' << record::csa::show(record.best_move) << "\n";
02707     std::cerr << "  " << record.proof_disproof << ' ' << record.node_count;
02708     if (path_record) {
02709       std::cerr << " distance " << path_record->distance << " twins";
02710       for (SimpleTwinList::const_iterator p=path_record->twin_list.begin();
02711            p!=path_record->twin_list.end(); ++p) {
02712         std::cerr << ' ' << *p;
02713       }
02714     }
02715     std::cerr << "\n";
02716     DfpnMoveVector moves;
02717     if (state.turn() == table->attack()) {
02718       bool has_pawn_checkmate=false;
02719       if (state.turn() == BLACK)
02720         generateCheck<BLACK>(state, moves, has_pawn_checkmate);
02721       else
02722         generateCheck<WHITE>(state, moves, has_pawn_checkmate);
02723     }    
02724     else {
02725       const Square grand_parent_delay_last_to
02726         = (record.last_to != state.kingSquare(state.turn())) ? record.last_to : Square();
02727       if (state.turn() == BLACK)
02728         generateEscape<WHITE>(state, true, grand_parent_delay_last_to, moves);
02729       else
02730         generateEscape<BLACK>(state, true, grand_parent_delay_last_to, moves);
02731     }
02732     for (size_t i=0; i<moves.size(); ++i) {
02733       const Move m = moves[i];
02734       std::cerr << "    " << m;
02735       DfpnRecord child = table->probe(key.newMakeMove(m), 
02736                                       PieceStand(WHITE, state).nextStand(WHITE, m));
02737       std::cerr << ' ' << child.proof_disproof << ' ' << child.node_count;
02738       const DfpnPathRecord *child_path_record = path_table->probe(key.newMakeMove(m));
02739       if (child_path_record) {
02740         std::cerr << " d " << child_path_record->distance << " twins";
02741         BOOST_FOREACH(const PathEncoding& path, child_path_record->twin_list) {
02742           std::cerr << ' ' << path;
02743         }
02744       }
02745       if (record.dag_moves & (1ull << i))
02746         std::cerr << " (*)";
02747       std::cerr << "\n";
02748     }
02749   }
02750   std::cerr << state;
02751 }
02752 #endif
02753 /* ------------------------------------------------------------------------- */
02754 template <osl::Player P, bool UseTable>
02755 struct osl::checkmate::Dfpn::CallProofOracleAttack
02756 {
02757   Dfpn *search;
02758   ProofOracle oracle;
02759   int proof_limit;
02760   CallProofOracleAttack(Dfpn *s, const ProofOracle& o, int pl) : search(s), oracle(o), proof_limit(pl)
02761   {
02762   }
02763   void operator()(Square) const 
02764   {
02765     search->proofOracleAttack<P,UseTable>(oracle, proof_limit);
02766   }
02767 };
02768 
02769 template <osl::Player P, bool UseTable>
02770 struct osl::checkmate::Dfpn::CallProofOracleDefense
02771 {
02772   Dfpn *search;
02773   ProofOracle oracle;
02774   int proof_limit;
02775   CallProofOracleDefense(Dfpn *s, const ProofOracle& o, int pl) : search(s), oracle(o), proof_limit(pl)
02776   {
02777   }
02778   void operator()(Square) const 
02779   {
02780     search->proofOracleDefense<P,UseTable>(oracle, proof_limit);
02781   }
02782 };
02783 
02784 template <osl::Player P, bool UseTable> 
02785 void osl::checkmate::
02786 Dfpn::proofOracleAttack(const ProofOracle& key, int proof_limit)
02787 {
02788 #ifdef DFPN_DEBUG
02789   Tree::Logging logging(tree.get(), table, UseTable ? "tpatta" : "pattac");
02790 #endif
02791   assert(! tree->inCheck(alt(P)));
02792   const int my_distance = (UseTable && tree->depth) ? (tree->node[tree->depth-1].path_record->distance+1) : 0;
02793   Node& node = tree->node[tree->depth];
02794   DfpnRecord& record = node.record;
02795   LoopToDominance loop;
02796   DfpnVisitLock<UseTable> lk((node.path_record = (UseTable ? path_table->allocate<P>(node.hash_key, my_distance, loop) : 0)));
02797   if (UseTable && loop == BadAttackLoop) {
02798     record = DfpnRecord();
02799     node.setLoopDetection();
02800     return;
02801   }
02802   assert(node.white_stand == PieceStand(WHITE, tree->state));
02803   const size_t node_count_org = node_count++;
02804   if (node_count_limit == root_proof_simulation_limit
02805       && node_count > 100000) {
02806     std::cerr << "dfpn proof simulation > 100000 throw " << thread_id << "\n";
02807     throw DepthLimitReached();
02808   }
02809   assert(tree->depth + 2 < tree->MaxDepth);
02810   if (tree->depth + 2 >= tree->MaxDepth) {
02811     std::cerr << "throw " << thread_id << "\n";
02812     throw DepthLimitReached();
02813   }
02814   record = table->probe<P>(node.hash_key, node.white_stand);
02815   if (record.proof_disproof.isFinal())
02816     return;
02817 #if (defined CHECKMATE_A3_SIMULLATION) || (defined CHECKMATE_A3)
02818   if (record.node_count == 0)
02819   {
02820 #ifdef DFPN_STAT
02821     static stat::Ratio oracle_success("a3-simulation");
02822 #endif
02823     FixedDepthSearcher fixed_searcher(tree->state);
02824     PieceStand proof_pieces;
02825     ProofDisproof pdp = fixed_searcher.hasCheckmateMove<P>(2, record.best_move, proof_pieces);
02826     ++node_count;
02827 #ifdef DFPN_STAT
02828     oracle_success.add(pdp.isCheckmateSuccess());
02829 #endif
02830     if (pdp.isCheckmateSuccess()) {
02831       record.proof_disproof = pdp;
02832       record.setProofPieces(proof_pieces);
02833       record.node_count++;
02834       return;
02835     }
02836   }
02837 #elif (!defined CHECKMATE_D2) && (!defined NO_IMMEDIATE_CHECKMATE)
02838   if (! tree->inCheck(P)
02839       && ImmediateCheckmate::hasCheckmateMove<P>(tree->state, record.best_move)) {
02840     PieceStand proof_pieces;    // Note: ImmediateCheckmate が合駒が必要な王手を使わないことに依存
02841     if (record.best_move.isDrop())
02842       proof_pieces.add(record.best_move.ptype());
02843     record.setProofPieces(proof_pieces);
02844     record.proof_disproof = ProofDisproof::Checkmate();
02845     return;
02846   }
02847 #endif
02848 #ifdef DFPN_DEBUG
02849   if (tree->depth > 1000) {
02850     std::cerr << tree->state;
02851     node.hash_key.dumpContents(std::cerr);
02852     std::cerr << "\n";
02853     table->showProofOracles<P>(key.key, key.white_stand, node.moved);
02854   }
02855 #endif
02856   DfpnRecord oracle = table->findProofOracle<P>(key.key, key.white_stand, node.moved);
02857   if (! oracle.proof_disproof.isCheckmateSuccess() || ! oracle.best_move.isNormal())
02858     return;
02859   const Move check_move = OracleAdjust::attack(tree->state, oracle.best_move);
02860   if (! check_move.isNormal() || ! key.traceable(P, check_move))
02861     return;
02862   
02863   node.allocate(1);
02864   node.moves.clear();
02865   node.moves.push_back(check_move);
02866   const HashKey new_key = node.hash_key.newHashWithMove(node.moves[0]);
02867   if (UseTable) {
02868     node.children[0] = table->probe<P>(new_key, node.nextWhiteStand(P, node.moves[0]));
02869     node.children_path[0] = path_table->probe(new_key);
02870     if (node.isLoop(0))
02871       return;
02872   } else {
02873     node.children[0] = DfpnRecord();
02874     node.children_path[0] = 0;
02875   }
02876 
02877   if (! UseTable || ! node.children[0].proof_disproof.isFinal()) {
02878     Node& next = tree->node[tree->depth+1];
02879     tree->newVisit(P, node.moves[0], new_key);
02880 
02881     CallProofOracleDefense<P,UseTable> helper(this, key.newOracle(P, check_move), proof_limit);
02882     tree->depth += 1;
02883     next.path.pushMove(next.moved);
02884     tree->state.makeUnmakeMove(Player2Type<P>(), next.moved, helper);
02885     tree->depth -= 1;
02886     node.children[0] = next.record;
02887     node.children_path[0] = next.path_record;
02888 
02889     if (next.record.proof_disproof == ProofDisproof::NoEscape()
02890         && next.moved.isDrop() && next.moved.ptype() == PAWN)
02891       node.children[0].proof_disproof = ProofDisproof::PawnCheckmate();
02892   }  
02893   if (node.children[0].proof_disproof.isCheckmateSuccess()) {
02894     node.setCheckmateAttack(P,0);
02895     record.node_count += node_count - node_count_org;
02896     if (UseTable || node_count - node_count_org > 32) {
02897       record.last_move = node.moved;
02898       table->store(node.hash_key, record);
02899     }
02900   }
02901   else if (UseTable) {
02902     // dag analyses
02903     if (record.last_move.isNormal() && record.last_move != node.moved
02904         && std::max(record.proof(), record.disproof()) >= 128)
02905       findDagSource();
02906     record.last_move = node.moved;
02907   }
02908 }
02909 
02910 template <osl::Player P, bool UseTable> 
02911 void osl::checkmate::
02912 Dfpn::proofOracleDefense(const ProofOracle& key, int proof_limit)
02913 {
02914 #ifdef DFPN_DEBUG
02915   Tree::Logging logging(tree.get(), table, UseTable ? "tpdefe" : "pdefen");
02916 #endif
02917   const int my_distance = (UseTable && tree->depth) ? (tree->node[tree->depth-1].path_record->distance+1) : 0;
02918   Node& node = tree->node[tree->depth];
02919   LoopToDominance loop;
02920   DfpnVisitLock<UseTable> lk((node.path_record = (UseTable ? path_table->allocate<P>(node.hash_key, my_distance, loop) : 0)));
02921   DfpnRecord& record = node.record;
02922   if (UseTable && loop == BadAttackLoop) {
02923     record = DfpnRecord();
02924     node.setLoopDetection();
02925     return;
02926   }
02927   if (! UseTable && tree->depth >= 4) {
02928     if (tree->node[tree->depth-4].hash_key == node.hash_key
02929         || (tree->depth >= 6 && tree->node[tree->depth-6].hash_key == node.hash_key)) {
02930       record = DfpnRecord();
02931       return;
02932     }
02933   }
02934   const size_t node_count_org = node_count++;
02935   assert(node.white_stand == PieceStand(WHITE, tree->state));
02936   if (! tree->inCheck(alt(P)) || tree->inCheck(P)) {
02937     record = DfpnRecord();
02938     record.proof_disproof = ProofDisproof::NoCheckmate();
02939     return;
02940   }
02941 
02942   record = table->probe<P>(node.hash_key, node.white_stand);
02943   if (record.proof_disproof.isFinal())
02944     return;
02945   if (proof_limit > ProofSimulationTolerance)
02946     proof_limit = ProofSimulationTolerance;
02947   // move generation
02948   const bool grand_parent_simulation = grandParentSimulationSuitable();
02949   if (record.last_to == Square())
02950     record.last_to = grand_parent_simulation ? node.moved.to() : tree->king(alt(P)).square();
02951   const Square grand_parent_delay_last_to
02952     = (record.last_to != tree->king(alt(P)).square()) ? record.last_to : Square();
02953   generateEscape<P>(tree->state, true, grand_parent_delay_last_to, node.moves);
02954   if (node.moves.empty()) {
02955     record.setProofPieces(ProofPieces::leaf(tree->state, P, record.stands[P]));
02956     record.proof_disproof = ProofDisproof::NoEscape();
02957     return;
02958   }
02959 
02960   // probe all
02961   assert(node.children.empty());
02962   {
02963     node.allocate(node.moves.size());
02964     for (size_t i=0;i <node.moves.size(); ++i) {
02965 #ifdef MEMORIZE_SOLVED_IN_BITSET
02966       if (record.solved & (1ull << i))
02967         continue;
02968 #endif
02969       const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
02970       node.children[i] = UseTable 
02971         ? table->probe<P>(new_key, node.nextWhiteStand(alt(P), node.moves[i])) 
02972         : DfpnRecord();
02973       if (node.children[i].proof_disproof.isCheckmateSuccess()) {
02974         node.setCheckmateChildInDefense(i);
02975       }
02976 #ifdef CHECKMATE_D2
02977       else if (node.record.node_count == 0 && node.children[i].node_count == 0) {
02978         FixedDepthSearcher fixed_searcher(tree->state);
02979         PieceStand proof_pieces;
02980         Move check_move;
02981         node.children[i].proof_disproof
02982           = fixed_searcher.hasEscapeByMove<P>(node.moves[i], 0, check_move, proof_pieces);
02983         if (node.children[i].proof_disproof.isCheckmateSuccess()) {       
02984           node.children[i].best_move = check_move;
02985           node.children[i].setProofPieces(proof_pieces);
02986           node.setCheckmateChildInDefense(i);
02987         }
02988         else {
02989           if (node.children[i].proof_disproof.isCheckmateFail())
02990             node.children[i].proof_disproof = ProofDisproof(1,1);
02991         }
02992         ++node_count;
02993       }
02994 #endif
02995       if (node.children[i].proof_disproof.isCheckmateFail()) {
02996         tree->setNoCheckmateDefense(P, i);
02997         if (UseTable)
02998           table->store(node.hash_key, record);
02999         return;
03000       }      
03001       node.children_path[i] = UseTable ? path_table->probe(new_key) : 0;
03002     }
03003   }
03004   assert(node.children.size() == node.moves.size());
03005   if (UseTable) {
03006     for (size_t i=0; i<node.children.size(); ++i) {
03007       if (node.isLoop(i)) {
03008         node.setLoopDetection();
03009         return;
03010       }
03011     }
03012   }
03013   unsigned int sum_proof=0, min_disproof=record.min_pdp;
03014   int num_proof = 0;
03015   for (size_t next_i=0; next_i<node.children.size(); ++next_i) {
03016 #ifdef MEMORIZE_SOLVED_IN_BITSET
03017     if (record.solved & (1ull << next_i))
03018       continue;
03019 #endif
03020     if (node.children[next_i].proof_disproof.isCheckmateSuccess()) {
03021       min_disproof = std::min(min_disproof, node.children[next_i].disproof());
03022       continue;
03023     }
03024     if (! key.traceable(alt(P), node.moves[next_i])) {
03025       ++sum_proof;
03026       min_disproof = 1;
03027       if (! UseTable)
03028         break;
03029       continue;
03030     }
03031     const Square next_to = node.moves[next_i].to();
03032     if (sum_proof && tree->state.hasEffectAt(P, next_to)
03033         && (! tree->state.hasEffectAt(alt(P), next_to)
03034             || (tree->state.countEffect(alt(P), next_to) == 1
03035                 && ! node.moves[next_i].isDrop())))
03036       continue;
03037     assert(! node.isLoop(next_i));
03038     Node& next = tree->node[tree->depth+1];
03039 #ifdef MEMORIZE_SOLVED_IN_BITSET
03040     assert(! (record.solved & (1ull << next_i)));
03041 #endif
03042     tree->newVisit(alt(P), node.moves[next_i], node.hashes[next_i]);
03043 
03044     CallProofOracleAttack<P,UseTable> helper(this, key.newOracle(alt(P), node.moves[next_i]), proof_limit-sum_proof);
03045     tree->depth += 1;
03046     next.path.pushMove(node.moves[next_i]);
03047     tree->state.makeUnmakeMove(Player2Type<PlayerTraits<P>::opponent>(), node.moves[next_i], helper);
03048     tree->depth -= 1;
03049 
03050     node.children[next_i] = next.record;
03051     node.children_path[next_i] = next.path_record;
03052     if (next.record.proof_disproof.isCheckmateFail()) {
03053       if (record.proof_disproof.isLoopDetection())
03054         node.setLoopDetection();
03055       else
03056         tree->setNoCheckmateDefense(P, next_i);
03057       record.node_count += node_count - node_count_org;
03058       if (UseTable)
03059         table->store(node.hash_key, record);
03060       return;
03061     }
03062     if (next.record.proof_disproof.isCheckmateSuccess()) {
03063       node.setCheckmateChildInDefense(next_i);
03064       ++num_proof;
03065     }
03066     sum_proof += next.record.proof();
03067     min_disproof = std::min(min_disproof, next.record.disproof());
03068     if ((sum_proof && ! UseTable) || (int)sum_proof > proof_limit)
03069       break;
03070   }
03071   if (sum_proof == 0) {
03072     node.record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
03073     node.setCheckmateDefense(P, tree->state);
03074   }
03075   else if (UseTable) {
03076     // dag analyses
03077     if (record.last_move.isNormal() && record.last_move != node.moved
03078         && std::max(record.proof(), record.disproof()) >= 128)
03079       findDagSource();
03080     record.last_move = node.moved;
03081   }
03082 }
03083 
03084 template <osl::Player P> 
03085 void osl::checkmate::
03086 Dfpn::blockingSimulation(int oracle_i, const ProofOracle& oracle)
03087 {
03088 #ifdef DFPN_DEBUG
03089   Tree::Logging logging(tree.get(), table, "blocks");
03090 #endif
03091 #ifdef DFPN_STAT
03092   static stat::Ratio oracle_success("blocking proof");
03093 #endif
03094   Node& node = tree->node[tree->depth];
03095   Node& next = tree->node[tree->depth+1];
03096   const Move oracle_move = node.moves[oracle_i];
03097   const Square to = oracle_move.to();
03098   assert((node.record.solved & (1ull << oracle_i))
03099          || node.children[oracle_i].proof_disproof.isCheckmateSuccess());
03100   for (size_t i=0; i<node.moves.size(); ++i) {
03101 #ifdef MEMORIZE_SOLVED_IN_BITSET
03102     if (node.record.solved & (1ull << i))
03103       continue;
03104 #endif
03105     if (node.isLoop(i))
03106       break;
03107     if (node.children[i].proof_disproof.isFinal() || node.moves[i].to() != to)
03108       continue;
03109     if (! oracle.traceable(alt(P), node.moves[i]))
03110       continue;
03111 #ifdef MEMORIZE_SOLVED_IN_BITSET
03112     assert(! (node.record.solved & (1ull << i)));
03113 #endif
03114     tree->newVisit(alt(P), node.moves[i], node.hashes[i]);
03115     CallProofOracleAttack<P,true> helper(this, oracle, node.threshold.proof());
03116 
03117     tree->depth += 1;
03118     next.path.pushMove(node.moves[i]);
03119     tree->state.makeUnmakeMove(Player2Type<PlayerTraits<P>::opponent>(), node.moves[i], helper);
03120     tree->depth -= 1;
03121 
03122     node.children[i] = next.record;
03123     node.children_path[i] = next.path_record;    
03124 
03125 #ifdef DFPN_STAT
03126     oracle_success.add(next.record.proof_disproof.isCheckmateSuccess());
03127 #endif
03128     if (next.record.proof_disproof.isCheckmateSuccess()) {
03129       node.setCheckmateChildInDefense(i);
03130     }
03131   }
03132 }
03133 
03134 template <osl::Player P> 
03135 void osl::checkmate::
03136 Dfpn::grandParentSimulation(int cur_i, const Node& gparent, int gp_i)
03137 {
03138 #ifdef DFPN_DEBUG
03139   Tree::Logging logging(tree.get(), table, "grands");
03140 #endif
03141 #ifdef DFPN_STAT
03142   static stat::Ratio oracle_success("grandparent proof", true);
03143 #endif
03144   Node& node = tree->node[tree->depth];
03145   Node& next = tree->node[tree->depth+1];
03146 
03147   const Move move = gparent.moves[gp_i];
03148   assert(move == node.moves[cur_i]);
03149   const HashKey& oracle_hash = (gparent.record.solved & (1ull << gp_i)) 
03150     ? gparent.hash_key.newHashWithMove(move)
03151     : gparent.hashes[gp_i];
03152   const ProofOracle oracle(oracle_hash, gparent.nextWhiteStand(alt(P), move));
03153 
03154   tree->newVisit(alt(P), node.moves[cur_i], node.hashes[cur_i]);
03155   CallProofOracleAttack<P,true> helper(this, oracle, gparent.threshold.proof());
03156 
03157   tree->depth += 1;
03158   next.path.pushMove(move);
03159   tree->state.makeUnmakeMove(Player2Type<PlayerTraits<P>::opponent>(), move, helper);
03160   tree->depth -= 1;
03161 
03162   node.children[cur_i] = next.record;
03163   node.children_path[cur_i] = next.path_record;    
03164 #ifdef DFPN_STAT
03165   oracle_success.add(next.record.proof_disproof.isCheckmateSuccess());
03166 #endif
03167 }
03168 
03169 // debug
03170 int osl::checkmate::
03171 Dfpn::distance(const HashKey& key) const
03172 {
03173   const DfpnPathRecord *record = path_table->probe(key);
03174   if (record)
03175     return record->distance;
03176   return -1;
03177 }
03178 
03179 /* ------------------------------------------------------------------------- */
03180 // ;;; Local Variables:
03181 // ;;; mode:c++
03182 // ;;; c-basic-offset:2
03183 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines