Hatena::ブログ(Diary)

ひよこ将棋、はじめました。


  ひよこ将棋関連の実行ファイルは → ここ

2011-10-21

ひよこ将棋v.0.07の実行ファイルを公開しました。

18:51 | ひよこ将棋v.0.07の実行ファイルを公開しました。を含むブックマーク ひよこ将棋v.0.07の実行ファイルを公開しました。のブックマークコメント

ひよこ将棋v.0.07の実行ファイルを公開しました。
このブログの一番上のところからダウンロードできます。

・純粋αβ探索
・killer move + 置換表
・駒得のみの評価関数
・静止探索

だけあれば、floodgateでR1953もの点数がつくのだとわかりました。(人間の10級の人にも負けるぐらい弱いのに…)

ところで、誰も将棋所を使ってひよこ将棋で遊んでいないと思いますが、ひよこ将棋PV(最前応手列)の表示はおかしいです。PVの表示って真面目にやろうと思うと結構難しいんですよねぇ…。次のバージョンまでになおします。

人は駒得のみに生くるにあらず その11

18:36 | 人は駒得のみに生くるにあらず その11を含むブックマーク 人は駒得のみに生くるにあらず その11のブックマークコメント

昨日投入したひよこ将棋v0.06がfloodgateでR1953のレートが付きました!!
たぶん、Gasyou_Atom-D510_2c4tとlesserpyon_sakura_vps_testに勝ったのが大きかったのでしょうけども、本来のRより高すぎます。


あと、gps_lには何度も同じ進行で負けて悔しいので、ひよこ将棋のほうで対策を練ることにしました。

「いよいよ、ひよこ将棋にも定跡が追加されるのかぁ…」と過ぎゆく日々を名残惜しそうに見つめる皆さんも、ご安心ください!

今回、私が追加したコードはこれです。

// 平手で初期化されたあとの初手か2手目ならランダムに指す。
// しかし角頭歩は除外。
if (no_handicap_start && (moves == 0 || moves == 1))
{
  while (true)
  {
    Move move = GetRandomMove(tree);
    BoardPosition pos = Moves::From(move);
    // 駒の移動元が87か23。
    if (pos == H7 || pos == B3)
      continue;
    return move;
  }
}

ご覧の通り、初手&2手目はランダムに指します!ひよこ将棋は永遠に不滅です!!


それからBonanza相当の1手詰め関数を静止探索中に呼び出すように変更しました。これがやや重くて探索深さが減ってしまいました。終盤より序盤がひどすぎるのを改善したいのに…。

これでは木の船に巨大な大砲を積載しているようなもので、なんだか弱くなった気がします。(気のせいであることを祈ります。)

以上の修正をしたひよこ将棋v0.07をfloodgateに投入しておきました。

人は駒得のみに生くるにあらず その10

18:21 | 人は駒得のみに生くるにあらず その10を含むブックマーク 人は駒得のみに生くるにあらず その10のブックマークコメント

ひよこ将棋は序盤で定跡形から外れてしまうので、相手はなかなか飛車先を突いてこないのですが、飛車先を突いてくるソフトの場合、簡単に飛車先を切られて損です。

そこで手駒の価値のほうが盤上の駒より上だというのを簡単に表現するために次のように改造してみました。

// 駒得のみを評価する。ただし手駒の価値のほうが若干上。
Score Eval::evaluate_test2( const Tree* tree )
{
  // 現在の駒割
  // 手駒のほうが盤上の駒より価値があるので、手駒のbit数を足しておくという手抜き実装。
  return TURN == black ?
        tree->board.material + (Score)BitOp::PopuCount64(HAND_B) - (Score)BitOp::PopuCount64(HAND_W)
    : - tree->board.material - (Score)BitOp::PopuCount64(HAND_B) + (Score)BitOp::PopuCount64(HAND_W);
}

ご覧の通り、手駒を表現している構造体の1になっているbitを数えてその数だけ加点するという素晴らしいhackです。


しかし、これをするとどう見ても弱体化しているようでした。

確かに飛車先の歩は切られないように34歩〜33角のように防ぐようになるのですが、その代わりに歩の交換大好きっ子になってしまいまして、玉頭だろうと小ビンだろうどこの歩でも交換します。そして盤上に交換した歩を置くと損をするとひよこ将棋は思っていますから、玉頭ががら空き。

もともとひよこ将棋は居玉のまま囲わないのですが、囲わない + がら空き。
これには参りました。どう見ても弱いです。

素晴らしいhackは大変な副作用を産み出していきました。(←「魔理沙は大変なものを盗んでいきました」の動画を頭のなかで再生しながら)


