package be.vandewalleh.aoc.days import be.vandewalleh.aoc.utils.input.Day import be.vandewalleh.aoc.utils.input.Input import be.vandewalleh.aoc.utils.input.Lines import org.eclipse.collections.api.factory.Bags import org.eclipse.collections.api.multimap.set.MutableSetMultimap import org.eclipse.collections.impl.factory.Multimaps @Day(21) class Day21(@Lines val input: Input>) { private val foods = input.value.map { line -> val parOpen = line.indexOf('(') val ingredients = line.substring(0 until parOpen - 1).split(" ") val allergens = line.substring(parOpen + 1 until line.length - 1).removePrefix("contains ").split(", ") ingredients to allergens } private val allIngredients = foods.flatMap { it.first }.toSet() private val allAllergens = foods.flatMap { it.second }.toSet() private val dangerousIngredients = dangerousIngredients() fun part1(): Int { val occurrences = Bags.mutable.empty() foods.forEach { (ingredients) -> occurrences.addAll(ingredients) } return allIngredients.filter { !dangerousIngredients.containsValue(it) } .map { ingredient -> occurrences.count { it == ingredient } } .sum() } fun part2(): String { while (!dangerousIngredients.multiValuesView().all { it.size() == 1 }) { dangerousIngredients.multiValuesView() .filter { it.size() == 1 } .map { it.first() } .forEach { removeMe -> dangerousIngredients.keyMultiValuePairsView() .filter { it.two.size() != 1 && it.two.contains(removeMe) } .forEach { dangerousIngredients.remove(it.one, removeMe) } } } return dangerousIngredients.keySet().sorted().joinToString(",") { dangerousIngredients.get(it).first() } } private fun dangerousIngredients(): MutableSetMultimap { val map = Multimaps.mutable.set.empty() allAllergens.forEach { map.putAll(it, allIngredients) } foods.forEach { (ingredients, allergens) -> allergens.forEach { allergen -> allIngredients.forEach { if (!ingredients.contains(it)) map.remove(allergen, it) } } } return map } }