mirror of
https://github.com/RussellChamp/advent-of-code-2021.git
synced 2026-10-27 21:52:32 +00:00
day 9 and 10
This commit is contained in:
1 parent
c7ea0afedc
commit
41c5e86e7c
6 files changed
+479
-1
No files matched your search
+215
@@ -0,0 +1,215 @@
|
||||
/* Day 10: Syntax Scoring */
|
||||
package main
|
||||
|
||||
import (
|
||||
"AoC2021/utils/arrays"
|
||||
"AoC2021/utils/log"
|
||||
"AoC2021/utils/timer"
|
||||
"bufio"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
log.Println(log.DEBUG, "Oh snap!")
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Println(log.NORMAL, "--- Day 10: Syntax Scoring ---")
|
||||
timer.Start()
|
||||
part1()
|
||||
timer.Tick()
|
||||
log.Println(log.NORMAL)
|
||||
|
||||
part2()
|
||||
timer.Tick()
|
||||
log.Println(log.NORMAL)
|
||||
}
|
||||
|
||||
func part1() {
|
||||
log.LOG_LEVEL = log.DEBUG
|
||||
log.Println(log.NORMAL, "* Part 1 *")
|
||||
log.Println(log.NORMAL, " Goal: Find the first illegal character in each corrupted line of the navigation subsystem.")
|
||||
log.Println(log.NORMAL, " Answer: What is the total syntax error score for those errors?")
|
||||
|
||||
input, err := os.Open("./input.txt")
|
||||
check(err)
|
||||
defer input.Close()
|
||||
|
||||
scanner := bufio.NewScanner(input)
|
||||
lines := 0
|
||||
tokenCount := 0
|
||||
totalScore := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
_, errScore := validateNavLine(line)
|
||||
totalScore += errScore
|
||||
lines++
|
||||
tokenCount += len(line)
|
||||
}
|
||||
|
||||
log.Printf(log.NORMAL, "Read %d lines with %d tokens from input\n", lines, tokenCount)
|
||||
log.Printf(log.NORMAL, "The total syntax error score is %d\n", totalScore)
|
||||
}
|
||||
|
||||
func part2() {
|
||||
log.Println(log.NORMAL, "* Part 2 *")
|
||||
log.Println(log.NORMAL, " Goal: Find the completion string for each incomplete line, score the completion strings, and sort the scores.")
|
||||
log.Println(log.NORMAL, " Answer: What is the middle score?")
|
||||
|
||||
input, err := os.Open("./input.txt")
|
||||
check(err)
|
||||
defer input.Close()
|
||||
|
||||
scanner := bufio.NewScanner(input)
|
||||
lines := 0
|
||||
tokenCount := 0
|
||||
|
||||
var autoScores []int
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
lines++
|
||||
tokenCount += len(line)
|
||||
|
||||
remainingStr, errScore := validateNavLine(line)
|
||||
if errScore > 0 || len(remainingStr) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
autoScores = append(autoScores, getAutoScore(remainingStr))
|
||||
}
|
||||
|
||||
// stort the scores
|
||||
sort.Slice(autoScores, func(i, j int) bool { return autoScores[i] < autoScores[j] })
|
||||
// find the middle-ist index
|
||||
middleIdx := len(autoScores) / 2
|
||||
|
||||
log.Printf(log.NORMAL, "Read %d lines with %d tokens from input\n", lines, tokenCount)
|
||||
log.Printf(log.NORMAL, "Autocompleted %d lines\n", len(autoScores))
|
||||
log.Printf(log.NORMAL, "The middle-ist auto-correct score at pos %d is %d\n", middleIdx, autoScores[middleIdx])
|
||||
}
|
||||
|
||||
// validates the nav line and returns a non-zero value as a syntax error score
|
||||
func BAD_validateNavLine(line string) int {
|
||||
// split the line into tokens
|
||||
tokens := strings.Split(line, "")
|
||||
invalidStr := "An invalid token '%s' was found at position %3d while validating string: %s\n"
|
||||
|
||||
// I thought of doing this generically but there are only 4 hard-coded options so just do it manually
|
||||
parens, brackets, braces, gators := 0, 0, 0, 0
|
||||
|
||||
for idx, token := range tokens {
|
||||
switch token {
|
||||
case "(":
|
||||
parens++
|
||||
case "[":
|
||||
brackets++
|
||||
case "{":
|
||||
braces++
|
||||
case "<":
|
||||
gators++
|
||||
case ")":
|
||||
if parens > 0 {
|
||||
parens--
|
||||
} else {
|
||||
log.Printf(log.DEBUG, invalidStr, token, idx, line)
|
||||
return 3
|
||||
}
|
||||
case "]":
|
||||
if brackets > 0 {
|
||||
brackets--
|
||||
} else {
|
||||
log.Printf(log.DEBUG, invalidStr, token, idx, line[:idx+1])
|
||||
return 57
|
||||
}
|
||||
case "}":
|
||||
if braces > 0 {
|
||||
braces--
|
||||
} else {
|
||||
log.Printf(log.DEBUG, invalidStr, token, idx, line)
|
||||
return 1197
|
||||
}
|
||||
case ">":
|
||||
if gators > 0 {
|
||||
gators--
|
||||
} else {
|
||||
log.Printf(log.DEBUG, invalidStr, token, idx, line)
|
||||
return 25137
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func validateNavLine(line string) (string, int) {
|
||||
reducedLine := reduceNavLine(line)
|
||||
if len(reducedLine) == 0 {
|
||||
return reducedLine, 0
|
||||
}
|
||||
// find the first closing token
|
||||
firstCloseTokenIdx := strings.IndexAny(reducedLine, ")]}>")
|
||||
// if no closing tokens are found, this is just an incomplete line (not corrupt)
|
||||
if firstCloseTokenIdx == -1 {
|
||||
return reducedLine, 0
|
||||
}
|
||||
log.Printf(log.DIAGNOSTIC, "First invalid token is at %d on %s\n", firstCloseTokenIdx, reducedLine)
|
||||
switch reducedLine[firstCloseTokenIdx] {
|
||||
case ')':
|
||||
return reducedLine, 3
|
||||
case ']':
|
||||
return reducedLine, 57
|
||||
case '}':
|
||||
return reducedLine, 1197
|
||||
case '>':
|
||||
return reducedLine, 25137
|
||||
}
|
||||
|
||||
return reducedLine, 0
|
||||
}
|
||||
|
||||
func reduceNavLine(line string) string {
|
||||
//recursively remove empty chunks from the line
|
||||
oldLen := len(line)
|
||||
for {
|
||||
line = strings.ReplaceAll(line, "()", "")
|
||||
line = strings.ReplaceAll(line, "[]", "")
|
||||
line = strings.ReplaceAll(line, "{}", "")
|
||||
line = strings.ReplaceAll(line, "<>", "")
|
||||
|
||||
if len(line) == oldLen {
|
||||
break
|
||||
}
|
||||
oldLen = len(line)
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
// get an auto-complete score from a line that has already been reduced
|
||||
func getAutoScore(reducedLine string) int {
|
||||
// we'll have to auto-complete tokens in REVERSE order of the opening tokens in the reducedLine
|
||||
score := 0
|
||||
for _, token := range arrays.Reverse(strings.Split(reducedLine, "")) {
|
||||
score *= 5
|
||||
switch token {
|
||||
case "(":
|
||||
score += 1
|
||||
case "[":
|
||||
score += 2
|
||||
case "{":
|
||||
score += 3
|
||||
case "<":
|
||||
score += 4
|
||||
}
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
Reference in new issue
Block a user