day21.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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 onList(item string, list []string) bool {
  80. for _, element := range list {
  81. if item == element {
  82. return true
  83. }
  84. }
  85. return false
  86. }
  87. func analyse(highestAllergens map[string][]string) []string {
  88. var foundIngredients []string
  89. found := 0
  90. target := len(highestAllergens)
  91. for found < target {
  92. currentAllergens := make(map[string][]string)
  93. for key, value := range highestAllergens {
  94. if len(value) == 1 {
  95. foundIngredients = append(foundIngredients, value[0])
  96. found++
  97. continue
  98. }
  99. var newList []string
  100. for _, item := range value {
  101. if onList(item, foundIngredients) {
  102. continue
  103. }
  104. newList = append(newList, item)
  105. }
  106. currentAllergens[key] = newList
  107. }
  108. highestAllergens = currentAllergens
  109. }
  110. return foundIngredients
  111. }
  112. func part1(foods []dish, badIngredients []string) int {
  113. sum := 0
  114. for _, food := range foods {
  115. for _, ingredient := range food.ingredients {
  116. if !onList(ingredient, badIngredients) {
  117. sum++
  118. }
  119. }
  120. }
  121. return sum
  122. }
  123. func main() {
  124. if len(os.Args) < 2 {
  125. log.Fatal("You need to specify a file!")
  126. }
  127. filePath := os.Args[1]
  128. file, err := os.Open(filePath)
  129. if err != nil {
  130. log.Fatalf("Failed to open %s!\n", filePath)
  131. }
  132. foods := readFile(file)
  133. if err := file.Close(); err != nil {
  134. log.Fatalf("Failed to close file: %s", err)
  135. }
  136. allergens := processFoods(foods)
  137. highestAllergens := findAllergicIngredients(allergens)
  138. badIngredients := analyse(highestAllergens)
  139. fmt.Println("Part1:", part1(foods, badIngredients))
  140. }