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