day19.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strconv"
  8. "strings"
  9. )
  10. type rule struct {
  11. value string
  12. mapping [][]int
  13. }
  14. var rules map[int]rule
  15. func init() {
  16. rules = make(map[int]rule)
  17. }
  18. func readRule(line string) {
  19. var newRule rule
  20. parts := strings.Split(line, ": ")
  21. if len(parts) != 2 {
  22. log.Fatalf("Invalid line: %s", line)
  23. }
  24. id, err := strconv.Atoi(parts[0])
  25. if err != nil {
  26. log.Fatalf("Error processing rule id for %s: %s", line, err)
  27. }
  28. var currentMapping []int
  29. for _, item := range strings.Split(parts[1], " ") {
  30. if strings.Contains(item, "\"") {
  31. newRule.value = strings.ReplaceAll(item, "\"", "")
  32. break
  33. }
  34. if item == "|" {
  35. newRule.mapping = append(newRule.mapping, currentMapping)
  36. currentMapping = []int{}
  37. continue
  38. }
  39. itemID, err := strconv.Atoi(item)
  40. if err != nil {
  41. log.Fatalf("Error processing id for %s: %s", item, err)
  42. }
  43. currentMapping = append(currentMapping, itemID)
  44. }
  45. newRule.mapping = append(newRule.mapping, currentMapping)
  46. rules[id] = newRule
  47. }
  48. var messages []string
  49. func readFile(file *os.File) {
  50. scanner := bufio.NewScanner(file)
  51. currentFunction := readRule
  52. changed := false
  53. for scanner.Scan() {
  54. line := scanner.Text()
  55. if line == "" {
  56. if changed {
  57. break
  58. }
  59. currentFunction = func(line string) { messages = append(messages, line) }
  60. changed = true
  61. continue
  62. }
  63. currentFunction(line)
  64. }
  65. if err := scanner.Err(); err != nil {
  66. log.Fatalf("Scanner error: %s", err)
  67. }
  68. }
  69. func buildStringsForRule(ruleID int, stringsSoFar []string) []string {
  70. var newStrings []string
  71. if rules[ruleID].value != "" {
  72. for _, item := range stringsSoFar {
  73. item += rules[ruleID].value
  74. newStrings = append(newStrings, item)
  75. }
  76. return newStrings
  77. }
  78. for _, item := range stringsSoFar {
  79. for _, array := range rules[ruleID].mapping {
  80. partial := []string{item}
  81. for _, id := range array {
  82. partial = buildStringsForRule(id, partial)
  83. }
  84. newStrings = append(newStrings, partial...)
  85. }
  86. }
  87. return newStrings
  88. }
  89. var notMatched []string
  90. func part1(validStrings []string) int {
  91. valid := 0
  92. for _, message := range messages {
  93. matched := false
  94. for _, item := range validStrings {
  95. if message == item {
  96. valid++
  97. matched = true
  98. break
  99. }
  100. }
  101. if !matched {
  102. notMatched = append(notMatched, message)
  103. }
  104. }
  105. return valid
  106. }
  107. func startsWith(toCheck []string, message string) bool {
  108. for _, item := range toCheck {
  109. if strings.HasPrefix(message, item) {
  110. return true
  111. }
  112. }
  113. return false
  114. }
  115. func endsWith(toCheck []string, message string) bool {
  116. for _, item := range toCheck {
  117. if strings.HasSuffix(message, item) {
  118. return true
  119. }
  120. }
  121. return false
  122. }
  123. func part2(max int) int {
  124. valid := 0
  125. thirtyOne := buildStringsForRule(31, []string{""})
  126. fortyTwo := buildStringsForRule(42, []string{""})
  127. length := len(thirtyOne[0])
  128. currentLength := 3 * length
  129. for len(notMatched) > 0 && currentLength <= max {
  130. currentLength += length
  131. var toCheck []string
  132. for _, message := range notMatched {
  133. if len(message)%currentLength == 0 {
  134. if startsWith(fortyTwo, message) && endsWith(thirtyOne, message) {
  135. valid++
  136. } else {
  137. toCheck = append(toCheck, message)
  138. }
  139. } else {
  140. toCheck = append(toCheck, message)
  141. }
  142. }
  143. notMatched = toCheck
  144. }
  145. return valid
  146. }
  147. func longest() int {
  148. max := 0
  149. for _, message := range notMatched {
  150. length := len(message)
  151. if length > max {
  152. max = length
  153. }
  154. }
  155. return max
  156. }
  157. func main() {
  158. if len(os.Args) < 2 {
  159. log.Fatal("You need to specify a file!")
  160. }
  161. filePath := os.Args[1]
  162. file, err := os.Open(filePath)
  163. if err != nil {
  164. log.Fatalf("Failed to open %s!\n", filePath)
  165. }
  166. readFile(file)
  167. if err := file.Close(); err != nil {
  168. log.Fatalf("Failed to close file: %s", err)
  169. }
  170. validStrings := buildStringsForRule(0, []string{""})
  171. resultPart1 := part1(validStrings)
  172. fmt.Println("Part1:", resultPart1)
  173. max := longest()
  174. fmt.Println("Part2:", resultPart1+part2(max))
  175. }