Day 21: Part 2

Signed-off-by: James Griffin <james@unsupervised.ca>
This commit is contained in:
2020-12-21 21:23:33 -04:00
parent 9b3b8011df
commit ecafea7adf
3 changed files with 139 additions and 8 deletions

View File

@@ -89,4 +89,5 @@ func main() {
// Day 21 // Day 21
fmt.Println(twentyone.PartOne()) fmt.Println(twentyone.PartOne())
fmt.Println(twentyone.PartTwo())
} }

View File

@@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"sort"
"strings" "strings"
) )
@@ -13,9 +14,9 @@ type food struct {
} }
type list struct { type list struct {
foods []food foods []food
allergens map[string]bool allergens map[string]bool
possibleIngredientAllergens map[string][]string ingredientAllergens map[string][]string
} }
func (l *list) load(filename string) error { func (l *list) load(filename string) error {
@@ -26,7 +27,7 @@ func (l *list) load(filename string) error {
defer file.Close() defer file.Close()
l.allergens = map[string]bool{} l.allergens = map[string]bool{}
l.possibleIngredientAllergens = map[string][]string{} l.ingredientAllergens = map[string][]string{}
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
@@ -41,7 +42,7 @@ func (l *list) load(filename string) error {
ingredients := strings.Split(foodParts[0], " ") ingredients := strings.Split(foodParts[0], " ")
for _, i := range ingredients { for _, i := range ingredients {
f.ingredients[i] = true f.ingredients[i] = true
l.possibleIngredientAllergens[i] = []string{} l.ingredientAllergens[i] = []string{}
} }
allergens := strings.Split(foodParts[1], ")") allergens := strings.Split(foodParts[1], ")")
@@ -85,10 +86,72 @@ func (l *list) isolateIngredientAllergens() {
for i, cause := range possibleIngredients { for i, cause := range possibleIngredients {
if cause { if cause {
l.possibleIngredientAllergens[i] = append(l.possibleIngredientAllergens[i], allergen) l.ingredientAllergens[i] = append(l.ingredientAllergens[i], allergen)
} }
} }
} }
tmpIngredients := map[string][]string{}
// Remove duplicate allergens
for ingredient, allergens := range l.ingredientAllergens {
if len(allergens) < 1 {
continue
}
tmpIngredients[ingredient] = allergens
}
stable := false
for !stable {
causes := l.getIngredientsWithAllergens()
// Check to see if every ingredient has 1 allergen
stable = true
for _, allergens := range causes {
if len(allergens) != 1 {
stable = false
}
}
if stable {
break
}
// Find all allergens with with only 1 option. Remove that option from others
for i, a := range causes {
if len(a) == 1 {
l.claimAllergen(i, a[0])
}
}
}
}
func (l *list) getIngredientsWithAllergens() map[string][]string {
tmpIngredients := map[string][]string{}
// Remove duplicate allergens
for ingredient, allergens := range l.ingredientAllergens {
if len(allergens) < 1 {
continue
}
tmpIngredients[ingredient] = allergens
}
return tmpIngredients
}
func (l *list) claimAllergen(ingredient, allergen string) {
for i, alls := range l.ingredientAllergens {
if i == ingredient {
continue
}
filtered := []string{}
for _, a := range alls {
if a != allergen {
filtered = append(filtered, a)
}
}
l.ingredientAllergens[i] = filtered
}
} }
func (l *list) countAllergenFreeAppearances() (int, []string) { func (l *list) countAllergenFreeAppearances() (int, []string) {
@@ -96,7 +159,7 @@ func (l *list) countAllergenFreeAppearances() (int, []string) {
// Find allergen free ingredients // Find allergen free ingredients
ingredients := []string{} ingredients := []string{}
for ingredient, allergens := range l.possibleIngredientAllergens { for ingredient, allergens := range l.ingredientAllergens {
if len(allergens) == 0 { if len(allergens) == 0 {
ingredients = append(ingredients, ingredient) ingredients = append(ingredients, ingredient)
} }
@@ -116,6 +179,31 @@ func (l *list) countAllergenFreeAppearances() (int, []string) {
return count, ingredients return count, ingredients
} }
func (l *list) canonicalDangerList() string {
dangerous := []string{}
for _, allergens := range l.ingredientAllergens {
if len(allergens) == 1 {
dangerous = append(dangerous, allergens[0])
}
}
sort.Strings(dangerous)
ingredients := []string{}
for _, allergen := range dangerous {
for ingredient, allergens := range l.ingredientAllergens {
if len(allergens) == 1 {
if allergens[0] == allergen {
ingredients = append(ingredients, ingredient)
break
}
}
}
}
return strings.Join(ingredients, ",")
}
// PartOne How many times do allergen free ingredients appear // PartOne How many times do allergen free ingredients appear
func PartOne() string { func PartOne() string {
l := list{} l := list{}
@@ -125,3 +213,13 @@ func PartOne() string {
count, ingredients := l.countAllergenFreeAppearances() count, ingredients := l.countAllergenFreeAppearances()
return fmt.Sprintf("There are %d appearances of %d allergen free ingredients", count, len(ingredients)) return fmt.Sprintf("There are %d appearances of %d allergen free ingredients", count, len(ingredients))
} }
// PartTwo What is the canonical dangerous list of ingredients
func PartTwo() string {
l := list{}
l.load("twentyone/input.txt")
l.isolateIngredientAllergens()
return fmt.Sprintf("The canonical list of dangerous ingredients is %q", l.canonicalDangerList())
}

View File

@@ -16,7 +16,7 @@ func Test_loading_list(t *testing.T) {
t.FailNow() t.FailNow()
} }
ingredients := len(l.possibleIngredientAllergens) ingredients := len(l.ingredientAllergens)
if ingredients != 7 { if ingredients != 7 {
t.Logf("Expected 7 ingredients, found %d", ingredients) t.Logf("Expected 7 ingredients, found %d", ingredients)
t.FailNow() t.FailNow()
@@ -38,3 +38,35 @@ func Test_find_allergenfree(t *testing.T) {
t.FailNow() t.FailNow()
} }
} }
func Test_canonical_list(t *testing.T) {
l := list{}
err := l.load("sample.txt")
if err != nil {
t.Logf(err.Error())
t.FailNow()
}
l.isolateIngredientAllergens()
list := l.canonicalDangerList()
if list != "mxmxvkd,sqjhc,fvjkl" {
t.Logf("Expected list of mxmxvkd,sqjhc,fvjkl, got %s", list)
t.Fail()
}
}
func Test_fullcanonical_list(t *testing.T) {
l := list{}
err := l.load("input.txt")
if err != nil {
t.Logf(err.Error())
t.FailNow()
}
l.isolateIngredientAllergens()
list := l.canonicalDangerList()
if list != "vmhqr,qxfzc,khpdjv,gnrpml,xrmxxvn,rfmvh,rdfr,jxh" {
t.Logf("Expected list \"vmhqr,qxfzc,khpdjv,gnrpml,xrmxxvn,rfmvh,rdfr,jxh\", got %s", list)
t.Fail()
}
}