-- !!! Brute force soln to a puzzle. Sent to us by Stephen Eldridge
module Main(main) where
data ItemType = Bono
| Edge
| Larry
| Adam
deriving (Eq, Ord, Enum)
data BankType = LeftBank
| RightBank
deriving Eq
data StateType = State {bonoPos :: BankType,
edgePos :: BankType,
larryPos :: BankType,
adamPos :: BankType}
deriving Eq
type History = [(Int, StateType)]
type Solutions = [History]
initialState, finalState :: StateType
initialState = State LeftBank LeftBank LeftBank LeftBank
finalState = State RightBank RightBank RightBank RightBank
position :: ItemType -> StateType -> BankType
position Bono = bonoPos
position Edge = edgePos
position Larry = larryPos
position Adam = adamPos
updateState :: StateType -> ItemType -> BankType -> StateType
updateState state Bono pos = state {bonoPos = pos}
updateState state Edge pos = state {edgePos = pos}
updateState state Larry pos = state {larryPos = pos}
updateState state Adam pos = state {adamPos = pos}
opposite :: BankType -> BankType
opposite LeftBank = RightBank
opposite RightBank = LeftBank
notSeen :: StateType -> History -> Bool
notSeen state = all (\(_, s) -> state /= s)
writeItem :: ItemType -> BankType -> ShowS
writeItem Bono LeftBank
= showString " Bono | |\n"
writeItem Edge LeftBank
= showString "The Edge | |\n"
writeItem Larry LeftBank
= showString " Larry | |\n"
writeItem Adam LeftBank
= showString " Adam | |\n"
writeItem Bono RightBank
= showString " | | Bono\n"
writeItem Edge RightBank
= showString " | | The Edge\n"
writeItem Larry RightBank
= showString " | | Larry\n"
writeItem Adam RightBank
= showString " | | Adam\n"
writeState :: StateType -> ShowS
writeState state
= showString "----------------------------------------\n"
. writeItem Bono (bonoPos state)
. writeItem Edge (edgePos state)
. writeItem Larry (larryPos state)
. writeItem Adam (adamPos state)
. showString "----------------------------------------\n"
totalTime :: History -> Int
totalTime ((time, _) : _) = time
writeHistory :: History -> ShowS
writeHistory [ ] = id
writeHistory history
= foldr
(\(time, state) acc ->
showString "Time: "
. shows (total - time)
. showChar '\n'
. writeState state . acc) id history
where
total = totalTime history
minSolutions :: Solutions -> Solutions
minSolutions [ ] = [ ]
minSolutions (history : next)
= reverse (minAcc (totalTime history) [history] next)
where
minAcc minSoFar mins [ ] = mins
minAcc minSoFar mins (history : next)
= case compare minSoFar total of
LT -> minAcc minSoFar mins next
EQ -> minAcc minSoFar (history : mins) next
GT -> minAcc total [history] next
where
total = totalTime history
writeSolutions :: Solutions -> Int -> ShowS
writeSolutions [ ] _ = id
writeSolutions (item : next) count
= showString "Solution " . shows count . showChar '\n'
. writeHistory item
. writeSolutions next (succ count)
u2times :: ItemType -> Int
u2times Bono = 10
u2times Edge = 5
u2times Larry = 2
u2times Adam = 1
transfer :: StateType -> StateType -> BankType -> Int -> History -> Solutions
-- We are trying to get from a legal state, source, to another legal
-- state and history tells us one way to do it in time countdown
-- starting from dest where the torch is at location.
-- If we find newDest from which we can get to dest in one step
-- we can find all the solutions recursively.
transfer source dest location countdown history
| source == dest
= [(countdown, dest) : history]
| otherwise
= moveOne ++ moveTwo
where
newHistory = (countdown, dest) : history
newLocation = opposite location
moveOne = concat
[transfer
source newDest
newLocation newTime newHistory
| item <- [Bono .. Adam],
position item dest == location,
let newDest = updateState dest item newLocation,
notSeen newDest history,
let newTime = countdown + u2times item]
moveTwo = concat
[transfer
source newDest
newLocation newTime newHistory
| i <- [Bono .. Larry],
j <- [succ i .. Adam],
position i dest == location &&
position j dest == location,
let
newDest
= updateState
(updateState dest i newLocation)
j newLocation,
notSeen newDest history,
let newTime = countdown + u2times i]
main :: IO ( )
main
= putStr (writeSolutions mins 1 "")
where
solutions
= transfer initialState finalState
RightBank 0 [ ]
mins = minSolutions solutions
|