public Band(Map myMap, Coords startPos, UInt16 ID, Team team, Brain brain, List <Creature> members, SpriteBatchCreature myBitmap) : base(myMap, startPos, ID, team, brain) { _members = members; _myBitmap = myBitmap; }
private List<Direction> _PathfinderAStar(Coords start, Coords endTopLeft, Coords endBottomRight, BitArray[] _passabilityMap, hFunction h) { // NOTE: Should later implemented a collision predictor mechanic to work in tandem // with the path-finder to provide better agent behavior. // NOTE: Consider returning the number of tiles scanned in case no path is found. // This will alert a boxed-in creature of its predicament. // NOTE: Introduce a flag for a straight-line initial check(for outdoors environmens and // for when the goal is near). Int32 rangeX = _passabilityMap.Length; Int32 rangeY = _passabilityMap[0].Count; NodeAStar?[,] nodeArray = new NodeAStar?[rangeX, rangeY]; NodeAStar startNode = new NodeAStar(); startNode.costSoFar = 0; startNode.estimatedTotalCost = h(start); nodeArray[start.X, start.Y] = startNode; List<Coords> ListOpen = new List<Coords>(); ListOpen.Add(start); while (ListOpen.Count > 0) { // I have to use this bool the way I've implemented the algo. Consider rewriting. bool resortList = false; Coords currentCoords = ListOpen.First(); // Check to see if goal is reached. //if (currentCoords.Equals(endTopLeft)) if (StaticMathFunctions.CoordinateIsInBox(currentCoords, endTopLeft, endBottomRight)) { break; } NodeAStar currentNode = nodeArray[currentCoords.X, currentCoords.Y].Value; for (byte i = 0; i <= 5; ++i) { Direction currentDir = (Direction)(i); //Coords dirCoords = StaticMathFunctions.DirectionToCoords(currentDir); Coords potential = StaticMathFunctions.CoordsNeighboringInDirection(currentCoords, currentDir); // check if move in dir is allowed if (potential.X >= 0 && potential.X < rangeX && potential.Y >= 0 && potential.Y < rangeY // bounds check && _passabilityMap[potential.X][potential.Y]) // passability check { // Using the simplest cost function possible. Can be easily updated // once tile walkability coefficients are added. //Coords newNodePosition = new Coords(CoordsType.General, currentCoords.X + dirCoords.X, currentCoords.Y + dirCoords.Y); Coords newNodePosition = potential; float accruedCost = currentNode.costSoFar + 1; // Straight line correction if (currentDir == nodeArray[currentCoords.X, currentCoords.Y].Value.connection) { accruedCost -= Constants.PathfinderStraightPathCorrection; } // Check to see if the node under examination is in the closed list. //NodeAStar? oldNode = nodeArray[newNodePosition.X, newNodePosition.Y]; if (nodeArray[newNodePosition.X, newNodePosition.Y] != null) { // If node is in closed list, see if it needs updating. if (nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar > accruedCost) { float expectedAdditionalCost = nodeArray[newNodePosition.X, newNodePosition.Y].Value.estimatedTotalCost - nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar; NodeAStar nodeToAdd = new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost); nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd; ListOpen.Add(newNodePosition); resortList = true; } } // Node is in open list. Process it. else { float expectedAdditionalCost = h(newNodePosition); NodeAStar nodeToAdd = new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost); nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd; ListOpen.Add(newNodePosition); resortList = true; } } } ListOpen.RemoveAt(0); if (resortList) { ListOpen.Sort( delegate(Coords c1, Coords c2) { float difference = nodeArray[c1.X, c1.Y].Value.estimatedTotalCost - nodeArray[c2.X, c2.Y].Value.estimatedTotalCost; Int32 returnValue = 0; if (difference > 0) { returnValue = 1; } else if (difference < 0) { returnValue = -1; } return returnValue; } ); } } List<Direction> ListRoute = new List<Direction>(); // Return empty route if the open list is empty, i.e. there is no path to the target // Ideally, the game logic should be fixed so that the search isn't even attempted // if there is no path between the two points. if (ListOpen.Count == 0) { return ListRoute; } Coords trackbackCoords = endTopLeft; while (trackbackCoords != start) { Direction newDirection = nodeArray[trackbackCoords.X, trackbackCoords.Y].Value.connection; ListRoute.Add(newDirection); trackbackCoords = StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, trackbackCoords), StaticMathFunctions.OppositeDirection(newDirection)); } // Might be faster without reversing //ListRoute.Reverse(); // We skip the reversal, so pick directions from the END of the list. return ListRoute; }
public virtual void PositionSet(Coords newValue) { _positionTile = newValue; }
/// <summary> /// Tile-level (coarse) A* pathfinding. /// </summary> /// <param name="start"> Start Coords </param> /// <param name="end"> Target tile Coords </param> /// <param name="h"> Heuristic function </param> /// <returns> Route to goal, as a list of Directions </returns> public List<Direction> PathfinderAStarCoarse(Coords start, Coords end, HeuristicFunction h) { return this.PathfinderAStarCoarse(start, end, end, h); }
private void CalculateMoveRange(Coords origin, UInt16[] moveCosts, UInt16 availableAP) { _currentOrigin = origin; for (int i = 0; i < _currentMap.BoundX; ++i) { for (int j = 0; j < _currentMap.BoundY; ++j) { _APCount[i, j] = -1; _directionMap[i, j] = null; } } _APCount[origin.X, origin.Y] = availableAP; // WARNING: Code repetition with influence maps / A* Queue<Coords> currentQueue = new Queue<Coords>(); Queue<Coords> nextQueue = new Queue<Coords>(); currentQueue.Enqueue(origin); UInt32 currentDistance = 0; // main loop // Stopping conditions: the two queues are exhausted, OR InfluenceMapMaxDistance is reached while ( ((currentQueue.Count > 0) & (nextQueue.Count > 0)) | (currentDistance < Constants.InfluenceMapMaxDistance) ) { // Checks if it's time to start the next pass if (currentQueue.Count == 0) { currentQueue = nextQueue; nextQueue = new Queue<Coords>(); currentDistance++; continue; } Coords currentCoords = currentQueue.Peek(); //Coords delta1 = currentCoords - origin; Tile currentTile = _currentMap.GetTile(currentCoords); // Analyzes the neighbors of the current Tile for possible additions to nextQueue for (byte i = 0; i < 6; i++) { Direction currentDir = (Direction)i; Coords toCheck = StaticMathFunctions.CoordsNeighboringInDirection(currentCoords, currentDir); if (_currentMap.CheckInBounds(toCheck)) { Tile targetTile = _currentMap.GetTile(toCheck); UInt16 cost = moveCosts[(sbyte)targetTile.MyTerrainType]; if (cost > 0 && _currentMap.TenancyMap[toCheck.X, toCheck.Y] == null) // ignore impassable terrain and ignore occupied tiles { // checks if this approach is cheaper than the best approach so far Int32 currentAPleft = _APCount[toCheck.X, toCheck.Y]; Int32 potentialAPleft = _APCount[currentCoords.X, currentCoords.Y] - cost; if (currentAPleft < potentialAPleft) { _APCount[toCheck.X, toCheck.Y] = (UInt16)potentialAPleft; _directionMap[toCheck.X, toCheck.Y] = currentDir; nextQueue.Enqueue(toCheck); } } } } currentQueue.Dequeue(); } for (int i = 0; i < _currentMap.BoundX; ++i) { for (int j = 0; j < _currentMap.BoundY; ++j) { _rangeMap[i][j] = _APCount[i, j] >= 0; } } //return rangeMap; }
public InfluenceSourceMap(Map currentMap, Coords source, InfluenceSpreadFunction f, float minTreshold, float maxRange) : this(currentMap, source, f) { _lowTreshold = minTreshold; _maxTreshold = maxRange; }
/// <summary> /// Tile-level (coarse) A* pathfinding. /// </summary> /// <param name="start"> Start Coords </param> /// <param name="endTopLeft"> Goal-box TopLeft Coords </param> /// <param name="endBottomRight"> Goal-box BottomRight Coords </param> /// <param name="h"> Heuristic function </param> /// <returns> Route to goal, as a list of Directions </returns> public List<Direction> PathfinderAStarCoarse(Coords start, Coords endTopLeft, Coords endBottomRight, HeuristicFunction h) { return this._PathfinderAStar(new Coords(CoordsType.General, start), new Coords(CoordsType.General, endTopLeft), new Coords(CoordsType.General, endBottomRight), this._passabilityMap, delegate(Coords c) { return h(c, StaticMathFunctions.CoordsAverage(endTopLeft, endBottomRight)); }); }
/// <summary> /// Returns the visibility status of coord c for a team: -1 is unexplored, 0 unseen, a positive return /// indicates the number of team members observing the tile. /// </summary> public Int16 VisibilityCheck(Coords c, Team team) { return(_teamVisibilityTracker[team.UniqueID][c.X, c.Y]); }
/// Generates the influence map. /// Uses a silly recursive algorithm. /// Stopping conditions: Let's use two, to avoid stupid infinite loops. /// One is a distance threshold check. // Second is a min influence threshold check. /// <summary> /// Generates the influence map. /// Uses a silly recursive algorithm. /// Stopping conditions: Let's use two, to avoid stupid infinite loops. /// One is a distance threshold check. /// Second is a min influence threshold check. /// </summary> public void GenerateInfluenceMap() { // boolean array to keep note of which tiles have been processed //BitArray[,] takenCareOf = new BitArray[_currentMap.BoundX, _currentMap.BoundY]; BitArray[] takenCareOf = new BitArray[_currentMap.BoundX]; for (int i = 0; i < _currentMap.BoundX; ++i) { takenCareOf[i] = new BitArray(_currentMap.BoundY); } takenCareOf[Source.X][Source.Y] = true; // sets up two queues - one for the current pass, one for the next one // distance increments by one at each pass // if too slow, the process should be broken up so it does a number of passes each tick Queue <Coords> currentQueue = new Queue <Coords>(); Queue <Coords> nextQueue = new Queue <Coords>(); currentQueue.Enqueue(_source); UInt32 currentDistance = 0; // main loop // Stopping conditions: the two queues are exhausted, OR InfluenceMapMaxDistance is reached while ( ((currentQueue.Count > 0) & (nextQueue.Count > 0)) | (currentDistance < Constants.InfluenceMapMaxDistance) ) { // Checks if it's time to start the next pass if (currentQueue.Count == 0) { currentQueue = nextQueue; nextQueue = new Queue <Coords>(); currentDistance++; continue; } Coords currentCoords = currentQueue.Peek(); Tile currentTile = CurrentMap.GetTile(currentCoords); // Analyzes the neighbors of the current Tile for possible additions to nextQueue for (byte i = 0; i < 6; i++) { Direction currentDir = (Direction)i; if (currentTile.AllowedMovesCheckInDirection(currentDir)) { Coords toCheck = StaticMathFunctions.CoordsNeighboringInDirection(currentCoords, currentDir); if (!takenCareOf[toCheck.X][toCheck.Y]) { nextQueue.Enqueue(toCheck); takenCareOf[toCheck.X][toCheck.Y] = true; } } } float newVal = _f(currentDistance); // Check to avert infnite / excessively deep loop if (newVal > _lowTreshold) { this.SetMapValue(currentCoords, newVal); } currentQueue.Dequeue(); } }
/// <summary> /// Checks if an agent sees the tile at coordinates 'c'. /// </summary> public bool VisibilityCheck(Coords c, Unit critter) { //Coords c = new Coords(CoordsType.Tile, critter.PositionDouble); return(_visibilityTrackers[c.X, c.Y].ContainsKey(critter.UniqueID)); }
/// <summary> /// Returns true if the agent standing at c1 can see c2. /// If diagonal strictness if flagged, returns false when the ray passes through /// an intersection, one of the lots on which is impassable. /// </summary> public bool RayTracerVisibilityCheckTile(Coords c1, Coords c2, bool diagonalStrictness, UInt16 sightRange) { // CODE REPETITION! CONSIDER REVISING. // NOTES: // -This is not done for hexes, it's for squares // -I rasterize the grid by another factor of 4 so I can shift the y-odd rows one to the right. This leads to twice // as many checks, half unnecessary. // This is only for tiles. if (c1.Type != CoordsType.Tile || c2.Type != CoordsType.Tile) { throw new Exception("Non-Tile coords passed for RayTracerVisibilityCheckTile."); //return false; } Int32 x0 = c1.X * 2 + c1.Y % 2; Int32 y0 = c1.Y * 2; Int32 x1 = c2.X * 2 + c2.Y % 2; Int32 y1 = c2.Y * 2; if (x0 < x1) { ++x0; } else if (x0 > x1) { ++x1; } if (y0 < y1) { ++y0; } else { ++y1; } int dx = Math.Abs(x1 - x0); int dy = Math.Abs(y1 - y0); int x = x0; int y = y0; int n = 1 + dx + dy; int x_inc = (x1 > x0) ? 1 : -1; int y_inc = (y1 > y0) ? 1 : -1; int error = dx - dy; dx *= 2; dy *= 2; //n *= 2; //n -= 1; //x *= 2; //y *= 2; float accrued = 0; for (; n > 0; --n) { Coords currentCoords = new Coords(CoordsType.Tile, (x - (y / 2) % 2) / 2, (y) / 2); if (y < 0) { continue; } accrued += _visibilityMap[currentCoords.X, currentCoords.Y]; // We ignore accrued visibility for now. Can add it later. if (((this._visibilityMap[currentCoords.X, currentCoords.Y] == 0) || accrued > sightRange) && (currentCoords != c2) ) { return(false); } if (!diagonalStrictness) { if (error > 0) { x += x_inc; error -= dy; } else if (error < 0) { y += y_inc; error += dx; } else { x += x_inc; error -= dy; y += y_inc; error += dx; --n; } } else { if (error > 0) { x += x_inc; error -= dy; } else { y += y_inc; error += dx; } } } return(true); }
public bool CheckInBounds(Coords c) { return((c.X >= 0) && (c.Y >= 0) && (c.X < this._xMax) && (c.Y < this._yMax)); }
/* * /// <summary> * /// Performs a terrain passability check betwee two points by doing pixel validity checks at interval delta. * /// </summary> * public List<Creature> RayTracerPassabilityCheckRough(Creature client, Vector v1, Vector v2, double delta) * { * Vector difference = v2 - v1; * Vector deltaV = difference; * deltaV.ScaleToLength(delta); * * Vector currentPosition = v1; * * for (int i = 0; i < difference.Length() / deltaV.Length(); ++i) * { * Coords pixel = new Coords(CoordsType.Pixel, currentPosition); * List<Creature> collision = _myCollider.CreatureClippingCheck(client, pixel, false); * if (collision == null || collision.Count > 0) * { * return collision; * } * currentPosition += deltaV; * } * * return new List<Creature>(); * } */ /* * /// <summary> * /// Returns the Bresenham line between p0 and p1; Borrowed the code * /// from some dude whose name I don't have, who in turn borrowed from Wikipedia. * /// </summary> * private List<Coords> BresenhamLine(Coords p0, Coords p1) * { * List<Coords> returnList = new List<Coords>(); * * Boolean steep = Math.Abs(p1.Y - p0.Y) > Math.Abs(p1.X - p0.X); * * if (steep == true) * { * Coords tmpPoint = new Coords(CoordsType.Tile, p0.X, p0.Y); * p0 = new Coords(CoordsType.Tile, tmpPoint.Y, tmpPoint.X); * * tmpPoint = p1; * p1 = new Coords(CoordsType.Tile, tmpPoint.Y, tmpPoint.X); * } * * Int32 deltaX = Math.Abs(p1.X - p0.X); * Int32 deltaY = Math.Abs(p1.Y - p0.Y); * Int32 error = 0; * Int32 deltaError = deltaY; * Int32 yStep = 0; * Int32 xStep = 0; * Int32 y = p0.Y; * Int32 x = p0.X; * * if (p0.Y < p1.Y) * { * yStep = 1; * } * else * { * yStep = -1; * } * * if (p0.X < p1.X) * { * xStep = 1; * } * else * { * xStep = -1; * } * * Int32 tmpX = 0; * Int32 tmpY = 0; * * while (x != p1.X) * { * * x += xStep; * error += deltaError; * * //if the error exceeds the X delta then * //move one along on the Y axis * if ((2 * error) > deltaX) * { * y += yStep; * error -= deltaX; * } * * //flip the coords if they're steep * if (steep) * { * tmpX = y; * tmpY = x; * } * else * { * tmpX = x; * tmpY = y; * } * * //check the point generated is legal * //and if it is add it to the list * if (_myCollider.CheckInBounds(new Coords(CoordsType.Tile, tmpX, tmpY)) == true) * { * returnList.Add(new Coords(CoordsType.Tile, tmpX, tmpY)); * } * else * { //a bad point has been found, so return the list thus far * return returnList; * } * * } * * return returnList; * } */ /// <summary> /// Checks if the Bresenham line between p0 and p1 goes only through visible tiles /// !!! Code repetition, should redo. /// </summary> public bool BresenhamLineCheckVisible(Coords p0, Coords p1) { if (p0.Equals(p1)) { return(true); } Boolean steep = Math.Abs(p1.Y - p0.Y) > Math.Abs(p1.X - p0.X); // fix this stupidity Coords p0original = new Coords(CoordsType.Tile, p0.X, p0.Y); Coords p1original = new Coords(CoordsType.Tile, p1.X, p1.Y); if (steep == true) { Coords tmpPoint = new Coords(CoordsType.Tile, p0.X, p0.Y); p0 = new Coords(CoordsType.Tile, tmpPoint.Y, tmpPoint.X); tmpPoint = p1; p1 = new Coords(CoordsType.Tile, tmpPoint.Y, tmpPoint.X); } Int32 deltaX = Math.Abs(p1.X - p0.X); Int32 deltaY = Math.Abs(p1.Y - p0.Y); Int32 error = 0; Int32 deltaError = deltaY; Int32 yStep = 0; Int32 xStep = 0; Int32 y = p0.Y; Int32 x = p0.X; if (p0.Y < p1.Y) { yStep = 1; } else { yStep = -1; } if (p0.X < p1.X) { xStep = 1; } else { xStep = -1; } Int32 tmpX = 0; Int32 tmpY = 0; float visibilityTotal = 1f; while (x != p1.X) { x += xStep; error += deltaError; //if the error exceeds the X delta then //move one along on the Y axis if ((2 * error) > deltaX) { y += yStep; error -= deltaX; } //flip the coords if they're steep if (steep) { tmpX = y; tmpY = x; } else { tmpX = x; tmpY = y; } // check the point generated is legal // using passability check. creatures will leave shadows. should write a visibility // check later Coords currentCoords = new Coords(CoordsType.Tile, tmpX, tmpY); // for this to look good you must make sure it takes account of the eucledean distances over which the coeffcients hold // otherwise you get square FOVs. visibilityTotal *= this._visibilityMap[currentCoords.X, currentCoords.Y]; if ( (visibilityTotal < Constants.VisibilityTreshold) & (!(currentCoords.Equals(p0original) | currentCoords.Equals(p1original))) ) { return(false); } } return(true); }
public bool RayTracerVisibilityCheckPixel(Vector c1, Vector c2) { double x0 = c1.X; double y0 = c1.Y; double x1 = c2.X; double y1 = c2.Y; double dx = Math.Abs(x1 - x0); double dy = Math.Abs(y1 - y0); int x = (int)(Math.Floor(x0)); int y = (int)(Math.Floor(y0)); int n = 1; int x_inc, y_inc; double error; if (dx == 0) { x_inc = 0; error = Double.PositiveInfinity; } else if (x1 > x0) { x_inc = 1; n += (int)(Math.Floor(x1)) - x; error = (Math.Floor(x0) + 1 - x0) * dy; } else { x_inc = -1; n += x - (int)(Math.Floor(x1)); error = (x0 - Math.Floor(x0)) * dy; } if (dy == 0) { y_inc = 0; error -= Double.PositiveInfinity; } else if (y1 > y0) { y_inc = 1; n += (int)(Math.Floor(y1)) - y; error -= (Math.Floor(y0) + 1 - y0) * dx; } else { y_inc = -1; n += y - (int)(Math.Floor(y1)); error -= (y0 - Math.Floor(y0)) * dx; } Coords c2Tile = new Coords(CoordsType.Tile, c2); for (; n > 0; --n) { Coords currentCoords = new Coords(CoordsType.Tile, x, y); // We ignore accrued visibility for now. Can add it later. if ((this._visibilityMap[currentCoords.X, currentCoords.Y] == 0) && (currentCoords != c2Tile)) { return(false); } if (error > 0) { y += y_inc; error -= dx; } else { x += x_inc; error += dy; } } return(true); }
public bool TileIsPassable(Coords c) { return(Constants.APMoveCostsStandard[(sbyte)GetTile(c).MyTerrainType] > 0); }
public void SetTile(Coords coords, Tile newValue) { _tiles[coords.X, coords.Y] = newValue; }