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