Passed
Branch master (3c0c2d)
by Vincent
11:07
created

valid(CastSimulation)   B

Complexity

Conditions 6

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
dl 0
loc 27
ccs 11
cts 11
cp 1
crap 6
rs 8.6666
c 1
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2021 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.fight.ai.action;
21
22
import fr.quatrevieux.araknemu.game.fight.ai.AI;
23
import fr.quatrevieux.araknemu.game.fight.ai.action.util.CastSpell;
24
import fr.quatrevieux.araknemu.game.fight.ai.simulation.CastSimulation;
25
import fr.quatrevieux.araknemu.game.fight.ai.simulation.Simulator;
26
import fr.quatrevieux.araknemu.game.fight.turn.action.Action;
27
28
import java.util.Optional;
29
30
/**
31
 * Try to attack enemies
32
 *
33
 * Select spells causing damage on enemies
34
 * All cells are tested for select the most effective target for each spells
35
 */
36
public final class Attack implements ActionGenerator, CastSpell.SimulationSelector {
37
    private final CastSpell generator;
38
    private final SuicideStrategy suicideStrategy;
39
40 1
    private double averageEnemyLifePoints = 0;
41 1
    private int enemiesCount = 0;
42
43
    public Attack(Simulator simulator) {
44 1
        this(simulator, SuicideStrategy.IF_KILL_ENEMY);
45 1
    }
46
47 1
    public Attack(Simulator simulator, SuicideStrategy suicideStrategy) {
48 1
        this.generator = new CastSpell(simulator, this);
49 1
        this.suicideStrategy = suicideStrategy;
50 1
    }
51
52
    @Override
53
    public void initialize(AI ai) {
54 1
        generator.initialize(ai);
55 1
        averageEnemyLifePoints = ai.helper().enemies().stream().mapToInt(fighter -> fighter.life().max()).average().orElse(0);
56 1
        enemiesCount = ai.helper().enemies().count();
57 1
    }
58
59
    @Override
60
    public Optional<Action> generate(AI ai) {
61 1
        return generator.generate(ai);
62
    }
63
64
    @Override
65
    public boolean valid(CastSimulation simulation) {
66 1
        if (simulation.enemiesLife() >= 0) {
67 1
            return false;
68
        }
69
70
        // Kill all enemies
71 1
        if (simulation.killedEnemies() >= enemiesCount) {
72 1
            return true;
73
        }
74
75 1
        if (!suicideStrategy.allow(simulation)) {
76 1
            return false;
77
        }
78
79
        // Kill more allies than enemies
80 1
        if (simulation.killedAllies() > simulation.killedEnemies()) {
81 1
            return false;
82
        }
83
84
        // At least one enemy will be killed
85 1
        if (simulation.killedEnemies() >= 0.99) {
86 1
            return true;
87
        }
88
89
        // Cause more damage on enemies than allies
90 1
        return simulation.enemiesLife() < simulation.alliesLife() + simulation.selfLife();
91
    }
92
93
    @Override
94
    public boolean compare(CastSimulation a, CastSimulation b) {
95 1
        return score(a) > score(b);
96
    }
97
98
    /**
99
     * Compute the score for the given simulation
100
     *
101
     * @param simulation The simulation result
102
     *
103
     * @return The score of the simulation. 0 is null
104
     */
105
    public double score(CastSimulation simulation) {
106 1
        final double score = damageScore(simulation) + killScore(simulation) + boostScore(simulation);
107
108 1
        return score / simulation.spell().apCost();
109
    }
110
111
    private double damageScore(CastSimulation simulation) {
112 1
        return - simulation.enemiesLife() + simulation.alliesLife() + simulation.selfLife() * 2;
113
    }
114
115
    private double killScore(CastSimulation simulation) {
116 1
        final double killRatio = simulation.killedEnemies()
117 1
            - 1.5 * simulation.killedAllies()
118 1
            - 2 * simulation.suicideProbability()
119
        ;
120
121 1
        return averageEnemyLifePoints * killRatio;
122
    }
123
124
    private double boostScore(CastSimulation simulation) {
125 1
        return (simulation.alliesBoost() + simulation.selfBoost() - simulation.enemiesBoost()) / 10;
126
    }
127
128
    /**
129
     * Filter the cast by the suicide probability
130
     *
131
     * @see CastSimulation#suicideProbability()
132
     */
133 1
    enum SuicideStrategy {
134
        /**
135
         * Always allow suicide
136
         * Should be used on The Sacrificial Doll AI
137
         */
138 1
        ALLOW {
139
            @Override
140
            public boolean allow(CastSimulation simulation) {
141 1
                return true;
142
            }
143
        },
144
145
        /**
146
         * Suicide is never accepted
147
         */
148 1
        DENY {
149
            @Override
150
            public boolean allow(CastSimulation simulation) {
151 1
                return simulation.suicideProbability() <= 0;
152
            }
153
        },
154
155
        /**
156
         * Suicide is accepted only if there is more chance (or number) to kill an enemy
157
         */
158 1
        IF_KILL_ENEMY {
159
            @Override
160
            public boolean allow(CastSimulation simulation) {
161 1
                return simulation.suicideProbability() <= simulation.killedEnemies();
162
            }
163
        },
164
        ;
165
166
        /**
167
         * Does the simulation is allowed about the suicide probability ?
168
         *
169
         * @param simulation The cast simulation to check
170
         *
171
         * @return false if the cast must not be performed
172
         */
173
        public abstract boolean allow(CastSimulation simulation);
174
    }
175
}
176