You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
3.3 KiB
Go
170 lines
3.3 KiB
Go
package day19
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type Rule struct {
|
|
category byte
|
|
operator byte
|
|
value int
|
|
action string
|
|
}
|
|
|
|
type Part struct {
|
|
xVal int
|
|
mVal int
|
|
aVal int
|
|
sVal int
|
|
}
|
|
|
|
type Workflow []Rule
|
|
|
|
func ParseWorkflows(lines []string) map[string]Workflow {
|
|
workflows := map[string]Workflow{}
|
|
var re1 = regexp.MustCompile(`^([a-z]+){(([^,}]+,)+)([a-zA-Z]+)}$`)
|
|
var re2 = regexp.MustCompile(`^([a-z]+)(<|>)([0-9]+):([a-zA-Z]+)$`)
|
|
|
|
for _, line := range lines {
|
|
matches1 := re1.FindStringSubmatch(strings.TrimSpace(line))
|
|
|
|
if len(matches1) != 5 {
|
|
log.Fatalf("Regexp for %s should only have 5 parts and got %d", line, len(matches1))
|
|
}
|
|
|
|
wfName := matches1[1]
|
|
wfExp := strings.TrimSuffix(matches1[2], ",")
|
|
wfLast := matches1[4]
|
|
|
|
expressions := strings.Split(wfExp, ",")
|
|
|
|
wf := Workflow{}
|
|
for _, exp := range expressions {
|
|
matches2 := re2.FindStringSubmatch(exp)
|
|
|
|
if len(matches2) != 5 {
|
|
log.Fatalf("Regexp for %s should only have 5 parts and got %d", line, len(matches1))
|
|
}
|
|
|
|
val, _ := strconv.Atoi(matches2[3])
|
|
|
|
rule := Rule{
|
|
category: matches2[1][0],
|
|
operator: matches2[2][0],
|
|
value: val,
|
|
action: matches2[4],
|
|
}
|
|
|
|
wf = append(wf, rule)
|
|
}
|
|
|
|
wf = append(wf, Rule{
|
|
category: 'F',
|
|
action: wfLast,
|
|
})
|
|
|
|
workflows[wfName] = wf
|
|
}
|
|
|
|
return workflows
|
|
}
|
|
|
|
func ParsParts(lines []string) []Part {
|
|
parts := []Part{}
|
|
|
|
var re = regexp.MustCompile(`^{x=([0-9]+),m=([0-9]+),a=([0-9]+),s=([0-9]+)}$`)
|
|
|
|
for _, line := range lines {
|
|
matches := re.FindStringSubmatch(strings.TrimSpace(line))
|
|
|
|
if len(matches) != 5 {
|
|
log.Fatalf("Regexp for %s should only have 5 parts and got %d", line, len(matches))
|
|
}
|
|
|
|
xVal, _ := strconv.Atoi(matches[1])
|
|
mVal, _ := strconv.Atoi(matches[2])
|
|
aVal, _ := strconv.Atoi(matches[3])
|
|
sVal, _ := strconv.Atoi(matches[4])
|
|
|
|
part := Part{xVal, mVal, aVal, sVal}
|
|
|
|
parts = append(parts, part)
|
|
}
|
|
|
|
return parts
|
|
}
|
|
|
|
func evalRule(partVal int, operator byte, ruleVal int, action string) string {
|
|
switch {
|
|
case operator == '<' && partVal < ruleVal:
|
|
return action
|
|
case operator == '>' && partVal > ruleVal:
|
|
return action
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func isAccepted(part Part, workflows map[string]Workflow) bool {
|
|
nextAction := "in"
|
|
|
|
for {
|
|
wf := workflows[nextAction]
|
|
|
|
nextAction = ""
|
|
|
|
for _, rule := range wf {
|
|
switch rule.category {
|
|
case 'F':
|
|
nextAction = rule.action
|
|
case 'x':
|
|
nextAction = evalRule(part.xVal, rule.operator, rule.value, rule.action)
|
|
case 'm':
|
|
nextAction = evalRule(part.mVal, rule.operator, rule.value, rule.action)
|
|
case 'a':
|
|
nextAction = evalRule(part.aVal, rule.operator, rule.value, rule.action)
|
|
case 's':
|
|
nextAction = evalRule(part.sVal, rule.operator, rule.value, rule.action)
|
|
}
|
|
|
|
if nextAction == "A" {
|
|
return true
|
|
}
|
|
if nextAction == "R" {
|
|
return false
|
|
}
|
|
if nextAction != "" {
|
|
break // move to next workflow
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func Part1(lines []string) int {
|
|
idx := 0
|
|
for i, line := range lines {
|
|
if strings.TrimSpace(line) == "" {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
Workflows := ParseWorkflows(lines[:idx])
|
|
parts := ParsParts(lines[idx+1:])
|
|
|
|
fmt.Println(len(Workflows))
|
|
fmt.Println(len(parts))
|
|
|
|
sum := 0
|
|
for _, part := range parts {
|
|
if isAccepted(part, Workflows) {
|
|
sum += part.aVal + part.mVal + part.sVal + part.xVal
|
|
}
|
|
}
|
|
|
|
return sum
|
|
}
|