下手に評価関数をいじると副作用で弱くなるということをひよこは今日学びました。
しばらくひよこは駒割のみで頑張ります。

人は駒得のみに生くるにあらず その9

17:56 | 人は駒得のみに生くるにあらず その9を含むブックマーク 人は駒得のみに生くるにあらず その9のブックマークコメント

floodgateでひよこ将棋v0.06がLesserkai_test(R1465)をフルボッコにしたようです。


http://wdoor.c.u-tokyo.ac.jp/shogi/view/2011/10/21/wdoor+floodgate-900-0+hiyoko_shogi_v0.06_1t1c+Lesserkai_test+20111021130004.csa


歩で角をぽろっと取って序盤早々に勝負ありです。
ひよこ将棋側は自陣の駒は歩と飛車・角しか動かしていません。

こんな将棋で本当に良いのでしょうか…。


そのあと宿敵lesserpyon_sakura_vps_test(R1642)とも当たりまして、ごちゃごちゃやっているうちに勝てた模様。


http://wdoor.c.u-tokyo.ac.jp/shogi/view/2011/10/21/wdoor+floodgate-900-0+lesserpyon_sakura_vps_test+hiyoko_shogi_v0.06_1t1c+20111021163002.csa


相変わらず、ひよこ将棋は8手先まで読んでも評価値が0なので歩を左から順番に突いていくという傍若無人っぷりですが。


そのあとGasyou_Atom-D510_2c4t(R1716)と当たったのですが、何故かGasyouが途中で時間切れ。


http://wdoor.c.u-tokyo.ac.jp/shogi/view/2011/10/21/wdoor+floodgate-900-0+Gasyou_Atom-D510_2c4t+hiyoko_shogi_v0.06_1t1c+20111021173003.csa


こんな感じでして、おそらくひよこ将棋v0.06は、floodgateのレーティング的にはR1600ぐらいの点数がつくと思われます。


人間とやった場合ですと、人間側は最初に穴熊なり銀冠なりに囲うので10級ぐらいの人にも勝てないはずの強さなのですが、floodgateの他の将棋ソフトにはそこそこ通用するというのが面白いですね。

人は駒得のみに生くるにあらず その8

11:34 | 人は駒得のみに生くるにあらず その8を含むブックマーク 人は駒得のみに生くるにあらず その8のブックマークコメント

さっきfloodgateに投入したひよこ将棋v0.06の棋譜がこれまた笑えるのでご紹介します。pishogiとの一戦です。


http://wdoor.c.u-tokyo.ac.jp/shogi/view/2011/10/21/wdoor+floodgate-900-0+hiyoko_shogi_v0.06_1t1c+pishogi+20111021110004.csa

f:id:hiyokoshogi:20111021113437p:image

端歩突き×2のあと角頭歩!!全盛期の米長会長を彷彿とさせますね。相手の角道が空いているときに角頭歩はそこまで大損ではないですが、相手の角道が空いてないときにこれはないでしょう。相手の飛車先の歩を交換するお手伝いをしているだけです。

どうしてこんな指し手を指すようになったかと言うと、いままでは角道を空けていたのですが、それは水平線効果で、角で相手の駒を取る手を見越していたのでした。(そのあと取り返されるとも知らずに)

ところが静止探索を入れたために、取り返されることに気づくようになり、角道をあけるのをやめました。それで何故8筋の歩なのかと言いますと、指し手生成のときに歩からやっており、それも盤面の左上から右下方向に順番に調べているからです。

そこで96歩、95歩としたあと94歩だと取り返されて損なので、指し手生成で次の手である86歩を突くわけです。放っておくと、各筋の歩を順番に突いて大変面白いことになります。

静止探索を入れて、むしろ弱くなっている気がしないでもありませんがしばらくfloodgateでの様子を静観するとしましょう。

ちなみに上の棋譜は終局までひよこ将棋側は自陣にあった駒は飛車と歩しか動かさずにpishogiに勝っています。こんな将棋でいいのでしょうか…。


あと、ひよこ将棋は後手番ですと(同じく左上から指し手を生成する都合上)94歩〜95歩〜84歩のように歩を突いていき、こちらはそれほどの損ではありません。

つまり、ひよこ将棋は後手番が強いのです。(たぶん)

「いい加減、定跡ぐらい入れろよ」という声が聞こえてきそうですが、定跡を入れてしまうと面白くなくなるのでもう少しこのままにして他の改良をしていきます。

人は駒得のみに生くるにあらず その7

11:08 | 人は駒得のみに生くるにあらず その7を含むブックマーク 人は駒得のみに生くるにあらず その7のブックマークコメント

静止探索、実装しました。ひよこ将棋v0.06としてfloodgateに投入しました。私は開発環境でのデバッグはほとんどしていないので例によって何かバグってるかも知れませんが。


評価が安定してきたのは良いのですが、探索深さを8まで読んでも0のままです。0,0,0,0,…。
本当に強くなっているのか甚だ疑問ではありますが。

今度こそれさぴょんに勝ちたいところです。


ここまでのまとめ。
・killer move
・置換表
・純粋αβ
・静止探索 + 1手詰み判定
・駒得だけの評価関数(持ち駒も盤上の駒も同じ価値!)


私が一昨日、このブログに貼りつけておいたコードを良く見た人はお気づきかも知れませんが、置換表の指し手もkiller moveの指し手もその後の指し手生成から除外もせず、再度検索しています。まったく探索エネルギーの無駄使いとなっております。地球に優しくない、電力の無駄遣いです。


あと、今日書いた静止探索のコードも貼りつけておきます。これはひよこ将棋フレームワークの一部となるはずです。どこかで見たコードだなとかそういう苦情は受け付けておりません。

コードについてアドバイス等がありましたらよろしくお願いします。

// 静止探索
Score Search::search_quies( Tree * RESTRICT tree, Score alpha, Score beta, Ply qui_ply )
// qui_ply : 現在の静止探索深さ。
// 静止探索は再帰的に呼び出されるのでこの探索深さを持っていて、限界を超えると打ち切るようになっている。
{

  // 現局面の評価値を受け取る用。
  Score value;
  // この局面でそのまま評価関数を呼び出したときの値。
  Score stand_pat;

  // 探索したノード数のカウント
  tree->node_searched ++;

  // 取り合いをしない現在の評価値をstand_patとして、これを基準に考える。

  stand_pat = EVAL_FUNCTION(tree);

  // 指し手を動かさなくとも最低値を更新したので、alpha値更新。
  // 指し手側の手番だから、何もしないという選択肢がある。
  // どの駒も取らなければ取り返されないわけで、stand_patより下回ることはないと考えられる。
  // すなわち、stand_patとは最低保証値である。
  // 手番の価値があるから、stand_pat + 手番の価値、となるはずで、手番を生かしてこのstand_patの値を
  // 超えることが出来るはずだという仮定のものに成り立っている。
  if ( alpha < stand_pat )
  {
    // β値を超えていればhigh fail..こんなことってあるんか?
    if ( beta <= stand_pat )
        {

      // PASSして、何も指さなくても(手番の価値を生かさなくても)βcutが起きるほどいいよー。
          return stand_pat;
        }
    alpha = stand_pat;
  }

  Turn turn = TURN;

  // 1手ずつ取る手を調べていく。
  tree->anext_move[ply].next_phase = next_quies_gencap;

  while ( true )
  {
    Move move = gen_next_quies( tree, alpha, qui_ply );

    // 指し手がなくなったのか?
    if (move == MOVE_NA)
      break;

    // make_moveしてみる。非合法手ならば局面を進めずcontinue
    if (MakeMove::make_move_for_ques(tree,move))
        continue;

    tree->inc_ply();
        // 静止探索を再帰的に呼び出す
    value = -search_quies( tree, -beta, -alpha , qui_ply+1 );
    tree->dec_ply();

    MakeMove::unmake_move( tree , move);

    // alpha値を更新した。
    if ( alpha < value )
        {
      // beta cutまで発生した。
            if ( beta <= value ) return value;

      // alpha値を更新した
            alpha = value;
        }
  }
  return alpha;
}


