全ての選出と交換に渡る全ての敵の組み合わせを計算 その2

  • エントリーの衝突判定が種族とアイテムの二つあるのは複雑なので、衝突判定はエントリーのIDだけによるものとする

という設定はそのままで、複数回の戦闘と戦闘ごとの交換を設定に入れて作ってみた。

実際にはありえない結果も混じる出力を出すプログラムを作るところまではさくっといけた。あとはありえない結果を除くプログラムを書けばいいのだけれど、それがなかなか難しく、まだできていない。

require "set"
require "pp"
require_relative "prng.rb"

NUM_ENTRIES = 30
NUM_STARTERS = 6
NUM_PARTY = 3
NUM_BATTLES = 7

def main
  starters = (0...NUM_STARTERS).to_a
  prng = PRNG.new(0)
  pp Counter.count(prng, starters)
end

class Counter
  def initialize(starters)
    @starters = starters
  end

  def self.count(prng, starters)
    new(starters).count(prng)
  end

  def count(prng)
    count0(prng, [], [])
  end

  Result = Struct.new(:prng, :enemies, :skipped)

  def count0(prng, enemies, skipped)
    if enemies.length == NUM_BATTLES
      return Set.new([Result.new(prng, enemies, skipped)])
    end
    prev_enemy = enemies.last || @starters
    maybe_players = @starters + enemies[0..-2].flatten
    surely_players = []
    results = OneEnemyCounter.count(prng, prev_enemy, maybe_players, surely_players)
    results.map {|result|
      count0(result.prng, enemies + [result.chosen], skipped + [result.skipped])
    }.inject(:+)
  end
end

class OneEnemyCounter
  def initialize(prev_enemy, maybe_players, surely_players)
    @prev_enemy = prev_enemy
    @maybe_players = maybe_players
    @surely_players = surely_players
  end

  def self.count(prng, prev_enemy, maybe_players, surely_players)
    new(prev_enemy, maybe_players, surely_players).count(prng)
  end

  def count(prng)
    count0(prng, @surely_players, [])
  end

  Result = Struct.new(:prng, :chosen, :skipped)

  # 戻り値の中の異なるResultオブジェクトは必ず異なるchosenになる
  def count0(prng, surely_players, chosen)
    if chosen.length == NUM_PARTY
       return Set.new([Result.new(prng, chosen, surely_players)])
    end
    prngp, x = prng.rand(NUM_ENTRIES)
    if (@prev_enemy + surely_players + chosen).include?(x)
      # 常にスキップ
      count0(prngp, surely_players, chosen)
    elsif not @maybe_players.include?(x) or surely_players.length == NUM_PARTY
      # 常に採用
      count0(prngp, surely_players, chosen + [x])
    else
      # プレイヤーがxを持っていてスキップする場合
      result1 = count0(prngp, surely_players + [x], chosen)
      # 採用する場合
      result2 = count0(prngp, surely_players, chosen + [x])

      result1 + result2
    end
  end
end

main()
  • PRNG#randは前回とは違って副作用のない関数になっている
  • maybe_playersとsurely_playersのplayersは複数形ではなくてplayer'sのつもり…
筆者: oupo (連絡先: oupo.nejiki@gmail.com)