{-# LANGUAGE InstanceSigs #-} import Common -- not to be confused with AStar.hs! that one was bugged :) import Data.Graph.AStar import Data.List.Split (splitOn) import Data.List (elemIndex) import Data.Maybe (fromJust, mapMaybe) import qualified Data.Map.Strict as M import Data.Map.Strict (Map, (!)) import qualified Data.HashSet as HS import Data.HashSet (HashSet) import Data.Hashable (Hashable (hash, hashWithSalt)) import Data.Bits data Material = Ore | Clay | Obsidian | Geode deriving (Eq, Show) newtype Robot = Robot Material deriving (Eq, Show) data Recipe = Recipe [(Int, Material)] Robot deriving (Eq, Show) type Blueprint = [Recipe] parseMaterial :: String -> Material parseMaterial "ore" = Ore parseMaterial "clay" = Clay parseMaterial "obsidian" = Obsidian parseMaterial "geode" = Geode parseMaterial _ = undefined parseRecipe :: String -> Maybe Recipe parseRecipe s = do let split = words s typeIndex <- elemIndex "Each" split recipeIndex <- elemIndex "costs" split let ingredients = map ((\[a, b] -> (read a, parseMaterial b)) . words . trim) $ splitOn "and" $ unwords $ drop (recipeIndex + 1) split let robotType = parseMaterial $ split !! (typeIndex + 1) return $ Recipe ingredients (Robot robotType) parse :: String -> [Recipe] parse s = mapMaybe parseRecipe recipes where recipes = map trim $ splitOn "." s newtype State = State (Map Material Int, Map Robot Int, Int) deriving (Eq) -- for Some reason hashable doesn't export these. Cool :) hashInt s x = (s * 16777619) `xor` x defaultHashWithSalt salt x = salt `hashInt` hash x instance Hashable State where hash :: State -> Int hash (State (materials, robots, minute)) = foldr hashInt minute (M.elems materials ++ M.elems robots) hashWithSalt :: Int -> State -> Int hashWithSalt = defaultHashWithSalt nextNodes :: Blueprint -> State -> HashSet State nextNodes recipes s = set where set = HS.fromList [s] main = interact $ show . map parse . lines