mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
2020-day16: slow/730 - lots of input parsing, sample set -> corresponding rules
This commit is contained in:
+126
-22
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/mathutil"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
@@ -15,32 +16,135 @@ func main() {
|
||||
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 {
|
||||
ans := part1(util.ReadFile("./input.txt"))
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
} else {
|
||||
ans := part2(util.ReadFile("./input.txt"))
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
return errorRate
|
||||
}
|
||||
}
|
||||
|
||||
func part1(input string) int {
|
||||
parsed := parseInput(input)
|
||||
_ = parsed
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
// append this rule name as one that passed for these values
|
||||
if allValuesPassed {
|
||||
passingNames = append(passingNames, ruleName)
|
||||
}
|
||||
}
|
||||
|
||||
func part2(input string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func parseInput(input string) (ans []int) {
|
||||
lines := strings.Split(input, "\n")
|
||||
for _, l := range lines {
|
||||
ans = append(ans, mathutil.StrToInt(l))
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans
|
||||
|
||||
// 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, mathutil.StrToInt(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, mathutil.StrToInt(v))
|
||||
}
|
||||
nearbyTickets = append(nearbyTickets, near)
|
||||
}
|
||||
|
||||
return rules, myTicket, nearbyTickets
|
||||
}
|
||||
|
||||
+21
-20
@@ -2,37 +2,38 @@ package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
func Test_part1(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := part1(tt.input); got != tt.want {
|
||||
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
var example1 = `class: 1-3 or 5-7
|
||||
row: 6-11 or 33-44
|
||||
seat: 13-40 or 45-50
|
||||
|
||||
func Test_part2(t *testing.T) {
|
||||
your ticket:
|
||||
7,1,14
|
||||
|
||||
nearby tickets:
|
||||
7,3,47
|
||||
40,4,50
|
||||
55,2,20
|
||||
38,6,12`
|
||||
|
||||
func Test_ticketTranslation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
part int
|
||||
want int
|
||||
}{
|
||||
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
|
||||
{"example_part1", example1, 1, 71},
|
||||
{"actual", util.ReadFile("input.txt"), 1, 32835},
|
||||
{"actual", util.ReadFile("input.txt"), 2, 514662805187},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := part2(tt.input); got != tt.want {
|
||||
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||
if got := ticketTranslation(tt.input, tt.part); got != tt.want {
|
||||
t.Errorf("ticketTranslation() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user