オオカミとヤギとキャベツを移動させるやつ

研究室の一部の人が uppaal で解こうとしていた問題。
オオカミとヤギとキャベツを対岸に移動させたいけど、オオカミとヤギ、あるいはヤギとキャベツを残したまま人が対岸に行ってはダメ、というやつ。
最初は prolog でやってやろうとしたけど、くじけて Haskell でお茶を濁しました。 Snow LeopardGHC が動かないのもあって、最近 Haskell のコードを書いてなかったからいいんです。
いつか、圏論を使って考えてみました!とか言いたい。(適当)

人とオオカミとヤギとキャベツの位置を Bool で表してます。Move0 は人が対岸に渡り、Move1 Move2 Move3 はそれぞれ、オオカミとヤギとキャベツが移動します。move 関数のガードは、人なしで勝手にキャベツなどが移動しないようにするために必要です。

import Control.Monad
import List

type Game = (Bool, Bool, Bool, Bool)
data Move = Move0 | Move1 | Move2 | Move3 deriving (Eq,Show)

move :: Move -> Game -> Maybe Game
move Move0 (h,x,y,z)        = Just (not h,x,y,z)
move Move1 (h,x,y,z) | h==x = Just (not h,not x,y,z)
move Move2 (h,x,y,z) | h==y = Just (not h,x,not y,z)
move Move3 (h,x,y,z) | h==z = Just (not h,x,y,not z)
move _     _                = Nothing

ok (True,False,False,_) = False
ok (True,_,False,False) = False
ok (False,True,True,_)  = False
ok (False,_,True,True)  = False
ok _                    = True

solved (_,False,False,False) = True
solved _                     = False

step :: (Game, [Move]) -> [(Game, [Move])]
step (state, ops) = do op <- [Move0,Move1,Move2,Move3]
                       case move op state of
                         Nothing     -> mzero
                         Just state' -> do guard (ok $ state')
                                           return (state', op:ops)

calc :: [(Game, [Move])] -> [Move]
calc states = let states' = concatMap step states in
              case find (solved . fst) states' of
                Nothing -> calc states'
                Just (_, moves) -> reverse moves

main = print $ calc [((True,True,True,True), [])]
$ runhaskell tmp.hs
[Move2,Move0,Move1,Move2,Move3,Move0,Move2]
$