204 lines
4.4 KiB
Go
204 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
func main() {
|
|
inp := h.StdinToStringSlice()
|
|
part1(inp)
|
|
fmt.Println()
|
|
part2(inp)
|
|
}
|
|
|
|
func parse(input []string) ([]*Part, map[string]*Workflow) {
|
|
var parts []*Part
|
|
workflows := make(map[string]*Workflow)
|
|
var parseParts bool
|
|
for i := range input {
|
|
if input[i] == "" {
|
|
parseParts = true
|
|
continue
|
|
}
|
|
if parseParts {
|
|
// Parse Parts
|
|
parts = append(parts, NewPart(input[i]))
|
|
} else {
|
|
// Parse Rules
|
|
w := NewWorkflow(input[i])
|
|
workflows[w.name] = w
|
|
}
|
|
}
|
|
// Add 'Accepted' Workflow
|
|
workflows["A"] = &Workflow{
|
|
name: "A",
|
|
process: func(p *Part, workflows map[string]*Workflow) bool {
|
|
return true
|
|
},
|
|
}
|
|
// Add 'Rejected' Workflow
|
|
workflows["R"] = &Workflow{
|
|
name: "R",
|
|
process: func(p *Part, workflows map[string]*Workflow) bool {
|
|
return false
|
|
},
|
|
}
|
|
return parts, workflows
|
|
}
|
|
|
|
func part1(input []string) {
|
|
parts, workflows := parse(input)
|
|
var total int
|
|
for i := range parts {
|
|
if workflows["in"].process(parts[i], workflows) {
|
|
total += parts[i].Value()
|
|
}
|
|
}
|
|
fmt.Println("# Part 1")
|
|
fmt.Println(total)
|
|
}
|
|
|
|
func part2(input []string) {
|
|
_, workflows := parse(input)
|
|
fmt.Println("# Part 2")
|
|
fmt.Println(findAcceptable(workflows, "in", map[byte]Range{'x': DefaultRange(), 'm': DefaultRange(), 'a': DefaultRange(), 's': DefaultRange()}))
|
|
}
|
|
|
|
func findAcceptable(workflows map[string]*Workflow, w string, ranges map[byte]Range) int {
|
|
var result int
|
|
if w == "R" {
|
|
return result
|
|
} else if w == "A" {
|
|
result = 1
|
|
for _, r := range ranges {
|
|
result *= r.end - r.start + 1
|
|
}
|
|
return result
|
|
}
|
|
currWrk := workflows[w]
|
|
for _, rule := range currWrk.rawRules {
|
|
updRanges := copyRangeMap(ranges)
|
|
if rule.cmp == '<' {
|
|
updRng := getRangeFor(updRanges, rule.cat)
|
|
updRng.end = rule.val - 1
|
|
updRanges[rule.cat] = updRng
|
|
upd := getRangeFor(ranges, rule.cat)
|
|
upd.start = rule.val
|
|
ranges[rule.cat] = upd
|
|
result += findAcceptable(workflows, rule.res, updRanges)
|
|
} else if rule.cmp == '>' {
|
|
updRng := getRangeFor(updRanges, rule.cat)
|
|
updRng.start = rule.val + 1
|
|
updRanges[rule.cat] = updRng
|
|
upd := getRangeFor(ranges, rule.cat)
|
|
upd.end = rule.val
|
|
ranges[rule.cat] = upd
|
|
result += findAcceptable(workflows, rule.res, updRanges)
|
|
} else {
|
|
result += findAcceptable(workflows, rule.res, ranges)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
func copyRangeMap(ranges map[byte]Range) map[byte]Range {
|
|
n := make(map[byte]Range)
|
|
for k, v := range ranges {
|
|
n[k] = v
|
|
}
|
|
return n
|
|
}
|
|
func getRangeFor(ranges map[byte]Range, cat byte) Range {
|
|
if v, ok := ranges[cat]; ok {
|
|
return v
|
|
}
|
|
fmt.Println(ranges)
|
|
fmt.Println("Requested:", string(cat))
|
|
panic("Range not found")
|
|
}
|
|
|
|
type Range struct {
|
|
start, end int
|
|
}
|
|
|
|
func DefaultRange() Range { return Range{start: 1, end: 4000} }
|
|
|
|
type Part struct {
|
|
categories map[byte]int
|
|
x, m, a, s int
|
|
}
|
|
|
|
func NewPart(input string) *Part {
|
|
p := Part{categories: make(map[byte]int)}
|
|
fmt.Sscanf(input, "{x=%d,m=%d,a=%d,s=%d}", &p.x, &p.m, &p.a, &p.s)
|
|
p.categories['x'] = p.x
|
|
p.categories['m'] = p.m
|
|
p.categories['a'] = p.a
|
|
p.categories['s'] = p.s
|
|
return &p
|
|
}
|
|
|
|
func (p *Part) Value() int { return p.x + p.m + p.a + p.s }
|
|
|
|
type Rule struct {
|
|
cat byte
|
|
cmp byte
|
|
val int
|
|
res string
|
|
}
|
|
|
|
func NewRule(input string) Rule {
|
|
t := strings.Index(input, ":")
|
|
if t >= 0 {
|
|
return Rule{
|
|
cat: input[0],
|
|
cmp: input[1],
|
|
val: h.Atoi(input[2:t]),
|
|
res: input[t+1:],
|
|
}
|
|
} else {
|
|
return Rule{res: input}
|
|
}
|
|
}
|
|
func (r Rule) String() string {
|
|
return fmt.Sprintf("%s%s%d:%s", string(r.cat), string(r.cmp), r.val, r.res)
|
|
}
|
|
|
|
type Workflow struct {
|
|
name string
|
|
rawRules []Rule
|
|
process func(*Part, map[string]*Workflow) bool
|
|
}
|
|
|
|
func NewWorkflow(input string) *Workflow {
|
|
start := strings.Index(input, "{")
|
|
w := Workflow{
|
|
name: input[:start],
|
|
}
|
|
rules := strings.Split(strings.Trim(input[start:], "{}"), ",")
|
|
for i := range rules {
|
|
w.rawRules = append(w.rawRules, NewRule(rules[i]))
|
|
}
|
|
w.process = func(p *Part, workflows map[string]*Workflow) bool {
|
|
for _, rule := range w.rawRules {
|
|
switch rule.cmp {
|
|
case '>':
|
|
if p.categories[rule.cat] > rule.val {
|
|
return workflows[rule.res].process(p, workflows)
|
|
}
|
|
case '<':
|
|
if p.categories[rule.cat] < rule.val {
|
|
return workflows[rule.res].process(p, workflows)
|
|
}
|
|
default:
|
|
return workflows[rule.res].process(p, workflows)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
return &w
|
|
}
|