// Methode permettant de retourner si deux tableau sont égaux public override bool Equals(object obj) { bool equal = true; if (obj != null) { EvaluableBoard board = (EvaluableBoard)obj; foreach (Cell cell in board) { int posI, posJ; board.Board.FindCellByValue(out posI, out posJ, cell.Value); if (cell.Value != "-") { if (Board.Structure[posI, posJ].Value != "-1" && board.Board.Structure[posI, posJ].Value != "-1") { if (Board.Structure[posI, posJ].Value != cell.Value) { equal = false; } } } } } else { equal = false; } return(equal); }
// Méthode permettant de dupliquer un board en rompant // les effet de bord dus au type référence des tableaux public static EvaluableBoard CopyBoard(EvaluableBoard board) { EvaluableBoard result = new EvaluableBoard(board.Score); Cell[,] structure = new Cell[board.Size, board.Size]; // On replace au bon endroit toute les cellules foreach (Cell cell in board) { Cell newCell = new Cell(cell.Value); int posI, posJ; board.Board.FindCellByValue(out posI, out posJ, cell.Value); structure[posI, posJ] = newCell; } // On s'occupe de replaer les trous Cell emptyOne = new Cell("-"); Cell emptyTwo = new Cell("-"); int e1i, e1j, e2i, e2j; board.Board.FindEmptyOne(out e1i, out e1j); board.Board.FindEmptyTwo(out e2i, out e2j); structure[e1i, e1j] = emptyOne; structure[e2i, e2j] = emptyTwo; // On créer le duplicata result.Board = new Board(structure); result.Size = result.Board.Structure.GetLength(0); return(result); }
public EvaluableBoard(Board board) { Previous = null; Board = board; Size = board.Structure.GetLength(0); Score = 0; }
public override List <Board> Solve(EvaluableBoard board) { // Mise en place du seuil à partir du noeud de départ Size = board.Board.Structure.GetLength(0); _destination = Functions.CreateTarget(Size); int threshold = Heuristic.EvaluateBoard(board.Board, _destination.Board); //Setting the StartNode for this iteration EvaluableBoard start = board; while (true) { // Départ de la fonction récursive sur le premier noeud EvaluableBoard temp = Search(start, 0, threshold); int tempScore = temp.Score; if (temp.Equals(_destination)) { openCount = _openSet.Count; closedCount = _closedSet.Count; return(Unpile(temp)); } threshold = tempScore; _openSet = new List <EvaluableBoard>(); _closedSet = new List <EvaluableBoard>(); } }
// Boucle de résoution public override List <Board> Solve(EvaluableBoard board) { // Etape d'initialisation _openSet = new List <EvaluableBoard>(); _closedSet = new List <EvaluableBoard>(); Size = board.Board.Structure.GetLength(0); _openSet.Add(board); EvaluableBoard result; // Boucle de résolution en ligne for (int rank = 0; rank < (Size * Size) - Size * 2; rank++) { _currentBoard = UnderStep(rank); } bool lineOne = true; int start = Size * Size - Size * 2; // Ensuite on place les deux dernières ligne par colonnes for (int rank = start; rank < start + Size; rank++) { _currentBoard = UnderStep(rank); int step = rank + Size; if (step < Size * Size - 2) { _currentBoard = UnderStep(step); } } return(Unpile(_currentBoard)); }
// Evaluation de l'heuristique humaine private int Eval(EvaluableBoard board, int step) { int score = 0; int optI, optJ, currI, currJ; if (step < (Size * Size) - Size * 2) { for (int i = 0; i < step; i++) { // Recherche de la position optimale Functions.pos2coord(out optI, out optJ, i, Size); // Recherche de la position courante board.Board.FindCellByValue(out currI, out currJ, Convert.ToString(i)); // Prise ne compte de la distance case destination score += 20 * (Math.Abs(optI - currI) + Math.Abs(optJ - currJ)); } } else { for (int i = 0; i < (Size * Size) - Size * 2; i++) { // Recherche de la position optimale Functions.pos2coord(out optI, out optJ, i, Size); // Recherche de la position courante board.Board.FindCellByValue(out currI, out currJ, Convert.ToString(i)); // Prise ne compte de la distance case destination score += 20 * (Math.Abs(optI - currI) + Math.Abs(optJ - currJ)); } } // Prise en compte de la case courante a placer Functions.pos2coord(out optI, out optJ, step, Size); board.Board.FindCellByValue(out currI, out currJ, Convert.ToString(step)); score += 8 * (Math.Abs(optI - currI) + Math.Abs(optJ - currJ)); // Recherche des trous int hole1I, hole1J, hole2I, hole2J; board.Board.FindEmptyOne(out hole1I, out hole1J); board.Board.FindEmptyTwo(out hole2I, out hole2J); // Prise en compte de la distance trou case à placer score += 3 * (Math.Abs(hole1I - currI) + Math.Abs(hole1J - currJ)); score += 3 * (Math.Abs(hole2I - currI) + Math.Abs(hole2J - currJ)); // Prise en compte de la distance trou destination int distH1 = Math.Abs(optI - hole1I) + Math.Abs(optJ - hole1J); int distH2 = Math.Abs(optI - hole2I) + Math.Abs(optJ - hole2J); //score += 5 * distH1; //score += 5 * distH2; score += 5 * Math.Min(distH1, distH2); return(score); }
// Methode permettant de récupérer le chemin de résolution public List <Board> Unpile(EvaluableBoard board) { List <Board> result = new List <Board>(); while (board != null) { result.Add(board.Board); //Console.WriteLine(board.Board); board = board.Previous; } return(result); }
// Fonction de création des enfants spécifiques public static List <EvaluableBoard> CreateChild(EvaluableBoard board, int step) { List <EvaluableBoard> neighbours = new List <EvaluableBoard>(); int size = board.Board.Structure.GetLength(0); List <int> values; // Valeurs possibles if (size == 3) { values = new List <int>() { 0, 1, 2, 3, 6, 4, 5 } } ; else { values = new List <int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 16, 21, 17, 22, 18, 19 } }; int maxIndex = values.IndexOf(step); foreach (Cell cell in board) { if (cell.Value != "-") { int index = values.IndexOf(Convert.ToInt32(cell.Value)); // On interdit les mouvements au case déjà placées if (index >= maxIndex) { int i, j; board.Board.FindCellByValue(out i, out j, cell.Value); if (cell.IsMovable()) { foreach (Cell.Moves move in cell.AvailableMoves) { EvaluableBoard neighbour = CopyBoard(board); neighbour.Cost = board.Cost; neighbour.Board.Move(neighbour.Board.Structure[i, j], move); neighbour.Previous = board; neighbours.Add(neighbour); } } } } } return(neighbours); } #endregion }
public bool FindPast(EvaluableBoard board) { bool result = false; foreach (EvaluableBoard oldBoard in _closedSet) { if (oldBoard.Equals(board)) { result = true; } } return(result); }
// Fonction de résolution intermédiaire qui applique A* entre deux états quelconques EvaluableBoard UnderStep(int rank) { _destination = new EvaluableBoard(CreateStep(Size, rank)); while (_openSet.Count > 0) { _currentBoard = _openSet[0]; if (_currentBoard.Equals(_destination)) { closedCount += _closedSet.Count; openCount += _openSet.Count; // Si on est à la dernière étape, on renvoit le résultat if (rank == Size * Size - 3) { return(_currentBoard); } // Sinon on nettoie le solver pour la prochaine étape else { _openSet = new List <EvaluableBoard>(); _closedSet = new List <EvaluableBoard>(); _currentBoard.Cost = 0; _currentBoard.Score = 0; _openSet.Add(_currentBoard); return(_currentBoard); } } List <EvaluableBoard> holder = CreateChild(_currentBoard, rank); foreach (EvaluableBoard testBoard in holder) { if (FindPast(testBoard) || FindBest(testBoard)) { } else { testBoard.Cost += 1; // Evaluation d'une heuristique humaine spécifique int thisHumanHeuri = Eval(testBoard, rank); testBoard.Score = testBoard.Cost + thisHumanHeuri; _openSet.Add(testBoard); } } _closedSet.Add(_currentBoard); _openSet.Remove(_currentBoard); _openSet = _openSet.OrderBy(b => b.Score).ToList(); } return(null); }
public bool FindBest(EvaluableBoard board) { bool result = false; foreach (EvaluableBoard currentBoard in _openSet) { if (board.Equals(currentBoard) && currentBoard.Cost <= board.Cost) { result = true; } else if (board.Equals(currentBoard) && currentBoard.Cost > board.Cost) { currentBoard.Cost = board.Cost; } } return(result); }
/// <summary> /// Implémentation de l'algorithme A* /// </summary> /// <param name="board"> tableau de départ à résoudre </param> /// <returns></returns> public override List <Board> Solve(EvaluableBoard board) { // Création de la liste des ouvert vides _openSet = new List <EvaluableBoard>(); int size = board.Size; // taille de l'example à résoudre _destination = Functions.CreateTarget(size); _openSet.Add(board); List <Board> result = new List <Board>(); // Initialisation du chemin // Boucle de résolution while (_openSet.Count > 0) { _currentBoard = _openSet[0]; // On récupère le meilleur élément if (_currentBoard.Equals(_destination)) // Si on est arrivé, on arrête { openCount = _openSet.Count; closedCount = _closedSet.Count; result = Unpile(_currentBoard); return(result); } // Sinon on créer les voisins List <EvaluableBoard> holder = CreateChild(_currentBoard); foreach (EvaluableBoard testBoard in holder) { if (FindPast(testBoard) || FindBest(testBoard)) { } // Si le voisin à déjà été évalué on ne le considère pas else { // On applique les mesure de cout et d'heuristique à l'enfant et on l'ajoute à la liste testBoard.Cost += 1; testBoard.Score = testBoard.Cost + Heuristic.EvaluateBoard(testBoard.Board, _destination.Board); _openSet.Add(testBoard); } } // On enlève le noeud courrant des ouvert et on la joute à l aliste des fermés _closedSet.Add(_currentBoard); _openSet.Remove(_currentBoard); // On ordonne la liste pour garder les meilleurs en premier _openSet = _openSet.OrderBy(b => b.Score).ToList(); } openCount = _openSet.Count; closedCount = _closedSet.Count; return(result); }
/// <summary> /// Fonction récursive au coeur de l'algorithme IDA* /// permet d'évaluer les parcours potentiels vers la solution /// </summary> /// <param name="currEval"></param> /// <param name="cost"></param> /// <param name="threshold"></param> /// <returns></returns> public EvaluableBoard Search(EvaluableBoard currEval, int cost, int threshold) { // Evaluation du cout récursif int f = cost + Heuristic.EvaluateBoard(currEval.Board, _destination.Board); _openSet.Add(currEval); // Si le score dépasse le seuil on coupe la branche if (f > threshold) { currEval.Score = f; return(currEval); } if (currEval.Equals(_destination)) { return(currEval); } int min = int.MaxValue; // Recherche et evaluaiton des voisins List <EvaluableBoard> holder = CreateChild(currEval); foreach (EvaluableBoard child in holder) { child.Score = cost + Heuristic.EvaluateBoard(child.Board, _destination.Board); } holder = holder.OrderBy(b => b.Score).ToList(); foreach (EvaluableBoard child in holder) { _openSet.Remove(currEval); _closedSet.Add(currEval); // Appel récrsif on évalue chaque enfant dans la fonction de recherche EvaluableBoard temp = Search(child, cost + 1, threshold); int tempScore = temp.Score; if (temp.Equals(_destination)) { return(temp); } if (tempScore < min) { min = tempScore; } } currEval.Score = min; return(currEval); }
/// <summary> /// Méthode retournant les voisins d'un état spécifique /// </summary> public static List <EvaluableBoard> CreateChild(EvaluableBoard board) { List <EvaluableBoard> neighbours = new List <EvaluableBoard>(); // On regarde si les cellules peuvent bouger foreach (Cell cell in board) { int i, j; board.Board.FindCellByValue(out i, out j, cell.Value); if (cell.IsMovable()) { // Pour chaque mouvement possible, on crée un tableau ou on effectue le mouvement foreach (Cell.Moves move in cell.AvailableMoves) { EvaluableBoard neighbour = CopyBoard(board); neighbour.Cost = board.Cost; neighbour.Board.Move(neighbour.Board.Structure[i, j], move); neighbour.Previous = board; neighbours.Add(neighbour); } } } return(neighbours); }
} // L'algorithme de résolution à besoin d'une estimation du coup #endregion #region AbstractsToOverride /// <summary> /// La fonction ou s'implémente l'algorithme de résolution choisi /// </summary> /// <param name="board"> tableau cible à résoudre </param> /// <returns></returns> public abstract List <Board> Solve(EvaluableBoard board);
public EvaluableBoard(int score) { Previous = null; Score = score; }