-
Notifications
You must be signed in to change notification settings - Fork 10
/
Day21.cs
110 lines (95 loc) · 3.71 KB
/
Day21.cs
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
using AdventOfCode.CSharp.Common;
using System;
namespace AdventOfCode.CSharp.Y2015.Solvers;
public class Day21 : ISolver
{
public record Equipment(int Cost, int Damage, int Armor);
private static readonly Equipment[] s_weapons =
[
new(8, 4, 0),
new(10, 5, 0),
new(25, 6, 0),
new(40, 7, 0),
new(74, 8, 0)
];
private static readonly Equipment[] s_armor =
[
new(0, 0, 0),
new(13, 0, 1),
new(31, 0, 2),
new(53, 0, 3),
new(75, 0, 4),
new(102, 0, 5)
];
private static readonly Equipment[] s_rings =
[
new(0, 0, 0),
new(25, 1, 0),
new(50, 2, 0),
new(100, 3, 0),
new(20, 0, 1),
new(40, 0, 2),
new(80, 0, 3)
];
public static void Solve(ReadOnlySpan<byte> input, Solution solution)
{
ParseInput(input, out int bossHp, out int bossDamage, out int bossArmor);
int cheapestWin = int.MaxValue;
int mostExpensiveLoss = int.MinValue;
foreach (Equipment weapon in s_weapons)
{
foreach (Equipment armor in s_armor)
{
for (int ring1Index = 0; ring1Index < s_rings.Length; ring1Index++)
{
Equipment ring1 = s_rings[ring1Index];
for (int ring2Index = ring1Index; ring2Index < s_rings.Length; ring2Index++)
{
// can't pick same ring twice, except for no ring
if (ring1Index == ring2Index && ring1Index > 0)
{
continue;
}
Equipment purchase = Purchase(weapon, armor, ring1, s_rings[ring2Index]);
if (DoesPlayerWin(purchase, bossHp, bossDamage, bossArmor))
{
cheapestWin = Math.Min(purchase.Cost, cheapestWin);
}
else
{
mostExpensiveLoss = Math.Max(purchase.Cost, mostExpensiveLoss);
}
}
}
}
}
solution.SubmitPart1(cheapestWin);
solution.SubmitPart2(mostExpensiveLoss);
}
private static void ParseInput(ReadOnlySpan<byte> input, out int bossHp, out int bossDamage, out int bossArmor)
{
var reader = new SpanReader(input);
reader.SkipLength("Hit Points: ".Length);
bossHp = reader.ReadPosIntUntil('\n');
reader.SkipLength("Damage: ".Length);
bossDamage = reader.ReadPosIntUntil('\n');
reader.SkipLength("Armor: ".Length);
bossArmor = reader.ReadPosIntUntil('\n');
}
private static Equipment Purchase(Equipment weapon, Equipment armor, Equipment ring1, Equipment ring2)
{
return new Equipment(
weapon.Cost + armor.Cost + ring1.Cost + ring2.Cost,
weapon.Damage + armor.Damage + ring1.Damage + ring2.Damage,
weapon.Armor + armor.Armor + ring1.Armor + ring2.Armor);
}
private static bool DoesPlayerWin(Equipment equipment, int bossHp, int bossDamage, int bossArmor)
{
const int playerHp = 100;
int actualPlayerDamage = Math.Max(1, equipment.Damage - bossArmor);
int actualBossDamage = Math.Max(1, bossDamage - equipment.Armor);
int turnsTillPlayerDies = playerHp / actualBossDamage + (playerHp % actualBossDamage == 0 ? 0 : 1);
int turnsTillBossDies = bossHp / actualPlayerDamage + (bossHp % actualPlayerDamage == 0 ? 0 : 1);
return turnsTillBossDies <= turnsTillPlayerDies;
}
}