Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Projet Calculateur
#20
40K, ce jeu où il y a trop de règles Mur

Revenant enfin sur le métier, dans le calcul (projet calcul, il était temps), j'ai essayé de décomposer les étapes (d'abord déterminer le jet pour toucher, pour blesser ...).

Mais je suis arrivé à un problème: il faut vérifier que celui qui utilise l'API ne donne pas en entrée de la merde.

Par exemple, que l'on ne simule pas un phase de tir en mêlée alors que l'on est avec une arme à tir rapide (encore un problème, le Dark Angel peut en mêlée avec une arme à tir rapide ou une arme d'assaut en doctrine tactique) Mur

Et mine de rien, il y a plein de choses à valider Injures .

Du coup, j'ai re-décomposé et je me suis occupé que de la validation.

Pour commencé, comme Java est un bon langage, j'ai commencé par définir de annotations pour savoir quoi valider lors des différents calculs:
Code:
package com.calculateur.warhammer.calcul.mort.validation.annotations;

/**
* Validation pour un corps à corps
* @author phili
*
*/
public @interface ValidationCorpsACorps {

}


Code:
package com.calculateur.warhammer.calcul.mort.validation.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Si validation à la phase de tir
* @author phili
*
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidationPhaseTir {

}

Code:
package com.calculateur.warhammer.calcul.mort.validation.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Valmidation pour un tir de contre charge
* @author phili
*
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidationTirContreCharge {

}
Ce qui permettra de fabriquer une factory (Design Pattern du GOFF).

Du coup, il n'y a plus qu'à valider:
Code:
package com.calculateur.warhammer.calcul.mort.validation;

import com.calculateur.warhammer.calcul.mort.validation.exception.ValidationCalculException;
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;

/**
* Validateur pour savoir si l'action calculé ou simulée est possible
* @author phili
*
* @param <A> Arme utilisée
*/
public interface IValidationCalcul <A extends IArme>{

    /**
     * Valide l'action simulée ou calculée
     * @param attaquant Attaquant effectuant l'attaque
     * @param defenseur Défenseur effectuant l'attaque
     * @param contexteAction Contexte de l'action
     * @throws ValidationCalculException Si l'action n'est pas possible
     */
    void valideCalcul(IConstituantAttaquant<A> attaquant,IConstituantDefenseur defenseur,IContexteAction<A> contexteAction)throws ValidationCalculException;
}

Avec un utilisateur qui verra un truc traduit:
Code:
package com.calculateur.warhammer.calcul.mort.validation.exception;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

import com.calculateur.warhammer.base.traduction.ITraduction;

/**
* Exception lancée lorsque le calcul n'est pas possible
* @author phili
*
*/
@SuppressWarnings("all")
public class ValidationCalculException extends Exception implements ITraduction{

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private final String resources;
    
    private final Object[] objets;
    
    private final String key;
    
    private String message;
    
    public ValidationCalculException(String resources, Object[] objets, String key) {
        this.resources = resources;
        this.objets = objets;
        this.key = key;
    }

    @Override
    public ResourceBundle getResourceBundle(Locale locale) {
        return ResourceBundle.getBundle(resources, locale);
    }

    @Override
    public void traduireLibelles(Locale locale) {
        String messageToFormat = getResourceBundle(locale).getString(key);
        message = (objets != null)? MessageFormat.format(messageToFormat, objets):messageToFormat;
    }

    @Override
    public String getMessage() {
        return message != null?message:key;
    }
    
    public Object[] getObjets() {
        return objets;
    }
}
Et un test de base (toujours faire un TU):
Code:
package com.calculateur.warhammer.calcul.mort.validation;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.calculateur.warhammer.calcul.mort.validation.exception.ValidationCalculException;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.identifiable.IProfil;
import com.calculateur.warhammer.data.regles.IRegleAttaque;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;
import com.calculateur.warhammer.data.unite.implementation.BasicConstituantUniteAttaquant;

@Execution(ExecutionMode.SAME_THREAD)
@ExtendWith(MockitoExtension.class)
public abstract class AbstractValidationTest <A extends IArme>{

    private Locale[] LOCALES =  {Locale.FRANCE,Locale.ENGLISH};
    
    private IConstituantAttaquant<A> attaquant;
    
    @Mock
    protected IProfil profilAttaquant;
    
    @Mock
    protected IRegleAttaque regleAttaquant;
    
    @Mock
    protected IContexteAction<A> contextAction;
    
    private final IValidationCalcul<A> validator;

    protected AbstractValidationTest(IValidationCalcul<A> validator) {
        this.validator = validator;
    }
    
    @BeforeEach
    public void beforeEach() {
        attaquant = new BasicConstituantUniteAttaquant<A>(profilAttaquant, null, null, getArme(), regleAttaquant);
    }
    
    protected abstract A getArme();
    
    protected void testValidationStandard(boolean isExceptionAttendue) {
        testValidationStandard(isExceptionAttendue, attaquant, contextAction);
    }
    
    protected void testValidationStandard(boolean isExceptionAttendue,IConstituantAttaquant<A> aAttaquant,IContexteAction<A> aContext) {
        testValidationStandard(isExceptionAttendue, aAttaquant, null,contextAction);
    }
    
    protected void testValidationStandard(boolean isExceptionAttendue,IConstituantAttaquant<A> aAttaquant,IConstituantDefenseur defenseur,IContexteAction<A> aContext) {
        try {
            validator.valideCalcul(aAttaquant,defenseur, aContext);
            Assertions.assertThat(isExceptionAttendue).isFalse();
        }catch(ValidationCalculException ex) {
            Assertions.assertThat(isExceptionAttendue).isTrue();
            valideException(ex);
        }
    }
    
    private void valideException(ValidationCalculException 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));
        }
    }
}

