テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

コウのロジックを実装した

コウは囲碁が終局するために必要である. 同じところをとって取り返してとって取り返して・・・と繰り返すと永遠に終局しなくなるため, このような場合にはすぐに取り返すことは出来ず, コウダテを打つ必要があるというルールになっている. コウは囲碁においてかなり深い部分だ.

以下の2つを実装する.

  1. そこに打つことが出来るか判定
  2. 打ったあと, 相手がコウダテを必要とするようにする

ボードのデータ構造を以下のようにする. kouは, 存在する場合には, 着手禁止点を表す(前回の着手ではない)

以下が, 着手可能かどうかを調べる関数(can_put)の実装である. can_putでは,

  1. すでに石があるか?
  2. コウダテを打つ必要があるか?
  3. それが自殺手でないか?

の3つがすべてfalseの時のみtrueとなる. 自殺手かどうかは, try_suicideによって「仮にそこに打ったとして, 石がとられてしまうか?とられるとしたらその点のリスト」を取得して, そいつが空かどうかを調べている. koudate_needは, 打とうとしている点が, 一手前にとられた点ならばコウダテが必要ですということを言っている.

関数型言語はこういうロジックがきれいに表現出来るのでいい. きれいに書いたものがそのままパフォーマンスも良い(に決まってる). 型推論によって, いちいち型を書く必要もない. 型チェックで守られるから, リファクタリングも全く恐ろしくない.

type t = {
  matrix: int array array ;
  (* the last kou taken *)
  mutable kou: (int * int) option;
}
(* before put *)
let is_single_suicide t (i, j, a) =
  match try_suicide t (i, j, a) with
  | [_] -> true
  | _ -> false
;;

(* before put *)
let is_suicide t (i, j, a) =
  try_suicide t (i, j, a) = []
;;

(* before put *)
let is_kou_take t (i, j, a) =
  is_single_suicide t (i, j, a)  &&
  will_take_one t (i, j, a)
;;

(* before put *)
let can_put t (i, j, a): bool =
  let stone_exists = t.matrix.(i).(j) < 2 in
  let koudate_need = match t.kou with
  | Some (i', j') -> (i, j) = (i', j')
  | _ -> false
  in
  not @@ (stone_exists || koudate_need || is_suicide t (i, j, a))
;;

さて, 着手可能と分かった場合, 以下のput_stoneを呼ぶ(put_stoneの中ではチェックしていない). この関数は, 「仮にそこに打つ手がコウの形になるならば(#), 打ったあとにとった石を覚えておく」という処理が追加されている.

(#)は,

  1. それが自殺であり, 打つ石のみが死ぬ場合(single suicide)である
  2. 経った一つの石をとる. (2個以上とった場合は, コウにならないはず)

が両方ともtrueの時にtrueとなる.

let put_stone t (i, j, a) =
  show t ;
  let was_kou_take = is_kou_take t (i, j, a) in
  t.matrix.(i).(j) <- a ;
  let xs = remove_list_by_put t (i, j, a) in
  remove_stones t xs ;
  (* if this put is kou-take then we remember the point *)
  if was_kou_take then
    t.kou <- Some (List.hd xs)
  else
    t.kou <- None
;;

一応, ここまでで, 碁盤としてのロジックはすべて実装したことになると思う. トータル200行程度であり, 恐らく銀河系最小コードの碁盤であろう. OCamlは素晴らしい. CもJavaもGoも完全なるゴミだ.

今後の課題は, 以下のようなものだ. このプロジェクトはOCamlの学習目的が主である. したがって色々なことを体験するといい.

  • OUnitを使い, テストを自動化する.
  • ライブラリとして利用可能にする.
  • SGFファイルを読んで流し込む. テストとパフォーマンス計測
  • コマンドラインツールを何か作る