Skip to content

Commit 00d1b5b

Browse files
committed
Solved day 22 part 1
1 parent 3beaec3 commit 00d1b5b

File tree

8 files changed

+339
-2
lines changed

8 files changed

+339
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
import java.util.Collections;
4+
import java.util.LinkedList;
5+
import java.util.List;
6+
import java.util.Queue;
7+
import java.util.function.Consumer;
8+
import java.util.function.Function;
9+
import java.util.function.Predicate;
10+
11+
public final class Algorithms {
12+
13+
private Algorithms() {
14+
}
15+
16+
public static <S> List<S> breadthFirstSearch(
17+
final S state,
18+
final Function<StateWrapper<S>,
19+
Iterable<S>> stateProducer,
20+
final Consumer<StateWrapper<S>> consumer,
21+
final Predicate<StateWrapper<S>> acceptanceCriterion
22+
) {
23+
final Queue<StateWrapper<S>> queue = new LinkedList<>();
24+
StateWrapper<S> currentState = new StateWrapper<>(state, null);
25+
queue.add(currentState);
26+
27+
if (consumer != null) {
28+
consumer.accept(currentState);
29+
}
30+
31+
while (!queue.isEmpty()) {
32+
currentState = queue.poll();
33+
final Iterable<S> possibleStates = stateProducer.apply(currentState);
34+
for (final S possibleState : possibleStates) {
35+
final StateWrapper<S> newState = new StateWrapper<>(possibleState, currentState);
36+
if (consumer != null) {
37+
consumer.accept(newState);
38+
}
39+
if (acceptanceCriterion.test(newState)) {
40+
return newState.getStates();
41+
}
42+
queue.add(newState);
43+
}
44+
}
45+
return Collections.emptyList();
46+
}
47+
}

