BFS - HelmDefense/HelmDefense GitHub Wiki

L'algorithme du BFS

Explication de l'algorithme

Son objectif

Dans un graphe, on peut utiliser l'algorithme du BFS (Breadth-First Search / Algorithme de Parcours en Largeur) pour construire un arbre couvrant tout le graphe en ayant pour chaque sommet le chemin le plus direct vers le sommet de départ.

Son principe

On choisi le sommet de départ vers lequel "pointerons" tous les autres. On prend ensuite chaque voisin et on les visite, puis les voisins des voisins, et ainsi de suite en avançant par "couche" en notant à chaque fois quel voisin nous a mené à qui. Ensuite, pour chaque sommet du graphe, il suffit de suivre les voisins dans le sens inverse pour obtenir le chemin le plus court vers le sommet de départ.

Algorithme de parcours en largeur

Explication du principe de l'algorithme en image

Notre utilisation

Pourquoi utiliser cet algorithme ?

Nous avons utilisé le BFS pour calculer, depuis n'importe quelle position sur les chemins de notre plateau de jeu, le trajet le plus court pour atteindre l'arrivée. Cela est utilisé par les attaquants possédant la capacité MoveToEndAbility (actuellement tous).

L'implémentation

Il est assez simple de faire "tourner" cet algorithme à la main mais la mise en place dans le code est légèrement plus délicate. Cela utilise un concept de "file" ("Queue" en anglais).

Pseudo-code

La logique derrière cet algorithme correspond, en pseudo code, à :

Procédure calculerBFS(graphe : Graphe, départ : Sommet)
	Construire une file f
	Marquer départ
	Enfiler départ dans f
	
	TantQue f n'est pas vide Faire
		Défiler dans f et appeler le sommet obtenu s
		TantQue s a un voisin non marqué dans graphe Faire
			Appeler t le voisin obtenu
			Marquer t
			Enfiler t
			Enregistrer que t vient de s
		Fin TantQue
	Fin TantQue
Fin Procédure

Java

Notre implémentation en Java utilise une List<Cell> plutôt qu'une Queue<Cell> car cela nous a paru plus simple et totalement équivalant en terme de résultat final. Nous avons une classe Cell qui matérialise un sommet de graphe ayant un état (marqué ou non) et pouvant mémoriser son suivant (dans l'ordre de lecture, donc son prédécesseur dans l'ordre de construction). Cette méthode fait parti d'un objet Graphe qui possède un départ fixe et chaque sommet connait son graphe, ce qui leur permet de donner la liste de leurs voisins.

L'algorithme :

public void bfs() {
	// On réinitialise le graphe au cas où des opérations aient déjà été effectuées.
	// Cela équivaut à réinitialiser le marquage de tous les sommets et de supprimer leur "suivant"
	this.reset();
	// On construit notre file avec une liste dans laquelle on ajoutera toujours à la fin et on récupèrera au début
	List<Cell> queue = new ArrayList<Cell>();
	Cell c, d;
	this.start.setMarked(true);
	queue.add(this.start); // Enfiler
	
	while (! queue.isEmpty()) {
		c = queue.remove(0); // Défiler
		while ((d = c.getUnmarkedNeighbor()) != null) {
			d.setMarked(true);
			d.setNext(c);
			queue.add(d); // Enfiler
		}
	}
}

La subtilité réside dans le fait de récupérer les voisins non marqués tant qu'il y en a. Nous avons donc opté pour une méthode getUnmarkedNeighbor() dans la classe Cell qui permet d'obtenir un voisin non marqué (de manière arbitraire), ou null si il n'y en a pas. Voici la méthode :

public Cell getUnmarkedNeighbor() {
	Cell c;
	for (Dir d : Dir.list(this.paths)) // On récupère les directions dans lesquelles la cellule a un chemin
		if ((c = this.graph.getCellAt(d.n(this.loc))) != null && ! c.isMarked()) // On récupère et vérifie le voisin
			return c;
	return null;
}
⚠️ **GitHub.com Fallback** ⚠️