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