day22/src/main/java/de/havox_design/aoc2015/day22/MainClass.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class MainClass {
66
private static final Logger LOGGER = Logger.getLogger(MainClass.class.getName());
77

88
public static void main(String[] args) {
9-
LOGGER.info("Solution 1: 13");
10-
LOGGER.info("Solution 2: 23");
9+
LOGGER.info(() -> "Solution 1: " + RPGWizardFight.solvePart1("day22.txt"));
10+
LOGGER.info(() -> "Solution 2: " + RPGWizardFight.solvePart2("day22.txt"));
1111
}
1212
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
record RPGBoss(int hitPoints, int damage) {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
record RPGPlayer(int hitPoints, int mana, int manaSpent) {
4+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
import java.util.Arrays;
4+
5+
enum RPGSpell {
6+
MAGIC_MISSILE(53),
7+
DRAIN(73),
8+
SHIELD(113),
9+
POISON(173),
10+
RECHARGE(229);
11+
12+
private final int costs;
13+
14+
RPGSpell(int costs) {
15+
this.costs = costs;
16+
}
17+
18+
static RPGSpell[] getPossibleSpells(int budget, int[] durations) {
19+
return Arrays
20+
.stream(values())
21+
.filter(sp -> sp.costs <= budget && durations[sp.ordinal()] <= 1)
22+
.toArray(RPGSpell[]::new);
23+
}
24+
25+
public int getCosts() {
26+
return costs;
27+
}
28+
29+
public int duration() {
30+
return switch (this) {
31+
case MAGIC_MISSILE, DRAIN -> 0;
32+
case SHIELD, POISON -> 6;
33+
case RECHARGE -> 5;
34+
};
35+
}
36+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
import java.util.Arrays;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
public class RPGState {
7+
8+
final RPGPlayer player;
9+
10+
final RPGBoss boss;
11+
12+
final AtomicInteger best;
13+
14+
private final int[] spellDuration;
15+
16+
public RPGState(RPGPlayer player, RPGBoss boss, int[] spellDuration, AtomicInteger best) {
17+
this.player = player;
18+
this.boss = boss;
19+
this.spellDuration = spellDuration;
20+
this.best = best;
21+
}
22+
23+
public RPGState apply(final RPGSpell spell) {
24+
int playerHitPoints = player.hitPoints();
25+
int playerMana = player.mana() - spell.getCosts();
26+
int bossHitPoints = boss.hitPoints();
27+
int bossDamage = boss.damage();
28+
29+
final int[] newSpellDuration = Arrays.copyOf(spellDuration, spellDuration.length);
30+
31+
// players turn
32+
if (newSpellDuration[RPGSpell.POISON.ordinal()] > 0) {
33+
bossHitPoints -= 3;
34+
}
35+
if (newSpellDuration[RPGSpell.RECHARGE.ordinal()] > 0) {
36+
playerMana += 101;
37+
}
38+
for (int i = 0; i < newSpellDuration.length; i++) {
39+
newSpellDuration[i] = Math.max(0, newSpellDuration[i] - 1);
40+
}
41+
newSpellDuration[spell.ordinal()] = spell.duration();
42+
43+
if (spell == RPGSpell.MAGIC_MISSILE) {
44+
bossHitPoints -= 4;
45+
}
46+
if (spell == RPGSpell.DRAIN) {
47+
bossHitPoints -= 2;
48+
playerHitPoints += 2;
49+
}
50+
51+
if (bossHitPoints <= 0) { // win
52+
return new RPGState
53+
(
54+
new RPGPlayer
55+
(
56+
playerHitPoints,
57+
playerMana,
58+
player.manaSpent() + spell.getCosts()
59+
),
60+
new RPGBoss
61+
(
62+
bossHitPoints,
63+
bossDamage
64+
),
65+
newSpellDuration,
66+
best
67+
);
68+
}
69+
70+
// boss turn
71+
if (newSpellDuration[RPGSpell.POISON.ordinal()] > 0) {
72+
bossHitPoints -= 3;
73+
}
74+
if (newSpellDuration[RPGSpell.RECHARGE.ordinal()] > 0) {
75+
playerMana += 101;
76+
}
77+
for (int i = 0; i < newSpellDuration.length; i++) {
78+
newSpellDuration[i] = Math.max(0, newSpellDuration[i] - 1);
79+
}
80+
if (bossHitPoints <= 0) { // win
81+
return new RPGState
82+
(
83+
new RPGPlayer
84+
(
85+
playerHitPoints,
86+
playerMana,
87+
player.manaSpent() + spell.getCosts()
88+
),
89+
new RPGBoss
90+
(
91+
bossHitPoints,
92+
bossDamage
93+
),
94+
newSpellDuration,
95+
best
96+
);
97+
}
98+
99+
playerHitPoints -= Math.max(1, bossDamage - (newSpellDuration[RPGSpell.SHIELD.ordinal()] > 0 ? 7 : 0));
100+
101+
return new RPGState
102+
(
103+
new RPGPlayer
104+
(
105+
playerHitPoints,
106+
playerMana,
107+
player.manaSpent() + spell.getCosts()
108+
),
109+
new RPGBoss
110+
(
111+
bossHitPoints,
112+
bossDamage
113+
),
114+
newSpellDuration,
115+
best
116+
);
117+
}
118+
119+
public int[] getSpellDuration() {
120+
return spellDuration;
121+
}
122+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
import de.havox_design.aoc2015.utils.DataReader;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.List;
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
import java.util.regex.Matcher;
10+
import java.util.regex.Pattern;
11+
12+
public class RPGWizardFight {
13+
private final List<String> input;
14+
15+
public RPGWizardFight(String fileName) {
16+
input = readData(fileName);
17+
}
18+
19+
public static int solvePart1(String fileName) {
20+
RPGWizardFight instance = new RPGWizardFight(fileName);
21+
return instance.solvePart1();
22+
}
23+
24+
public static int solvePart2(String fileName) {
25+
RPGWizardFight instance = new RPGWizardFight(fileName);
26+
return instance.solvePart2();
27+
}
28+
29+
public int solvePart1() {
30+
return calc();
31+
}
32+
33+
public int solvePart2() {
34+
return 0;
35+
}
36+
37+
private Integer calc() {
38+
int hitPoints = Integer.parseInt(matchRegex("Hit Points: (\\d+)", input.get(0)).group(1));
39+
int damage = Integer.parseInt(matchRegex("Damage: (\\d+)", input.get(1)).group(1));
40+
RPGBoss boss = new RPGBoss(hitPoints, damage);
41+
RPGPlayer player = new RPGPlayer(50, 500, 0);
42+
AtomicInteger best = new AtomicInteger(Integer.MAX_VALUE);
43+
RPGState state = new RPGState(player, boss, new int[RPGSpell.values().length], best);
44+
45+
Algorithms.breadthFirstSearch(state, this::producer, (x -> {
46+
if (x.getState().player.hitPoints() >= 0 && x.getState().boss.hitPoints() <= 0) {
47+
best.getAndAccumulate(x.getState().player.manaSpent(), Math::min);
48+
}
49+
}), s -> false);
50+
51+
return best.get();
52+
}
53+
54+
public Iterable<RPGState> producer(StateWrapper<RPGState> wrappedState) {
55+
RPGState state = wrappedState.getState();
56+
if (state.player.hitPoints() <= 0 || state.boss.hitPoints() <= 0) {
57+
return Collections.emptyList();
58+
}
59+
RPGSpell[] spells = RPGSpell.getPossibleSpells(state.player.mana(), state.getSpellDuration());
60+
List<RPGState> result = new ArrayList<>();
61+
for (RPGSpell spell : spells) {
62+
RPGState test = state.apply(spell);
63+
if (test.player.manaSpent() < test.best.get()) {
64+
result.add(test);
65+
}
66+
}
67+
if (result.stream().anyMatch(s -> s.player.hitPoints() > 0 & s.boss.hitPoints() <= 0)) {
68+
result.removeIf(s -> s.player.hitPoints() <= 0 || s.boss.hitPoints() > 0);
69+
}
70+
return result;
71+
}
72+
73+
public Matcher matchRegex(final String regex, final CharSequence input) {
74+
return matchRegex(Pattern.compile(regex), input);
75+
}
76+
77+
public static Matcher matchRegex(final Pattern pattern, final CharSequence input) {
78+
final Matcher matcher = pattern.matcher(input);
79+
if (matcher.matches()) {
80+
return matcher;
81+
} else {
82+
throw new IllegalArgumentException("Input '" + input + "' does not match pattern " + pattern.pattern());
83+
}
84+
}
85+
86+
private List<String> readData(String fileName) {
87+
return DataReader.readData(fileName, MainClass.class);
88+
}
89+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package de.havox_design.aoc2015.day22;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class StateWrapper<StateT> {
7+
8+
private final StateT state;
9+
10+
private final StateWrapper<StateT> predecessor;
11+
12+
StateWrapper(final StateT state, final StateWrapper<StateT> predecessor) {
13+
this.state = state;
14+
this.predecessor = predecessor;
15+
}
16+
17+
public StateT getState() {
18+
return state;
19+
}
20+
21+
public StateWrapper<StateT> getPredecessor() {
22+
return predecessor;
23+
}
24+
25+
public List<StateT> getStates() {
26+
final List<StateT> stateList;
27+
if (predecessor == null) {
28+
stateList = new ArrayList<>();
29+
} else {
30+
stateList = predecessor.getStates();
31+
}
32+
stateList.add(state);
33+
return stateList;
34+
}
35+
}

0 commit comments

Comments
 (0)