クラウドワークスでエンジニアをしている八木 ( @negito6 )です。
私は今年から、将棋のネット観戦が趣味になっています。最近では、映像に名前や顔が映らずとも、声や手の仕草だけで棋士の先生を判別できるようになってきました(だからどうってこともないですが・・・)。好きな解説の先生は、ズバリ!渡辺明竜王*1です。
「棋譜」と「検討」
(会社のエンジニアブログなので、普段の記事では以上のような軽い自己紹介をした上で技術的な内容に入ることが多いですが、今回はもう少しだけ続けます)
将棋では、対局の中のある場面でのいろいろな手のパターンを考えることを、「検討」と言います。こうすればもっと早く決着がついてたとか、ああすれば勝ち負けが逆転したしたのではないか、といったことを一人で考えたり、人と話し合ったりします。私自身も(棋力はさっぱりですが)強くなりたいので、自分が指した後は検討をすることがあります。
しかし、あれこれパターンを試していると、さっき考えたパターンが思い出せなくなったりしてきます。パターンは多分岐の木構造に枝分かれしていくので、紙に残すのもちょっと面倒で、どうにかならないかと悩むようになりました。盤面の変更を棋譜として順番に記録していきたいが、変更の各地点で枝分かれの可能性があり、進んでは戻り、また新しい枝を作り...と、ここまで考えると、将棋以外の手慣れた作業が思い出されます。そうですね。Git です。検討ついて記録することは、Git のブランチ・コミット操作と同一視できます。
というわけで、棋譜を Git にコミットして記録していくプログラムを作ってみることにしました。名前は Shogi + Git で安直に Shogit です。本記事では Shogit を作る過程と実装に関して簡単にご紹介したいと思います。
インターフェース
まずは作るプロダクトの機能について考えるところから始めました。とりあえず想定するユーザーは自分です。棋譜の入力は枝分かれとか関係なく手間なので、いかに楽に棋譜を入力できるか、ということを考えます。
よくある表記は「▲7六歩」の形ですが、記号・算用数字、漢数字が混じっているため、これを入力するのは大変です。特に面倒なのは、漢字変換と Shift キーなので、その二つを一切使わないように
> sente 76,fu
という形式でいくことにしました。「sente」には正直なところ抵抗がありますが、調べるとBlack
*2やthe first move
などが出てきて、将棋っぽくなかったりタイプ数が増えたりするので敢えて日本語にしておきました *3。
ただしこのままローマ字の表記だと見慣れないので、
- 入力に対して日本語表記 (この例だと「▲7六歩」) を出力
- 日本語表記を git のコミットメッセージに入れる
ということにします。これで、「git log によって歴史を閲覧」という、馴染み深い操作形式を提供することができそうです。
イメージ:
$ git log commit a7564242ccae71c2a79e8900a87adf870b0b7844 Author: Test Author <testuser@github.com> Date: Tue Sep 20 16:28:08 2016 +0900 ▲6六歩 commit 74961381ca5cf7e38edc9f378aec376380b959f4 Author: Test Author <testuser@github.com> Date: Tue Sep 20 16:27:59 2016 +0900 △3四歩 commit 306d9b6244a771beb138a7925dde14b0070d415d Author: Test Author <testuser@github.com> Date: Tue Sep 20 16:27:52 2016 +0900 ▲7六歩
実装
Git 操作
Git の操作には、 rugged という Ruby の Gem を使いました。 まず libgit2 という Git 操作の API を提供するライブラリがあり、 rugged は libgit2 の Ruby バインディングです。
rugged は正直なところそこまでドキュメントが充実していないように思いましたが、今回必要な操作は
- テキストファイルの書き込み
- commit
- ブランチ作成
- ブランチの切り替え
という基本的なものでしたので、ほとんど README のサンプルで足りました。また、テストが一通り揃っており、README や実装を読んでもわからないところはテストコードを真似ることで補うことができました。
コンソール
とりあえず irb にしました。 irb はファイルの中でコンソールを起動できるほか、コンソール上で使えるメソッドを自分で定義することができます。
簡単ですが Qiita 記事に説明を書いたので、興味がある方はご参照ください。 irb が動くシンプルな何かを作る - Qiita
成果物 (動作サンプル)
棋譜の記録
irb(main):004:0> sente 76,fu => "1 ▲7六歩" irb(main):005:0> gote 34,fu => "2 △3四歩" irb(main):006:0>
枝分かれ
二手目から枝分かれするには、 branch に 2 を渡します (また、負の数は現在からの差分を指します。たとえば -2 を入れると、二手前
から枝分かれします )
irb(main):001:0> branch 2 1 ▲7六歩 >>> 2 △3四歩 => "game on yokofudori-2_1474567455"
その時点でまた棋譜を積み重ねることができます。
irb(main):003:0> sente 66,fu => "3 ▲6六歩" irb(main):004:0>
元々の棋譜に戻るには、 checkout を使います
irb(main):004:0> checkout 18 △8四飛 19 ▲2六飛 20 △2二銀 21 ▲8七歩 22 △5二玉 >>> 23 ▲5八玉 => "game on yokofudori" irb(main):005:0>
Q&A
入力に応じて、その時点での盤を再現したりはできないの?
これは、私も考えたのですが、大きな課題があります。
棋譜には「どこに駒が動いたか」の情報しかなく、「どこから駒がきたか」の情報がありません。ある地点に動けた駒が二つ以上ある場合は「右」「左」「上」「引」などの字を足して補うなどバリエーションが多く、判別する実装は今回は間に合いませんでした。気になる方は「棋譜 表記」などで検索して調べてみてください *4。自動で判別せずに入力するという方法も考えられますが、タイプ数が多くなり利便性が下がってしまいそうです。
今後、時間を見つけてトライしたいと思います。
▲ と △ の大きさの違いが気持ち悪い
私もそう思います。何かいい方法はないのでしょうか。
なんの役に立つの?使うの?
さあ・・・
感想
テストの重要性を再認識しました。
実装の項目で触れましたが、rugged というライブラリを新しく触るにあたって、ドキュメントが足りない部分をテストコードを見ることで補って実装を進めることができました。テストは、カバレッジももちろん大丈夫ですが、インターフェースを一通り示しておくことで参入障壁を下げるという効果があります。何かライブラリなどを公開するときに、 README を完璧にこしらえるよりは、テストケースを揃えるほうが費用対効果が高くなるケースもありそうです。
なお、 Shogit-Ruby のほうではまだ特にテストは書いていません。余裕があればコードも綺麗にしたいですね...
We're Hiring !
クラウドソーシングのクラウドワークスでは、エンジニアを募集中です。
興味のある方はお寿司ランチを無料で食べながらお話してみませんか?
*1:2016年9月23日現在、竜王・棋王の二つを保持されている強豪の棋士です。解説としての人気も高く、将棋ファンからするとベタ中のベタな回答です。
*2:チェスを始めとして、二者間ボードゲームでは先番が黒のものが多いのでしょうか。
*3:たとえば柔道では「イッポン」という言葉がそのまま世界中で使われているそうなので、将棋の国際化が進めばきっと「先手」「後手」も国際共通語になると適当に期待しています。なお、当初は chakushu という private なメソッドも作っていましたが、これはさすがにやめました。
*4:以前は将棋連盟のサイトに書いてあったのですが、先日の例のリニューアルの際にどこかにいってしまったようです。