X-Git-Url: https://git.njae.me.uk/?p=advent-of-code-20.git;a=blobdiff_plain;f=advent21%2Fsrc%2Fadvent21.hs;fp=advent21%2Fsrc%2Fadvent21.hs;h=1b2f418750fdf4632b478ae6201e0037cb30f301;hp=0000000000000000000000000000000000000000;hb=fd1a5d57031cffaa3ed70935690e5cf8a4bc6061;hpb=c124d58aa7efa3f25f0a82993059f183e22ddd3f

diff --git a/advent21/src/advent21.hs b/advent21/src/advent21.hs
new file mode 100644
index 0000000..1b2f418
--- /dev/null
+++ b/advent21/src/advent21.hs
@@ -0,0 +1,81 @@
+-- import Debug.Trace
+
+-- import Data.Text (Text)
+-- import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+
+import Data.Attoparsec.Text hiding (take)
+-- import Data.Attoparsec.Combinator
+-- import Control.Applicative
+-- import Control.Applicative.Combinators
+
+import qualified Data.Set as S
+import qualified Data.Map.Strict as M
+import Data.List
+import Control.Monad.CSP
+
+
+type Ingredient = String
+type Allergen = String
+data Food = Food 
+  { ingredients :: S.Set Ingredient
+  , allergens :: S.Set Allergen
+  } deriving (Show, Eq)
+
+type CandidateIngredients = M.Map Allergen (S.Set Ingredient)
+
+
+
+main :: IO ()
+main = 
+  do  text <- TIO.readFile "data/advent21.txt"
+      let foods = successfulParse text
+      let candidates = M.unionsWith S.intersection $ map allergenMap foods
+      -- print candidates
+      print $ part1 candidates foods
+      putStrLn $ part2 candidates
+
+
+part1 candidates foods = sum $ map countSafe foods
+  where possibleAllergens = S.unions $ M.elems candidates
+        countSafe food = S.size $ (ingredients food) `S.difference` possibleAllergens
+
+part2 candidates = intercalate "," $ map snd $ sortOn fst assignments
+  where assignments = knownAllergens candidates
+
+
+allergenMap :: Food -> CandidateIngredients
+allergenMap food = M.fromList $ S.toList $ S.map (, ingredients food) $ allergens food
+
+knownAllergens :: CandidateIngredients -> [(Allergen, Ingredient)]
+knownAllergens candidates = zip allergens assignedIngredients 
+  where 
+    (allergens, possibleIngredients) = unzip $ M.toList candidates
+    assignedIngredients = solveAllergens $ map S.toList possibleIngredients
+
+solveAllergens :: [[Ingredient]] -> [Ingredient]
+solveAllergens possibleIngredients = oneCSPSolution $ do
+  dvs <- mapM mkDV possibleIngredients
+  mapAllPairsM_ (constraint2 (/=)) dvs
+  return dvs
+
+mapAllPairsM_ :: Monad m => (a -> a -> m b) -> [a] -> m ()
+mapAllPairsM_ _f []     = return ()
+mapAllPairsM_ _f (_:[]) = return ()
+mapAllPairsM_  f (a:l)  = mapM_ (f a) l >> mapAllPairsM_ f l
+
+
+-- Parse the input file
+
+foodsP = foodP `sepBy` endOfLine
+foodP = Food <$> ingredientsP <* " (contains " <*> allergensP <* ")"
+
+ingredientsP = S.fromList <$> (many1 letter) `sepBy` (many1 space)
+allergensP = S.fromList <$> (many1 letter) `sepBy` (string ", ")
+
+
+-- successfulParse :: Text -> (Integer, [Maybe Integer])
+successfulParse input = 
+  case parseOnly foodsP input of
+    Left  _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+    Right foods -> foods