Tech KAYAC Advent Calendar 2017の13日目の記事をお届けします。 クライアントワーク事業部でサーバーサイドエンジニアをしている森です。よろしくお願いします。
過去最大の将棋の盛り上がり
今年は将棋がとてもとても盛り上がりましたね。
- 最年少プロ棋士誕生
- 藤井四段29連勝
- 加藤一二三九段引退
- 流行語大賞候補に藤井フィーバーとひふみんがノミネート
- 羽生棋聖竜王位を獲得し永世七冠に
かつてこんなに将棋がニュースに出たことはあったでしょうか。
こうなってくると将棋人口が増えて来そうです。
将棋人口が増えた時の問題点
将棋人口が増えると何が困るでしょうか?
webのエンジニアなので将棋をwebに例えて考えてみました。
対局者:ユーザー 将棋盤・駒:UI・UX 指し手:アクセス 棋譜:ログ
という感じでしょうか。
将棋人口が増えると棋譜がとんでもないことになりそうです。 とは言え何時間もかけて指した棋譜捨ててしまうのは勿体無いですよね。
そんな勿体無いことせずに人の棋譜も含めて全て集めましょう!
どうやって棋譜を集めるか
棋譜≒ログということが分かったのでwebで一般的にログを扱うように棋譜を扱うことを考えてみます。 今回はtd-agent(fluentd)で受けてBigQueryに投入するというシステムにすることにしました。
なぜtd-agentを使うの?
様々な入力に柔軟に対応できるからです。例えば盤面を動画で撮影しながら指されたら自動的に棋譜データを送るとかも頑張ればできそうです。また、今回は出力にBigQueryを使用してますが状況によっては他のものに変更することも可能です。
なぜBigQueryを使うの?
BigQueryをデータベースとして使用することでかなりのデータ数まで耐えられます。
集めた棋譜はどうするの?
ログに入れても目的のデータが見つけられないと何のために保存しているかわからないので、効率的に表示することができるツールも作成しました。
システム
ログで記録する項目
項目名 | 型 | 詳細 |
---|---|---|
game_code | STRING | ハッシュ値など対局ごとに固有の値を設定します |
first_name | STRING | 先手対局者名 |
second_name | STRING | 後手対局者名 |
no | INTEGER | 〇〇手目 |
move | STRING | 指し手 |
position | STRING | 局面 |
is_first | BOOLEAN | 手番(先手:true 後手:false) |
first_hand | STRING | 先手持ち駒 |
second_hand | STRING | 後手持ち駒 |
moved_at | DATETIME | 指した時刻 |
ログの送信形式
ログを送信するときは日本語名ではなく以下の表にマッピングさせます。
先手 | 後手 | 駒 |
---|---|---|
a | A | 玉 |
b | B | 飛 |
c | C | 角 |
d | D | 金 |
e | E | 銀 |
f | F | 桂 |
g | G | 香 |
h | H | 歩 |
i | I | 竜 |
j | J | 馬 |
k | K | 成銀 |
l | L | 成桂 |
m | M | 成香 |
n | N | と |
また、局面は1一、1二、1三・・・2一、2二・・・9八、9九
という様に筋ごとに全て1行にまとめます
設定前の前提条件
- BigQueryにアクセスする為のjsonキーを取得
- BigQueryのテーブルを「ログで記録する項目」の様に作成
- td-agentをインストール
設定
td-agentからBigQueryは幸いにもプラグインが用意されているのでそれを使います。 こちらの fluent-plugin-bigquery を使用しました。
td-agentを使用しているので受け口は柔軟に変えられます。今回はhttpで受けることにしました。
/etc/td-agent/td-agent.conf
<match kifulog.**> @type bigquery method insert <buffer time> flush_interval 0.1 timekey 1d </buffer> auth_method json_key json_key /path/to/gcpkey.json auto_create_table true project project-name dataset kifulog table states$%Y%m%d schema_path /etc/td-agent/schema.json </match> <source> @type http port 8888 </source>
BigQueryはスキャンした量に対して課金され、毎回テーブルに対してフルスキャンがかかるので同じテーブルに全データを突っ込んでしまうととんでもない費用がかかってしまうことがあるので、日付ごとテーブルを分けて保存する様にしています。
/etc/td-agent/schema.json
[ { "name": "game_code", "type": "STRING" }, { "name": "first_name", "type": "STRING" }, { "name": "second_name", "type": "STRING" }, { "name": "no", "type": "INTEGER" }, { "name": "move", "type": "STRING" }, { "name": "position", "type": "STRING" }, { "name": "is_first", "type": "BOOLEAN" }, { "name": "first_hand", "type": "STRING" }, { "name": "second_hand", "type": "STRING" }, { "name": "moved_at", "type": "DATETIME" } ]
棋譜データを送る
curlで送ってみます。
curl -X POST -d 'json={"game_code":"ewqfnna","no":4, "first_name": "muutaro729", "second_name": "dcsyhi", "position": "G-H---h-gFCH--h-bfE--H--h-eD-H---h-dA--H--h-aD-H---h-dE-H--h--eFBH---hcfG-H---h-g", "first_hand": "", "second_hand": "", "is_first": false, "move": "5354H", "moved_at": "2017-12-11 12:00:00"}' http://localhost:8888/kifulog.http
この様な感じでBigQueryにデータを送ってみて入っていれば完了です。
目的の棋譜データを探す
棋譜データを保存しても目的のデータが探し出せなくては意味がないので、日付を指定してどの様な対局があるか、特定の囲いがあるかなどをキーとして検索できる様なkifulogというツールを作成しました。
コマンド
今回日付ごとにテーブルを分けている為クエリを実行する際には必ず期間を指定する様にしています。
対局一覧
指定日付の期間内での対局一覧を表示します
bundle exec ruby kifulog.rb game 20171201 20171207
出力
2017/11/18 10:00 > ewqfnna 2017/11/18 10:30 > inzqina 2017/11/19 11:00 > viziqfh
対局の表示
対局の詳細を表示します。
bundle exec ruby kifulog.rb show 20171201 20171207 ewqfnna
出力
CLIで操作することができます。
コマンドはこんな感じです
- 数字を入力するとその局面に飛ぶ
- nまたはnextと入力すると次の局面に移る
- qまたはquitと入力すると終了
囲いの検索
指定の囲いを使用している対局を検索します。
bundle exec ruby kifulog.rb castle 20171201 20171207 mino
出力
ewqfnna : 30手目 zeiwzyq : 35手目 pzoqnae : 28手目
の様にgame_codeと何手目に最初に囲いが現れたかが表示されます。これで気になった対局を show
コマンドで表示できます。
BigQueryではクエリに正規表現が使えることを活かしてこの様な正規表現でマッチさせています。局面を81文字で表すことにより正規表現での抽出がしやすくなっています。以下美濃囲いの場合の正規表現です(厳密な美濃囲いではなく近い状態で検索に引っかかる様にしています)
(.{45}D.{8}-EH.{6}FAH.{6}G-(H.{6}|-H.{5})|(.{6}h|.{5}h-)-g.{6}haf.{6}he-.{8}d)
BigQueryに投げているクエリとしてはこんな感じです。
"SELECT * FROM kifulog.states WHERE REGEXP_CONTAINS(position, '(.{45}D.{8}-EH.{6}FAH.{6}G-(H.{6}|-H.{5})|(.{6}h|.{5}h-)-g.{6}haf.{6}he-.{8}d)') AND _PARTITIONTIME BETWEEN TIMESTAMP('20171201') AND TIMESTAMP('20171207') LIMIT 1000"
最後に
いかがだったでしょうか。来年もこれまで以上に将棋が盛り上がっていくことを願っております。
カヤックでは無類の将棋好きも、そうでない方も募集しております!
次回はまっちーによるHoudiniの記事です。期待!!