ヤギ、キャベツ、狼

前回に続いて、抽象によるソフトウェア設計−Alloyではじめる形式手法− の練習問題を考える。



A.3.5 「ヤギ、キャベツ、狼」

一人の農夫が、一頭のヤギと、一玉のキャベツと、一匹の狼を川の向こう側に運びたい。しかし彼のボートには一度に一つしか載せるスペースがない。もしヤギをキャベツと一緒にしたまま農夫が離れると、ヤギがキャベツを食べてしまう。もしヤギと狼を一緒にしておくと、ヤギが食べられてしまう。彼はどうすべきか? Alloyで解け。
(ヒント: Alloyの標準ディストリビューションにはモジュールutil/orderingがあり、全順序を定義できる。)



util/orderingの使い方を知らなかったが、他のサンプルプログラムを見てわかった。firstとlastに最初と最後の状態を指定、ある状態から次の状態の変化はnextで指定すればよいらしい。
各状態で可能な農夫の動作は四通りある。(手ぶらで移動、三通りの荷物のどれかと一緒に移動)
nextの状態として、この四通りの状態を選言で並べればよい。

open util/ordering [S]
enum 岸 {こちら, あちら}

-- 状態
sig S {
  人: 岸,
  狼: 岸,
  山羊: 岸,
  キャベツ: 岸
} {
  -- 安全な状態の条件
  狼 != 山羊 or 人 = 狼
  山羊 != キャベツ or 人 = 山羊
}

one sig スタート extends S {} {
  人 = こちら
  狼 = こちら
  山羊 = こちら
  キャベツ = こちら
}
one sig ゴール extends S {} {
  人 = あちら
  狼 = あちら
  山羊 = あちら
  キャベツ = あちら
}

-- 境界条件
fact {
  first = スタート
  last = ゴール
}

-- 移動
fact move {
  all a: S, b: a.next {
    (b.人 != a.人) and (
      (b.狼 = a.狼 && b.山羊 = a.山羊 && b.キャベツ = a.キャベツ) ||
      (b.狼 != a.狼 && b.山羊 = a.山羊 && b.キャベツ = a.キャベツ) ||
      (b.狼 = a.狼 && b.山羊 != a.山羊 && b.キャベツ = a.キャベツ) ||
      (b.狼 = a.狼 && b.山羊 = a.山羊 && b.キャベツ != a.キャベツ)
    )
  }
}

pred show {}
run show for 8

パズル自体の答えは、川渡り問題に載っている。