00001
00002
00003 #include "osl/checkmate/dualDfpn.h"
00004 #include "osl/checkmate/dfpn.h"
00005 #include "osl/checkmate/dfpnRecord.h"
00006 #ifdef OSL_DFPN_SMP
00007 # include "osl/checkmate/dfpnParallel.h"
00008 #endif
00009 #include "osl/repetitionCounter.h"
00010 #include "osl/stl/hash_map.h"
00011 #include "osl/stl/slist.h"
00012 #include "osl/stat/average.h"
00013 #include "osl/centering3x3.h"
00014 #include "osl/centering5x3.h"
00015 #ifdef OSL_SMP
00016 # include "osl/misc/lightMutex.h"
00017 #endif
00018 #ifdef OSL_SHOW_PROOF_TREE_MIGRATION_STAT
00019 # include "osl/stat/ratio.h"
00020 #endif
00021 #include "osl/misc/milliSeconds.h"
00022 #include <boost/foreach.hpp>
00023 #include <iostream>
00024 #include <iomanip>
00025
00026 #define DFPN_SHARE_TABLE
00027
00028 static const int max_oracle_list_size = 2;
00029 static const size_t local_table_growth_limit = 40000;
00030 struct osl::checkmate::DualDfpn::OraclePool
00031 {
00032 struct Element
00033 {
00034 Dfpn::ProofOracle oracle;
00035 PieceStand proof_pieces;
00036 unsigned int id;
00037 bool in_check;
00038 Element() : oracle(HashKey(), PieceStand()), id((unsigned int)-1), in_check(false)
00039 {
00040 }
00041 Element(const Dfpn::ProofOracle& o, PieceStand p, size_t i, bool c) : oracle(o), proof_pieces(p), id(i), in_check(c)
00042 {
00043 }
00044 };
00045 struct List : FixedCapacityVector<Element, max_oracle_list_size>
00046 {
00047 void add(const Element& e)
00048 {
00049 if (size() == capacity())
00050 back() = e;
00051 else
00052 push_back(e);
00053 }
00054 };
00055 #ifdef OSL_SMP
00056 typedef osl::misc::LightMutex Mutex;
00057 mutable Mutex mutex;
00058 #endif
00059 typedef hash_map<HashKey, List> table_t;
00060 table_t table;
00061 Player defender;
00062 void setAttack(Player attack)
00063 {
00064 defender = alt(attack);
00065 }
00066 void addProof(const NumEffectState& state, const HashKey& key, PieceStand proof_pieces)
00067 {
00068 const Dfpn::ProofOracle oracle(key, PieceStand(WHITE, state));
00069 const std::pair<HashKey,HashKey> king = makeLargeKey(state);
00070 #ifdef OSL_SMP
00071 SCOPED_LOCK(lk,mutex);
00072 #endif
00073 const Element e(oracle, proof_pieces, table.size(), state.inCheck());
00074 table[king.first].add(e);
00075 table[king.second].add(e);
00076 }
00077 const List probe(const NumEffectState& state) const
00078 {
00079 const std::pair<HashKey,HashKey> key = makeLargeKey(state);
00080 #ifdef OSL_SMP
00081 SCOPED_LOCK(lk,mutex);
00082 #endif
00083 table_t::const_iterator p = table.find(key.first);
00084 if (p != table.end())
00085 return p->second;
00086 p = table.find(key.second);
00087 if (p != table.end())
00088 return p->second;
00089 return List();
00090 }
00091
00092 template <Direction DIR>
00093 static void addKey(HashKey& key, const SimpleState& state, Position target)
00094 {
00095 const Offset offset = DirectionTraits<DIR>::blackOffset();
00096 target += offset;
00097 const Piece piece = state.getPieceOnBoard(target);
00098 hash::Hash_Gen_Table.addHashKey(key, target, piece.ptypeO());
00099 }
00100 template <Direction DIR, Direction DIR2>
00101 static void addKey(HashKey& key, const SimpleState& state, Position target)
00102 {
00103 const Offset offset = DirectionTraits<DIR>::blackOffset()
00104 + DirectionTraits<DIR2>::blackOffset();
00105 target += offset;
00106 const Piece piece = state.getPieceOnBoard(target);
00107 hash::Hash_Gen_Table.addHashKey(key, target, piece.ptypeO());
00108 }
00109 const HashKey makeKey(const SimpleState& state) const
00110 {
00111 const Position target_king=state.getKingPosition(defender);
00112 const Position center = Centering3x3::adjustCenter(target_king);
00113 HashKey key;
00114 hash::Hash_Gen_Table.addHashKey(key, center,
00115 state.getPieceOnBoard(center).ptypeO());
00116 addKey<UL>(key, state, center); addKey<U> (key, state, center);
00117 addKey<UR>(key, state, center);
00118 addKey<L> (key, state, center); addKey<R> (key, state, center);
00119 addKey<DL>(key, state, center); addKey<D> (key, state, center);
00120 addKey<DR>(key, state, center);
00121 return key;
00122 }
00123 const std::pair<HashKey,HashKey> makeLargeKey(const SimpleState& state) const
00124 {
00125 HashKey key_small = makeKey(state), key_large;
00126 const Position target_king=state.getKingPosition(defender);
00127 const Position center = Centering5x3::adjustCenter(target_king);
00128 hash::Hash_Gen_Table.addHashKey(key_large, center,
00129 state.getPieceOnBoard(center).ptypeO());
00130 addKey<UL>(key_large, state, center); addKey<U> (key_large, state, center);
00131 addKey<UR>(key_large, state, center);
00132 addKey<L> (key_large, state, center); addKey<R> (key_large, state, center);
00133 addKey<DL>(key_large, state, center); addKey<D> (key_large, state, center);
00134 addKey<DR>(key_large, state, center);
00135 addKey<L,UL>(key_large, state, center); addKey<L,L> (key_large, state, center);
00136 addKey<L,DL>(key_large, state, center);
00137 addKey<R,UR>(key_large, state, center); addKey<R,R> (key_large, state, center);
00138 addKey<R,DR>(key_large, state, center);
00139 return std::make_pair(key_large, key_small);
00140 }
00141 };
00142
00143 struct osl::checkmate::DualDfpn::Shared
00144 {
00145 CArray<DfpnTable,2> table;
00146 CArray<OraclePool,2> pool;
00147 size_t main_node_count;
00148 size_t simulation_count;
00149 CArray<stat::Average,max_oracle_list_size> proof_by_oracle;
00150 CArray<bool,2> blocking_verify;
00151 #ifdef OSL_SMP
00152 LightMutex mutex;
00153 CArray<LightMutex,max_oracle_list_size> proof_by_oracle_mutex;
00154 #endif
00155 #ifdef OSL_DFPN_SMP
00156 boost::scoped_ptr<DfpnParallel> parallel_search;
00157 #endif
00158 typedef slist<PathEncoding> disproof_list_t;
00159 typedef hash_map<HashKey, disproof_list_t> disproof_table_t;
00160 disproof_table_t disproof_table;
00161
00162 Shared() : main_node_count(0), simulation_count(0)
00163 {
00164 table[BLACK].setAttack(BLACK);
00165 table[WHITE].setAttack(WHITE);
00166 pool[BLACK].setAttack(BLACK);
00167 pool[WHITE].setAttack(WHITE);
00168 blocking_verify.fill(true);
00169 }
00170 ~Shared()
00171 {
00172 showStats();
00173 }
00174 void showStats()
00175 {
00176 if (main_node_count || simulation_count) {
00177 #ifdef DFPN_DEBUG
00178 std::cerr << "shared " << main_node_count << " " << simulation_count << "\n";
00179 BOOST_FOREACH(stat::Average& a, proof_by_oracle)
00180 std::cerr << a.getAverage()
00181 << " " << (int)(a.getAverage()*a.numElements()) << "\n";
00182 std::cerr << "oracles " << pool[BLACK].table.size() << " " << pool[WHITE].table.size() << "\n";
00183 std::cerr << "table " << table[0].totalSize() << " " << table[1].totalSize() << "\n";
00184 table[0].showStats();
00185 table[1].showStats();
00186 #endif
00187 }
00188 }
00189 void addMainNodeCount(int add)
00190 {
00191 #ifdef OSL_SMP
00192 SCOPED_LOCK(lk,mutex);
00193 #endif
00194 main_node_count += add;
00195 }
00196 void addSimulationNodeCount(int add)
00197 {
00198 #ifdef OSL_SMP
00199 SCOPED_LOCK(lk,mutex);
00200 #endif
00201 simulation_count += add;
00202 }
00203 };
00204
00205 struct osl::checkmate::DualDfpn::Local
00206 {
00207 Dfpn dfpn;
00208 #ifndef DFPN_SHARE_TABLE
00209 CArray<DfpnTable,2> table;
00210 #endif
00211 CArray<DfpnTable,2> table_small;
00212 size_t local_node_count;
00213 Local() : local_node_count(0)
00214 {
00215 #ifndef DFPN_SHARE_TABLE
00216 table[BLACK].setAttack(BLACK);
00217 table[WHITE].setAttack(WHITE);
00218 #endif
00219 table_small[BLACK].setAttack(BLACK);
00220 table_small[WHITE].setAttack(WHITE);
00221 }
00222 ~Local()
00223 {
00224 #ifdef DFPN_DEBUG
00225 std::cerr << "local " << table_small[0].totalSize()
00226 << " " << table_small[1].totalSize() << "\n";
00227 #endif
00228 }
00229 };
00230
00231
00232
00233 osl::checkmate::
00234 DualDfpn::DualDfpn(uint64_t )
00235 : shared(new Shared), local(new Local)
00236 {
00237 }
00238
00239 osl::checkmate::
00240 DualDfpn::DualDfpn(const DualDfpn& src)
00241 : shared(src.shared), local(new Local)
00242 {
00243 }
00244
00245 osl::checkmate::
00246 DualDfpn::~DualDfpn()
00247 {
00248 }
00249
00250 osl::checkmate::Dfpn& osl::checkmate::
00251 DualDfpn::prepareDfpn(Player attack)
00252 {
00253 #ifdef DFPN_SHARE_TABLE
00254 local->dfpn.setTable(&(shared->table[attack]));
00255 #else
00256 local->dfpn.setTable(&(local->table[attack]));
00257 #endif
00258 local->dfpn.setBlockingVerify(shared->blocking_verify[attack]);
00259 return local->dfpn;
00260 }
00261
00262 osl::checkmate::Dfpn& osl::checkmate::
00263 DualDfpn::prepareDfpnSmall(Player attack)
00264 {
00265 local->dfpn.setTable(&(local->table_small[attack]));
00266 local->dfpn.setBlockingVerify(shared->blocking_verify[attack]);
00267 return local->dfpn;
00268 }
00269
00270 void osl::checkmate::
00271 DualDfpn::runGC()
00272 {
00273 #ifdef DFPN_SHARE_TABLE
00274 size_t removed = 0;
00275 size_t total = shared->table[BLACK].size() + shared->table[WHITE].size();
00276 if (total < local_table_growth_limit*8)
00277 return;
00278 # ifdef DFPN_DEBUG
00279 MilliSeconds start = MilliSeconds::now();
00280 #endif
00281 {
00282 # ifdef OSL_SMP
00283 SCOPED_LOCK(lk,shared->mutex);
00284 # endif
00285 removed += shared->table[BLACK].smallTreeGC();
00286 removed += shared->table[WHITE].smallTreeGC();
00287 }
00288 # ifdef DFPN_DEBUG
00289 const double elapsed = start.elapsedSeconds();
00290 if (removed > 10000 || elapsed > 0.1)
00291 std::cerr << " GC " << removed
00292 << " entries " << std::setprecision(3)
00293 << ((sizeof(HashKey)+sizeof(DfpnRecord)+sizeof(char*)*2)
00294 * removed / (1<<20)) << "MB "
00295 << 100.0*removed/total << "%"
00296 << " (" << elapsed << " s)\n";
00297 # endif
00298 #endif
00299 }
00300
00301
00302 template <osl::Player P>
00303 bool
00304 #if (defined __GNUC__) && (! defined GPSONE) && (! defined GPSUSIONE)
00305 __attribute__ ((noinline))
00306 #endif
00307 osl::checkmate::
00308 DualDfpn::isWinningState(int node_limit, const NumEffectState& state,
00309 const HashKey& key, const PathEncoding& path,
00310 Move& best_move, Move last_move)
00311 {
00312 assert(state.getTurn() == P);
00313
00314 Dfpn& dfpn = prepareDfpn(P);
00315 const OraclePool::List l = shared->pool[P].probe(state);
00316 const PieceStand attack_stand = (P==BLACK) ? key.blackStand() : PieceStand(WHITE, state);
00317 int num_tried = 0;
00318 for (size_t i=0; i<l.size(); ++i)
00319 {
00320 if (! attack_stand.isSuperiorOrEqualTo(l[i].proof_pieces)
00321 || l[i].in_check != state.inCheck())
00322 continue;
00323 ++num_tried;
00324 const ProofDisproof pdp = (node_limit > 20)
00325 ? dfpn.tryProof(state, key, path, l[i].oracle, l[i].id, best_move, last_move)
00326 : dfpn.tryProofLight(state, key, path, l[i].oracle, l[i].id, best_move, last_move);
00327 const size_t count = dfpn.nodeCount();
00328 local->local_node_count += count;
00329 shared->addSimulationNodeCount(count);
00330 if (count) {
00331 #ifdef OSL_SMP
00332 SCOPED_LOCK(lk,shared->proof_by_oracle_mutex[i]);
00333 #endif
00334 shared->proof_by_oracle[i].add(pdp.isCheckmateSuccess());
00335 }
00336 if (pdp.isCheckmateSuccess())
00337 assert(best_move.isNormal());
00338 if (pdp.isFinal())
00339 return pdp.isCheckmateSuccess();
00340 }
00341 if (node_limit == 0 && num_tried)
00342 return false;
00343 const ProofDisproof table_pdp = dfpn.hasCheckmateMove(state, key, path, 0, best_move, last_move);
00344 if (table_pdp.isCheckmateSuccess())
00345 return true;
00346 {
00347 #ifdef OSL_SMP
00348 SCOPED_LOCK(lk,shared->mutex);
00349 #endif
00350 Shared::disproof_table_t::const_iterator p = shared->disproof_table.find(key);
00351 if (p != shared->disproof_table.end()) {
00352 BOOST_FOREACH(const PathEncoding& ppath, p->second)
00353 if (ppath == path)
00354 return false;
00355 }
00356 }
00357 #ifdef OSL_SHOW_PROOF_TREE_MIGRATION_STAT
00358 static stat::Ratio migration_success("migration_success", true);
00359 bool need_migration = false;
00360 #endif
00361
00362 if (node_limit < 80) {
00363 if (local->table_small[P].totalSize() >= local_table_growth_limit) {
00364 local->table_small[P].clear();
00365 }
00366 Dfpn& dfpn_small = prepareDfpnSmall(P);
00367 const ProofDisproof pdp = dfpn_small.hasCheckmateMove(state, key, path, node_limit, best_move, last_move);
00368 const size_t count = dfpn_small.nodeCount();
00369 local->local_node_count += count;
00370 shared->addMainNodeCount(count);
00371 if (pdp.isLoopDetection()) {
00372 #ifdef OSL_SMP
00373 SCOPED_LOCK(lk,shared->mutex);
00374 #endif
00375 shared->disproof_table[key].push_front(path);
00376 }
00377 if (! pdp.isCheckmateSuccess())
00378 return false;
00379 assert(best_move.isNormal());
00380
00381 #ifdef OSL_SHOW_PROOF_TREE_MIGRATION_STAT
00382 need_migration = true;
00383 #endif
00384 }
00385
00386 PieceStand proof_pieces;
00387 const ProofDisproof pdp = dfpn.hasCheckmateMove(state, key, path, node_limit, best_move, proof_pieces, last_move);
00388 const size_t count = dfpn.nodeCount();
00389 local->local_node_count += count;
00390 shared->addMainNodeCount(count);
00391 if (pdp.isCheckmateSuccess())
00392 shared->pool[P].addProof(state, key, proof_pieces);
00393 #ifdef OSL_SHOW_PROOF_TREE_MIGRATION_STAT
00394 if (need_migration)
00395 migration_success.add(pdp.isCheckmateSuccess());
00396 #endif
00397 if (pdp.isLoopDetection()) {
00398 #ifdef OSL_SMP
00399 SCOPED_LOCK(lk,shared->mutex);
00400 #endif
00401 shared->disproof_table[key].push_front(path);
00402 }
00403 if (pdp.isCheckmateSuccess())
00404 assert(best_move.isNormal());
00405 return pdp.isCheckmateSuccess();
00406 }
00407
00408 bool osl::checkmate::
00409 DualDfpn::isWinningState(int node_limit, const NumEffectState& state,
00410 const HashKey& key, const PathEncoding& path,
00411 Move& best_move, Move last_move)
00412 {
00413 if (state.getTurn() == BLACK)
00414 return isWinningState<BLACK>(node_limit, state, key, path, best_move, last_move);
00415 else
00416 return isWinningState<WHITE>(node_limit, state, key, path, best_move, last_move);
00417 }
00418
00419 #ifdef OSL_DFPN_SMP
00420 template <osl::Player P>
00421 bool
00422 #if (defined __GNUC__) && (! defined GPSONE) && (! defined GPSUSIONE)
00423 __attribute__ ((noinline))
00424 #endif
00425 osl::checkmate::
00426 DualDfpn::isWinningStateParallel(int node_limit, const NumEffectState& state,
00427 const HashKey& key, const PathEncoding& path,
00428 Move& best_move, Move last_move)
00429 {
00430 PieceStand proof_pieces;
00431 size_t count;
00432 ProofDisproof pdp;
00433 {
00434 #ifdef OSL_SMP
00435 SCOPED_LOCK(lk,shared->mutex);
00436 #endif
00437 if (! shared->parallel_search)
00438 shared->parallel_search.reset(new DfpnParallel(std::min(OslConfig::numCPUs(), 8)));
00439 #ifdef DFPN_SHARE_TABLE
00440 shared->parallel_search->setTable(&(shared->table[P]));
00441 #else
00442 shared->parallel_search->setTable(&(local->table[P]));
00443 #endif
00444
00445 pdp = shared->parallel_search->hasCheckmateMove
00446 (state, key, path, node_limit, best_move, proof_pieces, last_move);
00447 count = shared->parallel_search->nodeCount();
00448 }
00449 shared->addMainNodeCount(count);
00450 if (pdp.isCheckmateSuccess())
00451 shared->pool[P].addProof(state, key, proof_pieces);
00452 if (pdp.isLoopDetection()) {
00453 shared->disproof_table[key].push_front(path);
00454 }
00455 if (pdp.isCheckmateSuccess())
00456 assert(best_move.isNormal());
00457 return pdp.isCheckmateSuccess();
00458 }
00459
00460 bool osl::checkmate::
00461 DualDfpn::isWinningStateParallel(int node_limit, const NumEffectState& state,
00462 const HashKey& key, const PathEncoding& path,
00463 Move& best_move, Move last_move)
00464 {
00465 if (state.getTurn() == BLACK)
00466 return isWinningStateParallel<BLACK>(node_limit, state, key, path, best_move, last_move);
00467 else
00468 return isWinningStateParallel<WHITE>(node_limit, state, key, path, best_move, last_move);
00469 }
00470 #endif
00471
00472 template <osl::Player P>
00473 bool
00474 #if (defined __GNUC__) && (! defined GPSONE) && (! defined GPSUSIONE)
00475 __attribute__ ((noinline))
00476 #endif
00477 osl::checkmate::
00478 DualDfpn::isLosingState(int node_limit, NumEffectState& state,
00479 const HashKey& key, const PathEncoding& path,
00480 Move last_move)
00481 {
00482 assert(state.getTurn() == P);
00483 Dfpn& dfpn = prepareDfpn(alt(P));
00484 const ProofDisproof pdp = dfpn.hasEscapeMove(state, key, path, node_limit, last_move);
00485 const size_t count = dfpn.nodeCount();
00486 local->local_node_count += count;
00487 shared->addMainNodeCount(count);
00488 return pdp.isCheckmateSuccess();
00489 }
00490
00491 bool osl::checkmate::
00492 DualDfpn::isLosingState(int node_limit, NumEffectState& state,
00493 const HashKey& key, const PathEncoding& path,
00494 Move last_move)
00495 {
00496 if (state.getTurn() == BLACK)
00497 return isLosingState<BLACK>(node_limit, state, key, path, last_move);
00498 else
00499 return isLosingState<WHITE>(node_limit, state, key, path, last_move);
00500 }
00501
00502 void osl::checkmate::
00503 DualDfpn::writeRootHistory(const RepetitionCounter& counter,
00504 const MoveStack& moves,
00505 const SimpleState& state, Player attack)
00506 {
00507
00508 Dfpn& dfpn = prepareDfpn(attack);
00509 PieceStand white_stand(WHITE, state);
00510 for (int i=0; i<counter.checkCount(attack); ++i)
00511 {
00512 const HashKey& key = counter.history().top(i);
00513 if (key != counter.history().top(0))
00514 {
00515 dfpn.setIllegal(key, white_stand);
00516 }
00517 assert(moves.hasLastMove(i+1));
00518 if (! moves.hasLastMove(i+1))
00519 break;
00520 const Move last_move = moves.lastMove(i+1);
00521 if (last_move.isNormal())
00522 white_stand = white_stand.previousStand(WHITE, last_move);
00523 }
00524 }
00525
00526 void osl::checkmate::
00527 DualDfpn::setRootPlayer(Player root)
00528 {
00529 shared->blocking_verify[root] = true;
00530 shared->blocking_verify[alt(root)] = true;
00531 }
00532
00533 void osl::checkmate::
00534 DualDfpn::setVerbose(int )
00535 {
00536 }
00537
00538 int osl::checkmate::
00539 DualDfpn::distance(Player attack, const HashKey& key)
00540 {
00541 return prepareDfpn(attack).distance(key);
00542 }
00543
00544 size_t osl::checkmate::
00545 DualDfpn::mainNodeCount() const
00546 {
00547 #ifdef OSL_USE_RACE_DETECTOR
00548 SCOPED_LOCK(lk,shared->mutex);
00549 #endif
00550 return shared->main_node_count;
00551
00552 }
00553
00554 size_t osl::checkmate::
00555 DualDfpn::totalNodeCount() const
00556 {
00557 #ifdef OSL_USE_RACE_DETECTOR
00558 SCOPED_LOCK(lk,shared->mutex);
00559 #endif
00560 return shared->main_node_count + shared->simulation_count;
00561 }
00562
00563 const osl::checkmate::DfpnTable& osl::checkmate::
00564 DualDfpn::table(Player attack) const
00565 {
00566 return shared->table[attack];
00567 }
00568
00569
00570
00571
00572
00573