Day 19 (Golang)

This commit is contained in:
akp 2020-12-19 20:29:34 +00:00
parent b5c226594b
commit e8371b1801
No known key found for this signature in database
GPG key ID: D3E7EAA31B39637E
8 changed files with 262 additions and 82 deletions

2
.github/README.md vendored
View file

@ -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 | | | |

View file

@ -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

View 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")
}

View 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)
}

View 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)
}

View 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()
}

View file

@ -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

View file

@ -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 + "$")