Day 19 (Golang)
This commit is contained in:
parent
b5c226594b
commit
e8371b1801
8 changed files with 262 additions and 82 deletions
2
.github/README.md
vendored
2
.github/README.md
vendored
|
@ -34,7 +34,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have
|
||||||
| [16](/16-ticketTranslation) | ![Partially complete][partial] | [Link](/16-ticketTranslation/python) | |
|
| [16](/16-ticketTranslation) | ![Partially complete][partial] | [Link](/16-ticketTranslation/python) | |
|
||||||
| [17](/17-conwayCubes) | ![Partially complete][partial] | [Link](/17-conwayCubes/python) | |
|
| [17](/17-conwayCubes) | ![Partially complete][partial] | [Link](/17-conwayCubes/python) | |
|
||||||
| [18](/18-operationOrder) | ![Partially complete][partial] | [Link](/18-operationOrder/python) | |
|
| [18](/18-operationOrder) | ![Partially complete][partial] | [Link](/18-operationOrder/python) | |
|
||||||
| [19](/19-monsterMessages) | ![Partially complete][partial] | [Link](/19-monsterMessages/python) | |
|
| [19](/19-monsterMessages) | ![Completed][check] | [Link](/19-monsterMessages/python) | [Link](/19-monsterMessages/go) |
|
||||||
| 20 | | | |
|
| 20 | | | |
|
||||||
| 21 | | | |
|
| 21 | | | |
|
||||||
| 22 | | | |
|
| 22 | | | |
|
||||||
|
|
|
@ -27,6 +27,18 @@ Test cases
|
||||||
1.1 pass
|
1.1 pass
|
||||||
2.1 pass
|
2.1 pass
|
||||||
|
|
||||||
|
Answers
|
||||||
|
Part 1: 222
|
||||||
|
Part 2: 339
|
||||||
|
|
||||||
|
❯ go run .\go\
|
||||||
|
AoC 2020: day 19 - Monster Messages
|
||||||
|
Go go1.15.2
|
||||||
|
|
||||||
|
Test cases
|
||||||
|
1.1 pass
|
||||||
|
2.1 pass
|
||||||
|
|
||||||
Answers
|
Answers
|
||||||
Part 1: 222
|
Part 1: 222
|
||||||
Part 2: 339
|
Part 2: 339
|
||||||
|
|
111
19-monsterMessages/go/challenge/common.go
Normal file
111
19-monsterMessages/go/challenge/common.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package challenge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
charRegex = regexp.MustCompile(`^\"([a-z])\"$`) // group 1 contains interesting information
|
||||||
|
orRuleRegex = regexp.MustCompile(`^((\d+ ?)+) \| ((\d+ ?)+)$`) // group 1 and 3 contain interesting info
|
||||||
|
singleRuleRegex = regexp.MustCompile(`^((\d+ ?)+)$`) // group 0 and 1 both contain the same interesting information
|
||||||
|
)
|
||||||
|
|
||||||
|
const replaceMarker = "!!!REPLACETHISMARKER!!!"
|
||||||
|
|
||||||
|
func generateCompoundRuleRegex(ruleset map[int]string, compound string, current int) string {
|
||||||
|
|
||||||
|
// Used when you have a rule that looks like `125: 116 33 | 131 113`
|
||||||
|
// compound should be a sequence like `116 33`
|
||||||
|
// current should be the current rule number
|
||||||
|
|
||||||
|
var o string
|
||||||
|
|
||||||
|
for _, xs := range strings.Split(compound, " ") {
|
||||||
|
x, err := strconv.Atoi(xs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if x == current {
|
||||||
|
if x == 8 {
|
||||||
|
o += generateRuleRegex(ruleset, 42)
|
||||||
|
o += "+"
|
||||||
|
} else {
|
||||||
|
o += replaceMarker
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
o += generateRuleRegex(ruleset, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRuleRegex(ruleset map[int]string, rule int) string {
|
||||||
|
|
||||||
|
// Rule is the integer number of the rule to generate a regular expression for
|
||||||
|
// Said integer should correspond to a key-value pair in the ruleset
|
||||||
|
|
||||||
|
ruleContent := ruleset[rule]
|
||||||
|
|
||||||
|
if charRegex.MatchString(ruleContent) {
|
||||||
|
// is a single character
|
||||||
|
// `116: "a"`
|
||||||
|
return charRegex.FindAllStringSubmatch(ruleContent, -1)[0][1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here, that means there are multiple options for the rule
|
||||||
|
// hence we need to open a new set of brackets
|
||||||
|
output := "("
|
||||||
|
|
||||||
|
if orRuleRegex.MatchString(ruleContent) {
|
||||||
|
// rule is an OR rule
|
||||||
|
// `83: 26 131 | 47 116`
|
||||||
|
submatches := orRuleRegex.FindAllStringSubmatch(ruleContent, -1)[0]
|
||||||
|
|
||||||
|
output += generateCompoundRuleRegex(ruleset, submatches[1], rule)
|
||||||
|
output += "|"
|
||||||
|
output += generateCompoundRuleRegex(ruleset, submatches[3], rule)
|
||||||
|
} else if singleRuleRegex.MatchString(ruleContent) {
|
||||||
|
// rule is a simple sequence rule
|
||||||
|
// `92: 67 131`
|
||||||
|
output += generateCompoundRuleRegex(ruleset, singleRuleRegex.FindAllStringSubmatch(ruleContent, -1)[0][1], rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close original set of brackets
|
||||||
|
output += ")"
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRulesetRegex(ruleset map[int]string) string {
|
||||||
|
return "^" + generateRuleRegex(ruleset, 0) + "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(messages []string, regex *regexp.Regexp) int {
|
||||||
|
var validInputs int
|
||||||
|
for _, message := range messages {
|
||||||
|
if regex.MatchString(message) {
|
||||||
|
validInputs += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validInputs
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(instr string) (map[int]string, []string) {
|
||||||
|
|
||||||
|
splitInput := strings.Split(strings.TrimSpace(instr), "\n\n")
|
||||||
|
|
||||||
|
rules := make(map[int]string)
|
||||||
|
for _, x := range strings.Split(splitInput[0], "\n") {
|
||||||
|
splitRule := strings.Split(x, ":")
|
||||||
|
num, err := strconv.Atoi(strings.TrimSpace(splitRule[0]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rules[num] = strings.TrimSpace(splitRule[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, strings.Split(strings.TrimSpace(splitInput[1]), "\n")
|
||||||
|
}
|
9
19-monsterMessages/go/challenge/partOne.go
Normal file
9
19-monsterMessages/go/challenge/partOne.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package challenge
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
func PartOne(instr string) int {
|
||||||
|
rules, messages := parse(instr)
|
||||||
|
ruleRegex := regexp.MustCompile(makeRulesetRegex(rules))
|
||||||
|
return run(messages, ruleRegex)
|
||||||
|
}
|
29
19-monsterMessages/go/challenge/partTwo.go
Normal file
29
19-monsterMessages/go/challenge/partTwo.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package challenge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PartTwo(instr string) int {
|
||||||
|
|
||||||
|
// patch input
|
||||||
|
instr = strings.ReplaceAll(instr, "8: 42", "8: 42 | 42 8")
|
||||||
|
instr = strings.ReplaceAll(instr, "11: 42 31", "11: 42 31 | 42 11 31")
|
||||||
|
|
||||||
|
rules, messages := parse(instr)
|
||||||
|
|
||||||
|
// Since we have sections of our ruleset, we have markers in the regex returned by `make_ruleset_regex`
|
||||||
|
// that denote where we need to insert a copy of the rule 11 regular expression (which also happens to
|
||||||
|
// have one of those markers in it)
|
||||||
|
|
||||||
|
rr := makeRulesetRegex(rules)
|
||||||
|
elevenRegex := generateRuleRegex(rules, 11)
|
||||||
|
for i := 0; i < 10; i += 2 {
|
||||||
|
rr = strings.ReplaceAll(rr, replaceMarker, elevenRegex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run as usual
|
||||||
|
ruleRegex := regexp.MustCompile(rr)
|
||||||
|
return run(messages, ruleRegex)
|
||||||
|
}
|
100
19-monsterMessages/go/main.go
Normal file
100
19-monsterMessages/go/main.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adventOfCode/19-monsterMessages/go/challenge"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var infoStruct info
|
||||||
|
{
|
||||||
|
inb, err := ioutil.ReadFile("info.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: could not open info.json")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(inb, &infoStruct)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: could not parse info.json")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
year = infoStruct.Year
|
||||||
|
day = infoStruct.Day
|
||||||
|
title = infoStruct.Title
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Fprintf(color.Output, "%s: day %s - %s\n", color.YellowString("AoC "+year), color.BlueString(day), title)
|
||||||
|
fmt.Fprintf(color.Output, "Go %s\n\n", color.BlueString(runtime.Version()))
|
||||||
|
|
||||||
|
var challengeInput string
|
||||||
|
{
|
||||||
|
inb, err := ioutil.ReadFile("input.txt")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: could not open input.txt")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
challengeInput = string(inb)
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(infoStruct)
|
||||||
|
|
||||||
|
fmt.Println("Answers")
|
||||||
|
fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("1"), color.BlueString(strconv.Itoa(challenge.PartOne(challengeInput))))
|
||||||
|
fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("2"), color.BlueString(strconv.Itoa(challenge.PartTwo(challengeInput))))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type tc struct {
|
||||||
|
Input string `json:"input"`
|
||||||
|
Expected int `json:"expected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type info struct {
|
||||||
|
Year string `json:"year"`
|
||||||
|
Day string `json:"day"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
TestCases struct {
|
||||||
|
One []tc `json:"one"`
|
||||||
|
Two []tc `json:"two"`
|
||||||
|
} `json:"testCases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTests(infoStruct info) {
|
||||||
|
|
||||||
|
if len(infoStruct.TestCases.One) == 0 && len(infoStruct.TestCases.Two) == 0 {
|
||||||
|
fmt.Println("Info: no test cases specified. Skipping tests")
|
||||||
|
fmt.Println()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Test cases")
|
||||||
|
|
||||||
|
rt := func(tcs []tc, f func(string) int, n string) {
|
||||||
|
for i, tc := range tcs {
|
||||||
|
fmt.Fprintf(color.Output, "%s ", color.BlueString(n+"."+strconv.Itoa(i+1)))
|
||||||
|
result := f(tc.Input)
|
||||||
|
if result == tc.Expected {
|
||||||
|
fmt.Fprintf(color.Output, "%s", color.GreenString("pass"))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(color.Output, "%s (got %s, expected %s)", color.RedString("fail"), color.BlueString(strconv.Itoa(result)), color.BlueString(strconv.Itoa(tc.Expected)))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rt(infoStruct.TestCases.One, challenge.PartOne, "1")
|
||||||
|
rt(infoStruct.TestCases.Two, challenge.PartTwo, "2")
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
}
|
|
@ -2,13 +2,6 @@ from common import *
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def recurse_replace(instr: str, old: str, new: str, limit: int, n: int = 0) -> str:
|
|
||||||
# I *think* this is tail recursive?
|
|
||||||
if n + 1 == limit:
|
|
||||||
return instr
|
|
||||||
return recurse_replace(instr.replace(old, new), old, new, limit, n=n + 1)
|
|
||||||
|
|
||||||
|
|
||||||
def partTwo(instr: str) -> int:
|
def partTwo(instr: str) -> int:
|
||||||
|
|
||||||
# patch input
|
# patch input
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import re
|
|
||||||
from typing import Dict
|
|
||||||
import time
|
|
||||||
|
|
||||||
char_regex = re.compile(r"^\"([a-z])\"$") # group 1 contains interesting information
|
|
||||||
or_rule_regex = re.compile(r"^((\d+ ?)+) \| ((\d+ ?)+)$") # group 1 and 3 contain interesting info
|
|
||||||
single_rule_regex = re.compile(r"^((\d+ ?)+)$") # group 0 and 1 both contain the same interesting information
|
|
||||||
|
|
||||||
rules = {
|
|
||||||
0: "4 1 5",
|
|
||||||
1: "2 3 | 3 2",
|
|
||||||
2: "4 4 | 5 5",
|
|
||||||
3: "4 5 | 5 4",
|
|
||||||
4: "\"a\"",
|
|
||||||
5: "\"b\"",
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_compound_rule(ruleset:Dict[int, str], compound:str) -> str:
|
|
||||||
o = ""
|
|
||||||
for x in [int(a) for a in compound.split(" ")]:
|
|
||||||
o += generate_rule(ruleset, rule=x)
|
|
||||||
return o
|
|
||||||
|
|
||||||
|
|
||||||
memo = {}
|
|
||||||
|
|
||||||
|
|
||||||
def generate_rule(ruleset:Dict[int, str], rule:int=0) -> str:
|
|
||||||
|
|
||||||
if rule in memo:
|
|
||||||
return memo[rule]
|
|
||||||
|
|
||||||
output = ""
|
|
||||||
|
|
||||||
rule_content = ruleset[rule]
|
|
||||||
|
|
||||||
r = char_regex.match(rule_content)
|
|
||||||
if r:
|
|
||||||
# is a single character
|
|
||||||
return r.group(1)
|
|
||||||
|
|
||||||
# if we get here, that means there are multiple options for the rule
|
|
||||||
# hence we need to open a new set of brackets
|
|
||||||
output += "("
|
|
||||||
|
|
||||||
r = or_rule_regex.match(rule_content)
|
|
||||||
if r:
|
|
||||||
# rule is an OR rule
|
|
||||||
output += generate_compound_rule(ruleset, r.group(1))
|
|
||||||
output += "|"
|
|
||||||
output += generate_compound_rule(ruleset, r.group(3))
|
|
||||||
|
|
||||||
r = single_rule_regex.match(rule_content)
|
|
||||||
if r:
|
|
||||||
# rule is a simple sequence rule
|
|
||||||
output += generate_compound_rule(ruleset, r.group(1))
|
|
||||||
|
|
||||||
# close original set of brackets
|
|
||||||
output += ")"
|
|
||||||
|
|
||||||
memo[rule] = output
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
rules, _ = open("input.txt").read().strip().split("\n\n")
|
|
||||||
|
|
||||||
rules_dict = {}
|
|
||||||
for x in rules.split("\n"):
|
|
||||||
num, definition = x.split(":")
|
|
||||||
rules_dict[int(num.strip())] = definition.strip()
|
|
||||||
|
|
||||||
res = generate_rule(rules_dict)
|
|
||||||
print("^" + res + "$")
|
|
Loading…
Add table
Add a link
Reference in a new issue