Passed
Pull Request — master (#193)
by Vincent
11:12
created

boostScore(CastSimulation)   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
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.simulation.CastSimulation;
24
import fr.quatrevieux.araknemu.game.fight.ai.simulation.Simulator;
25
import fr.quatrevieux.araknemu.game.fight.turn.action.Action;
26
27
import java.util.Optional;
28
29
/**
30
 * Try to attack enemies
31
 *
32
 * Select spells causing damage on enemies
33
 * All cells are tested for select the most effective target for each spells
34
 */
35
public final class Attack implements ActionGenerator, CastSpell.SimulationSelector {
36
    private final CastSpell generator;
37
    private final SuicideStrategy suicideStrategy;
38
39 1
    private double averageEnemyLifePoints = 0;
40 1
    private int enemiesCount = 0;
41
42
    public Attack(Simulator simulator) {
43 1
        this(simulator, SuicideStrategy.IF_KILL_ENEMY);
44 1
    }
45
46 1
    public Attack(Simulator simulator, SuicideStrategy suicideStrategy) {
47 1
        this.generator = new CastSpell(simulator, this);
48 1
        this.suicideStrategy = suicideStrategy;
49 1
    }
50
51
    @Override
52
    public void initialize(AI ai) {
53 1
        generator.initialize(ai);
54 1
        averageEnemyLifePoints = ai.helper().enemies().stream().mapToInt(fighter -> fighter.life().max()).average().orElse(0);
55 1
        enemiesCount = ai.helper().enemies().count();
56 1
    }
57
58
    @Override
59
    public Optional<Action> generate(AI ai) {
60 1
        return generator.generate(ai);
61
    }
62
63
    @Override
64
    public boolean valid(CastSimulation simulation) {
65 1
        if (simulation.enemiesLife() >= 0) {
66 1
            return false;
67
        }
68
69
        // Kill all enemies
70 1
        if (simulation.killedEnemies() >= enemiesCount) {
71 1
            return true;
72
        }
73
74 1
        if (!suicideStrategy.allow(simulation)) {
75 1
            return false;
76
        }
77
78
        // Kill more allies than enemies
79 1
        if (simulation.killedAllies() > simulation.killedEnemies()) {
80 1
            return false;
81
        }
82
83
        // At least one enemy will be killed
84 1
        if (simulation.killedEnemies() >= 0.99) {
85 1
            return true;
86
        }
87
88
        // Cause more damage on enemies than allies
89 1
        return simulation.enemiesLife() < simulation.alliesLife() + simulation.selfLife();
90
    }
91
92
    @Override
93
    public boolean compare(CastSimulation a, CastSimulation b) {
94 1
        return score(a) > score(b);
95
    }
96
97
    /**
98
     * Compute the score for the given simulation
99
     *
100
     * @param simulation The simulation result
101
     *
102
     * @return The score of the simulation. 0 is null
103
     */
104
    public double score(CastSimulation simulation) {
105 1
        final double score = damageScore(simulation) + killScore(simulation) + boostScore(simulation);
106
107 1
        return score / simulation.spell().apCost();
108
    }
109
110
    private double damageScore(CastSimulation simulation) {
111 1
        return - simulation.enemiesLife() + simulation.alliesLife() + simulation.selfLife() * 2;
112
    }
113
114
    private double killScore(CastSimulation simulation) {
115 1
        final double killRatio = simulation.killedEnemies()
116 1
            - 1.5 * simulation.killedAllies()
117 1
            - 2 * simulation.suicideProbability()
118
        ;
119
120 1
        return averageEnemyLifePoints * killRatio;
121
    }
122
123
    private double boostScore(CastSimulation simulation) {
124 1
        return (simulation.alliesBoost() + simulation.selfBoost() - simulation.enemiesBoost()) / 10;
125
    }
126
127
    /**
128
     * Filter the cast by the suicide probability
129
     *
130
     * @see CastSimulation#suicideProbability()
131
     */
132 1
    enum SuicideStrategy {
133
        /**
134
         * Always allow suicide
135
         * Should be used on The Sacrificial Doll AI
136
         */
137 1
        ALLOW {
138
            @Override
139
            public boolean allow(CastSimulation simulation) {
140 1
                return true;
141
            }
142
        },
143
144
        /**
145
         * Suicide is never accepted
146
         */
147 1
        DENY {
148
            @Override
149
            public boolean allow(CastSimulation simulation) {
150 1
                return simulation.suicideProbability() <= 0;
151
            }
152
        },
153
154
        /**
155
         * Suicide is accepted only if there is more chance (or number) to kill an enemy
156
         */
157 1
        IF_KILL_ENEMY {
158
            @Override
159
            public boolean allow(CastSimulation simulation) {
160 1
                return simulation.suicideProbability() <= simulation.killedEnemies();
161
            }
162
        },
163
        ;
164
165
        /**
166
         * Does the simulation is allowed about the suicide probability ?
167
         *
168
         * @param simulation The cast simulation to check
169
         *
170
         * @return false if the cast must not be performed
171
         */
172
        public abstract boolean allow(CastSimulation simulation);
173
    }
174
}
175