Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f38dbe6

Browse files
Solve day 19 2015 part 2: Medicine for Rudolph
1 parent 6583632 commit f38dbe6

File tree

3 files changed

+275
-26
lines changed

3 files changed

+275
-26
lines changed

‎src/main/java/com/sbaars/adventofcode/network/DownloadPuzzles.java‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ public DownloadPuzzles() {
1717
public static void main(String[] args) {
1818
DownloadPuzzles dlPuzzles = new DownloadPuzzles();
1919
dlPuzzles.runForYear("2015");
20-
dlPuzzles.runForYear("2016");
21-
dlPuzzles.runForYear("2017");
22-
dlPuzzles.runForYear("2018");
23-
dlPuzzles.runForYear("2019");
24-
dlPuzzles.runForYear("2020");
25-
dlPuzzles.runForYear("2021");
26-
dlPuzzles.runForYear("2022");
27-
dlPuzzles.runForYear("2023");
2820
}
2921

3022
public void retrieveTests(String day, String year) {

‎src/main/java/com/sbaars/adventofcode/year15/days/Day19.java‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static void main(String[] args) {
2020
Day19 day = new Day19();
2121
day.printParts();
2222
new com.sbaars.adventofcode.network.Submit().submit(day.part1(), 2015, 19, 1);
23+
new com.sbaars.adventofcode.network.Submit().submit(day.part2(), 2015, 19, 2);
2324
}
2425

2526
private void parseInput() {
@@ -65,6 +66,28 @@ public Object part1() {
6566

6667
@Override
6768
public Object part2() {
68-
return 0; // Implement in next part
69+
// Count total elements (uppercase followed by optional lowercase)
70+
int elements = molecule.replaceAll("[^A-Z]", "").length();
71+
72+
// Count special elements
73+
int rn = countOccurrences(molecule, "Rn");
74+
int ar = countOccurrences(molecule, "Ar");
75+
int y = countOccurrences(molecule, "Y");
76+
77+
// Formula: elements - rn - ar - 2*y - 1
78+
// Each Rn...Ar pair reduces steps by 2
79+
// Each Y reduces steps by 2 (as it's always between Rn and Ar)
80+
// Subtract 1 for the initial 'e'
81+
return elements - rn - ar - 2 * y - 1;
82+
}
83+
84+
private int countOccurrences(String str, String substr) {
85+
int count = 0;
86+
int index = 0;
87+
while ((index = str.indexOf(substr, index)) >= 0) {
88+
count++;
89+
index += substr.length();
90+
}
91+
return count;
6992
}
7093
}
Lines changed: 251 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,257 @@
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 Day22 extends Day2015 {
6-
public Day22() {
7-
super(22);
8-
}
9-
10-
public static void main(String[] args) {
11-
new Day22().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 = 50;
9+
private static final int PLAYER_MANA = 500;
10+
11+
private enum Spell {
12+
MAGIC_MISSILE(53, 0, 4, 0, 0, 0),
13+
DRAIN(73, 0, 2, 2, 0, 0),
14+
SHIELD(113, 6, 0, 0, 7, 0),
15+
POISON(173, 6, 0, 0, 0, 3),
16+
RECHARGE(229, 5, 0, 0, 0, 0);
17+
18+
final int cost;
19+
final int duration;
20+
final int damage;
21+
final int heal;
22+
final int armor;
23+
final int manaRegen;
24+
25+
Spell(int cost, int duration, int damage, int heal, int armor, int manaRegen) {
26+
this.cost = cost;
27+
this.duration = duration;
28+
this.damage = damage;
29+
this.heal = heal;
30+
this.armor = armor;
31+
this.manaRegen = manaRegen;
32+
}
33+
}
34+
35+
private record GameState(int playerHp, int playerMana, int bossHp, Map<Spell, Integer> activeEffects) {
36+
public GameState applyEffects() {
37+
int newPlayerHp = playerHp;
38+
int newPlayerMana = playerMana;
39+
int newBossHp = bossHp;
40+
41+
Map<Spell, Integer> newEffects = new HashMap<>();
42+
for (var entry : activeEffects.entrySet()) {
43+
Spell spell = entry.getKey();
44+
int duration = entry.getValue();
45+
46+
if (duration > 0) {
47+
if (spell == Spell.POISON) {
48+
newBossHp -= 3;
49+
} else if (spell == Spell.RECHARGE) {
50+
newPlayerMana += 101;
51+
}
52+
53+
if (duration > 1) {
54+
newEffects.put(spell, duration - 1);
55+
}
56+
}
57+
}
58+
59+
return new GameState(newPlayerHp, newPlayerMana, newBossHp, newEffects);
60+
}
61+
62+
public boolean canCastSpell(Spell spell) {
63+
return playerMana >= spell.cost && !activeEffects.containsKey(spell);
64+
}
65+
66+
public GameState castSpell(Spell spell) {
67+
Map<Spell, Integer> newEffects = new HashMap<>(activeEffects);
68+
int newPlayerHp = playerHp;
69+
int newPlayerMana = playerMana - spell.cost;
70+
int newBossHp = bossHp;
71+
72+
if (spell.duration > 0) {
73+
newEffects.put(spell, spell.duration);
74+
} else {
75+
if (spell == Spell.MAGIC_MISSILE) {
76+
newBossHp -= 4;
77+
} else if (spell == Spell.DRAIN) {
78+
newBossHp -= 2;
79+
newPlayerHp += 2;
80+
}
81+
}
82+
83+
return new GameState(newPlayerHp, newPlayerMana, newBossHp, newEffects);
84+
}
85+
86+
public int getArmor() {
87+
return activeEffects.containsKey(Spell.SHIELD) && activeEffects.get(Spell.SHIELD) > 0 ? 7 : 0;
88+
}
89+
90+
@Override
91+
public boolean equals(Object o) {
92+
if (this == o) return true;
93+
if (o == null || getClass() != o.getClass()) return false;
94+
GameState that = (GameState) o;
95+
return playerHp == that.playerHp &&
96+
playerMana == that.playerMana &&
97+
bossHp == that.bossHp &&
98+
activeEffects.equals(that.activeEffects);
99+
}
100+
101+
@Override
102+
public int hashCode() {
103+
return Objects.hash(playerHp, playerMana, bossHp, activeEffects);
104+
}
105+
}
106+
107+
public Day22() {
108+
super(22);
109+
}
110+
111+
public static void main(String[] args) {
112+
Day22 day = new Day22();
113+
day.printParts();
114+
new com.sbaars.adventofcode.network.Submit().submit(day.part2(), 2015, 22, 2);
115+
}
116+
117+
@Override
118+
public Object part1() {
119+
int[] bossStats = parseBoss();
120+
return findMinManaToWin(bossStats[0], bossStats[1]);
121+
}
122+
123+
@Override
124+
public Object part2() {
125+
int[] bossStats = parseBoss();
126+
return findMinManaToWinHardMode(bossStats[0], bossStats[1]);
127+
}
128+
129+
private int[] parseBoss() {
130+
List<String> lines = dayStream().collect(Collectors.toList());
131+
int hp = Integer.parseInt(lines.get(0).split(": ")[1]);
132+
int damage = Integer.parseInt(lines.get(1).split(": ")[1]);
133+
return new int[]{hp, damage};
134+
}
135+
136+
private int findMinManaToWin(int bossHp, int bossDamage) {
137+
PriorityQueue<Map.Entry<GameState, Integer>> queue = new PriorityQueue<>(Map.Entry.comparingByValue());
138+
Set<GameState> visited = new HashSet<>();
139+
140+
GameState initialState = new GameState(PLAYER_HP, PLAYER_MANA, bossHp, new HashMap<>());
141+
queue.offer(Map.entry(initialState, 0));
142+
143+
while (!queue.isEmpty()) {
144+
var entry = queue.poll();
145+
GameState state = entry.getKey();
146+
int totalMana = entry.getValue();
147+
148+
if (visited.contains(state)) {
149+
continue;
150+
}
151+
visited.add(state);
152+
153+
// Player's turn - Apply effects first
154+
GameState afterEffects = state.applyEffects();
155+
if (afterEffects.bossHp <= 0) {
156+
return totalMana;
157+
}
158+
159+
// Try each spell
160+
for (Spell spell : Spell.values()) {
161+
if (!afterEffects.canCastSpell(spell)) {
162+
continue;
163+
}
164+
165+
GameState afterSpell = afterEffects.castSpell(spell);
166+
if (afterSpell.bossHp <= 0) {
167+
return totalMana + spell.cost;
168+
}
169+
170+
// Boss's turn - Apply effects first
171+
GameState afterBossEffects = afterSpell.applyEffects();
172+
if (afterBossEffects.bossHp <= 0) {
173+
return totalMana + spell.cost;
174+
}
175+
176+
// Boss attacks
177+
int damage = Math.max(1, bossDamage - afterBossEffects.getArmor());
178+
GameState afterBossTurn = new GameState(
179+
afterBossEffects.playerHp - damage,
180+
afterBossEffects.playerMana,
181+
afterBossEffects.bossHp,
182+
afterBossEffects.activeEffects
183+
);
184+
185+
if (afterBossTurn.playerHp > 0) {
186+
queue.offer(Map.entry(afterBossTurn, totalMana + spell.cost));
187+
}
188+
}
189+
}
190+
191+
return -1; // No solution found
192+
}
193+
194+
private int findMinManaToWinHardMode(int bossHp, int bossDamage) {
195+
PriorityQueue<Map.Entry<GameState, Integer>> queue = new PriorityQueue<>(Map.Entry.comparingByValue());
196+
Set<GameState> visited = new HashSet<>();
197+
198+
GameState initialState = new GameState(PLAYER_HP, PLAYER_MANA, bossHp, new HashMap<>());
199+
queue.offer(Map.entry(initialState, 0));
200+
201+
while (!queue.isEmpty()) {
202+
var entry = queue.poll();
203+
GameState state = entry.getKey();
204+
int totalMana = entry.getValue();
205+
206+
if (visited.contains(state)) {
207+
continue;
208+
}
209+
visited.add(state);
210+
211+
// Hard mode: Player loses 1 HP at start of turn
212+
GameState afterHardMode = new GameState(state.playerHp() - 1, state.playerMana(), state.bossHp(), state.activeEffects());
213+
if (afterHardMode.playerHp() <= 0) {
214+
continue;
215+
}
216+
217+
// Player's turn - Apply effects first
218+
GameState afterEffects = afterHardMode.applyEffects();
219+
if (afterEffects.bossHp() <= 0) {
220+
return totalMana;
221+
}
222+
223+
// Try each spell
224+
for (Spell spell : Spell.values()) {
225+
if (!afterEffects.canCastSpell(spell)) {
226+
continue;
227+
}
228+
229+
GameState afterSpell = afterEffects.castSpell(spell);
230+
if (afterSpell.bossHp() <= 0) {
231+
return totalMana + spell.cost;
232+
}
233+
234+
// Boss's turn - Apply effects first
235+
GameState afterBossEffects = afterSpell.applyEffects();
236+
if (afterBossEffects.bossHp() <= 0) {
237+
return totalMana + spell.cost;
238+
}
239+
240+
// Boss attacks
241+
int damage = Math.max(1, bossDamage - afterBossEffects.getArmor());
242+
GameState afterBossTurn = new GameState(
243+
afterBossEffects.playerHp() - damage,
244+
afterBossEffects.playerMana(),
245+
afterBossEffects.bossHp(),
246+
afterBossEffects.activeEffects()
247+
);
248+
249+
if (afterBossTurn.playerHp() > 0) {
250+
queue.offer(Map.entry(afterBossTurn, totalMana + spell.cost));
251+
}
252+
}
253+
}
254+
255+
return -1; // No solution found
256+
}
23257
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /