Could you please review the following code, and point out how I can make it cleaner, more idiomatic and easier to understand?
module Cabbage (
solve
) where
data Place = Here | There deriving (Eq, Show)
data Pos = Pos { cabb :: Place
, goat :: Place
, wolf :: Place
, farmer :: Place
} deriving (Eq, Show)
opp :: Place -> Place
opp Here = There
opp There = Here
valid :: Pos -> Bool
valid (Pos {cabb = c, goat = g, wolf = w, farmer = f}) = (c /= g && g /= w) || g == f
findMoves :: Pos -> [Pos]
findMoves pos@(Pos {cabb = c, goat = g, wolf = w, farmer = f}) =
filter valid $ moveCabb ++ moveGoat ++ moveWolf ++ moveFarmer where
moveCabb | c == f = [pos {cabb = opp c, farmer = opp f}] | otherwise = []
moveGoat | g == f = [pos {goat = opp g, farmer = opp f}] | otherwise = []
moveWolf | w == f = [pos {wolf = opp w, farmer = opp f}] | otherwise = []
moveFarmer = [pos {farmer = opp f}]
findSolution :: Pos -> Pos -> [Pos]
findSolution from to = head $ loop [[from]] where
loop pps = do ps <- pps
let moves = filter (flip notElem ps) $ findMoves $ head ps
if to `elem` moves
then return $ reverse $ to:ps
else loop $ map (:ps) moves
solve :: [Pos]
solve = findSolution (setAll Here) (setAll There) where
setAll x = Pos{ cabb = x, goat = x, wolf = x, farmer = x }
IMHO the findMoves function seems to be quite verbose, and the findSolutions function looks confusing.
Thank you!
data Pos = Pos {cabb, goat, wolf, farmer :: Place} deriving (Eq, Show). – Landei Jul 30 '11 at 17:19