day21.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strings"
  8. )
  9. type dish struct {
  10. ingredients []string
  11. allergens []string
  12. }
  13. func readDish(line string) dish {
  14. var food dish
  15. parts := strings.Split(line, " (contains ")
  16. if len(parts) != 2 {
  17. log.Fatalf("Invalid line: %s", line)
  18. }
  19. for _, ing := range strings.Split(parts[0], " ") {
  20. food.ingredients = append(food.ingredients, ing)
  21. }
  22. cleanedPart2 := strings.TrimSuffix(parts[1], ")")
  23. for _, allergen := range strings.Split(cleanedPart2, ", ") {
  24. food.allergens = append(food.allergens, allergen)
  25. }
  26. return food
  27. }
  28. func readFile(file *os.File) []dish {
  29. var foods []dish
  30. scanner := bufio.NewScanner(file)
  31. for scanner.Scan() {
  32. line := scanner.Text()
  33. if line == "" {
  34. break
  35. }
  36. foods = append(foods, readDish(line))
  37. }
  38. if err := scanner.Err(); err != nil {
  39. log.Fatalf("Scanner error: %s", err)
  40. }
  41. return foods
  42. }
  43. type alerg struct {
  44. count int
  45. ingredients map[string]int
  46. }
  47. func processFoods(foods []dish) map[string]alerg {
  48. allergensPossibleForIngredients := make(map[string]alerg)
  49. for _, food := range foods {
  50. for _, allergen := range food.allergens {
  51. var currentAllergen alerg
  52. if _, ok := allergensPossibleForIngredients[allergen]; !ok {
  53. currentAllergen = alerg{count: 0, ingredients: make(map[string]int)}
  54. } else {
  55. currentAllergen = allergensPossibleForIngredients[allergen]
  56. }
  57. currentAllergen.count++
  58. for _, ingredient := range food.ingredients {
  59. currentAllergen.ingredients[ingredient]++
  60. }
  61. allergensPossibleForIngredients[allergen] = currentAllergen
  62. }
  63. }
  64. return allergensPossibleForIngredients
  65. }
  66. func findAllergicIngredients(allergensPossibleForIngredients map[string]alerg) map[string][]string {
  67. highestAllergens := make(map[string][]string)
  68. for allergen, item := range allergensPossibleForIngredients {
  69. var highestIngredients []string
  70. for key, value := range item.ingredients {
  71. if value == item.count {
  72. highestIngredients = append(highestIngredients, key)
  73. }
  74. }
  75. highestAllergens[allergen] = highestIngredients
  76. }
  77. return highestAllergens
  78. }
  79. func analyse(highestAllergens map[string][]string) map[string]bool {
  80. foundIngredients := make(map[string]bool)
  81. found := 0
  82. target := len(highestAllergens)
  83. for found < target {
  84. currentAllergens := make(map[string][]string)
  85. for key, value := range highestAllergens {
  86. if len(value) == 1 {
  87. foundIngredients[value[0]] = true
  88. found++
  89. continue
  90. }
  91. var newList []string
  92. for _, item := range value {
  93. if foundIngredients[item] {
  94. continue
  95. }
  96. newList = append(newList, item)
  97. }
  98. currentAllergens[key] = newList
  99. }
  100. highestAllergens = currentAllergens
  101. }
  102. return foundIngredients
  103. }
  104. func part1(foods []dish, badIngredients map[string]bool) int {
  105. sum := 0
  106. for _, food := range foods {
  107. for _, ingredient := range food.ingredients {
  108. if !badIngredients[ingredient] {
  109. sum++
  110. }
  111. }
  112. }
  113. return sum
  114. }
  115. func main() {
  116. if len(os.Args) < 2 {
  117. log.Fatal("You need to specify a file!")
  118. }
  119. filePath := os.Args[1]
  120. file, err := os.Open(filePath)
  121. if err != nil {
  122. log.Fatalf("Failed to open %s!\n", filePath)
  123. }
  124. foods := readFile(file)
  125. if err := file.Close(); err != nil {
  126. log.Fatalf("Failed to close file: %s", err)
  127. }
  128. allergens := processFoods(foods)
  129. highestAllergens := findAllergicIngredients(allergens)
  130. badIngredients := analyse(highestAllergens)
  131. fmt.Println("Part1:", part1(foods, badIngredients))
  132. }