Files
2020/days/day16.go
Parnic 0397fa2821 Day 23 in Go, just 'cause
Also resaved 23input.txt in code page 1252 to remove the UTF-8 BOM
2020-12-23 22:41:03 -06:00

279 lines
4.8 KiB
Go

package days
import (
"fmt"
"io/ioutil"
"log"
"regexp"
"strconv"
"strings"
"time"
)
const ()
var (
rules = make(map[string][]int)
myTicket = make([]int, 0)
otherTickets = make([][]int, 0)
ruleRegex = regexp.MustCompile(`(.+): (\d+)-(\d+) or (\d+)-(\d+)`)
)
func atoi(val string) int {
iVal, err := strconv.Atoi(val)
if err != nil {
panic(err)
}
return iVal
}
func atol(val string) int64 {
iVal, err := strconv.ParseInt(val, 10, 64)
if err != nil {
panic(err)
}
return iVal
}
func msDuration(startTime time.Time) time.Duration {
return time.Since(startTime)
}
// Day16 runs day 16
func Day16() {
start := time.Now()
makeList()
log.Printf("Q16MakeList took %s\n", msDuration(start))
start = time.Now()
part1()
log.Printf("Q16Part1 took %s\n", msDuration(start))
start = time.Now()
part2()
log.Printf("Q16Part2 took %s\n", msDuration(start))
}
func makeList() {
bytes, err := ioutil.ReadFile("16input.txt")
if err != nil {
panic(err)
}
fileStr := string(bytes)
mode := 0
for _, line := range strings.Split(fileStr, "\n") {
line = strings.TrimSpace(line)
if len(line) == 0 {
mode++
continue
}
switch mode {
case 0:
matches := ruleRegex.FindStringSubmatch(line)
if matches == nil {
panic("no regex match")
}
rules[matches[1]] = []int{
atoi(matches[2]),
atoi(matches[3]),
atoi(matches[4]),
atoi(matches[5]),
}
break
case 1:
if line == "your ticket:" {
continue
}
for _, val := range strings.Split(line, ",") {
myTicket = append(myTicket, atoi(val))
}
break
case 2:
if line == "nearby tickets:" {
continue
}
otherTicket := make([]int, 0)
for _, val := range strings.Split(line, ",") {
otherTicket = append(otherTicket, atoi(val))
}
otherTickets = append(otherTickets, otherTicket)
break
case 3:
panic("unexpected")
}
}
}
func isValidForAnyRule(val int) bool {
for rule := range rules {
if isValidForRule(val, rule) {
return true
}
}
return false
}
func isValidForRule(val int, rule string) bool {
if (val >= rules[rule][0] && val <= rules[rule][1]) || (val >= rules[rule][2] && val <= rules[rule][3]) {
return true
}
return false
}
func isValidTicket(ticket []int) bool {
for _, val := range ticket {
if !isValidForAnyRule(val) {
return false
}
}
return true
}
func part1() {
invalidValues := make([]int, 0)
for _, ticket := range otherTickets {
for _, val := range ticket {
if !isValidForAnyRule(val) {
invalidValues = append(invalidValues, val)
}
}
}
result := 0
for _, val := range invalidValues {
result += val
}
log.Printf("Q16part1: invalid sum=%d\n", result)
}
func part2() {
validTickets := make([][]int, 0)
for _, ticket := range otherTickets {
if isValidTicket(ticket) {
validTickets = append(validTickets, ticket)
}
}
ruleMatches := make(map[string][]int)
for ruleName := range rules {
numCols := len(validTickets[0])
for checkCol := 0; checkCol < numCols; checkCol++ {
ruleValid := true
for _, ticket := range validTickets {
if !isValidForRule(ticket[checkCol], ruleName) {
ruleValid = false
break
}
}
if ruleValid {
ruleMatches[ruleName] = append(ruleMatches[ruleName], checkCol)
}
}
if _, ok := ruleMatches[ruleName]; !ok {
panic("didn't map column to rule")
}
}
finalMap := make(map[string]int)
ruleMatched := func(test string) bool {
for name := range finalMap {
if name == test {
return true
}
}
return false
}
valMatched := func(test int) bool {
for _, val := range finalMap {
if val == test {
return true
}
}
return false
}
for len(finalMap) < len(ruleMatches) {
for ruleName, ruleVals := range ruleMatches {
if ruleMatched(ruleName) {
continue
}
unmatchedVals := 0
unmatchedValIdx := 0
for idx, val := range ruleVals {
if !valMatched(val) {
unmatchedVals++
unmatchedValIdx = idx
}
}
if unmatchedVals == 1 {
finalMap[ruleName] = ruleVals[unmatchedValIdx]
continue
}
for _, val := range ruleVals {
if valMatched(val) {
continue
}
numMatches := 0
for checkRule, checkVals := range ruleMatches {
if ruleMatched(checkRule) {
continue
}
for _, checkVal := range checkVals {
if checkVal == val {
numMatches++
break
}
}
}
if numMatches == 1 {
finalMap[ruleName] = val
break
}
}
}
}
ruleForColumn := func(col int) string {
for name, checkVal := range finalMap {
if col == checkVal {
return name
}
}
panic(fmt.Sprintf("no rule found for column %d", col))
}
finalVal := 1
for idx, val := range myTicket {
rule := ruleForColumn(idx)
if strings.HasPrefix(rule, "departure ") {
finalVal *= val
}
}
log.Printf("Q16part2: departures multiplied=%d\n", finalVal)
}