public void Setup(Map m, PathFinder pathing) { pFinder = pathing; map = m; }
public void computeAllPaths(Map map) { Point currentPoint = Point.Empty, previousPoint = Point.Empty, firstPoint = Point.Empty; MapSquare square, firstSquare, previousSquare, currentSquare; MapSquare square, firstSquare, previousSquare, currentSquare, perpSquare1, perpSquare2; double pathScore, calculatedSpeed; for (int i = 0; i < map.Width; i++) { //Starting a new row, reset the score pathScore = 1; calculatedSpeed = 1; for (int j = 0; j < map.Height; j++) { firstPoint = new Point(i, j); firstSquare = map.SquareOrDefault(firstPoint); previousPoint = new Point(i, j - 1); previousSquare = map.SquareOrDefault(previousPoint); //Stop on lights, stop signs and after grass breaks if (firstSquare == null || !firstSquare.IsDriveable || (previousSquare.IsDriveable && (!firstSquare.Signal || firstSquare.StopSigns == MapSquare.STOP_SIGNS.NONE))) { continue; } for (int k = j + 1; k < map.Height; k++) { perpSquare1 = map.SquareOrDefault(new Point(i + 1, k)); perpSquare2 = map.SquareOrDefault(new Point(Math.Max(0, i - 1), k)); previousPoint = new Point(i, k - 1); previousSquare = map.SquareOrDefault(previousPoint); currentPoint = new Point(i, k); currentSquare = map.SquareOrDefault(currentPoint); if (!currentSquare.IsDriveable) { //Reached the end of the path paths.Add(new Path(firstPoint, previousPoint, pathScore / (previousPoint.Y - firstPoint.Y + previousPoint.X - firstPoint.X + 1))); break; } calculatedSpeed = Math.Max(MIN_SPEED, Math.Min(calculatedSpeed + 0.1, MAX_SPEED)); pathScore += calculatedSpeed; if (currentSquare.Signal || currentSquare.StopSigns != MapSquare.STOP_SIGNS.NONE || (perpSquare1 != null && perpSquare1.IsDriveable) || (perpSquare2 != null && perpSquare2.IsDriveable)) { paths.Add(new Path(firstPoint, currentPoint, pathScore / (currentPoint.Y - firstPoint.Y + currentPoint.X - firstPoint.X))); if (currentSquare.StopSigns == MapSquare.STOP_SIGNS.STOP_NORTH || currentSquare.StopSigns == MapSquare.STOP_SIGNS.STOP_SOUTH) { //Stop sign is the end of a continuous drivable segment break; } } } } } for (int i = 0; i < map.Height; i++) { //Starting a new row, reset the score pathScore = 1; calculatedSpeed = 1; for (int j = 0; j < map.Width; j++) { firstPoint = new Point(j, i); firstSquare = map.SquareOrDefault(firstPoint); previousPoint = new Point(j - 1, i); previousSquare = map.SquareOrDefault(previousPoint); //Stop on lights, stop signs and after grass breaks if (firstSquare == null || !firstSquare.IsDriveable || (previousSquare.IsDriveable && (!firstSquare.Signal || firstSquare.StopSigns == MapSquare.STOP_SIGNS.NONE))) { continue; } for (int k = j + 1; k < map.Width; k++) { perpSquare1 = map.SquareOrDefault(new Point(k, i + 1)); perpSquare2 = map.SquareOrDefault(new Point(k, Math.Max(0, i - 1))); previousPoint = new Point(k - 1, i); previousSquare = map.SquareOrDefault(previousPoint); currentPoint = new Point(k, i); currentSquare = map.SquareOrDefault(currentPoint); if (!currentSquare.IsDriveable) { //Reached the end of the path paths.Add(new Path(firstPoint, previousPoint, pathScore / (previousPoint.Y - firstPoint.Y + previousPoint.X - firstPoint.X + 1))); break; } calculatedSpeed = Math.Max(MIN_SPEED, Math.Min(calculatedSpeed + 0.1, MAX_SPEED)); pathScore += calculatedSpeed; if (currentSquare.Signal || currentSquare.StopSigns != MapSquare.STOP_SIGNS.NONE || (perpSquare1 != null && perpSquare1.IsDriveable) || (perpSquare2 != null && perpSquare2.IsDriveable)) { paths.Add(new Path(firstPoint, currentPoint, pathScore / (currentPoint.Y - firstPoint.Y + currentPoint.X - firstPoint.X))); if (currentSquare.StopSigns == MapSquare.STOP_SIGNS.STOP_EAST || currentSquare.StopSigns == MapSquare.STOP_SIGNS.STOP_WEST) { //Stop sign is the end of a continuous drivable segment break; } } } } } /* ORIGINAL IMPLEMENTATION currentPoint = new Point(i, j); square = map.SquareOrDefault(currentPoint); //Not a road tile or not driveable or IS a stop sign or IS a light //TODO: Fix light logic if (square == null || !square.IsDriveable || (firstPoint != Point.Empty && (square.StopSigns != MapSquare.STOP_SIGNS.NONE || square.Signal))) { if (square.StopSigns != MapSquare.STOP_SIGNS.NONE || square.Signal) { //Hacky fix to intersection overlap previousPoint = currentPoint; MapSquare nextSquare = map.SquareOrDefault(new Point(i, j + 1)); if (nextSquare != null && nextSquare.IsDriveable) { j--; } } //We have a path of at least length 1 //TODO: Fix when we have a point at 0,0 if (firstPoint != Point.Empty) { paths.Add(new Path(firstPoint, previousPoint, pathScore / (previousPoint.Y - firstPoint.Y))); } calculatedSpeed = 0; pathScore = 0; firstPoint = Point.Empty; previousPoint = Point.Empty; continue; } //We have a road tile! //Starting a new segment if (calculatedSpeed == 0) { if (map.SquareOrDefault(previousPoint).IsDriveable) { firstPoint = previousPoint; calculatedSpeed = 1; pathScore = 1; } else { firstPoint = currentPoint; } } calculatedSpeed = Math.Max(MIN_SPEED, Math.Min(calculatedSpeed + 0.1, MAX_SPEED)); pathScore += calculatedSpeed; previousPoint = currentPoint; } } for (int i = 0; i < map.Height; i++) { //Starting a new row, reset the score pathScore = 0; calculatedSpeed = 0; for (int j = 0; j < map.Width; j++) { currentPoint = new Point(j, i); square = map.SquareOrDefault(currentPoint); //Not a road tile or not driveable or IS a stop sign or IS a light //TODO: Fix light logic if (square == null || !square.IsDriveable || (firstPoint != Point.Empty && (square.StopSigns != MapSquare.STOP_SIGNS.NONE || square.Signal))) { if (square.StopSigns != MapSquare.STOP_SIGNS.NONE || square.Signal) { //Hacky fix to intersection overlap previousPoint = currentPoint; MapSquare nextSquare = map.SquareOrDefault(new Point(j+1, i)); if (nextSquare != null && nextSquare.IsDriveable) { j--; } } //We have a path of at least length 1 //TODO: Fix when we have a point at 0,0 if (firstPoint != Point.Empty) { paths.Add(new Path(firstPoint, previousPoint, pathScore / (previousPoint.X - firstPoint.X))); } calculatedSpeed = 0; firstPoint = Point.Empty; previousPoint = currentPoint; continue; } //We have a road tile! //Starting a new segment if (calculatedSpeed == 0) { if (map.SquareOrDefault(previousPoint).IsDriveable) { firstPoint = previousPoint; calculatedSpeed = 1; pathScore = 1; } else { firstPoint = currentPoint; } } calculatedSpeed = Math.Max(MIN_SPEED, Math.Min(calculatedSpeed + 0.1, MAX_SPEED)); pathScore += calculatedSpeed; previousPoint = currentPoint; } } */ //Clean up the paths to remove length 1 paths that are in other paths paths = (from a in paths orderby a.start.X, a.start.Y select a).ToList(); IQueryable<Path> path1 = (from a in paths where (a.end.X == a.start.X && a.end.Y == a.start.Y) orderby a.start.X, a.end.X, a.start.Y, a.end.Y select a).AsQueryable(); IQueryable<Path> pathOver1 = (from a in paths where (a.end.X - a.start.X) + (a.end.Y - a.start.Y) > 0 orderby a.start.X, a.end.X, a.start.Y, a.end.Y select a).AsQueryable(); foreach (Path p in path1){ foreach (Path p1 in pathOver1) { if (p.start.X >= p1.start.X && p.start.X <= p1.end.X && p.start.Y >= p1.start.Y && p.start.Y <= p1.end.Y) { paths.Remove(p); } } } }
/// <summary> /// Calculate a path from start to end. No comments about how this is the world's worst A* implementation. It is purposely /// simplistic to leave the teams the opportunity to improve greatly upon this. (I was yelled at last year for making the /// sample A.I.'s too good.) /// </summary> /// <param name="map">The game map.</param> /// <param name="start">The tile units of the start point (inclusive).</param> /// <param name="end">The tile units of the end point (inclusive).</param> /// <returns>The path from start to end.</returns> public static List<Point> CalculatePath(Map map, Point start, Point end) { // should never happen but just to be sure if (start == end) return new List<Point> { start }; // nodes are points we have walked to Dictionary<Point, TrailPoint> nodes = new Dictionary<Point, TrailPoint>(); // points we have in a TrailPoint, but not yet evaluated. List<TrailPoint> notEvaluated = new List<TrailPoint>(); TrailPoint tpOn = new TrailPoint(start, end, 0); while (true) { nodes.Add(tpOn.MapTile, tpOn); // get the neighbors TrailPoint tpClosest = null; foreach (Point ptOffset in offsets) { Point pt = new Point(tpOn.MapTile.X + ptOffset.X, tpOn.MapTile.Y + ptOffset.Y); MapSquare square = map.SquareOrDefault(pt); // off the map or not a road/bus stop if ((square == null) || (!square.IsDriveable)) continue; // already evaluated - add it in if (nodes.ContainsKey(pt)) { TrailPoint tpAlreadyEvaluated = nodes[pt]; tpAlreadyEvaluated.Cost = Math.Min(tpAlreadyEvaluated.Cost, tpOn.Cost + 1); tpOn.Neighbors.Add(tpAlreadyEvaluated); continue; } // add this one in TrailPoint tpNeighbor = new TrailPoint(pt, end, tpOn.Cost + 1); tpOn.Neighbors.Add(tpNeighbor); // may already be in notEvaluated. If so remove it as this is a more recent cost estimate int indTp = notEvaluated.FindIndex(tp => tp.MapTile == tpNeighbor.MapTile); if (indTp != -1) notEvaluated.RemoveAt(indTp); // we only assign to tpClosest if it is closer to the destination. If it's further away, then we // use notEvaluated below to find the one closest to the dest that we have not walked yet. if (tpClosest == null) { if (tpNeighbor.Distance < tpOn.Distance) // new neighbor is closer - work from this next. tpClosest = tpNeighbor; else // this is further away - put in the list to try if a better route is not found notEvaluated.Add(tpNeighbor); } else if (tpClosest.Distance <= tpNeighbor.Distance) // this is further away - put in the list to try if a better route is not found notEvaluated.Add(tpNeighbor); else { // this is closer than tpOn and another neighbor - use it next. notEvaluated.Add(tpClosest); tpClosest = tpNeighbor; } } // re-calc based on neighbors tpOn.RecalculateDistance(ptOffMap, map.Width); // if no closest, then get from notEvaluated. This is where it guarantees that we are getting the shortest // route - we go in here if the above did not move a step closer. This may not either as the best choice // may be the neighbor we didn't go with above - but we drop into this to find the closest based on what we know. if (tpClosest == null) { if (notEvaluated.Count == 0) { Trap.trap(); break; } // We need the closest one as that's how we find the shortest path. tpClosest = notEvaluated[0]; int index = 0; for (int ind = 1; ind < notEvaluated.Count; ind++) { TrailPoint tpNotEval = notEvaluated[ind]; if (tpNotEval.Distance >= tpClosest.Distance) continue; tpClosest = tpNotEval; index = ind; } notEvaluated.RemoveAt(index); } // if we're at end - we're done! if (tpClosest.MapTile == end) { tpClosest.Neighbors.Add(tpOn); nodes.Add(tpClosest.MapTile, tpClosest); break; } // try this one tpOn = tpClosest; } // Create the return path - from end back to beginning. List<Point> path = new List<Point>(); tpOn = nodes[end]; path.Add(tpOn.MapTile); while (tpOn.MapTile != start) { List<TrailPoint> neighbors = tpOn.Neighbors; int cost = tpOn.Cost; tpOn = tpOn.Neighbors[0]; for (int ind = 1; ind < neighbors.Count; ind++) if (neighbors[ind].Cost < tpOn.Cost) tpOn = neighbors[ind]; // we didn't get to the start. if (tpOn.Cost >= cost) { Trap.trap(); return path; } path.Insert(0, tpOn.MapTile); } return path; }
public void IncomingMessage(string message) { try { DateTime startTime = DateTime.Now; // get the xml - we assume we always get a valid message from the server. XDocument xml = XDocument.Parse(message); switch (xml.Root.Name.LocalName) { case "setup": Console.Out.WriteLine("Received setup message"); if (log.IsInfoEnabled) log.Info("Received setup message"); List<Player> players = Player.FromXml(xml.Root.Element("players")); List<Company> companies = Company.FromXml(xml.Root.Element("companies")); List<Passenger> passengers = Passenger.FromXml(xml.Root.Element("passengers"), companies); Map map = new Map(xml.Root.Element("map"), companies); myGuid = xml.Root.Attribute("my-guid").Value; Player me2 = players.Find(plyr => plyr.Guid == myGuid); brain.Setup(map, me2, players, companies, passengers, PlayerOrdersEvent); break; case "status": // may be here because re-started and got this message before the re-send of setup. if (string.IsNullOrEmpty(myGuid)) { Trap.trap(); return; } PlayerAIBase.STATUS status = (PlayerAIBase.STATUS)Enum.Parse(typeof(PlayerAIBase.STATUS), xml.Root.Attribute("status").Value); XAttribute attr = xml.Root.Attribute("player-guid"); string guid = attr != null ? attr.Value : myGuid; lock (this) { if (signal > 0) { // bad news - we're throwing this message away. Trap.trap(); return; } signal++; } Player.UpdateFromXml(brain.Players, brain.Passengers, xml.Root.Element("players")); Passenger.UpdateFromXml(brain.Passengers, brain.Companies, xml.Root.Element("passengers")); // update my path & pick-up. Player plyrStatus = brain.Players.Find(plyr => plyr.Guid == guid); XElement elem = xml.Root.Element("path"); if (elem != null) { string [] path = elem.Value.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries); plyrStatus.Limo.Path.Clear(); foreach (string stepOn in path) { int pos = stepOn.IndexOf(','); plyrStatus.Limo.Path.Add(new Point(Convert.ToInt32(stepOn.Substring(0, pos)), Convert.ToInt32(stepOn.Substring(0, pos)))); } } elem = xml.Root.Element("pick-up"); if (elem != null) { string [] names = elem.Value.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries); plyrStatus.PickUp.Clear(); foreach (Passenger psngrOn in names.Select(pickupOn => brain.Passengers.Find(ps => ps.Name == pickupOn))) plyrStatus.PickUp.Add(psngrOn); } // pass in to generate new orders brain.GameStatus(status, plyrStatus, brain.Players, brain.Passengers); lock (this) { signal--; } break; case "exit": Console.Out.WriteLine("Received exit message"); if (log.IsInfoEnabled) log.Info("Received exit message"); Environment.Exit(0); break; default: Trap.trap(); string msg = string.Format("ERROR: bad message (XML) from server - root node {0}", xml.Root.Name.LocalName); log.Warn(msg); Trace.WriteLine(msg); break; } TimeSpan turnTime = DateTime.Now.Subtract(startTime); if (turnTime.TotalMilliseconds > 800) Console.Out.WriteLine("WARNING - turn took {0} seconds", turnTime.TotalMilliseconds/1000); } catch (Exception ex) { Console.Out.WriteLine(string.Format("Error on incoming message. Exception: {0}", ex)); log.Error("Error on incoming message.", ex); } }
/// <summary> /// Called at the start of the game. /// </summary> /// <param name="map">The game map.</param> /// <param name="me">You. This is also in the players list.</param> /// <param name="players">All players (including you).</param> /// <param name="companies">The companies on the map.</param> /// <param name="passengers">The passengers that need a lift.</param> /// <param name="ordersEvent">Method to call to send orders to the server.</param> public void Setup(Map map, Player me, List<Player> players, List<Company> companies, List<Passenger> passengers, PlayerAIBase.PlayerOrdersEvent ordersEvent) { try { pFinder = new PathFinder(); GameMap = map; GameInfo = new GameStatusInfo(); GameInfo.Setup(map, pFinder); Players = players; Me = me; Companies = companies; Passengers = passengers; sendOrders = ordersEvent; List<Passenger> pickup = AllPickups(me, passengers); pFinder.computeAllPaths(map); pFinder.generateAdjacencyMatrix(); // get the path from where we are to the dest. List<Point> path = CalculatePathPlus1(me, pickup[0].Lobby.BusStop); sendOrders("ready", path, pickup); } catch (Exception ex) { log.Fatal(string.Format("Setup({0}, ...", me == null ? "{null}" : me.Name), ex); } }