釣りブログランキングに参加してます。プログラミングDEN将棋だけでも十分スマートフォン同士の手軽な対局を楽しめるのですが、人間欲が出てくるものです。昼休みに将棋を指しているといくら早指しでも時間が無くなってしまうことが有ります。いう時昔であれば、将棋盤を事務所の片隅に置いて、「また明日ね!」が出来たんです。スマートフォンをそのままにしておくわけにはいかないので、何とかしたいと思いました。そこでshogi-serverの仕様書(?)を読んでいると、拡張モードを使用すれば中断/再開機能が実装できそうです。これだと思い、さっそく開発開始。当然ですが色々課題が壁が立ちはだかり、思ったより時間がかかりました。これはその苦労話的な備忘録です。[実際の対局はこちら]1.拡張コマンドの使用ログインshogi-serverは、状態遷移図を見るとログイン時から標準モードと拡張モードに完全に分かれます。Login文字列の最後にx1を付けると拡張モードになります。拡張モードでは、ログイン後対局しないこともできますし、対局終了後もログイン状態を継続することもできます。このような仕様にしたかはわかりませんが、ログイン状態で他人の対局を観戦したりできるようにしたかったのだと思います。fladgateはこの機能を使い多くの人が集まっているわけです。Xでは観戦機能は実装していません。拡張モードで使用できるコマンドが拡張コマンドです。Xでは、LOGIN、%%GAME、%%SETBUOY、%%DELETEBUOY、%%CHATの5つのコマンドを使用しました。LOGINコマンドはCSA標準モードでも使用するものですが、パラメータの指定が若干異なり、下記のようにしています。x1CSAモードのname-total_time-byoyomi_timeの部分はパスワードとしてなんでも良いのですが、この書式(gamename)にすると対局時間や秒読み時間を指定できるため、このように使っています。Xではログイン後、%%GAMEコマンドのパラメータとしてCSAモードと同様の時間指定を渡します。これで対局待ちとなります。[+-*]最後の部分は拡張モード特有の先手/後手指定部分です。+は先手指定、-は後手指定、*はどちらでも良いという意味です。XのLoginボタンを押すと、name-total_time-byoyomi_time(gamename)と先手/後手指定の利害が一致するクライアントが現れるまで待ちます。現れた時点で対局開始となり、先手か後手のいずれかに決定したかをブラウザ上に表示します。対局中はCSAモード同様に、CSA仕様の指し手データをサーバーに送り、PSGIが着席番号に対応したワーカープロセスに転送し、さらにワーカープロセスがshogi-serverに転送します。指し手を受信したshogi-serverは局面分析後問題なければ、この指し手データに消費時間を付加したデータを、対局者2人分のワーカープロセスに配信します。ワーカープロセスは受信キューにshogi-serverからのデータを溜めます。このキューデータは、ブラウザからの定期的なポーリングリクエストで取り出され、JSONデータに変換されてブラウザのJavascriptに渡されます。Xでは対局中、自分の手番の時だけ「中断ボタン」を押せるようになります。これによりサーバー側にそこまでの棋譜を保存します。ここで使用するコマンドが%%SETBUOYです。このコマンドは下記のように使います。[count]「count回対戦されると、サーバはgamenameを解除する」と仕様書にはありますので、countは1回で削除したくない場合に使うようですが、ここでは常に省略(=1)しています。このコマンドが成功するとgamenameの対局の棋譜がshogi-serverに保存されます。「buoy_」を付けるのがルールです。保存した棋譜は、ログアウト後も再度ログインすれば、いつでも誰でも(?)呼び出して再開することができます。再開にも%%GAMEコマンドを使うのですが、この時の指定gamenameには、頭にbuoy_を付けた名前を使用します。Xでは、中断ボタンを押すと、このコマンド実行後にログアウトします。Xでは観戦などの機能は今のところありませんので、ログイン状態は保持せずに、中断でも対局終了でもログアウトします。中断機能を検討していると大事な機能がshogi-serverに無いのに気づきました。それは、現在保存されている中断対局リストを参照する機能です。shogi-serverにはこの機能が無いのです。コマンドというのがあり、gameid(gamenameを含む情報)一覧を返してくれるようなのですが、%%FORKコマンドの説明箇所で「%%LIST←対局中のgame_idを確認」とあるので、SETBUOYした対局一覧は含まれないと思い、TELNETで実験してみるとやはり、中断中の一覧は返してくれませんでした。保存されているのは間違いないので、どのファイルにあるのか探しましたが、おそらくbuoy.yamlに保存されているところまではわかりましたが、中身はスクランブルがかかっているので仕様がわからないと手も足も出ません。そこでワーカープロセスが独自に保存する事にしました。保存ファイルはcsvファイルとし、各レコードフォーマットは下記の通りです。登録者名,登録者先手/後手,相手名,相手先手/後手,GameNameこのファイルへの書き込みは各ワーカープロセスが行い、読み出しはPSGIが行います。中断した対局のgamenameを各自覚えているわけにもいかないので、再開のためにブラウザ側で中断対局一覧を表示して、そこから選択させるようにしました。「再開」ボタンを押すと、中断一覧を返すURLに対し、ajaxでJSONリクエストします。このURLもPSGIのbuilderブロックにmountされていて、対応するパッケージのto_app関数内で上記csvファイルにアクセスして、対局者2人のどちらかのusernameが記載されているレコード一覧を返します。中断対局一覧から選択したら、Loginボタンを押して対局開始待ちになるのは新規対局と同じです。再開の場合(%%GAMEコマンドの指定gamenameの頭にbuoy_が付加されていたら)shogi-serverは、サマリー情報の最後で、中断時点までの棋譜データを送ってきます。Game_Summaryワーカープロセスはこのサマリー情報から抽出した必要情報をキューに格納するのですが、この棋譜データもキューに格納しますので、ポーリングリクエストにより中断前棋譜がブラウザ側に伝わります。Game_Summary」受信後「AGREE」を送信し「START」が配信されたらMatchMakeされた事になりますので、この時点でCSVファイルから選択した中断gamename情報を削除します。この後Javascriptは、盤面に棋譜を反映させてから中断を実行したユーザーから対局を開始します。対局中は手番のユーザーが再度中断する事ができます。何回でも中断できるわけです。再開後のサマリー情報には中断前までの残り時間情報は入っておらず、指し手毎の消費時間も全て1秒になっていますので、再開後は中断前の指し手全て1秒で指したものとして開始する事になります。チャットshogi-serverに足らない機能はもう一つありました。それは、中断成功が中断を実行したユーザーにしか返されない事です。Xでは中断実行したユーザー側は自動でLogoutしますので、結果的に中断実行したユーザーの相手のユーザーには、いきなり#WINが伝えられます。これによりブラウザ画面には、「あなたの勝ちです」が出ます。「相手は何で投了したんだろう?」ってことになってしまうので、これをカバーするためにチャット機能を使用する事にしました。中断しました」というメッセージをajaxで送信します。PSGIはそのままshogi-serverに送信し、%%CHATコマンドで受信した文字列はそのまま2人のユーザーに下記のように返されますので、これをキューに溜めれば、2人のブラウザ画面に伝わります。##[CHAT][username]メッセージあと、デバッグ中なぜかチャットメッセージが返信されない事があり、結構悩んでしまいました。ログイン時パスワードがNULLだとチャットが使えない仕様のようです。Xの場合意味が無いのでNULLでもOKだと思い込んでいたのですがそうでは無く、パスワード入れないと対局はできますが、チャットできませんので注意!2.GUI画面が無いとイメージ沸かないと思うので、CSS,html,Javascriptで実現している画面を抜粋して説明します。チャット部分チャットのSendボタンは対局中のみ押せます。棋譜反映ボタンは対局していない時のみに押せます。反映できる棋譜はJKFファイルのみです。電子メールURLサインイン情報を記憶Captcha:画像の中に見える文字を入力してください。