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 }