ntesukiSearcher.tcc Source File

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ntesukiSearcher.tcc
Go to the documentation of this file.
1 /* ntesukiSearcher.tcc
2 */
3 #include "osl/ntesuki/ntesukiSearcher.h"
4 #include "osl/ntesuki/ntesukiMove.h"
5 #include "osl/ntesuki/ntesukiMoveList.h"
6 #include "osl/ntesuki/ntesukiSimulationSearcher.h"
7 #include "osl/apply_move/applyMoveWithPath.h"
8 #include "osl/effect_util/effectUtil.h"
9 // for release, inline the following templates as well
10 #ifdef NDEBUG
11 # include "osl/ntesuki/ntesukiMove.tcc"
12 # include "osl/ntesuki/ntesukiRecord.tcc"
13 #endif
14
15 #include "osl/record/csaRecord.h"
16 #include <climits>
17 #include <list>
18 #include <algorithm>
19
20 using namespace osl;
21 using namespace osl::ntesuki;
22
23 /* 409600 nodes at root node for MTDF
24 * (/ 40960 8) => 5120
25 * cf. 1600 for brinkmate searcher
26 */
27 const int READ_ATTACK_BACK_LIMIT = 5120;
28
29 #ifndef NDEBUG
30 #define RETURN(val) \
31 if (record->getValueWithPath<A>(pass_left, path).proof() == 0)\
32 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() > ProofDisproof::DISPROOF_LIMIT);\
33 if (record->getValueWithPath<A>(pass_left, path).disproof() == 0)\
34 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() > ProofDisproof::PROOF_LIMIT);\
35 ntesuki_assert(val.isFinal() == record->getValueWithPath<A>(pass_left, path).isFinal());\
36 return val
37 #else
38 #define RETURN(val) return val
39 #endif
40
41 #define RETURN_ON_STOP \
42 if (node_count > read_node_limit || *stop_flag)\
43 return
44
45 /* ===================
46 * misc
47 */
48 static
49 unsigned int addWithSaturation(unsigned int limit,
50 unsigned int l, unsigned int r)
51 {
52 if (limit < l)
53 return limit;
54 const unsigned int sum = l+r;
55 if (limit < sum)
56 return limit;
57 // guard against overflow
58 if (sum < l)
59 return limit;
60 ntesuki_assert(r <= sum);
61 return sum;
62 }
63
64 struct PlayMoveLock
65 {
66 std::vector<Move>& mlist;
67
68 PlayMoveLock(std::vector<Move>& l,
69 const osl::Move& m)
70 : mlist(l)
71 {
72 mlist.push_back(m);
73 }
74
75 ~PlayMoveLock()
76 {
77 mlist.pop_back();
78 }
79 };
80
81 struct LockGC
82 {
83 osl::ntesuki::NtesukiTable& table;
84 LockGC(osl::ntesuki::NtesukiTable& t)
85 : table(t)
86 {
87 table.lockGC();
88 }
89
90 ~LockGC()
91 {
92 table.unlockGC();
93 }
94 };
95
96 /* ===================
97 * Attack helper
98 */
99 template <class Search,Player T>
100 class
101 osl::ntesuki::NtesukiSearcher::
102 AttackHelper
103 {
104 unsigned int proof_limit, disproof_limit, pass_left;
105 Search* searcher;
106 NtesukiResult& result;
107 NtesukiRecord* record;
108 const NtesukiRecord* oracle_attack;
109 const NtesukiRecord* oracle_defense;
110 const Move last_move;
111 public:
112 AttackHelper(Search* searcher,
113 NtesukiResult& result,
114 NtesukiRecord* record,
115 const NtesukiRecord* oracle_attack,
116 const NtesukiRecord* oracle_defense,
117 unsigned int proof_limit,
118 unsigned int disproof_limit,
119 unsigned int pass_left,
120 const Move last_move)
121 : proof_limit(proof_limit), disproof_limit(disproof_limit),
122 pass_left(pass_left),
123 searcher(searcher), result(result), record(record),
124 oracle_attack(oracle_attack), oracle_defense(oracle_defense),
125 last_move(last_move)
126 {
127 }
128
129 void operator()(Square last_to)
130 {
131 result = (*searcher).template defense<PlayerTraits<T>::opponent>
132 (record, oracle_attack, oracle_defense,
133 proof_limit, disproof_limit, pass_left, last_move);
134 }
135 }; // AttackHelper
136
137 /* ===================
138 * Call Simulation Attack
139 */
140 template <class Search,Player T>
141 class
142 osl::ntesuki::NtesukiSearcher::
143 CallSimulationAttack
144 {
145 Search &simulator;
146 NtesukiTable& table;
147 NtesukiRecord *record;
148 const NtesukiRecord *record_orig;
149 unsigned int pass_left;
150 bool& simulation_result;
151 const Move last_move;
152
153 public:
154 CallSimulationAttack(Search& simulator,
155 NtesukiTable& table,
156 NtesukiRecord *record,
157 const NtesukiRecord *record_orig,
158 unsigned int pass_left,
159 bool& simulation_result,
160 const Move last_move)
161 : simulator(simulator), table(table),
162 record(record), record_orig(record_orig),
163 pass_left(pass_left),
164 simulation_result(simulation_result),
165 last_move(last_move)
166 {
167 }
168
169 void operator()(Square last_to)
170 {
171 LockGC glock(table);
172 simulation_result = simulator.template
173 startFromDefenseDisproof<PlayerTraits<T>::opponent>
174 (record, record_orig, pass_left, last_move);
175 }
176 }; // Call Simulation Attack
177
178 /* ===================
179 * Defense helper
180 */
181 template <class Search,Player T>
182 class
183 osl::ntesuki::NtesukiSearcher::
184 DefenseHelper
185 {
186 unsigned int proof_limit, disproof_limit, pass_left;
187 Search* searcher;
188 NtesukiResult& result;
189 NtesukiRecord* record;
190 const NtesukiRecord* oracle_attack;
191 const NtesukiRecord* oracle_defense;
192 const Move last_move;
193
194 public:
195 DefenseHelper(Search* searcher,
196 NtesukiResult& result,
197 NtesukiRecord* record,
198 const NtesukiRecord* oracle_attack,
199 const NtesukiRecord* oracle_defense,
200 unsigned int proof_limit,
201 unsigned int disproof_limit,
202 unsigned int pass_left,
203 const Move last_move)
204 : proof_limit(proof_limit), disproof_limit(disproof_limit),
205 pass_left(pass_left), searcher(searcher), result(result), record(record),
206 oracle_attack(oracle_attack), oracle_defense(oracle_defense),
207 last_move(last_move)
208 {
209 }
210
211 void operator()(Square p)
212 {
213 (*searcher).template attack<PlayerTraits<T>::opponent>
214 (record, oracle_attack, oracle_defense,
215 proof_limit, disproof_limit,
216 pass_left, last_move);
217 }
218 };// DefenseHelper
219
220 /* ===================
221 * Call Simulation Defense
222 */
223 template <class Search,Player T>
224 class
225 osl::ntesuki::NtesukiSearcher::
226 CallSimulationDefense
227 {
228 Search &simulator;
229 NtesukiTable &table;
230 NtesukiRecord *record;
231 const NtesukiRecord *record_orig;
232 unsigned int pass_left;
233 bool& simulation_result;
234 const Move last_move;
235 public:
236 CallSimulationDefense(Search& simulator,
237 NtesukiTable& table,
238 NtesukiRecord *record,
239 const NtesukiRecord *record_orig,
240 unsigned int pass_left,
241 bool& simulation_result,
242 const Move last_move)
243 : simulator(simulator), table(table),
244 record(record), record_orig(record_orig),
245 pass_left(pass_left),
246 simulation_result(simulation_result),
247 last_move(last_move)
248 {
249 }
250
251 void operator()(Square last_to)
252 {
253 LockGC glock(table);
254 simulation_result = simulator.template
255 startFromAttackProof<PlayerTraits<T>::opponent>
256 (record, record_orig, pass_left, last_move);
257 }
258 }; // Call Simulation Defense
259
260 /* ===================
261 * Call Simulation Defense Disproof
262 */
263 template <class Search,Player T>
264 class
265 osl::ntesuki::NtesukiSearcher::
266 CallSimulationDefenseDisproof
267 {
268 Search &simulator;
269 NtesukiTable &table;
270 NtesukiRecord *record;
271 const NtesukiRecord *record_orig;
272 unsigned int pass_left;
273 bool& simulation_result;
274 const Move last_move;
275 public:
276 CallSimulationDefenseDisproof(Search& simulator,
277 NtesukiTable& table,
278 NtesukiRecord *record,
279 const NtesukiRecord *record_orig,
280 unsigned int pass_left,
281 bool& simulation_result,
282 const Move last_move)
283 : simulator(simulator), table(table),
284 record(record), record_orig(record_orig),
285 pass_left(pass_left),
286 simulation_result(simulation_result),
287 last_move(last_move)
288 {
289 }
290
291 void operator()(Square last_to)
292 {
293 LockGC glock(table);
294 simulation_result = simulator.template
295 startFromAttackDisproof<PlayerTraits<T>::opponent>
296 (record, record_orig, pass_left, last_move);
297 }
298 }; // Call Simulation Defense Disproof
299
300 template <Player T>
301 void osl::ntesuki::NtesukiSearcher::
302 simulateSiblingsFail(NtesukiRecord *record,
303 NtesukiRecord *record_best,
304 int pass_left,
305 unsigned int& success_count,
306 unsigned int& total_count)
307 {
308 LockGC glock(table);
309
310 const Player A = T;
311 if (!record_best) return;
312 ntesuki_assert(record_best);
313
314 NtesukiMoveList moves;
315 mg->generate<T>(state, moves);
316
317 for (NtesukiMoveList::iterator move_it = moves.begin();
318 move_it != moves.end(); move_it++)
319 {
320 NtesukiMove& move = *move_it;
321 NtesukiRecord *record_child = table.allocateWithMove(record,
322 move);
323 if (record_child == 0)
324 {
325 *stop_flag = TableLimitReached;
326 return;
327 }
328 ntesuki_assert(record_child);
329 if (record_child == record_best) continue;
330 if (record_child->isVisited()) continue;
331 if (move.isCheckmateFail<A>(pass_left)) continue;
332 const PathEncoding path_child(path, move.getMove());
333 const NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
334 path_child);
335 if (result_child.isFinal())
336 {
337 continue;
338 }
339
340 bool simulation_result;
341 total_count++;
342 CallSimulationAttack<NtesukiSimulationSearcher, T>
343 helper(simulator, table, record_child, record_best,
344 pass_left, simulation_result, move.getMove());
345 TRY_DFPN;
346 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move.getMove(), helper);
347 CATCH_DFPN;
348 RETURN_ON_STOP;
349
350 if (simulation_result)
351 {
352 success_count++;
353 ntesuki_assert(record_child->getValueWithPath<A>(pass_left,
354 path_child).isCheckmateFail());
355 TRY_DFPN;
356 move.setBySimulation();
357 move.setCheckmateFail<A>(pass_left);
358 CATCH_DFPN;
359 }
360 }
361 }
362
363 /* ===================
364 * Count the increase of child nodes
365 */
366 class CountChildLock
367 {
368 public:
369 CountChildLock(NtesukiRecord* r,
370 const NtesukiTable& t)
371 : record(r), table(t)
372 {
373 size_start = table.size();
374 }
375
376 ~CountChildLock()
377 {
378 record->addChildCount(table.size() - size_start);
379 }
380 private:
381 osl::ntesuki::NtesukiRecord* record;
382 const osl::ntesuki::NtesukiTable& table;
383 unsigned int size_start;
384 };
385
386
387 /* ===================
388 * Attack
389 */
390 template <Player T>
391 NtesukiResult osl::ntesuki::NtesukiSearcher::
392 attack(NtesukiRecord* record,
393 const NtesukiRecord* oracle_attack,
394 const NtesukiRecord* oracle_defense,
395 unsigned int proof_limit, unsigned int disproof_limit,
396 const int pass_left, const Move last_move)
397 {
398 CountChildLock cclock(record, table);
399 const Player A = T;
400 #ifndef NDEBUG
401 const Player D = PlayerTraits<T>::opponent;
402 #endif
403
404 ntesuki_assert(T == state.turn());
405 ntesuki_assert(!state.inCheck(D));
406 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
407 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
408 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).proof()
409 < proof_limit);
410 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).disproof()
411 < disproof_limit);
412
413 RETURN_ON_STOP (record->getValueOr<A>(pass_left, path, iwscheme));
414
415 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).proof()
416 < proof_limit);
417 ntesuki_assert(record->getValueOr<A>(pass_left, path, iwscheme).disproof()
418 < disproof_limit);
419 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
420
421 ntesuki_assert(proof_limit > 0);
422 ntesuki_assert(disproof_limit > 0);
423
424 /* ノードの初期化． 必要なら FixedDepthSearcher も呼ばれる．
425 */
426 TRY_DFPN;
427 if (record->setUpNode<T>())
428 {
429 const NtesukiResult result_cur =
430 record->getValueWithPath<A>(pass_left, path);
431 if (result_cur.isCheckmateSuccess())
432 {
433 /* By fixed searcher */
434 ++immediate_win;
435 RETURN (result_cur);
436 }
437 else if (result_cur.isCheckmateFail() && pass_left == 0)
438 {
439 RETURN (result_cur);
440 }
441 }
442 CATCH_DFPN;
443
444 /* Iterative Wideningc Scheme に応じて探索
445 */
446 NtesukiResult result_cur = record->getValueOr<A>(pass_left, path, iwscheme);
447 while ((result_cur.proof() < proof_limit) &&
448 (result_cur.disproof() < disproof_limit))
449 {
450 if (iwscheme == NtesukiRecord::no_iw)
451 {
452 /* Order は変えない
453 */
454 TRY_DFPN;
455 attackWithOrder<T>(record, NULL, NULL,
456 proof_limit, disproof_limit,
457 pass_left, last_move);
458
459 CATCH_DFPN;
460 RETURN_ON_STOP (result_cur);
461 }// no_iw
462 else if (iwscheme == NtesukiRecord::strict_iw)
463 {
464 /* 必ず小さい方から探索
465 */
466 for (int pass_left_child = 0; pass_left_child <= pass_left; pass_left_child++)
467 {
468 NtesukiResult result_st = record->getValueWithPath<A>(pass_left_child, path);
469 if (!result_st.isCheckmateFail())
470 {
471 ntesuki_assert(result_st.proof() < proof_limit);
472 ntesuki_assert(result_st.disproof() < disproof_limit);
473 TRY_DFPN;
474 attackWithOrder<T>(record, NULL, NULL,
475 proof_limit, disproof_limit,
476 pass_left_child, last_move);
477
478 CATCH_DFPN;
479 RETURN_ON_STOP (result_cur);
480 break;
481 }
482 }
483 }// strict_iw
484 else if (iwscheme == NtesukiRecord::pn_iw)
485 {
486 /* proof number の小さい方から探索
487 */
488 unsigned int p_min = ProofDisproof::BigProofNumber,
489 p_2nd = ProofDisproof::BigProofNumber;
490 int pass_left_best = -1;
491 for (int pass_left_child = 0; pass_left_child <= pass_left; pass_left_child++)
492 {
493 NtesukiResult result_st = record->getValueWithPath<A>(pass_left_child, path);
494 if (result_st.isCheckmateFail()) continue;
495
496 const unsigned int proof = result_st.proof();
497 if (proof < p_min)
498 {
499 pass_left_best = pass_left_child;
500 p_2nd = p_min;
501 p_min = proof;
502 }
503 else if (proof < p_2nd)
504 {
505 p_2nd = proof;
506 }
507 }
508
509 unsigned int proof_limit_child = std::min(proof_limit, p_2nd + 1);
510 unsigned int disproof_limit_child = disproof_limit;
511 TRY_DFPN;
512 attackWithOrder<T>(record, NULL, NULL,
513 proof_limit_child, disproof_limit_child,
514 pass_left_best, last_move);
515
516 CATCH_DFPN;
517 RETURN_ON_STOP (result_cur);
518 }// pn_iw
519 result_cur = record->getValueOr<A>(pass_left, path, iwscheme);
520 }
521 return result_cur;
522 }
523
524 template <Player T>
525 void osl::ntesuki::NtesukiSearcher::
526 attackWithOrder(NtesukiRecord* record,
527 const NtesukiRecord* oracle_attack,
528 const NtesukiRecord* oracle_defense,
529 unsigned int proof_limit, unsigned int disproof_limit,
530 const int pass_left, const Move last_move)
531 {
532 ++node_count;
533
534 const Player A = T;
535 #ifndef NDEBUG
536 const Player D = PlayerTraits<T>::opponent;
537 #endif
538 ntesuki_assert(T == state.turn());
539 ntesuki_assert(!state.inCheck(D));
540 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
541 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
542
543 RETURN_ON_STOP;
544 const bool under_attack = state.inCheck(T);
545
546 ntesuki_assert (record->getValueWithPath<A>(pass_left, path).proof()
547 < proof_limit);
548 ntesuki_assert (record->getValueWithPath<A>(pass_left, path).disproof()
549 < disproof_limit);
550 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
551
552 NtesukiRecord::VisitLock visitLock(record);
553
554 ntesuki_assert(proof_limit > 0);
555 ntesuki_assert(disproof_limit > 0);
556
557 NtesukiMoveList moves;
558 /* 手の生成
559 */
560 TRY_DFPN;
561 record->generateMoves<T>(moves, pass_left, false);
562 CATCH_DFPN;
563
564 /* collect statistic information */
565 ++attack_node_count;
566 if (under_attack)
567 {
568 ++attack_node_under_attack_count;
569 }
570 attack_node_moves_count += moves.size();
571
572 /* 攻め手がなくなる→失敗 */
573 if (moves.empty())
574 {
575 if (pass_left != 0 &&
576 record->rzone_move_generation)
577 {
578 /* まだ未生成の手がある */
579 NtesukiResult r = record->getValueWithPath<A>(pass_left - 1, path);
580
581 if (r.isCheckmateFail())
582 {
583 /* rzone はこれ以上は拡大しない */
584 record->rzone_move_generation = false;
585 TRY_DFPN;
586 record->setResult<A>(pass_left, ProofDisproof(1,1),
587 NtesukiMove::INVALID(), false);
588 CATCH_DFPN;
589 }
590 else
591 {
592 /* rzone が増えるまで，より低い order での探索を優先させる */
593 TRY_DFPN;
594 record->setResult<A>(pass_left, ProofDisproof(r.proof() + 2,
595 r.disproof() + 2),
596 NtesukiMove::INVALID(), false);
597 CATCH_DFPN;
598 }
599 return;
600 }
601 TRY_DFPN;
602 /* 双玉問題なら D の NoEscape だが，
603 * 片玉問題で，全合法手を生成しない場合(pass_left==0 の場合など)では
604 * ただの NoCheckmate */
605 record->setResult<A>(pass_left, ProofDisproof::NoCheckmate(),
606 NtesukiMove::INVALID(), false);
607 CATCH_DFPN;
608 return;
609 }
610
611 ntesuki_assert(!moves.empty());
612 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() != 0);
613 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() != 0);
614
615 /* 攻める手の実行
616 */
617 for (;;)
618 {
619 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).proof() != 0);
620 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() != 0);
621
622 unsigned int best_proof = ProofDisproof::BigProofNumber,
623 best_disproof = 0,
624 second_proof = ProofDisproof::BigProofNumber,
625 sum_disproof = 0;
626 unsigned int step_cost = 1;
627
628 NtesukiMove* best_move =
629 selectMoveAttack<T>(record,
630 best_proof, sum_disproof,
631 second_proof, best_disproof,
632 step_cost,
633 moves,
634 pass_left);
635 if (best_move == NULL)
636 {
637 if (pass_left != 0 &&
638 record->rzone_move_generation)
639 {
640 /* まだ未生成の手がある */
641 NtesukiResult r = record->getValueWithPath<A>(pass_left - 1, path);
642
643 if (r.isCheckmateFail())
644 {
645 /* rzone はこれ以上は拡大しない */
646 record->rzone_move_generation = false;
647 TRY_DFPN;
648 record->setResult<A>(pass_left, ProofDisproof(1,1),
649 NtesukiMove::INVALID(), false);
650 CATCH_DFPN;
651 }
652 else
653 {
654 /* rzone が増えるまで，より低い order での探索を優先させる */
655 TRY_DFPN;
656 record->setResult<A>(pass_left, ProofDisproof(r.proof() + 2,
657 r.disproof() + 2),
658 NtesukiMove::INVALID(), false);
659 CATCH_DFPN;
660 }
661 }
662 else
663 {
664 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).disproof() == 0);
665 }
666 return;
667 }
668 else if (best_move->isCheckmateSuccess<A>(pass_left))
669 {
670 return;
671 }
672
673 /* このノードの証明数・反証数を設定する
674 */
675 const NtesukiResult result_cur(best_proof, sum_disproof);
676 record->setResult<A>(pass_left, result_cur,
677 NtesukiMove::INVALID(), false);
678
679 /* 他を読む
680 */
681 if ((proof_limit <= best_proof) ||
682 (disproof_limit <= sum_disproof))
683 {
684 ntesuki_assert(!result_cur.isFinal());
685 return;
686 }
687
688 /* 手を試す
689 */
690 unsigned int proof_child = std::min(proof_limit, second_proof + step_cost);
691 ntesuki_assert(disproof_limit > sum_disproof);// disproof_child
692 unsigned int disproof_child =
693 addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
694 disproof_limit, best_disproof)
695 - sum_disproof;
696
697 NtesukiRecord *record_child = table.allocateWithMove(record, *best_move);
698 if (record_child == 0)
699 {
700 *stop_flag = TableLimitReached;
701 return;
702 }
703 ntesuki_assert(record_child);
704 const PathEncoding path_child(path, best_move->getMove());
705 NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
706 path_child);
707 if (!result_child.isFinal())
708 {
709 if (best_move->isCheck())
710 {
711 oracle_attack = NULL;
712 }
713 if (ptt_aunt &&
714 pass_left != 0 &&
715 record->getValueWithPath<A>(pass_left - 1, path).isCheckmateFail())
716 {
717 oracle_defense = record;
718 }
719
720 AttackHelper<NtesukiSearcher, T> helper(this,
721 result_child,
722 record_child,
723 oracle_attack,
724 oracle_defense,
725 proof_child,
726 disproof_child,
727 pass_left,
728 best_move->getMove());
729 PlayMoveLock pml(moves_played, best_move->getMove());
730 TRY_DFPN;
731 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, best_move->getMove(), helper);
732 CATCH_DFPN;
733 record->updateWithChild(record_child, pass_left);
734 RETURN_ON_STOP;
735
736 const NtesukiResult result_cur = record->getValueWithPath<A>(pass_left, path_child);
737 if (result_cur.isFinal())
738 {
739 return;
740 }
741 }
742
743 if (result_child.isPawnDropFoul(best_move->getMove()))
744 {
745 best_move->setPawnDropCheckmate();
746 best_move->setCheckmateFail<A>(pass_left);
747 }
748 else if (result_child.isCheckmateSuccess())
749 {
750 best_move->setCheckmateSuccess<A>(pass_left);
751 TRY_DFPN;
752 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
753 *best_move, false);
754 CATCH_DFPN;
755 return;
756 }
757 else if (result_child.isCheckmateFail())
758 {
759 if (result_child != ProofDisproof::LoopDetection())
760 {
761 best_move->setCheckmateFail<A>(pass_left);
762 }
763
764 if (result_child == ProofDisproof::PawnCheckmate())
765 {
766 best_move->setPawnDropCheckmate();
767 }
768
769 if (ptt_siblings_fail &&
770 !best_move->isCheck() &&
771 result_child != ProofDisproof::LoopDetection())
772 {
773 TRY_DFPN;
774 simulateSiblingsFail<T>(record, record_child, pass_left,
775 sibling_attack_success_count,
776 sibling_attack_count);
777 CATCH_DFPN;
778 }
779 }
780 }//for(;;)
781 }
782
783 /* dynamic widening */
784 typedef std::pair<unsigned int, unsigned int> top_pdp_t;
785 static bool sorter(const top_pdp_t& lhs,
786 const top_pdp_t& rhs)
787 {
788 if (lhs.first == rhs.first)
789 {
790 //return rhs.second > rhs.second;
791 return rhs.second < rhs.second;
792 }
793 else
794 {
795 return lhs.first > rhs.first;
796 }
797 }
798
799 template <Player T>
800 osl::ntesuki::NtesukiMove * osl::ntesuki::NtesukiSearcher::
801 selectMoveAttack(NtesukiRecord* record,
802 unsigned int& best_proof,
803 unsigned int& sum_disproof,
804 unsigned int& second_proof,
805 unsigned int& best_disproof,
806 unsigned int& step_cost,
807 NtesukiMoveList& moves,
808 const int pass_left)
809 {
810 const Player A = T;
811
812 bool read_nopromote = false;
813
814 re_select_move_attack:
815 NtesukiMove *best_move = NULL;
816
817 bool loop = false;
818 bool pawn_checkmate = false;
819 unsigned short min_child_age = SHRT_MAX;
820
821 int average_cost = 0;
822 int average_cost_count = 0;
823
824 /* reset values */
825 best_proof = ProofDisproof::BigProofNumber;
826 best_disproof = 0;
827 second_proof = ProofDisproof::BigProofNumber;
828 sum_disproof = 0;
829
830 /* dynamic Widening */
831 std::list<top_pdp_t> pdps;
832
833 /* 手を選ぶ
834 */
835 for (NtesukiMoveList::iterator move_it = moves.begin();
836 move_it != moves.end(); ++move_it)
837 {
838 NtesukiMove& move = *move_it;
839 pawn_checkmate |= move.isPawnDropCheckmate();
840
841 if (move.isPass())
842 {
843 continue;
844 }
845
846 if (!move.isCheck() && 0 == pass_left)
847 {
848 continue;
849 }
850
851 if (move.isCheckmateFail<A>(pass_left))
852 {
853 continue;
854 }
855
856 if (delay_nopromote &&
857 !read_nopromote &&
858 move.isNoPromote())
859 {
860 continue;
861 }
862
863 if (delay_non_attack &&
864 !record->readNonAttack(pass_left) &&
865 ((move.getOrder() > pass_left) ||
866 move.isLameLong())
867 )
868 {
869 continue;
870 }
871
872 unsigned int proof = move.h_a_proof;
873 unsigned int disproof = move.h_a_disproof;
874
875 if (tsumero_estimate && !move.isCheck())
876 {
877 proof = tsumero_estimate;
878 disproof = 1;
879 }
880
881 average_cost += proof;
882 average_cost_count++;
883
884 NtesukiRecord *record_child = table.findWithMove(record, move);
885 if (record_child)
886 {
887 if (record_child->isVisited())
888 {
889 /* ループ発見 */
890 /* defender の方からの王手千日手の可能性アリ
891 */
892 loop = true;
893 continue;
894 }
895
896 const PathEncoding path_child(path, move.getMove());
897 NtesukiResult result_child;
898 TRY_DFPN;
899 result_child =
900 record_child->getValueAnd<A>(pass_left, path_child,
901 iwscheme, psscheme);
902 CATCH_DFPN;
903 proof = result_child.proof();
904 disproof = result_child.disproof();
905
906 if (0 == disproof)/* 不詰み */
907 {
908 if (result_child == ProofDisproof::LoopDetection())
909 {
910 loop = true;
911 continue;
912 }
913 if (record_child->getValueWithPath<A>(pass_left, path_child) ==
914 ProofDisproof::PawnCheckmate())
915 {
916 move.setPawnDropCheckmate();
917 pawn_checkmate = true;
918 }
919
920 ntesuki_assert(proof >= ProofDisproof::PROOF_LIMIT);
921 move.setCheckmateFail<A>(pass_left);
922
923 /* should do ptt_siblings_fail */
924 continue;
925 }
926 else if (0 == proof)/* 詰み */
927 {
928 /* 打歩詰めだけチェック */
929 if (record_child->getValueWithPath<A>(pass_left, path_child).
930 isPawnDropFoul(move.getMove()))
931 {
932 move.setPawnDropCheckmate();
933 move.setCheckmateFail<A>(pass_left);
934 pawn_checkmate = true;
935 continue;
936 }
937
938 /* 間違いなく勝ち手 */
939 ntesuki_assert(disproof >= ProofDisproof::DISPROOF_LIMIT);
940 move.setCheckmateSuccess<A>(pass_left);
941 TRY_DFPN;
942 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
943 move, false);
944 CATCH_DFPN;
945
946 return &move;
947 }
948
949 min_child_age = std::min(record_child->distance,
950 min_child_age);
951 if (record_child->distance <= record->distance)
952 {
953 if (!record->useOld<A>(pass_left))
954 {
955 continue;
956 }
957 }
958
959 /* the assertion:
960 * use_old == (record_child->distance <= record->distance)
961 * does not hold, as the distance gets updated to youngest.
962 */
963 }
964
965 /* Proof Disproof の調整はここで */
966 if (!move.isCheck())
967 {
968 ntesuki_assert(pass_left > 0);
969 proof += tsumero_cost;
970 disproof += tsumero_cost;
971 }
972
973 if (!record->useOld<A>(pass_left)
974 && !(NtesukiRecord::max_for_split && record->is_split))
975 {
976 sum_disproof = addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
977 disproof, sum_disproof);
978 }
979 else
980 {
981 sum_disproof = std::max(disproof, sum_disproof);
982 }
983
984 /* best move の設定 */
985 if (proof < best_proof)
986 {
987 best_move = &move;
988 second_proof = best_proof;
989 best_proof = proof;
990 best_disproof = disproof;
991 }
992 else if (proof < second_proof)
993 {
994 second_proof = proof;
995 }
996
997 /* dynamic widening: 良い手の選定 */
998 if (dynamic_widening_width > 0)
999 {
1000 if (pdps.size() < dynamic_widening_width)
1001 {
1002 pdps.push_back(top_pdp_t(proof, disproof));
1003 //Sorter sorter;
1004 pdps.sort(sorter);
1005 }
1006 else
1007 {
1008 if (pdps.back().first > proof)
1009 {
1010 pdps.pop_back();
1011 pdps.push_back(top_pdp_t(proof, disproof));
1012 }// back().proof == proof だった場合は？
1013 pdps.sort(sorter);
1014 }
1015 }
1016 }
1017
1018 /* dynamic widening: 良い手の集計 */
1019 if ((dynamic_widening_width > 0 &&
1020 dynamic_widening_width < moves.size())
1021 &&
1022 (!record->useOld<A>(pass_left)
1023 && !(NtesukiRecord::max_for_split && record->is_split)))
1024 {
1025 sum_disproof = 0;
1026 for (std::list<top_pdp_t>::const_iterator it = pdps.begin();
1027 it != pdps.end(); ++it)
1028 {
1029 sum_disproof += it->second;
1030 }
1031 }
1032
1033 /* 選んだ手を吟味する
1034 */
1035 if (best_move == NULL)
1036 {
1037 if (false == record->useOld<A>(pass_left))
1038 {
1039 if (SHRT_MAX != min_child_age)
1040 {
1041 record->setUseOld<A>(pass_left, true);
1042
1043 ntesuki_assert(min_child_age <= record->distance);
1044 record->distance = min_child_age;
1045
1046 goto re_select_move_attack;
1047 }
1048 }
1049
1050 if (!record->readNonAttack(pass_left))
1051 {
1052 if (!read_attack_only)
1053 {
1054 record->setReadNonAttack(pass_left);
1055 if (ptt_non_attack &&
1056 pass_left != 0)
1057 {
1058 TRY_DFPN;
1059 handleNonAttack<T>(record, pass_left);
1060 CATCH_DFPN;
1061 RETURN_ON_STOP NULL;
1062 }
1063 record->setUseOld<A>(pass_left, false);
1064 goto re_select_move_attack;
1065 }
1066 }
1067
1068 if (delay_nopromote &&
1069 !read_nopromote &&
1070 (pass_left > 0 || pawn_checkmate))
1071 {
1072 read_nopromote = true;
1073 record->setUseOld<A>(pass_left, false);
1074 goto re_select_move_attack;
1075 }
1076
1077 ntesuki_assert(best_proof == ProofDisproof::BigProofNumber);
1078
1079 if (pass_left != 0 &&
1080 record->rzone_move_generation)
1081 {
1082 /* まだ未生成の手がある */
1083 return NULL;
1084 }
1085 /* 本当に disproof されてしまった */
1086 if (pawn_checkmate)/* 打歩詰めがあった */
1087 {
1088 if (delay_nopromote) assert(read_nopromote);
1089
1090 TRY_DFPN;
1091 record->setResult<A>(pass_left, ProofDisproof::PawnCheckmate(),
1092 NtesukiMove::INVALID(), false);
1093 CATCH_DFPN;
1094 return NULL;
1095 }
1096 // pawn checkmate and loop??
1097 else if (loop) /* ループがあった */
1098 {
1099 record->setLoopWithPath<A>(pass_left, path);
1100 TRY_DFPN;
1101 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1102 NtesukiMove::INVALID(), false);
1103 CATCH_DFPN;
1104 return NULL;
1105 }
1106 else /* 全ての手が普通に反証された */
1107 {
1108 TRY_DFPN;
1109 record->setResult<A>(pass_left, ProofDisproof::NoCheckmate(),
1110 NtesukiMove::INVALID(), false);
1111 CATCH_DFPN;
1112 return NULL;
1113 }
1114 }
1115
1116 ntesuki_assert(best_proof != 0);
1117 ntesuki_assert(sum_disproof != 0);
1118 ntesuki_assert(best_proof < ProofDisproof::PROOF_LIMIT);
1119 ntesuki_assert(sum_disproof < ProofDisproof::DISPROOF_LIMIT);
1120
1121 if (record->useOld<A>(pass_left))
1122 {
1123 ntesuki_assert(min_child_age != SHRT_MAX);
1124 record->distance = min_child_age;
1125 }
1126 average_cost /= average_cost_count;
1127 step_cost = std::max(average_cost, 1);
1128 return best_move;
1129 }
1130
1131 template <Player T>
1132 void osl::ntesuki::NtesukiSearcher::
1133 handleNonAttack(NtesukiRecord* record,
1134 int pass_left)
1135 {
1136 const Player A = T;
1137 ntesuki_assert(T == state.turn());
1138
1139 NtesukiMoveList moves;
1140 mg->generate<T>(state, moves);
1141
1142 for (NtesukiMoveList::iterator move_it = moves.begin();
1143 move_it != moves.end(); ++move_it)
1144 {
1145 NtesukiRecord *record_best = table.findWithMove(record,
1146 *move_it);
1147 const PathEncoding path_best(path, move_it->getMove());
1148 if (!record_best ||
1149 !record_best->getValueWithPath<A>(pass_left,
1150 path_best).isCheckmateFail()) continue;
1151
1152 /* record_best is checkmate fail */
1153 for (NtesukiMoveList::iterator move_it2 = moves.begin();
1154 move_it2 != moves.end(); ++move_it2)
1155 {
1156 if (move_it2->isPass())
1157 {
1158 continue;
1159 }
1160 if (*move_it2 == *move_it)
1161 {
1162 continue;
1163 }
1164 if (move_it2->isCheckmateFail<A>(pass_left))
1165 {
1166 continue;
1167 }
1168 NtesukiRecord *record_child = table.allocateWithMove(record,
1169 *move_it2);
1170 if (record_child == 0)
1171 {
1172 *stop_flag = TableLimitReached;
1173 return;
1174 }
1175 ntesuki_assert(record_child);
1176 const PathEncoding path_child(path, move_it->getMove());
1177 if(record_child->getValueWithPath<A>(pass_left,
1178 path_child).isFinal())
1179 {
1180 continue;
1181 }
1182
1183 if (record_child->isVisited())
1184 {
1185 TRY_DFPN;
1186 move_it2->setCheckmateFail<A>(pass_left);
1187 record->setLoopWithPath<A>(pass_left, path_child);
1188 CATCH_DFPN;
1189 continue;
1190 }
1191
1192 bool simulation_result;
1193 CallSimulationAttack<NtesukiSimulationSearcher, T>
1194 helper(simulator, table, record_child, record_best,
1195 pass_left, simulation_result, move_it2->getMove());
1196 TRY_DFPN;
1197 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move_it2->getMove(), helper);
1198 CATCH_DFPN;
1199 RETURN_ON_STOP;
1200
1201 if (simulation_result)
1202 {
1203 move_it2->setBySimulation();
1204 move_it2->setCheckmateFail<A>(pass_left);
1205 }
1206 }
1207 }
1208 }
1209
1210 /* ===================
1211 * defense
1212 */
1213 template <Player T>
1214 NtesukiResult osl::ntesuki::NtesukiSearcher::
1215 defense(NtesukiRecord* record,
1216 const NtesukiRecord* oracle_attack,
1217 const NtesukiRecord* oracle_defense,
1218 unsigned int proof_limit, unsigned int disproof_limit,
1219 int pass_left,
1220 const Move last_move)
1221 {
1222 const Player A = PlayerTraits<T>::opponent;
1223 const Player D = T;
1224 CountChildLock cclock(record, table);
1225
1226 ntesuki_assert(T == state.turn());
1227 ntesuki_assert(proof_limit < ProofDisproof::PROOF_LIMIT);
1228 ntesuki_assert(disproof_limit < ProofDisproof::DISPROOF_LIMIT);
1229 ntesuki_assert (record->getValueAnd<A>(pass_left, path,
1230 iwscheme, psscheme).proof()
1231 < proof_limit);
1232 ntesuki_assert (record->getValueAnd<A>(pass_left, path,
1233 iwscheme, psscheme).disproof()
1234 < disproof_limit);
1235
1236 ntesuki_assert(state.inCheck(T) || (pass_left > 0));
1237 ntesuki_assert(!state.inCheck(A));
1238
1239 ++node_count;
1240 RETURN_ON_STOP (record->getValueAnd<A>(pass_left, path,
1241 iwscheme, psscheme));
1242
1243 ntesuki_assert(record);
1244 ntesuki_assert(!record->getValueWithPath<A>(pass_left, path).isFinal());
1245 ntesuki_assert(record->getBestMove<A>(pass_left).isInvalid());
1246
1247 NtesukiRecord::VisitLock visitLock(record);
1248
1249 /* ノードの初期化． 必要なら FixedDepthSearcher も呼ばれる．
1250 */
1251 TRY_DFPN;
1252 if (record->setUpNode<T>())
1253 {
1254 const NtesukiResult r = record->getValueWithPath<A>(pass_left, path);
1255 if (r.isCheckmateFail())
1256 {
1257 /* By fixed searcher */
1258 ++immediate_lose;
1259 RETURN (r);
1260 }
1261 else if (r.isFinal())
1262 {
1263 RETURN (r);
1264 }
1265 }
1266 CATCH_DFPN;
1267
1268 /* Player Selection Scheme に応じて探索
1269 */
1270 NtesukiResult result_cur = record->getValueAnd<A>(pass_left, path, iwscheme, psscheme);
1271 while ((result_cur.proof() < proof_limit) &&
1272 (result_cur.disproof() < disproof_limit))
1273 {
1274 bool read_attack_first = false;
1275 if (psscheme)
1276 {
1277 /* Dual Lambda 探索を行う:
1278 * pn_D(n-1) < dn_A(n) なら order を下げて，攻守を入れ換える. ただし，
1279 * pn_D(n-1) : 受け方の order n-1 における証明数
1280 * dn_A(n) : 攻め方の order n における反証数
1281 */
1282 if (pass_left > 0)
1283 {
1284 const NtesukiResult result_attacker =
1285 record->getValueWithPath<A>(pass_left, path);
1286 const NtesukiResult result_defender =
1287 record->getValueOr<D>(pass_left - 1, path, iwscheme);
1288 if (result_defender.proof() < result_attacker.disproof())
1289 {
1290 read_attack_first = true;
1291 }
1292 }
1293 }
1294
1295 if (read_attack_first)
1296 {
1297 NtesukiRecord::UnVisitLock unVisitLock(record);
1298
1299 TRY_DFPN;
1300 attack<T>(record, NULL, NULL,
1301 disproof_limit, proof_limit,
1302 pass_left - 1, last_move);
1303 CATCH_DFPN;
1304 RETURN_ON_STOP (result_cur);
1305 }
1306 else
1307 {
1308 TRY_DFPN;
1309 defenseWithPlayer<T>(record, oracle_attack, oracle_defense,
1310 proof_limit, disproof_limit,
1311 pass_left, last_move);
1312 CATCH_DFPN;
1313 RETURN_ON_STOP (result_cur);
1314 }
1315 result_cur = record->getValueAnd<A>(pass_left, path, iwscheme, psscheme);
1316 }
1317 return result_cur;
1318 }
1319
1320 template <Player T>
1321 void osl::ntesuki::NtesukiSearcher::
1322 defenseWithPlayer(NtesukiRecord* record,
1323 const NtesukiRecord* oracle_attack,
1324 const NtesukiRecord* oracle_defense,
1325 unsigned int proof_limit, unsigned int disproof_limit,
1326 int pass_left,
1327 const Move last_move)
1328 {
1329 const Player A = PlayerTraits<T>::opponent;
1330 const bool under_attack = state.inCheck(T);
1331
1332 /* 受け手の生成
1333 */
1334 NtesukiMoveList moves;
1335
1336 TRY_DFPN;
1337 record->generateMoves<T>(moves, pass_left, true);
1338 CATCH_DFPN;
1339
1340 if (moves.empty())
1341 {
1342 TRY_DFPN;
1343 record->setResult<A>(0, ProofDisproof::NoEscape(),
1344 NtesukiMove::INVALID(), false);
1345 CATCH_DFPN;
1346 return;
1347 }
1348
1349 /* collect statistic information */
1350 ++defense_node_count;
1351 if (under_attack)
1352 {
1353 ++defense_node_under_attack_count;
1354 }
1355 defense_node_moves_count += moves.size();
1356
1357 ntesuki_assert(!moves.empty());
1358 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).isUnknown());
1359
1360 /* Pass の disproof simulation. */
1361 if (!under_attack &&
1362 record->do_oracle_aunt &&
1363 oracle_defense)
1364 {
1365 record->do_oracle_aunt = false;
1366
1367 NtesukiMove pass(Move::PASS(T));
1368 NtesukiRecord *record_pass = table.allocateWithMove(record,
1369 pass);
1370 if (record_pass == 0)
1371 {
1372 *stop_flag = TableLimitReached;
1373 return;
1374 }
1375 ntesuki_assert(record_pass);
1376
1377 if (record_pass->isVisited())
1378 {
1379 /* ループ発見 */
1380 record->setLoopWithPath<A>(pass_left, path);
1381 assert(record->isLoopWithPath<A>(pass_left, path));
1382 TRY_DFPN;
1383 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1384 NtesukiMove::INVALID(), false);
1385 CATCH_DFPN;
1386 return;
1387 }
1388
1389 ntesuki_assert(record_pass);
1390 const PathEncoding path_pass(path, pass.getMove());
1391 const NtesukiResult result_pass = record_pass->getValueWithPath<A>(pass_left - 1,
1392 path_pass);
1393 if (!result_pass.isFinal())
1394 {
1395 bool simulation_result;
1396 CallSimulationDefenseDisproof<NtesukiSimulationSearcher, T>
1397 helper(simulator, table, record_pass, oracle_defense,
1398 pass_left - 1, simulation_result, pass.getMove());
1399 TRY_DFPN;
1400 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, pass.getMove(), helper);
1401 CATCH_DFPN;
1402 return;
1403
1404 if (simulation_result)
1405 {
1406 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
1407 path_pass).isCheckmateFail());
1408 pass.setBySimulation();
1409 pass.setCheckmateFail<A>(pass_left);
1410 }
1411 }
1412 }
1413
1414 /* IS 将棋の simulation */
1415 if (record->do_oracle_attack && oracle_attack)
1416 {
1417 record->do_oracle_attack = false;
1418
1419 ntesuki_assert(ptt_uncle && !under_attack); // 本来は pass_left >= 1 か
1420 NtesukiMove pass(Move::PASS(T));
1421
1422 NtesukiRecord *record_pass = table.allocateWithMove(record,
1423 pass);
1424 if (record_pass == 0)
1425 {
1426 *stop_flag = TableLimitReached;
1427 return;
1428 }
1429 ntesuki_assert(record_pass);
1430
1431 if (record_pass->isVisited())
1432 {
1433 /* ループ発見 */
1434 record->setLoopWithPath<A>(pass_left, path);
1435 assert(record->isLoopWithPath<A>(pass_left, path));
1436 TRY_DFPN;
1437 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1438 NtesukiMove::INVALID(), false);
1439 CATCH_DFPN;
1440 return;
1441 }
1442
1443 ntesuki_assert(record_pass);
1444 const PathEncoding path_pass(path, pass.getMove());
1445 const NtesukiResult result_pass = record_pass->getValueWithPath<A>(pass_left - 1,
1446 path_pass);
1447 if (!result_pass.isFinal())
1448 {
1449 ++isshogi_attack_count;
1450
1451 bool simulation_result;
1452 CallSimulationDefense<NtesukiSimulationSearcher, T>
1453 helper(simulator, table, record_pass, oracle_attack,
1454 pass_left - 1, simulation_result, pass.getMove());
1455 TRY_DFPN;
1456 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, pass.getMove(), helper);
1457 CATCH_DFPN;
1458 return;
1459
1460 if (simulation_result)
1461 {
1462 ++isshogi_attack_success_count;
1463 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
1464 path_pass).isCheckmateSuccess());
1465 pass.setBySimulation();
1466 pass.setCheckmateSuccess<A>(pass_left);
1467 record->setNtesuki<A>(pass_left);
1468
1469 if (ptt_invalid_defense)
1470 {
1471 TRY_DFPN;
1472 simulateSiblingsSuccess<T>(record, record_pass, pass_left,
1473 pass_success_count,
1474 pass_count);
1475 CATCH_DFPN;
1476 return;
1477 }
1478 }
1479 }
1480 }
1481
1482 for (;;)
1483 {
1484 unsigned int best_disproof = ProofDisproof::BigProofNumber,
1485 sum_proof = 0,
1486 second_disproof = ProofDisproof::BigProofNumber,
1487 best_proof = 0;
1488
1489 unsigned int step_cost = 1;
1490 NtesukiMove *best_move = NULL;
1491
1492 best_move = selectMoveDefense<T>(record,
1493 best_disproof,
1494 sum_proof,
1495 second_disproof,
1496 best_proof,
1497 step_cost,
1498 moves,
1499 pass_left,
1500 last_move);
1501 RETURN_ON_STOP;
1502 if (NULL == best_move)
1503 {
1504 ntesuki_assert(record->getValueWithPath<A>(pass_left, path).
1505 isCheckmateSuccess());
1506 return;
1507 }
1508 else if (best_disproof == 0)
1509 {
1510 ntesuki_assert(best_move->isCheckmateFail<A>(pass_left) ||
1511 record->isLoopWithPath<A>(pass_left, path));
1512 return;
1513 }
1514
1515 #ifndef NDEBUG
1516 { //XXXXXXX
1517 NtesukiRecord* best_child = table.findWithMove(record, *best_move);
1518 if (best_child)
1519 {
1520 const PathEncoding path_child(path, best_move->getMove());
1521 int pass_left_child = pass_left;
1522 if (best_move->isPass()) --pass_left_child;
1523 const NtesukiResult r =
1524 best_child->getValueOr<A>(pass_left_child, path_child,iwscheme);
1525 ntesuki_assert(r.disproof() == best_disproof);
1526 ntesuki_assert(r.proof() <= sum_proof);
1527 }
1528 } //XXXXXXX
1529 #endif
1530
1531 /* このノードの証明数・反証数を設定する
1532 */
1533 const NtesukiResult result_cur = ProofDisproof(sum_proof, best_disproof);
1534 record->setResult<A>(pass_left, result_cur,
1535 NtesukiMove::INVALID(), false);
1536
1537 /* 他を読む
1538 */
1539 if ((disproof_limit <= best_disproof) ||
1540 (proof_limit <= sum_proof))
1541 {
1542 ntesuki_assert(!result_cur.isFinal());
1543 return;
1544 }
1545
1546 unsigned int proof_child =
1547 addWithSaturation(ProofDisproof::DISPROOF_LIMIT,
1548 proof_limit, best_proof) - sum_proof;
1549 unsigned int disproof_child = std::min(disproof_limit,
1550 second_disproof + step_cost);
1551
1552 /* 手を試す
1553 */
1554 int pass_left_child = pass_left;
1555 if (best_move->isPass())
1556 {
1557 --pass_left_child;
1558 }
1559 NtesukiRecord *record_child =
1560 table.allocateWithMove(record, *best_move);
1561 if (record_child == 0)
1562 {
1563 *stop_flag = TableLimitReached;
1564 return;
1565 }
1566 ntesuki_assert(record_child);
1567 const PathEncoding path_child(path, best_move->getMove());
1568 ntesuki_assert(pass_left_child >= 0);
1569 NtesukiResult result_child =
1570 record_child->getValueOr<A>(pass_left_child,
1571 path_child,
1572 iwscheme);
1573
1574 if (!result_child.isFinal())
1575 {
1576 if (best_move->isCheck())
1577 {
1578 if (ptt_uncle &&
1579 !under_attack &&
1580 delay_non_pass)
1581 {
1582 NtesukiMove& pass = moves.front();
1583 ntesuki_assert(pass.isPass());
1584
1585 oracle_attack = table.findWithMove(record, pass);
1586 ntesuki_assert(oracle_attack);
1587 }
1588 }
1589
1590 if (result_child.proof() >= proof_child)
1591 {
1592 std::cerr << *record_child
1593 << result_child << "<- result \n"
1594 << proof_child << "/" << disproof_child << "<- limit\n"
1595 << *best_move << "\n"
1596 << sum_proof << "/" << best_disproof << " <- cur\n";
1597 }
1598 DefenseHelper<NtesukiSearcher, T> helper(this,
1599 result_child,
1600 record_child,
1601 oracle_attack,
1602 oracle_defense,
1603 proof_child,
1604 disproof_child,
1605 pass_left_child,
1606 best_move->getMove());
1607
1608 PlayMoveLock pml(moves_played, best_move->getMove());
1609 if (best_move->isPass())
1610 {
1611 NtesukiRecord::pass_count++;
1612 }
1613 TRY_DFPN;
1614 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, best_move->getMove(), helper);
1615 CATCH_DFPN;
1616
1617 if (best_move->isPass())
1618 {
1619 NtesukiRecord::pass_count--;
1620 }
1621 record->updateWithChild(record_child, pass_left);
1622 RETURN_ON_STOP;
1623
1624 if (record->getValueWithPath<A>(pass_left, path).isFinal())
1625 {
1626 return;
1627 }
1628 }
1629
1630 /* 結果を吟味する
1631 */
1632 if (result_child.isCheckmateFail())
1633 {
1634 if (result_child == ProofDisproof::AttackBack())
1635 {
1636 ++disproof_by_inversion_count;
1637 }
1638 if (result_child == ProofDisproof::LoopDetection())
1639 {
1640 record->setLoopWithPath<A>(pass_left, path);
1641 TRY_DFPN;
1642 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1643 NtesukiMove::INVALID(), false);
1644 CATCH_DFPN;
1645 return;
1646 }
1647
1648 best_move->setCheckmateFail<A>(pass_left);
1649 TRY_DFPN;
1650 record->setResult<A>(pass_left, result_child,
1651 *best_move, false);
1652 CATCH_DFPN;
1653 return;
1654 }
1655 else if (result_child.isCheckmateSuccess())
1656 {
1657 best_move->setCheckmateSuccess<A>(pass_left);
1658 NtesukiRecord *best_record = table.findWithMove(record, *best_move);
1659 if ((ptt_invalid_defense && best_move->isPass()) ||
1660 (ptt_siblings_success && !best_move->isCheck())
1661 )
1662 {
1663 TRY_DFPN;
1664 simulateSiblingsSuccess<T>(record, best_record, pass_left,
1665 sibling_defense_success_count,
1666 sibling_defense_count);
1667 CATCH_DFPN;
1668 RETURN_ON_STOP;
1669 }
1670 }
1671 }//for(;;)
1672 }
1673
1674 template <Player T>
1675 osl::ntesuki::NtesukiMove* osl::ntesuki::NtesukiSearcher::
1676 selectMoveDefense(NtesukiRecord* record,
1677 unsigned int& best_disproof,
1678 unsigned int& sum_proof,
1679 unsigned int& second_disproof,
1680 unsigned int& best_proof,
1681 unsigned int& step_cost,
1682 NtesukiMoveList& moves,
1683 const int pass_left,
1684 const Move last_move)
1685 {
1686 const Player A = PlayerTraits<T>::opponent;
1687 const bool under_attack = state.inCheck(T);
1688
1689 bool read_interpose = record->readInterpose(pass_left);
1690 /* GCによって情報がかわっている可能性が
1691 * bool read_non_pass = record->isNtesuki<A>(pass_left);*/
1692
1693 bool read_non_pass = under_attack;
1694 if (pass_left > 0 && !under_attack)
1695 {
1696 NtesukiMove pass(Move::PASS(T));
1697 NtesukiRecord *record_pass = table.findWithMove(record, pass);
1698 if (record_pass)
1699 {
1700 const PathEncoding path_child(path, pass.getMove());
1701 read_non_pass =
1702 record_pass->getValueWithPath<A>(pass_left - 1,
1703 path_child).isCheckmateSuccess();
1704 }
1705 }
1706 if (under_attack) ntesuki_assert(read_non_pass);
1707
1708 bool read_check_defense = record->readCheckDefense(pass_left);
1709 if (isscheme == NtesukiRecord::normal_is)
1710 {
1711 read_check_defense = true;
1712 }
1713
1714 re_select_move_defense:
1715 unsigned short min_child_age = SHRT_MAX;
1716 NtesukiMove *best_move = NULL;
1717
1718 int average_cost = 0;
1719 int average_cost_count = 0;
1720
1721 /* reset values */
1722 best_disproof = ProofDisproof::BigProofNumber;
1723 sum_proof = 0;
1724 second_disproof = ProofDisproof::BigProofNumber;
1725 best_proof = 0;
1726
1727 /* dynamic Widening */
1728 std::list<top_pdp_t> pdps;
1729
1730 /* 手を選ぶ
1731 */
1732 for (NtesukiMoveList::iterator move_it = moves.begin();
1733 move_it != moves.end(); ++move_it)
1734 {
1735 NtesukiMove& move = *move_it;
1736 if (move.isCheckmateSuccess<A>(pass_left))
1737 {
1738 continue;
1739 }
1740 ntesuki_assert(!move.isCheckmateFail<A>(pass_left));
1741
1742 if (delay_non_pass &&
1743 !read_non_pass &&
1744 !move.isPass())
1745 {
1746 continue;
1747 }
1748
1749 if (delay_interpose &&
1750 (move.isInterpose() ||
1751 move.isLameLong()) &&
1752 !read_interpose)
1753 {
1754 continue;
1755 }
1756
1757 if (move.isCheck() &&
1758 !under_attack &&
1759 !read_check_defense)
1760 {
1761 continue;
1762 }
1763
1764 unsigned int proof = move.h_d_proof;
1765 unsigned int disproof = move.h_d_disproof;
1766
1767 average_cost += disproof;
1768 average_cost_count++;
1769
1770 NtesukiRecord *record_child = table.findWithMove(record, move);
1771 if (record_child)
1772 {
1773 int pass_left_child = pass_left;
1774 if (move.isPass()) --pass_left_child;
1775 const PathEncoding path_child(path, move.getMove());
1776 NtesukiResult result_child;
1777 TRY_DFPN;
1778 result_child =
1779 record_child->getValueOr<A>(pass_left_child, path_child,
1780 iwscheme);
1781 CATCH_DFPN;
1782
1783 if (record_child->isVisited())
1784 {
1785 /* ループ発見 */
1786 record->setLoopWithPath<A>(pass_left, path);
1787 ntesuki_assert(record->isLoopWithPath<A>(pass_left, path));
1788 TRY_DFPN;
1789 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1790 NtesukiMove::INVALID(), false);
1791 CATCH_DFPN;
1792 best_disproof = 0;
1793 return &move;
1794 }
1795
1796 proof = result_child.proof();
1797 disproof = result_child.disproof();
1798
1799 if (result_child.isCheckmateSuccess())//証明
1800 {
1801 ntesuki_assert(disproof >= ProofDisproof::DISPROOF_LIMIT);
1802 move.setCheckmateSuccess<A>(pass_left);
1803 if (move.isPass())
1804 {
1805 /* 既にパスの後が証明されていた */
1806 record->setNtesuki<A>(pass_left);
1807
1808 if (ptt_invalid_defense)
1809 {
1810 TRY_DFPN;
1811 simulateSiblingsSuccess<T>(record, record_child, pass_left,
1812 pass_success_count,
1813 pass_count);
1814 CATCH_DFPN;
1815 RETURN_ON_STOP(NULL);
1816 goto re_select_move_defense;
1817 }
1818 }
1819 if (ptt_siblings_success && !move.isCheck())
1820 {
1821 TRY_DFPN;
1822 simulateSiblingsSuccess<T>(record, record_child, pass_left,
1823 sibling_defense_success_count,
1824 sibling_defense_count);
1825 CATCH_DFPN;
1826 RETURN_ON_STOP(NULL);
1827 //re search as simulation is done.
1828 goto re_select_move_defense;
1829 }
1830 continue;
1831 }
1832 else if (result_child.isCheckmateFail())//反証
1833 {
1834 if (move.isCheck() && read_check_defense)
1835 {
1836 ++disproof_by_inversion_count;
1837 }
1838 if (result_child == ProofDisproof::LoopDetection())
1839 {
1840 record->setLoopWithPath<A>(pass_left, path);
1841 TRY_DFPN;
1842 record->setResult<A>(pass_left, NtesukiResult(1, 1),
1843 NtesukiMove::INVALID(), false);
1844 CATCH_DFPN;
1845 best_disproof = 0;
1846 return &move;
1847 }
1848
1849 ntesuki_assert(proof >= ProofDisproof::PROOF_LIMIT);
1850 move.setCheckmateFail<A>(pass_left);
1851 TRY_DFPN;
1852 record->setResult<A>(pass_left, result_child,
1853 move, false);
1854 CATCH_DFPN;
1855 best_disproof = 0;
1856 return &move;
1857 }
1858
1859 min_child_age = std::min(min_child_age,
1860 record_child->distance);
1861 if ((record_child->distance <= record->distance) &&
1862 !move.isPass())
1863 {
1864 if (!record->useOld<A>(pass_left))
1865 {
1866 continue;
1867 }
1868 }
1869 }/* has record */
1870
1871 /* Proof Disproof の調整はここでする */
1872 if (record->useOld<A>(pass_left))
1873 {
1874 sum_proof = std::max(proof, sum_proof);
1875 }
1876 else if (NtesukiRecord::max_for_split && record->is_split)
1877 {
1878 sum_proof = std::max(proof, sum_proof);
1879 }
1880 else
1881 {
1882 sum_proof = addWithSaturation(ProofDisproof::PROOF_LIMIT,
1883 proof, sum_proof);
1884 }
1885
1886 if (disproof < best_disproof)
1887 {
1888 best_move = &move;
1889 second_disproof = best_disproof;
1890 best_disproof = disproof;
1891 best_proof = proof;
1892 }
1893 else if (disproof < second_disproof)
1894 {
1895 second_disproof = disproof;
1896 }
1897
1898 /* dynamic widening: 良い手の選定 */
1899 if (dynamic_widening_width > 0)
1900 {
1901 if (pdps.size() < dynamic_widening_width)
1902 {
1903 pdps.push_back(top_pdp_t(disproof, proof));
1904 pdps.sort(sorter);
1905 }
1906 else
1907 {
1908 if (pdps.back().first > disproof)
1909 {
1910 pdps.pop_back();
1911 pdps.push_back(top_pdp_t(disproof, proof));
1912 }// back().disproof == disproof だった場合は？
1913 pdps.sort(sorter);
1914 }
1915 }
1916 }/* foreach move */
1917
1918 /* dynamic widening: 良い手の集計 */
1919 if (dynamic_widening_width > 0 &&
1920 dynamic_widening_width < moves.size())
1921 {
1922 sum_proof = 0;
1923 for (std::list<top_pdp_t>::const_iterator it = pdps.begin();
1924 it != pdps.end(); ++it)
1925 {
1926 sum_proof += it->second;
1927 }
1928 }
1929
1930 /* 選んだ手を吟味する
1931 */
1932 if (NULL == best_move)
1933 {
1934 ntesuki_assert(sum_proof == 0);
1935 /* パスだけ先に読む enhancement */
1936 if (delay_non_pass &&
1937 read_non_pass == false)
1938 {
1939 ntesuki_assert(!under_attack);
1940
1941 read_non_pass = true;
1942 record->setUseOld<A>(pass_left, false);
1943 record->setNtesuki<A>(pass_left);
1944
1945 if (ptt_invalid_defense)
1946 {
1947 NtesukiMove move_pass = moves.front();
1948 ntesuki_assert(move_pass.isPass());
1949 NtesukiRecord *record_pass = table.findWithMove(record, move_pass);
1950 const PathEncoding path_child(path, move_pass.getMove());
1951
1952 ntesuki_assert(record_pass->getValueWithPath<A>(pass_left - 1,
1953 path_child).isCheckmateSuccess());
1954 TRY_DFPN;
1955 simulateSiblingsSuccess<T>(record, record_pass, pass_left,
1956 pass_success_count,
1957 pass_count);
1958 CATCH_DFPN;
1959 RETURN_ON_STOP(NULL);
1960 }
1961 goto re_select_move_defense;
1962 } /* delay non pass */
1963
1964 if (!record->useOld<A>(pass_left))
1965 {
1966 if (SHRT_MAX != min_child_age)
1967 {
1968 record->setUseOld<A>(pass_left, true);
1969
1970 ntesuki_assert(min_child_age <= record->distance);
1971 record->distance = min_child_age;
1972
1973 goto re_select_move_defense;
1974 }
1975 }
1976
1977 if (delay_interpose &&
1978 read_interpose == false)
1979 {
1980 read_interpose = true;
1981 record->setUseOld<A>(pass_left, false);
1982 record->setReadInterpose(pass_left);
1983 TRY_DFPN;
1984 handleInterpose<T>(record, pass_left);
1985 CATCH_DFPN;
1986 RETURN_ON_STOP NULL;
1987
1988 goto re_select_move_defense;
1989 }
1990
1991 /* 逆王手の扱い */
1992 switch(isscheme)
1993 {
1994 case NtesukiRecord::no_is:
1995 ntesuki_assert(read_check_defense == false);
1996 break;
1997 case NtesukiRecord::tonshi_is:
1998 handleTonshi<T>(record, pass_left, last_move);
1999 RETURN_ON_STOP NULL;
2000 break;
2001 case NtesukiRecord::delay_is:
2002 if (read_check_defense == false)
2003 {
2004 ++proof_without_inversion_count;
2005 read_check_defense = true;
2006 record->setReadCheckDefense(pass_left);
2007 goto re_select_move_defense;
2008 }
2009 break;
2010 case NtesukiRecord::normal_is:
2011 ntesuki_assert(read_check_defense == true);
2012 break;
2013 }
2014
2015 /* 全ての手が普通に証明された */
2016 TRY_DFPN;
2017 record->setResult<A>(pass_left, ProofDisproof::Checkmate(),
2018 NtesukiMove::INVALID(), false);
2019 CATCH_DFPN;
2020 return NULL;
2021 }
2022 ntesuki_assert(best_move);
2023 ntesuki_assert(sum_proof != 0);
2024 ntesuki_assert(best_disproof != 0);
2025
2026 if (record->useOld<A>(pass_left))
2027 {
2028 ntesuki_assert(min_child_age != SHRT_MAX);
2029 record->distance = min_child_age;
2030 }
2031 average_cost /= average_cost_count;
2032 step_cost = std::max(average_cost, 1);
2033 return best_move;
2034 }
2035
2036 template <Player T>
2037 void osl::ntesuki::NtesukiSearcher::
2038 handleTonshi(NtesukiRecord *record,
2039 int pass_left,
2040 const Move last_move)
2041 {
2042 const Player A = PlayerTraits<T>::opponent;
2043 const Player D = T;
2044
2045 if (pass_left > 0)
2046 {
2047 NtesukiResult result_defender =
2048 record->getValueWithPath<D>(pass_left - 1, path);
2049 if (!result_defender.isFinal())
2050 {
2051 /* to make sure not to come back here
2052 */
2053 record->setResult<A>(pass_left, ProofDisproof::Bottom(),
2054 NtesukiMove::INVALID(), false);
2055 const unsigned int read_node_limit_orig = read_node_limit;
2056 int ratio = 1;
2057
2058 if ((record->distance / 2) == 0)
2059 ratio = 8;
2060 else if ((record->distance / 2) == 1)
2061 ratio = 2;
2062
2063 read_node_limit = node_count + READ_ATTACK_BACK_LIMIT * ratio;
2064
2065 NtesukiRecord::UnVisitLock unVisitLock(record);
2066 TRY_DFPN;
2067 result_defender = attack<T>(record, NULL, NULL,
2068 INITIAL_PROOF_LIMIT, INITIAL_PROOF_LIMIT,
2069 pass_left - 1, last_move);
2070 CATCH_DFPN;
2071
2072 if (result_defender.isCheckmateSuccess())
2073 {
2074 ++attack_back_count;
2075 }
2076
2077 read_node_limit = read_node_limit_orig;
2078 RETURN_ON_STOP;
2079 }
2080
2081 if (result_defender.isFinal())
2082 {
2083 return;
2084 }
2085 }
2086 }
2087
2088 template <Player T>
2089 void osl::ntesuki::NtesukiSearcher::
2090 simulateSiblingsSuccess(NtesukiRecord *record,
2091 NtesukiRecord *record_best,
2092 int pass_left,
2093 unsigned int& success_count,
2094 unsigned int& total_count)
2095 {
2096 LockGC glock(table);
2097
2098 const Player A = PlayerTraits<T>::opponent;
2099 if (!record_best) return;
2100 ntesuki_assert(record_best);
2101 ntesuki_assert(record_best->getValue<A>(pass_left).isCheckmateSuccess());
2102
2103 NtesukiMoveList moves;
2104 mg->generate<T>(state, moves);
2105
2106 for (NtesukiMoveList::iterator move_it = moves.begin();
2107 move_it != moves.end(); ++move_it)
2108 {
2109 NtesukiMove& move = *move_it;
2110 NtesukiRecord *record_child = table.allocateWithMove(record,
2111 move);
2112 if (record_child == 0)
2113 {
2114 *stop_flag = TableLimitReached;
2115 return;
2116 }
2117 ntesuki_assert(record_child);
2118 if (record_child == record_best) continue;
2119 if (record_child->isVisited()) continue;
2120
2121 ntesuki_assert(record_child);
2122 const PathEncoding path_child(path, move.getMove());
2123 const NtesukiResult result_child = record_child->getValueWithPath<A>(pass_left,
2124 path_child);
2125 if (result_child.isFinal())
2126 {
2127 continue;
2128 }
2129
2130 bool simulation_result;
2131 total_count++;
2132 CallSimulationDefense<NtesukiSimulationSearcher, T>
2133 helper(simulator, table, record_child, record_best,
2134 pass_left, simulation_result, move.getMove());
2135 TRY_DFPN;
2136 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move.getMove(), helper);
2137 CATCH_DFPN;
2138 RETURN_ON_STOP;
2139
2140 if (simulation_result)
2141 {
2142 success_count++;
2143 ntesuki_assert(record_child->getValueWithPath<A>(pass_left,
2144 path_child).isCheckmateSuccess());
2145 move.setBySimulation();
2146 move.setCheckmateSuccess<A>(pass_left);
2147 }
2148 }
2149 }
2150
2151 template <Player T>
2152 void osl::ntesuki::NtesukiSearcher::
2153 handleInterpose(NtesukiRecord* record,
2154 int pass_left)
2155 {
2156 const Player A = PlayerTraits<T>::opponent;
2157 ntesuki_assert(T == state.turn());
2158
2159 NtesukiMoveList moves;
2160 mg->generate<T>(state, moves);
2161
2162 for (NtesukiMoveList::iterator move_it = moves.begin();
2163 move_it != moves.end(); ++move_it)
2164 {
2165 if (move_it->isInterpose() &&
2166 !move_it->isCheckmateSuccess<A>(pass_left))
2167 {
2168 NtesukiRecord *record_child = table.allocateWithMove(record,
2169 *move_it);
2170 if (record_child == 0)
2171 {
2172 *stop_flag = TableLimitReached;
2173 return;
2174 }
2175 ntesuki_assert(record_child);
2176
2177 const PathEncoding path_child(path, move_it->getMove());
2178 if(record_child->getValueWithPath<A>(pass_left,
2179 path_child).isFinal())
2180 {
2181 continue;
2182 }
2183 ntesuki_assert(record_child->getBestMove<A>(pass_left).isInvalid());
2184
2185 NtesukiMoveList::iterator best_it = moves.begin();
2186 for (; best_it != moves.end(); ++best_it)
2187 {
2188 if (best_it->to() == move_it->to() &&
2189 best_it->isCheckmateSuccess<A>(pass_left)) break;
2190 }
2191 if (best_it == moves.end())
2192 {
2193 continue;
2194 }
2195 const NtesukiRecord* record_best = table.findWithMove(record, *best_it);
2196 ntesuki_assert(record_best);
2197
2198 bool simulation_result;
2199 CallSimulationDefense<NtesukiSimulationSearcher, T>
2200 helper(simulator, table, record_child, record_best,
2201 pass_left, simulation_result, move_it->getMove());
2202 TRY_DFPN;
2203 ApplyMoveWithPath<T>::doUndoMoveOrPass(state, path, move_it->getMove(), helper);
2204 CATCH_DFPN;
2205 RETURN_ON_STOP;
2206
2207 if (simulation_result)
2208 {
2209 move_it->setBySimulation();
2210 move_it->setCheckmateSuccess<A>(pass_left);
2211 }
2212 else if (record_child->getValue<A>(pass_left).isCheckmateFail())
2213 {
2214 break;
2215 }
2216 }
2217 }
2218 }
2219
2220 /* 外から呼ばれる関数.
2221 */
2222 template <Player A>
2223 int osl::ntesuki::NtesukiSearcher::
2224 search()
2225 {
2226 NtesukiRecord::pass_count = 0;
2227 const Player D = PlayerTraits<A>::opponent;
2228 //const HashKey key = HashKey::calcHash(state);
2229 const HashKey key(state);
2230
2231 NtesukiRecord *record = table.allocateRoot(key, PieceStand(WHITE, state),
2232 0, &state);
2233 ntesuki_assert(record);
2234
2235 NtesukiResult result;
2236 if (A == state.turn())
2237 {
2238 const Player T = A;
2239 result = attack<T>(record, NULL, NULL,
2240 INITIAL_PROOF_LIMIT,
2241 INITIAL_DISPROOF_LIMIT,
2242 max_pass - 1, Move::INVALID());
2243 }
2244 else//A != turn
2245 {
2246 const Player T = D;
2247 if (0 == (max_pass - 1) &&
2248 !state.inCheck(D))
2249 {
2250 if (verbose) std::cerr << "No Check" << std::endl;
2251 return NtesukiNotFound;
2252 }
2253 else
2254 {
2255 result = defense<T>(record, NULL, NULL,
2256 INITIAL_PROOF_LIMIT,
2257 INITIAL_DISPROOF_LIMIT,
2258 max_pass - 1,
2259 Move::INVALID());
2260 }
2261 }
2262
2263 if (node_count > read_node_limit || *stop_flag)
2264 {
2265 if (verbose) std::cerr << "Limit Reached\t" << result << std::endl;
2266 return ReadLimitReached;
2267 }
2268 else
2269 {
2270 if (verbose) std::cerr << "result:\t" << result << std::endl;
2271 if (result.isCheckmateSuccess())
2272 {
2273 for (unsigned int i = 0; i < max_pass; ++i)
2274 {
2275 if (record->getValue<A>(i).isCheckmateSuccess())
2276 {
2277 return i;
2278 }
2279 }
2280 }
2281 }
2282
2283 ntesuki_assert(result.isCheckmateFail());
2284 return NtesukiNotFound;
2285 }
2286 // ;;; Local Variables:
2287 // ;;; mode:c++
2288 // ;;; c-basic-offset:2
2289 // ;;; End: