about summary refs log tree commit diff
path: root/Day14.cs
blob: 873d74f56ae085a8b2c01e6ecf3f5268c12be3ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using System;
using System.Collections.Generic;
using System.Linq;

namespace aoc2019
{
    internal sealed class Day14 : Day
    {
        private readonly Dictionary<string, Reaction> reactions;

        private Dictionary<string, long> available;

        public Day14()
        {
            reactions = Input
                .Select(Reaction.Parse)
                .ToDictionary(r => r.product.Name);
        }

        public override int DayNumber => 14;

        private bool Consume(string chem, long quantity)
        {
            if (quantity <= 0)
                throw new ArgumentOutOfRangeException(nameof(quantity));

            if (!available.ContainsKey(chem))
                available[chem] = 0;

            if (available[chem] < quantity && !Produce(chem, quantity - available[chem]))
                return false;

            available[chem] -= quantity;
            return true;
        }

        private bool Produce(string chem, long quantity)
        {
            if (chem == "ORE")
                return false;

            var reaction = reactions[chem];
            var reactionCount = (long) Math.Ceiling((double) quantity / reaction.product.Quantity);

            foreach (var reactant in reaction.reactants)
                if (!Consume(reactant.Name, reactionCount * reactant.Quantity))
                    return false;

            available[chem] = available.GetValueOrDefault(chem) + reactionCount * reaction.product.Quantity;
            return true;
        }

        protected override string Part1()
        {
            available = new Dictionary<string, long> {{"ORE", long.MaxValue}};
            Consume("FUEL", 1);
            return $"{long.MaxValue - available["ORE"]}";
        }

        protected override string Part2()
        {
            const long capacity = 1_000_000_000_000;
            available = new Dictionary<string, long> {{"ORE", capacity}};
            Consume("FUEL", 1);

            var oreConsumed = capacity - available["ORE"];
            while (Produce("FUEL", Math.Max(1, available["ORE"] / oreConsumed)))
            {
            }

            return $"{available["FUEL"] + 1}";
        }

        private struct Component
        {
            public string Name { get; set; }
            public int Quantity { get; set; }
        }

        private class Reaction
        {
            public readonly Component product;
            public readonly Component[] reactants;

            private Reaction(Component[] reactants, Component product)
            {
                this.reactants = reactants;
                this.product = product;
            }

            public static Reaction Parse(string s)
            {
                var ss = s.Split(new[] {", ", " => "}, StringSplitOptions.None);

                return new Reaction(
                    ss.Take(ss.Length - 1).Select(ParseComponent).ToArray(),
                    ParseComponent(ss[^1])
                );

                static Component ParseComponent(string s)
                {
                    var spl = s.Split(' ', 2);
                    return new Component
                    {
                        Quantity = int.Parse(spl[0]),
                        Name = spl[1]
                    };
                }
            }
        }
    }
}