JavaScriptでシェフィを実装する(イベントカード実装編(ユーティリティ系))


2014年 09月 03日

前回までのあらすじ

シェフィを実装しようと思い立った俺達はようやくひつじを増やすカードの実装を終えた。しかし実装すべきカードはまだいくつか残っている状態。果たして無事にゲームを実装し終えることができるのか。

《牧羊犬/Sheep Dog》

手札1枚を捨てる。

Discard one card from your hand.

ひつじを減らすお邪魔カードを捨てて被害を避ける為のカードです。
シンプルな効果なので実装も簡単です。
ただし、プレイする時に手札が《牧羊犬/Sheep Dog》1枚だけだった場合、
「捨てるものが無いので何も起きなかった」ことになりますから、
その点は注意が必要です。

cardHandlerTable['Sheep Dog'] = function (world, state) {
  if (world.hand.length == 0) {
    return [{
      description: 'No card in hand - nothing happened',
      gameTreePromise: S.delay(function () {
        return S.makeGameTree(world);
      })
    }];
  } else {
    return world.hand.map(function (c, i) {
      return {
        description: 'Discard ' + c.name,
        gameTreePromise: S.delay(function () {
          var wn = S.clone(world);
          S.discardX(wn, i);
          return S.makeGameTree(wn);
        })
      };
    });
  }
};

《対策ひつじ/Planning Sheep》

手札1枚を追放する。

Remove one card in your hand from the game.

《牧羊犬/Sheep Dog》の亜種です。
「捨てる」が「追放」になっただけなので実装の要領も一緒です。

cardHandlerTable['Planning Sheep'] = function (world, state) {  //{{{2
  if (world.hand.length == 0) {
    return [{
      description: 'No card to exile - nothing happened',
      gameTreePromise: S.delay(function () {
        return S.makeGameTree(world);
      })
    }];
  } else {
    return world.hand.map(function (c, i) {
      return {
        description: 'Exile ' + c.name,
        gameTreePromise: S.delay(function () {
          var wn = S.clone(world);
          S.exileX(wn, wn.hand, i);
          return S.makeGameTree(wn);
        })
      };
    });
  }
};

《万能ひつじ/All-purpose Sheep》

手札1枚を選び、その代わりとして使う。

Choose a card in your hand. Play this card in place of the card you chose.

手札を選ぶという点では《牧羊犬/Sheep Dog》と同じですが、問題はその効果です。
これは「選んだカードを手札からプレイしたものとして次の局面を列挙する」と考えれば良いでしょう。

基本ルールを実装した時に、
state{step: 'カード名'} の場合は「そのカードがプレイされた」という状況を表すことにしました。
よって、手札を選んだ後の処理はこれを再現すればOKということになります。
具体的には以下の通りです:

cardHandlerTable['All-purpose Sheep'] = function (world, state) {
  if (world.hand.length == 0) {
    return [{
      description: 'No card in hand - nothing happened',
      gameTreePromise: S.delay(function () {
        return S.makeGameTree(world);
      })
    }];
  } else {
    return world.hand.map(function (c, i) {
      return {
        description: 'Copy ' + c.name,
        gameTreePromise: S.delay(function () {
          return S.makeGameTree(world, {step: c.name});
        })
      };
    });
  }
};

《霊感/Inspiration》

山札を見て好きなカード1枚を手札にし、残りをよく切る。

Look through the deck and add one card of your choice to your hand, and then re-shuffle the deck.

最後に残ったカードはこれです。これまでに無いもののオンパレードです。とはいえ、

  • カードの選択はこれまでに散々実装してきました。選択元の領域が山札に変わったというだけです。
  • 「手札に加える」「山札をシャッフルする」は個々のカードの実装では触れていない事柄ではあるものの、
    以前基礎部分の実装をした時に類似する事柄は行っています。

という訳なので他のカードと比べて異色ではあるものの、実装が難しいというものではありません。

なお、処理経過を見せる為、「カードのサーチ」の後に「山札のシャッフル」のみの選択肢を提示することにします。

cardHandlerTable['Inspiration'] = function (world, state) {
  if (world.deck.length == 0) {
    return [{
      description: 'No card in deck - nothing happened',
      gameTreePromise: S.delay(function () {
        return S.makeGameTree(world);
      })
    }];
  } else if (state.searched === undefined) {
    return world.deck.map(function (c, i) {
      return {
        description: 'Put ' + c.name + ' into your hand',
        gameTreePromise: S.delay(function () {
          var wn = S.clone(world);
          wn.hand.push(wn.deck.splice(i, 1)[0]);
          return S.makeGameTree(wn, {step: state.step, searched: true});
        })
      };
    });
  } else {
    return [{
      description: 'Shuffle the deck',
      gameTreePromise: S.delay(function () {
        var wn = S.clone(world);
        shuffle(wn.deck);
        return S.makeGameTree(wn);
      })
    }];
  }
};

実際に遊んでみる

これで全てのイベントカードを実装し終えることができました。
早速遊んでみましょう!

ちゃんと全カードを実装した場合のゲームの様子

おー、カードが盤面を縦横無尽に行き来しています。何だかすごい。

が、論理的にはカードゲームの体を成しているものの、
やはりUIがしょぼいので全然エキサイティングではありませんね……

次回予告

という訳で次回はUI本格化編です。