Ruby の AASM でズンドコキヨシ

  • 5
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

AASM は,オブジェクトに状態遷移の仕組みを簡単に持ち込むことができるライブラリーだ。

AASM の勉強を兼ねてズンドコキヨシを書いてみた。

参考:ズンドコキヨシまとめ

コード 1

最初に書いたのがこれ。

require 'aasm'

class Zundoko
  include AASM

  aasm do
    # ズンがまだ無い状態(初期状態)
    state :z0, initial: true
    # ズンが一つ溜まった状態(以下同様)
    state :z1
    state :z2
    state :z3
    # ズンが四つ以上溜まった状態
    state :z4
    # 完了状態
    state :completed

    # zun をもらったときの遷移規則
    event :zun do
      # z0 からは z1 へ(以下同様)
      transitions from: :z0, to: :z1
      transitions from: :z1, to: :z2
      transitions from: :z2, to: :z3
      # z3 と z4 からは z4 へ
      transitions from: [:z3, :z4], to: :z4
    end

    # doko をもらったときの遷移規則
    event :doko do
      # z0~z3 からは z0 へ
      transitions from: %i(z0 z1 z2 z3), to: :z0
      # z4 からは完了状態へ
      transitions from: :z4, to: :completed
    end
  end
end

zundoko = Zundoko.new

zundoko_strings = {zun: "ズン", doko: "ドコ"}

loop do
  m = zundoko_strings.keys.sample
  print zundoko_strings[m], " "
  zundoko.send m
  if zundoko.completed?
    puts "キヨシ"
    break
  end
  sleep 0.1
end

簡単のため,完了状態になったあとのことは考えてない。さらに続けられるようにするならもう少し複雑になる。

コード 2

コード 1 は,ズンが 0 回溜まった状態から 4 回以上溜まった状態までを別々の AASM 状態として管理しているのがイマイチ。仕様変更に弱そうだし,見通しが悪い。

いっそ状態は未完了・完了の二つだけにして,ズンの溜まった数をインスタンス変数で管理するのはどうか?

その場合,数によって遷移の仕方が変わるのをどう書くのか分からなくて,調べながら書いた。

できたのがこれ。

require 'aasm'

class Zundoko
  include AASM

  NECESSARY_ZUN_COUNT = 4

  def initialize
    @zun_count = 0
  end

  aasm do
    # 未完了状態(初期状態)
    state :running, initial: true

    # 完了状態
    state :completed

    # zun をもらったときの遷移規則
    event :zun do
      # 未完了状態のまま
      transitions from: :running, to: :running do
        # 遷移後にカウントアップ
        after{@zun_count += 1}
      end
    end

    # doko をもらったときの遷移規則
    event :doko do
      # ズンが十分溜まっていれば未完了状態から完了状態に遷移する
      transitions from: :running, to: :completed,
        guard: :adequate_zun?

      # 上記の規則に当てはまってなかったら未完了状態のまま
      transitions from: :running, to: :running do
        # 遷移後にカウンターをリセットする
        after{@zun_count = 0}
      end
    end
  end

  # 十分な数のズンが溜まっているか
  def adequate_zun?
    @zun_count >= NECESSARY_ZUN_COUNT
  end
end

zundoko = Zundoko.new

zundoko_strings = {zun: "ズン", doko: "ドコ"}

loop do
  m = zundoko_strings.keys.sample
  print zundoko_strings[m], " "
  zundoko.send m
  if zundoko.completed?
    puts "キヨシ"
    break
  end
  sleep 0.1
end

transition にはブロックを与えることができて,遷移の前や後に何かをやらせることができる。

また,現在の状態と遷移メソッドだけで遷移先を決めるのではなく,guard を使って条件を付けることができる。判定メソッドの名前をシンボルで与えればいい。

全部をクラスに詰め込むことも

コード 1 も 2 も,Zundoko クラスの役割は状態遷移に特化していて,ズン/ドコを与えることや画面表示などは外部で行っていた。

しかし,全部を Zundoko に詰め込んでもいい。