module Euler.Problems.Problem15
  ( answer
  , countPaths
  , countPaths'
  , P
  , Counts
  ) where
import Euler.Prelude
import qualified Data.Foldable as Foldable
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
answer :: Integer
answer = countPaths (20, 20)
countPaths :: P       
           -> Integer 
countPaths d = (Maybe.fromJust . Map.lookup d) (countPaths' Map.empty [d])
type P = (Integer, Integer)
type Counts = Map P Integer
countPaths'
  :: Counts 
  -> [P]    
  -> Counts 
countPaths' counts [] = counts
countPaths' counts stack@((x, y):restOfStack)
  
  | Map.member (x, y) counts = countPaths' counts restOfStack
  
  
  | x == 0 || y == 0 = countPaths' (Map.insert (x, y) 1 counts) restOfStack
  
  
  | Foldable.null unknownAdjacencies =
      let c = Foldable.sum $ mapMaybe (`Map.lookup` counts) adjacencies
      in  countPaths' (Map.insert (x, y) c counts) restOfStack
  
  | otherwise = countPaths' counts (unknownAdjacencies <> stack)
  where
    adjacencies = [(x - 1, y), (x, y - 1)]
    unknownAdjacencies = List.filter (`Map.notMember` counts) adjacencies