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