読者です 読者をやめる 読者になる 読者になる

daruma3940の日記

理解や文章に間違い等あればどんなことでもご指摘お願いします

HandとHandkindの変換なのじぇ

f:id:daruma3940:20160520223745p:plain
ゆっくりするのじぇ
今回はやねうら王のHandとHandkindの変換なのじぇ

'' Hand 歩の枚数を8bit、香、桂、銀、角、飛、金を4bitずつで持つ 32bit整数 '' f:id:daruma3940:20160520223745p:plain
つまりどの駒を何枚持っているかという情報をbit単位で格納しているいわゆる普通の持ち駒表現なのじぇ

'' HandKind 特定種の手駒を持っているかどうかをbitで表現する32bit整数 bit0..歩を持っているか , bit1..香 , bit2..桂 , bit3..銀 , bit4..角 , bit5..飛 , bit6..金 , bit7..玉(フラグとして用いるため) '' f:id:daruma3940:20160520223745p:plain
つまりある種の駒を1枚でも持っているかのフラグなのじぇ

f:id:daruma3940:20160520223745p:plain
そしてHandからHandKindを用意する部分はこの関数なのじぇ

// Hand型からHandKind型への変換子
// 例えば歩の枚数であれば5bitで表現できるが、011111bを加算すると1枚でもあれば桁あふれしてbit5が1になる。
// これをPEXT32で回収するという戦略。
inline HandKind toHandKind(Hand h) {return (HandKind)PEXT32(h + HAND_BIT_MASK, HAND_BORROW_MASK);}

f:id:daruma3940:20160520223530p:plain
う~んなんだかわかりにくいね...

f:id:daruma3940:20160520223745p:plain
今から解説していくのじぇ
f:id:daruma3940:20160520223745p:plain
HAND_BIT_MASKは駒の枚数を格納するためのbitが1となっている32bit整数で次のようになっているのじぇ。

int32_t  HAND_BIT_MASK = PIECE_BIT_MASK2[PAWN] | PIECE_BIT_MASK2[LANCE] | PIECE_BIT_MASK2[KNIGHT] | PIECE_BIT_MASK2[SILVER]
          | PIECE_BIT_MASK2[BISHOP] | PIECE_BIT_MASK2[ROOK] | PIECE_BIT_MASK2[GOLD];

PIECE_BIT_MASK2は以下のようになっているのじぇ

 PIECE_BIT_MASK2[PIECE_HAND_NB] = { 0,
  PIECE_BIT_MASK[PAWN]   << PIECE_BITS[PAWN]  , PIECE_BIT_MASK[LANCE]  << PIECE_BITS[LANCE] , PIECE_BIT_MASK[KNIGHT] << PIECE_BITS[KNIGHT],
  PIECE_BIT_MASK[SILVER] << PIECE_BITS[SILVER], PIECE_BIT_MASK[BISHOP] << PIECE_BITS[BISHOP], PIECE_BIT_MASK[ROOK]   << PIECE_BITS[ROOK]  ,
  PIECE_BIT_MASK[GOLD]   << PIECE_BITS[GOLD] };

ここで

PIECE_BIT_MASK
その持ち駒を表現するのに必要なbit数のmask(例えば3bitなら2の3乗-1で7)
int PIECE_BIT_MASK[PIECE_HAND_NB] = { 0,31/歩は5bit/, 7/香は3bit/, 7//, 7//, 3//, 3//, 7// };

PIECE_BITS
手駒のbit位置  0~8歩 9~12香 13~16桂 17~20銀 21~24角 24~28飛 29~32金
int PIECE_BITS[PIECE_HAND_NB] = { 0, 0 //, 8 //, 12 //, 16 //, 20 //, 24 // , 28 // };

つまり
PIECE_BIT_MASK2は
{0,31<<0,7<<8,7<<12,7<<16,7<<20,7<<24,7<<28}
つまり
{0,
11111,
11100000000,
111000000000000
111000000000000000
1100000000000000000000
....
}
のようになっていて

32ビット中1が立っている場所を1,立っていない場所を*とすると

**************************11111 歩  
********************111********香車  
****************111************桂馬  
************111****************銀  
*********11********************角  
*****11************************飛車  
111****************************金  
1110011001101110111011100011111  

よって
HAND_BIT_MASKは
1110011001101110111011100011111のようになっているのじぇ

f:id:daruma3940:20160520223745p:plain
また
HAND_BORROW_MASK は
(HAND_BIT_MASK << 1) & ~HAND_BIT_MASKであり

1100110011011101110111000111111 &
0001100110010001000100011100000 =
0000100010010001000100000100000

となるのじぇ。

f:id:daruma3940:20160520223745p:plain
もう一度HandからHandkindへの変換のコードをここに書くと

// Hand型からHandKind型への変換子  
// 例えば歩の枚数であれば5bitで表現できるが、011111bを加算すると1枚でもあれば桁あふれしてbit5が1になる。  
// これをPEXT32で回収するという戦略。  
inline HandKind toHandKind(Hand h) {return (HandKind)PEXT32(h + HAND_BIT_MASK, HAND_BORROW_MASK);}  

これをわかりやすくすると

 HandKind toHandKind(Hand h) {return (HandKind)PEXT32(h +1110011001101110111011100011111 ,0000100010010001000100000100000 );}

hは 歩の枚数を8bit、香、桂、銀、角、飛、金を4bitずつで持つ 32bit整数であり,

hに1110011001101110111011100011111を足すと
とある駒種の駒を一枚でも持って居ればその部分の2進数における桁が一つ上がる
それをpextでかき集めHandkind型として駒を持っているかどうかのフラグとしてかき集めるということであるのじぇ!
f:id:daruma3940:20160521003616p:plain
賢い 

f:id:daruma3940:20160520223530p:plain
すごいよ!

f:id:daruma3940:20160520223745p:plain
ここまで考えられたbit演算はなかなかお目にかかれないと思うのじぇ
すごい世界なのじぇ

f:id:daruma3940:20160520223745p:plain
ちなみにここで用いたテクニックは2歩判定のためにこの筋は歩のある筋かどうかという情報を9段目に集めるテクニックにも用いられているのじぇ!

そのテクニックはこんな感じなのじぇ

 // RANK9のところに歩の情報がかき集められる。
Bitboard a = pos.pieces(US, PAWN) + rank1_n_bb(BLACK, RANK_8); 
//rank1_n_bb(BLACK, RANK_8) 1~8段目がすべて1で埋まっているbitboard

もしある筋の5段目に歩があったとするのじぇそうするとこの計算は

000010000+
011111111=
100001111

もし歩がこの筋になければ

000000000+
011111111=
011111111 

歩が9段目にあれば

100000000+
011111111=
111111111

となり、最上位ビットにここに歩があるという情報が出てくるのじぇ!
これによって求められた歩の情報から
29通りの歩のある筋の通りの数を求め、そこから前回説明したテーブルを引いてどこが歩を打てる筋なのかということを判断するのじぇ!

  // これにより、RANK9のところに歩の情報がかき集められた。
  Bitboard a = pos.pieces(US, PAWN) + rank1_n_bb(BLACK, RANK_8); // 1~8段目を意味するbitboard

 // このRANK9に集まった情報をpextで回収。後者はPEXT32でもいいがlatencyたぶん変わらないので…。
  uint32_t index = uint32_t(PEXT64(a.p[0], RANK9_BB.p[0]) + (PEXT64(a.p[1],RANK9_BB.p[1]) << 7));

 // 歩の打てる場所
 Bitboard target2 = PAWN_DROP_MASK_BB[index][US] & target;