2020 Day 16 Complete!
This commit is contained in:
223
2020/day16/main.go
Normal file
223
2020/day16/main.go
Normal file
@@ -0,0 +1,223 @@
|
||||
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 15")
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user