daruma3940の日記

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

KPPの差分計算なのじぇ

f:id:daruma3940:20160520223745p:plain
ゆっくりするのじぇ

f:id:daruma3940:20160520223745p:plain
今日はやねうら王の評価値の差分計算部分のコードを見ていくのじぇ。

差分計算部分は駒得の差分計算と3駒関係の差分計算の2つにわけることができるのじぇ
ここでは3駒関係の差分計算についてのみ見ていくのじぇ。

f:id:daruma3940:20160521003616p:plain
駒得の差分計算は駒をとられた、取った、駒が成ったに応じて評価値を変化させればいいだけなので
簡単そうだけれど
3駒関係の差分計算っていうのはなかなか一筋縄ではいかなさそうね...

f:id:daruma3940:20160520223530p:plain
難しそうだよ...
f:id:daruma3940:20160520223745p:plain
ゆゆっ!
まいちゃも最初はそう思っていて
差分計算の部分を読まず嫌いさんだったのじぇ
でも読んでみるとそんなに難しいことはしていないのじぇ。

f:id:daruma3940:20160520223745p:plain
ここで用語についての注意なのじぇ

やねうら王に倣って駒のいる場所とその駒の種類を合わせてひとまとめにした数値(ex. 1一の香車の数値は239)をBonapieceと呼ばせてもらい、
駒を管理するための番号(ex. 一枚目の香車なら18)のことを独自流に駒の背番号とよばせてもらうことにするのじぇ。
KPPとは(王)(王以外の駒)(王以外の駒)の三駒関係
KKPとは(王)(王)(王以外の駒)の三駒関係なのじぇ。

またこの記事ではある程度のKKP,KPPの計算方法について理解しているものとして記事をかくのじぇ
まりちゃはめんどくさがりなんで最初から説明するのはめんどくさいのじぇ。
LS3600ブログに詳しく乗ってるのでそこを見てほしいのじぇ。
また時間を取れれば詳しく解説するのじぇ。

f:id:daruma3940:20160520223745p:plain

詳しい話を始める前に全体の見通しを良くするためにざっくりと三駒関係の差分計算の概要を述べておくと

駒を動かしたり、打ったりするときに

動かす前の駒のbonapieceとその駒の背番号
移動後の駒のbonapieceとその駒の背番号を記録しておいて

これを使って
動かす前の駒に関連したKPP,KKPの値を全体から引いて
移動後の駒に関連したKPP,KKPの値をそこに足すだけなのじぇ。

f:id:daruma3940:20160521003616p:plain
それだけ聞くと簡単そうね...
f:id:daruma3940:20160520223745p:plain
そうなのじぇこわがらなくてもいいのじぇ

それでは詳しい説明に入っていくのじぇ
まずは駒を動かしたときに
動かす前の駒のbonapieceとその駒の背番号
移動後の駒のbonapieceとその駒の背番号を記録しないといけないのじぇ

駒を動かすという関数はdo_move関数なのじぇ。
またこれらを記録するのに使う構造体はStateinfo構造体の中の変数なのじぇ

struct Stateinfo{

materialdiff

sumKPP//KKPの計算値
sumBKPP//先手の王によるKPPの計算値
sumWKPP//後手の王によるKPPの計算値
Dirtypiece//この局面で動かした駒の情報を記録

}

このようにしてKPPを二つに分けておくのは差分計算上重要なのじぇ。
f:id:daruma3940:20160520223745p:plain
それでは駒を移動させる関数do?move内でどのようにして
駒の移動の種類によって移動した駒の情報をDirtypieceに記録していくのかの説明なのじぇ。

①駒を打った場合

これが一番考えるのが簡単な場合なのじぇ
動いた駒は打った駒の1つだけで
動かす前のBonapieceは手ゴマだった時のbonapiece
動かした後のBonapieceは盤上の位置と駒種によるbonapiece

これらをdirtypieceにこの情報を記録する

これだけなのじぇ

③駒を移動させた場合

3-1 駒をとらなかった場合

これも簡単なのじぇ
動いた駒は動かした駒の1つだけで
動かす前のBonapieceは動かす前の盤上の位置によるbonapiece
動かした後のBonapieceは動かした位置と駒種によるbonapiece

これらをdirtypieceにこの情報を記録する

3-2 駒をとった場合

駒をとった場合には
動いた駒の情報だけでなく
取られた駒の情報も記録しなければならないのじぇ

動かす前のBonapieceは取られる前のbonapiece
動かした後のBonapieceは手ゴマの種類と枚数によるbonapiece

これらのBonapieceと駒の背番号を
dirtypieceにこの情報を記録する

ここまでがどの駒が動いたのかによる情報なのじぇ
f:id:daruma3940:20160520223745p:plain
さあここからが
KPP差分計算のための解説なのじぇ。

先ほど用意した動かす前と動かした後のBonapieceの組と駒の背番号を使うのじぇ 。

ここでは今の局面の一戸前の局面で三駒関係による評価値は計算されているものとするのじぇ
まあ一個前の局面で3駒関係が計算されていなかった場合は差分ではなくフルで計算しなおすだけなのじぇ。

①先手の玉が移動したとき

