- import scala.collection.mutable.{Map => MMap}
- import scala.collection.mutable.{Set => MSet}
- case class Food(ingredients: Set[String], allergents: Set[String]) {
- // Ingredient suspectes per allergent
- def suspects: List[(String, Set[String])] = allergents.map(a => (a -> ingredients)).toList
- }
- object Food {
- def apply(line:String): Food = {
- val Array(in, al) = line.dropRight(1).split(" \\(contains ")
- Food(in.split(" ").toSet, al.split(", ").toSet)
- }
- }
- import Food._
- object Wrapper {
- val lines = scala.io.Source.fromFile("input").getLines.toArray
- def part1(foods: Array[Food]) = {
- val allergentSuspects = foods.flatMap(_.suspects)
- .groupBy(_._1) // group by allergen
- .mapValues(_.map(_._2) // project ingredients
- .reduce(_ intersect _)) // reduce ingredients suspected having the allergent
- val allIngredients = foods.flatMap(_.ingredients)
- val unifiedSuspects = allergentSuspects.values.reduce(_ union _)
- println(allIngredients.filterNot(unifiedSuspects).size)
- allergentSuspects
- }
- def part2(suspects: Map[String, Set[String]]) = {
- var allSuspects = suspects
- val dangerousMap = MMap[String, String]()
- var converged = false
- while(!converged){
- converged = true
- val singles = allSuspects.filter { case (_, ing) => ing.size == 1 }
- val dangerous = MSet[String]()
- singles.foreach { case (allergent, ingredient) =>
- val dangerousIng = ingredient.head
- dangerous.add(dangerousIng)
- dangerousMap(allergent) = dangerousIng
- allSuspects = allSuspects - allergent
- converged = false
- }
- allSuspects = allSuspects.mapValues(_ -- dangerous)
- }
- println(dangerousMap.toList.sortBy(_._1).map(_._2).mkString(","))
- }
- def solve() = {
- val foods = lines.map(Food.apply)
- val allergentSuspects = part1(foods)
- part2(allergentSuspects)
- }
- }
- Wrapper.solve()