public async Task LogAction() { // Arrange var logActionRequest = new GameActionRequest() { GameId = 1, TeamId = 1, Latitude = 45.26, Longitude = 4.63, Action = (int)Action.StartGame.GetTypeCode() }; var httpResponse = new HttpResponseMessage() { Content = new StringContent("OK") }; A.CallTo(_fakeHttpMessageHandler) .Where(x => x.Method.Name == "PostAsync") .WithReturnType <Task <HttpResponseMessage> >() .Returns(httpResponse); // Act await _target.LogAction(logActionRequest); // Assert A.CallTo(_fakeHttpMessageHandler) .Where(x => x.Method.Name == "SendAsync") .WithReturnType <Task <HttpResponseMessage> >().MustHaveHappened(); }
static async Task InsertRandomPosition(int gameId, int minTeamId, int maxTeamId, double latitude, double longitude, int interval = 15000) { var random = new Random((int)DateTime.Now.Ticks); var gameActionRequest = new GameActionRequest() { GameId = gameId, Latitude = latitude, Longitude = longitude, }; do { gameActionRequest.Action = random.Next(0, 7); gameActionRequest.TeamId = random.Next(minTeamId, maxTeamId + 1); gameActionRequest.TeamId = random.Next(minTeamId, maxTeamId + 1); gameActionRequest.Latitude += 0.001 * (random.NextDouble() - 0.5); gameActionRequest.Longitude += 0.001 * (random.NextDouble() - 0.5); try { await _actionWebService.LogAction(gameActionRequest); } catch (Exception e) { Console.WriteLine(e); } Thread.Sleep(interval); } while (true); }
public void AddGameAction_ReplyQuestion() { // Arrange var gameActionRequest = new GameActionRequest() { Action = 4, Latitude = 1.2, Longitude = 50.2, NodeId = 2, GameId = 2, TeamId = 5, AnswerId = 3 }; var questionNode = new QuestionNode() { Answers = new List <Answer>() { new Answer(), new Answer() { Correct = true }, new Answer() } }; A.CallTo(() => _nodeService.GetNode(A <int> ._)).Returns(questionNode); // Act var result = _target.AddGameAction(gameActionRequest); // Assert A.CallTo(() => _nodeService.GetAnswer(A <int> ._)).MustHaveHappened(); A.CallTo(() => _nodeService.GetNode(gameActionRequest.NodeId)).MustHaveHappened(); A.CallTo(() => _actionService.AddGameAction(A <GameAction> .That.Matches(ga => CheckGameActionWithAnswer(ga)))) .MustHaveHappened(); }
public override async Task Begin(ITurnContext turnContext) { var state = turnContext.GetConversationState <ImageHuntState>(); if (state.Status != Status.Initialized) { LogInfo <ImageHuntState>(turnContext, "Game not initialized"); await turnContext.ReplyActivity("Le chat n'a pas été initialisé, impossible de commencer maintenant!"); await turnContext.End(); return; } LogInfo <ImageHuntState>(turnContext, "Start Game"); var gameActionRequest = new GameActionRequest() { Action = (int)ImageHuntWebServiceClient.Action.StartGame, GameId = state.GameId, TeamId = state.TeamId, Latitude = state.CurrentLatitude, Longitude = state.CurrentLongitude }; state.Status = Status.Started; await _actionWebService.LogAction(gameActionRequest); await turnContext.ReplyActivity($"La chasse commence maintenant! Bonne chance!"); await turnContext.End(); }
public override async Task Begin(ITurnContext turnContext) { var state = turnContext.GetConversationState <ImageHuntState>(); if (state.Status != Status.Started) { LogInfo <ImageHuntState>(turnContext, "The game had not started!"); await turnContext.ReplyActivity("La partie n'a pas encore commencée, vous ne pouvez par l'arrêter!"); await turnContext.End(); } _logger.LogInformation($"The Hunt of GameId={state.GameId} for teamid={state.TeamId} had ended."); var gameActionRequest = new GameActionRequest() { Action = (int)ImageHuntWebServiceClient.Action.EndGame, GameId = state.GameId, TeamId = state.TeamId, Latitude = state.CurrentLatitude, Longitude = state.CurrentLongitude }; state.Status = Status.Ended; await _actionWebService.LogAction(gameActionRequest); await turnContext.ReplyActivity( $"La chasse vient de prendre fin, vos actions ont été enregistrée et un orga va les valider."); await turnContext.End(); }
public override async Task OnGameActionRequested(GameActionRequest actionRequest) { // OnGameActionRequested fires when your bot actually needs to do something. In the request, you will find the entire game // state (what you would normally see on the game table) and a list of possible actions. Some actions have options that // you need to provide when taking them, things like how many dice to roll, or which building you would like to buy. string log = "OnGameActionRequested - Possible Actions:"; bool first = true; foreach (GameActionType type in actionRequest.PossibleActions) { if (!first) { log += ","; } first = false; log += $" {type.ToString()}"; } Logger.Log(Log.Info, log); // This state helper object is super useful object that helps you understand the current state of the game. // There are 4 modules to the state helper. Each helper has functions specific the to topic. // Player // ex. GetPlayer(), GetNumberOfLandmarksOwned(), GetMaxRollsAllowed(), CanHaveExtraTurn() // Marketplace // ex. GetMaxBuildingsInGame(), GetBuiltBuildingsInCurrentGame(), GetBuildingTypesBuildableInMarketplace() // CurrentTurn // ex. CanRollOrReRoll(), GetPossibleActions(), CanTakeAction() // BuildingRules // This helper holds all of the building types and the rules of them. ex. BuildingRules[buildingIndex].GetColor() // StateHelper stateHelper = actionRequest.State.GetStateHelper(GetCurrentUserName()); // Ask the log core to handle the action request. await m_logicCore.OnGameActionRequested(actionRequest, stateHelper); }
public async Task LogAction(GameActionRequest logActionRequest, CancellationToken cancellationToken = default(CancellationToken)) { using (var content = new MultipartFormDataContent()) { content.Add(new StringContent(logActionRequest.GameId.ToString()), "gameId"); content.Add(new StringContent(logActionRequest.TeamId.ToString()), "teamId"); content.Add(new StringContent(logActionRequest.Latitude.ToString()), "latitude"); content.Add(new StringContent(logActionRequest.Longitude.ToString()), "longitude"); content.Add(new StringContent(logActionRequest.Action.ToString()), "action"); var result = await PostAsync <string>($"{_httpClient.BaseAddress}api/Action/AddGameAction/", content, cancellationToken); } }
public async Task <GameNotification> SendGameActionAsync(GameActionRequest actionRequest) { GameState gameState = serverState.GetGameState(actionRequest.GameId); if (!gameLogic.ValidateGameAction(actionRequest, gameState)) { // TODO: make error messages clearer throw new ArgumentException("Invalid move"); } GameState newState = gameLogic.ApplyAction(gameState, actionRequest.Action); Player player = GameLogicUtils.GetCurrentPlayer(newState); if (player.Type == PlayerType.COMPUTER) { if (newState.Stage == GameStage.GAME_OVER) { return(new GameNotification { NewGameState = newState }); } // Get computer's move and return new state to player GameAction computerAction = gameAI.CalculateComputerMove(newState.BoardState, newState.Difficulty); GameState afterComputerMove = gameLogic.ApplyAction(newState, computerAction); serverState.UpdateGameState(afterComputerMove); return(new GameNotification { LastAction = computerAction, NewGameState = afterComputerMove }); } else { serverState.UpdateGameState(newState); // Notify opponent var notification = new GameNotification { NewGameState = newState, LastAction = actionRequest.Action }; await Clients.Client(player.Id).ReceiveGameStateUpdate(notification).ConfigureAwait(false); return(notification); } }
public async Task <IActionResult> AddGameAction(GameActionRequest gameActionRequest) { var gameAction = Mapper.Map <GameAction>(gameActionRequest); gameAction.Team = _teamService.GetTeamById(gameActionRequest.TeamId); gameAction.Game = _gameService.GetGameById(gameActionRequest.GameId); gameAction.Latitude = gameActionRequest.Latitude; gameAction.Longitude = gameActionRequest.Longitude; gameAction.Action = (Action)gameActionRequest.Action; if (gameActionRequest.NodeId != 0) { gameAction.Node = _nodeService.GetNode(gameActionRequest.NodeId); } gameAction.DateOccured = DateTime.Now; switch (gameAction.Action) { case Action.DoAction: case Action.SubmitPicture: if (string.IsNullOrEmpty(gameActionRequest.Picture)) { _logger.LogError("The picture Base64 string is empty, action is ignored"); return(BadRequest(gameActionRequest)); } var picture = new Picture(); var bytes = Convert.FromBase64String(gameActionRequest.Picture); picture.Image = bytes; _imageService.AddPicture(picture); gameAction.Picture = picture; break; case Action.ReplyQuestion: var answer = _nodeService.GetAnswer(gameActionRequest.AnswerId); gameAction.SelectedAnswer = answer; var correctAnswer = ((QuestionNode)gameAction.Node).Answers.Single(a => a.Correct); gameAction.CorrectAnswer = correctAnswer; break; } _actionService.AddGameAction(gameAction); await NotifyClientsForGameAction(gameAction); return(CreatedAtAction("AddGameAction", gameAction)); }
public async Task AddGameAction_StartGame() { // Arrange var gameActionRequest = new GameActionRequest() { Action = 0, Latitude = 1.2, Longitude = 50.2, GameId = 2, TeamId = 5 }; // Act var result = await _target.AddGameAction(gameActionRequest); // Assert Check.That(result).IsInstanceOf <CreatedAtActionResult>(); A.CallTo(() => _actionService.AddGameAction(A <GameAction> .That.Matches(ga => CheckGameActionForAction(ga, Action.StartGame, gameActionRequest.Latitude, gameActionRequest.Longitude)))) .MustHaveHappened(); }
public IActionResult Put(int id, [FromBody] GameActionRequest gameUpdateRequest) { Player player = (Player)HttpContext.Items["Player"]; Race race = raceRepo.Find(gameUpdateRequest.gameId); if (race == null) { return(new NotFoundResult()); } GameActionRequest.ActionType type = gameUpdateRequest.type; if (type == GameActionRequest.ActionType.FORFEIT) { bool result = race.Remove(player); if (result == true) { player.nrOfDroppedGames++; // Bad for reputation. return(new OkResult()); } return(new BadRequestResult()); } else if (type == GameActionRequest.ActionType.MOVE) { int newX = gameUpdateRequest.x; int newY = gameUpdateRequest.y; // Check if user are allowed to make move. bool allowed = race.UpdatePlayer(player, newX, newY); if (allowed != true) { return(new BadRequestResult()); } } else { return(new BadRequestResult()); } return(new OkResult()); }
public async Task AddGameAction_UploadPicture() { // Arrange var gameActionRequest = new GameActionRequest() { Action = 2, Latitude = 1.2, Longitude = 50.2, Picture = "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", GameId = 2, TeamId = 5 }; // Act var result = await _target.AddGameAction(gameActionRequest); // Assert Check.That(result).IsInstanceOf <CreatedAtActionResult>(); A.CallTo(() => _teamService.GetTeamById(gameActionRequest.TeamId)).MustHaveHappened(); A.CallTo(() => _gameService.GetGameById(gameActionRequest.GameId)).MustHaveHappened(); A.CallTo(() => _imageService.AddPicture(A <Picture> .That.Matches(p => CheckPicture(p)))).MustHaveHappened(); A.CallTo(() => _actionService.AddGameAction(A <GameAction> .That.Matches(ga => CheckGameActionForImage(ga, gameActionRequest.Latitude, gameActionRequest.Longitude)))).MustHaveHappened(); A.CallTo(() => _nodeService.GetNode(A <int> ._)).MustNotHaveHappened(); }
public override async Task OnGameActionRequested(GameActionRequest actionRequest) { // OnGameActionRequested fires when your bot actually needs to do something. In the request, you will find the entire game state (what you would normally see on the table) // and a list of possible actions. Some actions have options that you need to provide when taking them, things like how many dice to roll, or which building you would like to buy. string log = "OnGameActionRequested - Possible Actions:"; bool first = true; foreach (GameActionType type in actionRequest.PossibleActions) { if (!first) { log += ","; } first = false; log += $" {type.ToString()}"; } Logger.Log(Log.Info, log); // This state helper object is useful to validate state questions. StateHelper stateHelper = actionRequest.State.GetStateHelper(GetCurrentUserName()); // Ask the log core to handle the action request. await m_logicCore.OnGameActionRequested(actionRequest, stateHelper); }
public async Task <ActionResult <MinesweeperApi.Models.Minesweeper> > PostGame(string gameID, GameActionRequest action) {
public bool ValidateGameAction([DisallowNull] GameActionRequest actionRequest, [DisallowNull] GameState state) { // We have client side validation now // TODO: Implement it correctly return(true); }
// Fires when the game the bot has connected wants the bot to decide on an action. // This will fire as part of the bot's turn, the argument object has details on the actions that can be preformed. public abstract Task OnGameActionRequested(GameActionRequest actionRequest);
public async Task Test_Game_Vs_Computer_Async() { //TODO: make test deterministic if possible var initRequest = new InitGameRequest { GameType = GameType.VS_COMPUTER, UserName = "******", UserTurn = PlayerTurn.ONE }; // Needed to test playing the game var gameAi = new RandomActionGameAI(); GameDifficulty difficulty = GameDifficulty.MEDIUM; // Totally random int maxActionCount = 5; int actionCount = 0; HubConnection connection = await StartNewConnectionAsync().ConfigureAwait(false); GameState initialGameState = await connection.InvokeAsync <GameState>(GameHubMethodNames.INIT_GAME, initRequest).ConfigureAwait(false); initialGameState.Should().NotBeNull(); initialGameState.BoardState.Should().NotBeNull(); initialGameState.Player1.Should().BeEquivalentTo(new Player { Id = ExtractUserId(connection), Name = initRequest.UserName, Type = PlayerType.HUMAN }, "Player 1 must be the one who requested new game"); initialGameState.Stage.Should().Be(GameStage.PLAYING); initialGameState.Player2.Type.Should().Be(PlayerType.COMPUTER); // Play random moves vs computer GameState currentState = initialGameState; GameState previousState; while (currentState.Stage != GameStage.GAME_OVER && actionCount < maxActionCount) { actionCount++; GameAction nextMove = gameAi.CalculateComputerMove(currentState.BoardState, difficulty); GameActionRequest actionRequest = new GameActionRequest { GameId = initialGameState.Id, Action = nextMove }; Console.WriteLine($"Sending game move on turn {currentState.BoardState.TurnNumber}"); previousState = currentState; var notification = await connection.InvokeAsync <GameNotification>(GameHubMethodNames.SEND_GAME_ACTION, actionRequest).ConfigureAwait(false); currentState = notification.NewGameState; // Check new BoardState if (currentState.Stage != GameStage.GAME_OVER) { currentState.BoardState.TurnNumber.Should().Be(previousState.BoardState.TurnNumber + 2); currentState.BoardState.CurrentTurn.Should().Be(initRequest.UserTurn); System.Console.WriteLine($"Current Board State after computer move:\n{ToMatrixString(currentState.BoardState.Board)}"); } else { System.Console.WriteLine($"Game over, Winner is {currentState.BoardState.Winner} current state:\n{ToMatrixString(currentState.BoardState.Board)}"); } } }
public async Task OnGameActionRequested(GameActionRequest actionRequest, StateHelper stateHelper) { // Always roll if it's an option. if (actionRequest.PossibleActions.Contains(GameActionType.RollDice)) { // If we can roll the dice.... LET'S DO IT! // Always roll ALL THE DICE int maxDiceCount = stateHelper.Player.GetMaxCountOfDiceCanRoll(); // Check if we have another roll. int rollsSoFar = stateHelper.GetState().CurrentTurnState.Rolls; bool canReRoll = stateHelper.Player.GetMaxRollsAllowed() < rollsSoFar; // If we can't reroll, auto commit the dice. Otherwise don't, so we can reroll if we want. Logger.Log(Log.Info, "Requesting a dice roll! BIG MONEY NO WHAMMIES..."); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateRollDiceAction(maxDiceCount, !canReRoll)); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to roll dice.", result.Error); } else { Logger.Log(Log.Info, "Trust the dice gods, we roll the dice and commit!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.BuildBuilding)) { // WE ARE A BIG ROLLER... let's build. // Get all building that are in the marketplace currently. List <int> buildable = stateHelper.Marketplace.GetBuildingTypesBuildableInMarketplace(); // Filter it down to only buildings we can afford. List <int> affordable = stateHelper.Player.FilterBuildingIndexesWeCanAfford(buildable); // Randomly pick one. int buildingIndex = affordable[m_random.RandomInt(0, affordable.Count - 1)]; // IF WE BUILD IT... Logger.Log(Log.Info, $"Requesting to build {stateHelper.BuildingRules[buildingIndex].GetName()}..."); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateBuildBuildingAction(buildingIndex)); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to build building.", result.Error); } else { Logger.Log(Log.Info, $"We just bought {stateHelper.BuildingRules[buildingIndex].GetName()}!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.EndTurn)) { // If we can't roll the dice or build a building, we must not have enough funds. // Just end the turn. // End it! Logger.Log(Log.Info, "There's nothing to do, requesting turn end..."); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateEndTurnAction()); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to end our turn.", result.Error); } else { Logger.Log(Log.Info, $"We have {stateHelper.Player.GetPlayer().Coins} coins and can't buy anything, so we ended the turn."); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.TvStationPayout)) { // Our Tv Station activated! Let's take 5 coins from a player at random. GamePlayer randomPlayer = GetRandomPlayer(stateHelper); // DO IT!!! Logger.Log(Log.Info, $"Our Tv Station was activated, let's take coins from player {randomPlayer.Name}!"); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateTvStationPayoutAction(randomPlayer.PlayerIndex)); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to respond to tv station payout.", result.Error); } else { Logger.Log(Log.Info, $"We taken coins from player {randomPlayer.Name} for our tv station!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.BusinessCenterSwap)) { // Our Business Center activated! Let's randomly pick a player and building to swap. GamePlayer randomPlayer = GetRandomPlayer(stateHelper); BuildingBase ourBuilding = GetRandomOwnedNonMajorBuidling(stateHelper, null); BuildingBase theirBuilding = GetRandomOwnedNonMajorBuidling(stateHelper, randomPlayer.PlayerIndex); GameActionResponse result; if (randomPlayer == null || ourBuilding == null || theirBuilding == null) { // If there aren't any building we can use, skip the action. Logger.Log(Log.Info, $"Our Business Center was activated, but there weren't the correct building to swap. So we will skip!"); result = await m_bot.SendAction(GameAction <object> .CreateBusinessCenterSwapAction(0, 0, 0, true)); } else { Logger.Log(Log.Info, $"Our Business Center was activated, swap our {ourBuilding.GetName()} for {randomPlayer.Name}'s {theirBuilding.GetName()}!"); result = await m_bot.SendAction(GameAction <object> .CreateBusinessCenterSwapAction(randomPlayer.PlayerIndex, ourBuilding.GetBuildingIndex(), theirBuilding.GetBuildingIndex())); } if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to respond to business center swap.", result.Error); } else { Logger.Log(Log.Info, $"Business center swap done!"); } return; } Logger.Log(Log.Error, $"Hmm, we were asked for an action but didn't know what to do with..."); foreach (GameActionType type in actionRequest.PossibleActions) { Logger.Log(Log.Error, $" ... {type.ToString()}"); await Shutdown("received an unknown action.", null); } }
public async Task OnGameActionRequested(GameActionRequest actionRequest, StateHelper stateHelper) { // OnGameActionRequested is called when the bot actually needs to take an action. Below is an example of how this can // be done and what events your bot will need to handle. // // To see all of the actions your must handle, look at GameActionType. // // actionRequest.State is the root of the state object for the game. This holds all things like players, coin amounts, // what building are in the marketplace, states of the current turn, etc. // Essentially, this object is everything you would see on the table when playing the game. // // The statehelper is a very useful tool that will answer many current state questions. The state helper takes a perspective user // when it's created, that it will use as a default player if no player is given. // For example, the Player.GetPlayerName function takes an option playerIndex. If not given, it will return your name. // // There are 4 modules to the state helper. Each helper has functions specific the to topic. // Player // ex. GetPlayer(), GetNumberOfLandmarksOwned(), GetMaxRollsAllowed(), CanHaveExtraTurn() // Marketplace // ex. GetMaxBuildingsInGame(), GetBuiltBuildingsInCurrentGame(), GetBuildingTypesBuildableInMarketplace() // CurrentTurn // ex. CanRollOrReRoll(), GetPossibleActions(), CanTakeAction() // BuildingRules // This helper holds all of the building types and the rules of them. ex. BuildingRules[buildingIndex].GetColor() // // Always roll if it's an option. if (actionRequest.PossibleActions.Contains(GameActionType.RollDice)) { // How many dice can we roll? int maxDiceCount = stateHelper.Player.GetMaxCountOfDiceCanRoll(); // Can we re-roll int rollsSoFar = stateHelper.GetState().CurrentTurnState.Rolls; bool canReRoll = stateHelper.Player.GetMaxRollsAllowed() < rollsSoFar; // If we can't reroll, auto commit the dice. Otherwise don't, so we can reroll if we want. Logger.Log(Log.Info, "Rolling the dice!"); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateRollDiceAction(maxDiceCount, !canReRoll)); if (!result.Accepted) { await Shutdown("failed to roll dice.", result.Error); } else { Logger.Log(Log.Info, "Done"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.CommitDiceResult)) { // This action is used if you want to see the dice results before they are committed. // It's useful to see the results if you have the ability to re-roll, so you can decided to re-roll. // But if the `autoCommitResults` flag is set to true when you call `CreateRollDiceAction` this wont' get called, // because the server will always do this for you. GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateCommitDiceResultAction()); if (!result.Accepted) { await Shutdown("failed to commit dice.", result.Error); } else { Logger.Log(Log.Info, "Done"); } } if (actionRequest.PossibleActions.Contains(GameActionType.BuildBuilding)) { // Get all building that are in the marketplace currently. List <int> buildable = stateHelper.Marketplace.GetBuildingTypesBuildableInMarketplace(); // Filter it down to only buildings we can afford. List <int> affordable = stateHelper.Player.FilterBuildingIndexesWeCanAfford(buildable); // Randomly pick one. int buildingIndex = affordable[m_random.RandomInt(0, affordable.Count - 1)]; Logger.Log(Log.Info, $"Requesting to build {stateHelper.BuildingRules[buildingIndex].GetName()}..."); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateBuildBuildingAction(buildingIndex)); if (!result.Accepted) { await Shutdown("failed to build building.", result.Error); } else { Logger.Log(Log.Info, $"We just bought {stateHelper.BuildingRules[buildingIndex].GetName()}!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.TvStationPayout)) { // Our Tv Station activated! Let's take 5 coins from a player at random. GamePlayer randomPlayer = GetRandomPlayer(stateHelper); Logger.Log(Log.Info, $"Our Tv Station was activated, let's take coins from player {randomPlayer.Name}!"); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateTvStationPayoutAction(randomPlayer.PlayerIndex)); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to respond to tv station payout.", result.Error); } else { Logger.Log(Log.Info, $"We taken coins from player {randomPlayer.Name} for our tv station!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.BusinessCenterSwap)) { // Our Business Center activated! Let's randomly pick a player and building to swap. GamePlayer randomPlayer = GetRandomPlayer(stateHelper); BuildingBase ourBuilding = GetRandomOwnedNonMajorBuidling(stateHelper, null); BuildingBase theirBuilding = GetRandomOwnedNonMajorBuidling(stateHelper, randomPlayer.PlayerIndex); GameActionResponse result; if (randomPlayer == null || ourBuilding == null || theirBuilding == null) { // If there aren't any building we can use, skip the action. Logger.Log(Log.Info, $"Our Business Center was activated, but there weren't the correct building to swap. So we will skip!"); result = await m_bot.SendAction(GameAction <object> .CreateBusinessCenterSwapAction(0, 0, 0, true)); } else { Logger.Log(Log.Info, $"Our Business Center was activated, swap our {ourBuilding.GetName()} for {randomPlayer.Name}'s {theirBuilding.GetName()}!"); result = await m_bot.SendAction(GameAction <object> .CreateBusinessCenterSwapAction(randomPlayer.PlayerIndex, ourBuilding.GetBuildingIndex(), theirBuilding.GetBuildingIndex())); } if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to respond to business center swap.", result.Error); } else { Logger.Log(Log.Info, $"Business center swap done!"); } return; } if (actionRequest.PossibleActions.Contains(GameActionType.EndTurn)) { // If we can't roll the dice or build a building, we must not have enough funds. // Just end the turn. Logger.Log(Log.Info, "There's nothing to do, requesting turn end..."); GameActionResponse result = await m_bot.SendAction(GameAction <object> .CreateEndTurnAction()); if (!result.Accepted) { // If random bot fails, it instantly shuts down. await Shutdown("failed to end our turn.", result.Error); } else { Logger.Log(Log.Info, $"We have {stateHelper.Player.GetPlayer().Coins} coins and can't buy anything, so we ended the turn."); } return; } Logger.Log(Log.Error, $"Hmm, we were asked for an action but didn't know what to do with..."); foreach (GameActionType type in actionRequest.PossibleActions) { Logger.Log(Log.Error, $" ... {type.ToString()}"); await Shutdown("received an unknown action.", null); } }
public async Task Test_Game_Vs_Random_Opponent_Async() { // Test needs the list of open games on server to be empty to run reliably // TODO: Make this deterministic, one option is use an in-process TestServer #region Game initialization var initRequest = new InitGameRequest { UserName = "******", GameType = GameType.VS_RANDOM_PLAYER }; var joinRequest = new InitGameRequest { UserName = "******", GameType = GameType.VS_RANDOM_PLAYER }; // Request game by player1 HubConnection firstPlayerConnection = await StartNewConnectionAsync().ConfigureAwait(false); GameState initialGameState = await firstPlayerConnection.InvokeAsync <GameState>(GameHubMethodNames.INIT_GAME, initRequest).ConfigureAwait(false); initialGameState.Should().NotBeNull(); initialGameState.BoardState.Should().BeNull(); initialGameState.Player1.Should().BeEquivalentTo(new Player { Id = ExtractUserId(firstPlayerConnection), Name = initRequest.UserName, Type = PlayerType.HUMAN }, "Player 1 must be the one who requested new game"); initialGameState.Stage.Should().Be(GameStage.WAITING_FOR_OPPONENT); initialGameState.Player2.Should().Be(null); bool player1Notified = false; GameState player1NotificationGameState = null; Semaphore player1NotificationSem = new Semaphore(0, 1); // Setup gameStateUpdate message handler for player 1 firstPlayerConnection.On <GameNotification>(GameHubMethodNames.RECEIVE_GAME_STATE_UPDATE, (notification) => { if (!player1Notified) { // First game state update, Just store the state to compare it to state received by player2 player1NotificationGameState = notification.NewGameState; player1Notified = true; player1NotificationSem.Release(); } }); // Request game by player2 HubConnection secondPlayerConnection = await StartNewConnectionAsync().ConfigureAwait(false); secondPlayerConnection.ConnectionId.Should().NotBe(firstPlayerConnection.ConnectionId); // Just to be safe GameState joinedGameState = await secondPlayerConnection.InvokeAsync <GameState>(GameHubMethodNames.INIT_GAME, joinRequest).ConfigureAwait(false); joinedGameState.Id.Should().Be(initialGameState.Id); joinedGameState.Stage.Should().Be(GameStage.PLAYING); joinedGameState.Player2.Should().BeEquivalentTo(new Player { Id = ExtractUserId(secondPlayerConnection), Name = joinRequest.UserName, Type = PlayerType.HUMAN }, "Player 1 must be the one who requested new game"); joinedGameState.BoardState.Should().NotBeNull(); // Wait for notification handler to finish if (player1NotificationSem.WaitOne(80000)) { player1Notified.Should().Be(true); joinedGameState.Should().BeEquivalentTo(player1NotificationGameState); } else { Assert.Fail("Timeout waiting for first player connection to handle notification"); } #endregion #region Gameplay //Let's just play in PUT mode, the goal here is only to test game state update and notification logic not the actual game logic Queue <GameAction> firstMoveSeq = new Queue <GameAction>(); firstMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 0, Y = 0 } }); firstMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 0, Y = 1 } }); firstMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 0, Y = 2 } }); Queue <GameAction> secondMoveSeq = new Queue <GameAction>(); secondMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 2, Y = 0 } }); secondMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 2, Y = 1 } }); secondMoveSeq.Enqueue(new PutPieceAction { Position = new Point { X = 2, Y = 2 } }); var gameFinishedSem = new Semaphore(0, 1); bool gameOver = false; firstPlayerConnection.On <GameNotification>(GameHubMethodNames.RECEIVE_GAME_STATE_UPDATE, (notification) => { HandleGameStateUpdateNotificationAsync(firstPlayerConnection, notification.NewGameState).Wait(); }); secondPlayerConnection.On <GameNotification>(GameHubMethodNames.RECEIVE_GAME_STATE_UPDATE, (notification) => { HandleGameStateUpdateNotificationAsync(secondPlayerConnection, notification.NewGameState); }); //Play first move HubConnection currentPlayerConnection = joinedGameState.BoardState.CurrentTurn == PlayerTurn.ONE ? firstPlayerConnection : secondPlayerConnection; await HandleGameStateUpdateNotificationAsync(currentPlayerConnection, joinedGameState).ConfigureAwait(false); // Wait for game to finish with timeout gameFinishedSem.WaitOne(TimeSpan.FromSeconds(10)); gameOver.Should().BeTrue("Game must be over"); Task HandleGameStateUpdateNotificationAsync(HubConnection connection, GameState newGameState) { PlayerTurn currentTurn = newGameState.BoardState.CurrentTurn; Queue <GameAction> currentMoveSeq = currentTurn == PlayerTurn.ONE ? firstMoveSeq : secondMoveSeq; HubConnection currentPlayerConnection = currentTurn == PlayerTurn.ONE ? firstPlayerConnection : secondPlayerConnection; currentPlayerConnection.Should().Be(connection, "Connection to handle game state must correspond to the current turn"); Console.WriteLine($"\nReceived game state update, turn number {newGameState.BoardState.TurnNumber}. Board state:\n{ToMatrixString(newGameState.BoardState.Board)}"); if (newGameState.BoardState.Winner != null) { System.Console.WriteLine($"Game Over! Winner is {newGameState.BoardState.Winner}"); gameOver = true; gameFinishedSem.Release(); return(Task.CompletedTask); } else { GameAction nextMove = currentMoveSeq.Dequeue(); System.Console.WriteLine($"Sending game move {nextMove} as player {currentTurn}"); GameActionRequest request = new GameActionRequest { GameId = newGameState.Id, Action = nextMove }; return(currentPlayerConnection.SendAsync(GameHubMethodNames.SEND_GAME_ACTION, request)); } } #endregion }