aoc2019/aoc2019/Day14.cs

107 lines
2.8 KiB
C#
Raw Normal View History

2021-11-09 21:52:21 +00:00
namespace aoc2019;
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
public sealed class Day14 : Day
2019-12-16 06:51:19 +00:00
{
2021-11-09 21:52:21 +00:00
private readonly Dictionary<string, Reaction> reactions;
private Dictionary<string, long> available;
public Day14() : base(14, "Space Stoichiometry")
2019-12-16 06:51:19 +00:00
{
2021-11-09 21:52:21 +00:00
reactions = Input
.Select(Reaction.Parse)
.ToDictionary(r => r.Product.Name);
2021-11-09 21:52:21 +00:00
available = new();
}
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
private bool Consume(string chem, long quantity)
{
if (quantity <= 0)
throw new ArgumentOutOfRangeException(nameof(quantity));
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
if (!available.ContainsKey(chem))
available[chem] = 0;
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
if (available[chem] < quantity && !Produce(chem, quantity - available[chem]))
return false;
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
available[chem] -= quantity;
return true;
}
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
private bool Produce(string chem, long quantity)
{
if (chem == "ORE")
return false;
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
var reaction = reactions[chem];
var reactionCount = (long)Math.Ceiling((double)quantity / reaction.Product.Quantity);
2019-12-16 06:51:19 +00:00
if (reaction.Reactants.Any(reactant => !Consume(reactant.Name, reactionCount * reactant.Quantity)))
return false;
2019-12-16 06:51:19 +00:00
available[chem] = available.GetValueOrDefault(chem) + reactionCount * reaction.Product.Quantity;
2021-11-09 21:52:21 +00:00
return true;
}
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
public override string Part1()
{
available = new() { { "ORE", long.MaxValue } };
2021-11-09 21:52:21 +00:00
Consume("FUEL", 1);
return $"{long.MaxValue - available["ORE"]}";
}
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
public override string Part2()
{
const long capacity = 1_000_000_000_000;
available = new() { { "ORE", capacity } };
2021-11-09 21:52:21 +00:00
Consume("FUEL", 1);
2019-12-16 06:51:19 +00:00
2021-11-09 21:52:21 +00:00
var oreConsumed = capacity - available["ORE"];
while (Produce("FUEL", Math.Max(1, available["ORE"] / oreConsumed)))
2019-12-16 06:51:19 +00:00
{
}
2021-11-09 21:52:21 +00:00
return $"{available["FUEL"] + 1}";
}
2020-12-02 06:31:01 +00:00
2021-11-09 21:52:21 +00:00
private struct Component
{
public string Name { get; init; }
public int Quantity { get; init; }
2021-11-09 21:52:21 +00:00
}
2020-12-02 06:31:01 +00:00
2021-11-09 21:52:21 +00:00
private class Reaction
{
public readonly Component Product;
public readonly Component[] Reactants;
2021-11-09 21:52:21 +00:00
private Reaction(Component[] reactants, Component product)
{
Reactants = reactants;
Product = product;
}
2021-11-09 21:52:21 +00:00
public static Reaction Parse(string s)
{
2021-11-09 21:52:21 +00:00
var ss = s.Split(new[] { ", ", " => " }, StringSplitOptions.None);
return new(
2021-11-09 21:52:21 +00:00
ss.Take(ss.Length - 1).Select(ParseComponent).ToArray(),
ParseComponent(ss[^1])
);
2021-11-09 21:52:21 +00:00
static Component ParseComponent(string s)
{
2021-11-09 21:52:21 +00:00
var spl = s.Split(' ', 2);
return new()
{
2021-11-09 21:52:21 +00:00
Quantity = int.Parse(spl[0]),
Name = spl[1]
};
}
}
2019-12-16 06:51:19 +00:00
}
2021-11-09 21:52:21 +00:00
}