/// <summary> /// Call the AI with a status message. /// </summary> /// <param name="xmlMessage">Can be null. The remote AI XML for a message without the status or my player values set.</param> /// <param name="about">The player this status is about. Will be set to receiving user for status not specific to a player.</param> /// <param name="status">The status for this message.</param> /// <param name="players">All the players.</param> /// <param name="passengers">All the passengers.</param> public void GameStatus(XDocument xmlMessage, Player about, PlayerAIBase.STATUS status, List<Player> players, List<Passenger> passengers) { // only care if for me, and an action (ie not status) if ((status == PlayerAIBase.STATUS.UPDATE) || ((about != null) && (about.Guid != aiWorker.MyPlayerGuid))) return; Player player = players.Find(pl => pl.Guid == aiWorker.MyPlayerGuid); Trap.trap(about == null); if (about == null) about = player; aiWorker.AddMessage(status, about, AllPickups(player, passengers)); }
public void AddMessage(PlayerAIBase.STATUS status, Player about, List<Passenger> pickup) { StatusMessage msg = new StatusMessage(status, about, pickup); lock (messages) messages.Enqueue(msg); EventThread.Set(); }
public StatusMessage(PlayerAIBase.STATUS status, Player player, List<Passenger> pickup) { Status = status; LimoTileLocation = player.Limo.Location.TilePosition; PassengerDestBusStop = player.Passenger == null ? Point.Empty : player.Passenger.Destination.BusStop; Pickup = pickup; }
/// <summary> /// Called when the game starts, providing all info. /// </summary> /// <param name="map">The game map.</param> /// <param name="me">The player being setup..</param> /// <param name="players">All the players.</param> /// <param name="companies">All companies on the board.</param> /// <param name="passengers">All the passengers.</param> /// <param name="ordersEvent">Callback to pass orders to the engine.</param> public void Setup(GameMap map, Player me, List<Player> players, List<Company> companies, List<Passenger> passengers, PlayerAIBase.PlayerOrdersEvent ordersEvent) { // start the thread aiWorker = new AiWorker(map, me.Guid, companies, ordersEvent); aiThread = new Thread(aiWorker.MainLoop) {Name = me.Name.Replace(' ', '_'), IsBackground = true}; aiThread.Start(); // cause it to pick a passenger to pick up and calculate the path to that company. aiWorker.AddMessage(PlayerAIBase.STATUS.NO_PATH, me, AllPickups(me, passengers)); }
public AiWorker(GameMap gameMap, string myPlayerGuid, List<Company> companies, PlayerAIBase.PlayerOrdersEvent sendOrders) { this.gameMap = gameMap; MyPlayerGuid = myPlayerGuid; Companies = companies; this.sendOrders = sendOrders; messages = new Queue<StatusMessage>(); // get it ready to handle events. EventThread = new AutoResetEvent(false); ExitThread = false; }
/// <summary> /// Call the AI with a status message. /// </summary> /// <param name="xmlMessage">Can be null. The remote AI XML for a message without the status or my player values set.</param> /// <param name="about">The player this is about.</param> /// <param name="status">The status for this message.</param> /// <param name="players">All the players.</param> /// <param name="passengers">All the passengers.</param> public void GameStatus(XDocument xmlMessage, Player about, PlayerAIBase.STATUS status, List<Player> players, List<Passenger> passengers) { ai.GameStatus(xmlMessage, about, status, players, passengers); }
public void Setup(GameMap map, Player me, List<Player> players, List<Company> companies, List<Passenger> passengers, PlayerAIBase.PlayerOrdersEvent ordersEvent) { ai.Setup(map, me, players, companies, passengers, ordersEvent); }
/// <summary> /// Called when the game starts, providing all info. /// </summary> public void Setup(GameMap map, Player me, List<Player> players, List<Company> companies, List<Passenger> passengers, PlayerAIBase.PlayerOrdersEvent ordersEvent) { sendOrders = ordersEvent; XDocument doc = new XDocument(); XElement elemRoot = new XElement("setup", new XAttribute("game-start", true), new XAttribute("my-guid", me.Guid)); doc.Add(elemRoot); // the map XElement elemMap = new XElement("map", new XAttribute("width", map.Width), new XAttribute("height", map.Height), new XAttribute("units-tile", TileMovement.UNITS_PER_TILE)); elemRoot.Add(elemMap); for (int x = 0; x < map.Width; x++ ) for (int y = 0; y < map.Height; y++) { MapSquare square = map.Squares[x][y]; XElement elemRow = new XElement("tile", new XAttribute("x", x), new XAttribute("y", y), new XAttribute("type", square.Tile.Type)); if (square.Tile.IsDriveable) { elemRow.Add(new XAttribute("direction", square.Tile.Direction)); if (square.StopSigns != MapSquare.STOP_SIGNS.NONE) elemRow.Add(new XAttribute("stop-sign", square.StopSigns)); if (square.SignalDirection != MapSquare.SIGNAL_DIRECTION.NONE) elemRow.Add(new XAttribute("signal", true)); } elemMap.Add(elemRow); } // all players (including me) XElement elemPlayers = new XElement("players"); elemRoot.Add(elemPlayers); foreach (Player plyrOn in players) elemPlayers.Add(new XElement("player", new XAttribute("guid", plyrOn.Guid), new XAttribute("name", plyrOn.Name), new XAttribute("limo-x", plyrOn.Limo.Location.TilePosition.X), new XAttribute("limo-y", plyrOn.Limo.Location.TilePosition.Y), new XAttribute("limo-angle", plyrOn.Limo.Location.Angle))); // all companies XElement elemCompanies = new XElement("companies"); elemRoot.Add(elemCompanies); foreach (Company cmpnyOn in companies) elemCompanies.Add(new XElement("company", new XAttribute("name", cmpnyOn.Name), new XAttribute("bus-stop-x", cmpnyOn.BusStop.X), new XAttribute("bus-stop-y", cmpnyOn.BusStop.Y))); // all passengers XElement elemPassengers = new XElement("passengers"); elemRoot.Add(elemPassengers); foreach (Passenger psngrOn in passengers) { XElement elemPassenger = new XElement("passenger", new XAttribute("name", psngrOn.Name), new XAttribute("points-delivered", psngrOn.PointsDelivered)); // if due to a re-start, these can be null if (psngrOn.Lobby != null) elemPassenger.Add(new XAttribute("lobby", psngrOn.Lobby.Name)); if (psngrOn.Destination != null) elemPassenger.Add(new XAttribute("destination", psngrOn.Destination.Name)); foreach (Company route in psngrOn.Companies) elemPassenger.Add(new XElement("route", route.Name)); foreach (Passenger enemy in psngrOn.Enemies) elemPassenger.Add(new XElement("enemy", enemy.Name)); elemPassengers.Add(elemPassenger); } framework.tcpServer.SendMessage(TcpGuid, doc.ToString()); }
/// <summary> /// Call the AI with a status message. /// </summary> /// <param name="xmlMessage">Can be null. The remote AI XML for a message without the status or my player values set.</param> /// <param name="about">The player this status is about. Will be set to receiving user for status not specific to a player.</param> /// <param name="status">The status for this message.</param> /// <param name="players">All the players.</param> /// <param name="passengers">All the passengers.</param> public void GameStatus(XDocument xmlMessage, Player about, PlayerAIBase.STATUS status, List<Player> players, List<Passenger> passengers) { if (TcpGuid == null) return; if (xmlMessage == null) xmlMessage = BuildMessageXml(players, passengers); XAttribute attr = xmlMessage.Root.Attribute("status"); if (attr == null) xmlMessage.Root.Add(new XAttribute("status", status)); else attr.Value = status.ToString(); attr = xmlMessage.Root.Attribute("player-guid"); if (attr == null) xmlMessage.Root.Add(new XAttribute("player-guid", about.Guid)); else attr.Value = about.Guid; StringBuilder buf = new StringBuilder(); Player player = framework.GameEngine.Players.Find(pl => pl.TcpGuid == TcpGuid); if (player != null) foreach (Point ptOn in player.Limo.PathTiles) buf.Append(Convert.ToString(ptOn.X) + ',' + Convert.ToString(ptOn.Y) + ';'); XElement elem = xmlMessage.Root.Element("path"); if (elem == null) { elem = new XElement("path"); xmlMessage.Root.Add(elem); elem.Value = buf.ToString(); } else elem.Value = buf.ToString(); if (player != null) { buf.Clear(); foreach (Passenger psngr in player.PickUp) buf.Append(psngr.Name + ';'); } elem = xmlMessage.Root.Element("pick-up"); if (elem == null) { elem = new XElement("pick-up"); xmlMessage.Root.Add(elem); elem.Value = buf.ToString(); } else elem.Value = buf.ToString(); framework.tcpServer.SendMessage(TcpGuid, xmlMessage.ToString()); }
private List<Passenger> MakeDecision(PlayerAIBase.STATUS status, Player plyrStatus, List<Player> players, List<Passenger> passengers) { if (Me.Limo.Passenger != null) return Me.PickUp; switch (status) { // action should always be : // make sure action is still the desired one // if not, compare the scores of top actions // if there's not a big difference, stay on current action case PlayerAIBase.STATUS.UPDATE: // default action case PlayerAIBase.STATUS.NO_PATH: // that's fine, default action case PlayerAIBase.STATUS.PASSENGER_NO_ACTION: // if we have no passenger, decide who we want. // if we have a passenger, make sure we still want them. // that's the default action case PlayerAIBase.STATUS.PASSENGER_DELIVERED: // default action case PlayerAIBase.STATUS.PASSENGER_ABANDONED: // default action case PlayerAIBase.STATUS.PASSENGER_REFUSED: // default action case PlayerAIBase.STATUS.PASSENGER_DELIVERED_AND_PICKED_UP: //default action case PlayerAIBase.STATUS.PASSENGER_PICKED_UP: // default action default: { // Go to the best-expected company. SortedDictionary<float, Company> bestBusStop = new SortedDictionary<float, Company>(); foreach (Company company in GameInfo.activeBusStops()) { // Sort passengers at this stop. SortedDictionary<float, Passenger> passengersAtStop = GetPassengerWeights(Me, GameInfo.PassengerAtLocation(company)); // TODO: Ensure descending order. // Time for us to get there float our_time = (float)pFinder.getTimeForPath(pFinder.computeFastestPath(Me.Limo.TilePosition, company.BusStop)); // Number of other players closer/same than us. Buffer time of 100sec. int other_players = GameInfo.OtherPlayersWithinTimeToLocation(our_time+100f, company.BusStop).Count; // If there's one enemy within range, the 2nd highest has to be good enough. If 3rd, they have to be high enough. Etc. int minCount = Math.Min(other_players, passengersAtStop.Count); float average = 0; // have to go in descending order for this. for (int i = passengersAtStop.Count-1; i >= passengersAtStop.Count-minCount; i--) { average += passengersAtStop.Keys.ToList()[i]; } /* for (int i = 0; i < minCount; i++) { average += passengersAtStop.Keys.ToList()[i]; }*/ average /= minCount; // Add the average value & company to result bestBusStop.Add(average, company); } // go to the best bus stop. Set the desired player_list to the top at the bus stop int numberOfResults = bestBusStop.Count; Company winningCompany = bestBusStop.Values.ToList()[numberOfResults-1]; //List<Point> path = pFinder.computeFastestPath(Me.Limo.TilePosition, winningCompany.BusStop); List<Passenger> passengerList = GetPassengerWeights(Me, GameInfo.PassengerAtLocation(winningCompany)).Values.ToList(); passengerList.Reverse(); return passengerList; } } }
/// <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); } }
/// <summary> /// Called to send an update message to this A.I. We do NOT have to send orders in response. /// </summary> /// <param name="status">The status message.</param> /// <param name="plyrStatus">The player this status is about. THIS MAY NOT BE YOU.</param> /// <param name="players">The status of all players.</param> /// <param name="passengers">The status of all passengers.</param> public void GameStatus(PlayerAIBase.STATUS status, Player plyrStatus, List<Player> players, List<Passenger> passengers) { // bugbug - Framework.cs updates the object's in this object's Players, Passengers, and Companies lists. This works fine as long // as this app is single threaded. However, if you create worker thread(s) or respond to multiple status messages simultaneously // then you need to split these out and synchronize access to the saved list objects. try { // bugbug - we return if not us because the below code is only for when we need a new path or our limo hit a bus stop. // if you want to act on other players arriving at bus stops, you need to remove this. But make sure you use Me, not // plyrStatus for the Player you are updatiing (particularly to determine what tile to start your path from). if (plyrStatus != Me) return; GameInfo.update(players, passengers); Point ptDest; List<Passenger> pickup = new List<Passenger>(); // determine where we want to go and who we want to pick up switch (status) { case PlayerAIBase.STATUS.UPDATE: return; case PlayerAIBase.STATUS.NO_PATH: case PlayerAIBase.STATUS.PASSENGER_NO_ACTION: if (plyrStatus.Limo.Passenger == null) { pickup = MakeDecision(status, plyrStatus, players, passengers); ptDest = pickup[0].Lobby.BusStop; } else ptDest = plyrStatus.Limo.Passenger.Destination.BusStop; break; case PlayerAIBase.STATUS.PASSENGER_DELIVERED: case PlayerAIBase.STATUS.PASSENGER_ABANDONED: pickup = MakeDecision(status, plyrStatus, players, passengers); ptDest = pickup[0].Lobby.BusStop; break; case PlayerAIBase.STATUS.PASSENGER_REFUSED: pickup = MakeDecision(status, plyrStatus, players, passengers); ptDest = pickup[0].Lobby.BusStop; break; case PlayerAIBase.STATUS.PASSENGER_DELIVERED_AND_PICKED_UP: case PlayerAIBase.STATUS.PASSENGER_PICKED_UP: pickup = MakeDecision(status, plyrStatus, players, passengers); ptDest = pickup[0].Lobby.BusStop; break; default: throw new ApplicationException("unknown status"); } // get the path from where we are to the dest. List<Point> path = CalculatePathPlus1(plyrStatus, ptDest); if (log.IsDebugEnabled) log.Debug(string.Format("{0}; Path:{1}-{2}, {3} steps; Pickup:{4}, {5} total", status, path.Count > 0 ? path[0].ToString() : "{n/a}", path.Count > 0 ? path[path.Count - 1].ToString() : "{n/a}", path.Count, pickup.Count == 0 ? "{none}" : pickup[0].Name, pickup.Count)); // update our saved Player to match new settings if (path.Count > 0) { Me.Limo.Path.Clear(); Me.Limo.Path.AddRange(path); } if (pickup.Count > 0) { Me.PickUp.Clear(); Me.PickUp.AddRange(pickup); } sendOrders("move", path, pickup); } catch (Exception ex) { log.Error(string.Format("GameStatus({0}, {1}, ...", status, plyrStatus == null ? "{null}" : plyrStatus.Name), ex); } }