224 lines
4.2 KiB
Go
224 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
var Mine *Ticket
|
|
var ValidTickets []*Ticket
|
|
var AllFields []*Field
|
|
var FieldMap map[int]string
|
|
var UnknownFields map[int]bool
|
|
|
|
func main() {
|
|
fmt.Println("# Day 16")
|
|
inp := h.StdinToStringSlice()
|
|
FieldMap = make(map[int]string)
|
|
UnknownFields = make(map[int]bool)
|
|
findValidTickets(inp)
|
|
solveMyTicket()
|
|
}
|
|
|
|
const (
|
|
Criteria = iota
|
|
MyTicket
|
|
NearbyTickets
|
|
)
|
|
|
|
func findValidTickets(inp []string) {
|
|
var invalids []int
|
|
section := Criteria
|
|
for k := range inp {
|
|
if inp[k] == "" {
|
|
// End of a section
|
|
section++
|
|
fmt.Println("Section", section)
|
|
continue
|
|
} else if inp[k] == "your ticket:" || inp[k] == "nearby tickets:" {
|
|
continue
|
|
}
|
|
switch section {
|
|
case Criteria:
|
|
pts := strings.Split(inp[k], ": ")
|
|
f := Field{
|
|
Index: -1,
|
|
Name: pts[0],
|
|
}
|
|
ranges := strings.Split(pts[1], " or ")
|
|
for rk := range ranges {
|
|
vals := strings.Split(ranges[rk], "-")
|
|
f.AddRange(h.Atoi(vals[0]), h.Atoi(vals[1]))
|
|
}
|
|
AllFields = append(AllFields, &f)
|
|
UnknownFields[k] = true
|
|
case MyTicket:
|
|
vals := strings.Split(inp[k], ",")
|
|
var pts []int
|
|
for vk := range vals {
|
|
pts = append(pts, h.Atoi(vals[vk]))
|
|
}
|
|
Mine = NewTicket(k, pts)
|
|
|
|
case NearbyTickets:
|
|
vals := strings.Split(inp[k], ",")
|
|
var pts []int
|
|
for vk := range vals {
|
|
pts = append(pts, h.Atoi(vals[vk]))
|
|
}
|
|
t := NewTicket(k, pts)
|
|
if t.Valid {
|
|
ValidTickets = append(ValidTickets, t)
|
|
} else {
|
|
for _, iv := range t.InvalidValues {
|
|
invalids = append(invalids, iv)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var sum int
|
|
for k := range invalids {
|
|
sum = sum + invalids[k]
|
|
}
|
|
fmt.Println("## Part 1")
|
|
fmt.Println("Ticket Scanning Error Rate:", sum)
|
|
}
|
|
|
|
func solveMyTicket() {
|
|
fmt.Println()
|
|
var restarts int
|
|
for restarts < len(AllFields) {
|
|
for k := 0; k < len(AllFields); k++ {
|
|
f := AllFields[k]
|
|
if f.Index != -1 {
|
|
continue
|
|
}
|
|
poss := findCommonPossibilities(f.Name)
|
|
if len(poss) == 1 {
|
|
foundField(poss[0], f.Name)
|
|
k = 0
|
|
restarts++
|
|
}
|
|
}
|
|
}
|
|
Mine.SetFields(AllFields)
|
|
ans := 1
|
|
for _, f := range Mine.Fields {
|
|
if strings.HasPrefix(f.Name, "departure") {
|
|
ans = ans * f.Value
|
|
}
|
|
}
|
|
fmt.Println("## Part 2")
|
|
fmt.Println("Answer:", ans)
|
|
}
|
|
|
|
func foundField(num int, name string) {
|
|
for _, f := range AllFields {
|
|
if f.Name == name {
|
|
f.Index = num
|
|
}
|
|
}
|
|
FieldMap[num] = name
|
|
delete(UnknownFields, num)
|
|
}
|
|
|
|
func findCommonPossibilities(name string) []int {
|
|
var poss, not []int
|
|
for k := range UnknownFields {
|
|
poss = append(poss, k)
|
|
}
|
|
for _, t := range ValidTickets {
|
|
for fk, fv := range t.PossibleFieldMap {
|
|
if !h.StringSliceContains(fv, name) {
|
|
not = append(not, fk)
|
|
}
|
|
}
|
|
}
|
|
// Remove all of 'not' from 'poss'
|
|
for k := range not {
|
|
var rem int
|
|
for pk := range poss {
|
|
if poss[pk] == not[k] {
|
|
rem = pk
|
|
break
|
|
}
|
|
}
|
|
if len(poss) == 0 {
|
|
return poss
|
|
}
|
|
poss = append(poss[:rem], poss[rem+1:]...)
|
|
}
|
|
return poss
|
|
}
|
|
|
|
type Range struct {
|
|
Min int
|
|
Max int
|
|
}
|
|
|
|
type Field struct {
|
|
Index int
|
|
Name string
|
|
Ranges []Range
|
|
Value int
|
|
}
|
|
|
|
func (f *Field) ValidForValue(val int) bool {
|
|
for _, r := range f.Ranges {
|
|
if val >= r.Min && val <= r.Max {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (f *Field) AddRange(min, max int) {
|
|
f.Ranges = append(f.Ranges, Range{Min: min, Max: max})
|
|
}
|
|
|
|
type Ticket struct {
|
|
Number int
|
|
Fields map[string]*Field
|
|
UnnamedFields []int
|
|
PossibleFieldMap map[int][]string
|
|
InvalidValues []int
|
|
Valid bool
|
|
}
|
|
|
|
func NewTicket(num int, inp []int) *Ticket {
|
|
t := Ticket{
|
|
Number: num,
|
|
Fields: make(map[string]*Field),
|
|
PossibleFieldMap: make(map[int][]string),
|
|
Valid: true,
|
|
}
|
|
for k := range inp {
|
|
t.UnnamedFields = append(t.UnnamedFields, inp[k])
|
|
var found bool
|
|
for _, f := range AllFields {
|
|
if f.ValidForValue(inp[k]) {
|
|
found = true
|
|
t.PossibleFieldMap[k] = append(t.PossibleFieldMap[k], f.Name)
|
|
}
|
|
}
|
|
if !found {
|
|
t.InvalidValues = append(t.InvalidValues, inp[k])
|
|
t.Valid = false
|
|
}
|
|
}
|
|
return &t
|
|
}
|
|
|
|
func (t *Ticket) DefineField(f Field) {
|
|
f.Value = t.UnnamedFields[f.Index]
|
|
t.Fields[f.Name] = &f
|
|
}
|
|
|
|
func (t *Ticket) SetFields(fields []*Field) {
|
|
for _, f := range fields {
|
|
t.DefineField(*f)
|
|
}
|
|
}
|