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

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
}