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) } }