兄弟の論理クイズ

30分プログラム、その231。兄弟の論理クイズリストモナドで解いてみる。

5人兄弟が年長順に並んXいます。
それぞれの背中にはOかXのマークが書かれています。

自分の背中のマークは見えませんが、自分より前の兄弟の背中は見えます。
1〜5がそれぞれ上から何番目の息子さんか当てましょう。

  1. Oが2つあるな。
  2. Xが2つ見える。
  3. 1と2がそういうなら、俺はXかな
  4. 俺の前はOだ
  5. 帰っていい?

使い方

*Main> main
[('5',O),('4',O),('1',X),('3',X),('2',O)]
[('5',O),('4',O),('1',X),('3',X),('2',X)]
[('3',X),('5',X),('2',O),('4',O),('1',O)]
[('5',X),('3',X),('2',O),('4',O),('1',O)]
[('3',X),('5',X),('2',O),('4',O),('1',X)]
[('5',X),('3',X),('2',O),('4',O),('1',X)]

さて、困ったことに解がひとつじゃない。出題ミスだー、と騒いでおこう。

ソースコード

import Control.Monad
import Data.List

data Mark = Maru | Peke deriving Eq
type Order = Int
type State = [Order]
type Person = (Order,Mark)

instance Show Mark where
    show Maru = "O"
    show Peke = "X"

person :: State->[ (Person,State) ]
person orders = do order <- orders
                   mark  <- [Maru,Peke]
                   return ((order,mark),delete order orders)

getFront :: Order -> Mark -> [Person] -> Int
getFront n mark = length . filter (\(i,m)-> i < n && m == mark)

assertA :: Person -> [Person] -> Bool
assertB :: Person -> [Person] -> Bool
assertC :: Person -> [Person] -> Bool
assertD :: Person -> [Person] -> Bool
assertE :: Person -> [Person] -> Bool

assertA (n,_) xs = 2 == (getFront n Maru xs)
assertB (n,_) xs = 2 == (getFront n Peke xs)

assertC (n,_) xs = 
    let guess =  do (a,b,c,d,e) <- makePeople
                    guard $ map fst [a,b,d,e] == map fst xs
                    guard $ fst c == n
                    guard $ assertA a [b,c,d,e]
                    guard $ assertB b [a,c,d,e]
                    return $ snd c == Peke
    in and guess

assertD (n,_) xs = case find (\(m,_)-> m+1 == n) xs of
                     Just (_,mark) -> mark == Maru
                     _             -> False

assertE _ _ = True

makePeople = do (a,s1) <- person [1..5]
                (b,s2) <- person s1
                (c,s3) <- person s2
                (d,s4) <- person s3
                (e,_ ) <- person s4
                return (a,b,c,d,e)

selva = do (a,b,c,d,e) <- makePeople
           guard $ assertA a [b,c,d,e]
           guard $ assertB b [a,c,d,e]
           guard $ assertC c [a,b,d,e]
           guard $ assertD d [a,b,c,e]
           guard $ assertE e [a,b,c,d]
           return [a,b,c,d,e]

main = let sort xs = sortBy (\((a,_),_) ((b,_),_)->compare a b) $ zip xs ['1'..]
           format xs = map (\((_,m),n)-> (n,m)) xs
       in mapM_ (print.format.sort) selva