Bonanzaの置換表はどうなっているのか? 第1回

盤面が同じでhandが違う、という場合は書くべきかどうか微妙な気はしますが、そうしたいならばif条件は&&ではなくて||ですよね。
(中略)
まあここを変えてもそうドラスティックに強さ変わるわけではないとは思いますが、「盤面異なるが持ち駒は同じ」局面群に対してはpreferしか使わないことになるので、そこそこ発生するような気はします。


バグ?(A級リーグ指し手1号)
http://aleag.cocolog-nifty.com/blog/2010/02/post-5656.html

そうですね。私もその部分はバグかな?と思います。


該当部分、私がコメントを追記したソースを以下に掲載します。解析の初期段階に書いたコメントなので間違っているところがあるかも知れませんが、まあ何かの参考にはなるかと。

word1
   name    bits  shifts
   depth     8     56    ← 探索深さ(hash_storeの引数depth)
   value    16     40    ← 評価値(hash_storeの引数value) を +s16.Max だけ下駄履きしたもの
   // valueはマイナスの値をとるのでプラスの下駄履きにしたのか?
   // (u16)に castしてからシフトすればいいように思うが。
   move     19     21    ← 指し手を19bitにshrinkしたもの
        moveの上位には捕獲する駒が格納されているがこれは指し手を表現するのに必須ではないのでマスクしてしまう。
   hand     21      0    ← 手駒21bit
*/

  // HASH_KEYは64bitだが、その下位32bit。
  u32 index = (u32)HASH_KEY & hash_mask;
  // 現在の局面からhashのentryが確定するので、それを取得。

  u64 hash_word1 = ptrans_table[index].prefer.word1;
  u64 hash_word2 = ptrans_table[index].prefer.word2;

  SignKey( hash_word2, hash_word1 );

  // このentryの世代を調べる。
  Ply age_hash   = (Ply)((u32)(hash_word2    ) & 0x07U);
  Ply depth_hash = (Ply)((u32)(hash_word1>>56) & 0xffU);

  // 以前の世代のhashであったり、あるいはdepthがいまの探索深さより浅ければ
  // いま置換表にあるのは意味のないentryなので書き換えてしまう。
  if ( age_hash != trans_table_age || depth_hash <= depth )
  {
    // メインスロットはpreferで、サブスロットはalways[2]という構成になっている。
    // ハッシュ値が衝突した場合は、メインスロットには、なるべく意味のある局面を格納しておきたい。

      Hand hand_hash = (u32)hash_word1 & 0x1fffffU; // 下位21bitは手駒を得るmask
      u64 keyt_hash = hash_word2 & ~(u64)0x2fU;    // 局面のhash値を取得

      // 別局面のhash値が登録されているが、
      // 意味があるのでこの内容は予備スロットに移動させる。
      // ここの'&&'は'||'ではないのか?
      if ( hand_hash != HAND_B
           && keyt_hash != ( word2 & ~(u64)0x2fU ) )
      {
          // ここ、現在alwaysに格納されている内容より本当にハッシュとして優れた意味を持つのかを
          // もう少し統計学的に判定したほうが良いと思う。

          u32 slot = (u32)hash_word2 >> 31;
          // 31bit目を1bit拾う。

          // Craftyの方法。これなら別threadからの破壊をチェックできる
          SignKey( hash_word2, hash_word1 );

          // alwaysは予備スロット。ここに登録しておく。
          ptrans_table[index].always[slot].word1 = hash_word1;
          ptrans_table[index].always[slot].word2 = hash_word2;
      }

      // Craftyの方法。これなら別threadからの破壊をチェックできる
      SignKey( word2, word1 );

      // メインのスロットに登録する。
      ptrans_table[index].prefer.word1 = word1;
      ptrans_table[index].prefer.word2 = word2;
  }
  else
  {
    // すでに登録されていたので予備スロット(always)に登録
  
    u32 slot = (u32)HASH_KEY >> 31; // 31bit目を1bit拾う。

    // Craftyの方法。これなら別threadからの破壊をチェックできる
    SignKey( word2, word1 );

    // 予備スロットへの登録
    ptrans_table[index].always[slot].word1 = word1;
    ptrans_table[index].always[slot].word2 = word2;
  }
}