1. import scala.collection.mutable.{Map => MMap}
  2. import scala.collection.mutable.{Set => MSet}
  3. case class Food(ingredients: Set[String], allergents: Set[String]) {
  4. // Ingredient suspectes per allergent
  5. def suspects: List[(String, Set[String])] = allergents.map(a => (a -> ingredients)).toList
  6. }
  7. object Food {
  8. def apply(line:String): Food = {
  9. val Array(in, al) = line.dropRight(1).split(" \\(contains ")
  10. Food(in.split(" ").toSet, al.split(", ").toSet)
  11. }
  12. }
  13. import Food._
  14. object Wrapper {
  15. val lines = scala.io.Source.fromFile("input").getLines.toArray
  16. def part1(foods: Array[Food]) = {
  17. val allergentSuspects = foods.flatMap(_.suspects)
  18. .groupBy(_._1) // group by allergen
  19. .mapValues(_.map(_._2) // project ingredients
  20. .reduce(_ intersect _)) // reduce ingredients suspected having the allergent
  21. val allIngredients = foods.flatMap(_.ingredients)
  22. val unifiedSuspects = allergentSuspects.values.reduce(_ union _)
  23. println(allIngredients.filterNot(unifiedSuspects).size)
  24. allergentSuspects
  25. }
  26. def part2(suspects: Map[String, Set[String]]) = {
  27. var allSuspects = suspects
  28. val dangerousMap = MMap[String, String]()
  29. var converged = false
  30. while(!converged){
  31. converged = true
  32. val singles = allSuspects.filter { case (_, ing) => ing.size == 1 }
  33. val dangerous = MSet[String]()
  34. singles.foreach { case (allergent, ingredient) =>
  35. val dangerousIng = ingredient.head
  36. dangerous.add(dangerousIng)
  37. dangerousMap(allergent) = dangerousIng
  38. allSuspects = allSuspects - allergent
  39. converged = false
  40. }
  41. allSuspects = allSuspects.mapValues(_ -- dangerous)
  42. }
  43. println(dangerousMap.toList.sortBy(_._1).map(_._2).mkString(","))
  44. }
  45. def solve() = {
  46. val foods = lines.map(Food.apply)
  47. val allergentSuspects = part1(foods)
  48. part2(allergentSuspects)
  49. }
  50. }
  51. Wrapper.solve()