Skip to content

Commit

Permalink
finished day 19
Browse files Browse the repository at this point in the history
  • Loading branch information
devries committed Dec 19, 2023
1 parent 2c78444 commit be05b62
Show file tree
Hide file tree
Showing 6 changed files with 616 additions and 2 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Advent of Code 2023

[![Tests](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml/badge.svg)](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml)
[![Stars: 36](https://img.shields.io/badge/⭐_Stars-36-yellow)](https://adventofcode.com/2023)
[![Stars: 38](https://img.shields.io/badge/⭐_Stars-38-yellow)](https://adventofcode.com/2023)

## Plan for This Year

Expand All @@ -12,6 +12,9 @@ debugger. Second, I want to try asking some generative AIs for helpful functions
to see how it improves my speed. I was considering Github copilot, but I just
can't give up my current editor, [helix](https://helix-editor.com/), to use
vscode, and I don't really want to go down that neovim plugin rabbit hole


[![Tests](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml/badge.svg)](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml)
anymore.

I may use codespaces a bit. I've added some permissions so that I can clone my
Expand Down Expand Up @@ -297,3 +300,12 @@ the third run of my solution after compilation on my Raspberry Pi.
finish the first part before work and had to wait to finish the second
part until after work. I think there has to be a more elegant way to express
what I was trying to express, but I didn't find it today.
- [Day 19: Lavaduct Lagoon](https://adventofcode.com/2023/day/19) - [⭐ part 1](day19p1/solution.go), [⭐ part 2](day19p2/solution.go)
Today's code was heavy with structures. One for the part, the part range,
the operation, the workflow, and the rule. It got a little complicated, but
it was just breaking things down and then evaluating the next step. For the
final summation, I would just split the available ranges up and perform the
rule action on the part that satisfied the rule, and move on with the part
that didn't.
191 changes: 191 additions & 0 deletions day19p1/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package day19p1

import (
"fmt"
"io"
"strconv"
"strings"

"aoc/utils"
)

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)
workflows := make(workflowMap)
partList := []part{}

startParts := false
for _, ln := range lines {
if ln == "" {
startParts = true
continue
}

if startParts {
p := parsePart(ln)
partList = append(partList, p)
} else {
name, instructions := parseWorkflow(ln)
workflows[name] = instructions
}
}

var sum int64
for _, p := range partList {
accepted := workflows.accept(p, "in")
if utils.Verbose {
fmt.Printf("%#v %t\n", p, accepted)
}
if accepted {
sum += p.x + p.m + p.a + p.s
}
}
return sum
}

type operation int

const (
none operation = iota
lessthan
greaterthan
)

type part struct {
x int64
m int64
a int64
s int64
}

func (p part) getAttribute(a string) int64 {
switch a {
case "x":
return p.x
case "m":
return p.m
case "a":
return p.a
case "s":
return p.s
default:
return 0
}
}

type rule struct {
op operation
attribute string
argument int64
result string
}

func (r rule) evaluate(p part) string {
input := p.getAttribute(r.attribute)
switch r.op {
case none:
return r.result
case lessthan:
if input < r.argument {
return r.result
}
case greaterthan:
if input > r.argument {
return r.result
}
default:
panic("unknown operation")
}

return ""
}

type workflowMap map[string][]rule

// Check if part is accepted starting with workflow start
func (w workflowMap) accept(p part, start string) bool {
instructions := w[start]

for _, i := range instructions {
result := i.evaluate(p)
switch result {
case "A":
return true
case "R":
return false
case "":
// do nothing and move on
default:
return w.accept(p, result)
}
}

panic("Finished workflow with no result")
}

func parseWorkflow(ln string) (string, []rule) {
rules := []rule{}

ln = strings.TrimSuffix(ln, "}")
parts := strings.Split(ln, "{")

name := parts[0]

ruleStatements := strings.Split(parts[1], ",")

for _, s := range ruleStatements {
ruleComponents := strings.Split(s, ":")

if len(ruleComponents) == 1 {
// This is a non rule
r := rule{none, "", 0, ruleComponents[0]}
rules = append(rules, r)
} else {
opRune := ruleComponents[0][1]
var op operation
switch opRune {
case '>':
op = greaterthan
case '<':
op = lessthan
default:
panic("unknown operation in parse")
}

arg, err := strconv.ParseInt(ruleComponents[0][2:], 10, 64)
utils.Check(err, "Unable to parse %s to integer", ruleComponents[0][2:])
r := rule{op, ruleComponents[0][:1], arg, ruleComponents[1]}
rules = append(rules, r)
}
}

return name, rules
}

func parsePart(ln string) part {
ln = strings.TrimSuffix(ln, "}")
ln = strings.TrimPrefix(ln, "{")

parts := strings.Split(ln, ",")

ret := part{}

for _, p := range parts {
sides := strings.Split(p, "=")
value, err := strconv.ParseInt(sides[1], 10, 64)
utils.Check(err, "unable to parse %s to integer", sides[1])

switch sides[0] {
case "x":
ret.x = value
case "m":
ret.m = value
case "a":
ret.a = value
case "s":
ret.s = value
}
}

return ret
}
49 changes: 49 additions & 0 deletions day19p1/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package day19p1

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `px{a<2006:qkq,m>2090:A,rfg}
pv{a>1716:R,A}
lnx{m>1548:A,A}
rfg{s<537:gd,x>2440:R,A}
qs{s>3448:A,lnx}
qkq{x<1416:A,crn}
crn{x>2662:A,R}
in{s<1351:px,qqz}
qqz{s>2770:qs,m<1801:hdj,R}
gd{a>3333:R,R}
hdj{m>838:A,pv}
{x=787,m=2655,a=1222,s=2876}
{x=1679,m=44,a=2067,s=496}
{x=2036,m=264,a=79,s=2244}
{x=2461,m=1339,a=466,s=291}
{x=2127,m=1623,a=2188,s=1013}`

func TestSolve(t *testing.T) {
tests := []struct {
input string
answer int64
}{
{testInput, 19114},
}

if testing.Verbose() {
utils.Verbose = true
}

for _, test := range tests {
r := strings.NewReader(test.input)

result := Solve(r).(int64)

if result != test.answer {
t.Errorf("Expected %d, got %d", test.answer, result)
}
}
}
Loading

0 comments on commit be05b62

Please # to comment.