package main import ( "bufio" "fmt" "os" "strconv" "strings" ) // Rule ... type Rule struct { Name string Ranges [][2]int Not []int Column int } // IsValid ... func (r Rule) IsValid(val int) bool { for _, i := range r.Ranges { if val >= i[0] && val <= i[1] { return true } } return false } // Ticket ... type Ticket struct { Values []int } // Task1 ... func Task1(rules []Rule, tickets []Ticket) int { var result int for _, t := range tickets { for _, i := range t.Values { var valid bool for _, r := range rules { if r.IsValid(i) { valid = true break } } if !valid { result += i } } } return result } // Task2 ... func Task2(rules []Rule, tickets []Ticket, targetTicket Ticket) int { // Find valid tickets. validTickets := make([]Ticket, 0, len(tickets)/2) for _, t := range tickets { var invalid bool for _, i := range t.Values { var valid bool for _, r := range rules { if r.IsValid(i) { valid = true break } } if !valid { invalid = true break } } if !invalid { validTickets = append(validTickets, t) } } columns := make(map[int]int) for i := 0; i < len(validTickets[0].Values); i++ { columns[i] = -1 } numColumns := len(columns) // Whittle down columns one by one by identifying which rule is _not_ valid in // a given column's values, then removing that column from the iteration, and // running over it again. for numColumns > 0 { for i := range rules { if rules[i].Column > -1 { continue } rules[i].Not = make([]int, 0, 2) } for i := 0; i < len(columns); i++ { if columns[i] > -1 { continue } for _, t := range validTickets { for ri, r := range rules { if r.Column > -1 { continue } if !r.IsValid(t.Values[i]) { rules[ri].Not = append(r.Not, i) } } } } for ri, r := range rules { if r.Column > -1 { continue } if len(r.Not) == numColumns-1 { for c, n := range columns { if n > -1 { continue } var found bool for _, n := range r.Not { if n == c { found = true break } } if !found { columns[c] = ri rules[ri].Column = c numColumns-- break } } } } } // Calculate result by multiplying the correct column-values from the target // ticket. result := 1 for _, r := range rules { if strings.HasPrefix(r.Name, "departure") { result *= targetTicket.Values[r.Column] } } return result } func main() { file, _ := os.Open("input.txt") scanner := bufio.NewScanner(file) rules := make([]Rule, 0, 10) for scanner.Scan() { line := scanner.Text() if line == "" { break } parts := strings.Split(line, ": ") rangeParts := strings.Split(parts[1], " or ") ranges := make([][2]int, len(rangeParts)) for i := range rangeParts { fmt.Sscanf(rangeParts[i], "%d-%d", &ranges[i][0], &ranges[i][1]) } rules = append(rules, Rule{ Name: parts[0], Ranges: ranges, Not: make([]int, 0, 2), Column: -1, }) } scanner.Scan() scanner.Scan() // My ticket parts := strings.Split(scanner.Text(), ",") values := make([]int, len(parts)) for i := range parts { values[i], _ = strconv.Atoi(parts[i]) } myTicket := Ticket{ Values: values, } scanner.Scan() scanner.Scan() tickets := make([]Ticket, 0, 10) for scanner.Scan() { parts := strings.Split(scanner.Text(), ",") values := make([]int, len(parts)) for i := range parts { values[i], _ = strconv.Atoi(parts[i]) } tickets = append(tickets, Ticket{ Values: values, }) } file.Close() result := Task1(rules, tickets) fmt.Printf("Task 1: %d\n", result) result = Task2(rules, tickets, myTicket) fmt.Printf("Task 2: %d\n", result) }