//--- Methods --- public async Task StartAsync(int botCount) { // reset game state GameSession.LastStatusUpdate = _provider.UtcNow; GameSession.CurrentGameTurn = 0; GameSession.Missiles.Clear(); GameSession.Messages.Clear(); GameSession.Bots.Clear(); for (var i = 0; i < botCount; ++i) { GameSession.Bots.Add(BotBuild.GetDefaultBotBuild(GameSession.Id, i)); } // get configuration for all bots var messages = (await Task.WhenAll(GameSession.Bots.Select(bot => InitializeBotAsync(bot)))).ToList(); foreach (var message in messages) { AddMessage(message); } // place bots on playfield var marginWidth = GameSession.BoardWidth * 0.1f; var marginHeight = GameSession.BoardHeight * 0.1f; var attempts = 0; again: if (attempts >= 100) { throw new ApplicationException($"unable to place all bots with minimum separation of {GameSession.MinBotStartDistance:N2}"); } // assign random locations to all bots foreach (var bot in GameSession.Bots.Where(bot => bot.Status == BotStatus.Alive)) { bot.X = marginWidth + _provider.NextRandomFloat() * (GameSession.BoardWidth - 2.0f * marginWidth); bot.Y = marginHeight + _provider.NextRandomFloat() * (GameSession.BoardHeight - 2.0f * marginHeight); } // verify that none of the bots are too close to each other for (var i = 0; i < GameSession.Bots.Count; ++i) { for (var j = i + 1; j < GameSession.Bots.Count; ++j) { if ((GameSession.Bots[i].Status == BotStatus.Alive) && (GameSession.Bots[j].Status == BotStatus.Alive)) { var distance = GameMath.Distance(GameSession.Bots[i].X, GameSession.Bots[i].Y, GameSession.Bots[j].X, GameSession.Bots[j].Y); if (distance < GameSession.MinBotStartDistance) { ++attempts; goto again; } } } } }
private async Task <string> InitializeBotAsync(BotInfo bot) { var config = await _provider.GetBotBuild(bot); bot.Name = config?.Name ?? $"#{bot.Index}"; if (config is null) { // missing config information, consider bot dead bot.Status = BotStatus.Dead; bot.TimeOfDeathGameTurn = 0; return($"{bot.Name} (R{bot.Index}) was disqualified due to failure to initialize"); } // read bot configuration var success = true; var buildPoints = 0; var buildDescription = new StringBuilder(); // read radar configuration buildPoints += (int)config.Radar; buildDescription.Append($"{config.Radar} Radar"); success &= BotBuild.TrySetRadar(config.Radar, bot); // read engine configuration buildPoints += (int)config.Engine; buildDescription.Append($", {config.Engine} Engine"); success &= BotBuild.TrySetEngine(config.Engine, bot); // read armor configuration buildPoints += (int)config.Armor; buildDescription.Append($", {config.Armor} Armor"); success &= BotBuild.TrySetArmor(config.Armor, bot); // read missile configuration buildPoints += (int)config.Missile; buildDescription.Append($", {config.Missile} Missile"); success &= BotBuild.TrySetMissile(config.Missile, bot); // check if bot respected the max build points if (buildPoints > GameSession.MaxBuildPoints) { success = false; } // check if bot is disqualified due to a bad build if (!success) { bot.Status = BotStatus.Dead; bot.TimeOfDeathGameTurn = GameSession.CurrentGameTurn; return($"{bot.Name} (R{bot.Index}) was disqualified due to bad configuration ({buildDescription}: {buildPoints} points)"); } bot.InternalState = config.InternalStartState; bot.LastStatusUpdate = _provider.UtcNow; return($"{bot.Name} (R{bot.Index}) has joined the battle ({buildDescription}: {buildPoints} points)"); }