Utilisateur:Pumpkins : Différence entre versions

De Leek Wars Wiki
Aller à : navigation, rechercher
(Opérateurs)
m (Globale)
 
Ligne 351 : Ligne 351 :
 
* elles ne peuvent être déclarée qu'au périmètre le plus externe (top level), c'est-à-dire hors de tout bloc de code (enclos par des accolades <code>{ ... }</code>).
 
* elles ne peuvent être déclarée qu'au périmètre le plus externe (top level), c'est-à-dire hors de tout bloc de code (enclos par des accolades <code>{ ... }</code>).
 
* la valeur qu'elles contiennent est conservé entre chaque tour de jeu, elles ne sont pas réinitialisées.
 
* la valeur qu'elles contiennent est conservé entre chaque tour de jeu, elles ne sont pas réinitialisées.
Notes
 
  
 
= Ressource Intéressantes =
 
= Ressource Intéressantes =

Version actuelle en date du 13 avril 2019 à 10:58

Boop.

Ébauche : Structurer son Code

Prérequis:

  • Obligatoire:
Savoir définir une variable.
  • Recommandé:
Savoir faire une comparaison.
Savoir utiliser des opérateurs.
Savoir appeler des fonctions.
  • Utile:

Alors voilà, vous êtes nouveau en programmation, et vous débutez aussi sur Leekwars. Bien entendu, vous avez lu le Guide du débutant, et vous en êtes donc au code présenté ci-dessous.

//--------------------------------
//---------- Base code -----------
//--------------------------------

// We take the pistol
setWeapon(WEAPON_PISTOL); // Warning: costs 1 TP

// We get the nearest enemy
var enemy = getNearestEnemy();

// We move towards him
moveToward(enemy);

// We try to shoot him!
useWeapon(enemy);
// Again !
useWeapon(enemy);
// Again !
useWeapon(enemy);

Cependant, cela ne veux pas dire que vous comprenez ce qu'il se passe et vous avez donc lu les didacticiels de base suivant:

Peut-être même avez vous lu les autres. Dans ce cas, vous n'avez probablement pas besoin de lire la suite.
Mais.
Sachant que le cerveau apprends suite à exposition répété, vous allez tout de même lire la suite. N'est-ce pas ?

Bloc de Code

Bien que présenté ici de manière un peu abstraite, le bloc de code se caractérise par son omniprésence chez les langages de programmation structurée. Il s'agit tout simplement d'une suite d'instructions rassemblées par une syntaxe particulière. Cette syntaxe est généralement d'une des formes suivantes:

// Indentation
var someVar = someInit;
var someOtherVar = someOtherInit;
    var stillSomeOtherVar = someFunction(someVar);
    someOtherVar += stillSomeOtherVar + someConstant;
debug(someOtherVar);

// Accolades enclosantes
var someVar = someInit;
var someOtherVar = someOtherInit;
{
    var stillSomeOtherVar = someFunction(someVar);
    someOtherVar += stillSomeOtherVar + someConstant;
}
debug(someOtherVar);

// Mots clés
var someVar = someInit;
var someOtherVar = someOtherInit;
begin
    var stillSomeOtherVar = someFunction(someVar);
    someOtherVar += stillSomeOtherVar + someConstant;
end
debug(someOtherVar);

Il est à noter que l'indentation n'est pas nécessaire dans le cas d'un langage utilisant les accolades ou les mots clés.
Un bloc de code est donc une portion de code suivant quelques règles:

  • Ce qui existe en dehors du bloc peut être utilisé à l'intérieur.
  • Ce qui existe à l'intérieur du bloc ne peut être utilisé à l'extérieur.

C'est deux règles forme la notion de périmètre. (Scope en anglais.)

