Node.js Knockout で Utility/Fun Prize を受賞するためにやったこと


<div class="alert alert-info text-center"><img src="https://qiita-image-store.s3.amazonaws.com/0/25109/d193e0b9-ca5c-3ba2-d5b9-34ee0456c356.png" width="16" height="18" alt=""> この記事は <a href="http://qiita.com/advent-calendar/2015/dwango">『ドワンゴ Advent Calendar 2015』</a>22日目の記事です。</div>

こんにちは。ドワンゴでエンジニアという立場から色んなことに関わらせてもらっている清水([@meso](https://twitter.com/meso))です。

直近では、エンジニアの[新卒採用](http://dwango.co.jp/saiyo/)・[中途採用](http://dwango.co.jp/recruit/)、[新人研修](http://ch.nicovideo.jp/dwango-engineer/blomaga/ar831445)、[エンジニア以外の社員へのプログラミング教育](http://kawango.hatenablog.com/entry/2015/06/13/165953)、[N高生へのプログラミング教育](http://ed.kadokawadwango.co.jp/attractive/program/program.html)、[プロハイハイ](http://www.vantan-hs.com/phh/)などにも関わっています。興味がある方がいましたら、直接お声がけください。

さて、本題です。2015年11月に開催された [Node.js Knockout](http://www.nodeknockout.com/) というハッカソンで、Utility/Fun Prize を受賞しました。

<blockquote class="twitter-tweet" lang="ja"><p lang="en" dir="ltr">Utility/Fun Winner goes to Node Rogue by <a href="https://twitter.com/meso">@meso</a> <a href="https://t.co/04wlNpkT99">https://t.co/04wlNpkT99</a></p>&mdash; Node Knockout (@nodeknockout) <a href="https://twitter.com/nodeknockout/status/666264787421020160">2015, 11月 16</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

[賞品](http://www.nodeknockout.com/prizes#prize-utility-fun)は Amazon 等のお好きなギフトカード 700 ドル[^4]や GitHub の1年間の Bronze プランなど、総額5000ドル分ぐらい。
本人もまさか受賞するとは思っていなかったので大変びっくりしたのですが、2010年から行われているこのイベントで日本人が受賞したのは初めてですので、後世のためにも少しでも知見を残せればと思い、この記事を書きました。

[^4]: わざわざ Amazon Japan で日本円でプレゼントしてくれました

## Node.js Knockout とは

まず、Node.js Knockout というイベントについて説明します。Node.js Knockout とは、2010年からだいたい毎年開催されてきた、Node.js をターゲットとしたものとしては世界最大のハッカソンです。昨年は開催されなかったので、現在までに5回開催されたことになります。[ルール](http://www.nodeknockout.com/rules)は大雑把に説明すると、以下の3点です（詳細はリンク先参照）。

1. Node.js を使うこと
2. 1-4名のチームで取り組むこと
3. 48時間で開発すること

他にも、コードは GitHub のどのリポジトリに push するとか、deploy 先はどこになる、とか色々細かいのもありますが、大きな目玉としては上記3点です。

また、オンラインで開催されるというのも特徴です。時間は毎年土曜日の 00:00 UTC スタートなので、日本時間では土曜の朝9時開始で月曜の朝9時まで、ということになります。月曜有休とれば楽に参加できますね。

### Node.js Knockout と僕

初めての開催となる2010年から5回全てに参加しています。2010年は100チーム/250名ほどの参加でしたが、恐らく日本からの参加は僕だけだったように見えました。Brendan Eich や John Resig などのビッグネームが審査員に名前を連ねていました。僕はソロで参加し「リアルタイム対戦ができるタイピングゲーム」を作りました。イベント自体は [TechCrunch の記事](http://jp.techcrunch.com/2010/09/01/20100901nodejs-knockout/)にもなりました。 

2011年は [TechCrunch の記事](http://jp.techcrunch.com/2011/09/10/20110909node-js-knockout-2011-winners-revealed/)によると、320チーム/700名ほどの参加だったみたいです。また、この年から「[Node.js 日本ユーザグループ](http://nodejs.jp/)」として参加者が集まれる東京 Camp を用意したこともあって、日本からの参加者も僕以外に数名増えたと記憶しています。この年は「見ているWebサイトをリアルタイムでのぞき見できる Chrome Extension とサービス」を作りました。

2012年は、東京 Camp を浜町にあった旧ドワンゴ社内のカフェテリアに場所を移して開催しました。参加人数等の記録は残っていませんが、前年とほぼ同じ規模だったと思います。僕は初めて会社の後輩とチームを組んで「Enchant.js を使ったマルチプレイ横スクロールアクションっぽいもの」を作りました。

2013年は、東京 Camp を歌舞伎座タワーのドワンゴ社内カフェテリアで行いました。規模は前年同様です。というか恐らくこの規模が開催できる最大規模なので、それ以上のチーム募集をしていない感じです。この年は、会社の後輩と3人でチームを組んで「Google Street View を用いたドライブ共有サービス」を作りました。

2014年はイベント自体が開催されなかったのですが、2015年には無事再開されました。2015年はおおよそ350チームほどの登録があったと記憶していますが、残念ながら現地 Camp 的なものの募集がありませんでした。僕は「[リアルタイムマルチプレイな Rogue-Like 迷路ゲーム](http://node-rogue.2015.nodeknockout.com/)」を作り、Utility/Fun Prize を受賞することができました。

## 受賞するためにしたこと

### 当日まで

#### 参加登録

まず、何はともあれ参加登録をしなければなりません。Node.js Knockout は日本での知名度はともかく、世界的にはかなりの人気イベントなので、参加募集が始まるとすぐ枠が埋まってしまいます。そのため、事前にメール登録しておくのと[公式ツイッター](https://twitter.com/nodeknockout)をフォローしておくことをおすすめします。

参加登録をする際には、チームを事前に組んで置く必要はありません。とりあえず1人が登録さえできれば、あとで他のチームメンバを追加することはいつでも可能です。

また、募集開始時の枠が埋まると、それ以降は1日一回数枠ずつ追加募集するという形式に切り替わります。最初の募集で溢れてしまった場合には、毎日更新時間に張り付けば数日で参加登録できると思います。

今回は、僕は1人チームで参加しました。前回までのチーム参加も、確実に作品のクオリティが上がるためとても良いのですが、48時間という時間を自分のペースで使いたいことや、初日である土曜日が偶然「[東京Node学園祭2015](http://nodefest.jp/2015/)」と被ったことから、個人での参加にしました。

#### アイデア出し

アイデア出しはとても重要です。僕は常日頃から、次のハッカソンで作りたいものが思いついたら ToDo 管理ツールに書き記すということを行っています。そのアイデアたちの中から、Node.js Knockout に相応しいものを選びブラシュアップする、という作業を前日までに行いました。ポイントは以下の2つです。

1. 48時間以内に完成するもの
2. 審査員に受けの良いもの

まあ、本来的には作りたい物を作ればいいと思うのですが、賞を狙いに行く場合にはやはりこの2つのポイントは重要です。

Node.js Knockout の場合、審査員によって以下の4つの観点から採点され、それぞれについて、一番得点の高かったチームがその賞を受賞します。

1. Innovation
2. Design
3. Utility/Fun
4. Completeness

また、一般参加者による投票もあり、最も多くの投票を集めたチームが Popularity Prize を受賞します。
そして、総合点で最も高いチームが Team Winner となり、ソロ参加で最も総合点が高い人が Solo Winner を受賞します。

1人で参加するとなった場合、僕にできるのは3と4で勝負することだと考えました（これは人によって違うと思います）。上であげた2つのポイントと組み合わせて考えた結果、今回の作品に決めました。

なぜ Rogue-Like なのかというと、とにかく審査員のほとんどは欧米の Geek であるということ、そして欧米の Geek はたいてい Rogue 好きだよねという僕の偏見による決め付けが理由です。また [rot.js](http://ondras.github.io/rot.js/hp/) というとても素晴らしいが知名度はそこまで高くない JavaScript ライブラリが存在していることも決め手になりました。

### 当日

#### 開始まで

日本時間的には前日の夜中あたりに、リポジトリどうしろとかデプロイ先どうしろっていう指示がきます。とりあえず今すぐサンプルのデプロイまで試せっていうメールがきていたため、このタイミングで試しました。今年はデプロイ先は [Modulus](https://modulus.io/) という PaaS でした。

寝て起きたのち東京Node学園祭の会場までいき、Ustream 配信スタッフ[^1]だったのでその準備をし終わったあたりで、Node.js Knockout 開始の時間となりました。

[^1]: ドワンゴ社員なのにニコ生じゃなくて Ustream 配信してました

#### 開始後〜東京Node学園祭終わりまで

配信作業をしつつ、クライアント側の実装を進めました。

15:30 ぐらいで、1人プレイの Rogue-like な迷路のクライアント側の実装はできたようです。それもこれも全て rot.js というライブラリのお陰です。

rot.js は、Rogue-Like を実装するのに必要なほぼすべての機能を備えたライブラリで、Map の自動生成はもちろん、装飾のついた文字の出力や視野の計算、経路探索やスケジューラーまで揃っています。このライブラリがなければ「48時間以内に完成するもの」というポイントを満たすことができなかったでしょう。

その後、マルチプレイ化するために Socket.IO を導入しました。マップの自動生成という Rogue-Like の特徴とマルチプレイを両立するために、前人未到の階層に到達したプレーヤーがいたらそのタイミングでその階層のマップを生成し、それ以降はその時生成されたマップを共有される、というルールにしました。そのため、サーバサイドでマップを生成し、そのマップ情報をクライアントに配信しようと考えていたのですが、マップ生成時の Seed さえ決まれば同じマップが生成できることが分かった[^2]ので、Seed だけクライアントに配信し各クライアント上でマップを生成することにしました。

[^2]: あとで必ずしもそうでないことに気がつくわけですが

また、ゲームオーバーになる要素がないとつまらないため、各階層の探索可能時間に時間制限を設けました。タイマーはサーバサイドで管理し、時間がくるとクライアントに通知してゲームオーバーと表示されます。とはいえまあ、クライアントサイドでの処理をいじればチートはし放題な状態ではあります。

#### 帰宅後〜2日目昼まで

幸いにも眠くなかったため、黙々と実装を進めました。rot.js の視野計算機能を使って、一度 discover した場所は表示したままにする処理や、ヒントを書き残せる機能などを実装したところで、一通り遊べる感じになったため他の環境での動作を確認してみました。

開発には Chrome on Mac を使っていました。Safari や Firefox だと問題ないのですが、Internet Explorer や Edge だと同じ Seed でも違うマップが生成されてしまうことにこのタイミングで気がつき、どうしようか悩んだのですがこの2つのブラウザを動作対象から外すことにしました。これらのブラウザからアクセスされたら対象外だというメッセージを表示するよう処理を入れました。

また、Windows と Mac ではフォントの違いによって同じ Chrome でも見た目にかなり差がでてしまっていたため、Web Font を使って同じ見た目になるよう工夫しました。ただし、Web Fontで指定したフォントを Canvas 上に描画する文字のフォントとして使う場合、

```html
<link href='https://fonts.googleapis.com/css?family=Cousine' rel='stylesheet' type='text/css'>
```

で読み込む記述だけだと、フォントの読み込み完了よりも前に Canvas の描画が行われてしまうことがあるらしく、指定したフォントで描画されないことがままありました。それを避けるために、ページ上の他のテキスト要素にWeb Fontで読み込んでいるフォント（この場合 `Cousine`）を指定することで、確実に読み込みが完了されるように工夫してやる必要がありました。

#### 2日目夜〜終わり

上の状態まできたところで、一旦睡眠を取りました。まあ、もうやりたいことはほとんどできていたので安心して眠りに付きました。夕食時にまた起きだして、ヒントを書き残す機能の XSS を潰したり、細かい見た目の修正等を行ったのち、一通り満足したので審査をしてもらうのに必要な準備をしてまた寝ました。起きたら終わってました。

### 当日以降

48時間以降はコードの修正は不可能なので（サーバの再起動とかは可能）、あとは変なバグとかが発生しないことを祈るだけでした。とりあえず動作は問題なさそうだったので、ツイッターやFacebookで宣伝したりしてました。

## なぜ受賞できたのか

結果受賞できたのですが、なぜ受賞できたのかというと、rot.js が素晴らしかったからとしかいえません。作者にお会いできる機会があればビールでも奢ろうと思います。

まあ、あとは、やはり Rogue-Like が好きだというコメントがいくつかいただけたことからも、狙いが上手くヒットしたとも言えると思います。[採点結果のページ](http://www.nodeknockout.com/teams/mesolabs)を見ると、Utility/Fun と Completeness が高めなのがまさに狙いどおりといった感じです。

また、本当は Utility/Fun のスコアが一番高かったのは別のチームだったのですが、そのチームがソロチームで最も総合点が高かったため Solo Winner になったおかげで、2位だった僕に Utility/Fun Prize が回ってきた、という幸運もあります。

## まとめ

来年もまた恐らく開催されると思うので、そのときにはもっと多くの方が日本から参加されるのを期待しています。また、ドワンゴ社内からも5人以上は参加していたのに全員ぼっちチームだった[^3]ので、是非来年はチーム組んで参加しましょうね。

[^3]: とてもドワンゴのエンジニアらしいと言えばそうなのですが