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) | |
|
||||
| [17](/17-conwayCubes) | ![Partially complete][partial] | [Link](/17-conwayCubes/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 | | | |
|
||||
| 21 | | | |
|
||||
| 22 | | | |
|
||||
|
|
|
@ -27,6 +27,18 @@ Test cases
|
|||
1.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
|
||||
Part 1: 222
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
|
||||
# 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