All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ntesukiSearcher.tcc
Go to the documentation of this file.
1 /* ntesukiSearcher.tcc
2  */
7 #include "osl/apply_move/applyMoveWithPath.h"
9 // for release, inline the following templates as well
10 #ifdef NDEBUG
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 
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 
76  {
77  mlist.pop_back();
78  }
79 };
80 
81 struct LockGC
82 {
85  : table(t)
86  {
87  table.lockGC();
88  }
89 
91  {
92  table.unlockGC();
93  }
94 };
95 
96 /* ===================
97  * Attack helper
98  */
99 template <class Search,Player T>
100 class
102 AttackHelper
103 {
104  unsigned int proof_limit, disproof_limit, pass_left;
105  Search* searcher;
111 public:
112  AttackHelper(Search* searcher,
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
143 CallSimulationAttack
144 {
145  Search &simulator;
149  unsigned int pass_left;
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
185 {
186  unsigned int proof_limit, disproof_limit, pass_left;
187  Search* searcher;
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 
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
226 CallSimulationDefense
227 {
228  Search &simulator;
232  unsigned int pass_left;
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
266 CallSimulationDefenseDisproof
267 {
268  Search &simulator;
272  unsigned int pass_left;
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>
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 
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++;
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;
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  */
367 {
368 public:
370  const NtesukiTable& t)
371  : record(r), table(t)
372  {
373  size_start = table.size();
374  }
375 
377  {
378  record->addChildCount(table.size() - size_start);
379  }
380 private:
383  unsigned int size_start;
384 };
385 
386 
387 /* ===================
388  * Attack
389  */
390 template <Player T>
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
402 #endif
403 
404  ntesuki_assert(T == state.turn());
405  ntesuki_assert(!state.inCheck(D));
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,
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>
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
537 #endif
538  ntesuki_assert(T == state.turn());
539  ntesuki_assert(!state.inCheck(D));
542 
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 
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 =
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 
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);
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>
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,
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) ==
915  {
916  move.setPawnDropCheckmate();
917  pawn_checkmate = true;
918  }
919 
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  /* 間違いなく勝ち手 */
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  {
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 
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);
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>
1134  int pass_left)
1135 {
1136  const Player A = T;
1137  ntesuki_assert(T == state.turn());
1138 
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;
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;
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>
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 {
1223  const Player D = T;
1224  CountChildLock cclock(record, table);
1225 
1226  ntesuki_assert(T == state.turn());
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>
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 {
1330  const bool under_attack = state.inCheck(T);
1331 
1332  /* 受け手の生成
1333  */
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;
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;
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);
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 =
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  }
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);
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;
1669  }
1670  }
1671  }//for(;;)
1672 }
1673 
1674 template <Player T>
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,
1683  const int pass_left,
1684  const Move last_move)
1685 {
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  {
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 
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  {
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>
2039  int pass_left,
2040  const Move last_move)
2041 {
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;
2079  }
2080 
2081  if (result_defender.isFinal())
2082  {
2083  return;
2084  }
2085  }
2086 }
2087 
2088 template <Player T>
2091  NtesukiRecord *record_best,
2092  int pass_left,
2093  unsigned int& success_count,
2094  unsigned int& total_count)
2095 {
2096  LockGC glock(table);
2097 
2099  if (!record_best) return;
2100  ntesuki_assert(record_best);
2101  ntesuki_assert(record_best->getValue<A>(pass_left).isCheckmateSuccess());
2102 
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++;
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;
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>
2154  int pass_left)
2155 {
2157  ntesuki_assert(T == state.turn());
2158 
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;
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;
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>
2225 {
2226  NtesukiRecord::pass_count = 0;
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 
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: