df-pnアルゴリズム学習記(2)

[進捗]

  • 前回の文献[1]に記載されたアルゴリズムに基づくC++実装を一段落させた。
  • ループ(Directed acyclic graph)を検出するようにした。

 

 『ストラウストラップのプログラミング入門』を読んでいる。ほんとこれが無かったら実装するのは無理だった。いまは速さを全く追及していないので、とにかく安全に動いてテスト容易なプログラムにしていきたい。

 

それにしてもvectorが便利だ。とくに

 

  vector<char*> testBoards;

  testBoards.push_back("tsumeShogi/1_1.txt");

  testBoards.push_back("tsumeShogi/1_2.txt");

   ...

  for(unsigned int i=0; i < testBoards.size(); i++){

    bd.readBoard(testBoards[i], bd);

    ...

}

 

のように、テストする盤面をvectorで管理したことによる開発効率化は計り知れない。テストの追加が非常に楽に済むようになった。これはものすごい改善につながる。

 

それと、以前に有識者から習った発想が役に立った。「どういう風に切り分ければテストが容易になるのか」という点で考えた結果、以下の3点を思いついた。

  1. 関数単位でテストする。特にreturn a == 1 ? true : false; のように全出力がテストできるものはこれで以降のバグ探し領域から除外できるのが嬉しい。
  2. 関数内のまとまった仕事を書くごとにテストを書いて実行する、消して次の仕事を書いたらまたそのテストを書いて実行して、のようにやっていくと、関数がそれ以上切り分けられない複数の仕事を行うものである場合に、大方正常な動作をするだろうと判断できる。
     もちろん理想は全入出力をテストすることだが、実際にはコスパは逓減するばかりなので、この「完成時には消えているテストコード」程度が妥当と判断した。これはユニットテストにすら分類されないだろうから、あくまで石橋を叩いて渡る程度の気休めである。
  3. 構成はトップダウンに行い、実装を完了させるのはボトムアップに行う。つまり、一番上の例えばdf-pnならdfpn_root()関数から書き始めるが、テスト用に/実際に呼び出すのはそれが一番最後になるということ。
     呼び出さなければ型チェック程度のことしか行われないので、そこでバグが出ることは少ない。呼び出さないにしても書いておくと、実装を完了させるその小さな関数が何を満たせばよいのかがより明確になる。戻り値がintなのかや引数のどこが参照になるのかなど。

 

とりあえず、たまに5手詰まで解けるようになったので嬉しい。

しばらくはテストを助ける関数を作ることにする。内部で何を行っているのか、いまいちつかめていないので、それを視覚化する関数を実装する。

 

 このような関数は今のところ、board.print()とmoves.print()は当然として、gdbで実行時にabortした盤面をファイルに出力できるdump()が強力である。思いついて試しに作ってみたらものすごく便利。このような関数は、先走ってアルゴリズムを改良するよりも遠回りのようで近道なので、作る。