ECMAScript5を使ってSameGameを書いてみた

ECMAScript5の追加ArrayメソッドやObject.freeze/Object.createを使い、JavaScriptでSameGameを書いてみました。

意識した部分

  • ピュア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)どっちがいい?
  • さめがめは実装は数時間で作成できるので、設計演習にはちょうどいいかも。