/// <summary> /// Flee away from a set of foes, starting on a given cell /// * REVERSED PATH FINDING * /// </summary> public bool FleeFromFoes(short MyCell, short[] FoeCells, int distance) { // Set all Foes as starting points, Exit cell as exit (not used anyway in part 1 of PathFinding) short[] ExitCells = new short[] { MyCell }; FindPath(FoeCells, ExitCells, false, true); // Step 2 ExitCell = ExitCells[0]; short CurrentCell = ExitCell; PathResult.Add(ExitCell); _cells[ExitCell].IsInPath = true; int NbStepLeft = distance; while (NbStepLeft-- > 0) { // Look through each MapNeighbour and find the square // with the lowest number of steps marked. short highestPoint = CellInfo.CELL_ERROR; int PreviousDistance = _cells[CurrentCell].DistanceSteps; int highest = PreviousDistance; foreach (CellInfo NewCell in ValidMoves(_cells[CurrentCell], false)) { int count = NewCell.DistanceSteps; if (count > highest) { highest = count; highestPoint = NewCell.CellId; } } if (highest != PreviousDistance) { // Mark the square as part of the path if it is the lowest // number. Set the current position as the square with // that number of steps. PathResult.Add(highestPoint); _cells[highestPoint].IsInPath = true; CurrentCell = highestPoint; if (PathResult.Count > _cells.Length) { Debug.Assert(false, "PathFinder can't find a path - overflow"); break; } } else { // Can't find a longer path => stop now :( break; } } //PathResult.Reverse(); // Reverse the path, as we started from exit return(PathResult.Count > 0); }
/// <summary> /// PathFinding main method /// </summary> /// <param name="startingCells"></param> /// <param name="exitCells"></param> /// <param name="selectFartherCells"></param> /// <param name="firstStepOnly"></param> /// <returns></returns> public bool FindPath(int StartingSubMapId, int[] ExitSubMapIds, bool FirstStepOnly = false) { Random rnd = new Random(); ClearLogic(); if (cells == null) { throw new Exception("Cells are not initialized"); } if (StartingSubMapId < 0 || StartingSubMapId >= cells.Length) { return(false); // We need at least one starting cell } if (!FirstStepOnly && (ExitSubMapIds == null || ExitSubMapIds.Length == 0)) { return(false); // We need at least one exit cell for step 2 } // PC starts at distance of 0. Set 0 to all possible starting cells cells[StartingSubMapId].distanceSteps = 0; // cells[StartingCell].distanceSteps = 0; int NbMainLoop = 0; while (true) { NbMainLoop++; bool madeProgress = false; // Look at each square on the board. for (int CurrentSubMap = 0; CurrentSubMap < cells.Length; CurrentSubMap++) { Int16[] LocalLinks = links[CurrentSubMap]; uint penalty = 0; uint nexMapDistance = cells[CurrentSubMap].distanceSteps + 1; foreach (Int16 Link in LocalLinks) { if (mapPenalties != null) { penalty = mapPenalties[Link]; } if (cells[Link].distanceSteps > nexMapDistance + penalty) { cells[Link].distanceSteps = nexMapDistance + penalty; madeProgress = true; } } } if (!madeProgress) { break; } } if (FirstStepOnly) { return(true); } // Step 2 // Mark the path from Exit to Starting position. // if several Exit cells, then get the lowest distance one = the closest from one starting cell // (or the highest distance one if selectFartherCells) ExitCell = ExitSubMapIds[0]; uint MinDist = cells[ExitCell].distanceSteps; foreach (int cell in ExitSubMapIds) { if (cells[cell].distanceSteps < MinDist) { ExitCell = cell; MinDist = cells[cell].distanceSteps; } } int CurrentCell = ExitCell; PathResult.Add(ExitCell); cells[ExitCell].isInPath = true; // Look through each MapNeighbour and find the square // with the lowest number of steps marked. int lowestPoint; uint lowest; List <int> LowestPoints = new List <int>(10); while (true) { // Look through each MapNeighbour and find the square // with the lowest number of steps marked. lowestPoint = CellInfo.CELL_ERROR; lowest = DEFAULT_DISTANCE; foreach (int NewSubMapId in links[CurrentCell]) { uint count = cells[NewSubMapId].distanceSteps; if (count < lowest) { LowestPoints.Clear(); lowest = count; lowestPoint = NewSubMapId; } else if (count == lowest) // If more than one point, then push it in the list, for random determination { if (LowestPoints.Count == 0) { LowestPoints.Add(lowestPoint); } LowestPoints.Add(NewSubMapId); } } if (lowest == DEFAULT_DISTANCE) { break; // Can't find a valid way :( } if (LowestPoints.Count > 1) // Several points with same distance =>> randomly select one of them { lowestPoint = LowestPoints[rnd.Next(LowestPoints.Count)]; } // Mark the subMap as part of the path if it is the lowest // number. Set the current position as the subMap with // that number of steps. PathResult.Add(lowestPoint); cells[lowestPoint].isInPath = true; CurrentCell = lowestPoint; if (cells[CurrentCell].distanceSteps == 0) // Exit reached { StartingCell = CurrentCell; // We went from closest Exit to a Starting position, so we're finished. break; } } PathResult.Reverse(); // Reorder the path from starting position to target return(CurrentCell == StartingCell); }
/// <summary> /// PathFinding main method /// </summary> /// <param name="startingCells"></param> /// <param name="exitCells"></param> /// <param name="selectFartherCells"></param> /// <param name="firstStepOnly"></param> /// <returns></returns> public bool FindPath(short[] startingCells, short[] exitCells, bool selectFartherCells = false, bool firstStepOnly = false, int maxDistanceParam = CellInfo.DEFAULT_DISTANCE) { Random rnd = new Random(); if ((startingCells == null) || (startingCells.Length == 0)) { return(false); // We need at least one starting stCell } if (!firstStepOnly && (exitCells == null || exitCells.Length == 0)) { return(false); // We need at least one exit stCell for step 2 } // PC starts at distance of 0. Set 0 to all possible starting cells CellInfo[] changed = new CellInfo[560]; int changedPtr = 0; CellInfo[] changing = new CellInfo[560]; int changingPtr = 0; bool optimizerActiv = !firstStepOnly && !selectFartherCells; // This strong optimization may fail to find a path. In that case, the non-optimized algorithm is run while (true) { ClearLogic(startingCells, exitCells); uint EstimatedDistance = CellInfo.DEFAULT_DISTANCE; Cell bestStartingCell = null; Cell bestEndingCell = null; foreach (short stCell in startingCells) { if (_cells[stCell] != null) { _cells[stCell].DistanceSteps = 0; changed[changedPtr++] = _cells[stCell]; if (!firstStepOnly && !selectFartherCells) { foreach (short exCell in exitCells) { if (exCell == stCell) { PathResult = new List <short> { stCell }; return(true); // Empty path : starting stCell = exit stCell } if (optimizerActiv) { uint distance = _cells[stCell].Cell.ManhattanDistanceTo(_cells[exCell].Cell); if (distance < EstimatedDistance) { bestStartingCell = _cells[stCell].Cell; bestEndingCell = _cells[exCell].Cell; EstimatedDistance = distance; } } } } } } // cells[StartingCell].distanceSteps = 0; int maxDistance = maxDistanceParam; // We won't search over this distance - this optimization is OK in all cases if (optimizerActiv && bestStartingCell == null || bestEndingCell == null) { optimizerActiv = false; } while (changedPtr > 0) { changingPtr = 0; // Look at each square on the board. while (changedPtr > 0) { CellInfo curCell = changed[--changedPtr]; if (curCell.IsCloseToEnemy && _isCautious) { continue; // Cautious mode (in or out of fight) : Can't move from a cell near an ennemy } if (curCell.DistanceSteps < maxDistance) { if (optimizerActiv) // Strong optimisation { uint lastEstimatedDistance = curCell.Cell.ManhattanDistanceTo(bestEndingCell); uint startDistance = curCell.Cell.ManhattanDistanceTo(bestStartingCell); if (startDistance + lastEstimatedDistance > EstimatedDistance) { continue; } } //Debug.Assert((curCell != null && curCell.DistanceSteps < CellInfo.DEFAULT_DISTANCE)); short[] cellNeighbours = neighbours[curCell.CellId]; for (short i = 0; i < cellNeighbours.Length; i++) { CellInfo newCell = _cells[cellNeighbours[i]]; if (newCell == null) { continue; } if (newCell.DistanceSteps != 0 && !SquareOpen(newCell, null)) { continue; } //uint currentDistance = newCell.Cell.ManhattanDistanceTo(_cells[exitCells[0]].Cell); //if (currentDistance >= EstimatedDistance || currentDistance >= lastEstimatedDistance) continue; int newPass = curCell.DistanceSteps; if (curCell.IsCloseToEnemy) { newPass++; // Penality when close of an ennemy (same in fight and RP map) } if (_isInFight) { newPass++; } else { newPass += newCell.Weight; } if (newCell.DistanceSteps > newPass) { newCell.DistanceSteps = newPass; changing[changingPtr++] = newCell; if (!firstStepOnly && !selectFartherCells && newPass < maxDistance && exitCells.Any(id => newCell.CellId == id)) { maxDistance = newPass; // We won't search on distance over closest exit } } } if (_isInFight) { continue; } cellNeighbours = diagNeighbours[curCell.CellId]; for (short i = 0; i < cellNeighbours.Length; i++) // Process diagonals { CellInfo newCell = _cells[cellNeighbours[i]]; if (newCell == null) { continue; } if (newCell.DistanceSteps != 0 && !SquareOpen(newCell, null)) { continue; } int newPass = curCell.DistanceSteps; if (curCell.IsCloseToEnemy) { newPass++; // Penality when close of an ennemy (same in fight and RP map) } if (_isInFight) { newPass++; } else { newPass += (int)(newCell.Weight * 1.414); } if (newCell.DistanceSteps > newPass) { newCell.DistanceSteps = newPass; changing[changingPtr++] = newCell; if (!firstStepOnly && !selectFartherCells && newPass < maxDistance && exitCells.Any(id => newCell.CellId == id)) { maxDistance = newPass; // We won't search on distance over closest exit } } } } } CellInfo[] tmpChanged = changed; changed = changing; changedPtr = changingPtr; changing = tmpChanged; } if (firstStepOnly) { return(true); } // Step 2 // Mark the path from Exit to Starting position. // if several Exit cells, then get the lowest distance one = the closest from one starting cell // (or the highest distance one if selectFartherCells) ExitCell = exitCells[0]; int MinDist = _cells[ExitCell].DistanceSteps; foreach (short cell in exitCells) { if ((selectFartherCells && _cells[cell].DistanceSteps > MinDist) || (!selectFartherCells && _cells[cell].DistanceSteps < MinDist)) { ExitCell = cell; MinDist = _cells[cell].DistanceSteps; } } if (optimizerActiv == false || MinDist < CellInfo.DEFAULT_DISTANCE) { break; // No need to run a second unoptimized algorithm } else { optimizerActiv = false; } } //int no = 0; //Debug.WriteLine("PathFinding from {0} ({1}) to {2} ({3})", _cells[startingCells[0]].cell, _cells[startingCells[0]].distanceSteps, _cells[ExitCell].cell, _cells[ExitCell].distanceSteps); /*List<Cell> ListMax = new List<Cell>(); * List<Cell> ListInc = new List<Cell>(); * foreach (var cell in _cells) * { * if (cell.distanceSteps >= CellInfo.DEFAULT_DISTANCE) * ListMax.Add(cell.cell); * if (cell.distanceSteps <= MinDist) * ListInc.Add(cell.cell); * //else * // continue; * Debug.Write(String.Format("{0} : {1}, ", cell.cell, cell.distanceSteps)); * if (no++ == 5) * { * Debug.WriteLine(""); * no = 0; * } * }*/ //Debug.WriteLine(""); //BotManager.Instance.Bots[0].Character.ResetCellsHighlight(); //BotManager.Instance.Bots[0].Character.HighlightCells(ListMax, Color.Black); //BotManager.Instance.Bots[0].Character.HighlightCells(ListMax, Color.DarkGray); short CurrentCell = ExitCell; PathResult.Add(ExitCell); _cells[ExitCell].IsInPath = true; CellInfo[] lowestPoints = changed; // No ned to alloc a new one, this one won't be used anymore int lowestPointsPtr = 0; short lowestPoint; int lowest; while (true) { // Look through each MapNeighbour and find the square // with the lowest number of steps marked. lowestPoint = CellInfo.CELL_ERROR; lowest = CellInfo.DEFAULT_DISTANCE; short[] neighbours = GetNeighbours(CurrentCell, _isInFight); for (short i = 0; i < neighbours.Length; i++) { //foreach (CellInfo newCell in ValidMoves(curCell, false)) CellInfo newCell = _cells[neighbours[i]]; if (newCell == null || (newCell.IsCloseToEnemy && _isCautious && newCell.DistanceSteps != 0)) { continue; // In cautious mode, don't come close to an enemy } //for (CellInfo NewCell in ValidMoves(_cells[CurrentCell], true)) //{ int distance = newCell.DistanceSteps; /*if (distance > CellInfo.DEFAULT_DISTANCE) * { * Debug.Assert(false, "Distance shouldn't be higher than DEFAULT_DISTANCE", "Distance = {0} > Max = {1}", distance, CellInfo.DEFAULT_DISTANCE); * continue; * }*/ if (distance < lowest) { lowestPointsPtr = 1; lowest = distance; lowestPoints[0] = newCell; } else if (distance == lowest) { lowestPoints[lowestPointsPtr++] = newCell; } } if (lowest == CellInfo.DEFAULT_DISTANCE) { break; // Can't find a valid way :( } if (lowestPointsPtr > 1) // Several points with same distance =>> randomly select one of them { lowestPoint = lowestPoints[rnd.Next(lowestPointsPtr)].CellId; } else { lowestPoint = lowestPoints[0].CellId; } // Mark the square as part of the path if it is the lowest // number. Set the current position as the square with // that number of steps. PathResult.Add(lowestPoint); if (PathResult.Count > _cells.Length) { Debug.Assert(false, "PathFinder can't find a path - overflow"); break; } //Debug.Assert(_cells[lowestPoint].IsInPath == false, "Point already in path", "CurrentCell : {0}, Lowest : {1} - distance : {2}, path : {3}", _cells[CurrentCell].Cell, _cells[lowestPoint].Cell, lowest, string.Join(",", _cells.Where(stCell => stCell.IsInPath))); _cells[lowestPoint].IsInPath = true; CurrentCell = lowestPoint; if (_cells[CurrentCell].DistanceSteps == 0) // Exit reached { StartingCell = CurrentCell; // We went from closest Exit to a Starting position, so we're finished. break; } } PathResult.Reverse(); return(CurrentCell == StartingCell); }