Soit pour savoir si on tir en mêlée:
Code:
package com.calculateur.warhammer.calcul.mort.validation;

import java.util.Set;

import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationPhaseTir;
import com.calculateur.warhammer.calcul.mort.validation.exception.ValidationCalculException;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.enumeration.EProfil;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;

/**
* Validation si on peut tirer en mêlée
* @author phili
*
*/
@ValidationPhaseTir
public class ValidationPhaseTirMelee implements IValidationCalcul<IArmeDeTir>{

    @Override
    public void valideCalcul(IConstituantAttaquant<IArmeDeTir> attaquant,IConstituantDefenseur defenseur, IContexteAction<IArmeDeTir> contexteAction)
            throws ValidationCalculException {
        if(contexteAction.isZoneEngagement() &&
                !isVehiculeOuMonstre(attaquant.getProfil().getTypesProfils()) &&
                    !attaquant.getRegles().isPeutUtiliserArmePorteeEngagement(attaquant.getArme().getType())) {
            Object[] params = {attaquant.getArme().getType()};
            throw new ValidationCalculException(ValidationCalculUtil.RESOURCE_ERREUR_PHASE_TIR,params, "erreur.tir.melee");
        }
        
    }

    
    private boolean isVehiculeOuMonstre(Set<EProfil> typesProfils) {
        return typesProfils.contains(EProfil.VEHICULE) || typesProfils.contains(EProfil.MONSTRE);
    }
}

Ou si on peut charger après une retraite:
Code:
package com.calculateur.warhammer.calcul.mort.validation;

import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationCorpsACorps;
import com.calculateur.warhammer.calcul.mort.validation.exception.ValidationCalculException;
import com.calculateur.warhammer.data.action.IContexteAction;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArme;
import com.calculateur.warhammer.data.unite.IConstituantAttaquant;
import com.calculateur.warhammer.data.unite.IConstituantDefenseur;

/**
* Valida si la charge est possible
* @author phili
*
*/
@ValidationCorpsACorps
public class ValidationChargePossible implements IValidationCalcul<IArme>{

