Day 16: Part 2

Signed-off-by: James Griffin <james@unsupervised.ca>
This commit is contained in:
2020-12-16 12:35:36 -04:00
parent b633408e52
commit 86b00e46a4
5 changed files with 200 additions and 11 deletions

View File

@@ -69,4 +69,5 @@ func main() {
// Day 16
fmt.Println(sixteen.PartOne())
fmt.Println(sixteen.PartTwo())
}

View File

@@ -10,17 +10,21 @@ import (
type ticket struct {
values []int
fields []string
}
type constraint struct {
min, max int
}
type constraints []constraint
type ticketScanner struct {
rules map[string][]constraint
rules map[string]constraints
me *ticket
nearby []ticket
valid []ticket
fields []string
}
func (ts *ticketScanner) load(filename string) error {
@@ -30,7 +34,7 @@ func (ts *ticketScanner) load(filename string) error {
}
defer file.Close()
ts.rules = make(map[string][]constraint)
ts.rules = make(map[string]constraints)
var rules, myticket bool
scanner := bufio.NewScanner(file)
for scanner.Scan() {
@@ -95,19 +99,23 @@ func (ts *ticketScanner) load(filename string) error {
return nil
}
func (c *constraints) valid(input int) bool {
for _, r := range *c {
if input >= r.min && input <= r.max {
return true
}
}
return false
}
func (ts *ticketScanner) nearbyErrorRate() int {
errorRate := 0
for _, t := range ts.nearby {
for _, value := range t.values {
valid := false
for _, rule := range ts.rules {
for _, r := range rule {
if value >= r.min && value <= r.max {
valid = true
break
}
}
if valid {
if rule.valid(value) {
valid = true
break
}
}
@@ -120,6 +128,109 @@ func (ts *ticketScanner) nearbyErrorRate() int {
return errorRate
}
func (ts *ticketScanner) scanValid() {
for _, t := range ts.nearby {
validTicket := true
for _, v := range t.values {
validValue := false
for _, r := range ts.rules {
if r.valid(v) {
validValue = true
break
}
}
if !validValue {
validTicket = false
break
}
}
if validTicket {
ts.valid = append(ts.valid, t)
}
}
}
func (ts *ticketScanner) determineFields() {
ts.fields = make([]string, len(ts.rules))
possibilities := map[string][]int{}
for name, r := range ts.rules {
possibilities[name] = []int{}
for i := 0; i < len(ts.fields); i++ {
isValid := true
for j := 0; j < len(ts.valid); j++ {
if !r.valid(ts.valid[j].values[i]) {
isValid = false
}
}
if isValid {
possibilities[name] = append(possibilities[name], i)
}
}
}
for i := 0; i < len(ts.fields); i++ {
if ts.fields[i] != "" {
continue
}
options := []string{}
for name, indexes := range possibilities {
for _, index := range indexes {
if i == index {
options = append(options, name)
}
}
}
if len(options) == 1 {
ts.fields[i] = options[0]
possibilities = removeIndex(i, options[0], possibilities)
continue
}
min := len(ts.fields)
minKey := ""
for _, option := range options {
if len(possibilities[option]) < min {
min = len(possibilities[option])
minKey = option
}
}
ts.fields[i] = minKey
possibilities = removeIndex(i, minKey, possibilities)
}
}
func removeIndex(index int, used string, possibilities map[string][]int) map[string][]int {
updatedPossibilites := make(map[string][]int)
for key, indexes := range possibilities {
if used == key {
continue
}
updatedIndexes := []int{}
for _, i := range indexes {
if i != index {
updatedIndexes = append(updatedIndexes, i)
}
}
updatedPossibilites[key] = updatedIndexes
}
return updatedPossibilites
}
func (ts *ticketScanner) fieldProduct(prefix string) int {
product := 1
for i, f := range ts.fields {
if strings.Contains(f, prefix) {
product *= ts.me.values[i]
}
}
return product
}
// PartOne What is my nearby ticket scanning error rate
func PartOne() string {
ts := ticketScanner{}
@@ -129,3 +240,16 @@ func PartOne() string {
return fmt.Sprintf("The nearby scanning error rate is %d", ts.nearbyErrorRate())
}
// PartTwo What is the product of all fields beginning with "departure"
func PartTwo() string {
ts := ticketScanner{}
if err := ts.load("sixteen/input.txt"); err != nil {
return err.Error()
}
ts.scanValid()
ts.determineFields()
return fmt.Sprintf("The product of departure is %d", ts.fieldProduct("departure"))
}

View File

@@ -24,7 +24,7 @@ func Test_ticket_loading(t *testing.T) {
t.Fail()
}
}
func Test_ticket_scanerror(t *testing.T) {
ts := ticketScanner{}
if err := ts.load("sample.txt"); err != nil {
@@ -38,3 +38,45 @@ func Test_ticket_scanerror(t *testing.T) {
t.Fail()
}
}
func Test_ticket_fieldDetection(t *testing.T) {
ts := ticketScanner{}
if err := ts.load("sample2.txt"); err != nil {
t.Log(err.Error())
t.FailNow()
}
ts.scanValid()
if len(ts.valid) != 3 {
t.Logf("Expected 3 valid tickets, got %d", len(ts.valid))
t.FailNow()
}
ts.determineFields()
if len(ts.fields) != 3 || ts.fields[0] != "row" || ts.fields[1] != "class" || ts.fields[2] != "seat" {
t.Logf("Expected row, class, seat but got %v", ts.fields)
t.Fail()
}
}
func Test_ticket_product(t *testing.T) {
ts := ticketScanner{}
if err := ts.load("sample3.txt"); err != nil {
t.Log(err.Error())
t.FailNow()
}
ts.scanValid()
if len(ts.valid) != 3 {
t.Logf("Expected 3 valid tickets, got %d", len(ts.valid))
t.FailNow()
}
ts.determineFields()
p := ts.fieldProduct("departure")
if p != 132 {
t.Logf("Expected 132 got %d", p)
t.Fail()
}
}

11
sixteen/sample2.txt Normal file
View File

@@ -0,0 +1,11 @@
class: 0-1 or 4-19
row: 0-5 or 8-19
seat: 0-13 or 16-19
your ticket:
11,12,13
nearby tickets:
3,9,18
15,1,5
5,14,9

11
sixteen/sample3.txt Normal file
View File

@@ -0,0 +1,11 @@
departure class: 0-1 or 4-19
departure row: 0-5 or 8-19
arrival seat: 0-13 or 16-19
your ticket:
11,12,13
nearby tickets:
3,9,18
15,1,5
5,14,9