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