    @Override
    public void valideCalcul(IConstituantAttaquant<IArme> attaquant,IConstituantDefenseur defenseur, IContexteAction<IArme> contexteAction)
            throws ValidationCalculException {
        if(contexteAction.getSimule() == ESimule.CORPS_A_CORPS_APRES_CHARGE && !attaquant.getRegles().isChargeApresTypeMouvement(contexteAction.getMouvementAttaquant())) {
            Object[] param = {contexteAction.getMouvementAttaquant()};
            throw new ValidationCalculException(ValidationCalculUtil.RESOURCE_ERREUR_CORPS_A_CORPS, param, "erreur.charge.impossible");
        }
    }

}
Evidement, tout ça est testé (avec d'autres validateurs) et les TU ont été écrit avant selon le principe du TDD.
Reply


Messages In This Thread
Projet Calculateur - by Philou - 12-09-2022, 09:47 AM
RE: Projet Calculateur - by Philou - 12-09-2022, 09:52 AM
RE: Projet Calculateur - by Philou - 17-09-2022, 09:14 PM
RE: Projet Calculateur - by Philou - 25-09-2022, 07:26 AM
RE: Projet Calculateur - by Philou - 25-09-2022, 08:54 AM
RE: Projet Calculateur - by Philou - 03-10-2022, 10:54 AM
RE: Projet Calculateur - by Philou - 16-10-2022, 11:36 AM
RE: Projet Calculateur - by Philou - 24-10-2022, 01:08 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 09:52 AM
RE: Projet Calculateur - by sdm - 25-10-2022, 11:06 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 11:23 PM
RE: Projet Calculateur - by Philou - 26-10-2022, 03:09 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 11:25 PM
RE: Projet Calculateur - by Philou - 06-11-2022, 05:27 PM
RE: Projet Calculateur - by Philou - 19-11-2022, 10:36 AM
RE: Projet Calculateur - by Philou - 23-11-2022, 08:34 AM
RE: Projet Calculateur - by Philou - 24-11-2022, 08:49 PM
RE: Projet Calculateur - by Philou - 04-12-2022, 06:57 PM
RE: Projet Calculateur - by Philou - 11-12-2022, 12:11 PM
RE: Projet Calculateur - by Philou - 18-12-2022, 06:47 PM
RE: Projet Calculateur - by Philou - 19-12-2022, 09:05 PM
RE: Projet Calculateur - by Philou - 03-01-2023, 09:34 PM
RE: Projet Calculateur - by Philou - 07-01-2023, 06:36 PM
RE: Projet Calculateur - by Philou - 01-02-2023, 08:21 PM
RE: Projet Calculateur - by Philou - 16-02-2023, 08:31 PM
RE: Projet Calculateur - by Gaeriel - 17-02-2023, 10:56 PM
RE: Projet Calculateur - by Philou - 17-02-2023, 11:32 PM
RE: Projet Calculateur - by Philou - 05-03-2023, 05:45 PM
RE: Projet Calculateur - by Philou - 06-03-2023, 08:55 PM
RE: Projet Calculateur - by Philou - 08-03-2023, 08:28 PM
RE: Projet Calculateur - by Philou - 12-03-2023, 07:56 AM
RE: Projet Calculateur - by Philou - 23-03-2023, 06:44 PM
RE: Projet Calculateur - by Philou - 28-03-2023, 08:58 PM
RE: Projet Calculateur - by Philou - 27-04-2023, 08:05 PM
RE: Projet Calculateur - by Philou - 30-04-2023, 10:29 AM
RE: Projet Calculateur - by Philou - 02-05-2023, 08:03 AM
RE: Projet Calculateur - by Philou - 19-05-2023, 11:36 AM
RE: Projet Calculateur - by Philou - 19-05-2023, 05:44 PM
RE: Projet Calculateur - by Philou - 20-05-2023, 09:10 PM
RE: Projet Calculateur - by Philou - 22-06-2023, 07:38 AM
RE: Projet Calculateur - by Philou - 24-06-2023, 07:09 AM
RE: Projet Calculateur - by Philou - 26-06-2023, 07:44 PM
RE: Projet Calculateur - by Philou - 28-06-2023, 07:09 AM
RE: Projet Calculateur - by Philou - 28-06-2023, 07:43 PM
RE: Projet Calculateur - by Philou - 30-06-2023, 07:22 PM
RE: Projet Calculateur - by Philou - 08-08-2023, 07:54 PM
RE: Projet Calculateur - by Philou - 19-08-2023, 10:30 AM
RE: Projet Calculateur - by Philou - 19-08-2023, 06:20 PM
RE: Projet Calculateur - by Philou - 20-08-2023, 10:17 AM
RE: Projet Calculateur - by Philou - 22-01-2024, 09:02 PM
RE: Projet Calculateur - by Philou - 09-05-2024, 10:21 AM
RE: Projet Calculateur - by Philou - 11-05-2024, 05:41 PM
RE: Projet Calculateur - by Philou - 25-05-2024, 08:41 AM
RE: Projet Calculateur - by Philou - 24-04-2025, 04:09 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)