knockout.js が便利そう
Webアプリで MVVM やりたいなと思ったら、そのままのフレームワーク knockout.js を見つけたのでメモしておく。
MVVM が欲しくなった経緯
サーバー側でのWebページの生成にテンプレートエンジンを使う
これ無しに Webアプリ作るのは無いだろうと思うくらい普通に使う。
あと、最近は、jQuery がかっこいい。
jQuery とか jQueryUI を使ってページ遷移無しに動く Web ページを作る
これも最近は普通に見えるけど、作ろうとしてみると意外と手間がかかる。
動くだけならいいけど、サーバー上のデータを参照したり変更するときにとても手間がかかる。「jQuery には ajax のライブラリあるよ」とか言われても、変更されたり受診した時にビューを更新する処理をたくさん書くことになる。
リッチな機能が提供されている場合は変更結果の見た目も含めて完結した UI を提供してくれるやつも多くて、そういうやつだとあまり手間かからないのだけど、
結果を反映しないといけない部分については、変更したい部分をひとつひとつ把握して置き換える必要がある
「影響範囲をすべて調べて置き換えるコード書くのかよ!面倒だよ!」とか叫びたくなる。
変更したいのはデータだし、ビューに反映させるコードはサーバー側に置いてあるテンプレートに書いたよ。2度も書きたくないよ。
結局、あちこちでページ遷移してサーバー側のテンプレートエンジンに頼る実装になる。
クライアント側で使えるテンプレートエンジンあればいいんじゃないの?
と気づいたので探したらすぐに見つかった。むしろ使うのが一般的なムードさえあった。それでも、
テンプレートエンジン使っても影響範囲は把握しないといけない
テンプレートエンジン使っても更新時に見た目を更新する処理を挟む必要がある
うへぇ。面倒くさい。やっぱりページ遷移して済ませたくなる。
データを変更したら、それを必要とするビューが自動的に更新されてほしい
observer パターンで更新を通知して通知を受けたら自身が持ってるテンプレートで再描画できるモデルを javascript 上に作ればいいのかなー手間かかるなーと悶々とし始めた時に、ふと WPF の MVVM ( Model View ViewModel ) を思い出したので、 "javascript mvvm" とか検索し始めたら、今回の knockout.js が見つかった。
virtualenv
色んなモジュールをインストールする Python のバージョンとか、インストールの途中で何かしちゃった♪とかで、うまく動かなくなって最初から入れなおしたくなることがある。 そうすると、他の動いていたスクリプトとかが使えなくなって、がっかりする。
また、そうやって苦労して動くようになった環境が一度できてしまうと、そこから移動するのに億劫になってしまって、「動かなくなったらどうしよー」とか考えて、新しいものを試すのが面倒くさくなる。
virtualenv を使うとこの手の悩みが減る。
好きなバージョンの Python の実行環境を作ってくれて、その上にまっさらな状態から好きなライブラリを好きなだけインストールできる。仮想マシンと同じノリだけど、Python 限定で軽く試せるのがよさげ。
インストール
$ pip install virtualenv
仮想環境の作成
$ mkdir myproject $ cd myproject $ mkdir env $ python /usr/lib/python2.6/site-packages/virtualenv.py env
実行ファイルも含めて env 以下に全部まとまるので、まるごと消して作りなおすのも簡単。プロジェクトごとに作ってしまうのが良さそう。
仮想環境へ移行
$ cd myproject $ source ./env/bin/activate
必要なライブラリのインストール
$ pip install django $ pip install south
あとは、プロジェクトごとに必要なライブラリをポポイと適当にインストールしたら準備完了。
virtualenvwrapper
http://www.doughellmann.com/docs/virtualenvwrapper/ja/
virtualenv をさらにお手軽に使えるようにするラッパーとして、virtualenvwrapper ってのがあるらしい。今回は使う予定は無いので調べる気はないけど、複数の仮想環境の管理とか、仮想環境作成時にまとめて入れるパッケージを定義しておくこととかができるらしい。
[ゲーム] ラビリンスの彼方 攻略メモ
使える作戦の数があまりにも多くて、いつも何かを忘れているのでメモしておく。
細々とした指針
気をつけることは山ほどあって、いつも何か忘れている。
優先準備は状況によって変わる。基本的には、戦闘終了時の状態がより良くなるように選ぶ。
- 行動順
- 行動順が早い所で盾になるようにする
- 行動順が後ろの方にいる奴は盾になることを避ける
- 仲間を全員できるだけ近い行動順で動かす
- +xxx や ++xx の順番で行動出来るように調整する
- ガード
- 女の子の体力はできるだけ高く維持する
- 敵の攻撃はできるだけ多く仲間で盾になってガードする
- できるだけ複数属性でガードする
- 敵へのダメージ
- 敵を減らすことを優先する
- 敵にダメージを回復させない
- 属性変更や行動順へダメージを与えるやつを優先して倒す
- 放出されている属性を吸収する敵が居なくなるように倒す
- 戦闘終了時
- 終了時は女の子の魔法をできるだけ高く維持する
- 終了時はできるだけ行動順が小さい状態を維持する
- 仲間が死にそうな時
- 回復するチャンスが来るまで盾になる。行動順早めで、他の属性の仲間と一緒に。
今のところ、女の子の魔法と戦闘終了時は、全体を通して気をつけることが多く、効果も高い。
女の子の魔法に合わせて活動する
女の子の魔法は、属性も防御力も無視する。そして何より仲間の計算属性で倍々ゲームでダメージを増やせる。他の仲間が数回の攻撃で倒すところも一発で倒せるのでとても有効利用したい。
- 女の子の手番が来る前に弱点属性を連打できるようにする。敵に吸収されないように。
- 割り込まれないように盾になるのを繰り返して、敵を全員後方に持っていくのが簡単
- ++xx の順番になるように調節する
- rgbr のように放出3回、吸収3回のような攻撃に調節する
- 最大魔法になった後は、全員後方になることが多い
- 誰かが早い行動順のまま盾になれるようにする。でないと女の子が大ダメージを受ける。
- 誰かが手を抜くか、最後の攻撃を吸収だけを目的にして最弱で攻撃すれば良い。
- 少ない魔法ストックを使ってしまいそうなら、盾になって後で攻撃したほうが良い
最後の属性の吸収を忘れることが非常に多い。色を間違えることが多いのだけど、盾から復帰するときにうっかり敵に先を越されることも多くちょっと難しい。
戦闘終了時に行動順を早めておく
戦闘終了時に行動順を 0, 0, 1, 3 とかになるように調節する。次の戦闘で連続で先制攻撃を行うことが出来て、敵を減らしつつ回復することが出来るので、雑魚戦では優先したい。
1. 止めを刺すまでの仲間は、どんなに後ろでも良いので行動順を近い位置に揃えておく 0 0 50 51 53 2. 行動順の早い奴らは止めを刺す直前に盾になる 1(盾) 0 50 51 53 3. 行動順最小の仲間で止めを刺す(女の子でもOK) 1(盾) 50 50 51 53 4. 終了時 1(盾) 0 0 1 3
こうすると、戦闘終了時に次の仲間の行動順が 50 とかだったとしても 0 に進む。女の子がいつも 50 しか使わないので、揃えるのは最大でもこのあたりになる。ストックを使った魔法で一発で倒せそうなら仲間が後ろにいても問題なかったりする。
特殊な敵の対処方法
- 全属性吸収する奴
- 弱点攻撃と吸収を連打する順番で攻撃して直前で最弱攻撃で全部吸収すれば良い。女の子の魔法に頼るためなら別に吸収されても問題ない。
- 属性が変わる敵
- 順番に弱点属性を当てると吸収されないままダメージを与え続けられる。とても弱いので、早めに倒して頭数を減らす助けにする。
- 行動順にダメージを与える敵
- 鬱陶しいので最優先で倒す。複数人で盾になっているとまとめて遅れるので注意。
- こいつためだけに属性を偏らせてる
- 防御力が硬い奴、硬くなるやつ(甲羅付きの奴とか)
- 女の子の魔法ポイントは相手が硬くても貯まるので、自分でダメージを与えられなくても気にせず攻撃して、女の子に任せる。
最適な作戦の変化
進むにつれて敵の種類が変わったり、お互いの与えるダメージも追加される行動順も増えるので少しずつ必要な作戦が変わってくる。
- 攻撃力の増加(行動順が伸びる)
- 行動順序の調節が簡単になる
- 女の子の活動頻度が上がる(石投げが増える)
- 雑魚戦での女の子の攻撃倍率が無意味に高くなる
- 敵の攻撃力の増加
- 女の子へのダメージが致命的になりやすい。他の仲間も弱点属性だとやられやすい。
- 盾を欠かないようにする
- 属性的に弱い仲間を他の属性と一緒に盾にする
- 体力が最後まで 4000 位だったせいか、女の子に集中していた敵の攻撃が、4人に向いやすくなる。途中から女の子を盾で守るのではなく、4人を守るために女の子のところへ逃げこむようなバランスになってくる。
敵が使ってくる作戦
敵が適当に襲ってくるのではなく、よくあるいやらしい手段をきちんと使ってくる。
終盤は、行動パターンが分かりやすすぎた。
- 死にそうなやつを狙ってくる
- 当然のように体力の少ないやつを集中攻撃してくる
- 属性の相性が悪くても倒せるなら倒してくる
- 弱い属性のやつを狙う
- ダメージがでかくなるように狙ってくる
- こちらのターンになる前のダメージを最大化してくる
- 具体的には、こちらのターンが来る直前まで細かい攻撃を仕掛けて、最後に最大の攻撃をしかけてくる
- 弱いやつを狙う
- 最初は4人が狙われやすい
- 途中から女の子に集中する
- 攻撃力上がってきたところで4人がまた狙われるようになる
試していないこと
パラメータは基本的に平均的に上げていて不満は無いのだけど偏らせたらどうなったのだろうか。
- 攻撃力を特定の仲間に集中させる
- 全階層にわたって全属性が同時に出現するのに偏らせる意味はあるだろうか?
- 掛け算は、掛ける値の多さより、掛ける回数が多いほうがお得なので、一人だけ攻撃回数が違うのは面倒くさそう
- 体力を特定の仲間に集中させる
- 盾にしやすいかもしれない
- 他と体力のオーダーが違いすぎると回復が面倒かもしれない
- 女の子の回復優先
- 敵の攻撃時だけ盾を欠かないようにすれば、死にそうになること無いので試す気にならない
- マップの踏破
- 面倒くさくて上がれる所が合ったらさっさと上がっている。丁寧に回ってもアイテムしか無いよね?
[ゲーム] ラビリンスの彼方
超面白い。コマンドを選ぶタイプの戦闘が楽しいゲームは貴重だ。
女の子と迷宮を探索するムードを楽しむゲームに余計な戦闘がくっついたゲームなのかと思ったら、迷路も女の子もどうでも良くて、戦闘だけが面白いゲームだった。
- 装備品はなく、初期メンバーでひたすら進むだけ
- 迷宮と女の子について特に謎が解き明かされるわけでもなく、おしゃべりしながら進むだけ
- 鍵を拾ったりする程度の簡単な迷路をひたすら進むだけ
- その間、ずっと通り道にひたすら敵がいて倒し続ける
このゲームは、普通のRPGと比べると面白いとは言いがたい特徴を持ってる。どうしようも無いほど戦闘しか無い。でも、その戦闘が良くできていて、これだけで面白い。新しい種類の戦闘が発生するのを楽しみに進み続けられる。
自分が遊んだゲームの中で、回復することについて、ここまで頭を使って楽しめるゲームはなかった気がする。モンスターに高いダメージを与えることについては、ゼノサーガ2に近い。毎回の戦いで効率よく回復し、効率よくダメージを与えて、次の戦いを有利にするかを考えることができるのが良い。
- 敵の種類に合わせるとか
- 体力と攻撃力の増加に合わせるとか
- 戦闘状態を引き継ぐので、終了時の状態を調節するとか
特に盾を使えるようになってからの使える作戦の増えっぷりは半端無かった。盾になるかどうか選ぶだけなのに、最初は出来ることが多すぎて、しばらく混乱してた。効率的に戦おうと思うと、かなりの頻度で作戦の選択が必要になる。使える作戦の種類はとても多い。覚えきれなくて、ほとんどの戦闘では幾つかのテクニックを使い忘れてる。
それでも、実はそろそろ、新しい作戦が必要なくなってきて暇を感じ始めていたりする。使える作戦を慎重に選ぶ必要が無くなってる。
歩く速度がかなり遅いのは、少し鬱陶しい。鍵を拾って戻るときは、かなりだるい。
peg-sharp でインデント表現された木構造を読んでみる
Python や YAML は、インデントでツリーを表現しているけど、あれが簡単な BNF で書ける気がしなくて、どうやって書くのか気になっていたので書いて見ることにした。
試して分かったことというと、インデントを解釈するのにコンテキストを持たせる必要がありそうということくらい。
入力と出力
例えばこんな風に木構造を書く。
a aa aaa ab aba abb b ba
これを C# 側でこんなデータ構造で使いたいとする。
class Tree { public string Name; public Tree Parent; public List<Tree> ChildList = new List<Tree>(); public Tree(Tree parent, string name) { Parent = parent; Name = name; if (parent != null) { parent.ChildList.Add(this); } } public void Print(int level) { Console.WriteLine(new String(' ', level * 4) + Name); foreach (var tree in ChildList) { tree.Print(level + 1); } } }
何の面白みも無いのが残念だけど、PEG でどうやって書こうか考えてみたいだけなので問題なし。
peg-sharp のスクリプト
peg-sharp 側では、インデントをツリー構造に押しこむところは作らずに、インデントの数だけを処理するように作ってみた。
start = Start value = String namespace = IndentTree Start := LineSyntax (NewLine LineSyntax)*; LineSyntax := IndentedId / EmptyLine; IndentedId := Indent* Identifier `Context.AddTree(results.Count-1, results[results.Count-1].Text)` Indent := ' ' `;` `expected = "indent"` EmptyLine := Space*; Identifier := [a-zA-Z_] [a-zA-Z0-9_]* `value = null` `expected = "identifier"` Space := [ \t] `;` `expected = "whitespace"` NewLine := '\r'? '\n' `;` `expected = "newline"`
results ってので結果を扱わないといけないのがかなり分かりにくい。今回は各ノードはただの文字列として扱うからこの程度で良いけど、凝ったことを使用とすると分かりにくくなりそう。
ここで、IndentedId に登場しているコンテキストで今の木構造の状態を処理することにした。
コンテキスト
今、どこのインデントを処理しているのか表現するのに適当に List を使ってみた。
class Context { public List<Tree> TreeStack = new List<Tree>(); public Context() { TreeStack.Add(new Tree(null, "(root)")); } public void AddTree(int level, string name) { if (0 <= level && level < TreeStack.Count) { Tree tree = new Tree(TreeStack[level], name); TreeStack = TreeStack.Take(level+1).ToList(); TreeStack.Add(tree); } else if (level == TreeStack.Count) { Tree tree = new Tree(TreeStack[level], name); TreeStack.Add(tree); } else { throw new Exception("Illegal indentation"); } } public Tree GetRoot() { return TreeStack[0]; } }
思ったより複雑な書き方になっている気がして少しがっかり。
パーザクラスへコンテキストの追加
あとは、コンテキストをパーザに差し込んで、適当に表示するだけ。
internal sealed partial class IndentTree { Context Context = new Context(); public Tree GetTree() { return Context.GetRoot(); } }
出力された Parser クラスが partial クラスなので、追加のデータを別ファイルから追加することができる。便利だと思っってしまったけど、使い方としては間違っているような気がしなくもない。機能追加に partial クラスを使うってどうなんだ。
テスト
以下は、ツリーを表示するだけの簡単なコード。
static void Main(string[] args) { var parser = new IndentTree.IndentTree(); string testScript = @" a aa aaa ab aba abb b ba "; var result = parser.Parse(testScript); parser.GetTree().Print(0); }
peg-sharp
C# で スクリプトをパースしたくなったので、peg-sharp というのを試しに遊んで見ることにした。
http://code.google.com/p/peg-sharp/
OMeta とかに近いかな? 字句解析と構文解析を区別する必要がなくて、その場でデータを処理できる。前々からお手軽に使えそうだから使いたいなと思っていたのだけど、C# で使える実装を調べてなくて結局手を出してなかった。
C# での実装では IronMeta も候補にしていたのだけど、peg-sharp の方がお手軽そうな気配があったので peg-sharp を触ってみることにした。
http://ironmeta.sourceforge.net/
パースに失敗した時のエラー情報を自動で付けてくれる
この手のパーザを自前で作ったときには、エラー表示がとても面倒なのだけど、パースされた結果のオブジェクトにLineやColumnがついていたり、エラーが出た時に期待する構文を示した例外を投げてくれるのでありがたい。
こんな感じの例外を投げてくれる。
Expected indent or identifier or whitespace or newline at line 4 col 9.
型をひとつしか指定できない
パースした結果として返す値に全体で一つの型しか指定できない。数値も文字列もリストも何かしらの一つのクラスにまとめないと行けないのは苦行かもしれない。
もちろん抽象クラスを指定して、個々に生成する奴は異なる型に変更することは可能だけども、それを処理する時は結局、ダウンキャストとかしないと行けないのだろうか。
peg-sharp のドキュメントによると 「union ライクなクラス作ればなんとかなるよ」らしい。うーん。型付きが欲しい。
スペースの扱いが少し手間
字句解析と構文解析が分かれている場合は、スペースのカットを字句解析で済ませることができるのだけど、PEG ではそうもいかないようだ。でも、同じコード内に字句解析と構文解析を含めることができるだけで、書き分けはできる。スペースを許すトークンごとに S* な要素を挟めばよいようだ。
文字列しか扱えなさそう
構文解析ってデータに対してもやりたいことがあるから、そこが少し惜しい気がする。使う予定は無いので要らないのだけど。