I did the last part by hand first, then bothered to write out the reduction loop
This commit is contained in:
parent
353d5e92c9
commit
0883efd35e
229
16/main.go
Normal file
229
16/main.go
Normal file
@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type valuerange struct {
|
||||
min int
|
||||
max int
|
||||
}
|
||||
|
||||
type rule struct {
|
||||
name string
|
||||
ranges []valuerange
|
||||
field int
|
||||
}
|
||||
|
||||
type ticket struct {
|
||||
values []int
|
||||
valid bool
|
||||
}
|
||||
|
||||
func parseRule(s string) rule {
|
||||
q := strings.Split(s, ": ")
|
||||
ru := rule{
|
||||
name: q[0],
|
||||
ranges: make([]valuerange, 0, 2),
|
||||
}
|
||||
r := strings.Split(q[1], " or ")
|
||||
for _, rr := range r {
|
||||
mm := strings.Split(rr, "-")
|
||||
min, _ := strconv.Atoi(mm[0])
|
||||
max, _ := strconv.Atoi(mm[1])
|
||||
ru.ranges = append(ru.ranges, valuerange{min: min, max: max})
|
||||
}
|
||||
return ru
|
||||
}
|
||||
|
||||
func parseTicket(s string) ticket {
|
||||
t := ticket{
|
||||
values: []int{},
|
||||
}
|
||||
vs := strings.Split(s, ",")
|
||||
for _, v := range vs {
|
||||
vi, _ := strconv.Atoi(v)
|
||||
t.values = append(t.values, vi)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// return valid/invalid and the invalid value, if found
|
||||
func validateTicket(r []rule, t ticket) (bool, int) {
|
||||
for _, ta := range t.values {
|
||||
ok := false
|
||||
for _, ru := range r {
|
||||
outsideRule := (ta < ru.ranges[0].min || (ta > ru.ranges[0].max && ta < ru.ranges[1].min) || ta > ru.ranges[1].max)
|
||||
if ok || !outsideRule {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, ta
|
||||
}
|
||||
}
|
||||
return true, 0
|
||||
}
|
||||
|
||||
// returns a mapping of fields to possible rules
|
||||
func guessFields(rules []rule, tickets []ticket) map[int][]int {
|
||||
guesses := map[int][]int{}
|
||||
for ri, r := range rules {
|
||||
for i := range tickets[0].values { // loop over the fields on tickets, and...
|
||||
ok := true
|
||||
for _, t := range tickets { // loop over all the tickets...
|
||||
ta := t.values[i] // checking the current field value...
|
||||
outsideRule := (ta < r.ranges[0].min || (ta > r.ranges[0].max && ta < r.ranges[1].min) || ta > r.ranges[1].max)
|
||||
if !ok || outsideRule { // checking if any of the tickets fall outside our rule's range.
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if ok { // if all tickets pass, we've got a hit
|
||||
guesses[i] = append(guesses[i], ri)
|
||||
}
|
||||
}
|
||||
}
|
||||
return guesses
|
||||
}
|
||||
|
||||
func reduce(g map[int][]int) (int, int) {
|
||||
for i, j := range g {
|
||||
if len(j) == 1 {
|
||||
return i, j[0]
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func remove(a []int, rule int) []int {
|
||||
for i, j := range a {
|
||||
if j == rule {
|
||||
a[i] = a[len(a)-1]
|
||||
a[len(a)-1] = 0
|
||||
return a[:len(a)-1]
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
rules := []rule{}
|
||||
tickets := []ticket{}
|
||||
invalidityRate := 0
|
||||
|
||||
seenAllRules := false
|
||||
seenMyTicket := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// check for end of rules list
|
||||
if line == "" && !seenAllRules {
|
||||
seenAllRules = true
|
||||
scanner.Scan() // clear out the following line
|
||||
continue
|
||||
}
|
||||
// parse rules until we run out
|
||||
if !seenAllRules {
|
||||
rules = append(rules, parseRule(line))
|
||||
continue
|
||||
}
|
||||
// parse own ticket, then clear for remaining tickets
|
||||
if !seenMyTicket {
|
||||
tickets = append(tickets, parseTicket(scanner.Text()))
|
||||
seenMyTicket = true
|
||||
scanner.Scan() // clear out the next two lines
|
||||
scanner.Scan()
|
||||
continue
|
||||
}
|
||||
// parse and validate other people's tickets
|
||||
t := parseTicket(line)
|
||||
ok, i := validateTicket(rules, t)
|
||||
if !ok {
|
||||
t.valid = false
|
||||
invalidityRate = invalidityRate + i
|
||||
} else {
|
||||
t.valid = true
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
}
|
||||
fmt.Println(invalidityRate)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
rules := []rule{}
|
||||
myTicket := ticket{}
|
||||
tickets := []ticket{}
|
||||
|
||||
seenAllRules := false
|
||||
seenMyTicket := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// check for end of rules list
|
||||
if line == "" && !seenAllRules {
|
||||
seenAllRules = true
|
||||
scanner.Scan() // clear out the following line
|
||||
continue
|
||||
}
|
||||
// parse rules until we run out
|
||||
if !seenAllRules {
|
||||
rules = append(rules, parseRule(line))
|
||||
continue
|
||||
}
|
||||
// parse own ticket, then clear for remaining tickets
|
||||
if !seenMyTicket {
|
||||
myTicket = parseTicket(scanner.Text())
|
||||
tickets = append(tickets, myTicket)
|
||||
seenMyTicket = true
|
||||
scanner.Scan() // clear out the next two lines
|
||||
scanner.Scan()
|
||||
continue
|
||||
}
|
||||
// parse and validate other people's tickets
|
||||
t := parseTicket(line)
|
||||
ok, _ := validateTicket(rules, t)
|
||||
if ok {
|
||||
t.valid = true
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
}
|
||||
guesses := guessFields(rules, tickets)
|
||||
var lastRuleSolved int
|
||||
for i := 1; i < len(guesses); i++ {
|
||||
for i, g := range guesses {
|
||||
if len(g) == 1 {
|
||||
rules[g[0]].field = i
|
||||
lastRuleSolved = g[0]
|
||||
}
|
||||
}
|
||||
for i := range guesses {
|
||||
guesses[i] = remove(guesses[i], lastRuleSolved)
|
||||
}
|
||||
}
|
||||
result := 1
|
||||
// first 6 rules are our "departure" rules
|
||||
for _, r := range rules[:6] {
|
||||
result = result * myTicket.values[r.field]
|
||||
}
|
||||
fmt.Println(result)
|
||||
}
|
Loading…
Reference in New Issue
Block a user