Rubyでわかるマギカ世界の魔法少女システム論

まどか☆マギカから考えるメカニズムデザイン」も合わせてご参照ください。

class Hope
  attr_reader :value

  def initialize(value)
    @initial_value = value
    @value = value
    value_changed
  end

  def add(value)
    @value += value
    value_changed
  end

  def despair
    @value = - @initial_value
  end

  def value_changed=(block)
    @on_value_changed = block
  end

  def value_changed
    @on_value_changed.call(self) if @on_value_changed
  end
end

class Human
  def initialize
    h = Hope.new(10)
    h.value_changed = ->(hope) do
      check_hope
    end

    self.singleton_class.class_eval do
      define_method :hope do
        h
      end
    end
  end
  
  def check_hope
  end

  def to_s
    "Human:#{object_id}"
  end
end

class QB
  private_class_method :new
  
  def self.generate
    @qb ||= new
  end

  def sign(girl, contract)
    girl.singleton_class.class_eval do
      include Magica
    end
    contract.call(girl)
  end

  module Magica
    def use_magic
      hope.add(-1)
    end

    def give_hope(person, value)
      hope.add(-value)
      person.hope.add(value)
    end

    def check_hope
      become_witch unless hope.value > 0
    end

    def become_witch
      self.singleton_class.class_eval do
        include Witch
      end
      hope.despair
      freeze
    end

    def to_s
      "Magica:#{object_id}"
    end
  end

  module Witch
    def scatter_despair(person)
      person.add_hope(-person.hope.value)
    end

    def self.included(mod)
      mod.freeze
    end

    def to_s
      "Witch:#{object_id}"
    end
  end
end

qb = QB.generate

all_magica = []
100.times do
  magica = Human.new
  qb.sign(magica, ->(magica){})
  10.times{ magica.use_magic }
  all_magica << magica
end

madoka = Human.new
all_magica << madoka

qb.sign(madoka, ->(girl) do
  QB::Magica.class_eval do
    define_method :check_hope do
      if hope.value < 0
        girl.give_hope(self, -hope.value)
        freeze
      end
    end
  end

  all_magica.each do |m|
    if m.hope.value < 0
      girl.give_hope(m, -m.hope.value)
    end
  end
end)

少し補足

  1. singleton_classは特異クラスを返します
  2. includeは不可逆です
  3. QBはシングルトンパターンに従いました
  4. このプログラムは無限再帰します。