Skip to content

Commit 501e562

Browse files
committed
Solved Day 21 part 2 (2015) - RPG Simulator 20XX
1 parent 0ab3e7c commit 501e562

File tree

1 file changed

+196
-17
lines changed
  • src/main/java/com/sbaars/adventofcode/year15/days

1 file changed

+196
-17
lines changed
Lines changed: 196 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,202 @@
11
package com.sbaars.adventofcode.year15.days;
22

33
import com.sbaars.adventofcode.year15.Day2015;
4+
import java.util.*;
5+
import java.util.stream.Collectors;
46

57
public class Day21 extends Day2015 {
6-
public Day21() {
7-
super(21);
8-
}
9-
10-
public static void main(String[] args) {
11-
new Day21().printParts();
12-
}
13-
14-
@Override
15-
public Object part1() {
16-
return "";
17-
}
18-
19-
@Override
20-
public Object part2() {
21-
return "";
22-
}
8+
private static final int PLAYER_HP = 100;
9+
private static final List<Item> WEAPONS = Arrays.asList(
10+
new Item("Dagger", 8, 4, 0),
11+
new Item("Shortsword", 10, 5, 0),
12+
new Item("Warhammer", 25, 6, 0),
13+
new Item("Longsword", 40, 7, 0),
14+
new Item("Greataxe", 74, 8, 0)
15+
);
16+
17+
private static final List<Item> ARMOR = Arrays.asList(
18+
new Item("Leather", 13, 0, 1),
19+
new Item("Chainmail", 31, 0, 2),
20+
new Item("Splintmail", 53, 0, 3),
21+
new Item("Bandedmail", 75, 0, 4),
22+
new Item("Platemail", 102, 0, 5)
23+
);
24+
25+
private static final List<Item> RINGS = Arrays.asList(
26+
new Item("Damage +1", 25, 1, 0),
27+
new Item("Damage +2", 50, 2, 0),
28+
new Item("Damage +3", 100, 3, 0),
29+
new Item("Defense +1", 20, 0, 1),
30+
new Item("Defense +2", 40, 0, 2),
31+
new Item("Defense +3", 80, 0, 3)
32+
);
33+
34+
private record Item(String name, int cost, int damage, int armor) {}
35+
private record Character(int hp, int damage, int armor) {}
36+
private record Equipment(Item weapon, Item armor, Item ring1, Item ring2) {
37+
public int getTotalCost() {
38+
return weapon.cost() +
39+
(armor != null ? armor.cost() : 0) +
40+
(ring1 != null ? ring1.cost() : 0) +
41+
(ring2 != null ? ring2.cost() : 0);
42+
}
43+
44+
public int getTotalDamage() {
45+
return weapon.damage() +
46+
(armor != null ? armor.damage() : 0) +
47+
(ring1 != null ? ring1.damage() : 0) +
48+
(ring2 != null ? ring2.damage() : 0);
49+
}
50+
51+
public int getTotalArmor() {
52+
return (armor != null ? armor.armor() : 0) +
53+
(ring1 != null ? ring1.armor() : 0) +
54+
(ring2 != null ? ring2.armor() : 0);
55+
}
56+
}
57+
58+
public Day21() {
59+
super(21);
60+
}
61+
62+
public static void main(String[] args) {
63+
Day21 day = new Day21();
64+
day.printParts();
65+
new com.sbaars.adventofcode.network.Submit().submit(day.part2(), 2015, 21, 2);
66+
}
67+
68+
@Override
69+
public Object part1() {
70+
Character boss = parseBoss();
71+
return findMinGoldToWin(boss);
72+
}
73+
74+
@Override
75+
public Object part2() {
76+
Character boss = parseBoss();
77+
return findMaxGoldToLose(boss);
78+
}
79+
80+
private Character parseBoss() {
81+
List<String> lines = dayStream().collect(Collectors.toList());
82+
int hp = Integer.parseInt(lines.get(0).split(": ")[1]);
83+
int damage = Integer.parseInt(lines.get(1).split(": ")[1]);
84+
int armor = Integer.parseInt(lines.get(2).split(": ")[1]);
85+
return new Character(hp, damage, armor);
86+
}
87+
88+
private int findMinGoldToWin(Character boss) {
89+
int minGold = Integer.MAX_VALUE;
90+
91+
// Try all possible equipment combinations
92+
for (Item weapon : WEAPONS) {
93+
// Try with no armor and no rings
94+
minGold = Math.min(minGold, tryEquipment(weapon, null, null, null, boss));
95+
96+
// Try with armor but no rings
97+
for (Item armor : ARMOR) {
98+
minGold = Math.min(minGold, tryEquipment(weapon, armor, null, null, boss));
99+
}
100+
101+
// Try with one ring, no armor
102+
for (Item ring1 : RINGS) {
103+
minGold = Math.min(minGold, tryEquipment(weapon, null, ring1, null, boss));
104+
}
105+
106+
// Try with two rings, no armor
107+
for (int i = 0; i < RINGS.size(); i++) {
108+
for (int j = i + 1; j < RINGS.size(); j++) {
109+
minGold = Math.min(minGold, tryEquipment(weapon, null, RINGS.get(i), RINGS.get(j), boss));
110+
}
111+
}
112+
113+
// Try with armor and one ring
114+
for (Item armor : ARMOR) {
115+
for (Item ring1 : RINGS) {
116+
minGold = Math.min(minGold, tryEquipment(weapon, armor, ring1, null, boss));
117+
}
118+
}
119+
120+
// Try with armor and two rings
121+
for (Item armor : ARMOR) {
122+
for (int i = 0; i < RINGS.size(); i++) {
123+
for (int j = i + 1; j < RINGS.size(); j++) {
124+
minGold = Math.min(minGold, tryEquipment(weapon, armor, RINGS.get(i), RINGS.get(j), boss));
125+
}
126+
}
127+
}
128+
}
129+
130+
return minGold;
131+
}
132+
133+
private int tryEquipment(Item weapon, Item armor, Item ring1, Item ring2, Character boss) {
134+
Equipment equipment = new Equipment(weapon, armor, ring1, ring2);
135+
Character player = new Character(PLAYER_HP, equipment.getTotalDamage(), equipment.getTotalArmor());
136+
137+
return simulateFight(player, boss) ? equipment.getTotalCost() : Integer.MAX_VALUE;
138+
}
139+
140+
private boolean simulateFight(Character player, Character boss) {
141+
int playerDamagePerTurn = Math.max(1, player.damage() - boss.armor());
142+
int bossDamagePerTurn = Math.max(1, boss.damage() - player.armor());
143+
144+
// Player goes first, so they need to kill the boss in equal or fewer turns
145+
int turnsToKillBoss = (boss.hp() + playerDamagePerTurn - 1) / playerDamagePerTurn;
146+
int turnsToKillPlayer = (player.hp() + bossDamagePerTurn - 1) / bossDamagePerTurn;
147+
148+
return turnsToKillBoss <= turnsToKillPlayer;
149+
}
150+
151+
private int findMaxGoldToLose(Character boss) {
152+
int maxGold = 0;
153+
154+
// Try all possible equipment combinations
155+
for (Item weapon : WEAPONS) {
156+
// Try with no armor and no rings
157+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, null, null, null, boss));
158+
159+
// Try with armor but no rings
160+
for (Item armor : ARMOR) {
161+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, armor, null, null, boss));
162+
}
163+
164+
// Try with one ring, no armor
165+
for (Item ring1 : RINGS) {
166+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, null, ring1, null, boss));
167+
}
168+
169+
// Try with two rings, no armor
170+
for (int i = 0; i < RINGS.size(); i++) {
171+
for (int j = i + 1; j < RINGS.size(); j++) {
172+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, null, RINGS.get(i), RINGS.get(j), boss));
173+
}
174+
}
175+
176+
// Try with armor and one ring
177+
for (Item armor : ARMOR) {
178+
for (Item ring1 : RINGS) {
179+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, armor, ring1, null, boss));
180+
}
181+
}
182+
183+
// Try with armor and two rings
184+
for (Item armor : ARMOR) {
185+
for (int i = 0; i < RINGS.size(); i++) {
186+
for (int j = i + 1; j < RINGS.size(); j++) {
187+
maxGold = Math.max(maxGold, tryEquipmentToLose(weapon, armor, RINGS.get(i), RINGS.get(j), boss));
188+
}
189+
}
190+
}
191+
}
192+
193+
return maxGold;
194+
}
195+
196+
private int tryEquipmentToLose(Item weapon, Item armor, Item ring1, Item ring2, Character boss) {
197+
Equipment equipment = new Equipment(weapon, armor, ring1, ring2);
198+
Character player = new Character(PLAYER_HP, equipment.getTotalDamage(), equipment.getTotalArmor());
199+
200+
return simulateFight(player, boss) ? 0 : equipment.getTotalCost();
201+
}
23202
}

0 commit comments

Comments
 (0)