Node Knockout 2018 に参戦しました
Node Knockoutとは
Node Knockoutは、Node.jsを使って48時間でアプリケーションを作る世界規模のオンラインハッカソンです。2010年から開催されているようです。
今年は10/27(土)9:00から10/29(月)9:00(どちらもJST)の期間で開催されました。
今回、カブク社内のエンジニアで4人のチームを作ってこのハッカソンに参加したので、その報告をします。
作ったモノ
Quoridorというボードゲームをオンラインで遊べるアプリを作りました。
その名もKuoridorです(カブクにちなんでイニシャルをKにしました)。ここから遊ぶことができます。
ゲーム画面右上の「VOTE FOR US」ボタンから投票できるので、おもしろかったらぜひ投票をお願いします🙏(投票は11/5(月)9:00(JST)頃までと思われます)
https://www.nodeknockout.com/entries/146-kabuku
メンバー構成・役割分担
- サーバサイド× 1名
-
ゲームロジック× 1名
-
UI × 2名
アプリケーション構成図
※ HerokuはNode Knokoutのスポンサーで、Node.jsアプリケーションのデプロイ先として指定されていました。この制約がなければFirebaseのCloud Functionsなどを利用してもよかったかもしれません。
Quoridorとは
シンプルでありながら奥が深く、とてもおもしろいゲームです。
ルールはWikipediaを見ていただくといいと思います。
本当は4人まで対戦できるのですが、実装が複雑になってしまうのでKuoridorでは2人対戦に制限しました。
Quoridorが好きでしょうがなかった
3Dプリンタでプリントするほど好きでした。
タイムスケジュール
1日目
09:00 (JST) 集合, 開発開始
20:00 (JST) 解散
2日目
09:00 (JST) 集合, 開発開始
22:00 (JST) レッドブルを補給
- 20:00 くらいには終えて飲みに行こう、そんなふうに考えていた時期が私にもありました
24:00 (JST) 解散
- ここでメンバーの一人がビールの写真をツイートして,「もう出来たのか?」と海外勢から突っ込まれます
- もちろんまだできていませんでした
3日目
08:59 (JST) 最後の闘い
- 早めに起きて残作業実施
-
ギリギリに最後のfeatureをマージしてデプロイ!!
舞台裏
UIのプロトタイプ
ゲーム画面はHTML, CSS, JavaScriptで実装しました。以下はプロトタイプです。CSSグリッドをベースにして作りました。
See the Pen grid field demo by Sota Hatakeyama (@chooblarin) on CodePen.
Firestore
オンライン対戦ゲームを作る上で、Firestoreはとても便利でした。
クライアントの実装は、ユーザーのアクションに応じてサーバに操作(駒の移動、壁の配置)内容を投げつつ、状態はFirestoreから降ってくるものをただただSubscribeするというもので、キレイなCQRS (Command Query Responsibility Segregation)になります。
アプリケーション構成図にあるように、クライアントからサーバまでをひっくるめて一方向の矢印がくるっと円を描くような、とてもすっきりとした設計になりました。
RxFireという、RxJSのインターフェースで使用できるクライアントもオフィシャルに提供されており、とても使いやすいです。
import * as firebase from 'firebase/app';
import { fromDocRef } from 'rxfire/firestore';
import { Observable } from 'rxjs';
// 初期化
const app = firebase.initializeApp({ /* config */ });
const firestore = app.firestore();
/**
* ルームの情報のObserve
*/
export function observeRoom(roomId: string): Observable<Room> {
return fromDocRef(firestore.collection('rooms').doc(roomId)).pipe(
map(doc => doc.data())
);
}
非常に体験がよかったので、今後実務などでも取り入れていってみたいと思います。
ゲームロジック
Quoridorは完全情報ゲームなので「プレイヤー1が取った行動をプレイヤー2に対しては隠蔽する」といった考慮は必要ありません。そこで、FireStoreにはすべてのプレイヤーの行動履歴、すなわち行動オブジェクトの配列を保持する設計にしました。この設計にしておくと、リプレイの実装や不正な行動のValidationが簡単です。
主に実装したゲームロジックは行動履歴とターンを入力にして指定ターンの盤面オブジェクト返す関数、盤面オブジェクトを入力にして次に行使できる行動オブジェクトの配列を返す関数の2つです。今回はAPIサーバがNode.jsで実装されているのでクライアントとサーバの両方で単一のゲームロジックを使用することができます。
クライアントサイドでは前述の2つの関数を使用して行動履歴から現在の盤面を構成し、次の行動の一覧を取得することができます。サーバサイドでも同様に、プレイヤーのリクエストしたアクションをValidationすることが可能です。
新しい状態の即時反映
FireStoreの状態を正とする実装はかなり気に入っているのですが、個人的には一つ気になるところがありました。ユーザーがクライアントから行動をリクエストした際に、実際に行動が反映されて盤面が更新されるまでにタイムラグがあることです。
この問題への対応はいくつか考えられるのですが、今回は試しにFireStoreからSubscribeしているデータとローカルで作成した値のStreamをMergeしたものをローカル上での大本のStoreとして使用してみました。今回のケースでは使用上問題が起こりませんでしたが、色々なケースで試しながら最適な方法を模索していきたいです。
振り返り
今回はハッカソン開催の数日前に突然参加を決定し、Quoridorを作りたいという思いと勢いでアプリケーションを完成させました。やり残したことはいくつも思いつきますが、短期間でアプリケーションを完成までもっていくのは充実感にあふれていて楽しかったです。
最後に、カブクでは一緒にエキサイティングなことをするエンジニアを絶賛募集中です。興味があったら気軽に会社に遊びに来てください。
その他の記事
Other Articles
関連職種
Recruit