mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
150 lines
4.0 KiB
Go
150 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/cast"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
ans := ticketTranslation(util.ReadFile("./input.txt"), part)
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
|
|
func ticketTranslation(input string, part int) int {
|
|
rules, myTicket, nearbyTickets := parseInput(input)
|
|
|
|
var validTickets [][]int // for part 2
|
|
var errorRate int
|
|
for _, ticket := range nearbyTickets {
|
|
isValidTicket := true
|
|
for _, ticketValue := range ticket {
|
|
valuePassesRules := false
|
|
for _, ruleBounds := range rules {
|
|
if valuePassesRule(ticketValue, ruleBounds) {
|
|
valuePassesRules = true
|
|
break
|
|
}
|
|
}
|
|
if !valuePassesRules {
|
|
errorRate += ticketValue
|
|
isValidTicket = false // for part 2
|
|
break
|
|
}
|
|
}
|
|
// filter out valid tickets for part 2
|
|
if isValidTicket {
|
|
validTickets = append(validTickets, ticket)
|
|
}
|
|
}
|
|
|
|
if part == 1 {
|
|
return errorRate
|
|
}
|
|
|
|
// part 2, figure out which field belongs to which
|
|
fieldNameToIndex := map[string]int{}
|
|
skipTicketIndices := map[int]bool{}
|
|
// run until all the rules are accounted for
|
|
for len(rules) > 0 {
|
|
// iterate over "columns" of the valid tickets matrix
|
|
for ticketValIndex := range validTickets[0] {
|
|
if skipTicketIndices[ticketValIndex] {
|
|
continue
|
|
}
|
|
// run all the rules against each ticket, store which ones pass for
|
|
// all values at this ticket index. if only one rule applies, it
|
|
// must be for this index within a ticket
|
|
var passingNames []string
|
|
for ruleName, ruleBounds := range rules {
|
|
allValuesPassed := true
|
|
// iterate over all tickets and if any fail for this rule, break out
|
|
for _, ticket := range validTickets {
|
|
ticketValue := ticket[ticketValIndex]
|
|
if !valuePassesRule(ticketValue, ruleBounds) {
|
|
allValuesPassed = false
|
|
break
|
|
}
|
|
}
|
|
|
|
// append this rule name as one that passed for these values
|
|
if allValuesPassed {
|
|
passingNames = append(passingNames, ruleName)
|
|
}
|
|
}
|
|
|
|
// if only one rule passes, assign it to this ticket value index
|
|
// remove it from the rules list
|
|
if len(passingNames) == 1 {
|
|
fieldNameToIndex[passingNames[0]] = ticketValIndex
|
|
// remove the rule from the map b/c we've determined its index
|
|
delete(rules, passingNames[0])
|
|
// remember which indices have already been taken by a rule
|
|
skipTicketIndices[ticketValIndex] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// get final answer by multiplying all ticket details/rules prefixed "departure"
|
|
departureProduct := 1
|
|
for rule, valueIndex := range fieldNameToIndex {
|
|
if strings.HasPrefix(rule, "departure") {
|
|
departureProduct *= myTicket[valueIndex]
|
|
}
|
|
}
|
|
|
|
return departureProduct
|
|
}
|
|
|
|
func valuePassesRule(value int, ruleBounds [2][2]int) bool {
|
|
firstBounds := ruleBounds[0]
|
|
secondBounds := ruleBounds[1]
|
|
return ((value >= firstBounds[0] && value <= firstBounds[1]) ||
|
|
(value >= secondBounds[0] && value <= secondBounds[1]))
|
|
}
|
|
|
|
func parseInput(input string) (map[string][2][2]int, []int, [][]int) {
|
|
blocks := strings.Split(input, "\n\n")
|
|
|
|
// parse rules from first block
|
|
rules := map[string][2][2]int{}
|
|
for _, rule := range strings.Split(blocks[0], "\n") {
|
|
parts := strings.Split(rule, ": ")
|
|
name := parts[0]
|
|
|
|
var r1L, r1H, r2L, r2H int
|
|
fmt.Sscanf(parts[1], "%d-%d or %d-%d", &r1L, &r1H, &r2L, &r2H)
|
|
rules[name] = [2][2]int{
|
|
[2]int{r1L, r1H},
|
|
[2]int{r2L, r2H},
|
|
}
|
|
}
|
|
|
|
// my ticket values in second block
|
|
splitTicket := strings.Split(blocks[1], "\n")
|
|
var myTicket []int
|
|
for _, v := range strings.Split(splitTicket[1], ",") {
|
|
myTicket = append(myTicket, cast.ToInt(v))
|
|
}
|
|
|
|
// all values for nearby tickets
|
|
var nearbyTickets [][]int
|
|
for _, nearby := range strings.Split(blocks[2], "\n")[1:] {
|
|
var near []int
|
|
for _, v := range strings.Split(nearby, ",") {
|
|
near = append(near, cast.ToInt(v))
|
|
}
|
|
nearbyTickets = append(nearbyTickets, near)
|
|
}
|
|
|
|
return rules, myTicket, nearbyTickets
|
|
}
|