215 lines
4.4 KiB
Go
215 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
|
|
)
|
|
|
|
const (
|
|
FIELDS = iota
|
|
MY_TICKET
|
|
OTHER_TICKETS
|
|
)
|
|
|
|
type Field struct {
|
|
Name string
|
|
Min1 int
|
|
Min2 int
|
|
Max1 int
|
|
Max2 int
|
|
Index int
|
|
Indexes map[int]bool
|
|
}
|
|
|
|
func parseInput(input []string) ([]*Field, []int, [][]int) {
|
|
fields := make([]*Field, 0)
|
|
myTicket := make([]int, 0)
|
|
otherTickets := make([][]int, 0)
|
|
|
|
state := FIELDS
|
|
re := regexp.MustCompile("^(.*): ([0-9]+)-([0-9]+) or ([0-9]+)-([0-9]+)$")
|
|
|
|
for _, line := range input {
|
|
if line == "your ticket:" {
|
|
state = MY_TICKET
|
|
continue
|
|
} else if line == "nearby tickets:" {
|
|
state = OTHER_TICKETS
|
|
continue
|
|
} else if line == "" {
|
|
continue
|
|
}
|
|
|
|
switch state {
|
|
case FIELDS:
|
|
data := re.FindAllStringSubmatch(line, 32)
|
|
field := &Field{
|
|
Name: data[0][1],
|
|
Min1: util.MustAtoi(data[0][2]),
|
|
Max1: util.MustAtoi(data[0][3]),
|
|
Min2: util.MustAtoi(data[0][4]),
|
|
Max2: util.MustAtoi(data[0][5]),
|
|
Index: -1,
|
|
Indexes: make(map[int]bool),
|
|
}
|
|
fields = append(fields, field)
|
|
case MY_TICKET:
|
|
for _, num := range strings.Split(line, ",") {
|
|
myTicket = append(myTicket, util.MustAtoi(num))
|
|
}
|
|
case OTHER_TICKETS:
|
|
ticket := make([]int, 0)
|
|
for _, num := range strings.Split(line, ",") {
|
|
ticket = append(ticket, util.MustAtoi(num))
|
|
}
|
|
otherTickets = append(otherTickets, ticket)
|
|
}
|
|
}
|
|
|
|
return fields, myTicket, otherTickets
|
|
}
|
|
|
|
func makeValidValueMap(fields []*Field) map[int]bool {
|
|
valid := make(map[int]bool)
|
|
for _, field := range fields {
|
|
for i := field.Min1; i <= field.Max1; i++ {
|
|
valid[i] = true
|
|
}
|
|
for i := field.Min2; i <= field.Max2; i++ {
|
|
valid[i] = true
|
|
}
|
|
}
|
|
return valid
|
|
}
|
|
|
|
func findInvalidValues(fields []*Field, tickets [][]int) []int {
|
|
answers := make([]int, 0)
|
|
|
|
// make a map of all valid values
|
|
valid := makeValidValueMap(fields)
|
|
|
|
for _, ticket := range tickets {
|
|
for _, value := range ticket {
|
|
if _, ok := valid[value]; !ok {
|
|
answers = append(answers, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
return answers
|
|
}
|
|
|
|
func removeInvalidTickets(fields []*Field, tickets [][]int) [][]int {
|
|
answers := make([][]int, 0)
|
|
valid := makeValidValueMap(fields)
|
|
|
|
for _, ticket := range tickets {
|
|
badTicket := false
|
|
for _, value := range ticket {
|
|
if _, ok := valid[value]; !ok {
|
|
badTicket = true
|
|
}
|
|
}
|
|
if !badTicket {
|
|
answers = append(answers, ticket)
|
|
}
|
|
}
|
|
|
|
return answers
|
|
}
|
|
|
|
// sets the indexes in place
|
|
func findIndexes(fields []*Field, tickets [][]int) {
|
|
for _, field := range fields {
|
|
for i := 0; i < len(fields); i++ {
|
|
field.Indexes[i] = true
|
|
}
|
|
}
|
|
|
|
for _, ticket := range tickets {
|
|
for i, num := range ticket {
|
|
for _, f := range fields {
|
|
if f.Indexes[i] && !((num >= f.Min1 && num <= f.Max1) ||
|
|
(num >= f.Min2 && num <= f.Max2)) {
|
|
// this field cannot be in this space
|
|
f.Indexes[i] = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unique := false
|
|
for !unique {
|
|
unique = true
|
|
for _, field := range fields {
|
|
if field.Index != -1 {
|
|
continue
|
|
}
|
|
|
|
found := make([]int, 0)
|
|
for index, valid := range field.Indexes {
|
|
if valid {
|
|
found = append(found, index)
|
|
}
|
|
}
|
|
if len(found) == 0 {
|
|
log.Panicf("Failed to find any solution for field %s: %v", field.Name, field.Indexes)
|
|
}
|
|
if len(found) == 1 {
|
|
field.Index = found[0]
|
|
}
|
|
}
|
|
|
|
// now weed out duplicates
|
|
for _, field := range fields {
|
|
// any field we're certain of we can set to false on all fields
|
|
if field.Index != -1 {
|
|
for _, subField := range fields {
|
|
subField.Indexes[field.Index] = false
|
|
}
|
|
} else {
|
|
// any field we haven't set an Index for means it is still potentially ambiguous
|
|
unique = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func mulDepartureFields(fields []*Field, ticket []int) int {
|
|
product := 1
|
|
for _, field := range fields {
|
|
if strings.Contains(field.Name, "departure") {
|
|
product *= ticket[field.Index]
|
|
}
|
|
}
|
|
return product
|
|
}
|
|
|
|
// 980 too low
|
|
func main() {
|
|
step := os.Args[1]
|
|
values := util.InputParserStrings(os.Args[2])
|
|
|
|
fields, myTicket, otherTickets := parseInput(values)
|
|
|
|
switch step {
|
|
case "1":
|
|
invalidValues := findInvalidValues(fields, otherTickets)
|
|
total := 0
|
|
for _, value := range invalidValues {
|
|
total += value
|
|
}
|
|
fmt.Println(total)
|
|
|
|
case "2":
|
|
otherTickets = removeInvalidTickets(fields, otherTickets)
|
|
findIndexes(fields, otherTickets)
|
|
fmt.Println(mulDepartureFields(fields, myTicket))
|
|
}
|
|
}
|