00001
00002
00003 #include "osl/ntesuki/ntesukiSearcher.h"
00004 #include "osl/ntesuki/ntesukiMove.h"
00005 #include "osl/ntesuki/ntesukiMoveList.h"
00006 #include "osl/ntesuki/ntesukiSimulationSearcher.h"
00007 #include "osl/apply_move/applyMoveWithPath.h"
00008 #include "osl/effect_util/effectUtil.h"
00009
00010 #ifdef NDEBUG
00011 # include "osl/ntesuki/ntesukiMove.tcc"
00012 # include "osl/ntesuki/ntesukiRecord.tcc"
00013 #endif
00014
00015 #include "osl/record/csaRecord.h"
00016 #include <climits>
00017 #include <list>
00018 #include <algorithm>
00019
00020 using namespace osl;
00021 using namespace osl::ntesuki;
00022
00023
00024
00025
00026
00027 const int READ_ATTACK_BACK_LIMIT = 5120;
00028
00029 #ifndef NDEBUG
00030 #define RETURN(val) \
00031 if (record->getValueWithPath<A>(pass_left, path).proof() == 0)\
00032 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() > ProofDisproof::DISPROOF_LIMIT);\
00033 if (record->getValueWithPath<A>(pass_left, path).disproof() == 0)\
00034 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() > ProofDisproof::PROOF_LIMIT);\
00035 ntesuki_assert(val.isFinal() == record->getValueWithPath<A>(pass_left, path).isFinal());\
00036 return val
00037 #else
00038 #define RETURN(val) return val
00039 #endif
00040
00041 #define RETURN_ON_STOP \
00042 if (node_count > read_node_limit || *stop_flag)\
00043 return
00044
00045
00046
00047
00048 static
00049 unsigned int addWithSaturation(unsigned int limit,
00050 unsigned int l, unsigned int r)
00051 {
00052 if (limit < l)
00053 return limit;
00054 const unsigned int sum = l+r;
00055 if (limit < sum)
00056 return limit;
00057
00058 if (sum < l)
00059 return limit;
00060 ntesuki_assert(r <= sum);
00061 return sum;
00062 }
00063
00064 struct PlayMoveLock
00065 {
00066 std::vector<Move>& mlist;
00067
00068 PlayMoveLock(std::vector<Move>& l,
00069 const osl::Move& m)
00070 : mlist(l)
00071 {
00072 mlist.push_back(m);
00073 }
00074
00075 ~PlayMoveLock()
00076 {
00077 mlist.pop_back();
00078 }
00079 };
00080
00081 struct LockGC
00082 {
00083 osl::ntesuki::NtesukiTable& table;
00084 LockGC(osl::ntesuki::NtesukiTable& t)
00085 : table(t)
00086 {
00087 table.lockGC();
00088 }
00089
00090 ~LockGC()
00091 {
00092 table.unlockGC();
00093 }
00094 };
00095
00096
00097
00098
00099 template <class Search,Player T>
00100 class
00101 osl::ntesuki::NtesukiSearcher::
00102 AttackHelper
00103 {
00104 unsigned int proof_limit, disproof_limit, pass_left;
00105 Search* searcher;
00106 NtesukiResult& result;
00107 NtesukiRecord* record;
00108 const NtesukiRecord* oracle_attack;
00109 const NtesukiRecord* oracle_defense;
00110 const Move last_move;
00111 public:
00112 AttackHelper(Search* searcher,
00113 NtesukiResult& result,
00114 NtesukiRecord* record,
00115 const NtesukiRecord* oracle_attack,
00116 const NtesukiRecord* oracle_defense,
00117 unsigned int proof_limit,
00118 unsigned int disproof_limit,
00119 unsigned int pass_left,
00120 const Move last_move)
00121 : proof_limit(proof_limit), disproof_limit(disproof_limit),
00122 pass_left(pass_left),
00123 searcher(searcher), result(result), record(record),
00124 oracle_attack(oracle_attack), oracle_defense(oracle_defense),
00125 last_move(last_move)
00126 {
00127 }
00128
00129 void operator()(Position last_to)
00130 {
00131 result = (*searcher).template defense<PlayerTraits<T>::opponent>
00132 (record, oracle_attack, oracle_defense,
00133 proof_limit, disproof_limit, pass_left, last_move);
00134 }
00135 };
00136
00137
00138
00139
00140 template <class Search,Player T>
00141 class
00142 osl::ntesuki::NtesukiSearcher::
00143 CallSimulationAttack
00144 {
00145 Search &simulator;
00146 NtesukiTable& table;
00147 NtesukiRecord *record;
00148 const NtesukiRecord *record_orig;
00149 unsigned int pass_left;
00150 bool& simulation_result;
00151 const Move last_move;
00152
00153 public:
00154 CallSimulationAttack(Search& simulator,
00155 NtesukiTable& table,
00156 NtesukiRecord *record,
00157 const NtesukiRecord *record_orig,
00158 unsigned int pass_left,
00159 bool& simulation_result,
00160 const Move last_move)
00161 : simulator(simulator), table(table),
00162 record(record), record_orig(record_orig),
00163 pass_left(pass_left),
00164 simulation_result(simulation_result),
00165 last_move(last_move)
00166 {
00167 }
00168
00169 void operator()(Position last_to)
00170 {
00171 LockGC glock(table);
00172 simulation_result = simulator.template
00173 startFromDefenseDisproof<PlayerTraits<T>::opponent>
00174 (record, record_orig, pass_left, last_move);
00175 }
00176 };
00177
00178
00179
00180
00181 template <class Search,Player T>
00182 class
00183 osl::ntesuki::NtesukiSearcher::
00184 DefenseHelper
00185 {
00186 unsigned int proof_limit, disproof_limit, pass_left;
00187 Search* searcher;
00188 NtesukiResult& result;
00189 NtesukiRecord* record;
00190 const NtesukiRecord* oracle_attack;
00191 const NtesukiRecord* oracle_defense;
00192 const Move last_move;
00193
00194 public:
00195 DefenseHelper(Search* searcher,
00196 NtesukiResult& result,
00197 NtesukiRecord* record,
00198 const NtesukiRecord* oracle_attack,
00199 const NtesukiRecord* oracle_defense,
00200 unsigned int proof_limit,
00201 unsigned int disproof_limit,
00202 unsigned int pass_left,
00203 const Move last_move)
00204 : proof_limit(proof_limit), disproof_limit(disproof_limit),
00205 pass_left(pass_left), searcher(searcher), result(result), record(record),
00206 oracle_attack(oracle_attack), oracle_defense(oracle_defense),
00207 last_move(last_move)
00208 {
00209 }
00210
00211 void operator()(Position p)
00212 {
00213 (*searcher).template attack<PlayerTraits<T>::opponent>
00214 (record, oracle_attack, oracle_defense,
00215 proof_limit, disproof_limit,
00216 pass_left, last_move);
00217 }
00218 };
00219
00220
00221
00222
00223 template <class Search,Player T>
00224 class
00225 osl::ntesuki::NtesukiSearcher::
00226 CallSimulationDefense
00227 {
00228 Search &simulator;
00229 NtesukiTable &table;
00230 NtesukiRecord *record;
00231 const NtesukiRecord *record_orig;
00232 unsigned int pass_left;
00233 bool& simulation_result;
00234 const Move last_move;
00235 public:
00236 CallSimulationDefense(Search& simulator,
00237 NtesukiTable& table,
00238 NtesukiRecord *record,
00239 const NtesukiRecord *record_orig,
00240 unsigned int pass_left,
00241 bool& simulation_result,
00242 const Move last_move)
00243 : simulator(simulator), table(table),
00244 record(record), record_orig(record_orig),
00245 pass_left(pass_left),
00246 simulation_result(simulation_result),
00247 last_move(last_move)
00248 {
00249 }
00250
00251 void operator()(Position last_to)
00252 {
00253 LockGC glock(table);
00254 simulation_result = simulator.template
00255 startFromAttackProof<PlayerTraits<T>::opponent>
00256 (record, record_orig, pass_left, last_move);
00257 }
00258 };
00259
00260
00261
00262
00263 template <class Search,Player T>
00264 class
00265 osl::ntesuki::NtesukiSearcher::
00266 CallSimulationDefenseDisproof
00267 {
00268 Search &simulator;
00269 NtesukiTable &table;
00270 NtesukiRecord *record;
00271 const NtesukiRecord *record_orig;
00272 unsigned int pass_left;
00273 bool& simulation_result;
00274 const Move last_move;
00275 public:
00276 CallSimulationDefenseDisproof(Search& simulator,
00277 NtesukiTable& table,
00278 NtesukiRecord *record,
00279 const NtesukiRecord *record_orig,
00280 unsigned int pass_left,
00281 bool& simulation_result,
00282 const Move last_move)
00283 : simulator(simulator), table(table),
00284 record(record), record_orig(record_orig),
00285 pass_left(pass_left),
00286 simulation_result(simulation_result),
00287 last_move(last_move)
00288 {
00289 }
00290
00291 void operator()(Position last_to)
00292 {
00293 LockGC glock(table);
00294 simulation_result = simulator.template
00295 startFromAttackDisproof<PlayerTraits<T>::opponent>
00296 (record, record_orig, pass_left, last_move);
00297 }
00298 };
00299
00300 template <Player T>
00301 void osl::ntesuki::NtesukiSearcher::
00302 simulateSiblingsFail(NtesukiRecord *record,
00303 NtesukiRecord *record_best,
00304 int pass_left,
00305 unsigned int& success_count,
00306 unsigned int& total_count)
00307 {
00308 LockGC glock(table);
00309
00310 const Player A = T;
00311 if (!record_best) return;
00312 ntesuki_assert(record_best);
00313
00314 NtesukiMoveList moves;
00315 mg->generate<T>(state, moves);
00316
00317 for (NtesukiMoveList::iterator move_it = moves.begin();
00318 move_it != moves.end(); move_it++)
00319 {
00320 NtesukiMove& move = *move_it;
00321 NtesukiRecord *record_child = table.allocateWithMove(record,
00322 move);
00323 if (record_child == 0)
00324 {
00325 *stop_flag = TableLimitReached;
00326 return;
00327 }
00328 ntesuki_assert(record_child);
00329 if (record_child == record_best) continue;
00330 if (record_child->isVisited()) continue;
00331 if (move.isCheckmateFail<A>(pass_left)) continue;
00332 const PathEncoding path_child(path, move.getMove());
00333 const NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
00334 path_child);
00335 if (result_child.isFinal())
00336 {
00337 continue;
00338 }
00339
00340 bool simulation_result;
00341 total_count++;
00342 CallSimulationAttack<NtesukiSimulationSearcher, T>
00343 helper(simulator, table, record_child, record_best,
00344 pass_left, simulation_result, move.getMove());
00345 TRY_DFPN;
00346 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move.getMove(), helper);
00347 CATCH_DFPN;
00348 RETURN_ON_STOP;
00349
00350 if (simulation_result)
00351 {
00352 success_count++;
00353 ntesuki_assert(record_child->getValueWithPath<A>(pass_left,
00354 path_child).isCheckmateFail());
00355 TRY_DFPN;
00356 move.setBySimulation();
00357 move.setCheckmateFail<A>(pass_left);
00358 CATCH_DFPN;
00359 }
00360 }
00361 }
00362
00363
00364
00365
00366 class CountChildLock
00367 {
00368 public:
00369 CountChildLock(NtesukiRecord* r,
00370 const NtesukiTable& t)
00371 : record(r), table(t)
00372 {
00373 size_start = table.size();
00374 }
00375
00376 ~CountChildLock()
00377 {
00378 record->addChildCount(table.size() - size_start);
00379 }
00380 private:
00381 osl::ntesuki::NtesukiRecord* record;
00382 const osl::ntesuki::NtesukiTable& table;
00383 unsigned int size_start;
00384 };
00385
00386
00387
00388
00389
00390 template <Player T>
00391 NtesukiResult osl::ntesuki::NtesukiSearcher::
00392 attack(NtesukiRecord* record,
00393 const NtesukiRecord* oracle_attack,
00394 const NtesukiRecord* oracle_defense,
00395 unsigned int proof_limit, unsigned int disproof_limit,
00396 const int pass_left, const Move last_move)
00397 {
00398 CountChildLock cclock(record, table);
00399 const Player A = T;
00400 #ifndef NDEBUG
00401 const Player D = PlayerTraits<T>::opponent;
00402 #endif
00403
00404 ntesuki_assert(T == state.getTurn());
00405 ntesuki_assert(!state.inCheck(D));
00406 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
00407 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
00408 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).proof()
00409 < proof_limit);
00410 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).disproof()
00411 < disproof_limit);
00412
00413 RETURN_ON_STOP (record->getValueOr<A>(pass_left, path, iwscheme));
00414
00415 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).proof()
00416 < proof_limit);
00417 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).disproof()
00418 < disproof_limit);
00419 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
00420
00421 ntesuki_assert(proof_limit > 0);
00422 ntesuki_assert(disproof_limit > 0);
00423
00424
00425
00426 TRY_DFPN;
00427 if (record->setUpNode<T>())
00428 {
00429 const NtesukiResult result_cur =
00430 record->getValueWithPath<A>(pass_left, path);
00431 if (result_cur.isCheckmateSuccess())
00432 {
00433
00434 ++immediate_win;
00435 RETURN (result_cur);
00436 }
00437 else if (result_cur.isCheckmateFail() && pass_left == 0)
00438 {
00439 RETURN (result_cur);
00440 }
00441 }
00442 CATCH_DFPN;
00443
00444
00445
00446 NtesukiResult result_cur = record->getValueOr<A>(pass_left, path, iwscheme);
00447 while ((result_cur.proof() < proof_limit) &&
00448 (result_cur.disproof() < disproof_limit))
00449 {
00450 if (iwscheme == NtesukiRecord::no_iw)
00451 {
00452
00453
00454 TRY_DFPN;
00455 attackWithOrder<T>(record, NULL, NULL,
00456 proof_limit, disproof_limit,
00457 pass_left, last_move);
00458
00459 CATCH_DFPN;
00460 RETURN_ON_STOP (result_cur);
00461 }
00462 else if (iwscheme == NtesukiRecord::strict_iw)
00463 {
00464
00465
00466 for (int pass_left_child = 0; pass_left_child <= pass_left; pass_left_child++)
00467 {
00468 NtesukiResult result_st = record->getValueWithPath<A>(pass_left_child, path);
00469 if (!result_st.isCheckmateFail())
00470 {
00471 ntesuki_assert(result_st.proof() < proof_limit);
00472 ntesuki_assert(result_st.disproof() < disproof_limit);
00473 TRY_DFPN;
00474 attackWithOrder<T>(record, NULL, NULL,
00475 proof_limit, disproof_limit,
00476 pass_left_child, last_move);
00477
00478 CATCH_DFPN;
00479 RETURN_ON_STOP (result_cur);
00480 break;
00481 }
00482 }
00483 }
00484 else if (iwscheme == NtesukiRecord::pn_iw)
00485 {
00486
00487
00488 unsigned int p_min = ProofDisproof::BigProofNumber,
00489 p_2nd = ProofDisproof::BigProofNumber;
00490 int pass_left_best = -1;
00491 for (int pass_left_child = 0; pass_left_child <= pass_left; pass_left_child++)
00492 {
00493 NtesukiResult result_st = record->getValueWithPath<A>(pass_left_child, path);
00494 if (result_st.isCheckmateFail()) continue;
00495
00496 const unsigned int proof = result_st.proof();
00497 if (proof < p_min)
00498 {
00499 pass_left_best = pass_left_child;
00500 p_2nd = p_min;
00501 p_min = proof;
00502 }
00503 else if (proof < p_2nd)
00504 {
00505 p_2nd = proof;
00506 }
00507 }
00508
00509 unsigned int proof_limit_child = std::min(proof_limit, p_2nd + 1);
00510 unsigned int disproof_limit_child = disproof_limit;
00511 TRY_DFPN;
00512 attackWithOrder<T>(record, NULL, NULL,
00513 proof_limit_child, disproof_limit_child,
00514 pass_left_best, last_move);
00515
00516 CATCH_DFPN;
00517 RETURN_ON_STOP (result_cur);
00518 }
00519 result_cur = record->getValueOr<A>(pass_left, path, iwscheme);
00520 }
00521 return result_cur;
00522 }
00523
00524 template <Player T>
00525 void osl::ntesuki::NtesukiSearcher::
00526 attackWithOrder(NtesukiRecord* record,
00527 const NtesukiRecord* oracle_attack,
00528 const NtesukiRecord* oracle_defense,
00529 unsigned int proof_limit, unsigned int disproof_limit,
00530 const int pass_left, const Move last_move)
00531 {
00532 ++node_count;
00533
00534 const Player A = T;
00535 #ifndef NDEBUG
00536 const Player D = PlayerTraits<T>::opponent;
00537 #endif
00538 ntesuki_assert(T == state.getTurn());
00539 ntesuki_assert(!state.inCheck(D));
00540 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
00541 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
00542
00543 RETURN_ON_STOP;
00544 const bool under_attack = state.inCheck(T);
00545
00546 ntesuki_assert (record->getValueWithPath<A>(pass_left, path).proof()
00547 < proof_limit);
00548 ntesuki_assert (record->getValueWithPath<A>(pass_left, path).disproof()
00549 < disproof_limit);
00550 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
00551
00552 NtesukiRecord::VisitLock visitLock(record);
00553
00554 ntesuki_assert(proof_limit > 0);
00555 ntesuki_assert(disproof_limit > 0);
00556
00557 NtesukiMoveList moves;
00558
00559
00560 TRY_DFPN;
00561 record->generateMoves<T>(moves, pass_left, false);
00562 CATCH_DFPN;
00563
00564
00565 ++attack_node_count;
00566 if (under_attack)
00567 {
00568 ++attack_node_under_attack_count;
00569 }
00570 attack_node_moves_count += moves.size();
00571
00572
00573 if (moves.empty())
00574 {
00575 if (pass_left != 0 &&
00576 record->rzone_move_generation)
00577 {
00578
00579 NtesukiResult r = record->getValueWithPath<A>(pass_left - 1, path);
00580
00581 if (r.isCheckmateFail())
00582 {
00583
00584 record->rzone_move_generation = false;
00585 TRY_DFPN;
00586 record->setResult<A>(pass_left, ProofDisproof(1,1),
00587 NtesukiMove::INVALID(), false);
00588 CATCH_DFPN;
00589 }
00590 else
00591 {
00592
00593 TRY_DFPN;
00594 record->setResult<A>(pass_left, ProofDisproof(r.proof() + 2,
00595 r.disproof() + 2),
00596 NtesukiMove::INVALID(), false);
00597 CATCH_DFPN;
00598 }
00599 return;
00600 }
00601 TRY_DFPN;
00602
00603
00604
00605 record->setResult<A>(pass_left, ProofDisproof::NoCheckmate(),
00606 NtesukiMove::INVALID(), false);
00607 CATCH_DFPN;
00608 return;
00609 }
00610
00611 ntesuki_assert(!moves.empty());
00612 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() != 0);
00613 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() != 0);
00614
00615
00616
00617 for (;;)
00618 {
00619 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() != 0);
00620 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() != 0);
00621
00622 unsigned int best_proof = ProofDisproof::BigProofNumber,
00623 best_disproof = 0,
00624 second_proof = ProofDisproof::BigProofNumber,
00625 sum_disproof = 0;
00626 unsigned int step_cost = 1;
00627
00628 NtesukiMove* best_move =
00629 selectMoveAttack<T>(record,
00630 best_proof, sum_disproof,
00631 second_proof, best_disproof,
00632 step_cost,
00633 moves,
00634 pass_left);
00635 if (best_move == NULL)
00636 {
00637 if (pass_left != 0 &&
00638 record->rzone_move_generation)
00639 {
00640
00641 NtesukiResult r = record->getValueWithPath<A>(pass_left - 1, path);
00642
00643 if (r.isCheckmateFail())
00644 {
00645
00646 record->rzone_move_generation = false;
00647 TRY_DFPN;
00648 record->setResult<A>(pass_left, ProofDisproof(1,1),
00649 NtesukiMove::INVALID(), false);
00650 CATCH_DFPN;
00651 }
00652 else
00653 {
00654
00655 TRY_DFPN;
00656 record->setResult<A>(pass_left, ProofDisproof(r.proof() + 2,
00657 r.disproof() + 2),
00658 NtesukiMove::INVALID(), false);
00659 CATCH_DFPN;
00660 }
00661 }
00662 else
00663 {
00664 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() == 0);
00665 }
00666 return;
00667 }
00668 else if (best_move->isCheckmateSuccess<A>(pass_left))
00669 {
00670 return;
00671 }
00672
00673
00674
00675 const NtesukiResult result_cur(best_proof, sum_disproof);
00676 record->setResult<A>(pass_left, result_cur,
00677 NtesukiMove::INVALID(), false);
00678
00679
00680
00681 if ((proof_limit <= best_proof) ||
00682 (disproof_limit <= sum_disproof))
00683 {
00684 ntesuki_assert(!result_cur.isFinal());
00685 return;
00686 }
00687
00688
00689
00690 unsigned int proof_child = std::min(proof_limit, second_proof + step_cost);
00691 ntesuki_assert(disproof_limit > sum_disproof);
00692 unsigned int disproof_child =
00693 addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
00694 disproof_limit, best_disproof)
00695 - sum_disproof;
00696
00697 NtesukiRecord *record_child = table.allocateWithMove(record, *best_move);
00698 if (record_child == 0)
00699 {
00700 *stop_flag = TableLimitReached;
00701 return;
00702 }
00703 ntesuki_assert(record_child);
00704 const PathEncoding path_child(path, best_move->getMove());
00705 NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
00706 path_child);
00707 if (!result_child.isFinal())
00708 {
00709 if (best_move->isCheck())
00710 {
00711 oracle_attack = NULL;
00712 }
00713 if (ptt_aunt &&
00714 pass_left != 0 &&
00715 record->getValueWithPath<A>(pass_left - 1, path).isCheckmateFail())
00716 {
00717 oracle_defense = record;
00718 }
00719
00720 AttackHelper<NtesukiSearcher, T> helper(this,
00721 result_child,
00722 record_child,
00723 oracle_attack,
00724 oracle_defense,
00725 proof_child,
00726 disproof_child,
00727 pass_left,
00728 best_move->getMove());
00729 PlayMoveLock pml(moves_played, best_move->getMove());
00730 TRY_DFPN;
00731 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, best_move->getMove(), helper);
00732 CATCH_DFPN;
00733 record->updateWithChild(record_child, pass_left);
00734 RETURN_ON_STOP;
00735
00736 const NtesukiResult result_cur = record->getValueWithPath<A>(pass_left, path_child);
00737 if (result_cur.isFinal())
00738 {
00739 return;
00740 }
00741 }
00742
00743 if (result_child.isPawnDropFoul(best_move->getMove()))
00744 {
00745 best_move->setPawnDropCheckmate();
00746 best_move->setCheckmateFail<A>(pass_left);
00747 }
00748 else if (result_child.isCheckmateSuccess())
00749 {
00750 best_move->setCheckmateSuccess<A>(pass_left);
00751 TRY_DFPN;
00752 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
00753 *best_move, false);
00754 CATCH_DFPN;
00755 return;
00756 }
00757 else if (result_child.isCheckmateFail())
00758 {
00759 if (result_child != ProofDisproof::LoopDetection())
00760 {
00761 best_move->setCheckmateFail<A>(pass_left);
00762 }
00763
00764 if (result_child == ProofDisproof::PawnCheckmate())
00765 {
00766 best_move->setPawnDropCheckmate();
00767 }
00768
00769 if (ptt_siblings_fail &&
00770 !best_move->isCheck() &&
00771 result_child != ProofDisproof::LoopDetection())
00772 {
00773 TRY_DFPN;
00774 simulateSiblingsFail<T>(record, record_child, pass_left,
00775 sibling_attack_success_count,
00776 sibling_attack_count);
00777 CATCH_DFPN;
00778 }
00779 }
00780 }
00781 }
00782
00783
00784 typedef std::pair<unsigned int, unsigned int> top_pdp_t;
00785 static bool sorter(const top_pdp_t& lhs,
00786 const top_pdp_t& rhs)
00787 {
00788 if (lhs.first == rhs.first)
00789 {
00790
00791 return rhs.second < rhs.second;
00792 }
00793 else
00794 {
00795 return lhs.first > rhs.first;
00796 }
00797 }
00798
00799 template <Player T>
00800 osl::ntesuki::NtesukiMove * osl::ntesuki::NtesukiSearcher::
00801 selectMoveAttack(NtesukiRecord* record,
00802 unsigned int& best_proof,
00803 unsigned int& sum_disproof,
00804 unsigned int& second_proof,
00805 unsigned int& best_disproof,
00806 unsigned int& step_cost,
00807 NtesukiMoveList& moves,
00808 const int pass_left)
00809 {
00810 const Player A = T;
00811
00812 bool read_nopromote = false;
00813
00814 re_select_move_attack:
00815 NtesukiMove *best_move = NULL;
00816
00817 bool loop = false;
00818 bool pawn_checkmate = false;
00819 unsigned short min_child_age = SHRT_MAX;
00820
00821 int average_cost = 0;
00822 int average_cost_count = 0;
00823
00824
00825 best_proof = ProofDisproof::BigProofNumber;
00826 best_disproof = 0;
00827 second_proof = ProofDisproof::BigProofNumber;
00828 sum_disproof = 0;
00829
00830
00831 std::list<top_pdp_t> pdps;
00832
00833
00834
00835 for (NtesukiMoveList::iterator move_it = moves.begin();
00836 move_it != moves.end(); ++move_it)
00837 {
00838 NtesukiMove& move = *move_it;
00839 pawn_checkmate |= move.isPawnDropCheckmate();
00840
00841 if (move.isPass())
00842 {
00843 continue;
00844 }
00845
00846 if (!move.isCheck() && 0 == pass_left)
00847 {
00848 continue;
00849 }
00850
00851 if (move.isCheckmateFail<A>(pass_left))
00852 {
00853 continue;
00854 }
00855
00856 if (delay_nopromote &&
00857 !read_nopromote &&
00858 move.isNoPromote())
00859 {
00860 continue;
00861 }
00862
00863 if (delay_non_attack &&
00864 !record->readNonAttack(pass_left) &&
00865 ((move.getOrder() > pass_left) ||
00866 move.isLameLong())
00867 )
00868 {
00869 continue;
00870 }
00871
00872 unsigned int proof = move.h_a_proof;
00873 unsigned int disproof = move.h_a_disproof;
00874
00875 if (tsumero_estimate && !move.isCheck())
00876 {
00877 proof = tsumero_estimate;
00878 disproof = 1;
00879 }
00880
00881 average_cost += proof;
00882 average_cost_count++;
00883
00884 NtesukiRecord *record_child = table.findWithMove(record, move);
00885 if (record_child)
00886 {
00887 if (record_child->isVisited())
00888 {
00889
00890
00891
00892 loop = true;
00893 continue;
00894 }
00895
00896 const PathEncoding path_child(path, move.getMove());
00897 NtesukiResult result_child;
00898 TRY_DFPN;
00899 result_child =
00900 record_child->getValueAnd<A>(pass_left, path_child,
00901 iwscheme, psscheme);
00902 CATCH_DFPN;
00903 proof = result_child.proof();
00904 disproof = result_child.disproof();
00905
00906 if (0 == disproof)
00907 {
00908 if (result_child == ProofDisproof::LoopDetection())
00909 {
00910 loop = true;
00911 continue;
00912 }
00913 if (record_child->getValueWithPath<A>(pass_left, path_child) ==
00914 ProofDisproof::PawnCheckmate())
00915 {
00916 move.setPawnDropCheckmate();
00917 pawn_checkmate = true;
00918 }
00919
00920 ntesuki_assert(proof >= ProofDisproof::PROOF_LIMIT);
00921 move.setCheckmateFail<A>(pass_left);
00922
00923
00924 continue;
00925 }
00926 else if (0 == proof)
00927 {
00928
00929 if (record_child->getValueWithPath<A>(pass_left, path_child).
00930 isPawnDropFoul(move.getMove()))
00931 {
00932 move.setPawnDropCheckmate();
00933 move.setCheckmateFail<A>(pass_left);
00934 pawn_checkmate = true;
00935 continue;
00936 }
00937
00938
00939 ntesuki_assert(disproof >= ProofDisproof::DISPROOF_LIMIT);
00940 move.setCheckmateSuccess<A>(pass_left);
00941 TRY_DFPN;
00942 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
00943 move, false);
00944 CATCH_DFPN;
00945
00946 return &move;
00947 }
00948
00949 min_child_age = std::min(record_child->distance,
00950 min_child_age);
00951 if (record_child->distance <= record->distance)
00952 {
00953 if (!record->useOld<A>(pass_left))
00954 {
00955 continue;
00956 }
00957 }
00958
00959
00960
00961
00962
00963 }
00964
00965
00966 if (!move.isCheck())
00967 {
00968 ntesuki_assert(pass_left > 0);
00969 proof += tsumero_cost;
00970 disproof += tsumero_cost;
00971 }
00972
00973 if (!record->useOld<A>(pass_left)
00974 && !(NtesukiRecord::max_for_split && record->is_split))
00975 {
00976 sum_disproof = addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
00977 disproof, sum_disproof);
00978 }
00979 else
00980 {
00981 sum_disproof = std::max(disproof, sum_disproof);
00982 }
00983
00984
00985 if (proof < best_proof)
00986 {
00987 best_move = &move;
00988 second_proof = best_proof;
00989 best_proof = proof;
00990 best_disproof = disproof;
00991 }
00992 else if (proof < second_proof)
00993 {
00994 second_proof = proof;
00995 }
00996
00997
00998 if (dynamic_widening_width > 0)
00999 {
01000 if (pdps.size() < dynamic_widening_width)
01001 {
01002 pdps.push_back(top_pdp_t(proof, disproof));
01003
01004 pdps.sort(sorter);
01005 }
01006 else
01007 {
01008 if (pdps.back().first > proof)
01009 {
01010 pdps.pop_back();
01011 pdps.push_back(top_pdp_t(proof, disproof));
01012 }
01013 pdps.sort(sorter);
01014 }
01015 }
01016 }
01017
01018
01019 if ((dynamic_widening_width > 0 &&
01020 dynamic_widening_width < moves.size())
01021 &&
01022 (!record->useOld<A>(pass_left)
01023 && !(NtesukiRecord::max_for_split && record->is_split)))
01024 {
01025 sum_disproof = 0;
01026 for (std::list<top_pdp_t>::const_iterator it = pdps.begin();
01027 it != pdps.end(); ++it)
01028 {
01029 sum_disproof += it->second;
01030 }
01031 }
01032
01033
01034
01035 if (best_move == NULL)
01036 {
01037 if (false == record->useOld<A>(pass_left))
01038 {
01039 if (SHRT_MAX != min_child_age)
01040 {
01041 record->setUseOld<A>(pass_left, true);
01042
01043 ntesuki_assert(min_child_age <= record->distance);
01044 record->distance = min_child_age;
01045
01046 goto re_select_move_attack;
01047 }
01048 }
01049
01050 if (!record->readNonAttack(pass_left))
01051 {
01052 if (!read_attack_only)
01053 {
01054 record->setReadNonAttack(pass_left);
01055 if (ptt_non_attack &&
01056 pass_left != 0)
01057 {
01058 TRY_DFPN;
01059 handleNonAttack<T>(record, pass_left);
01060 CATCH_DFPN;
01061 RETURN_ON_STOP NULL;
01062 }
01063 record->setUseOld<A>(pass_left, false);
01064 goto re_select_move_attack;
01065 }
01066 }
01067
01068 if (delay_nopromote &&
01069 !read_nopromote &&
01070 (pass_left > 0 || pawn_checkmate))
01071 {
01072 read_nopromote = true;
01073 record->setUseOld<A>(pass_left, false);
01074 goto re_select_move_attack;
01075 }
01076
01077 ntesuki_assert(best_proof == ProofDisproof::BigProofNumber);
01078
01079 if (pass_left != 0 &&
01080 record->rzone_move_generation)
01081 {
01082
01083 return NULL;
01084 }
01085
01086 if (pawn_checkmate)
01087 {
01088 if (delay_nopromote) assert(read_nopromote);
01089
01090 TRY_DFPN;
01091 record->setResult<A>(pass_left, ProofDisproof::PawnCheckmate(),
01092 NtesukiMove::INVALID(), false);
01093 CATCH_DFPN;
01094 return NULL;
01095 }
01096
01097 else if (loop)
01098 {
01099 record->setLoopWithPath<A>(pass_left, path);
01100 TRY_DFPN;
01101 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01102 NtesukiMove::INVALID(), false);
01103 CATCH_DFPN;
01104 return NULL;
01105 }
01106 else
01107 {
01108 TRY_DFPN;
01109 record->setResult<A>(pass_left, ProofDisproof::NoCheckmate(),
01110 NtesukiMove::INVALID(), false);
01111 CATCH_DFPN;
01112 return NULL;
01113 }
01114 }
01115
01116 ntesuki_assert(best_proof != 0);
01117 ntesuki_assert(sum_disproof != 0);
01118 ntesuki_assert(best_proof < ProofDisproof::PROOF_LIMIT);
01119 ntesuki_assert(sum_disproof < ProofDisproof::DISPROOF_LIMIT);
01120
01121 if (record->useOld<A>(pass_left))
01122 {
01123 ntesuki_assert(min_child_age != SHRT_MAX);
01124 record->distance = min_child_age;
01125 }
01126 average_cost /= average_cost_count;
01127 step_cost = std::max(average_cost, 1);
01128 return best_move;
01129 }
01130
01131 template <Player T>
01132 void osl::ntesuki::NtesukiSearcher::
01133 handleNonAttack(NtesukiRecord* record,
01134 int pass_left)
01135 {
01136 const Player A = T;
01137 ntesuki_assert(T == state.getTurn());
01138
01139 NtesukiMoveList moves;
01140 mg->generate<T>(state, moves);
01141
01142 for (NtesukiMoveList::iterator move_it = moves.begin();
01143 move_it != moves.end(); ++move_it)
01144 {
01145 NtesukiRecord *record_best = table.findWithMove(record,
01146 *move_it);
01147 const PathEncoding path_best(path, move_it->getMove());
01148 if (!record_best ||
01149 !record_best->getValueWithPath<A>(pass_left,
01150 path_best).isCheckmateFail()) continue;
01151
01152
01153 for (NtesukiMoveList::iterator move_it2 = moves.begin();
01154 move_it2 != moves.end(); ++move_it2)
01155 {
01156 if (move_it2->isPass())
01157 {
01158 continue;
01159 }
01160 if (*move_it2 == *move_it)
01161 {
01162 continue;
01163 }
01164 if (move_it2->isCheckmateFail<A>(pass_left))
01165 {
01166 continue;
01167 }
01168 NtesukiRecord *record_child = table.allocateWithMove(record,
01169 *move_it2);
01170 if (record_child == 0)
01171 {
01172 *stop_flag = TableLimitReached;
01173 return;
01174 }
01175 ntesuki_assert(record_child);
01176 const PathEncoding path_child(path, move_it->getMove());
01177 if(record_child->getValueWithPath<A>(pass_left,
01178 path_child).isFinal())
01179 {
01180 continue;
01181 }
01182
01183 if (record_child->isVisited())
01184 {
01185 TRY_DFPN;
01186 move_it2->setCheckmateFail<A>(pass_left);
01187 record->setLoopWithPath<A>(pass_left, path_child);
01188 CATCH_DFPN;
01189 continue;
01190 }
01191
01192 bool simulation_result;
01193 CallSimulationAttack<NtesukiSimulationSearcher, T>
01194 helper(simulator, table, record_child, record_best,
01195 pass_left, simulation_result, move_it2->getMove());
01196 TRY_DFPN;
01197 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move_it2->getMove(), helper);
01198 CATCH_DFPN;
01199 RETURN_ON_STOP;
01200
01201 if (simulation_result)
01202 {
01203 move_it2->setBySimulation();
01204 move_it2->setCheckmateFail<A>(pass_left);
01205 }
01206 }
01207 }
01208 }
01209
01210
01211
01212
01213 template <Player T>
01214 NtesukiResult osl::ntesuki::NtesukiSearcher::
01215 defense(NtesukiRecord* record,
01216 const NtesukiRecord* oracle_attack,
01217 const NtesukiRecord* oracle_defense,
01218 unsigned int proof_limit, unsigned int disproof_limit,
01219 int pass_left,
01220 const Move last_move)
01221 {
01222 const Player A = PlayerTraits<T>::opponent;
01223 const Player D = T;
01224 CountChildLock cclock(record, table);
01225
01226 ntesuki_assert(T == state.getTurn());
01227 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
01228 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
01229 ntesuki_assert (record->getValueAnd<A>(pass_left, path,
01230 iwscheme, psscheme).proof()
01231 < proof_limit);
01232 ntesuki_assert (record->getValueAnd<A>(pass_left, path,
01233 iwscheme, psscheme).disproof()
01234 < disproof_limit);
01235
01236 ntesuki_assert(state.inCheck(T) || (pass_left > 0));
01237 ntesuki_assert(!state.inCheck(A));
01238
01239 ++node_count;
01240 RETURN_ON_STOP (record->getValueAnd<A>(pass_left, path,
01241 iwscheme, psscheme));
01242
01243 ntesuki_assert(record);
01244 ntesuki_assert(!record->getValueWithPath<A>(pass_left, path).isFinal());
01245 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
01246
01247 NtesukiRecord::VisitLock visitLock(record);
01248
01249
01250
01251 TRY_DFPN;
01252 if (record->setUpNode<T>())
01253 {
01254 const NtesukiResult r = record->getValueWithPath<A>(pass_left, path);
01255 if (r.isCheckmateFail())
01256 {
01257
01258 ++immediate_lose;
01259 RETURN (r);
01260 }
01261 else if (r.isFinal())
01262 {
01263 RETURN (r);
01264 }
01265 }
01266 CATCH_DFPN;
01267
01268
01269
01270 NtesukiResult result_cur = record->getValueAnd<A>(pass_left, path, iwscheme, psscheme);
01271 while ((result_cur.proof() < proof_limit) &&
01272 (result_cur.disproof() < disproof_limit))
01273 {
01274 bool read_attack_first = false;
01275 if (psscheme)
01276 {
01277
01278
01279
01280
01281
01282 if (pass_left > 0)
01283 {
01284 const NtesukiResult result_attacker =
01285 record->getValueWithPath<A>(pass_left, path);
01286 const NtesukiResult result_defender =
01287 record->getValueOr<D>(pass_left - 1, path, iwscheme);
01288 if (result_defender.proof() < result_attacker.disproof())
01289 {
01290 read_attack_first = true;
01291 }
01292 }
01293 }
01294
01295 if (read_attack_first)
01296 {
01297 NtesukiRecord::UnVisitLock unVisitLock(record);
01298
01299 TRY_DFPN;
01300 attack<T>(record, NULL, NULL,
01301 disproof_limit, proof_limit,
01302 pass_left - 1, last_move);
01303 CATCH_DFPN;
01304 RETURN_ON_STOP (result_cur);
01305 }
01306 else
01307 {
01308 TRY_DFPN;
01309 defenseWithPlayer<T>(record, oracle_attack, oracle_defense,
01310 proof_limit, disproof_limit,
01311 pass_left, last_move);
01312 CATCH_DFPN;
01313 RETURN_ON_STOP (result_cur);
01314 }
01315 result_cur = record->getValueAnd<A>(pass_left, path, iwscheme, psscheme);
01316 }
01317 return result_cur;
01318 }
01319
01320 template <Player T>
01321 void osl::ntesuki::NtesukiSearcher::
01322 defenseWithPlayer(NtesukiRecord* record,
01323 const NtesukiRecord* oracle_attack,
01324 const NtesukiRecord* oracle_defense,
01325 unsigned int proof_limit, unsigned int disproof_limit,
01326 int pass_left,
01327 const Move last_move)
01328 {
01329 const Player A = PlayerTraits<T>::opponent;
01330 const bool under_attack = state.inCheck(T);
01331
01332
01333
01334 NtesukiMoveList moves;
01335
01336 TRY_DFPN;
01337 record->generateMoves<T>(moves, pass_left, true);
01338 CATCH_DFPN;
01339
01340 if (moves.empty())
01341 {
01342 TRY_DFPN;
01343 record->setResult<A>(0, ProofDisproof::NoEscape(),
01344 NtesukiMove::INVALID(), false);
01345 CATCH_DFPN;
01346 return;
01347 }
01348
01349
01350 ++defense_node_count;
01351 if (under_attack)
01352 {
01353 ++defense_node_under_attack_count;
01354 }
01355 defense_node_moves_count += moves.size();
01356
01357 ntesuki_assert(!moves.empty());
01358 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).isUnknown());
01359
01360
01361 if (!under_attack &&
01362 record->do_oracle_aunt &&
01363 oracle_defense)
01364 {
01365 record->do_oracle_aunt = false;
01366
01367 NtesukiMove pass(Move::PASS(T));
01368 NtesukiRecord *record_pass = table.allocateWithMove(record,
01369 pass);
01370 if (record_pass == 0)
01371 {
01372 *stop_flag = TableLimitReached;
01373 return;
01374 }
01375 ntesuki_assert(record_pass);
01376
01377 if (record_pass->isVisited())
01378 {
01379
01380 record->setLoopWithPath<A>(pass_left, path);
01381 assert(record->isLoopWithPath<A>(pass_left, path));
01382 TRY_DFPN;
01383 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01384 NtesukiMove::INVALID(), false);
01385 CATCH_DFPN;
01386 return;
01387 }
01388
01389 ntesuki_assert(record_pass);
01390 const PathEncoding path_pass(path, pass.getMove());
01391 const NtesukiResult result_pass = record_pass->getValueWithPath<A>(pass_left - 1,
01392 path_pass);
01393 if (!result_pass.isFinal())
01394 {
01395 bool simulation_result;
01396 CallSimulationDefenseDisproof<NtesukiSimulationSearcher, T>
01397 helper(simulator, table, record_pass, oracle_defense,
01398 pass_left - 1, simulation_result, pass.getMove());
01399 TRY_DFPN;
01400 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, pass.getMove(), helper);
01401 CATCH_DFPN;
01402 return;
01403
01404 if (simulation_result)
01405 {
01406 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
01407 path_pass).isCheckmateFail());
01408 pass.setBySimulation();
01409 pass.setCheckmateFail<A>(pass_left);
01410 }
01411 }
01412 }
01413
01414
01415 if (record->do_oracle_attack && oracle_attack)
01416 {
01417 record->do_oracle_attack = false;
01418
01419 ntesuki_assert(ptt_uncle && !under_attack);
01420 NtesukiMove pass(Move::PASS(T));
01421
01422 NtesukiRecord *record_pass = table.allocateWithMove(record,
01423 pass);
01424 if (record_pass == 0)
01425 {
01426 *stop_flag = TableLimitReached;
01427 return;
01428 }
01429 ntesuki_assert(record_pass);
01430
01431 if (record_pass->isVisited())
01432 {
01433
01434 record->setLoopWithPath<A>(pass_left, path);
01435 assert(record->isLoopWithPath<A>(pass_left, path));
01436 TRY_DFPN;
01437 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01438 NtesukiMove::INVALID(), false);
01439 CATCH_DFPN;
01440 return;
01441 }
01442
01443 ntesuki_assert(record_pass);
01444 const PathEncoding path_pass(path, pass.getMove());
01445 const NtesukiResult result_pass = record_pass->getValueWithPath<A>(pass_left - 1,
01446 path_pass);
01447 if (!result_pass.isFinal())
01448 {
01449 ++isshogi_attack_count;
01450
01451 bool simulation_result;
01452 CallSimulationDefense<NtesukiSimulationSearcher, T>
01453 helper(simulator, table, record_pass, oracle_attack,
01454 pass_left - 1, simulation_result, pass.getMove());
01455 TRY_DFPN;
01456 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, pass.getMove(), helper);
01457 CATCH_DFPN;
01458 return;
01459
01460 if (simulation_result)
01461 {
01462 ++isshogi_attack_success_count;
01463 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
01464 path_pass).isCheckmateSuccess());
01465 pass.setBySimulation();
01466 pass.setCheckmateSuccess<A>(pass_left);
01467 record->setNtesuki<A>(pass_left);
01468
01469 if (ptt_invalid_defense)
01470 {
01471 TRY_DFPN;
01472 simulateSiblingsSuccess<T>(record, record_pass, pass_left,
01473 pass_success_count,
01474 pass_count);
01475 CATCH_DFPN;
01476 return;
01477 }
01478 }
01479 }
01480 }
01481
01482 for (;;)
01483 {
01484 unsigned int best_disproof = ProofDisproof::BigProofNumber,
01485 sum_proof = 0,
01486 second_disproof = ProofDisproof::BigProofNumber,
01487 best_proof = 0;
01488
01489 unsigned int step_cost = 1;
01490 NtesukiMove *best_move = NULL;
01491
01492 best_move = selectMoveDefense<T>(record,
01493 best_disproof,
01494 sum_proof,
01495 second_disproof,
01496 best_proof,
01497 step_cost,
01498 moves,
01499 pass_left,
01500 last_move);
01501 RETURN_ON_STOP;
01502 if (NULL == best_move)
01503 {
01504 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).
01505 isCheckmateSuccess());
01506 return;
01507 }
01508 else if (best_disproof == 0)
01509 {
01510 ntesuki_assert(best_move->isCheckmateFail<A>(pass_left) ||
01511 record->isLoopWithPath<A>(pass_left, path));
01512 return;
01513 }
01514
01515 #ifndef NDEBUG
01516 {
01517 NtesukiRecord* best_child = table.findWithMove(record, *best_move);
01518 if (best_child)
01519 {
01520 const PathEncoding path_child(path, best_move->getMove());
01521 int pass_left_child = pass_left;
01522 if (best_move->isPass()) --pass_left_child;
01523 const NtesukiResult r =
01524 best_child->getValueOr<A>(pass_left_child, path_child,iwscheme);
01525 ntesuki_assert(r.disproof() == best_disproof);
01526 ntesuki_assert(r.proof() <= sum_proof);
01527 }
01528 }
01529 #endif
01530
01531
01532
01533 const NtesukiResult result_cur = ProofDisproof(sum_proof, best_disproof);
01534 record->setResult<A>(pass_left, result_cur,
01535 NtesukiMove::INVALID(), false);
01536
01537
01538
01539 if ((disproof_limit <= best_disproof) ||
01540 (proof_limit <= sum_proof))
01541 {
01542 ntesuki_assert(!result_cur.isFinal());
01543 return;
01544 }
01545
01546 unsigned int proof_child =
01547 addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
01548 proof_limit, best_proof) - sum_proof;
01549 unsigned int disproof_child = std::min(disproof_limit,
01550 second_disproof + step_cost);
01551
01552
01553
01554 int pass_left_child = pass_left;
01555 if (best_move->isPass())
01556 {
01557 --pass_left_child;
01558 }
01559 NtesukiRecord *record_child =
01560 table.allocateWithMove(record, *best_move);
01561 if (record_child == 0)
01562 {
01563 *stop_flag = TableLimitReached;
01564 return;
01565 }
01566 ntesuki_assert(record_child);
01567 const PathEncoding path_child(path, best_move->getMove());
01568 ntesuki_assert(pass_left_child >= 0);
01569 NtesukiResult result_child =
01570 record_child->getValueOr<A>(pass_left_child,
01571 path_child,
01572 iwscheme);
01573
01574 if (!result_child.isFinal())
01575 {
01576 if (best_move->isCheck())
01577 {
01578 if (ptt_uncle &&
01579 !under_attack &&
01580 delay_non_pass)
01581 {
01582 NtesukiMove& pass = moves.front();
01583 ntesuki_assert(pass.isPass());
01584
01585 oracle_attack = table.findWithMove(record, pass);
01586 ntesuki_assert(oracle_attack);
01587 }
01588 }
01589
01590 if (result_child.proof() >= proof_child)
01591 {
01592 std::cerr << *record_child
01593 << result_child << "<- result \n"
01594 << proof_child << "/" << disproof_child << "<- limit\n"
01595 << *best_move << "\n"
01596 << sum_proof << "/" << best_disproof << " <- cur\n";
01597 }
01598 DefenseHelper<NtesukiSearcher, T> helper(this,
01599 result_child,
01600 record_child,
01601 oracle_attack,
01602 oracle_defense,
01603 proof_child,
01604 disproof_child,
01605 pass_left_child,
01606 best_move->getMove());
01607
01608 PlayMoveLock pml(moves_played, best_move->getMove());
01609 if (best_move->isPass())
01610 {
01611 NtesukiRecord::pass_count++;
01612 }
01613 TRY_DFPN;
01614 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, best_move->getMove(), helper);
01615 CATCH_DFPN;
01616
01617 if (best_move->isPass())
01618 {
01619 NtesukiRecord::pass_count--;
01620 }
01621 record->updateWithChild(record_child, pass_left);
01622 RETURN_ON_STOP;
01623
01624 if (record->getValueWithPath<A>(pass_left, path).isFinal())
01625 {
01626 return;
01627 }
01628 }
01629
01630
01631
01632 if (result_child.isCheckmateFail())
01633 {
01634 if (result_child == ProofDisproof::AttackBack())
01635 {
01636 ++disproof_by_inversion_count;
01637 }
01638 if (result_child == ProofDisproof::LoopDetection())
01639 {
01640 record->setLoopWithPath<A>(pass_left, path);
01641 TRY_DFPN;
01642 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01643 NtesukiMove::INVALID(), false);
01644 CATCH_DFPN;
01645 return;
01646 }
01647
01648 best_move->setCheckmateFail<A>(pass_left);
01649 TRY_DFPN;
01650 record->setResult<A>(pass_left, result_child,
01651 *best_move, false);
01652 CATCH_DFPN;
01653 return;
01654 }
01655 else if (result_child.isCheckmateSuccess())
01656 {
01657 best_move->setCheckmateSuccess<A>(pass_left);
01658 NtesukiRecord *best_record = table.findWithMove(record, *best_move);
01659 if ((ptt_invalid_defense && best_move->isPass()) ||
01660 (ptt_siblings_success && !best_move->isCheck())
01661 )
01662 {
01663 TRY_DFPN;
01664 simulateSiblingsSuccess<T>(record, best_record, pass_left,
01665 sibling_defense_success_count,
01666 sibling_defense_count);
01667 CATCH_DFPN;
01668 RETURN_ON_STOP;
01669 }
01670 }
01671 }
01672 }
01673
01674 template <Player T>
01675 osl::ntesuki::NtesukiMove* osl::ntesuki::NtesukiSearcher::
01676 selectMoveDefense(NtesukiRecord* record,
01677 unsigned int& best_disproof,
01678 unsigned int& sum_proof,
01679 unsigned int& second_disproof,
01680 unsigned int& best_proof,
01681 unsigned int& step_cost,
01682 NtesukiMoveList& moves,
01683 const int pass_left,
01684 const Move last_move)
01685 {
01686 const Player A = PlayerTraits<T>::opponent;
01687 const bool under_attack = state.inCheck(T);
01688
01689 bool read_interpose = record->readInterpose(pass_left);
01690
01691
01692
01693 bool read_non_pass = under_attack;
01694 if (pass_left > 0 && !under_attack)
01695 {
01696 NtesukiMove pass(Move::PASS(T));
01697 NtesukiRecord *record_pass = table.findWithMove(record, pass);
01698 if (record_pass)
01699 {
01700 const PathEncoding path_child(path, pass.getMove());
01701 read_non_pass =
01702 record_pass->getValueWithPath<A>(pass_left - 1,
01703 path_child).isCheckmateSuccess();
01704 }
01705 }
01706 if (under_attack) ntesuki_assert(read_non_pass);
01707
01708 bool read_check_defense = record->readCheckDefense(pass_left);
01709 if (isscheme == NtesukiRecord::normal_is)
01710 {
01711 read_check_defense = true;
01712 }
01713
01714 re_select_move_defense:
01715 unsigned short min_child_age = SHRT_MAX;
01716 NtesukiMove *best_move = NULL;
01717
01718 int average_cost = 0;
01719 int average_cost_count = 0;
01720
01721
01722 best_disproof = ProofDisproof::BigProofNumber;
01723 sum_proof = 0;
01724 second_disproof = ProofDisproof::BigProofNumber;
01725 best_proof = 0;
01726
01727
01728 std::list<top_pdp_t> pdps;
01729
01730
01731
01732 for (NtesukiMoveList::iterator move_it = moves.begin();
01733 move_it != moves.end(); ++move_it)
01734 {
01735 NtesukiMove& move = *move_it;
01736 if (move.isCheckmateSuccess<A>(pass_left))
01737 {
01738 continue;
01739 }
01740 ntesuki_assert(!move.isCheckmateFail<A>(pass_left));
01741
01742 if (delay_non_pass &&
01743 !read_non_pass &&
01744 !move.isPass())
01745 {
01746 continue;
01747 }
01748
01749 if (delay_interpose &&
01750 (move.isInterpose() ||
01751 move.isLameLong()) &&
01752 !read_interpose)
01753 {
01754 continue;
01755 }
01756
01757 if (move.isCheck() &&
01758 !under_attack &&
01759 !read_check_defense)
01760 {
01761 continue;
01762 }
01763
01764 unsigned int proof = move.h_d_proof;
01765 unsigned int disproof = move.h_d_disproof;
01766
01767 average_cost += disproof;
01768 average_cost_count++;
01769
01770 NtesukiRecord *record_child = table.findWithMove(record, move);
01771 if (record_child)
01772 {
01773 int pass_left_child = pass_left;
01774 if (move.isPass()) --pass_left_child;
01775 const PathEncoding path_child(path, move.getMove());
01776 NtesukiResult result_child;
01777 TRY_DFPN;
01778 result_child =
01779 record_child->getValueOr<A>(pass_left_child, path_child,
01780 iwscheme);
01781 CATCH_DFPN;
01782
01783 if (record_child->isVisited())
01784 {
01785
01786 record->setLoopWithPath<A>(pass_left, path);
01787 ntesuki_assert(record->isLoopWithPath<A>(pass_left, path));
01788 TRY_DFPN;
01789 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01790 NtesukiMove::INVALID(), false);
01791 CATCH_DFPN;
01792 best_disproof = 0;
01793 return &move;
01794 }
01795
01796 proof = result_child.proof();
01797 disproof = result_child.disproof();
01798
01799 if (result_child.isCheckmateSuccess())
01800 {
01801 ntesuki_assert(disproof >= ProofDisproof::DISPROOF_LIMIT);
01802 move.setCheckmateSuccess<A>(pass_left);
01803 if (move.isPass())
01804 {
01805
01806 record->setNtesuki<A>(pass_left);
01807
01808 if (ptt_invalid_defense)
01809 {
01810 TRY_DFPN;
01811 simulateSiblingsSuccess<T>(record, record_child, pass_left,
01812 pass_success_count,
01813 pass_count);
01814 CATCH_DFPN;
01815 RETURN_ON_STOP(NULL);
01816 goto re_select_move_defense;
01817 }
01818 }
01819 if (ptt_siblings_success && !move.isCheck())
01820 {
01821 TRY_DFPN;
01822 simulateSiblingsSuccess<T>(record, record_child, pass_left,
01823 sibling_defense_success_count,
01824 sibling_defense_count);
01825 CATCH_DFPN;
01826 RETURN_ON_STOP(NULL);
01827
01828 goto re_select_move_defense;
01829 }
01830 continue;
01831 }
01832 else if (result_child.isCheckmateFail())
01833 {
01834 if (move.isCheck() && read_check_defense)
01835 {
01836 ++disproof_by_inversion_count;
01837 }
01838 if (result_child == ProofDisproof::LoopDetection())
01839 {
01840 record->setLoopWithPath<A>(pass_left, path);
01841 TRY_DFPN;
01842 record->setResult<A>(pass_left, NtesukiResult(1, 1),
01843 NtesukiMove::INVALID(), false);
01844 CATCH_DFPN;
01845 best_disproof = 0;
01846 return &move;
01847 }
01848
01849 ntesuki_assert(proof >= ProofDisproof::PROOF_LIMIT);
01850 move.setCheckmateFail<A>(pass_left);
01851 TRY_DFPN;
01852 record->setResult<A>(pass_left, result_child,
01853 move, false);
01854 CATCH_DFPN;
01855 best_disproof = 0;
01856 return &move;
01857 }
01858
01859 min_child_age = std::min(min_child_age,
01860 record_child->distance);
01861 if ((record_child->distance <= record->distance) &&
01862 !move.isPass())
01863 {
01864 if (!record->useOld<A>(pass_left))
01865 {
01866 continue;
01867 }
01868 }
01869 }
01870
01871
01872 if (record->useOld<A>(pass_left))
01873 {
01874 sum_proof = std::max(proof, sum_proof);
01875 }
01876 else if (NtesukiRecord::max_for_split && record->is_split)
01877 {
01878 sum_proof = std::max(proof, sum_proof);
01879 }
01880 else
01881 {
01882 sum_proof = addWithSaturation(ProofDisproof::PROOF_LIMIT,
01883 proof, sum_proof);
01884 }
01885
01886 if (disproof < best_disproof)
01887 {
01888 best_move = &move;
01889 second_disproof = best_disproof;
01890 best_disproof = disproof;
01891 best_proof = proof;
01892 }
01893 else if (disproof < second_disproof)
01894 {
01895 second_disproof = disproof;
01896 }
01897
01898
01899 if (dynamic_widening_width > 0)
01900 {
01901 if (pdps.size() < dynamic_widening_width)
01902 {
01903 pdps.push_back(top_pdp_t(disproof, proof));
01904 pdps.sort(sorter);
01905 }
01906 else
01907 {
01908 if (pdps.back().first > disproof)
01909 {
01910 pdps.pop_back();
01911 pdps.push_back(top_pdp_t(disproof, proof));
01912 }
01913 pdps.sort(sorter);
01914 }
01915 }
01916 }
01917
01918
01919 if (dynamic_widening_width > 0 &&
01920 dynamic_widening_width < moves.size())
01921 {
01922 sum_proof = 0;
01923 for (std::list<top_pdp_t>::const_iterator it = pdps.begin();
01924 it != pdps.end(); ++it)
01925 {
01926 sum_proof += it->second;
01927 }
01928 }
01929
01930
01931
01932 if (NULL == best_move)
01933 {
01934 ntesuki_assert(sum_proof == 0);
01935
01936 if (delay_non_pass &&
01937 read_non_pass == false)
01938 {
01939 ntesuki_assert(!under_attack);
01940
01941 read_non_pass = true;
01942 record->setUseOld<A>(pass_left, false);
01943 record->setNtesuki<A>(pass_left);
01944
01945 if (ptt_invalid_defense)
01946 {
01947 NtesukiMove move_pass = moves.front();
01948 ntesuki_assert(move_pass.isPass());
01949 NtesukiRecord *record_pass = table.findWithMove(record, move_pass);
01950 const PathEncoding path_child(path, move_pass.getMove());
01951
01952 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
01953 path_child).isCheckmateSuccess());
01954 TRY_DFPN;
01955 simulateSiblingsSuccess<T>(record, record_pass, pass_left,
01956 pass_success_count,
01957 pass_count);
01958 CATCH_DFPN;
01959 RETURN_ON_STOP(NULL);
01960 }
01961 goto re_select_move_defense;
01962 }
01963
01964 if (!record->useOld<A>(pass_left))
01965 {
01966 if (SHRT_MAX != min_child_age)
01967 {
01968 record->setUseOld<A>(pass_left, true);
01969
01970 ntesuki_assert(min_child_age <= record->distance);
01971 record->distance = min_child_age;
01972
01973 goto re_select_move_defense;
01974 }
01975 }
01976
01977 if (delay_interpose &&
01978 read_interpose == false)
01979 {
01980 read_interpose = true;
01981 record->setUseOld<A>(pass_left, false);
01982 record->setReadInterpose(pass_left);
01983 TRY_DFPN;
01984 handleInterpose<T>(record, pass_left);
01985 CATCH_DFPN;
01986 RETURN_ON_STOP NULL;
01987
01988 goto re_select_move_defense;
01989 }
01990
01991
01992 switch(isscheme)
01993 {
01994 case NtesukiRecord::no_is:
01995 ntesuki_assert(read_check_defense == false);
01996 break;
01997 case NtesukiRecord::tonshi_is:
01998 handleTonshi<T>(record, pass_left, last_move);
01999 RETURN_ON_STOP NULL;
02000 break;
02001 case NtesukiRecord::delay_is:
02002 if (read_check_defense == false)
02003 {
02004 ++proof_without_inversion_count;
02005 read_check_defense = true;
02006 record->setReadCheckDefense(pass_left);
02007 goto re_select_move_defense;
02008 }
02009 break;
02010 case NtesukiRecord::normal_is:
02011 ntesuki_assert(read_check_defense == true);
02012 break;
02013 }
02014
02015
02016 TRY_DFPN;
02017 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
02018 NtesukiMove::INVALID(), false);
02019 CATCH_DFPN;
02020 return NULL;
02021 }
02022 ntesuki_assert(best_move);
02023 ntesuki_assert(sum_proof != 0);
02024 ntesuki_assert(best_disproof != 0);
02025
02026 if (record->useOld<A>(pass_left))
02027 {
02028 ntesuki_assert(min_child_age != SHRT_MAX);
02029 record->distance = min_child_age;
02030 }
02031 average_cost /= average_cost_count;
02032 step_cost = std::max(average_cost, 1);
02033 return best_move;
02034 }
02035
02036 template <Player T>
02037 void osl::ntesuki::NtesukiSearcher::
02038 handleTonshi(NtesukiRecord *record,
02039 int pass_left,
02040 const Move last_move)
02041 {
02042 const Player A = PlayerTraits<T>::opponent;
02043 const Player D = T;
02044
02045 if (pass_left > 0)
02046 {
02047 NtesukiResult result_defender =
02048 record->getValueWithPath<D>(pass_left - 1, path);
02049 if (!result_defender.isFinal())
02050 {
02051
02052
02053 record->setResult<A>(pass_left, ProofDisproof::Bottom(),
02054 NtesukiMove::INVALID(), false);
02055 const unsigned int read_node_limit_orig = read_node_limit;
02056 int ratio = 1;
02057
02058 if ((record->distance / 2) == 0)
02059 ratio = 8;
02060 else if ((record->distance / 2) == 1)
02061 ratio = 2;
02062
02063 read_node_limit = node_count + READ_ATTACK_BACK_LIMIT * ratio;
02064
02065 NtesukiRecord::UnVisitLock unVisitLock(record);
02066 TRY_DFPN;
02067 result_defender = attack<T>(record, NULL, NULL,
02068 INITIAL_PROOF_LIMIT, INITIAL_PROOF_LIMIT,
02069 pass_left - 1, last_move);
02070 CATCH_DFPN;
02071
02072 if (result_defender.isCheckmateSuccess())
02073 {
02074 ++attack_back_count;
02075 }
02076
02077 read_node_limit = read_node_limit_orig;
02078 RETURN_ON_STOP;
02079 }
02080
02081 if (result_defender.isFinal())
02082 {
02083 return;
02084 }
02085 }
02086 }
02087
02088 template <Player T>
02089 void osl::ntesuki::NtesukiSearcher::
02090 simulateSiblingsSuccess(NtesukiRecord *record,
02091 NtesukiRecord *record_best,
02092 int pass_left,
02093 unsigned int& success_count,
02094 unsigned int& total_count)
02095 {
02096 LockGC glock(table);
02097
02098 const Player A = PlayerTraits<T>::opponent;
02099 if (!record_best) return;
02100 ntesuki_assert(record_best);
02101 ntesuki_assert(record_best->getValue<A>(pass_left).isCheckmateSuccess());
02102
02103 NtesukiMoveList moves;
02104 mg->generate<T>(state, moves);
02105
02106 for (NtesukiMoveList::iterator move_it = moves.begin();
02107 move_it != moves.end(); ++move_it)
02108 {
02109 NtesukiMove& move = *move_it;
02110 NtesukiRecord *record_child = table.allocateWithMove(record,
02111 move);
02112 if (record_child == 0)
02113 {
02114 *stop_flag = TableLimitReached;
02115 return;
02116 }
02117 ntesuki_assert(record_child);
02118 if (record_child == record_best) continue;
02119 if (record_child->isVisited()) continue;
02120
02121 ntesuki_assert(record_child);
02122 const PathEncoding path_child(path, move.getMove());
02123 const NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
02124 path_child);
02125 if (result_child.isFinal())
02126 {
02127 continue;
02128 }
02129
02130 bool simulation_result;
02131 total_count++;
02132 CallSimulationDefense<NtesukiSimulationSearcher, T>
02133 helper(simulator, table, record_child, record_best,
02134 pass_left, simulation_result, move.getMove());
02135 TRY_DFPN;
02136 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move.getMove(), helper);
02137 CATCH_DFPN;
02138 RETURN_ON_STOP;
02139
02140 if (simulation_result)
02141 {
02142 success_count++;
02143 ntesuki_assert(record_child->getValueWithPath<A>(pass_left,
02144 path_child).isCheckmateSuccess());
02145 move.setBySimulation();
02146 move.setCheckmateSuccess<A>(pass_left);
02147 }
02148 }
02149 }
02150
02151 template <Player T>
02152 void osl::ntesuki::NtesukiSearcher::
02153 handleInterpose(NtesukiRecord* record,
02154 int pass_left)
02155 {
02156 const Player A = PlayerTraits<T>::opponent;
02157 ntesuki_assert(T == state.getTurn());
02158
02159 NtesukiMoveList moves;
02160 mg->generate<T>(state, moves);
02161
02162 for (NtesukiMoveList::iterator move_it = moves.begin();
02163 move_it != moves.end(); ++move_it)
02164 {
02165 if (move_it->isInterpose() &&
02166 !move_it->isCheckmateSuccess<A>(pass_left))
02167 {
02168 NtesukiRecord *record_child = table.allocateWithMove(record,
02169 *move_it);
02170 if (record_child == 0)
02171 {
02172 *stop_flag = TableLimitReached;
02173 return;
02174 }
02175 ntesuki_assert(record_child);
02176
02177 const PathEncoding path_child(path, move_it->getMove());
02178 if(record_child->getValueWithPath<A>(pass_left,
02179 path_child).isFinal())
02180 {
02181 continue;
02182 }
02183 ntesuki_assert(record_child->getBestMove<A>(pass_left).isInvalid());
02184
02185 NtesukiMoveList::iterator best_it = moves.begin();
02186 for (; best_it != moves.end(); ++best_it)
02187 {
02188 if (best_it->to() == move_it->to() &&
02189 best_it->isCheckmateSuccess<A>(pass_left)) break;
02190 }
02191 if (best_it == moves.end())
02192 {
02193 continue;
02194 }
02195 const NtesukiRecord* record_best = table.findWithMove(record, *best_it);
02196 ntesuki_assert(record_best);
02197
02198 bool simulation_result;
02199 CallSimulationDefense<NtesukiSimulationSearcher, T>
02200 helper(simulator, table, record_child, record_best,
02201 pass_left, simulation_result, move_it->getMove());
02202 TRY_DFPN;
02203 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move_it->getMove(), helper);
02204 CATCH_DFPN;
02205 RETURN_ON_STOP;
02206
02207 if (simulation_result)
02208 {
02209 move_it->setBySimulation();
02210 move_it->setCheckmateSuccess<A>(pass_left);
02211 }
02212 else if (record_child->getValue<A>(pass_left).isCheckmateFail())
02213 {
02214 break;
02215 }
02216 }
02217 }
02218 }
02219
02220
02221
02222 template <Player A>
02223 int osl::ntesuki::NtesukiSearcher::
02224 search()
02225 {
02226 NtesukiRecord::pass_count = 0;
02227 const Player D = PlayerTraits<A>::opponent;
02228
02229 const HashKey key(state);
02230
02231 NtesukiRecord *record = table.allocateRoot(key, PieceStand(WHITE, state),
02232 0, &state);
02233 ntesuki_assert(record);
02234
02235 NtesukiResult result;
02236 if (A == state.getTurn())
02237 {
02238 const Player T = A;
02239 result = attack<T>(record, NULL, NULL,
02240 INITIAL_PROOF_LIMIT,
02241 INITIAL_DISPROOF_LIMIT,
02242 max_pass - 1, Move::INVALID());
02243 }
02244 else
02245 {
02246 const Player T = D;
02247 if (0 == (max_pass - 1) &&
02248 !state.inCheck(D))
02249 {
02250 if (verbose) std::cerr << "No Check" << std::endl;
02251 return NtesukiNotFound;
02252 }
02253 else
02254 {
02255 result = defense<T>(record, NULL, NULL,
02256 INITIAL_PROOF_LIMIT,
02257 INITIAL_DISPROOF_LIMIT,
02258 max_pass - 1,
02259 Move::INVALID());
02260 }
02261 }
02262
02263 if (node_count > read_node_limit || *stop_flag)
02264 {
02265 if (verbose) std::cerr << "Limit Reached\t" << result << std::endl;
02266 return ReadLimitReached;
02267 }
02268 else
02269 {
02270 if (verbose) std::cerr << "result:\t" << result << std::endl;
02271 if (result.isCheckmateSuccess())
02272 {
02273 for (unsigned int i = 0; i < max_pass; ++i)
02274 {
02275 if (record->getValue<A>(i).isCheckmateSuccess())
02276 {
02277 return i;
02278 }
02279 }
02280 }
02281 }
02282
02283 ntesuki_assert(result.isCheckmateFail());
02284 return NtesukiNotFound;
02285 }
02286
02287
02288
02289