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
fmt.Println(twentyone.PartOne())
fmt.Println(twentyone.PartTwo())
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"sort"
"strings"
)
@@ -13,9 +14,9 @@ type food struct {
}
type list struct {
foods []food
allergens map[string]bool
possibleIngredientAllergens map[string][]string
foods []food
allergens map[string]bool
ingredientAllergens map[string][]string
}
func (l *list) load(filename string) error {
@@ -26,7 +27,7 @@ func (l *list) load(filename string) error {
defer file.Close()
l.allergens = map[string]bool{}
l.possibleIngredientAllergens = map[string][]string{}
l.ingredientAllergens = map[string][]string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
@@ -41,7 +42,7 @@ func (l *list) load(filename string) error {
ingredients := strings.Split(foodParts[0], " ")
for _, i := range ingredients {
f.ingredients[i] = true
l.possibleIngredientAllergens[i] = []string{}
l.ingredientAllergens[i] = []string{}
}
allergens := strings.Split(foodParts[1], ")")
@@ -85,10 +86,72 @@ func (l *list) isolateIngredientAllergens() {
for i, cause := range possibleIngredients {
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) {
@@ -96,7 +159,7 @@ func (l *list) countAllergenFreeAppearances() (int, []string) {
// Find allergen free ingredients
ingredients := []string{}
for ingredient, allergens := range l.possibleIngredientAllergens {
for ingredient, allergens := range l.ingredientAllergens {
if len(allergens) == 0 {
ingredients = append(ingredients, ingredient)
}
@@ -116,6 +179,31 @@ func (l *list) countAllergenFreeAppearances() (int, []string) {
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
func PartOne() string {
l := list{}
@@ -125,3 +213,13 @@ func PartOne() string {
count, ingredients := l.countAllergenFreeAppearances()
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()
}
ingredients := len(l.possibleIngredientAllergens)
ingredients := len(l.ingredientAllergens)
if ingredients != 7 {
t.Logf("Expected 7 ingredients, found %d", ingredients)
t.FailNow()
@@ -38,3 +38,35 @@ func Test_find_allergenfree(t *testing.T) {
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()
}
}