Bonanzaの駒の価値はどこに書かれているのか その2

p_valueに関しては、そこから派生する2つの配列があります。p_value_exとp_value_pmです。

p_value_exは、p_valueの値を2倍したものです。歩を相手に取られたときには、p_value_ex[pawn]分だけ駒割のスコアが変動します。「自分の駒を失った損」+「相手がその駒を獲得した得」で、駒の価値の2倍のスコアが変動するからです。この値は「交換値」と呼ぶこともあるようです。


set_derivative_param(ini.c)で次のように代入してあります。

  p_value_ex[15+pawn]       = p_value[15+pawn]       + p_value[15+pawn];
  p_value_ex[15+lance]      = p_value[15+lance]      + p_value[15+lance];
  p_value_ex[15+knight]     = p_value[15+knight]     + p_value[15+knight];
  p_value_ex[15+silver]     = p_value[15+silver]     + p_value[15+silver];
  p_value_ex[15+gold]       = p_value[15+gold]       + p_value[15+gold];
  p_value_ex[15+bishop]     = p_value[15+bishop]     + p_value[15+bishop];
  p_value_ex[15+rook]       = p_value[15+rook]       + p_value[15+rook];
  p_value_ex[15+king]       = p_value[15+king]       + p_value[15+king];
  p_value_ex[15+pro_pawn]   = p_value[15+pro_pawn]   + p_value[15+pawn];
  p_value_ex[15+pro_lance]  = p_value[15+pro_lance]  + p_value[15+lance];
  p_value_ex[15+pro_knight] = p_value[15+pro_knight] + p_value[15+knight];
  p_value_ex[15+pro_silver] = p_value[15+pro_silver] + p_value[15+silver];
  p_value_ex[15+horse]      = p_value[15+horse]      + p_value[15+bishop];
  p_value_ex[15+dragon]     = p_value[15+dragon]     + p_value[15+rook];

  p_value_ex[15-pawn]       = p_value_ex[15+pawn];
  p_value_ex[15-lance]      = p_value_ex[15+lance];
  p_value_ex[15-knight]     = p_value_ex[15+knight];
  p_value_ex[15-silver]     = p_value_ex[15+silver];
  p_value_ex[15-gold]       = p_value_ex[15+gold];
  p_value_ex[15-bishop]     = p_value_ex[15+bishop];
  p_value_ex[15-rook]       = p_value_ex[15+rook];
  p_value_ex[15-king]       = p_value_ex[15+king];
  p_value_ex[15-pro_pawn]   = p_value_ex[15+pro_pawn];
  p_value_ex[15-pro_lance]  = p_value_ex[15+pro_lance];
  p_value_ex[15-pro_knight] = p_value_ex[15+pro_knight];
  p_value_ex[15-pro_silver] = p_value_ex[15+pro_silver];
  p_value_ex[15-horse]      = p_value_ex[15+horse];
  p_value_ex[15-dragon]     = p_value_ex[15+dragon];

また、MT_CAP_XXXXというマクロが次のようにshogi.hで定義されています。MTはおそらくMaterial(駒の価値)の意味で、CAPはcapture(捕獲)の意味です。「駒を捕獲したときのスコアがいくら変動するか」という意味がこめられています。

#if defined(MINIMUM)

#  define MT_CAP_PAWN       ( DPawn      + DPawn )
#  define MT_CAP_LANCE      ( DLance     + DLance )
#  define MT_CAP_KNIGHT     ( DKnight    + DKnight )
#  define MT_CAP_SILVER     ( DSilver    + DSilver )
#  define MT_CAP_GOLD       ( DGold      + DGold )
#  define MT_CAP_BISHOP     ( DBishop    + DBishop )
#  define MT_CAP_ROOK       ( DRook      + DRook )
#  define MT_CAP_PRO_PAWN   ( DProPawn   + DPawn )
#  define MT_CAP_PRO_LANCE  ( DProLance  + DLance )
#  define MT_CAP_PRO_KNIGHT ( DProKnight + DKnight )
#  define MT_CAP_PRO_SILVER ( DProSilver + DSilver )
#  define MT_CAP_HORSE      ( DHorse     + DBishop )
#  define MT_CAP_DRAGON     ( DDragon    + DRook )
#  define MT_CAP_KING       ( DKing      + DKing )

#else

#  define MT_CAP_PAWN       ( p_value_ex[ 15 + pawn ] )
#  define MT_CAP_LANCE      ( p_value_ex[ 15 + lance ] )
#  define MT_CAP_KNIGHT     ( p_value_ex[ 15 + knight ] )
#  define MT_CAP_SILVER     ( p_value_ex[ 15 + silver ] )
#  define MT_CAP_GOLD       ( p_value_ex[ 15 + gold ] )
#  define MT_CAP_BISHOP     ( p_value_ex[ 15 + bishop ] )
#  define MT_CAP_ROOK       ( p_value_ex[ 15 + rook ] )
#  define MT_CAP_PRO_PAWN   ( p_value_ex[ 15 + pro_pawn ] )
#  define MT_CAP_PRO_LANCE  ( p_value_ex[ 15 + pro_lance ] )
#  define MT_CAP_PRO_KNIGHT ( p_value_ex[ 15 + pro_knight ] )
#  define MT_CAP_PRO_SILVER ( p_value_ex[ 15 + pro_silver ] )
#  define MT_CAP_HORSE      ( p_value_ex[ 15 + horse ] )
#  define MT_CAP_DRAGON     ( p_value_ex[ 15 + dragon ] )
#  define MT_CAP_KING       ( DKing + DKing )

上の引用部分に出てくるMINIMUMというシンボルについては解説を要するでしょう。
普通のリリース用の実行ファイルを作るときにはこのシンボルを定義しておくことになっています。そうすると、上のようにこのマクロを使った場合、コード上に定数が埋め込まれることになり、少々高速化します。


では、いつMINIMUMというシンボルを定義しないのかというと、それは棋譜からの学習時です。(以下、単に「学習」と記する。) 学習のときには駒の価値は徐々に変動しますから、定数であってはまずいわけです。常に配列の中身を参照するコードになっていなければなりません。


また逆に、MINIMUMというシンボルが定義されているなら、p_valueやp_value_exのような配列を用意しなくともいいんじゃないかと言われるかも知れませんが、そうではありません。


敵の駒を捕獲したとき、敵の駒の番号(歩なら1,香なら2,…)はすぐにわかりますが、その駒の番号によってswitch〜caseで場合分けして交換値をスコアに足し算するよりは、p_value_exのような配列が用意されていたほうが、処理は単純化できます。


ゆえに、
・駒の価値を表現する定数と配列
・駒の交換値を表現する定数と配列
のように、定数と配列と二つ必要で、かつ、学習時には定数ではなく配列のほうを使わなければならないという制約があるので、このようなdefineによるマクロが使われているわけです。


こういう処理をdefineによるマクロを使わずに書こうと思うと案外大変で、高速化のためにはdefineのような文字列置換型の処理は欠かせないと私は思います。世間ではdefineマクロを前時代の遺物のように言われることもありますが、最先端のプログラム(Bonanza)にdefineマクロが多用してあるという事実を我々は直視すべきではないでしょうか。


つづく