ECMAScript5を使ってSameGameを書いてみた
ECMAScript5の追加ArrayメソッドやObject.freeze/Object.createを使い、JavaScriptでSameGameを書いてみました。
- 2D Canvasによるデモ: http://dl.dropbox.com/u/14499563/samegame/samegame-view.html
- ソース: https://gist.github.com/984605
意識した部分
- ピュアECMAScript5仕様の機能のみでゲームロジックを書く(HTMLの機能を使うUIは完全分離する)
- forループを使わず、forEachやmapを使う
- Object.createを適切に使用する
- ステップで盤面生成し副作用なしなので、すべてObject.freezeする。
for文を避けるため、Array(n).forEach(...)/map(...)は機能しない仕様(呼び出しても引数を実行しない)のようなので、range(n)関数を用意しています。Selected.pointsはArrayのindexOfを使うため、座標を"x,y"形式の文字列にして保持させています。
コード
ブコメを見るとどうも誤解されているようですが、1個のSelectedが可能なのはバグではないです。デモUIの初期値が全部消せる設定になっているのも、コードを最後まで走れる(isClearがtrueになるところまで)ようにするため。
1個のSelectedが成立する設計は意図的なものです(そもそも0個のSelectedだって可能だ)。Boardが上書きせず、別Boardを生み出す構造で設計であるのと同様、数判定を混ぜないのも工夫によるものです(ヒントはコヒージョン)。
追記: 設計をいくつか更新
- Selected.forEachをArray.forEachのように第二引数にコールバックのthisオブジェクトを設定できるようにした。
- Board.forEachを用意し、tableイテレートを抽象化し、実装でtableをパディングしないようにした
- UI部分も汎用化し、canvas要素ごとに結合できるようにした
- Viewオブジェクト定義でObject.createを使う
- Viewオブジェクトは、状態を更新するがプロパティ構成の変更はないのでObject.sealする
- イベントリスナー登録でFunction.bindを使う
- コード全体に"use strict"をいれた
- ES5では、オブジェクトリテラルの最後のプロパティのあとの","が使えるようになったのでいれておく
- インスタンスはそのままだとconstructorプロパティはObjectを返すので、constructorプロパティを上書きしておく
- Viewのパラメータを変えるUIをつけた。HTML5追加のinput要素のrangeとnumberを利用した。
- モジュール変数はトップレベルでthis.MyModule = ...のスタイルでセットするようにした。このスタイルで作れば、nodejsのrequire(jsfilename)でも読み込めるようになる。
感想
- ES5で拡張された関数によって、だいぶシンプルにコードが書ける。
- Arrayがここまできたら組値としてのtupleかstructもほしい。ペア値で、===で同値比較したい。パターン展開出来ればなおよし。
- Object.freeze()がメソッドでもなく字面上も長いので、値オブジェクトで必要部分をfreezeして回ると結構ごちゃごちゃする。
- Object.createでの定義で、privateメソッドを使いたい場合は、PImpl風になるのかな?
- Arrayのコールバックを渡すメソッドでは第二引数でthisを渡せる。forEach(cb.bind(this))とforEach(cb, this)どっちがいい?
- さめがめは実装は数時間で作成できるので、設計演習にはちょうどいいかも。
リソース
- http://www.ecmascript.org/docs/tc39-2009-043.pdf
- Object.create() - JavaScript | MDN
- Object.freeze() - JavaScript | MDN
- Array.prototype.map() - JavaScript | MDN
- Array.prototype.forEach() - JavaScript | MDN
- Array.prototype.filter() - JavaScript | MDN
- Array.prototype.every() - JavaScript | MDN
- Array.prototype.indexOf() - JavaScript | MDN
- Function.prototype.bind() - JavaScript | MDN
- さめがめ - Wikipedia