Skip to content

Commit

Permalink
Day 21 2020 (#222)
Browse files Browse the repository at this point in the history
  • Loading branch information
peckb1 authored Oct 28, 2023
1 parent 6216567 commit 5507219
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 0 deletions.
103 changes: 103 additions & 0 deletions src/main/kotlin/me/peckb/aoc/_2020/calendar/day21/Day21.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package me.peckb.aoc._2020.calendar.day21

import javax.inject.Inject

import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory

class Day21 @Inject constructor(
private val generatorFactory: InputGeneratorFactory,
) {
fun partOne(filename: String) = generatorFactory.forFile(filename).readAs(::food) { input ->
val (allergenToPossibilities, nonAllergenIngredientCounts) = generatePossibilitiesAndCounts(input)

allergenToPossibilities.forEach { (_, ingredientPossibilityCounts) ->
val sortedByCounts = ingredientPossibilityCounts.entries.sortedByDescending { it.value }
val topCountValue = sortedByCounts.first().value

val mightBe = sortedByCounts.filter { it.value == topCountValue }

mightBe.forEach {
nonAllergenIngredientCounts.remove(it.key)
}
}

nonAllergenIngredientCounts.entries.sumOf { it.value }
}

fun partTwo(filename: String) = generatorFactory.forFile(filename).readAs(::food) { input ->
val (allergenToPossibilities, _) = generatePossibilitiesAndCounts(input)

val probableAllergenIngredients = mutableMapOf<String, MutableMap<String, Int>>()

allergenToPossibilities.forEach { (allergen, ingredientPossibilityCounts) ->
val sortedByCounts = ingredientPossibilityCounts.entries.sortedByDescending { it.value }
val topCountValue = sortedByCounts.first().value

val mightBe= sortedByCounts.filter { it.value == topCountValue }

probableAllergenIngredients[allergen] = mightBe.associateBy({it.key}, {it.value}).toMutableMap()
}

val ingredientWithAllergen = mutableMapOf<String, String>()
while (probableAllergenIngredients.isNotEmpty()) {
val (guaranteedValues, valuesStillUnknown) = probableAllergenIngredients.entries.partition { it.value.count() == 1 }

guaranteedValues.forEach { (allergen, ingredientMap) ->
val ingredient = ingredientMap.entries.first().key

ingredientWithAllergen[allergen] = ingredient

valuesStillUnknown.forEach { (_, ingredientPossibilities) ->
ingredientPossibilities.remove(ingredient)
}
}

guaranteedValues.forEach {
probableAllergenIngredients.remove(it.key)
}
}

ingredientWithAllergen.toSortedMap().values.joinToString(",")
}

private fun food(line: String): Food {
val (ingredientsString, allergensString) = line.split(" (contains ")
val ingredients = ingredientsString.split(" ")
val allergens = allergensString.dropLast(1).split(", ")

return Food(ingredients, allergens)
}

private fun generatePossibilitiesAndCounts(input: Sequence<Food>): FoodContents {
val allergenToPossibilities = mutableMapOf<String, MutableMap<String, Int>>()
val nonAllergenIngredientCounts = mutableMapOf<String, Int>()

input.forEach { (ingredients, allergens) ->
ingredients.forEach { i -> nonAllergenIngredientCounts.merge(i, 1, Int::plus) }
allergens.forEach { allergen ->
allergenToPossibilities.merge(
allergen,
ingredients.associateWith { 1 }.toMutableMap()
) { originalMap, newMap ->
originalMap.apply {
newMap.forEach { (ingredient, count) ->
this.merge(ingredient, count, Int::plus)
}
}
}
}
}

return FoodContents(allergenToPossibilities, nonAllergenIngredientCounts)
}

data class FoodContents(
val allergenToPossibilities: MutableMap<String, MutableMap<String, Int>>,
val nonAllergenIngredientCounts: MutableMap<String, Int>
)

data class Food(
val ingredients: List<String>,
val allergens: List<String>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## [Day 21: Allergen Assessment](https://adventofcode.com/2020/day/21)
2 changes: 2 additions & 0 deletions src/test/kotlin/me/peckb/aoc/_2020/TestDayComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import me.peckb.aoc._2020.calendar.day17.Day17Test
import me.peckb.aoc._2020.calendar.day18.Day18Test
import me.peckb.aoc._2020.calendar.day19.Day19Test
import me.peckb.aoc._2020.calendar.day20.Day20Test
import me.peckb.aoc._2020.calendar.day21.Day21Test
import javax.inject.Singleton

import me.peckb.aoc.DayComponent
Expand Down Expand Up @@ -49,4 +50,5 @@ internal interface TestDayComponent : DayComponent {
fun inject(day18Test: Day18Test)
fun inject(day19Test: Day19Test)
fun inject(day20Test: Day20Test)
fun inject(day21Test: Day21Test)
}
33 changes: 33 additions & 0 deletions src/test/kotlin/me/peckb/aoc/_2020/calendar/day21/Day21Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.peckb.aoc._2020.calendar.day21

import javax.inject.Inject


import me.peckb.aoc._2020.DaggerTestDayComponent
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

internal class Day21Test {
@Inject
lateinit var day21: Day21

@BeforeEach
fun setup() {
DaggerTestDayComponent.create().inject(this)
}

@Test
fun testDay21PartOne() {
assertEquals(2786, day21.partOne(DAY_21))
}

@Test
fun testDay21PartTwo() {
assertEquals("prxmdlz,ncjv,knprxg,lxjtns,vzzz,clg,cxfz,qdfpq", day21.partTwo(DAY_21))
}

companion object {
private const val DAY_21: String = "advent-of-code-input/2020/day21.input"
}
}

0 comments on commit 5507219

Please sign in to comment.