これはKPPのKの部分が動いたのでかなり大規模に計算しないといけないのじぇ
BKPPはそのまま再計算
もし王で駒をとっていた場合その分をWKPPに差分計算
つまり
動かす前の駒に関連したKPP,KKPの値を全体から引いて
移動後の駒に関連したKPP,KKPの値をそこに足す。
ここでKPPのPPに同じ値が入ってしまってはいけないことに注意するのじぇ。


 //先手玉が移動したとき
          //BKPPについて全計算
          //KKPについて全計算

        if (dirty == PIECE_NO_BKING)
        {
          // ----------------------------
          // 先手玉が移動したときの計算
          // ----------------------------

          // 現在の玉の位置に移動させて計算する。
          // 先手玉に関するKKP,KPPは全計算なので一つ前の値は関係ない。

          sumBKPP = 0;

          // このときKKPは差分で済まない。
          sumKKP = Eval::kkp[sq_bk0][sq_wk1][fe_end];

          // 片側まるごと計算
          for (i = 0; i < PIECE_NO_KING; i++)
          {
            k0 = now_list[i].fb;
            sumKKP += Eval::kkp[sq_bk0][sq_wk1][k0];

            for (j = 0; j < i; j++)
              sumBKPP += Eval::kpp[sq_bk0][k0][now_list[j].fb];
          }

          // もうひとつの駒がないならこれで計算終わりなのだが。
          if (k == 2)
          {
            // この駒についての差分計算をしないといけない。
            k1 = dp.piecePrevious[1].fw;
            k3 = dp.pieceNow[1].fw;

            dirty = dp.pieceNo[1];

            //dirtyは移動をした駒のpieceno

            // BKPPはすでに計算済みなのでWKPPのみ。
            // WKは移動していないのでこれは前のままでいい。

            //WKPPなので+が減算で-が可算ここに気をつけるべし
            for (i = 0; i < dirty; ++i)
            {
              sumWKPP += Eval::kpp[sq_wk1][k1][now_list[i].fw];//以前の値を引き
              sumWKPP -= Eval::kpp[sq_wk1][k3][now_list[i].fw];//今の値を足す
            }
            //dirtypieceのところは飛ばす(PPのPには同じBonapieceは入れない)
            for (++i; i < PIECE_NO_KING; ++i)
            {
              sumWKPP += Eval::kpp[sq_wk1][k1][now_list[i].fw];//以前の値を引き
              sumWKPP -= Eval::kpp[sq_wk1][k3][now_list[i].fw];//今の値を足す
            }
          }

②後手の玉が移動したとき
これは①の場合とほぼ同じなのじぇ。

③それ以外の駒が移動したとき

 // ----------------------------
        // 玉以外が移動したときの計算
        // ----------------------------

          //BKPP WKPPを一括差分計算するためのマクロ
#define ADD_BWKPP(W0,W1,W2,W3) { \
          sumBKPP -= Eval::kpp[sq_bk0][W0][now_list[i].fb]; \
          sumWKPP += Eval::kpp[sq_wk1][W1][now_list[i].fw]; \
          sumBKPP += Eval::kpp[sq_bk0][W2][now_list[i].fb]; \
          sumWKPP -= Eval::kpp[sq_wk1][W3][now_list[i].fw]; \
}

        if (k == 1)
        {
          // 移動した駒が一つ。

          k0 = dp.piecePrevious[0].fb;
          k1 = dp.piecePrevious[0].fw;
          k2 = dp.pieceNow[0].fb;
          k3 = dp.pieceNow[0].fw;

          // KKP差分
          sumKKP -= Eval::kkp[sq_bk0][sq_wk1][k0];
          sumKKP += Eval::kkp[sq_bk0][sq_wk1][k2];

          // KP値、要らんのでi==dirtyを除く
          for (i = 0; i < dirty; ++i)
            ADD_BWKPP(k0, k1, k2, k3);
          for (++i; i < PIECE_NO_KING; ++i)
            ADD_BWKPP(k0, k1, k2, k3);

        } else if (k == 2) {

          // 移動する駒が王以外の2つ。
            //駒取の場合
          PieceNo dirty2 = dp.pieceNo[1];
          if (dirty > dirty2) swap(dirty, dirty2);
          // PIECE_NO_ZERO <= dirty < dirty2 < PIECE_NO_KING
          // にしておく。

          k0 = dp.piecePrevious[0].fb;
          k1 = dp.piecePrevious[0].fw;
          k2 = dp.pieceNow[0].fb;
          k3 = dp.pieceNow[0].fw;

          int m0, m1, m2, m3;
          m0 = dp.piecePrevious[1].fb;
          m1 = dp.piecePrevious[1].fw;
          m2 = dp.pieceNow[1].fb;
          m3 = dp.pieceNow[1].fw;

          // KKP差分
          sumKKP -= Eval::kkp[sq_bk0][sq_wk1][k0];
          sumKKP += Eval::kkp[sq_bk0][sq_wk1][k2];
          sumKKP -= Eval::kkp[sq_bk0][sq_wk1][m0];
          sumKKP += Eval::kkp[sq_bk0][sq_wk1][m2];

          // KPP差分
          for (i = 0; i < dirty; ++i)
          {
            ADD_BWKPP(k0, k1, k2, k3);
            ADD_BWKPP(m0, m1, m2, m3);
          }
          for (++i; i < dirty2; ++i)
          {
            ADD_BWKPP(k0, k1, k2, k3);
            ADD_BWKPP(m0, m1, m2, m3);
          }
          for (++i; i < PIECE_NO_KING; ++i)
          {
            ADD_BWKPP(k0, k1, k2, k3);
            ADD_BWKPP(m0, m1, m2, m3);
          }

          //重なった部分だけここで計算しておく
          sumBKPP -= Eval::kpp[sq_bk0][k0][m0];
          sumWKPP += Eval::kpp[sq_wk1][k1][m1];
          sumBKPP += Eval::kpp[sq_bk0][k2][m2];
          sumWKPP -= Eval::kpp[sq_wk1][k3][m3];

        }
      }
    }

f:id:daruma3940:20160520223745p:plain
最後の方は説明が雑でコードを張り付けただけになってしまったけどざっとこんなもんなのじぇ。

まあ差分計算をしない三駒関係について理解していればこれも理解できると思うのじぇ。