2023 Day 19 Complete
This commit is contained in:
203
2023/day19/main.go
Normal file
203
2023/day19/main.go
Normal file
@@ -0,0 +1,203 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user