Un bloc de code seul, ça ne sert pas à grand chose, sinon à faire quelques manipulation qui n'ont pas vraiment d'intérêt dans le cadre du Leekscript. (Et il n'est d'ailleurs pas possible d'en faire avec ce langage. À moins de traffiquer avec une condition toujours vrai.)
Ces blocs vont nous permettre de réaliser quelque chose de très important pour nos programmes: contrôler le flot (l'ordre) d'exécution de nos instructions. C'est dû à cela que vous entendrez parler de structures de contrôle. Si nous analysons le code de base, nous pouvons remarquer ceci: toutes les instructions sont exécutées, et elles le sont toujours dans le même ordre.
Cela veut dire que concrètement, à chaque tours de jeu, vous allez d'abord vous équiper du pistolet (1 point d'action), ensuite vous stockez l'identifiant de l’ennemi le plus proche de vous dans une variable, vous vous en rapprochez, puis vous essayez de lui tirer dessus, encore et encore. Au tour suivant, exactement la même chose.

Exécution conditionnel

Première chose qui puisse nous déranger: rééquiper une arme pour rien. Cela vous coûte un point d'action inutilement. La première chose à faire, isoler la partie de code qui nous pose problème.

{
    setWeapon(WEAPON_PISTOL);
}

var enemy = getNearestEnemy();

moveToward(enemy);

useWeapon(enemy);
useWeapon(enemy);
useWeapon(enemy);

Nous avons maintenant un bloc qui isole les instructions que nous ne voulons exécuter qu'au premier tour. Il nous faut maintenant de quoi manipuler ce bloc pour qu'il s'exécute uniquement si une certaine condition est validée. Nous allons donc utiliser if (condition) block.

if (condition)
{
    setWeapon(WEAPON_PISTOL);
}

var enemy = getNearestEnemy();

moveToward(enemy);

useWeapon(enemy);
useWeapon(enemy);
useWeapon(enemy);

Je vous laisse écrire la condition vous même. Je vous invite à finir de lire la section sur les conditions, et si vous avez toujours un peu de mal, allez sur le chat, et demandez de l'aide.

Exécution répétée

La deuxième chose qui puisse nous déranger: la répétition de useWeapon(enemy). Nous allons ici introduire une structure de contrôle fictive: loop block. Elle sert à répéter indéfiniment les instructions d'un bloc. Après isolation et élimination du code qui nous intéresse.

if (condition)
{
    setWeapon(WEAPON_PISTOL);
}

var enemy = getNearestEnemy();

moveToward(enemy);

loop
{
    useWeapon(enemy);
}

Votre poireau essayera sans cesse d'utiliser son arme, jusqu'à ce qu'il déborde de la limite d'opérations. Ca n'est pas très grave puisque notre poireau n'a rien d'autre à faire ensuite. Mais ça n'est pas très propre. Nous allons donc forcer le flot d'exécution à sortir de la boucle avec l'instruction break, et un bloc conditionnel.

if (condition)
{
    setWeapon(WEAPON_PISTOL);
}

var enemy = getNearestEnemy();

moveToward(enemy);

loop
{
    if (condition)
    {
        useWeapon(enemy);
    }
    else
    {
        break;
    }
}

Maintenant notre programme se comporte mieux, mais cela reste relativement malpropre, de plus loop n'existe pas. En leekscript, la combinaison loop if (condition) corresponds directement à while (condition) block. Pour plus de détails, je vous invite à aller approfondir tout ça dans la section Les Boucles.

Réutilisation de bloc

Les boucles sont intéressante lorsque nous souhaitons éviter de réécrire plusieurs fois une même section de code à la suite. Mais lorsque nous souhaitons éliminer de la répétition de code à des endroits sans relation, elles ne nous sont d'aucune aide.
Pour cela, nous avons une autre structure de contrôle: function (parameters,...) block. La plus compliqué, mais aussi la plus puissante. Elle vous permet d'utiliser un bloc de manière arbitraire. Vous pouvez lui donner un nom, pour ensuite l'utiliser à divers endroits. Vous pouvez l'envoyer à un autre bloc fonction pour qu'il y soit utilisé à l'intérieur par le biais des arguments. Vous pouvez le renvoyer d'un bloc fonction via l'instruction return, afin qu'il soit utilisé plus tard. Plein de choses très intéressante, que je vous laisserais approfondir dans la section: Les Fonctions. Nous allons nous contenter de la version la plus simple qui soit, une fonction sans paramètre ni retour.

La dernière chose qui pourrait nous déranger dans le cadre du code de base, face à plusieurs adversaires: si nous achevons l'ennemie avant d'avoir pu écouler nos points d'action et qu'il nous est possible d'en cibler un autre, nous continuerons à essayer d'attaquer l'adversaire déjà éliminé. Afin de faire une démonstration limitée, nous allons définir une fonction qui récupère l'ennemie le plus proche et l'attaque une seul fois, lui assigner un nom et l'utiliser dans notre boucle.

var attackNearestEnemy = function()
{
    useWeapon(getNearestEnemy());
};

if (condition)
{
    setWeapon(WEAPON_PISTOL);
}

moveToward(getNearestEnemy());

while (condition)
{
    attackNearestEnemy();
}

Et voilà, nous avons ainsi traité dans cette section les principales méthodes pour structurer votre code. Permettant de le rendre plus clair, plus concis, et plus extensible.

Voir Aussi

Ébauche : Leekscript par exemples

Hello World

debug("Hello world!");

Valeurs

null

null;

Booléens

true;
false;

Nombres

Entiers

0;
1;
-1;
1345;
-42;

Flottant

0.0;
3.14;
-1.0;
3245.4508709;
0.006734009318370786;

Chaîne de caractères

"Hello world!";
'Pourquoi le poireau à-t-il traversé la route ?';

Tableau

[];

Fonctions

function() {};
function(param) { "body"; }
function(param1, @param2) { return param2; }

Notes:

  • les params sont des variables, nous verrons plus bas comment déclarer une variable qui n'est pas attachée aux paramètres d'une fonction.
  • l'@ sert à recevoir un argument par référence au lieu d'une copie.

Commentaire

0; // Commentaire simple, sur une ligne.
1 /* commentaire en bloc */ ; // sur une seul ligne
2; /* commentaire en bloc
sur plusieurs lignes */

Expression

Tableaux

Basique

[1, 2, 3, 4];
["Hello", "World", "!"];
[['Leek', 4], 3.0, "banana"];

Associatif

["first word": "Hello", "second word": "World"];
[1: "banana", 4: "apple"];
["sous tableau basique": ["truc", 4.5], 'sous tableau associatif': [1: 'banana', "two": 2], "pas de sous tableau": 0];

Notes:

  • Un tableau est remplacé par l'entier 0 lorsqu'il est utilisé comme clé.
  • Un nombre à virgule est tronqué à la partie entière lorsqu'il est utilisé comme clé.
  • Un tableau basique est un tableau associatif dont les clés vont de 0 à N - 1, où N est la taille du tableau. Ainsi [1, 2, 3, 4] est égale à [0: 1, 1: 2, 2: 3, 3: 4].

Opérateurs

+

1 + 1; // => 2
1 + 1.0; // => 2
1.0 + 1.0; // => 2
0.5 + 1.5: // 2.0
1 + 1.5; // => 2.5
"Hello " + 'World!'; // => "Hello World!"
1 + "2"; // => "12"
[1] + [2]; // => [1, 2]
['key1': 'value1'] + ["key2": "value2"]; // => ["key1": "value1", "key2": "value2"]
['key1': 'value1'] + ["value2"]; // => ["key1": "value1", 0: "value2"]
[1] + 2; // => "[1]2"
null + 1; // => 1;
[1] + null; // => "[1]null"
debug + 1; // => "#Function debug1"
['key' : "value"] + debug; // => "[key : value]#Function debug"
"" + function(){}; // => "#Anonymous Function"
debug + function(){}; // => "#Function debug#Anonymous Function"

Notes:

  • null est converti en 0 lorsque l'autre argument de l'opérateur est un nombre.
  • Lorsque les types des arguments sont différents, le retour est une chaîne de caractères.

-

-1 - 3; // => -4
3 - -1; // => 4
1 - 1; // 0
0.3 - 0.2; // => 0.09999999999999998
null - 0.2; // => -0.2

Variables

Le nom d'une variable peut être utilisé là où une expression attend une valeur. L'expression utilisera la valeur contenu dans la variable correspondante.

Locale

var x; // x est initialisée à null
var y = 1; // y est initialisé à 1

Notes:

  • les variables locales sont mutable, on peut modifier leur contenu sans les redéclarer.
  • elles sont accessibles uniquement par le code contenu dans le même périmètre.

Globale

global X; // X est initialisée à null
global Y = 1; // Y est initialisé à 1

Notes:

  • les variables globales sont mutable.
  • elles sont accessibles par n'importe quel code.
  • elles ne peuvent être déclarée qu'au périmètre le plus externe (top level), c'est-à-dire hors de tout bloc de code (enclos par des accolades { ... }).
  • la valeur qu'elles contiennent est conservé entre chaque tour de jeu, elles ne sont pas réinitialisées.

Ressource Intéressantes

Ébauche : Tutoriel : Le Cache-Cache

Prérequis:

  • Obligatoire:
Savoir définir une variable.
Savoir définir et manipuler un tableau.
  • Recommandé:
Savoir définir et utiliser des fonctions.
  • Utile:
Savoir manipuler des fonctions.
Connaitre une notation pour les types.

Comprendre le système de coordonnées

L'espace sur lesquels nos poireaux et bulbes évoluent est en deux dimensions. Chaque case de la carte possède des coordonnées X et Y, et se déplacer d'une case à une autre correspond à se déplacer d'une unité sur l'axe des Xs ou des Ys. Grâce à sa finitude, il est possible de transformer nos coordonnées à deux dimensions en coordonnées à une seule dimension. On appel cette unique coordonnée identifiant, c'est ce que vous obtenez lorsque vous utilisez getCell ou toute autre fonction vous retournant une ou des cases de la carte. Bien qu'il soit possible de manipuler directement ces coordonnées en utilisant quelques astuces arithmétiques, utiliser ces méthodes nous expose au risque de travailler avec des cases qui sont en réalité à l'opposé de là où nous les attendons, ou inexistantes. De plus, manipuler les coordonnées dans leur dimensions d'origine reste plus simple, et les fonctions de conversion entre les deux systèmes de coordonnées sont suffisamment efficace pour que nous nous permettions cet indirection.

On peut donc passer d'une cellule à une autre avec les méthodes suivante:

var cell = getCell(leekID);
var nextCell = cell + 17; // On se déplace d'une unité, vers le bas à gauche. En se retrouvant possiblement en dehors de la carte, ou de l'autre coté.
var cell = getCell(leekID);
var cellX = getCellX(cell);
var cellY = getCellY(cell);
var nextCellY = cellY + 1; // On se déplace d'une unité, vers le bas à gauche.
var nextCell = getCellFromXY(cellX, nextCellY); // On récupère l'identifiant de la case. Si on déborde, l'identifiant sera `null`.

Il est aisé de savoir si nous somme en dehors de la carte avec la deuxième méthode, mais cela n'est possible simplement avec la première que dans le cas où l'identifiant résultant est supérieur à 612, ou inférieur à 0.

Nous utiliserons donc le système à deux dimensions par la suite.

Obtenir les cellules accessibles

Commençons par des cas d'utilisation simple. Nous allons d'abord nous contenter de ce que nous savons déjà faire: trouver les cases qui nous sont accessibles avec zéro point de mouvement, et un point de mouvement. Ensuite nous essayeront de relier les deux cas et trouver une méthode qui s'applique à une quantité quelconque de point de mouvement.

Obtenir les cellules accessibles : 0MP

Nous avons affaire ici à l'étape la plus simple, nous pouvons nous contenter de retourner directement un tableau associatif contenant le résultat.

/* code */

Rien de bien compliqué, nous retournons la seul case sur laquelle nous pouvons effectuer des calculs, celle où nous somme déjà présent (ou plutôt celle qu'on nous donne comme point de départ).

A l'avenir, si nous souhaitons afficher les cellules avec mark, nous ne pouvons lui passer les résultats directement en argument. Nous avons besoin de récupérer les clés pour les mettre en tant que valeur dans un tableau. Nous utiliserons donc getKeys défini comme suit:

/* code */

Il est aussi possible d'utiliser directement: arrayMap(assoc, function(@k, @v) { return k; }), mais les clés seront conservés au lieu de devenir 0 .. n.

Obtenir les cellules accessibles : 1MP

Cette fois-ci, nous avons besoin de retourner la case de départ, ainsi que les cases adjacentes inoccupées.

/* code */

À nouveau, rien de bien compliqué. Nous initialisons le tableau {Cell: Distance} avec la case de départ. Ensuite nous récupérons les cases autour, nous vérifions que nous ne débordons pas, et que les cases sont inoccupées. Lorsque toutes les conditions sont remplis, nous ajoutons les cases avec leur distance associé.

Obtenir les cellules accessibles: de 0 à 1 MP

Maintenant nous voulons une fonction qui soit capable de gérer autant les situations sans point de mouvement que celles avec un. Il nous faut donc isoler le code en communs entre les deux fonctions précédentes et une manière simple de passer de l'une à l'autre.

Nous pouvons décider avec confiance que l'initialisation du tableau des cases accessibles est la seule chose en commun. Nous allons donc placer le code servant à obtenir les cases voisines dans une autre fonction. Cela nous permet de nous concentrer sur la décision de récupérer les cases qui sont plus éloignées ou non. Pour cela, nous récupérons la distance pour atteindre la cellule actuelle, nous y ajoutons 1, et nous comparons avec la quantité de points de mouvement disponible. Si nous possédons suffisamment de point de mouvement, nous récupérons les voisins, et nous les ajoutons au tableau des cases accessibles.

/* code */

Si nous voulons étendre ce code pour gérer n'importe quel quantité de point de mouvement, nous aurons besoin de conserver les nouvelles cellules accessibles. Nous pouvons faire cela avec un autre tableau, mais il nous faut savoir comme les ordonner.

Intermède : La File

La file (queue en anglais) est une structure de donnée servant à réaliser une file d'attente. On y place les objets que nous souhaitons traiter plus tard d'un côté, et nous récupérons ce que nous voulons manipuler maintenant de l'autre. En leekscript, nous pouvons réaliser cela avec un tableau et les fonctions push et shift.

Dans le cadre actuel, nous allons nous servir d'une file pour décider dans quel ordre parcourir les cases de la carte, ce sera donc un parcours en largeur. Nous visiterons d'abord toutes les cases à N de distance, et seulement après les cases à N + 1 de distance.

Obtenir les cellules accessibles: de 0 à N MPs

Si vous avez lu la section sur les cellules accessibles avant, vous avez pu observer un animation montrant l'évolution des cases découvertes. Vous avez peut-être remarqué que l'expansion se fait sous forme de vague qui s'étend de tous les côtés en même temps. C'est ce que fait le parcours en largeur, et nous allons nous en inspirer pour nommer notre file. Nous y placerons la case de départ, et nous piocherons dans la file jusqu'à ce qu'il n'y ai plus rien à explorer.
Lorsque nous explorons les voisines d'une cases, nous risquons de revenir sur une cases déjà visitée. Si nous l'ajoutons aux cellules accessibles, nous allons fausser le résultat en augmentant le nombre de point de mouvement nécessaires pour l'atteindre. Afin d'éviter cela, nous nous assurerons aussi de l'absence de la case concernée dans les cellules accessibles avant de l'y ajouter.

/* code */

Aller plus loin

Nous avons réalisé une fonction pour obtenir les cellules accessibles à notre poireau relativement efficace. C'est loin d'être la plus rapide, mais cela reste facilement meilleur comparé à ce que vous auriez fait si vous étiez parti entièrement dans le noir.
Si vous souhaitez améliorer votre fonction, il vous suffit de vous rendre sur le chat de leekwars et de discuter de quelques idées d'optimisations. Mais avant cela, nous allons apporter une dernière modification à notre fonction afin de la rendre plus facile à modifier. En retirant certaine partie du code un peu trop spécifique par rapport à l'intention générale de l'algorithme, nous allons déplacer certaines fonctionnalités en dehors de notre fonction. Cela va nous permettre d'étendre les possibilités de notre fonction sans y toucher. Et nous pourrons aussi tester différentes version en même temps avec aise. Nous allons retirer la fonction permettant d'obtenir les cases voisines, cela vous permettra d'en changer à la volée en modifiant le moins possible de code. Et nous ferons de même avec la condition de tri des voisines. Comme nous en avons tout de même besoin pour récupérer les cellules accessibles, nous allons les ajouter en paramètre de notre fonction.

/* code */
  • expand : Cell -> [Cell] - demande une cellule et retourne un tableau de cellule - nous sert à étendre notre espace d'exploration
  • check : ({Cell : Distance}, Cell) -> Bool - demande un tableau associatif et une cellule, et retourne un booléen - nous sert à vérifier ce qu'expand nous fournis.

En exemple, nous allons définir une fonction d'expansion qui se contente de retourner directement un tableau contenant le résultat de getCellFromXY, et un prédicat qui vérifie si expand nous fournit un voisin non visité, un voisin déjà visité, un obstacle, ou null.

/* code */

Cette version, getAccessibleCells(getDirtyNeighbors, checkDirtyCell, ..., ...) est un peu plus rapide que la précédente. Lorsque vous essaierez d'optimiser vos cellules accessibles, vous pourrez faire cela par le biais d'interaction intéressante entre expand et check, comme ici, ou simplement avec des versions de ces fonctions efficaces d'elles-mêmes, ou encore en modifiant getAccessibleCells. Il existe beaucoup de combinaisons correctes qui donnent des résultats acceptable.

Annoter les cellules accessibles

Cache-cache basique

Maintenant que nous avons de quoi obtenir les cellules accessibles à notre poireau, et à notre adversaire, nous pouvons nous occuper du cache-cache. La version la plus basique ce résume de la sorte: pour chacune de nos cellules accessibles, nous devons vérifier qu'aucune des cellules accessibles de l'adversaire n'a la vision sur nous. Les termes chacune et aucune nous indique que nous aurons besoin de deux boucles. La première pour nos cellules accessibles, et la deuxième à l'intérieur de la première pour essayer de trouver si oui ou non l'adversaire pourra voir la cellule en cours de traitement dans la première boucle.

Parce que lineOfSight prends en compte la position des entités, il va nous falloir passer des identifiants à ignorer afin d'éviter des erreurs lorsque nous testons des cellules dont la ligne passe par une des cases occupé par des entités.

/* code */

Pour récupérer les cellules qui ne seront pas visible par l'adversaire, nous pouvons utiliser le code suivant:

/* code */

Nous pouvons aussi utiliser directement getKeys(arrayFilter(annotated, function(cell, visible) { return !visible; })). Et ensuite nous servir de moveTowardCells pour nous déplacer vers la la cellule sûre la plus proche. Cependant, si aucune cellule n'est hors de vision, nous resterons sur place. Il faudra donc penser à vérifier que notre tableau n'est pas vide afin d'utiliser une alternative en cas d'exposition totale.

Aller plus loin

Se mettre hors de vue, c'est bien, mais se mettre hors de vue ou de portée, c'est mieux. Vérifier cette deuxième condition nous ouvre plus de possibilité en terme de cellules sûre. Nous allons donc retravailler l'évaluation de nos cellules. Pour vérifier la portée, nous avons besoin de plus d'information sur notre ennemie. Comme pour l'algorithme des cellules accessibles, nous allons contenter d'extraire certaine partie du code afin de n'avoir à nous occuper que des spécificités par la suite. Les informations que nous possédons actuellement sur l'adversaire sont ses cellules accessibles, et nous utilisons lineOfSight en combinaison avec les identifiants à ignorer. Nous allons donc retirer tout cela, et prendre une fonction en paramètre qui demandera une cellule et sa distance associé, (peut-être que ça pourrait être utile, on peut toujours l'ignorer sinon), et qui retournera une évaluation du danger sur cette cellule.

/* code */

Remrquez qu'hideNSeek fait exactement le même travail qu'arrayMap. Nous pouvons donc nous permettre de la supprimer et nous concentrer sur notre fonction d'évaluation. Pour vérifier que nous sommes hors de portée, nous avons besoin de parcourir la liste des équipements de l'adversaire et de récupérer la portée la plus élevée pour les équipements offensifs. Pour cela, nous avons besoin de getWeapons, getChips, isWeapon, getWeaponMaxRange, getChipMaxRange, getWeaponEffects, getChipEffect.

/* code */

Maintenant que nous sommes en possession de la portée maximale de l'ennemie, nous pouvons nous occuper de créer la fonction d'évaluation:

/* code */

Bien entendu, cela ne gère pas les puces sans nécessité de ligne de vue telle qu'Etincelle. Pour cela, il faudra faire plus de travail pour vérifier la dangerosité d'étincelle en comparant vos boucliers avec les dégâts de l'adversaire et juger si vous prenez cette puce en compte, ou tout autre équipement.
A l'avenir, vous voudrez sans doute faire évoluer votre évaluation de manière à juger la quantité de dégât reçus au lieu d'y placer un simple booléen. Bien entendu, vous pouvez appliquer un seuil de dégâts pour rester avec les booléens, mais peut-être cela sera-t-il quelque chose que vous préféreriez réaliser plus tard dans votre code. Tout dépends de l'ordre dans lesquels vous effectuez vos évaluations, et les notions de danger et menace peuvent être maintenu séparer encore quelques temps avant que vous ayez à vous poser ces questions.