Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
Et dans le projet calcul, couverture de test de 87,3%...
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
03-01-2023, 09:34 PM
(This post was last modified: 03-01-2023, 09:35 PM by Philou.)
Et enfin fini la validation du calcul.
Cette fois, on passe à la Factory (le Design Pattern du GOFF).
Pour commencer, comme on fait de l'introspection, on utilise cette librairie (dans le POM service):
Code: <!--Introspection-->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${jar.reflexion}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Le métier (data et calcul) n'aimant pas avoir des librairies (sauf Apache Common Math pour le calcul), l'injection se fait chez service où on a le célèbre framework Spring.
Pour commencer, on définit la factory par un contrat simple:
Code: package com.calculateur.warhammer.calcul.mort.factory.validation;
import java.util.List;
import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
/**
* Factory pour les validations avant le calcul
* @author phili
*
*/
public interface IFactoryValidation {
/**
*
* @param simule Ce qui est simulé.
* @return La liste des validations à effectuer avant la simulation/calcul.
* @throws SimulationException Si ce qui est demandé est incohérent.
*/
List<IValidationCalcul<IArmeDeTir>> getValidationsActionTir(ESimule simule) throws SimulationException;
/**
*
* @param simule Ce qui est simulé.
* @return La liste des validations pour un corps à corps.
* @throws SimulationException Si ce qui est demandé est incohérent.
*/
List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsActionCorpsACorps(ESimule simule) throws SimulationException;
}
Et son implémentation:
Code: package com.calculateur.warhammer.calcul.mort.factory.validation;
import java.util.List;
import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class FactoryValidation implements IFactoryValidation{
private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;
private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;
private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
public FactoryValidation(List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir,
List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge,
List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps) {
this.validationsPhaseTir = validationsPhaseTir;
this.validationsTirContreCharge = validationsTirContreCharge;
this.validationsCorpsACorps = validationsCorpsACorps;
}
@Override
public List<IValidationCalcul<IArmeDeTir>> getValidationsActionTir(ESimule simule) throws SimulationException {
if(simule.isActionCorpACorp()) {
Object[] param = {simule};
throw new SimulationException(param, "simule.tir.demande.melee");
}
return (simule == ESimule.TIR_PHASE_TIR)?validationsPhaseTir:validationsTirContreCharge;
}
@Override
public List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsActionCorpsACorps(ESimule simule)
throws SimulationException {
if(simule.isActionTir()) {
Object[] param = {simule};
throw new SimulationException(param, "simule.melee.demande.tir");
}
return validationsCorpsACorps;
}
}
Si l'utilisateur envoie de la merde, on lui chie dessus:
Code: package com.calculateur.warhammer.calcul.mort.factory.exception;
import com.calculateur.warhammer.base.exception.ParameterizedFunctionnalException;
public class SimulationException extends ParameterizedFunctionnalException{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final String RES = "com.calculateur.warhammer.calcul.mort.simulation.simulation";
public SimulationException(Object[] objets, String key) {
super(RES,objets, key);
}
}
Le tout testé, contrairement à certains rigolos et leurs projets à 60 Millions d'€:
Code: package com.calculateur.warhammer.calcul.mort.factory.validation;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.parametres.ParametresBuilder;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.calcul.mort.validation.ValidationArmeCorpsACorps;
import com.calculateur.warhammer.calcul.mort.validation.ValidationArmeTir;
import com.calculateur.warhammer.calcul.mort.validation.ValidationDefenseur;
import com.calculateur.warhammer.calcul.mort.validation.ValidationTirContreChargeMelee;
import com.calculateur.warhammer.calcul.mort.validation.ValidationTypeArme;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class FactoryValidationTest {
private static final Locale[] LOCALES = {Locale.FRANCE,Locale.ENGLISH};
private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;
private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;
private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
private final IFactoryValidation factory;
public FactoryValidationTest() {
validationsPhaseTir = Arrays.asList(new ValidationArmeTir(),new ValidationDefenseur<>());
validationsTirContreCharge = Arrays.asList(new ValidationTypeArme<>(),new ValidationTirContreChargeMelee());
validationsCorpsACorps = Arrays.asList(new ValidationArmeCorpsACorps(),new ValidationDefenseur<>());
ParametresBuilder builder = ParametresBuilder.getInstance();
validationsPhaseTir.forEach(builder::addValidationPhaseTir);
validationsTirContreCharge.forEach(builder::addValidationTirContreCharge);
validationsCorpsACorps.forEach(builder::addValidationCorpsACorps);
factory = builder.build().getFactoryValidation();
}
@Test
public void testContextTir() {
List<IValidationCalcul<IArmeDeTir>> lTest;
List<IValidationCalcul<IArmeDeTir>> lAttendue;
for(ESimule simule:ESimule.values()) {
try {
lTest = factory.getValidationsActionTir(simule);
Assertions.assertThat(simule.isActionTir()).isTrue();
lAttendue = (simule == ESimule.TIR_PHASE_TIR)?validationsPhaseTir:validationsTirContreCharge;
Assertions.assertThat(lTest).isEqualTo(lAttendue);
}catch(SimulationException ex) {
Assertions.assertThat(simule.isActionCorpACorp()).isTrue();
valideException(ex);
}
}
}
@Test
public void testContextMelee() {
List<IValidationCalcul<IArmeDeCorpsACorps>> lTest;
for(ESimule simule:ESimule.values()) {
try {
lTest = factory.getValidationsActionCorpsACorps(simule);
Assertions.assertThat(lTest).isEqualTo(validationsCorpsACorps);
}catch(SimulationException ex) {
Assertions.assertThat(simule.isActionTir()).isTrue();
valideException(ex);
}
}
}
private void valideException(SimulationException ex) {
String key = ex.getMessage();
Object[] params = ex.getObjets();
Assertions.assertThat(key).isNotNull();
ResourceBundle res;
for(Locale locale:LOCALES) {
res = ex.getResourceBundle(locale);
ex.traduireLibelles(locale);
Assertions.assertThat(ex.getMessage()).isEqualTo(MessageFormat.format(res.getString(key), params));
}
}
}
Selon le principe TDD, le test a été écrit avant l'implémentation.
Reste à injecter ça dans les services de Spring (indirectement testé, vu que j'ai aussi des tests Back-end).
Pour cela, on définit le paramétrage:
Code: package com.calculateur.warhammer.calcul.mort.parametres;
import com.calculateur.warhammer.calcul.mort.factory.validation.IFactoryValidation;
/**
* Paramètres pour fabriquer les factory nécessaires au calcul.
* @author phili
*
*/
public interface IParametres {
IFactoryValidation getFactoryValidation();
}
Et l'implémentation concrète:
Code: package com.calculateur.warhammer.calcul.mort.parametres;
import java.util.List;
import com.calculateur.warhammer.calcul.mort.factory.validation.FactoryValidation;
import com.calculateur.warhammer.calcul.mort.factory.validation.IFactoryValidation;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class Parametres implements IParametres{
private List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;
private List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;
private List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
@Override
public IFactoryValidation getFactoryValidation() {
return new FactoryValidation(validationsPhaseTir, validationsTirContreCharge, validationsCorpsACorps);
}
//Getter Setter
public List<IValidationCalcul<IArmeDeTir>> getValidationsPhaseTir() {
return validationsPhaseTir;
}
public void setValidationsPhaseTir(List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir) {
this.validationsPhaseTir = validationsPhaseTir;
}
public List<IValidationCalcul<IArmeDeTir>> getValidationsTirContreCharge() {
return validationsTirContreCharge;
}
public void setValidationsTirContreCharge(List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge) {
this.validationsTirContreCharge = validationsTirContreCharge;
}
public List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsCorpsACorps() {
return validationsCorpsACorps;
}
public void setValidationsCorpsACorps(List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps) {
this.validationsCorpsACorps = validationsCorpsACorps;
}
}
Pour construire ce truc, on utilise un autre Design Pattern du GOFF: Builder.
Code: package com.calculateur.warhammer.calcul.mort.parametres;
import java.util.ArrayList;
import java.util.List;
import com.calculateur.warhammer.base.builder.IBuilder;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class ParametresBuilder implements IBuilder<IParametres>{
private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;
private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;
private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
private ParametresBuilder() {
validationsPhaseTir = new ArrayList<>();
validationsTirContreCharge = new ArrayList<>();
validationsCorpsACorps = new ArrayList<>();
}
public static ParametresBuilder getInstance() {
return new ParametresBuilder();
}
@Override
public IParametres build() {
Parametres pReturn = new Parametres();
pReturn.setValidationsPhaseTir(validationsPhaseTir);
pReturn.setValidationsTirContreCharge(validationsTirContreCharge);
pReturn.setValidationsCorpsACorps(validationsCorpsACorps);
return pReturn;
}
public ParametresBuilder addValidationPhaseTir(IValidationCalcul<IArmeDeTir> validation) {
validationsPhaseTir.add(validation);
return this;
}
public ParametresBuilder addValidationTirContreCharge(IValidationCalcul<IArmeDeTir> validation) {
validationsTirContreCharge.add(validation);
return this;
}
public ParametresBuilder addValidationCorpsACorps(IValidationCalcul<IArmeDeCorpsACorps> validation) {
validationsCorpsACorps.add(validation);
return this;
}
}
Reste plus qu'à injecter ça avec les beans de Spring.
Pour savoir ce qu'il faut injecter, on fait une classe d'introspection:
Code: package com.calculateur.warhammer.service.configuration.introspection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.reflections.Reflections;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationCorpsACorps;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationPhaseTir;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationTirContreCharge;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class ValidationIntrospectionUtil {
private static final String PACKAGE = "com.calculateur.warhammer.calcul.mort.validation";
private ValidationIntrospectionUtil() {
}
@SuppressWarnings("unchecked")
public static List<IValidationCalcul<IArmeDeTir>> getListValidationPhaseTir() throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
List<IValidationCalcul<IArmeDeTir>> lReturn = new ArrayList<>();
Reflections reflections = new Reflections(PACKAGE);
Set<Class<?>> liste =
reflections.getTypesAnnotatedWith(ValidationPhaseTir.class);
Constructor<?> constructor;
IValidationCalcul<IArmeDeTir> validation;
for(Class<?> aClass:liste) {
constructor = aClass.getConstructors()[0];
validation = (IValidationCalcul<IArmeDeTir>)constructor.newInstance();
lReturn.add(validation);
}
return lReturn;
}
@SuppressWarnings("unchecked")
public static List<IValidationCalcul<IArmeDeTir>> getListValisationTirContreCharge()throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
List<IValidationCalcul<IArmeDeTir>> lReturn = new ArrayList<>();
Reflections reflections = new Reflections(PACKAGE);
Set<Class<?>> liste =
reflections.getTypesAnnotatedWith(ValidationTirContreCharge.class);
Constructor<?> constructor;
IValidationCalcul<IArmeDeTir> validation;
for(Class<?> aClass:liste) {
constructor = aClass.getConstructors()[0];
validation = (IValidationCalcul<IArmeDeTir>)constructor.newInstance();
lReturn.add(validation);
}
return lReturn;
}
@SuppressWarnings("unchecked")
public static List<IValidationCalcul<IArmeDeCorpsACorps>> getListValisationCorpsACorps()throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
List<IValidationCalcul<IArmeDeCorpsACorps>> lReturn = new ArrayList<>();
Reflections reflections = new Reflections(PACKAGE);
Set<Class<?>> liste =
reflections.getTypesAnnotatedWith(ValidationCorpsACorps.class);
Constructor<?> constructor;
IValidationCalcul<IArmeDeCorpsACorps> validation;
for(Class<?> aClass:liste) {
constructor = aClass.getConstructors()[0];
validation = (IValidationCalcul<IArmeDeCorpsACorps>)constructor.newInstance();
lReturn.add(validation);
}
return lReturn;
}
}
On a utilisé les annotations précédentes. Comme ça, on peut toujours ajouter dynamiquement des validations, au cas où j'en ai oublié. Sait-on jamais.
La conf de Spring devient:
Code: package com.calculateur.warhammer.service.configuration.general;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import com.calculateur.warhammer.base.traduction.ILangues;
import com.calculateur.warhammer.base.traduction.LanguesImplementation;
import com.calculateur.warhammer.calcul.mort.parametres.IParametres;
import com.calculateur.warhammer.calcul.mort.parametres.ParametresBuilder;
import com.calculateur.warhammer.calcul.pouvoir.psy.CalculProbaPouvoirPsy;
import com.calculateur.warhammer.calcul.pouvoir.psy.ICalculProbaPouvoirPsy;
import com.calculateur.warhammer.data.regles.factory.factory.FactoryFactoryRegles;
import com.calculateur.warhammer.data.regles.factory.factory.IFactoryFactoryRegle;
import com.calculateur.warhammer.service.configuration.introspection.ValidationIntrospectionUtil;
/**
* Configuration des services par Spring boot
* @author phili
*
*/
@ComponentScan(basePackages = {"com.calculateur.warhammer.service.service","com.calculateur.warhammer.service.mapping"})
public class AbstractConfigurationService {
@Bean(name = "langues")
public ILangues getLangues() {
Locale defaut = Locale.FRANCE;
Map<String, Locale> map = new HashMap<>();
map.put("fr", Locale.FRANCE);
map.put("en", Locale.ENGLISH);
return new LanguesImplementation(defaut, map);
}
//Fabrication de règle
@Bean(name = "factoryFactoryRegle")
public IFactoryFactoryRegle getFactoryFactoryRegle() {
return new FactoryFactoryRegles();
}
//Paramètres calcul
@Bean(name = "parametres")
public IParametres getParametres() throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
final ParametresBuilder builder = ParametresBuilder.getInstance();
ValidationIntrospectionUtil.getListValidationPhaseTir().forEach(builder::addValidationPhaseTir);
ValidationIntrospectionUtil.getListValisationTirContreCharge().forEach(builder::addValidationTirContreCharge);
ValidationIntrospectionUtil.getListValisationCorpsACorps().forEach(builder::addValidationCorpsACorps);
return builder.build();
}
//Calcul proba phase psychique
@Bean("calculProbaPouvoirPsy")
public ICalculProbaPouvoirPsy getCalculProbaPouvoirPsy() {
return new CalculProbaPouvoirPsy();
}
}
Si il y a une erreur lors de l'introspection, le serveur web ne démarre même pas. Comme les tests back-end fonctionnent...
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
Et maintenant, la sauvegarde qui s'est révélé relativement facile à traité (TU compris).
Pour commencé, j'ai défini l'interface:
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Calcule la sauvegarde d'une figururine.
* @author phili
*
*/
public interface ICalculSauvegarde<A extends IArme> {
/**
*
* @param attaquant Attaquant effectuant l'action
* @param defenseur Défenseur effectuant l'action
* @param contexteAction Contexte de l'action
* @param bonusPA Bonus supplémentaire à la PA d'armure (peut-être null).
* @return La sauvegarde d'armure (vide si pas de sauvegarde).
*/
Optional<Integer> getSauvegarde(IConstituantAttaquant<A> attaquant,IConstituantDefenseur defenseur,IContexteAction<A> contexteAction,Integer bonusPA);
}
Notez que l'on a un optional, étant donné qu'il se peut qu'il n'y ait pas de sauvegarde. On ajoute aussi le bonus de PA (d'armure) vu que certaines armées, comme les SDB augmentent la PA de 1 sur 6+ au tir. Le bonus dépend donc du jet d'avant.
Comme une sauvegarde est comprise entre 2 et 6, on a donc un traitement pour ramener dans la bonne fourchette:
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Template Methode permettant d'ajuster la sauvegarde entre 2+ et 6+.
* @author phili
*
* @param <A>
*/
public abstract class AbstractCalculSauvegarde<A extends IArme> implements ICalculSauvegarde<A>{
private static final Integer SAUVEGARDE_ARMURE_MAXIMUM = 6;
private static final Integer SAUVEGARDE_ARMURE_MINIMUM = 2;
protected AbstractCalculSauvegarde() {
}
@Override
public Optional<Integer> getSauvegarde(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
Optional<Integer> sauvegardeBase = getSauvegardeBase(attaquant, defenseur, contexteAction, bonusPA);
return (sauvegardeBase.isPresent())?ajusteSauvegarde(sauvegardeBase.get()):sauvegardeBase;
}
protected abstract Optional<Integer> getSauvegardeBase(IConstituantAttaquant<A> attaquant,IConstituantDefenseur defenseur,IContexteAction<A> contexteAction,Integer bonusPA);
private Optional<Integer> ajusteSauvegarde(Integer sauvegardeBase){
Integer sauvegarde;
if(sauvegardeBase < SAUVEGARDE_ARMURE_MINIMUM) {
sauvegarde = SAUVEGARDE_ARMURE_MINIMUM;
}else if(sauvegardeBase > SAUVEGARDE_ARMURE_MAXIMUM) {
sauvegarde = null;
}else {
sauvegarde = sauvegardeBase;
}
return Optional.ofNullable(sauvegarde);
}
}
Reste à implémenter pour les cas connus (l'invulnérable a 2 cas suivant que ce soit sur le profil ou les règles comme c'est le cas des eldars noirs).
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Calcul de la sauvegarde d'armure
* @author phili
*
* @param <A>
*/
public class CalculSauvegardeArmure<A extends IArme> extends AbstractCalculSauvegarde<A>{
private static final Integer MAXIMUM_PA = 0;
private static final Integer BONUS_BASE_PA = 0;
@Override
protected Optional<Integer> getSauvegardeBase(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
Integer sauvegardeBase = defenseur.getProfil().getSauvegarde();
return (sauvegardeBase != null)?Optional.of(getSauvegarde(sauvegardeBase, attaquant, defenseur, bonusPA)) :Optional.empty();
}
private Integer getSauvegarde(Integer sauvegardeBase,IConstituantAttaquant<A> attaquant,IConstituantDefenseur defenseur,Integer bonusPA) {
Integer pa = getPA(attaquant, defenseur, bonusPA);
boolean isCouvert = defenseur.getRegles().isSauvegardeCouvert() && !attaquant.getRegles().isIgnoreSauvegardeCouvert();
Integer bonusCouvert = isCouvert?-1:0;
Integer bonusDefenseurSauvegarde = defenseur.getRegles().getBonusSauvegardeArmure();
Integer bonusDefenseurSauvegardeCalcul = (bonusDefenseurSauvegarde != null)?bonusDefenseurSauvegarde:0;
return (sauvegardeBase + bonusCouvert + bonusDefenseurSauvegardeCalcul) - pa;
}
private Integer getPA(IConstituantAttaquant<A> attaquant,IConstituantDefenseur defenseur,Integer bonusPA) {
Integer paArme = attaquant.getArme().getPA();
Integer paArmeCalcul = (paArme != null)?paArme:MAXIMUM_PA;
Integer bonusPARegle = attaquant.getRegles().getBonusPA();
Integer bonusPaRegleCalcul = (bonusPARegle != null)?bonusPARegle:BONUS_BASE_PA;
Integer reductionPA = defenseur.getRegles().getReductionPA();
Integer reductionPACalcul = (reductionPA != null)?reductionPA:BONUS_BASE_PA;
Integer bonusPACalcul = (bonusPA != null)?bonusPA:BONUS_BASE_PA;
Integer pa = paArmeCalcul + bonusPaRegleCalcul + reductionPACalcul + bonusPACalcul;
return (pa < MAXIMUM_PA)?pa:MAXIMUM_PA;
}
}
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Calcul de la sauvegarde démoniaque
* @author phili
*
*/
public class CalculSauvegardeDemoniaque<A extends IArme> extends AbstractCalculSauvegarde<A>{
@Override
protected Optional<Integer> getSauvegardeBase(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
Optional<Integer> oSauvegarde;
if(contexteAction.getSimule().isActionTir()) {
oSauvegarde = Optional.ofNullable(defenseur.getProfil().getSauvegardeDemoniaqueTir());
}else {
oSauvegarde = Optional.ofNullable(defenseur.getProfil().getSauvegardeDemoniaqueCorpsACorps());
}
return oSauvegarde;
}
}
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Calcul de la sauvegarde invulnérable par rapport au profil.
* @author phili
*
*/
public class CalculSauvegardeInvulnerableProfil<A extends IArme> extends AbstractCalculSauvegarde<A>{
@Override
protected Optional<Integer> getSauvegardeBase(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
return attaquant.getRegles().isIgnoreSauvegardeInvulnerable()?Optional.empty():Optional.ofNullable(defenseur.getProfil().getSauvegardeInvulnerable());
}
}
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.Optional;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.regles.IRegleDefense;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Calcul de la sauvegarde invulnérable due aux règles
* @author phili
*
* @param <A>
*/
public class CalculSauvegardeInvulnerableRegle<A extends IArme> extends AbstractCalculSauvegarde<A>{
@Override
protected Optional<Integer> getSauvegardeBase(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
return attaquant.getRegles().isIgnoreSauvegardeInvulnerable()?Optional.empty():getSauvegardeFromRegles(defenseur.getRegles());
}
private Optional<Integer> getSauvegardeFromRegles(IRegleDefense regle){
return Optional.ofNullable(regle.getSauvegardeInvulnerable()).filter(s -> s > 0);
}
}
Evidement, hors de question de boucler sur toutes les sauvegardes qui existent.
On va donc utiliser un Design Pattern du GOFF pour ne voir qu'un seul calcul, le Design Pattern Décorateur:
Code: package com.calculateur.warhammer.calcul.mort.sauvegarde;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
/**
* Décorateur pour calculer toutes les sauvegardes et prendre la meilleure.
* @author phili
*
* @param <A>
*/
public class CalculSauvegardeDecorateur<A extends IArme> implements ICalculSauvegarde<A>{
private final List<ICalculSauvegarde<A>> sauvegardes;
public CalculSauvegardeDecorateur(List<ICalculSauvegarde<A>> sauvegardes) {
this.sauvegardes = sauvegardes;
}
@Override
public Optional<Integer> getSauvegarde(IConstituantAttaquant<A> attaquant, IConstituantDefenseur defenseur,
IContexteAction<A> contexteAction, Integer bonusPA) {
OptionalInt sRes = sauvegardes.stream().map(cs -> cs.getSauvegarde(attaquant, defenseur, contexteAction, bonusPA))
.filter(Optional::isPresent).filter(os -> os.get() > 0).mapToInt(Optional::get).min();
return sRes.isPresent()?Optional.of(sRes.getAsInt()):Optional.empty();
}
}
Reste à fabriquer tout ça avec une factory (autre Design pattern du GOFF):
Code: package com.calculateur.warhammer.calcul.mort.factory.sauvegarde;
import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.sauvegarde.ICalculSauvegarde;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
/**
* Factory pour instancier les calcul de sauvegarde
* @author phili
*
*/
public interface IFactorySauvegarde {
/**
*
* @param simule Ce qui est simulé
* @return Le calcul de la sauvegarde
* @throws SimulationException Si ce qui est simulé est une action de mêlée
*/
ICalculSauvegarde<IArmeDeTir> calculSauvegardeTir(ESimule simule)throws SimulationException;
/**
*
* @param simule Ce qui est simulé
* @return Le calcul de la sauvegarde
* @throws SimulationException Si ce qui est simulé est une action de tir
*/
ICalculSauvegarde<IArmeDeCorpsACorps> calculSauvegardeCorpsACorp(ESimule simule)throws SimulationException;
}
Et l'implémentation:
Code: package com.calculateur.warhammer.calcul.mort.factory.sauvegarde;
import java.util.Arrays;
import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.factory.utils.FactoryUtils;
import com.calculateur.warhammer.calcul.mort.sauvegarde.CalculSauvegardeDemoniaque;
import com.calculateur.warhammer.calcul.mort.sauvegarde.CalculSauvegardeArmure;
import com.calculateur.warhammer.calcul.mort.sauvegarde.CalculSauvegardeDecorateur;
import com.calculateur.warhammer.calcul.mort.sauvegarde.CalculSauvegardeInvulnerableProfil;
import com.calculateur.warhammer.calcul.mort.sauvegarde.CalculSauvegardeInvulnerableRegle;
import com.calculateur.warhammer.calcul.mort.sauvegarde.ICalculSauvegarde;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
public class FactorySauvegarde implements IFactorySauvegarde{
private final ICalculSauvegarde<IArmeDeTir> calculTir;
private final ICalculSauvegarde<IArmeDeCorpsACorps> calculCorpsACorps;
public FactorySauvegarde() {
calculTir = new CalculSauvegardeDecorateur<>(Arrays.asList(
new CalculSauvegardeArmure<>(),
new CalculSauvegardeDemoniaque<>(),
new CalculSauvegardeInvulnerableProfil<>(),
new CalculSauvegardeInvulnerableRegle<>()));
calculCorpsACorps = new CalculSauvegardeDecorateur<>(Arrays.asList(
new CalculSauvegardeArmure<>(),
new CalculSauvegardeDemoniaque<>(),
new CalculSauvegardeInvulnerableProfil<>(),
new CalculSauvegardeInvulnerableRegle<>()));
}
@Override
public ICalculSauvegarde<IArmeDeTir> calculSauvegardeTir(ESimule simule) throws SimulationException {
FactoryUtils.valideActionTir(simule);
return calculTir;
}
@Override
public ICalculSauvegarde<IArmeDeCorpsACorps> calculSauvegardeCorpsACorp(ESimule simule) throws SimulationException {
FactoryUtils.valideActionCorpsACorps(simule);
return calculCorpsACorps;
}
}
On n'a plus qu'à modifier le paramétrage pour Spring.
Code: package com.calculateur.warhammer.calcul.mort.parametres;
import com.calculateur.warhammer.calcul.mort.factory.sauvegarde.IFactorySauvegarde;
import com.calculateur.warhammer.calcul.mort.factory.validation.IFactoryValidation;
/**
* Paramètres pour fabriquer les factory nécessaires au calcul.
* @author phili
*
*/
public interface IParametres {
IFactoryValidation getFactoryValidation();
IFactorySauvegarde getFactorySauvegarde();
}
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
Voila, mon interface est fini, en web. Ici, on voit les SDB sanctionner l'Hérétique Mamar.
2023_02_01_site_phil1.png (Size: 170.89 KB / Downloads: 11)
2023_02_01_site_phil2.png (Size: 168.1 KB / Downloads: 4)
2023_02_01_site_phil3.png (Size: 185.05 KB / Downloads: 2)
2023_02_01_site_phil4.png (Size: 184.21 KB / Downloads: 6)
Et c'est internationalisé.
2023_02_01_site_phil5.png (Size: 177.17 KB / Downloads: 8)
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
J'ai fini les eldars, les vaisseaux monde, les drukhari, les arlequin et j'ai même ajouté les Ynnari.
Mention spéciale aux Drukhari qui m'on demandé plus de travail avec
- La Kabale de la Rose d'obsidienne, qui m'a obligé à introduire la notion de transformation d'arme (existe aussi dans la garde impériale).
- La cotterie des bourrins des prophètes charnels qui m'ont obligé à introduire le Design Pattern Adapteur. Effectivement, c'est sur la force de l'attaque (modifiable, surtout au corps à corps) que leur règle s'applique.
- Le noir credo qui m'a obligé à faire un refactoring (et une évolution de la BDD) pour prendre en compte que les auras dépendent des sous factions (existe dans le chaos avec les Night Lord que joue Mamar).
Je vais donc faire la todo liste suivante:
- Dockeriser enfin l'application en écrivant les Docker File et Docker Compose File. Je vais ajouter ELK pour les logs. J'y ai été forcé car ma prochaine mission aura pour sujet ELK et je me suis dit que le faire sur un vrai sujet pour apprendre la base serait mieux.
- Calcul des jets de blessure ce qui me permettra de faire une implémentation de l'adapteur évoqué au point 2.
- Implémenter la Garde impériale.
- Implémenter le chaos en général (avec correction pour sortir de certaines règles les cultistes/Zombie de la peste ...
- Implémenter les nécrons.
- Implémenter les Orks.
Comme ça, si on a un Hard Reboot (=> changement des profils), l'impact sera moins important.
Posts: 8,125
Threads: 166
Joined: Oct 2002
Reputation:
3
oh là là mon Philou il claque un dev de ouf pour calculer tout ça!!!
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
Code: package com.calculateur.warhammer.data.regles.regle.drukhari.coterie.prophetes.chanels;
import com.calculateur.warhammer.data.adapter.ICalculForceAttaqueAdapter;
import com.calculateur.warhammer.data.regles.IRegleDefense;
/**
* Règle Gourmets de souffrance pour les prophètes charnels, à savoir jet minimum de 4+ pour blesser si la force de l'arme est inférieur à 8.
* @author phili
*
*/
public class RegleGourmetsDeSouffrance implements IRegleDefense{
private final ICalculForceAttaqueAdapter adapter;
public RegleGourmetsDeSouffrance(ICalculForceAttaqueAdapter adapter) {
this.adapter = adapter;
}
@Override
public Integer getJetMinimumPourEtreBlesse() {
Integer jet = IRegleDefense.super.getJetMinimumPourEtreBlesse();
if(adapter != null) {
Integer force = adapter.getForceAttaque();
if(force != null && force < 8) {
jet = 4;
}
}
return jet;
}
}
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
05-03-2023, 05:45 PM
(This post was last modified: 05-03-2023, 05:46 PM by Philou.)
Le Devops c'est du pipeau  .
Les Méchants Ops, qui en plus votent Mélenchon, ils n'arrêtent pas d'emmerder les gentils dev
Tout ça parce que le patron des Ops (comme un certain Sébastien M.) ne les engueule pas assez  .
Heureusement, malgré les Ops, je réussi très difficilement à construire mes images/containers Docker.
docker_2023_03_05_n1.png (Size: 126.58 KB / Downloads: 6)
docker_2023_03_05_n2.png (Size: 131.5 KB / Downloads: 4)
Bon, reste le NGNX et un ELK, histoire d'avoir des logs.
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
Malgré les Ops, je commence à prendre le plis avec Docker.
Du coup, en moins de 30min, j'ai aussi ajouté le client.
docker_2023_03_06_1.png (Size: 133.44 KB / Downloads: 5)
docker_2023_03_06_2.png (Size: 179.02 KB / Downloads: 3)
Posts: 3,449
Threads: 126
Joined: Jun 2003
Reputation:
1
08-03-2023, 08:28 PM
(This post was last modified: 08-03-2023, 08:28 PM by Philou.)
Finalement, j'avoue, coller le ELK pour logger ce merdier, c'est un peu trop compliquer.
Je vais donc me contenter de BDD/serveur Java/Client Angular.
- Calcul des jets de blessure
- Implémenter la Garde impériale.
- Implémenter le chaos en général (avec correction pour sortir de certaines règles les cultistes/Zombie de la peste ... )
- Implémenter les nécrons.
- Implémenter les Orks.
|