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/apply_move/applyMoveWithPath.h"
00022 #include "osl/record/csa.h"
00023 #include "osl/container/moveVector.h"
00024 #ifdef USE_TBB_HASH
00025 # include <cstring>
00026 # include <tbb/concurrent_hash_map.h>
00027 #endif
00028 #include "osl/stl/hash_map.h"
00029 #include "osl/stl/vector.h"
00030 #include "osl/stl/slist.h"
00031 #include "osl/stat/ratio.h"
00032 #include "osl/misc/align16New.h"
00033 #include "osl/oslConfig.h"
00034 #include <boost/tuple/tuple.hpp>
00035 #include <boost/tuple/tuple_comparison.hpp>
00036 #include <iostream>
00037 #include <iomanip>
00038 #include <bitset>
00039
00040
00041
00042 #define GRAND_PARENT_SIMULATION
00043 #define GRAND_PARENT_DELAY
00044
00045 #define INITIAL_DOMINANCE
00046
00047 #define ROOT_PROOF_TOL 65536ul*1024
00048
00049 #define ROOT_DISPROOF_TOL 65536ul*1024
00050
00051
00052 #define CHECKMATE_D2
00053
00054 #define PROOF_AVERAGE
00055 #define DISPROOF_AVERAGE
00056
00057 #define KAKINOKI_FALSE_BRANCH_SEARCH
00058 #define IGNORE_MONSTER_CHILD
00059 #define KISHIMOTO_WIDEN_THRESHOLD
00060 #define CHECKMATE_A3_GOLD
00061 #define CHECKMATE_A3_SIMULLATION
00062
00063
00064 #define MEMORIZE_SOLVED_IN_BITSET
00065
00066
00067
00068 static const int UpwardWeight = 2, SacrificeBlockCount = 0, LongDropCount = 1;
00069 static const int MaxDagTraceDepth = 64;
00070 static const unsigned int NoPromoeIgnoreProofThreshold = 100;
00071 static const unsigned int NoPromoeIgnoreDisproofThreshold = 200;
00072 static const unsigned int IgnoreUpwardProofThreshold = 100;
00073 static const unsigned int IgnoreUpwardDisproofThreshold = 100;
00074 #ifdef MEMORIZE_SOLVED_IN_BITSET
00075 static const unsigned int InitialDominanceProofMax = 35;
00076 #else
00077 static const unsigned int InitialDominanceProofMax = 20;
00078 #endif
00079 static const unsigned int InitialDominanceDisproofMax = 110;
00080
00081
00082
00083 #ifndef NDEBUG
00084 static size_t timer = 0;
00085 const size_t debug_time_start = 3851080;
00086 #endif
00087
00088
00089 namespace osl
00090 {
00091 namespace checkmate
00092 {
00093 #ifdef DFPN_DEBUG
00094 struct NodeIDTable : public hash_map<HashKey, int>
00095 {
00096 size_t cur;
00097 NodeIDTable() : cur(0) {}
00098 int id(const HashKey& key)
00099 {
00100 int& ret = (*this)[key];
00101 if (ret == 0)
00102 ret = ++cur;
00103 return ret;
00104 }
00105 } node_id_table;
00106 CArray<int,3> debug_node = {{
00107 }};
00109 struct NodeCountTable : public hash_map<int, std::pair<int,vector<Move> > >
00110 {
00111 typedef std::pair<int,vector<Move> > pair_t;
00112 ~NodeCountTable()
00113 {
00114 std::cerr << "timer " << timer << "\n";
00115 vector<std::pair<int,int> > all;
00116 all.reserve(size());
00117 BOOST_FOREACH(const value_type& v, *this)
00118 all.push_back(std::make_pair(-v.second.first, v.first));
00119 std::sort(all.begin(), all.end());
00120 for (size_t i=0; i<std::min((size_t)10, size()); ++i){
00121 std::cerr << "freq " << -all[i].first << " id " << std::setw(5) << all[i].second << ' ';
00122 BOOST_FOREACH(Move m, (*this)[all[i].second].second)
00123 std::cerr << record::csa::show(m);
00124 std::cerr << "\n";
00125 }
00126 }
00127 } node_count_table;
00128 #endif
00129
00130 struct SimpleTwinList : slist<PathEncoding
00131 #ifdef USE_BOOST_POOL_ALLOCATOR
00132 , osl::stl::fast_pool_allocator<PathEncoding>
00133 #endif
00134 >
00135 {
00136 };
00137
00138 struct DfpnPathRecord
00139 {
00140 static const int MaxDistance = 1024*128;
00141 bool visiting;
00143 int distance;
00144 SimpleTwinList twin_list;
00145 DfpnPathRecord() : visiting(false), distance(MaxDistance)
00146 {
00147 }
00148 };
00149 template <bool Enabled=true>
00150 struct DfpnVisitLock : boost::noncopyable
00151 {
00152 DfpnPathRecord *record;
00153 DfpnVisitLock(DfpnPathRecord *r) : record(r)
00154 {
00155 if (! Enabled) return;
00156 assert(! record->visiting);
00157 record->visiting = true;
00158 }
00159 ~DfpnVisitLock()
00160 {
00161 if (! Enabled) return;
00162 assert(record->visiting);
00163 record->visiting = false;
00164 }
00165 };
00166 enum LoopToDominance { NoLoop=0, BadAttackLoop };
00167 struct DfpnPathList : public slist<std::pair<PieceStand, DfpnPathRecord>
00168 #ifdef USE_BOOST_POOL_ALLOCATOR
00169 , osl::stl::fast_pool_allocator<std::pair<PieceStand,DfpnPathRecord> >
00170 #endif
00171 >
00172 {
00173 private:
00174 template <Player Attack>
00175 iterator find(PieceStand black, LoopToDominance& loop)
00176 {
00177 loop = NoLoop;
00178 iterator ret = end();
00179 for (iterator p=begin(); p!=end(); ++p) {
00180 if (p->first == black) {
00181 assert(p->second.distance != DfpnPathRecord::MaxDistance);
00182 ret = p;
00183 if (loop || p->second.visiting) break;
00184 }
00185 if (! p->second.visiting)
00186 continue;
00187 if (p->first.isSuperiorOrEqualTo(black)) {
00188 if (Attack == BLACK) {
00189 loop = BadAttackLoop;
00190 if (ret != end()) break;
00191 }
00192 }
00193 else if (black.isSuperiorOrEqualTo(p->first)) {
00194 if (Attack == WHITE) {
00195 loop = BadAttackLoop;
00196 if (ret != end()) break;
00197 }
00198 }
00199 }
00200 return ret;
00201 }
00202 public:
00203 template <Player Attack>
00204 DfpnPathRecord *allocate(PieceStand black, int depth, LoopToDominance& loop)
00205 {
00206 iterator ret = find<Attack>(black, loop);
00207 if (ret != end()) {
00208 ret->second.distance = std::min(depth, ret->second.distance);
00209 return &(ret->second);
00210 }
00211 push_front(std::make_pair(black, DfpnPathRecord()));
00212 DfpnPathRecord *record = &(begin()->second);
00213 assert(record->distance == DfpnPathRecord::MaxDistance);
00214 record->distance = depth;
00215 return record;
00216 }
00217 const DfpnPathRecord *probe(PieceStand black) const
00218 {
00219 BOOST_FOREACH(const value_type& v, *this) {
00220 if (v.first == black)
00221 return &(v.second);
00222 }
00223 return 0;
00224 }
00225 };
00226 class DfpnPathTable
00227 {
00228 typedef hash_map<BoardKey, DfpnPathList
00229 # ifdef USE_BOOST_POOL_ALLOCATOR
00230 , osl::stl::hash<BoardKey>
00231 , std::equal_to<BoardKey>
00232 , osl::stl::fast_pool_allocator<std::pair<const BoardKey,DfpnPathList> >
00233 # endif
00234 > table_t;
00235 table_t table;
00236 public:
00237 template <Player Attack>
00238 DfpnPathRecord *allocate(const HashKey& key, int depth, LoopToDominance& loop)
00239 {
00240 DfpnPathList& l = table[key.boardKey()];
00241 return l.allocate<Attack>(key.blackStand(), depth, loop);
00242 }
00243 const DfpnPathRecord *probe(const HashKey& key) const
00244 {
00245 table_t::const_iterator p = table.find(key.boardKey());
00246 if (p == table.end())
00247 return 0;
00248 return p->second.probe(key.blackStand());
00249 }
00250 void clear() { table.clear(); }
00251 };
00252
00253 int attackProofCost(Player attacker, const NumEffectState& state, Move move)
00254 {
00255 int proof = 0;
00256 if (move.capturePtype() == PTYPE_EMPTY)
00257 {
00258 const Position from=move.from(), to=move.to();
00259 const int a = (state.countEffect(attacker,to)
00260 + (from.isPieceStand() ? 1 : 0));
00261 int d = state.countEffect(alt(attacker),to);
00262 if (a <= d)
00263 {
00264 const Ptype ptype = move.ptype();
00265 proof = PieceCost::attack_sacrifice_cost[ptype];
00266 if ((d >= 2) && (a == d))
00267 proof /= 2;
00268 }
00269 }
00270 return proof;
00271 }
00272 }
00273 }
00274
00275
00276 struct osl::checkmate::Dfpn::NodeBase
00277 {
00278
00279 HashKey hash_key;
00280 PathEncoding path;
00281 ProofDisproof threshold;
00282 Move moved;
00283 PieceStand white_stand;
00284
00285 DfpnRecord record;
00286 DfpnPathRecord *path_record;
00287 };
00288
00289 struct osl::checkmate::Dfpn::Node : NodeBase
00290 {
00291 DfpnMoveVector moves;
00292 FixedCapacityVector<DfpnRecord,DfpnMaxUniqMoves> children;
00293 FixedCapacityVector<const DfpnPathRecord*,DfpnMaxUniqMoves> children_path;
00294 CArray<HashKey,DfpnMaxUniqMoves> hashes;
00295 FixedCapacityVector<int8_t,DfpnMaxUniqMoves> proof_cost;
00296 size_t visit_time;
00297
00298 const PieceStand nextWhiteStand(Player P, Move move) const
00299 {
00300 assert(move.player() == P);
00301 return (P == WHITE) ? white_stand.nextStand(P, move) : white_stand;
00302 }
00303 void clear()
00304 {
00305 moves.clear();
00306 proof_cost.clear();
00307 children.clear();
00308 children_path.clear();
00309 }
00310 void allocate(int n)
00311 {
00312 while (n--) {
00313 proof_cost.push_back(0);
00314 children.push_back(DfpnRecord());
00315 children_path.push_back(0);
00316 }
00317 }
00318 void setLoopDetection()
00319 {
00320 assert(! (record.proof_disproof.isFinal()
00321 && ! record.proof_disproof.isLoopDetection()));
00322 record.proof_disproof = ProofDisproof(1,1);
00323 path_record->twin_list.push_front(path);
00324 }
00325 const PathEncoding newPath(int c) const
00326 {
00327 PathEncoding n = path;
00328 n.pushMove(moves[c]);
00329 return n;
00330 }
00331 bool isLoop(int c) const
00332 {
00333 if (! children_path[c] || children[c].proof_disproof.isFinal())
00334 return false;
00335 if (children_path[c]->visiting)
00336 return true;
00337 const PathEncoding p = newPath(c);
00338 const SimpleTwinList& tl = children_path[c]->twin_list;
00339 return std::find(tl.begin(), tl.end(), p) != tl.end();
00340 }
00341 void setCheckmateAttack(Player attack, int best_i)
00342 {
00343 DfpnRecord& child = children[best_i];
00344 assert(child.proof_disproof.isCheckmateSuccess());
00345 record.proof_disproof = child.proof_disproof;
00346 record.best_move = moves[best_i];
00347 const PieceStand proof_pieces
00348 = ProofPieces::attack(child.proofPieces(), record.best_move,
00349 record.stands[attack]);
00350 record.setProofPieces(proof_pieces);
00351 }
00352 void setNoCheckmateDefense(Player attack, int best_i)
00353 {
00354 DfpnRecord& child = children[best_i];
00355 assert(child.proof_disproof.isCheckmateFail());
00356 assert(! child.proof_disproof.isLoopDetection());
00357 record.proof_disproof = child.proof_disproof;
00358 record.best_move = moves[best_i];
00359 const PieceStand disproof_pieces
00360 = DisproofPieces::defense(child.disproofPieces(), record.best_move,
00361 record.stands[alt(attack)]);
00362 record.setDisproofPieces(disproof_pieces);
00363 }
00364 void setCheckmateDefense(Player attack, const NumEffectState& state)
00365 {
00366 assert(moves.size());
00367 assert(record.proof_disproof.isCheckmateSuccess());
00368 record.proof_disproof = ProofDisproof::Checkmate();
00369 PieceStand result = record.proof_pieces_candidate;
00370 const Player defender = alt(attack);
00371 if (! effect_util::UnblockableCheck::isMember(defender, state))
00372 ProofPiecesUtil::addMonopolizedPieces(state, attack, record.stands[attack],
00373 result);
00374 record.setProofPieces(result);
00375 }
00376 void setNoCheckmateAttack(Player attack, const NumEffectState& state)
00377 {
00378 assert(moves.size());
00379 assert(record.proof_disproof.isCheckmateFail());
00380 assert(! record.proof_disproof.isLoopDetection());
00381 PieceStand result = record.proof_pieces_candidate;
00382 ProofPiecesUtil::addMonopolizedPieces(state, alt(attack), record.stands[alt(attack)],
00383 result);
00384 record.setDisproofPieces(result);
00385 }
00386 void setCheckmateChildInDefense(size_t i)
00387 {
00388 assert(children[i].proof_disproof.isCheckmateSuccess());
00389 #ifdef MEMORIZE_SOLVED_IN_BITSET
00390 record.solved |= (1ull<<i);
00391 #endif
00392 record.min_pdp = std::min(record.min_pdp, children[i].proof_disproof.disproof());
00393 record.proof_pieces_candidate
00394 = record.proof_pieces_candidate.max(children[i].proofPieces());
00395 }
00396 void setNoCheckmateChildInAttack(size_t i)
00397 {
00398 assert(children[i].proof_disproof.isCheckmateFail());
00399 #ifdef MEMORIZE_SOLVED_IN_BITSET
00400 record.solved |= (1ull<<i);
00401 #endif
00402 record.min_pdp = std::min(record.min_pdp, children[i].proof_disproof.proof());
00403 record.proof_pieces_candidate
00404 = record.proof_pieces_candidate.max(children[i].disproofPieces());
00405 }
00406 };
00407
00408 struct osl::checkmate::Dfpn::Tree
00409 #if OSL_WORDSIZE == 32
00410 : public misc::Align16New
00411 #endif
00412 {
00413 NumEffectState state;
00414 int depth;
00415 Node node[MaxDepth];
00416 Tree() : state(SimpleState(HIRATE))
00417 {
00418 }
00419 bool inCheck(Player P) const
00420 {
00421 return state.inCheck(P);
00422 }
00423 const Piece king(Player P) const { return state.getKingPiece(P); }
00424 void newVisit(Player P, Move move, const HashKey& next_hash)
00425 {
00426 assert(P == move.player());
00427 const Node& node = this->node[depth];
00428 assert(next_hash == node.hash_key.newHashWithMove(move));
00429 Node& next = this->node[depth+1];
00430 next.moved = move;
00431 next.white_stand = node.nextWhiteStand(P, move);
00432 next.path = node.path;
00433 next.clear();
00434 next.hash_key = next_hash;
00435 }
00436 void setNoCheckmateChildInAttack(size_t best_i)
00437 {
00438 Node &node = this->node[depth];
00439 node.setNoCheckmateChildInAttack(best_i);
00440 }
00441 void setNoCheckmateDefense(Player attack, int best_i)
00442 {
00443 Node &node = this->node[depth];
00444 node.setNoCheckmateDefense(attack, best_i);
00445 }
00446 void dump(int lines, int depth=0) const
00447 {
00448 #ifndef NDEBUG
00449 if (depth == 0)
00450 depth = this->depth;
00451 for (int i=0; i<=depth; ++i) {
00452 std::cerr << "history " << i << " " << node[i].moved << " ";
00453 node[i].hash_key.dumpContentsCerr();
00454 std::cerr << "\n";
00455 }
00456 const int my_distance = node[depth].path_record ? node[depth].path_record->distance : -1;
00457 const Node &node = this->node[depth];
00458 std::cerr << "time " << node.visit_time << " (" << timer << ") here " << lines << "\n" << state;
00459 std::cerr << " false-branch? " << (bool)node.record.false_branch << "\n";
00460 #ifdef MEMORIZE_SOLVED_IN_BITSET
00461 std::cerr << " solved " << std::bitset<32>(node.record.solved) << "\n";
00462 #endif
00463 std::cerr << " dags " << std::bitset<32>(node.record.solved) << "\n";
00464 std::cerr << " last_to " << node.record.last_to
00465 << " threshold " << node.threshold
00466 << " my_distance " << my_distance << "\n";
00467 for (size_t i=0; i<node.moves.size(); ++i) {
00468 std::cerr << " " << i << " " << node.moves[i]
00469 << " " << node.children[i].proof_disproof
00470 << " " << (int)node.proof_cost[i]
00471 << " " << node.children[i].best_move
00472 << " depth " << (node.children_path[i] ? node.children_path[i]->distance : -1)
00473 << " count " << node.children[i].node_count
00474 << "\n";
00475 }
00476 std::cerr << node.record.proof_disproof << " " << node.record.best_move << "\n";
00477 std::cerr << "path " << node.path << " twins ";
00478 if (node.path_record) {
00479 BOOST_FOREACH(const PathEncoding& path, node.path_record->twin_list)
00480 std::cerr << path << " ";
00481 }
00482 std::cerr << "\n";
00483 #endif
00484 }
00485 #ifdef DFPN_DEBUG
00486 void showPath(const char *message, size_t table_size) const
00487 {
00488 std::cerr << message << " depth " << depth << " node " << node_id_table.id(node[depth].hash_key)
00489 << " time " << timer << " table " << table_size << ' ';
00490 for (int i=0; i<=depth; ++i)
00491 std::cerr << record::csa::show(node[i].moved);
00492 std::cerr << "\n";
00493 }
00494 struct Logging
00495 {
00496 const Tree *tree;
00497 const DfpnTable *table;
00498 const size_t old_table_size;
00499 Logging(Tree *tr, DfpnTable *tb, const char *message)
00500 : tree(tr), table(tb), old_table_size(table->size())
00501 {
00502 if (timer < debug_time_start)
00503 return;
00504 tree->showPath(message, old_table_size);
00505 }
00506 ~Logging()
00507 {
00508 if (timer < debug_time_start)
00509 return;
00510 const Node& node = tree->node[tree->depth];
00511 const int id = node_id_table.id(node.hash_key);
00512 std::cerr << " node " << id << " " << node.threshold
00513 << " " << node.record.proof_disproof << "\n";
00514 if (std::find(debug_node.begin(), debug_node.end(), id)
00515 != debug_node.end() && timer > debug_time_start)
00516 tree->dump(__LINE__);
00517 if (table->size() == old_table_size)
00518 countImmediateReturns(id);
00519 }
00520 void countImmediateReturns(int id)
00521 {
00522 NodeCountTable::pair_t& p = node_count_table[id];
00523 if (p.first == 0) {
00524 for (int i=0; i<=tree->depth; ++i)
00525 p.second.push_back(tree->node[i].moved);
00526 }
00527 ++(p.first);
00528 }
00529 };
00530 #endif
00531 };
00532
00533
00534 #ifdef DFPN_STAT
00535 osl::CArray<osl::CArray<int,64>,2> count2proof, count2disproof, count2unknown;
00536 #endif
00537
00538 struct osl::checkmate::DfpnTable::List : public slist<DfpnRecord
00539 #ifdef USE_BOOST_POOL_ALLOCATOR
00540 , osl::stl::fast_pool_allocator<DfpnRecord>
00541 #endif
00542 >
00543 {
00544 typedef slist<DfpnRecord
00545 #ifdef USE_BOOST_POOL_ALLOCATOR
00546 , osl::stl::fast_pool_allocator<DfpnRecord>
00547 #endif
00548 > list_t;
00549 #ifdef OSL_DFPN_SMP
00550 mutable Mutex mutex;
00551 #endif
00552 List() {}
00553 List(const List& src) : list_t(src) {}
00554
00555 template <Player Attack>
00556 const DfpnRecord probe(const HashKey& key, PieceStand white_stand) const;
00557 template <Player Attack>
00558 const DfpnRecord findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const;
00559 template <Player Attack>
00560 void showProofOracles(const HashKey& key, PieceStand white_stand, Move last_move) const;
00561 bool store(DfpnRecord& value, int leaving_thread_id)
00562 {
00563 #ifdef USE_TBB_HASH
00564 SCOPED_LOCK(lk,mutex);
00565 #endif
00566 BOOST_FOREACH(DfpnRecord& record, *this) {
00567 if (record.stands[BLACK] == value.stands[BLACK]) {
00568 #ifdef OSL_DFPN_SMP
00569 if (record.proof_disproof.isFinal()) {
00570 value = record;
00571 record.working_threads &= ~(1u << leaving_thread_id);
00572 return false;
00573 }
00574 if (! value.proof_disproof.isFinal()) {
00575 value.min_pdp = std::min(value.min_pdp, record.min_pdp);
00576 value.proof_pieces_candidate
00577 = value.proof_pieces_candidate.max(record.proof_pieces_candidate);
00578 value.dag_moves |= record.dag_moves;
00579 value.solved |= record.solved;
00580 value.false_branch |= record.false_branch;
00581 }
00582 value.working_threads = record.working_threads;
00583 if (leaving_thread_id >= 0) {
00584 assert(value.working_threads & (1u << leaving_thread_id));
00585 value.working_threads &= ~(1u << leaving_thread_id);
00586 }
00587 #endif
00588 record = value;
00589 return false;
00590 }
00591 }
00592 value.working_threads &= ~(1u << leaving_thread_id);
00593 push_front(value);
00594 return true;
00595 }
00596 void addDag(DfpnRecord& value)
00597 {
00598 #ifdef USE_TBB_HASH
00599 SCOPED_LOCK(lk,mutex);
00600 #endif
00601 BOOST_FOREACH(DfpnRecord& record, *this) {
00602 if (record.stands[BLACK] == value.stands[BLACK]) {
00603 #ifdef OSL_DFPN_SMP
00604 value.min_pdp = std::min(value.min_pdp, record.min_pdp);
00605 value.proof_pieces_candidate
00606 = value.proof_pieces_candidate.max(record.proof_pieces_candidate);
00607 value.dag_moves |= record.dag_moves;
00608 value.solved |= record.solved;
00609 value.false_branch |= record.false_branch;
00610 value.working_threads = record.working_threads;
00611 #endif
00612 record.dag_moves = value.dag_moves;
00613 return;
00614 }
00615 }
00616 }
00617 bool setWorking(const DfpnRecord& value, int thread_id)
00618 {
00619 #ifdef USE_TBB_HASH
00620 SCOPED_LOCK(lk,mutex);
00621 #endif
00622 BOOST_FOREACH(DfpnRecord& record, *this) {
00623 if (record.stands[BLACK] == value.stands[BLACK]) {
00624 assert(! (value.working_threads & (1u << thread_id)));
00625 record.working_threads |= 1u << thread_id;
00626 return false;
00627 }
00628 }
00629 push_front(value);
00630 front().working_threads |= 1u << thread_id;
00631 return true;
00632 }
00633 void leaveWorking(PieceStand black, int thread_id)
00634 {
00635 #ifdef USE_TBB_HASH
00636 SCOPED_LOCK(lk,mutex);
00637 #endif
00638 BOOST_FOREACH(DfpnRecord& record, *this) {
00639 if (record.stands[BLACK] == black) {
00640
00641 record.working_threads &= ~(1u << thread_id);
00642 return;
00643 }
00644 }
00645
00646 }
00647 void testTable(const BoardKey& ) const
00648 {
00649 #ifdef USE_TBB_HASH
00650 SCOPED_LOCK(lk,mutex);
00651 #endif
00652 BOOST_FOREACH(const DfpnRecord& record, *this) {
00653 if (record.working_threads)
00654 std::cerr << std::bitset<16>(record.working_threads) << "\n";
00655 assert(record.working_threads == 0);
00656 #ifdef DFPN_STAT
00657 const int count = misc::BitOp::countBit(record.solved);
00658 if (record.proof_disproof.isCheckmateSuccess())
00659 count2proof[key.turn()][count]++;
00660 else if (record.proof_disproof.isCheckmateFail())
00661 count2disproof[key.turn()][count]++;
00662 else
00663 count2unknown[key.turn()][count]++;
00664 #endif
00665 }
00666 }
00667 size_t smallTreeGC(size_t threshold)
00668 {
00669 size_t removed = 0;
00670 #ifdef USE_TBB_HASH
00671 SCOPED_LOCK(lk,mutex);
00672 #endif
00673 list_t::iterator p=begin();
00674 while (p!=end()) {
00675 list_t::iterator q=p;
00676 ++q;
00677 if (q == end())
00678 break;
00679 if (! q->proof_disproof.isFinal() && q->node_count < threshold) {
00680 erase_after(p);
00681 ++removed;
00682 continue;
00683 }
00684 p = q;
00685 }
00686 if (! empty() && begin()->node_count < threshold) {
00687 erase(begin());
00688 ++removed;
00689 }
00690 return removed;
00691 }
00692 };
00693 template <osl::Player A>
00694 const osl::checkmate::DfpnRecord osl::checkmate::DfpnTable::
00695 List::probe(const HashKey& key, PieceStand white_stand) const
00696 {
00697 #ifdef USE_TBB_HASH
00698 SCOPED_LOCK(lk,mutex);
00699 #endif
00700 DfpnRecord result(key.blackStand(), white_stand);
00701 const PieceStand attack_stand = (A == BLACK) ? key.blackStand() : white_stand;
00702 const PieceStand defense_stand = (A == BLACK) ? white_stand : key.blackStand();
00703 #ifdef INITIAL_DOMINANCE
00704 unsigned int proof_ll = 1, disproof_ll = 1;
00705 #endif
00706 BOOST_FOREACH(const DfpnRecord& record, *this) {
00707 if (record.stands[BLACK] == key.blackStand()) {
00708 result = record;
00709 if (result.proof_disproof.isFinal())
00710 break;
00711 continue;
00712 }
00713 if (record.proof_disproof.isCheckmateSuccess()) {
00714 if (attack_stand.isSuperiorOrEqualTo(record.proofPieces())) {
00715 result.setFrom(record);
00716 break;
00717 }
00718 }
00719 else if (record.proof_disproof.isCheckmateFail()) {
00720 if (defense_stand.isSuperiorOrEqualTo(record.disproofPieces())) {
00721 result.setFrom(record);
00722 break;
00723 }
00724 }
00725 #ifdef INITIAL_DOMINANCE
00726 else {
00727 if (record.stands[A].isSuperiorOrEqualTo(attack_stand)) {
00728 proof_ll = std::max(proof_ll, record.proof());
00729 }
00730 else if (attack_stand.isSuperiorOrEqualTo(record.stands[A])) {
00731 disproof_ll = std::max(disproof_ll, record.disproof());
00732 }
00733 }
00734 #endif
00735 }
00736 #ifdef INITIAL_DOMINANCE
00737 if (result.proof_disproof == ProofDisproof(1,1)) {
00738 result.proof_disproof = ProofDisproof(std::min(proof_ll, InitialDominanceProofMax),
00739 std::min(disproof_ll, InitialDominanceDisproofMax));
00740 }
00741 #endif
00742 return result;
00743 }
00744
00745 template <osl::Player A>
00746 const osl::checkmate::DfpnRecord osl::checkmate::DfpnTable::
00747 List::findProofOracle(const HashKey& key, PieceStand white_stand, Move last_move) const
00748 {
00749 #ifdef USE_TBB_HASH
00750 SCOPED_LOCK(lk,mutex);
00751 #endif
00752 const PieceStand attack_stand = (A == BLACK) ? key.blackStand() : white_stand;
00753 DfpnRecord result(key.blackStand(), white_stand);
00754 BOOST_FOREACH(const DfpnRecord& record, *this) {
00755 if (! record.proof_disproof.isCheckmateSuccess())
00756 continue;
00757 if (attack_stand.isSuperiorOrEqualTo(record.proofPieces())) {
00758 result.setFrom(record);
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::const_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()(Position) 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()(Position) 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(key.isPlayerOfTurn(BLACK) ? 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 = 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.getTurn() == 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 = 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.getTurn() == 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.getTurn() == 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.hasEffectBy(alt(state.getTurn()), state.getKingPosition(state.getTurn())))
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 = 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.getTurn() == BLACK)
01322 defense<WHITE>();
01323 else
01324 defense<BLACK>();
01325
01326 if (root.record.need_full_width) {
01327 root.clear();
01328 if (state.getTurn() == 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->getTurn());
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 const int from_to = (to_y*16+to_x)*256+m.from().index();
01368 return boost::make_tuple(a > d, from_to, m.isPromote());
01369 }
01370 bool operator()(Move l, Move r) const
01371 {
01372 return convert(l) > convert(r);
01373 }
01374 };
01375 }
01376 }
01377
01378 template <osl::Player Turn>
01379 void osl::checkmate::
01380 Dfpn::sort(const NumEffectState& state, DfpnMoveVector& moves)
01381 {
01382 #ifdef MEMORIZE_SOLVED_IN_BITSET
01383 int last_sorted = 0, cur = 0;
01384 Ptype last_ptype = PTYPE_EMPTY;
01385 for (;(size_t)cur < moves.size(); ++cur) {
01386 if (moves[cur].isDrop() || moves[cur].oldPtype() == last_ptype)
01387 continue;
01388 std::sort(moves.begin()+last_sorted, moves.begin()+cur, move_compare<Turn>(state));
01389 last_sorted = cur;
01390 last_ptype = moves[cur].oldPtype();
01391 }
01392 std::sort(moves.begin()+last_sorted, moves.begin()+cur, move_compare<Turn>(state));
01393 #endif
01394 }
01395
01396 template <osl::Player P>
01397 void osl::checkmate::
01398 Dfpn::generateCheck(const NumEffectState& state, DfpnMoveVector& moves, bool &has_pawn_checkmate)
01399 {
01400 assert(moves.empty());
01401 if (state.inCheck(P))
01402 {
01403 using namespace osl::move_classifier;
01404 DfpnMoveVector escape;
01405 move_generator::GenerateEscape<P>::generateKingEscape(state, escape);
01406 BOOST_FOREACH(Move move, escape)
01407 {
01408 if (MoveAdaptor<Check<P> >::isMember(state, move))
01409 moves.push_back(move);
01410 }
01411 }
01412 else
01413 {
01414 {
01415 move_action::Store store(moves);
01416 move_generator::AddEffectWithEffect<move_action::Store>::template generate<P,true>
01417 (state,state.getKingPiece(alt(P)).position(),store,has_pawn_checkmate);
01418 }
01419 BOOST_FOREACH(Move move, moves)
01420 {
01421 if(move.hasIgnoredUnpromote<P>()){
01422 if(Ptype_Table.getEffect(unpromote(move.ptypeO()),move.to(),
01423 state.getKingPosition(alt(P))).hasEffect()
01424 || (state.pinOrOpen(alt(P)).test
01425 (state.getPieceAt(move.from()).number())))
01426 moves.push_back(move.unpromote());
01427 }
01428 }
01429
01430 }
01431 sort<P>(state, moves);
01432 }
01433
01434 void osl::checkmate::
01435 Dfpn::findDagSource()
01436 {
01437 #ifdef NAGAI_DAG_TEST
01438 Node& node = tree->node[tree->depth];
01439 if (node.record.dag_terminal)
01440 return;
01441 PieceStand white_stand = node.white_stand;
01442 HashKey key = node.hash_key;
01443 DfpnRecord cur = node.record;
01444
01445 for (int d=1; d<MaxDagTraceDepth; ++d) {
01446 HashKey parent_key = key.newUnmakeMove(cur.last_move);
01447 white_stand = white_stand.previousStand(WHITE, cur.last_move);
01448 DfpnRecord parent = table->probe(parent_key, white_stand);
01449 if (parent.proof_disproof.isFinal())
01450 return;
01451
01452
01453 if (parent.best_move != cur.last_move)
01454 return;
01455 for (int i=tree->depth - 4 - (d%2); i>=0; i-=2) {
01456 if (parent_key == tree->node[i].hash_key) {
01457 for (size_t m=0; m<std::min(tree->node[i].moves.size(), (size_t)64); ++m) {
01458 if (tree->node[i].moves[m] == tree->node[i+1].moved
01459 || tree->node[i].moves[m] == cur.last_move)
01460 tree->node[i].record.dag_moves |= (1ull << m);
01461 }
01462 if (parallel_shared)
01463 table->addDag(tree->node[i].hash_key, tree->node[i].record);
01464 node.record.dag_terminal = true;
01465 return;
01466 }
01467 }
01468 key = parent_key;
01469 cur = parent;
01470 if (! cur.last_move.isNormal())
01471 return;
01472 }
01473 #endif
01474 }
01475
01476
01477 template <osl::Player P>
01478 void osl::checkmate::
01479 Dfpn::attack()
01480 {
01481 assert(! tree->inCheck(alt(P)));
01482 Node& node = tree->node[tree->depth];
01483 #if (! defined NDEBUG) && (! defined OSL_USE_RACE_DETECTOR)
01484 node.visit_time = ++timer;
01485 #endif
01486 #ifdef DFPN_DEBUG
01487 Tree::Logging logging(tree.get(), table, "attack");
01488 #endif
01489 const int my_distance = tree->depth ? tree->node[tree->depth-1].path_record->distance+1 : node.path.getDepth();
01490 LoopToDominance loop;
01491 DfpnVisitLock<> lk(node.path_record = path_table->allocate<P>(node.hash_key, my_distance, loop));
01492 DfpnRecord& record = node.record;
01493 record = DfpnRecord();
01494 if (loop == BadAttackLoop) {
01495 node.setLoopDetection();
01496 return;
01497 }
01498 assert(node.white_stand == PieceStand(WHITE, tree->state));
01499 const size_t node_count_org = node_count++;
01500 #if (! defined CHECKMATE_D2) && (! defined NO_IMMEDIATE_CHECKMATE)
01501 if (! tree->inCheck(P)
01502 && ImmediateCheckmate::hasCheckmateMove<P>(tree->state, record.best_move)) {
01503 PieceStand proof_pieces;
01504 if (record.best_move.isDrop())
01505 proof_pieces.add(record.best_move.ptype());
01506 record.setProofPieces(proof_pieces);
01507 record.proof_disproof = ProofDisproof::Checkmate();
01508 return;
01509 }
01510 #endif
01511 if (tree->depth + 2 >= MaxDepth) {
01512 std::cerr << "throw " << thread_id << "\n";
01513 throw DepthLimitReached();
01514 }
01515 assert(tree->depth + 2 < MaxDepth);
01516 record = table->probe<P>(node.hash_key, node.white_stand);
01517 assert(record.stands[BLACK] == node.hash_key.blackStand());
01518 assert(record.stands[WHITE] == node.white_stand);
01519 if (record.proof_disproof.isFinal())
01520 return;
01521 if (tree->depth == 0 && node_count_limit <= 50 && record.node_count >= node_count_limit)
01522 return;
01523 if (tree->depth == 0
01524 #ifdef CHECKMATE_A3
01525 || true
01526 #endif
01527 #ifdef CHECKMATE_A3_GOLD
01528 || (record.proof_disproof == ProofDisproof(1,1) && tree->state.hasPieceOnStand<GOLD>(P)
01529 && (tree->king(alt(P)).position().x() <= 3 || tree->king(alt(P)).position().x() >= 7
01530 || tree->king(alt(P)).position().template positionForBlack<P>().y() <= 3))
01531 #endif
01532 )
01533 {
01534 #ifdef DFPN_STAT
01535 static stat::Ratio oracle_success("a3-gold");
01536 #endif
01537 FixedDepthSearcher fixed_searcher(tree->state);
01538 PieceStand proof_pieces;
01539 ProofDisproof pdp = fixed_searcher.hasCheckmateMove<P>(2, record.best_move, proof_pieces);
01540 ++node_count;
01541 #ifdef DFPN_STAT
01542 oracle_success.add(pdp.isCheckmateSuccess());
01543 #endif
01544 if (pdp.isCheckmateSuccess()) {
01545 record.node_count++;
01546 record.proof_disproof = pdp;
01547 record.setProofPieces(proof_pieces);
01548 record.last_move = node.moved;
01549 table->store(node.hash_key, record);
01550 return;
01551 }
01552 }
01553
01554 if (parallel_shared) {
01555 if (parallel_shared->stop_all) {
01556
01557 throw DepthLimitReached();
01558 }
01559 if (parallel_shared->data[thread_id].restart) {
01560 for (int i=0; i<tree->depth; ++i) {
01561 if (tree->node[i].hash_key
01562 == parallel_shared->data[thread_id].restart_key)
01563 return;
01564 #if 0
01565 if (tree->node[i].record.dag_terminal)
01566 break;
01567 #endif
01568 }
01569
01570 parallel_shared->data[thread_id].clear();
01571 }
01572 }
01573
01574
01575 bool has_pawn_checkmate=false;
01576 generateCheck<P>(tree->state, node.moves,has_pawn_checkmate);
01577 if (node.moves.empty()) {
01578 record.setDisproofPieces(DisproofPieces::leaf(tree->state, alt(P),
01579 record.stands[alt(P)]));
01580 if(has_pawn_checkmate)
01581 record.proof_disproof = ProofDisproof::PawnCheckmate();
01582 else
01583 record.proof_disproof = ProofDisproof::NoCheckmate();
01584 return;
01585 }
01586
01587 #ifdef PROOF_AVERAGE
01588 int frontier_count = 0, sum_frontier_proof = 0;
01589 #endif
01590 assert(node.children.empty());
01591 {
01592 node.allocate(node.moves.size());
01593 const King8Info info_modified
01594 = Edge_Table.resetEdgeFromLiberty(alt(P), tree->king(alt(P)).position(), King8Info(tree->state.Iking8Info(alt(P))));
01595 for (size_t i=0; i<node.moves.size(); ++i) {
01596 #ifdef MEMORIZE_SOLVED_IN_BITSET
01597 if (record.solved & (1ull << i))
01598 continue;
01599 #endif
01600 const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
01601 node.children[i] = table->probe<P>(new_key, node.nextWhiteStand(P, node.moves[i]));
01602 if (node.children[i].proof_disproof == ProofDisproof(1,1)) {
01603 unsigned int proof, disproof;
01604 LibertyEstimator::attackH(P, tree->state, info_modified,
01605 node.moves[i], proof, disproof);
01606 node.children[i].proof_disproof = ProofDisproof(proof, disproof);
01607 }
01608 #ifdef PROOF_AVERAGE
01609 if (node.children[i].node_count == 0) {
01610 ++frontier_count;
01611 sum_frontier_proof += node.children[i].proof();
01612 }
01613 #endif
01614 if (node.children[i].proof_disproof == ProofDisproof::NoEscape()
01615 && node.moves[i].isDrop() && node.moves[i].ptype() == PAWN) {
01616 node.children[i].proof_disproof = ProofDisproof::PawnCheckmate();
01617 #ifdef MEMORIZE_SOLVED_IN_BITSET
01618 record.solved |= (1ull << i);
01619 #endif
01620 record.min_pdp = std::min(record.min_pdp, (unsigned int)ProofDisproof::PAWN_CHECK_MATE_PROOF);
01621 }
01622 else if (node.children[i].proof_disproof.isCheckmateFail())
01623 tree->setNoCheckmateChildInAttack(i);
01624 else if (node.children[i].proof_disproof.isCheckmateSuccess()) {
01625 record.node_count += node_count - node_count_org;
01626 node.setCheckmateAttack(P,i);
01627 record.last_move = node.moved;
01628 table->store(node.hash_key, record);
01629 return;
01630 }
01631 node.children_path[i] = path_table->probe(new_key);
01632 node.proof_cost[i] = attackProofCost(P, tree->state, node.moves[i]);
01633 }
01634 }
01635
01636
01637 if (parallel_shared)
01638 table->setWorking(node.hash_key, record, thread_id);
01639
01640
01641 if (record.last_move.isNormal() && record.last_move != node.moved)
01642 findDagSource();
01643 record.last_move = node.moved;
01644
01645 assert(node.children.size() == node.moves.size());
01646 #ifdef PROOF_AVERAGE
01647 const size_t proof_average = frontier_count ? sum_frontier_proof/frontier_count : 1;
01648 #else
01649 const size_t proof_average = 1;
01650 #endif
01651
01652 #ifdef DFPN_DEBUG
01653 if (std::find(debug_node.begin(), debug_node.end(), node_id_table.id(node.hash_key))
01654 != debug_node.end() && timer > debug_time_start)
01655 tree->dump(__LINE__);
01656 #endif
01657 for (int loop=0; true; ++loop) {
01658 unsigned int min_proof=record.min_pdp, min_proof2=record.min_pdp;
01659 size_t sum_disproof = 0, max_disproof = 0, max_disproof_dag = 0, next_i=node.children.size();
01660 size_t max_drop_disproof_rook = 0, max_drop_disproof_bishop = 0, max_drop_disproof_lance = 0;
01661 int max_children_depth = 0, upward_count = 0;
01662 for (size_t i=0; i<node.children.size(); ++i) {
01663 #ifdef MEMORIZE_SOLVED_IN_BITSET
01664 if (record.solved & (1ull << i))
01665 continue;
01666 #endif
01667 if (i > 0 && min_proof < ProofDisproof::PROOF_LIMIT
01668 && node.moves[i].fromTo() == node.moves[i-1].fromTo()
01669 && ! node.moves[i].isDrop()) {
01670
01671 assert(node.moves[i].ptype() == node.moves[i-1].oldPtype());
01672 record.dag_moves |= ((1ull << i) | (1ull << (i-1)));
01673 if (node.threshold.proof() < NoPromoeIgnoreProofThreshold
01674 && node.threshold.disproof() < NoPromoeIgnoreDisproofThreshold)
01675 continue;
01676
01677 }
01678 size_t proof = node.children[i].proof();
01679 size_t disproof = node.children[i].disproof();
01680 if (proof && disproof) {
01681 proof += node.proof_cost[i];
01682 if (parallel_shared && node.children[i].working_threads) {
01683
01684 proof += misc::BitOp::countBit(node.children[i].working_threads);
01685 }
01686 }
01687 if (node.children_path[i]) {
01688 if (node.isLoop(i)) {
01689 node.children[i].proof_disproof = ProofDisproof::LoopDetection();
01690 assert(proof < ProofDisproof::LOOP_DETECTION_PROOF);
01691 proof = ProofDisproof::LOOP_DETECTION_PROOF;
01692 disproof = 0;
01693 }
01694 else if (! node.children[i].proof_disproof.isFinal()) {
01695 max_children_depth = std::max(max_children_depth, node.children_path[i]->distance);
01696 #ifdef NAGAI_DAG_TEST
01697 if (record.dag_moves & (1ull<<i)) {
01698 max_disproof_dag = std::max(max_disproof_dag, disproof);
01699 disproof = 0;
01700 }
01701 else
01702 #endif
01703 #ifdef DELAY_UPWARD
01704 if (node.children_path[i]->distance <= node.path_record->distance) {
01705 max_disproof = std::max(max_disproof, disproof);
01706 ++upward_count;
01707 disproof = UpwardWeight;
01708 }
01709 else
01710 #endif
01711 if (node.moves[i].isDrop()
01712 || (isMajor(node.moves[i].ptype())
01713 && node.moves[i].capturePtype() == PTYPE_EMPTY
01714 && ! node.moves[i].isPromote() && isPromoted(node.moves[i].ptype())
01715 && ! tree->state.hasEffectBy(alt(P), node.moves[i].to()))) {
01716 const EffectContent e
01717 = Ptype_Table.getEffect(node.moves[i].ptypeO(),
01718 Offset32(tree->king(alt(P)).position(), node.moves[i].to()));
01719 if (! e.hasUnblockableEffect()) {
01720 size_t *target = &max_drop_disproof_lance;
01721 if (unpromote(node.moves[i].ptype()) == ROOK)
01722 target = &max_drop_disproof_rook;
01723 else if (unpromote(node.moves[i].ptype()) == BISHOP)
01724 target = &max_drop_disproof_bishop;
01725 *target = std::max(*target, disproof);
01726 disproof = LongDropCount;
01727 }
01728 }
01729 }
01730 }
01731 else {
01732 max_children_depth = node.path_record->distance+1;
01733 }
01734 if (proof < min_proof || (proof == min_proof && disproof && disproof < node.children[next_i].disproof())) {
01735 min_proof2 = min_proof;
01736 min_proof = proof;
01737 next_i = i;
01738 } else if (proof < min_proof2) {
01739 min_proof2 = proof;
01740 }
01741 sum_disproof += disproof;
01742 }
01743 sum_disproof += max_drop_disproof_rook + max_drop_disproof_bishop + max_drop_disproof_lance
01744 + max_disproof_dag;
01745 if (LongDropCount) {
01746 if (max_drop_disproof_rook) sum_disproof -= LongDropCount;
01747 if (max_drop_disproof_bishop) sum_disproof -= LongDropCount;
01748 if (max_drop_disproof_lance) sum_disproof -= LongDropCount;
01749 }
01750 if (upward_count) {
01751 if (sum_disproof == 0)
01752 sum_disproof = max_disproof;
01753 }
01754 if (node.path_record->distance >= max_children_depth) {
01755 node.path_record->distance = max_children_depth-1;
01756 }
01757 #ifdef KISHIMOTO_WIDEN_THRESHOLD
01758 if (loop == 0 && sum_disproof >= node.threshold.disproof() && sum_disproof > IgnoreUpwardDisproofThreshold)
01759 node.threshold = ProofDisproof(node.threshold.proof(), sum_disproof+1);
01760 #endif
01761 if (min_proof >= node.threshold.proof()
01762 || sum_disproof >= node.threshold.disproof()
01763 || next_i >= node.children.size()
01764 || node_count + min_proof >= node_count_limit) {
01765 record.proof_disproof = ProofDisproof(min_proof, sum_disproof);
01766 if (record.proof_disproof.isLoopDetection())
01767 node.setLoopDetection();
01768 else if (record.proof_disproof.isCheckmateFail()) {
01769 node.setNoCheckmateAttack(P, tree->state);
01770 }
01771 record.node_count += node_count - node_count_org;
01772 table->store(node.hash_key, record, thread_id);
01773 if (parallel_shared && record.proof_disproof.isFinal())
01774 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
01775 return;
01776 }
01777 #ifdef MEMORIZE_SOLVED_IN_BITSET
01778 assert(! (record.solved & (1ull << next_i)));
01779 #endif
01780 record.best_move = node.moves[next_i];
01781 tree->newVisit(P, node.moves[next_i], node.hashes[next_i]);
01782 Node& next = tree->node[tree->depth+1];
01783 next.threshold = ProofDisproof(std::min(min_proof2+proof_average, (size_t)node.threshold.proof())
01784 - node.proof_cost[next_i],
01785 node.threshold.disproof()
01786 - (sum_disproof - node.children[next_i].disproof()));
01787 CallDefense<P> helper(this);
01788 tree->depth += 1;
01789 ApplyMoveWithPath<P>::doUndoMove(tree->state, next.path, next.moved, helper);
01790 tree->depth -= 1;
01791 node.children[next_i] = next.record;
01792 node.children_path[next_i] = next.path_record;
01793 if (next.record.proof_disproof == ProofDisproof::NoEscape()
01794 && next.moved.isDrop() && next.moved.ptype() == PAWN)
01795 node.children[next_i].proof_disproof = ProofDisproof::PawnCheckmate();
01796 if (node.children[next_i].proof_disproof.isCheckmateSuccess()) {
01797 node.setCheckmateAttack(P,next_i);
01798 record.node_count += node_count - node_count_org;
01799 record.last_move = node.moved;
01800 table->store(node.hash_key, record, thread_id);
01801 if (parallel_shared)
01802 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
01803 return;
01804 }
01805 else if (next.record.proof_disproof.isCheckmateFail()
01806 && ! next.record.proof_disproof.isLoopDetection())
01807 tree->setNoCheckmateChildInAttack(next_i);
01808 min_proof = std::min(min_proof2, node.children[next_i].proof());
01809 if (min_proof < ProofDisproof::PROOF_LIMIT
01810 && node_count + min_proof >= node_count_limit) {
01811 record.proof_disproof = ProofDisproof(min_proof, sum_disproof);
01812 record.node_count += node_count - node_count_org;
01813 table->store(node.hash_key, record, thread_id);
01814 if (parallel_shared)
01815 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
01816 return;
01817 }
01818 if (parallel_shared && parallel_shared->data[thread_id].restart) {
01819 if (tree->depth == 0)
01820 parallel_shared->data[thread_id].clear();
01821 else {
01822 if (parallel_shared->data[thread_id].restart_key == node.hash_key) {
01823 record = table->probe<P>(node.hash_key, node.white_stand);
01824 if (! record.proof_disproof.isFinal())
01825 continue;
01826 parallel_shared->data[thread_id].clear();
01827 }
01828 table->leaveWorking(node.hash_key, thread_id);
01829 return;
01830 }
01831 }
01832 }
01833 }
01834
01835 template <osl::Player P>
01836 void osl::checkmate::
01837 Dfpn::generateEscape(const NumEffectState& state, bool need_full_width,
01838 Position last_to, DfpnMoveVector& moves)
01839 {
01840 assert(moves.empty());
01841 const Player AltP=PlayerTraits<P>::opponent;
01842 #ifdef GRAND_PARENT_DELAY
01843 const bool delay_node = last_to != Position()
01844 && state.hasEffectBy(alt(P), last_to)
01845 && (state.hasEffectNotBy(alt(P), state.getKingPiece(alt(P)), last_to)
01846 || ! state.hasEffectBy(P, last_to));
01847 if (delay_node)
01848 {
01849 DfpnMoveVector all;
01850 move_generator::GenerateEscape<AltP>::
01851 generateCheapKingEscape(state, all);
01852
01853 BOOST_FOREACH(Move move, all) {
01854 if (move.to() == last_to) {
01855 moves.push_back(move);
01856 }
01857 }
01858 #ifdef MEMORIZE_SOLVED_IN_BITSET
01859 sort<AltP>(state, moves);
01860 #endif
01861 }
01862 else
01863 #endif
01864 {
01865 move_generator::GenerateEscape<AltP>::
01866 generateCheapKingEscape(state, moves);
01867 #ifdef MEMORIZE_SOLVED_IN_BITSET
01868 sort<AltP>(state, moves);
01869 #endif
01870 }
01871
01872 if (need_full_width) {
01873 DfpnMoveVector others;
01874 move_generator::GenerateEscape<AltP>::
01875 generateKingEscape(state, others);
01876 #ifdef MEMORIZE_SOLVED_IN_BITSET
01877 sort<AltP>(state, others);
01878 #endif
01879 const int org_size = moves.size();
01880 BOOST_FOREACH(Move move, others) {
01881 if (std::find(moves.begin(), moves.begin()+org_size, move) == moves.begin()+org_size)
01882 moves.push_back(move);
01883 }
01884 BOOST_FOREACH(Move move, moves)
01885 {
01886 if(move.hasIgnoredUnpromote<AltP>())
01887 moves.push_back(move.unpromote());
01888 }
01889 }
01890
01891 }
01892
01893 bool osl::checkmate::
01894 Dfpn::grandParentSimulationSuitable() const
01895 {
01896 #ifdef GRAND_PARENT_SIMULATION
01897 Node& node = tree->node[tree->depth];
01898 if (tree->depth >= 2) {
01899 const Node& parent = tree->node[tree->depth-1];
01900 const Node& gparent = tree->node[tree->depth-2];
01901 const Move alm = node.moved;
01902 const Move dlm = parent.moved;
01903 const Move alm2 = gparent.moved;
01904 if (dlm.isNormal() && alm.to() == dlm.to() && dlm.capturePtype() == PTYPE_EMPTY
01905 && alm2.isNormal() && alm2.to() == alm.from()) {
01906 return true;
01907 }
01908 }
01909 #endif
01910 return false;
01911 }
01912
01913 template <osl::Player P>
01914 void osl::checkmate::
01915 Dfpn::defense()
01916 {
01917 #if 0
01918 if (parallel_shared) {
01919 if (parallel_shared->stop_all)
01920 throw DepthLimitReached();
01921 if (parallel_shared->data[thread_id].restart) {
01922 for (int i=0; i<tree->depth; ++i) {
01923 if (tree->node[i].hash_key == parallel_shared->data[thread_id].restart_key)
01924 return;
01925 #if 0
01926 if (tree->node[i].record.dag_terminal)
01927 break;
01928 #endif
01929 }
01930
01931 parallel_shared->data[thread_id].clear();
01932 }
01933 }
01934 #endif
01935 Node& node = tree->node[tree->depth];
01936 #if (! defined NDEBUG) && (! defined OSL_USE_RACE_DETECTOR)
01937 node.visit_time = ++timer;
01938 #endif
01939 #ifdef DFPN_DEBUG
01940 Tree::Logging logging(tree.get(), table, "defens");
01941 #endif
01942 const int my_distance = tree->depth ? tree->node[tree->depth-1].path_record->distance+1 : node.path.getDepth();
01943 LoopToDominance loop;
01944 DfpnVisitLock<> lk(node.path_record = path_table->allocate<P>(node.hash_key, my_distance, loop));
01945 DfpnRecord& record = node.record;
01946 if (loop == BadAttackLoop) {
01947 record = DfpnRecord();
01948 node.setLoopDetection();
01949 return;
01950 }
01951 const size_t node_count_org = node_count++;
01952 assert(tree->inCheck(alt(P)));
01953 assert(node.white_stand == PieceStand(WHITE, tree->state));
01954
01955 record = table->probe<P>(node.hash_key, node.white_stand);
01956 assert(record.stands[BLACK] == node.hash_key.blackStand());
01957 assert(record.stands[WHITE] == node.white_stand);
01958 if (record.proof_disproof.isFinal())
01959 return;
01960 const bool grand_parent_simulation = grandParentSimulationSuitable();
01961 if (record.last_to == Position())
01962 record.last_to = grand_parent_simulation ? node.moved.to() : tree->king(alt(P)).position();
01963 const Position grand_parent_delay_last_to
01964 = (record.last_to != tree->king(alt(P)).position()) ? record.last_to : Position();
01965
01966 generateEscape<P>(tree->state, record.need_full_width, grand_parent_delay_last_to, node.moves);
01967 if (node.moves.empty() && ! record.need_full_width) {
01968 record.need_full_width = true;
01969 generateEscape<P>(tree->state, record.need_full_width, grand_parent_delay_last_to, node.moves);
01970 }
01971 if (node.moves.empty()) {
01972 record.setProofPieces(ProofPieces::leaf(tree->state, P, record.stands[P]));
01973 record.proof_disproof = ProofDisproof::NoEscape();
01974 return;
01975 }
01976
01977 #ifdef DISPROOF_AVERAGE
01978 int frontier_count = 0, sum_frontier_disproof = 0;
01979 #endif
01980 assert(node.children.empty());
01981 {
01982 node.allocate(node.moves.size());
01983 for (size_t i=0;i <node.moves.size(); ++i) {
01984 #ifdef MEMORIZE_SOLVED_IN_BITSET
01985 if (record.solved & (1ull << i))
01986 continue;
01987 #endif
01988 const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
01989 node.children[i] = table->probe<P>(new_key, node.nextWhiteStand(alt(P), node.moves[i]));
01990 if (node.children[i].proof_disproof.isCheckmateSuccess()) {
01991 node.setCheckmateChildInDefense(i);
01992 }
01993 #ifdef CHECKMATE_D2
01994 else if (node.children[i].proof_disproof == ProofDisproof(1,1)) {
01995 FixedDepthSearcher fixed_searcher(tree->state);
01996 PieceStand proof_pieces;
01997 Move check_move;
01998 node.children[i].proof_disproof
01999 = fixed_searcher.hasEscapeByMove<P>(node.moves[i], 0, check_move, proof_pieces);
02000 ++node_count;
02001 if (node.children[i].proof_disproof.isCheckmateSuccess()) {
02002 node.children[i].best_move = check_move;
02003 node.children[i].setProofPieces(proof_pieces);
02004 node.children[i].node_count++;
02005 node.setCheckmateChildInDefense(i);
02006 }
02007 else {
02008 if (node.children[i].proof_disproof.isCheckmateFail()) {
02009 node.children[i].proof_disproof = ProofDisproof(1,1);
02010 if (i) {
02011 node.moves[0] = node.moves[i];
02012 node.children[0] = node.children[i];
02013 node.children_path[0] = node.children_path[i];
02014 const int old_size = (int)node.moves.size();
02015 for (int j=1; j<old_size; ++j) {
02016 node.moves.pop_back();
02017 node.children.pop_back();
02018 node.children_path.pop_back();
02019 }
02020 }
02021 break;
02022 }
02023 #ifdef DISPROOF_AVERAGE
02024 ++frontier_count;
02025 sum_frontier_disproof += node.children[i].proof_disproof.disproof();
02026 #endif
02027 }
02028
02029 }
02030 #endif
02031 if (! node.children[i].proof_disproof.isCheckmateFail()) {
02032 node.children_path[i] = path_table->probe(new_key);
02033 if (node.isLoop(i)) {
02034 node.setLoopDetection();
02035 return;
02036 }
02037 #ifdef GRAND_PARENT_SIMULATION
02038 if (grand_parent_simulation && node.children[i].proof_disproof == ProofDisproof(1,1)) {
02039 const Node& gparent = tree->node[tree->depth-2];
02040 size_t gi=std::find(gparent.moves.begin(), gparent.moves.end(), node.moves[i]) - gparent.moves.begin();
02041 if (gi < gparent.moves.size()
02042 && (
02043 #ifdef MEMORIZE_SOLVED_IN_BITSET
02044 (gparent.record.solved & (1ull<<gi))
02045 ||
02046 #endif
02047 gparent.children[gi].proof_disproof.isCheckmateSuccess())) {
02048 grandParentSimulation<P>(i, gparent, gi);
02049 if (node.children[i].proof_disproof.isCheckmateSuccess())
02050 node.setCheckmateChildInDefense(i);
02051 }
02052 }
02053 #endif
02054 }
02055 if (node.children[i].proof_disproof.isCheckmateFail()) {
02056 tree->setNoCheckmateDefense(P, i);
02057 table->store(node.hash_key, record);
02058 return;
02059 }
02060 }
02061 if (record.need_full_width==1) {
02062 record.need_full_width++;
02063 for (size_t i=0;i <node.moves.size(); ++i) {
02064 if (
02065 #ifdef MEMORIZE_SOLVED_IN_BITSET
02066 ((record.solved & (1ull<<i))
02067 || (i >= 64 && node.children[i].proof_disproof.isCheckmateSuccess()))
02068 #else
02069 node.children[i].proof_disproof.isCheckmateSuccess()
02070 #endif
02071
02072 && node.moves[i].isDrop()) {
02073 blockingSimulation<P>(i, ProofOracle(node.hash_key.newHashWithMove(node.moves[i]),
02074 node.nextWhiteStand(alt(P), node.moves[i])));
02075 }
02076 }
02077 }
02078 }
02079 assert(node.children.size() == node.moves.size());
02080
02081
02082 if (parallel_shared)
02083 table->setWorking(node.hash_key, record, thread_id);
02084
02085
02086 if (record.last_move.isNormal() && record.last_move != node.moved)
02087 findDagSource();
02088 record.last_move = node.moved;
02089
02090 #ifdef DISPROOF_AVERAGE
02091 const size_t disproof_average = frontier_count ? sum_frontier_disproof/frontier_count : 1;
02092 #else
02093 const size_t disproof_average = 1;
02094 #endif
02095
02096 #ifdef DFPN_DEBUG
02097 if (std::find(debug_node.begin(), debug_node.end(), node_id_table.id(node.hash_key))
02098 != debug_node.end() && timer > debug_time_start)
02099 tree->dump(__LINE__);
02100 #endif
02101 CArray<char,DfpnMaxUniqMoves> target;
02102 for (int loop=0; true; ++loop) {
02103 std::fill(target.begin(), target.begin()+(int)node.moves.size(), false);
02104 unsigned int min_disproof=record.min_pdp, min_disproof2=record.min_pdp;
02105 size_t sum_proof = 0, max_upward_proof = 0, max_drop_proof = 0, next_i=node.children.size();
02106 size_t max_proof_dag = 0;
02107 int max_children_depth = 0, upward_count = 0;
02108 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02109 size_t max_proof = 0;
02110 bool false_branch_candidate = !record.false_branch;
02111 #endif
02112 for (size_t i=0; i<node.children.size(); ++i) {
02113 #ifdef MEMORIZE_SOLVED_IN_BITSET
02114 if (record.solved & (1ull << i))
02115 continue;
02116 #endif
02117 if (i > 0 && min_disproof < ProofDisproof::DISPROOF_LIMIT
02118 && node.moves[i].fromTo() == node.moves[i-1].fromTo()
02119 && ! node.moves[i].isDrop()) {
02120
02121 assert(node.moves[i].ptype() == node.moves[i-1].oldPtype());
02122 continue;
02123 }
02124 size_t disproof = node.children[i].disproof();
02125 size_t proof = node.children[i].proof();
02126 if (node.children[i].proof_disproof.isCheckmateFail()) {
02127
02128 assert(! node.children[i].proof_disproof.isLoopDetection());
02129 tree->setNoCheckmateDefense(P, i);
02130 table->store(node.hash_key, record, thread_id);
02131 if (parallel_shared)
02132 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02133 return;
02134 }
02135 if (proof && disproof) {
02136 if (parallel_shared && node.children[i].working_threads) {
02137
02138 disproof += misc::BitOp::countBit(node.children[i].working_threads);
02139 }
02140 }
02141 if (node.children_path[i]) {
02142 if (node.isLoop(i)) {
02143 node.setLoopDetection();
02144 if (parallel_shared)
02145 table->leaveWorking(node.hash_key, thread_id);
02146 return;
02147 }
02148 if (! node.children[i].proof_disproof.isFinal()) {
02149 max_children_depth = std::max(max_children_depth, node.children_path[i]->distance);
02150 #ifdef IGNORE_MONSTER_CHILD
02151 if (node.children_path[i]->distance <= node.path_record->distance
02152 && (! record.need_full_width || min_disproof < ProofDisproof::DISPROOF_LIMIT)
02153 && node.children[i].proof_disproof.proof() >= node.threshold.proof()
02154 && node.threshold.proof() > IgnoreUpwardProofThreshold) {
02155 false_branch_candidate = false;
02156 continue;
02157 }
02158 else
02159 #endif
02160 #ifdef NAGAI_DAG_TEST
02161 if (record.dag_moves & (1ull << i)) {
02162 max_proof_dag = std::max(max_proof_dag, proof);
02163 proof = 0;
02164 }
02165 else
02166 #endif
02167 #ifdef DELAY_UPWARD
02168 if (node.children_path[i]->distance <= node.path_record->distance) {
02169 max_upward_proof = std::max(max_upward_proof , proof);
02170 ++upward_count;
02171 proof = UpwardWeight;
02172 }
02173 else
02174 #endif
02175 if (node.moves[i].isDrop() && !tree->state.hasEffectBy(alt(P), node.moves[i].to())) {
02176 max_drop_proof = std::max(max_drop_proof, proof);
02177 proof = SacrificeBlockCount;
02178 }
02179 }
02180 }
02181 else {
02182 max_children_depth = node.path_record->distance+1;
02183 }
02184 target[i] = true;
02185 if (disproof < min_disproof
02186 || (disproof == min_disproof && proof && proof < node.children[next_i].proof())) {
02187 min_disproof2 = min_disproof;
02188 min_disproof = disproof;
02189 next_i = i;
02190 } else if (disproof < min_disproof2) {
02191 min_disproof2 = disproof;
02192 }
02193 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02194 if (false_branch_candidate && ! node.children[i].proof_disproof.isFinal()
02195 && (node.children[i].node_count == 0
02196 || ! node.children[i].best_move.isNormal()
02197 || ! (node.moves[i].ptype() == KING && node.moves[i].capturePtype() == PTYPE_EMPTY)))
02198 false_branch_candidate = false;
02199 max_proof = std::max(max_proof, proof);
02200 #endif
02201 sum_proof += proof;
02202 }
02203 #ifdef KAKINOKI_FALSE_BRANCH_SEARCH
02204 if (false_branch_candidate) {
02205 record.false_branch = true;
02206 HashKey goal;
02207 for (size_t i=0; i<node.children.size(); ++i) {
02208 if (! target[i])
02209 continue;
02210 HashKey key = node.hashes[i];
02211 key = key.newHashWithMove(node.children[i].best_move);
02212 if (goal == HashKey()) {
02213 goal = key;
02214 continue;
02215 }
02216 if (goal != key) {
02217 record.false_branch = false;
02218 break;
02219 }
02220 }
02221 }
02222 if (record.false_branch)
02223 sum_proof = max_proof;
02224 #endif
02225 sum_proof += max_drop_proof + max_proof_dag;
02226 if (SacrificeBlockCount && max_drop_proof)
02227 sum_proof -= SacrificeBlockCount;
02228 if (upward_count) {
02229 if (sum_proof == 0)
02230 sum_proof = std::max(sum_proof, max_upward_proof);
02231 }
02232 if (node.path_record->distance >= max_children_depth) {
02233 node.path_record->distance = max_children_depth-1;
02234 }
02235 if (min_disproof >= ProofDisproof::DISPROOF_MAX) {
02236 assert(! record.need_full_width);
02237 record.proof_disproof = ProofDisproof(1,1);
02238 record.need_full_width = 1;
02239 table->store(node.hash_key, record, thread_id);
02240 return;
02241 }
02242 #ifdef KISHIMOTO_WIDEN_THRESHOLD
02243 if (loop == 0 && sum_proof >= node.threshold.proof() && sum_proof > IgnoreUpwardProofThreshold)
02244 node.threshold = ProofDisproof(sum_proof+1, node.threshold.disproof());
02245 #endif
02246 if (min_disproof >= node.threshold.disproof()
02247 || sum_proof >= node.threshold.proof()
02248 || next_i >= node.children.size()
02249 || node_count + sum_proof >= node_count_limit) {
02250 record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
02251 if (record.proof_disproof.isLoopDetection())
02252 node.setLoopDetection();
02253 else if (record.proof_disproof.isCheckmateSuccess()) {
02254 if (blocking_verify && ! record.need_full_width) {
02255
02256 record.need_full_width = 1;
02257 record.proof_disproof = ProofDisproof(1,1);
02258 table->store(node.hash_key, record, thread_id);
02259 return;
02260 }
02261 node.setCheckmateDefense(P, tree->state);
02262 }
02263 record.node_count += node_count - node_count_org;
02264 table->store(node.hash_key, record, thread_id);
02265 if (parallel_shared && record.proof_disproof.isFinal())
02266 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02267 return;
02268 }
02269 #ifdef MEMORIZE_SOLVED_IN_BITSET
02270 assert(! (record.solved & (1ull << next_i)));
02271 #endif
02272 record.best_move = node.moves[next_i];
02273 tree->newVisit(alt(P), node.moves[next_i], node.hashes[next_i]);
02274 Node& next = tree->node[tree->depth+1];
02275 next.threshold = ProofDisproof(node.threshold.proof()
02276 - (sum_proof - node.children[next_i].proof()),
02277 std::min(min_disproof2+disproof_average,
02278 (size_t)node.threshold.disproof()));
02279 CallAttack<P> helper(this);
02280 tree->depth += 1;
02281 ApplyMoveWithPath<PlayerTraits<P>::opponent>::doUndoMove(tree->state, next.path, node.moves[next_i], helper);
02282 tree->depth -= 1;
02283 if (parallel_shared && parallel_shared->data[thread_id].restart) {
02284 if (tree->depth == 0)
02285 parallel_shared->data[thread_id].clear();
02286 else {
02287 if (parallel_shared->data[thread_id].restart_key == node.hash_key) {
02288 record = table->probe<P>(node.hash_key, node.white_stand);
02289 assert(record.proof_disproof.isFinal());
02290 parallel_shared->data[thread_id].clear();
02291 }
02292 table->leaveWorking(node.hash_key, thread_id);
02293 return;
02294 }
02295 }
02296
02297 node.children[next_i] = next.record;
02298 node.children_path[next_i] = next.path_record;
02299 if (next.record.proof_disproof.isCheckmateFail()) {
02300 if (record.proof_disproof.isLoopDetection())
02301 node.setLoopDetection();
02302 else
02303 tree->setNoCheckmateDefense(P, next_i);
02304 record.node_count += node_count - node_count_org;
02305 table->store(node.hash_key, record, thread_id);
02306 if (parallel_shared && record.proof_disproof.isFinal())
02307 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02308 return;
02309 }
02310 if (next.record.proof_disproof.isCheckmateSuccess())
02311 node.setCheckmateChildInDefense(next_i);
02312 if (node_count >= node_count_limit) {
02313 record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
02314 record.node_count += node_count - node_count_org;
02315 table->store(node.hash_key, record, thread_id);
02316 if (parallel_shared && record.proof_disproof.isFinal())
02317 parallel_shared->restartThreads(node.hash_key, tree->depth, record.working_threads);
02318 return;
02319 }
02320 if (next.moved.isDrop() && next.record.proof_disproof.isCheckmateSuccess()) {
02321 blockingSimulation<P>(next_i, ProofOracle(next.hash_key, next.white_stand));
02322 }
02323 }
02324 }
02325
02326 #ifndef MINIMAL
02327 void osl::checkmate::
02328 Dfpn::analyze(const PathEncoding& path_src,
02329 const NumEffectState& src, const vector<Move>& moves) const
02330 {
02331 NumEffectState state(src);
02332 HashKey key(state);
02333 PathEncoding path(path_src);
02334 for (size_t i=0; i<moves.size(); ++i) {
02335 if (! state.isAlmostValidMove(moves[i]))
02336 break;
02337 ApplyMoveOfTurn::doMove(state, moves[i]);
02338 key = key.newMakeMove(moves[i]);
02339 path.pushMove(moves[i]);
02340 DfpnRecord record = table->probe(key, PieceStand(WHITE, state));
02341 const DfpnPathRecord *path_record = path_table->probe(key);
02342 std::cerr << i << ' ' << moves[i] << " " << path
02343 << ' ' << record::csa::show(record.best_move) << "\n";
02344 std::cerr << " " << record.proof_disproof << ' ' << record.node_count;
02345 if (path_record) {
02346 std::cerr << " distance " << path_record->distance << " twins";
02347 for (SimpleTwinList::const_iterator p=path_record->twin_list.begin();
02348 p!=path_record->twin_list.end(); ++p) {
02349 std::cerr << ' ' << *p;
02350 }
02351 }
02352 std::cerr << "\n";
02353 DfpnMoveVector moves;
02354 if (state.getTurn() == table->attack()) {
02355 bool has_pawn_checkmate=false;
02356 if (state.getTurn() == BLACK)
02357 generateCheck<BLACK>(state, moves, has_pawn_checkmate);
02358 else
02359 generateCheck<WHITE>(state, moves, has_pawn_checkmate);
02360 }
02361 else {
02362 const Position grand_parent_delay_last_to
02363 = (record.last_to != state.getKingPosition(state.getTurn())) ? record.last_to : Position();
02364 if (state.getTurn() == BLACK)
02365 generateEscape<WHITE>(state, true, grand_parent_delay_last_to, moves);
02366 else
02367 generateEscape<BLACK>(state, true, grand_parent_delay_last_to, moves);
02368 }
02369 BOOST_FOREACH(Move m, moves) {
02370 std::cerr << " " << m;
02371 DfpnRecord child = table->probe(key.newMakeMove(m),
02372 PieceStand(WHITE, state).nextStand(WHITE, m));
02373 std::cerr << ' ' << child.proof_disproof << ' ' << child.node_count;
02374 const DfpnPathRecord *child_path_record = path_table->probe(key.newMakeMove(m));
02375 if (child_path_record) {
02376 std::cerr << " d " << child_path_record->distance << " twins";
02377 BOOST_FOREACH(const PathEncoding& path, child_path_record->twin_list) {
02378 std::cerr << ' ' << path;
02379 }
02380 }
02381 std::cerr << "\n";
02382 }
02383 }
02384 std::cerr << state;
02385 }
02386 #endif
02387
02388 template <osl::Player P, bool UseTable>
02389 struct osl::checkmate::Dfpn::CallProofOracleAttack
02390 {
02391 Dfpn *search;
02392 ProofOracle oracle;
02393 CallProofOracleAttack(Dfpn *s, const ProofOracle& o) : search(s), oracle(o)
02394 {
02395 }
02396 void operator()(Position) const
02397 {
02398 search->proofOracleAttack<P,UseTable>(oracle);
02399 }
02400 };
02401
02402 template <osl::Player P, bool UseTable>
02403 struct osl::checkmate::Dfpn::CallProofOracleDefense
02404 {
02405 Dfpn *search;
02406 ProofOracle oracle;
02407 CallProofOracleDefense(Dfpn *s, const ProofOracle& o) : search(s), oracle(o)
02408 {
02409 }
02410 void operator()(Position) const
02411 {
02412 search->proofOracleDefense<P,UseTable>(oracle);
02413 }
02414 };
02415
02416 template <osl::Player P, bool UseTable>
02417 void osl::checkmate::
02418 Dfpn::proofOracleAttack(const ProofOracle& key)
02419 {
02420 #ifdef DFPN_DEBUG
02421 Tree::Logging logging(tree.get(), table, UseTable ? "tpatta" : "pattac");
02422 #endif
02423 assert(! tree->inCheck(alt(P)));
02424 const int my_distance = (UseTable && tree->depth) ? (tree->node[tree->depth-1].path_record->distance+1) : 0;
02425 Node& node = tree->node[tree->depth];
02426 DfpnRecord& record = node.record;
02427 LoopToDominance loop;
02428 DfpnVisitLock<UseTable> lk((node.path_record = (UseTable ? path_table->allocate<P>(node.hash_key, my_distance, loop) : 0)));
02429 if (UseTable && loop == BadAttackLoop) {
02430 record = DfpnRecord();
02431 node.setLoopDetection();
02432 return;
02433 }
02434 assert(node.white_stand == PieceStand(WHITE, tree->state));
02435 const size_t node_count_org = node_count++;
02436 if (node_count_limit == root_proof_simulation_limit
02437 && node_count > 100000) {
02438 std::cerr << "dfpn proof simulation > 100000 throw " << thread_id << "\n";
02439 throw DepthLimitReached();
02440 }
02441 assert(tree->depth + 2 < MaxDepth);
02442 if (tree->depth + 2 >= MaxDepth) {
02443 std::cerr << "throw " << thread_id << "\n";
02444 throw DepthLimitReached();
02445 }
02446 record = table->probe<P>(node.hash_key, node.white_stand);
02447 if (record.proof_disproof.isFinal())
02448 return;
02449 #if (defined CHECKMATE_A3_SIMULLATION) || (defined CHECKMATE_A3)
02450 if (record.node_count == 0)
02451 {
02452 #ifdef DFPN_STAT
02453 static stat::Ratio oracle_success("a3-simulation");
02454 #endif
02455 FixedDepthSearcher fixed_searcher(tree->state);
02456 PieceStand proof_pieces;
02457 ProofDisproof pdp = fixed_searcher.hasCheckmateMove<P>(2, record.best_move, proof_pieces);
02458 ++node_count;
02459 #ifdef DFPN_STAT
02460 oracle_success.add(pdp.isCheckmateSuccess());
02461 #endif
02462 if (pdp.isCheckmateSuccess()) {
02463 record.proof_disproof = pdp;
02464 record.setProofPieces(proof_pieces);
02465 record.node_count++;
02466 return;
02467 }
02468 }
02469 #elif (!defined CHECKMATE_D2) && (!defined NO_IMMEDIATE_CHECKMATE)
02470 if (! tree->inCheck(P)
02471 && ImmediateCheckmate::hasCheckmateMove<P>(tree->state, record.best_move)) {
02472 PieceStand proof_pieces;
02473 if (record.best_move.isDrop())
02474 proof_pieces.add(record.best_move.ptype());
02475 record.setProofPieces(proof_pieces);
02476 record.proof_disproof = ProofDisproof::Checkmate();
02477 return;
02478 }
02479 #endif
02480 #ifndef NDEBUG
02481 if (tree->depth > 1000) {
02482 std::cerr << tree->state;
02483 node.hash_key.dumpContents(std::cerr);
02484 std::cerr << "\n";
02485 table->showProofOracles<P>(key.key, key.white_stand, node.moved);
02486 }
02487 #endif
02488 DfpnRecord oracle = table->findProofOracle<P>(key.key, key.white_stand, node.moved);
02489 if (! oracle.proof_disproof.isCheckmateSuccess() || ! oracle.best_move.isNormal())
02490 return;
02491 const Move check_move = OracleAdjust::attack(tree->state, oracle.best_move);
02492 if (! check_move.isNormal() || ! key.traceable(P, check_move))
02493 return;
02494
02495 node.allocate(1);
02496 node.moves.clear();
02497 node.moves.push_back(check_move);
02498 const HashKey new_key = node.hash_key.newHashWithMove(node.moves[0]);
02499 if (UseTable) {
02500 node.children[0] = table->probe<P>(new_key, node.nextWhiteStand(P, node.moves[0]));
02501 node.children_path[0] = path_table->probe(new_key);
02502 if (node.isLoop(0))
02503 return;
02504 } else {
02505 node.children[0] = DfpnRecord();
02506 node.children_path[0] = 0;
02507 }
02508
02509 if (! UseTable || ! node.children[0].proof_disproof.isFinal()) {
02510 Node& next = tree->node[tree->depth+1];
02511 tree->newVisit(P, node.moves[0], new_key);
02512
02513 CallProofOracleDefense<P,UseTable> helper(this, key.newOracle(P, check_move));
02514 tree->depth += 1;
02515 ApplyMoveWithPath<P>::doUndoMove(tree->state, next.path, next.moved, helper);
02516 tree->depth -= 1;
02517 node.children[0] = next.record;
02518 node.children_path[0] = next.path_record;
02519
02520 if (next.record.proof_disproof == ProofDisproof::NoEscape()
02521 && next.moved.isDrop() && next.moved.ptype() == PAWN)
02522 node.children[0].proof_disproof = ProofDisproof::PawnCheckmate();
02523 }
02524 if (node.children[0].proof_disproof.isCheckmateSuccess()) {
02525 node.setCheckmateAttack(P,0);
02526 record.node_count += node_count - node_count_org;
02527 if (UseTable || node_count - node_count_org > 32) {
02528 record.last_move = node.moved;
02529 table->store(node.hash_key, record);
02530 }
02531 }
02532 }
02533
02534 template <osl::Player P, bool UseTable>
02535 void osl::checkmate::
02536 Dfpn::proofOracleDefense(const ProofOracle& key)
02537 {
02538 #ifdef DFPN_DEBUG
02539 Tree::Logging logging(tree.get(), table, UseTable ? "tpdefe" : "pdefen");
02540 #endif
02541 const int my_distance = (UseTable && tree->depth) ? (tree->node[tree->depth-1].path_record->distance+1) : 0;
02542 Node& node = tree->node[tree->depth];
02543 LoopToDominance loop;
02544 DfpnVisitLock<UseTable> lk((node.path_record = (UseTable ? path_table->allocate<P>(node.hash_key, my_distance, loop) : 0)));
02545 DfpnRecord& record = node.record;
02546 if (UseTable && loop == BadAttackLoop) {
02547 record = DfpnRecord();
02548 node.setLoopDetection();
02549 return;
02550 }
02551 if (! UseTable && tree->depth >= 4) {
02552 if (tree->node[tree->depth-4].hash_key == node.hash_key
02553 || (tree->depth >= 6 && tree->node[tree->depth-6].hash_key == node.hash_key)) {
02554 record = DfpnRecord();
02555 return;
02556 }
02557 }
02558 const size_t node_count_org = node_count++;
02559 assert(node.white_stand == PieceStand(WHITE, tree->state));
02560 if (! tree->inCheck(alt(P)) || tree->inCheck(P)) {
02561 record = DfpnRecord();
02562 record.proof_disproof = ProofDisproof::NoCheckmate();
02563 return;
02564 }
02565
02566 record = table->probe<P>(node.hash_key, node.white_stand);
02567 if (record.proof_disproof.isFinal())
02568 return;
02569
02570 const bool grand_parent_simulation = grandParentSimulationSuitable();
02571 if (record.last_to == Position())
02572 record.last_to = grand_parent_simulation ? node.moved.to() : tree->king(alt(P)).position();
02573 const Position grand_parent_delay_last_to
02574 = (record.last_to != tree->king(alt(P)).position()) ? record.last_to : Position();
02575 generateEscape<P>(tree->state, true, grand_parent_delay_last_to, node.moves);
02576 if (node.moves.empty()) {
02577 record.setProofPieces(ProofPieces::leaf(tree->state, P, record.stands[P]));
02578 record.proof_disproof = ProofDisproof::NoEscape();
02579 return;
02580 }
02581
02582
02583 assert(node.children.empty());
02584 {
02585 node.allocate(node.moves.size());
02586 for (size_t i=0;i <node.moves.size(); ++i) {
02587 #ifdef MEMORIZE_SOLVED_IN_BITSET
02588 if (record.solved & (1ull << i))
02589 continue;
02590 #endif
02591 const HashKey& new_key = node.hashes[i] = node.hash_key.newHashWithMove(node.moves[i]);
02592 node.children[i] = UseTable
02593 ? table->probe<P>(new_key, node.nextWhiteStand(alt(P), node.moves[i]))
02594 : DfpnRecord();
02595 if (node.children[i].proof_disproof.isCheckmateSuccess()) {
02596 node.setCheckmateChildInDefense(i);
02597 }
02598 #ifdef CHECKMATE_D2
02599 else if (node.record.node_count == 0 && node.children[i].node_count == 0) {
02600 FixedDepthSearcher fixed_searcher(tree->state);
02601 PieceStand proof_pieces;
02602 Move check_move;
02603 DfpnRecord old_child = node.children[i];
02604 node.children[i].proof_disproof
02605 = fixed_searcher.hasEscapeByMove<P>(node.moves[i], 0, check_move, proof_pieces);
02606 if (node.children[i].proof_disproof.isCheckmateSuccess()) {
02607 node.children[i].best_move = check_move;
02608 node.children[i].setProofPieces(proof_pieces);
02609 node.setCheckmateChildInDefense(i);
02610 }
02611 else {
02612 if (node.children[i].proof_disproof.isCheckmateFail())
02613 node.children[i].proof_disproof = ProofDisproof(1,1);
02614 }
02615 ++node_count;
02616 }
02617 #endif
02618 if (node.children[i].proof_disproof.isCheckmateFail()) {
02619 tree->setNoCheckmateDefense(P, i);
02620 if (UseTable)
02621 table->store(node.hash_key, record);
02622 return;
02623 }
02624 node.children_path[i] = UseTable ? path_table->probe(new_key) : 0;
02625 }
02626 }
02627 assert(node.children.size() == node.moves.size());
02628 if (UseTable) {
02629 for (size_t i=0; i<node.children.size(); ++i) {
02630 if (node.isLoop(i)) {
02631 node.setLoopDetection();
02632 return;
02633 }
02634 }
02635 }
02636 unsigned int sum_proof=0, min_disproof=record.min_pdp;
02637 int num_proof = 0;
02638 for (size_t next_i=0; next_i<node.children.size(); ++next_i) {
02639 #ifdef MEMORIZE_SOLVED_IN_BITSET
02640 if (record.solved & (1ull << next_i))
02641 continue;
02642 #endif
02643 if (node.children[next_i].proof_disproof.isCheckmateSuccess()) {
02644 min_disproof = std::min(min_disproof, node.children[next_i].disproof());
02645 continue;
02646 }
02647 if (! key.traceable(alt(P), node.moves[next_i])) {
02648 ++sum_proof;
02649 min_disproof = 1;
02650 if (! UseTable)
02651 break;
02652 continue;
02653 }
02654 const Position next_to = node.moves[next_i].to();
02655 if (sum_proof && tree->state.hasEffectBy(P, next_to)
02656 && (! tree->state.hasEffectBy(alt(P), next_to)
02657 || (tree->state.countEffect(alt(P), next_to) == 1
02658 && ! node.moves[next_i].isDrop())))
02659 continue;
02660 assert(! node.isLoop(next_i));
02661 Node& next = tree->node[tree->depth+1];
02662 #ifdef MEMORIZE_SOLVED_IN_BITSET
02663 assert(! (record.solved & (1ull << next_i)));
02664 #endif
02665 tree->newVisit(alt(P), node.moves[next_i], node.hashes[next_i]);
02666
02667 CallProofOracleAttack<P,UseTable> helper(this, key.newOracle(alt(P), node.moves[next_i]));
02668 tree->depth += 1;
02669 ApplyMoveWithPath<PlayerTraits<P>::opponent>::doUndoMove(tree->state, next.path, node.moves[next_i], helper);
02670 tree->depth -= 1;
02671
02672 node.children[next_i] = next.record;
02673 node.children_path[next_i] = next.path_record;
02674 if (next.record.proof_disproof.isCheckmateFail()) {
02675 if (record.proof_disproof.isLoopDetection())
02676 node.setLoopDetection();
02677 else
02678 tree->setNoCheckmateDefense(P, next_i);
02679 record.node_count += node_count - node_count_org;
02680 if (UseTable)
02681 table->store(node.hash_key, record);
02682 return;
02683 }
02684 if (next.record.proof_disproof.isCheckmateSuccess()) {
02685 node.setCheckmateChildInDefense(next_i);
02686 ++num_proof;
02687 }
02688 sum_proof += next.record.proof();
02689 min_disproof = std::min(min_disproof, next.record.disproof());
02690 if (sum_proof && ! UseTable)
02691 break;
02692 }
02693 if (sum_proof == 0) {
02694 node.record.proof_disproof = ProofDisproof(sum_proof, min_disproof);
02695 node.setCheckmateDefense(P, tree->state);
02696 }
02697 }
02698
02699 template <osl::Player P>
02700 void osl::checkmate::
02701 Dfpn::blockingSimulation(int oracle_i, const ProofOracle& oracle)
02702 {
02703 #ifdef DFPN_DEBUG
02704 Tree::Logging logging(tree.get(), table, "blocks");
02705 #endif
02706 #ifdef DFPN_STAT
02707 static stat::Ratio oracle_success("blocking proof");
02708 #endif
02709 Node& node = tree->node[tree->depth];
02710 Node& next = tree->node[tree->depth+1];
02711 const Move oracle_move = node.moves[oracle_i];
02712 const Position to = oracle_move.to();
02713 assert((node.record.solved & (1ull << oracle_i))
02714 || node.children[oracle_i].proof_disproof.isCheckmateSuccess());
02715 for (size_t i=0; i<node.moves.size(); ++i) {
02716 #ifdef MEMORIZE_SOLVED_IN_BITSET
02717 if (node.record.solved & (1ull << i))
02718 continue;
02719 #endif
02720 if (node.isLoop(i))
02721 break;
02722 if (node.children[i].proof_disproof.isFinal() || node.moves[i].to() != to)
02723 continue;
02724 if (! oracle.traceable(alt(P), node.moves[i]))
02725 continue;
02726 #ifdef MEMORIZE_SOLVED_IN_BITSET
02727 assert(! (node.record.solved & (1ull << i)));
02728 #endif
02729 tree->newVisit(alt(P), node.moves[i], node.hashes[i]);
02730 CallProofOracleAttack<P,true> helper(this, oracle);
02731
02732 tree->depth += 1;
02733 ApplyMoveWithPath<PlayerTraits<P>::opponent>::doUndoMove(tree->state, next.path, node.moves[i], helper);
02734 tree->depth -= 1;
02735
02736 node.children[i] = next.record;
02737 node.children_path[i] = next.path_record;
02738
02739 #ifdef DFPN_STAT
02740 oracle_success.add(next.record.proof_disproof.isCheckmateSuccess());
02741 #endif
02742 if (next.record.proof_disproof.isCheckmateSuccess()) {
02743 node.setCheckmateChildInDefense(i);
02744 }
02745 }
02746 }
02747
02748 template <osl::Player P>
02749 void osl::checkmate::
02750 Dfpn::grandParentSimulation(int cur_i, const Node& gparent, int gp_i)
02751 {
02752 #ifdef DFPN_DEBUG
02753 Tree::Logging logging(tree.get(), table, "grands");
02754 #endif
02755 #ifdef DFPN_STAT
02756 static stat::Ratio oracle_success("grandparent proof", true);
02757 #endif
02758 Node& node = tree->node[tree->depth];
02759 Node& next = tree->node[tree->depth+1];
02760
02761 const Move move = gparent.moves[gp_i];
02762 assert(move == node.moves[cur_i]);
02763 const HashKey& oracle_hash = (gparent.record.solved & (1ull << gp_i))
02764 ? gparent.hash_key.newHashWithMove(move)
02765 : gparent.hashes[gp_i];
02766 const ProofOracle oracle(oracle_hash, gparent.nextWhiteStand(alt(P), move));
02767
02768 tree->newVisit(alt(P), node.moves[cur_i], node.hashes[cur_i]);
02769 CallProofOracleAttack<P,true> helper(this, oracle);
02770
02771 tree->depth += 1;
02772 ApplyMoveWithPath<PlayerTraits<P>::opponent>::doUndoMove(tree->state, next.path, move, helper);
02773 tree->depth -= 1;
02774
02775 node.children[cur_i] = next.record;
02776 node.children_path[cur_i] = next.path_record;
02777 #ifdef DFPN_STAT
02778 oracle_success.add(next.record.proof_disproof.isCheckmateSuccess());
02779 #endif
02780 }
02781
02782
02783 int osl::checkmate::
02784 Dfpn::distance(const HashKey& key) const
02785 {
02786 const DfpnPathRecord *record = path_table->probe(key);
02787 if (record)
02788 return record->distance;
02789 return -1;
02790 }
02791
02792
02793
02794
02795
02796