--- runDroid :: Droid -> Direction -> (Droid, ReturnValue)
--- runDroid droid direction = (droid', found)
--- where ci = _currentInput droid
--- droid' = runDroidMachine (droid {_currentInput = ci ++ [commandOf direction]})
--- found = returnValue $ last $ _machineOutput droid'
-
-
--- runDroidMachine :: Droid -> Droid
--- runDroidMachine d = d { _machine = machine'
--- , _executionState = halted
--- , _machineOutput = output
--- }
--- where machine = _machine d
--- input = _currentInput d
--- (halted, machine', output) = runMachine input machine
-
-
-showHull :: Hull -> String
-showHull screen = unlines rows
- where minX = minimum $ map fst $ M.keys screen
- minY = minimum $ map snd $ M.keys screen
- maxX = maximum $ map fst $ M.keys screen
- maxY = maximum $ map snd $ M.keys screen
- rows = [showHullRow screen minX maxX y | y <- [minY..maxY]]
-
-showHullRow :: Hull -> Integer -> Integer -> Integer -> String
-showHullRow screen minX maxX y = [showHullCell screen x y | x <- [minX..maxX]]
-
-showHullCell :: Hull -> Integer -> Integer -> Char
-showHullCell screen x y =
- case (M.findWithDefault Unknown (x, y) screen) of
- Empty _ _ True -> 'O'
- Empty _ _ _ -> '.'
- Wall -> '\x2588'
- Unknown -> ' '
+
+findRoutine :: ScaffoldBuilder -> Routine
+findRoutine scaff = head $ compressedCmds
+ where path = findPath scaff
+ cmds = toCommands path
+ compressedCmds = compress cmds
+
+encodeRoutine :: Routine -> [Integer]
+encodeRoutine (abc, a, b, c) = map (fromIntegral . ord) $ unlines [show abc, show a, show b, show c, "n", ""]
+
+
+findPath :: ScaffoldBuilder -> [Step]
+findPath = unfoldr takeStep
+
+takeStep :: ScaffoldBuilder -> Maybe (Step, ScaffoldBuilder)
+takeStep visitedScaffold = step
+ where scaff = _scaffold visitedScaffold
+ here = _droidPos visitedScaffold
+ dir = _droidDirection visitedScaffold
+ fPos = ahead here dir
+ cwPos = ahead here $ succW dir
+ acwPos = ahead here $ predW dir
+ step = if canVisit scaff fPos
+ then Just (F, visitedScaffold {_droidPos = fPos})
+ else if canVisit scaff cwPos
+ then Just (CW, visitedScaffold {_droidDirection = succW dir})
+ else if canVisit scaff acwPos
+ then Just (ACW, visitedScaffold {_droidDirection = predW dir})
+ else Nothing
+
+ahead :: Position -> Direction -> Position
+ahead (r, c) North = (r - 1, c)
+ahead (r, c) South = (r + 1, c)
+ahead (r, c) West = (r, c - 1)
+ahead (r, c) East = (r, c + 1)
+
+canVisit :: Scaffold -> Position -> Bool
+canVisit scaff here = (S.member here scaff)
+
+toCommands :: [Step] -> [Command]
+toCommands path = map toCommand segments
+ where segments = group path
+
+toCommand :: [Step] -> Command
+toCommand segment = case (head $ segment) of
+ F -> FN (length segment)
+ CW -> R
+ ACW -> L
+
+compress :: [Command] -> [Routine]
+compress commands =
+ do a <- tail $ inits commands
+ guard $ length (show a) <= 20
+ let commandsA = replace a A commands
+ let commandsABase = dropWhile (not . isBase) commandsA
+ b <- tail $ inits commandsABase
+ guard $ onlyBase b
+ guard $ length (show b) <= 20
+ let commandsAB = replace b B commandsA
+ let commandsABBase = dropWhile (not . isBase) commandsAB
+ c <- tail $ inits commandsABBase
+ guard $ onlyBase c
+ guard $ length (show c) <= 20
+ let commandsABC = replace c C commandsAB
+ guard $ length (show commandsABC) <= 20
+ guard $ onlyNonBase commandsABC
+ return (commandsABC, a, b, c)
+
+
+replace :: Eq a => [a] -> a -> [a] -> [a]
+-- replace moves label commands | trace (show moves ++ " " ++ show label ++ " " ++ show commands) False = undefined
+replace _ _ [] = []
+replace moves label commands =
+ if moves `isPrefixOf` commands
+ then (label:(replace moves label commands'))
+ else (head commands:(replace moves label (tail commands)))
+ where commands' = drop (length moves) commands
+
+onlyBase :: [Command] -> Bool
+onlyBase moves = all isBase moves
+
+onlyNonBase :: [Command] -> Bool
+onlyNonBase moves = all (not . isBase) moves
+
+isBase :: Command -> Bool
+isBase (FN _) = True
+isBase L = True
+isBase R = True
+isBase _ = False
+
+
+-- | a `succ` that wraps
+succW :: (Bounded a, Enum a, Eq a) => a -> a
+succW dir | dir == maxBound = minBound
+ | otherwise = succ dir
+
+-- | a `pred` that wraps
+predW :: (Bounded a, Enum a, Eq a) => a -> a
+predW dir | dir == minBound = maxBound
+ | otherwise = pred dir
+
+
+
+-- showScaffold :: VisitedScaffold -> String
+-- showScaffold scaff = unlines rows
+-- where minR = S.findMin $ S.map fst $ _scaffold scaff
+-- minC = S.findMin $ S.map snd $ _scaffold scaff
+-- maxR = S.findMax $ S.map fst $ _scaffold scaff
+-- maxC = S.findMax $ S.map snd $ _scaffold scaff
+-- rows = [showScaffoldRow scaff minC maxC r | r <- [minR..maxR]]
+
+-- showScaffoldRow :: VisitedScaffold -> Integer -> Integer -> Integer -> String
+-- showScaffoldRow scaff minC maxC r = [showScaffoldCell scaff r c | c <- [minC..maxC]]
+
+-- showScaffoldCell :: VisitedScaffold -> Integer -> Integer -> Char
+-- showScaffoldCell scaff r c =
+-- if S.member (r, c) (_scaffold scaff)
+-- then '#'
+-- else ' '
+