Move Search::gen_next_quies( Tree* RESTRICT tree, Score alpha, Ply qui_ply )
{
  Ply ply = PLY;
  switch ( tree->anext_move[ply].next_phase )
  {
        // 捕獲する手(と成る手)を生成するフェーズ
    case next_quies_gencap:
    { 
            GenMove::captures(tree);

            /* set sort values */

      // 生成した指し手バッファの先頭
            Move * RESTRICT pmove = MOVE_LAST;
      // 点数をつけてソートをするので、そのために使うテンポラリ配列
            Score * RESTRICT psortv = tree->sort_value;

            // 生成した指し手の数
            s32 n      = (u32)( MOVE_CURRENT - pmove );

      Score diff ; // スコアの差分評価値
      Score value; // スコアを代入するテンポラリ変数。

      Move move;
      s32 i,j;  // insertion sortで使うので符号型でないとまずい。

      // 生成した指し手のうちSEE>=0の指し手の数。
      s32 nqmove = 0;

      Turn turn = TURN;

            for ( i = 0; i < n; i++ )
          {
            move = pmove[i];

                // 静止探索の限界深さを超えているなら、
                // 1. 歩を取る手で成りを伴わない手
                // 2 駒を取らず歩以外を成る手(歩は成ると大きいのでこれは無視できないが他の駒はたいして
                // 価値が変わらないので無視できる)
                // を生成しない。
                if ( qui_ply >= 7 /*QUIES_PLY_LIMIT*/
                            && ( ( Moves::Cap(move) == pawn && ! Moves::IsPromote(move) )
                    || ( ! Moves::Cap(move) && Moves::PieceMove(move) != pawn ) ) )
          {
                    continue;
          }

        // 指し手の大雑把な見積り。
        // とりあえず最初にcapする駒の価値で考えれ。
        diff = Materials::piece_value[Moves::Cap(move)]/4;

        {
          // SEEの値が0以上のものに対して。MT_CAP_SILVERを超えることはそうそうないだろうし、
          // そういうのはもう一律同じ扱いで構わない。
                  value = SEE( tree, move, -1, Material::MT_CAP_SILVER, turn );
                  if ( -1 < value )
                  {
            // SEEの値 + 指し手の差分評価値を加算。それでソート。
                    psortv[nqmove]  = value + diff;
                    pmove[nqmove++] = move;
                  }
        }
      }
    
        /* insertion sort */
        psortv[nqmove] = -score_bound; // 番人
        for ( i = nqmove-2; i >= 0; i-- )
        {
        // sortvの値でソート。これ…64bit化して、scoreと指し手とまとめて
    // ソートしたほうが速い。あとで書きなおす。
          value = psortv[i];  move = pmove[i];
          for ( j = i+1; psortv[j] > value; j++ )
          {
          psortv[j-1] = psortv[j];  pmove[j-1] = pmove[j];
          }
          psortv[j-1] = value;  pmove[j-1] = move;
        }
      // --- ソート、ここまで。

        MOVE_CURRENT  = MOVE_LAST + nqmove; // SEE>=0だったので使った指し手分。SEE<0の指し手は返さない。
        tree->anext_move[ply].move_last  = pmove; // == MOVE_LAST

      // 次のフェーズ
        tree->anext_move[ply].next_phase = next_quies_captures;

      // ↓ fall through
    }
      
    // 生成した手を1手ずつ返すフェーズ
    case next_quies_captures:
      if ( tree->anext_move[ply].move_last != MOVE_CURRENT )
        {
          return *tree->anext_move[ply].move_last++;
        }
  }

  // 指し手がもう無いんですけど。
  return MOVE_NA;
}

人は駒得のみに生くるにあらず その6

08:05 | 人は駒得のみに生くるにあらず その6を含むブックマーク 人は駒得のみに生くるにあらず その6のブックマークコメント

昨日floodgateに投入した、ひよこ将棋v.0.05はGasyou_Atom-D510_2c4tに勝ったみたいです。lesserpyon_sakura_vps_testには2回当たり、2回とも負け。

ひよこ将棋v0.05にはR1519というレーティングが付きました。アマ初段レベルに…?

Gasyou_Atom-D510_2c4tに勝った記念にひよこ将棋v0.05を公開しておきました。このブログの一番上のところからダウンロード出来ます。


そのGasyou_Atom-D510_2c4tに勝った棋譜がこれです。


http://wdoor.c.u-tokyo.ac.jp/shogi/view/2011/10/21/wdoor+floodgate-900-0+hiyoko_shogi_v0.05_1t2m+Gasyou_Atom-D510_2c4t+20111021040002.csa


f:id:hiyokoshogi:20111021082800p:image

ひよこ将棋側は宇宙流とでも呼ぶにふさわしい、端歩突き〜角出+居玉中飛車の自由奔放な将棋です。

飛車は香で串刺しにされるわ、玉頭にと金が迫っていようが我関せず。よくこれで勝ったなぁというのが正直なところで、人間のアマ初段とは似ても似つかない棋譜です。

アマ五段ぐらいの人なのに初心者相手にわざと変な指し方をしてるの?というようなハンゲーム将棋対戦をも彷彿とさせる、何かイラっとくるものがありますが。

ひよこ将棋v0.05には、静止探索はありませんし、駒得だけって言うのは持ち駒も盤上の駒も同じ価値(←ひどい)です。この二つは特にひどいと思うので今日、改善しようと思っています。