1
main.go
1
main.go
@@ -89,4 +89,5 @@ func main() {
|
|||||||
|
|
||||||
// Day 21
|
// Day 21
|
||||||
fmt.Println(twentyone.PartOne())
|
fmt.Println(twentyone.PartOne())
|
||||||
|
fmt.Println(twentyone.PartTwo())
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ 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())
|
||||||
|
}
|
||||||
|
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user