code.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strconv"
  8. "strings"
  9. )
  10. type Node struct {
  11. isExpression bool
  12. greater bool
  13. id string
  14. value int
  15. wrong *Node
  16. right *Node
  17. }
  18. type Workflow struct {
  19. id string
  20. expression *Node
  21. }
  22. func parseExpression(text string) []Node {
  23. var nodes []Node
  24. if !strings.Contains(text, ",") {
  25. var node Node
  26. var parts []string
  27. if strings.Contains(text, ">") {
  28. node.greater = true
  29. parts = strings.Split(text, ">")
  30. } else if strings.Contains(text, "<") {
  31. parts = strings.Split(text, "<")
  32. } else {
  33. node.id = text
  34. nodes = append(nodes, node)
  35. return nodes
  36. }
  37. node.isExpression = true
  38. if len(parts) != 2 {
  39. log.Fatalf("Can't parse expression: %s", text)
  40. }
  41. node.id = parts[0]
  42. n, err := strconv.Atoi(parts[1])
  43. if err != nil {
  44. log.Fatalf("Can't parse a number %s: %s", parts[1], err)
  45. }
  46. node.value = n
  47. nodes = append(nodes, node)
  48. } else {
  49. parts := strings.Split(text, ",")
  50. if len(parts) != 2 {
  51. log.Fatalf("Wrong number of parts from: %s", text)
  52. }
  53. for i := range parts {
  54. if strings.ContainsAny(parts[i], "<>") {
  55. nodes = append(nodes, parseExpression(parts[i])...)
  56. } else {
  57. nodes = append(nodes, Node{id: parts[i]})
  58. }
  59. }
  60. }
  61. return nodes
  62. }
  63. func parseWorkflow(line string) Workflow {
  64. var workflow Workflow
  65. idAndExpression := strings.Split(line, "{")
  66. if len(idAndExpression) != 2 {
  67. log.Fatalf("Can't extract id and expression from: %s", line)
  68. }
  69. workflow.id = idAndExpression[0]
  70. expressions := strings.Split(strings.TrimRight(idAndExpression[1], "}"), ":")
  71. if len(expressions) < 2 {
  72. log.Fatalf("Can't extract expressions from: %s", idAndExpression[1])
  73. }
  74. first := parseExpression(expressions[0])
  75. if len(first) != 1 {
  76. log.Fatalf("First expression should be single one: %s", expressions[0])
  77. }
  78. workflow.expression = &first[0]
  79. current := workflow.expression
  80. for i := 1; i < len(expressions); i++ {
  81. ex := parseExpression(expressions[i])
  82. if len(ex) != 2 {
  83. log.Fatalf("Need two: %s", expressions[i])
  84. }
  85. current.right = &ex[0]
  86. if current.right.isExpression {
  87. current = current.right
  88. }
  89. current.wrong = &ex[1]
  90. if current.wrong.isExpression {
  91. current = current.wrong
  92. }
  93. }
  94. return workflow
  95. }
  96. type Rating struct {
  97. x, m, a, s int
  98. }
  99. func readInput(file *os.File) (map[string]Workflow, []Rating) {
  100. scanner := bufio.NewScanner(file)
  101. workflows := make(map[string]Workflow)
  102. var ratings []Rating
  103. readingRatings := false
  104. for scanner.Scan() {
  105. line := scanner.Text()
  106. if line == "" {
  107. if !readingRatings {
  108. readingRatings = true
  109. continue
  110. }
  111. break
  112. }
  113. if !readingRatings {
  114. workflow := parseWorkflow(line)
  115. workflows[workflow.id] = workflow
  116. } else {
  117. var rating Rating
  118. n, err := fmt.Sscanf(line, "{x=%d,m=%d,a=%d,s=%d}", &rating.x, &rating.m, &rating.a, &rating.s)
  119. if n != 4 || err != nil {
  120. log.Fatalf("Bad input for rating: %s\n%s", line, err)
  121. }
  122. ratings = append(ratings, rating)
  123. }
  124. }
  125. return workflows, ratings
  126. }
  127. func (n *Node) test(value int) *Node {
  128. if n.greater {
  129. if value > n.value {
  130. return n.right
  131. }
  132. } else {
  133. if value < n.value {
  134. return n.right
  135. }
  136. }
  137. return n.wrong
  138. }
  139. func sortRatings(workflows map[string]Workflow, ratings []Rating) ([]Rating, []Rating) {
  140. var accepted []Rating
  141. var rejected []Rating
  142. for i := range ratings {
  143. current := workflows["in"].expression
  144. for {
  145. if current == nil {
  146. break
  147. }
  148. if current.isExpression {
  149. switch current.id {
  150. case "x":
  151. current = current.test(ratings[i].x)
  152. case "m":
  153. current = current.test(ratings[i].m)
  154. case "a":
  155. current = current.test(ratings[i].a)
  156. case "s":
  157. current = current.test(ratings[i].s)
  158. default:
  159. fmt.Println(current.id, "No match!")
  160. }
  161. } else {
  162. if current.id == "A" {
  163. accepted = append(accepted, ratings[i])
  164. break
  165. } else if current.id == "R" {
  166. rejected = append(rejected, ratings[i])
  167. break
  168. } else {
  169. current = workflows[current.id].expression
  170. }
  171. }
  172. }
  173. }
  174. return accepted, rejected
  175. }
  176. func part1(accepted []Rating) int {
  177. var result int
  178. for i := range accepted {
  179. result += accepted[i].x + accepted[i].m + accepted[i].a + accepted[i].s
  180. }
  181. return result
  182. }
  183. func main() {
  184. if len(os.Args) < 2 {
  185. log.Fatal("You need to specify a file!")
  186. }
  187. filePath := os.Args[1]
  188. file, err := os.Open(filePath)
  189. if err != nil {
  190. log.Fatalf("Failed to open %s!\n", filePath)
  191. }
  192. workflows, ratings := readInput(file)
  193. accepted, _ := sortRatings(workflows, ratings)
  194. fmt.Println("Part1:", part1(accepted))
  195. }