day21.go 3.9 KB

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