#!/usr/bin/runhaskell
import Data.Maybe
average :: (Int, Int) -> (Int, Int) -> (Int, Int)
average (x, y) (x', y') = ((x + x') `div` 2, (y + y') `div` 2)
notBlocked :: [String] -> ((Int, Int), (Int, Int)) -> Bool
notBlocked maze (_, (x, y)) = ' ' == (maze !! y) !! x
substitute :: [a] -> Int -> a -> [a]
substitute orig pos el =
let (before, after) = splitAt pos orig
in before ++ [el] ++ tail after
draw :: [String] -> (Int, Int) -> [String]
draw maze (x,y) = substitute maze y $ substitute row x '*'
where row = maze !! y
tryMoves :: [String] -> (Int, Int) -> [((Int, Int), (Int, Int))] -> Maybe [String]
tryMoves _ _ [] = Nothing
tryMoves maze prevPos ((newPos,wallPos):more) =
case solve' maze newPos prevPos
of Nothing -> tryMoves maze prevPos more
Just maze' -> Just $ foldl draw maze' [newPos, wallPos]
solve' :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String]
solve' maze (2, 1) _ = Just maze
solve' maze pos@(x, y) prevPos =
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)]
notPrev pos' = pos' /= prevPos
newPositions' = filter notPrev newPositions
wallPositions = map (average pos) newPositions'
zipped = zip newPositions' wallPositions
legalMoves = filter (notBlocked maze) zipped
in tryMoves maze pos legalMoves
solve :: [String] -> Maybe [String]
solve maze = solve' (draw maze start) start (-1, -1)
where startx = length (head maze) - 3
starty = length maze - 2
start = (startx, starty)
main = interact main'
where main' x = unlines $ fromMaybe ["can't solve"] $ solve $ lines x