Programmer's Nightmare実装

ルールの拡張や改良が簡単なような設計を考える。

  • プログラムは何枚かのカードである。サブルーチンが入るとここがとたんにややこしくなる。とりあえずサブルーチンは機能が単にランダムなカードを追加するだけなのにルールが3倍くらい複雑になるので悪だと思っている。サブルーチンを考慮しなければ単純に「プログラムはカードのリストである」って言える。 Program :: [Card]
  • カードには機能がついている。機能は「発動したときに何が起きるか」「発動しなかったときに何が起こるか」のセット。現状では大部分のカードは「発動しなかったときには何も起こらない」だけど、サブルーチン的なものをもう一度ルールに取り入れる場合は「発動しない場合はプログラムカウンタを3進める」とかがあると便利だから。(if (yes-or-no-p "activate?") then-action else-action)って感じ。a Card has-a then-action. a Card has-a else-action.
  • カードには「置かれているビット」がある。Bits :: [Player]。a Card has-a Bits.
  • then-actionやelse-actionはおそらくコンテキストを受け取ってそれを参照したり更新したするだろう。参照する値としては現在のプログラムカウンタ、実行したユーザが誰か、etc
    • FUTUREはPC+1とPC+2の位置のカードのthen-actionを実行するthen-actionを持つカードになる。この場合Card[PC + 1] == GOTOの場合の挙動が今までのゲームでの説明とはちょっと違う。GOTOしてZAPの場合にGOTOでPCが移動するからZAPは発動しない、っていうのが今までの説明だったけども、GOTOの時には後続のアクションをキャンセルできる設計にするよりも「GOTOでPCが変更されようが次の命令はすでにフェッチ済なので実行されるのです」の方が実装がシンプル。
  • プレイヤーはライフポイントを持っている。ライフポイントが0になると死ぬが、0になったタイミングで死亡するのではない。相打ちの判定のためにはアクションの実行後にユーザのライフポイントを判定して勝利判定をしてゲームを停止したりする「ゲームマネージャ」が走る必要がある。死亡判定はそこにまかせるべき。ユーザオブジェクトにまかせると個々のユーザオブジェクトが他のユーザオブジェクトの残っているライフポイントを知る必要が出てきてよくない。a Player has-a lifepoint. lifepoint :: Int
  • INCREMENTやDECREMENTができるためには、カードは「変更可能な数値」を0個以上もつ必要がある。POWER_SURGE has [3, 1]。個々のカードインスタンスごとに持つ必要がある。カードはこの数値の初期値を持つ必要がある。
  • SKIPの振る舞いはルールブックでは明確に定義されていない。しかし今日のプレイである程度弱化が必要だなーと思ったので「SKIPはthen-actionで特定のカードのthen-actionを『何もしない』に一時的に書き換える」という実装がよいのではないかと思う。さて。正確には「then-actionを『then-actionをオリジナルのものに戻しPC++する』という関数に置き換える」になるのかな。
  • DEFENDとPOINTERは難しいなぁ
  • POINTERができるためには前回2回のアクションで誰が何ポイントのダメージを受けたかをキャッシュしておく必要がある
  • DEFENDができるためには「DEFENDが発動されてから何アクション経過したか」を保持する必要がある
  • ゲームマネージャーに「アクション実行後に呼ばれるイベントハンドラ」を登録できるようにする a GameManager has-a EventHandlers. EventHandlers :: [Function]
  • プレイヤーのライフポイントは直接増減されるのではなく、DEFENDがダメージ前にフックをかけて無効化できるようにしなければならない