Skip to content

Commit d5296c2

Browse files
authored
GOOD GRIEF I SUCK AT THIS
1 parent 773b8db commit d5296c2

File tree

4 files changed

+1072
-3
lines changed

4 files changed

+1072
-3
lines changed

2020/.vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"mode": "auto",
1212
"program": "${workspaceRoot}/main.go",
1313
"env": {},
14-
"args": ["16"]
14+
"args": ["19"]
1515
}
1616
]
1717
}

2020/day19/day19.go

+299-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,304 @@
11
package day19
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
)
48

59
func Run(lines []string) error {
6-
return fmt.Errorf("not yet implemented")
10+
rulesParsed := false
11+
rules := map[int]Rule{}
12+
inputs := []string{}
13+
for _, line := range lines {
14+
if rulesParsed {
15+
inputs = append(inputs, line)
16+
} else if len(line) == 0 {
17+
rulesParsed = true
18+
} else {
19+
rule, err := ParseRule(line)
20+
if err != nil {
21+
return fmt.Errorf("error parsing rule '%s': %v", line, err)
22+
}
23+
rules[rule.Id] = rule
24+
}
25+
}
26+
27+
matchCount := 0
28+
rule0 := rules[0]
29+
for _, input := range inputs {
30+
result, _ := rule0.Matches(input, rules)
31+
if result == len(input) {
32+
matchCount++
33+
}
34+
}
35+
fmt.Println("Part 1:", matchCount)
36+
37+
matchCount = 0
38+
for idx, input := range inputs {
39+
remaining := MatchPart2(rules, input)
40+
fmt.Println("Processed line", idx)
41+
if len(remaining) == 0 {
42+
matchCount++
43+
}
44+
}
45+
fmt.Println("Part 2:", matchCount)
46+
47+
return nil
48+
}
49+
50+
type Rule struct {
51+
Id int
52+
expressions []RuleExpression
53+
}
54+
55+
func NewRule(id int, expressions ...RuleExpression) Rule {
56+
return Rule{id, expressions}
57+
}
58+
59+
func (r *Rule) Matches(input string, allRules map[int]Rule) (int, string) {
60+
bestLength := 0
61+
bestRemaining := input
62+
for _, expr := range r.expressions {
63+
match, remaining := expr.Matches(input, allRules)
64+
if match > bestLength {
65+
bestLength = match
66+
bestRemaining = remaining
67+
}
68+
}
69+
return bestLength, bestRemaining
70+
}
71+
72+
func (r *Rule) Stringify(allRules map[int]Rule) []string {
73+
results := []string{}
74+
for _, expr := range r.expressions {
75+
for _, result := range expr.Stringify(allRules) {
76+
results = append(results, result)
77+
}
78+
}
79+
return results
80+
}
81+
82+
type RuleExpression interface {
83+
Matches(input string, allRules map[int]Rule) (int, string)
84+
Stringify(allRules map[int]Rule) []string
85+
}
86+
87+
type FuncRule func(string, map[int]Rule) (int, string)
88+
89+
func (f FuncRule) Matches(input string, allRules map[int]Rule) (int, string) {
90+
return f(input, allRules)
91+
}
92+
93+
func (f FuncRule) Stringify(allRules map[int]Rule) []string {
94+
return []string{fmt.Sprintf("[fn:%v]", f)}
95+
}
96+
97+
type LiteralRule struct {
98+
value string
99+
}
100+
101+
func (s *LiteralRule) Matches(input string, allRules map[int]Rule) (int, string) {
102+
if len(s.value) <= len(input) {
103+
matches := input[:len(s.value)] == s.value
104+
if matches {
105+
return len(s.value), input[len(s.value):]
106+
}
107+
}
108+
return 0, input
109+
}
110+
111+
func (s *LiteralRule) Stringify(allRules map[int]Rule) []string {
112+
return []string{s.value}
113+
}
114+
115+
type RuleSequence struct {
116+
rules []int
117+
}
118+
119+
func (s *RuleSequence) Matches(input string, allRules map[int]Rule) (int, string) {
120+
remaining := input
121+
matchLength := 0
122+
for _, ruleID := range s.rules {
123+
rule := allRules[ruleID]
124+
125+
var match int
126+
match, remaining = rule.Matches(remaining, allRules)
127+
if match == 0 {
128+
return 0, input
129+
}
130+
matchLength += match
131+
}
132+
return matchLength, remaining
133+
}
134+
135+
func (s *RuleSequence) Stringify(allRules map[int]Rule) []string {
136+
firstRule := allRules[s.rules[0]]
137+
results := firstRule.Stringify(allRules)
138+
for i := 1; i < len(s.rules); i++ {
139+
newResults := []string{}
140+
rule := allRules[s.rules[i]]
141+
for _, outResult := range rule.Stringify(allRules) {
142+
for _, inResult := range results {
143+
newResults = append(newResults, fmt.Sprint(inResult, outResult))
144+
}
145+
}
146+
results = newResults
147+
}
148+
return results
149+
}
150+
151+
func ParseRules(lines []string) (map[int]Rule, error) {
152+
rules := map[int]Rule{}
153+
for _, line := range lines {
154+
rule, err := ParseRule(line)
155+
if err != nil {
156+
return nil, err
157+
}
158+
rules[rule.Id] = rule
159+
}
160+
return rules, nil
161+
}
162+
163+
func ParseRule(line string) (Rule, error) {
164+
colonIdx := strings.Index(line, ":")
165+
if colonIdx < 0 {
166+
return Rule{}, fmt.Errorf("invalid rule: '%s'", line)
167+
}
168+
id, err := strconv.Atoi(line[:colonIdx])
169+
if err != nil {
170+
return Rule{}, fmt.Errorf("invalid id: %s", line[:colonIdx])
171+
}
172+
173+
exprstrs := strings.Split(line[colonIdx+1:], "|")
174+
expressions := []RuleExpression{}
175+
for _, exprstr := range exprstrs {
176+
exprstr = strings.TrimSpace(exprstr)
177+
178+
var expr RuleExpression
179+
if exprstr[0] == '"' {
180+
expr, err = ParseLiteral(exprstr)
181+
if err != nil {
182+
return Rule{}, err
183+
}
184+
} else {
185+
expr, err = ParseRuleSequence(exprstr)
186+
if err != nil {
187+
return Rule{}, err
188+
}
189+
}
190+
expressions = append(expressions, expr)
191+
}
192+
193+
return Rule{id, expressions}, nil
194+
}
195+
196+
func ParseLiteral(lit string) (*LiteralRule, error) {
197+
return &LiteralRule{lit[1 : len(lit)-1]}, nil
198+
}
199+
200+
func ParseRuleSequence(seq string) (*RuleSequence, error) {
201+
ids := []int{}
202+
for _, idStr := range strings.Split(seq, " ") {
203+
id, err := strconv.Atoi(idStr)
204+
if err != nil {
205+
return nil, fmt.Errorf("invalid id: %s", idStr)
206+
}
207+
ids = append(ids, id)
208+
}
209+
return &RuleSequence{ids}, nil
210+
}
211+
212+
func MatchPart2(rules map[int]Rule, input string) string {
213+
rule0 := &Part2Rule{42, 42, 31}
214+
_, remaining := rule0.Matches(input, rules)
215+
return remaining
216+
}
217+
218+
type Part2Rule struct {
219+
repeatRuleID int
220+
openRuleID int
221+
closeRuleID int
222+
}
223+
224+
func (r *Part2Rule) Stringify(allRules map[int]Rule) []string {
225+
return []string{"PART2"}
226+
}
227+
228+
func (r *Part2Rule) Matches(input string, allRules map[int]Rule) (int, string) {
229+
repeatRule := allRules[r.repeatRuleID]
230+
openRule := allRules[r.openRuleID]
231+
closeRule := allRules[r.closeRuleID]
232+
233+
notMaxedOut := true
234+
for repetitionCount := 1; notMaxedOut; repetitionCount++ {
235+
fmt.Println("Trying repetition count:", repetitionCount)
236+
repeatLen, repeatRemaining, didMaxOut := r.tryRepeat(input, allRules, &repeatRule, repetitionCount)
237+
notMaxedOut = !didMaxOut
238+
if repeatLen == 0 {
239+
// Must have matched at least one
240+
return 0, input
241+
}
242+
243+
// Now do the open/close with the remainder
244+
openCloseLen, openCloseRemaining := r.tryBalance(repeatRemaining, allRules, &openRule, &closeRule)
245+
if openCloseLen == 0 {
246+
continue
247+
}
248+
if len(openCloseRemaining) == 0 {
249+
// Found a match!
250+
return openCloseLen + repeatLen, openCloseRemaining
251+
}
252+
}
253+
254+
// No match at any repeat count
255+
return 0, input
256+
}
257+
258+
func (r *Part2Rule) tryBalance(input string, allRules map[int]Rule, openRule *Rule, closeRule *Rule) (int, string) {
259+
openCount := 0
260+
totalLen := 0
261+
remaining := input
262+
263+
// Find opens
264+
for {
265+
matchLen, matchRemaining := openRule.Matches(remaining, allRules)
266+
if matchLen == 0 {
267+
break
268+
}
269+
openCount++
270+
totalLen += matchLen
271+
remaining = matchRemaining
272+
}
273+
274+
// Balance closes
275+
for i := 0; i < openCount; i++ {
276+
matchLen, matchRemaining := closeRule.Matches(remaining, allRules)
277+
if matchLen == 0 {
278+
// We failed to do balancing
279+
return 0, ""
280+
}
281+
totalLen += matchLen
282+
remaining = matchRemaining
283+
}
284+
285+
return totalLen, remaining
286+
}
287+
288+
func (r *Part2Rule) tryRepeat(input string, allRules map[int]Rule, repeatRule *Rule, maxRepetitions int) (int, string, bool) {
289+
remaining := input
290+
matchLen := 0
291+
// Run the repeat rule repetitionCount times
292+
for i := 0; i < maxRepetitions; i++ {
293+
subMatchLen, newRemaining := repeatRule.Matches(remaining, allRules)
294+
matchLen += subMatchLen
295+
if subMatchLen == 0 {
296+
// stopped because further reptitions are impossible
297+
return matchLen, newRemaining, true
298+
}
299+
remaining = newRemaining
300+
}
301+
302+
// stopped because we hit our repetition max
303+
return matchLen, remaining, false
7304
}

0 commit comments

Comments
 (0)