public async Task OPlayerWinTestAsync() // Validate that the application properly detects a human player "O" victory { //�Arrange ExecuteMove payload = new ExecuteMove() { Move = 1, AzurePlayerSymbol = "X", HumanPlayerSymbol = "O", GameBoard = new List <string> { "X", "O", "X", "?", "O", "?", "?", "O", "?" } }; // Act HttpOperationResponse <object> resultObject = await _client.ExecuteMoveResponseWithHttpMessagesAsync(payload); ExecuteMoveResponse resultPayload = resultObject.Body as ExecuteMoveResponse; // Assert if (resultObject != null) { Assert.AreEqual(resultPayload.Winner, "O"); } else { Assert.Fail("Expected an ExecuteMoveResponse but didn't recieve one."); } }
public async Task HumanMoveTestAsync() // Validate that the application returns a 400 error if the human player's move is not represented on the gameBoard. { //�Arrange ExecuteMove payload = new ExecuteMove() { Move = 8, AzurePlayerSymbol = "O", HumanPlayerSymbol = "X", GameBoard = new List <string> { "X", "O", "X", "?", "?", "?", "?", "?" } }; // Act HttpOperationResponse <object> resultObject = await _client.ExecuteMoveResponseWithHttpMessagesAsync(payload); // Assert if (resultObject != null) { // Assert if (resultObject != null) { Assert.AreEqual(StatusCodes.Status400BadRequest, (int)resultObject.Response.StatusCode); } else { Assert.Fail("Expected an ExecuteMoveResponse but didn't recieve one."); } } }
public async Task PositionCountTestAsync() // Validate that the application returns a 400 error if the difference between the HumanPlayer's positions and Azure Player's Positions is greater than 1. { //�Arrange ExecuteMove payload = new ExecuteMove() { Move = 2, AzurePlayerSymbol = "O", HumanPlayerSymbol = "X", GameBoard = new List <string> { "X", "O", "X", "X", "?", "?", "?", "?" } }; // Act HttpOperationResponse <object> resultObject = await _client.ExecuteMoveResponseWithHttpMessagesAsync(payload); // Assert if (resultObject != null) { // Assert if (resultObject != null) { Assert.AreEqual(StatusCodes.Status400BadRequest, (int)resultObject.Response.StatusCode); } else { Assert.Fail("Expected an ExecuteMoveResponse but didn't recieve one."); } } }
public async Task HappyPathTestAsync() // Validate that the application is providing a valid response to a properly-formatted request. { //�Arrange ExecuteMove payload = new ExecuteMove() { Move = 1, AzurePlayerSymbol = "X", HumanPlayerSymbol = "O", GameBoard = new List <string> { "X", "O", "?", "?", "?", "?", "?", "?", "?" } }; // Act HttpOperationResponse <object> resultObject = await _client.ExecuteMoveResponseWithHttpMessagesAsync(payload); // Assert if (resultObject != null) { Assert.AreEqual(StatusCodes.Status200OK, (int)resultObject.Response.StatusCode); //Assert.AreEqual(resultPayload.Move, 4); //Assert.AreEqual(resultPayload.GameBoard[4], "X"); //Assert.AreEqual(resultPayload.Winner, "inconclusive"); //Assert.AreEqual(resultPayload.WinPositions, null); } else { Assert.Fail("Expected an ExecuteMoveResponse but didn't recieve one."); } }
[ProducesResponseType(typeof(int), StatusCodes.Status400BadRequest)] // Tells swagger that the response format will be an int for a BadRequest (400) public ActionResult <ExecuteMoveResponse> ExecuteMoveResponse([FromBody] ExecuteMove inputPayload) { if (PayloadValidation.ValidatePayload(inputPayload) == false) { return(BadRequest(4)); } ExecuteMoveResponse response = CalculateResponse.CalculateMoveResponse(inputPayload); return(response); }
public async Task ValidResponseTestAsync() // Validates that the ExecuteMove Response is properly formatted // Validate that the Azure and Human player symbols are either "X" or "O" and are not the same. { //�Arrange ExecuteMove payload = new ExecuteMove() { Move = 2, AzurePlayerSymbol = "O", HumanPlayerSymbol = "X", GameBoard = new List <string> { "X", "O", "X", "O", "?", "?", "?", "?", "?" } }; // Act HttpOperationResponse <object> resultObject = await _client.ExecuteMoveResponseWithHttpMessagesAsync(payload); ExecuteMoveResponse resultPayload = resultObject.Body as ExecuteMoveResponse; // Assert if (resultObject != null) { // Validate that the Azure and Human player symbols are either "X" or "O" and are not the same. Assert.IsTrue(resultPayload.AzurePlayerSymbol == "O" || resultPayload.AzurePlayerSymbol == "X"); Assert.IsTrue(resultPayload.HumanPlayerSymbol == "O" || resultPayload.HumanPlayerSymbol == "X"); Assert.AreNotEqual(resultPayload.AzurePlayerSymbol, resultPayload.HumanPlayerSymbol); // Validate that the gameBoard is the proper size and all values are 'X', 'O', or '?' Assert.AreEqual(resultPayload.GameBoard.Count, 9); for (int i = 0; i < resultPayload.GameBoard.Count; i++) { Assert.IsTrue(resultPayload.GameBoard[i] == "X" || resultPayload.GameBoard[i] == "O" || resultPayload.GameBoard[i] == "?"); } } else { Assert.Fail("Expected an ExecuteMoveResponse but didn't recieve one."); } }
public static bool ValidatePayload(ExecuteMove messagePayload) { // Validate that symbols are 'X' or 'O' and are different if (messagePayload.humanPlayerSymbol != 'X' && messagePayload.humanPlayerSymbol != 'O') { return(false); } if (messagePayload.azurePlayerSymbol != 'X' && messagePayload.azurePlayerSymbol != 'O') { return(false); } if (messagePayload.azurePlayerSymbol == messagePayload.humanPlayerSymbol) { return(false); } // Validate that the gameBoard is the proper size and all values are 'X', 'O', or '?' if (messagePayload.gameBoard.Length > 9 || messagePayload.gameBoard.Length < 9) { return(false); } for (int i = 0; i < messagePayload.gameBoard.Length; i++) { if (messagePayload.gameBoard[i] != 'X' && messagePayload.gameBoard[i] != 'O' && messagePayload.gameBoard[i] != '?') { return(false); } } // Validate that the difference between the number of X's and O's is not greater than 1 int humanCount = 0; int azureCount = 0; for (int i = 0; i < messagePayload.gameBoard.Length; i++) { if (messagePayload.gameBoard[i] == messagePayload.humanPlayerSymbol) { humanCount++; } else if (messagePayload.gameBoard[i] == messagePayload.azurePlayerSymbol) { azureCount++; } } // Validate that the player's move is represented on the gameBoard. If the player did not move, gameBoard must be all ?'s. if (messagePayload.move == null) { for (int i = 0; i < messagePayload.gameBoard.Length; i++) { if (messagePayload.gameBoard[i] != '?') { return(false); } } } else { if (messagePayload.gameBoard[(int)messagePayload.move] != messagePayload.humanPlayerSymbol) { return(false); } } return(true); }
/// <summary> /// Facilitates the game of Tic Tac Toe by accepting a JSON input representing /// the human player's move and returning a JSON object with Azure's move /// </summary> /// <param name='body'> /// The input payload representing human player's move. /// </param> /// <param name='customHeaders'> /// Headers that will be added to request. /// </param> /// <param name='cancellationToken'> /// The cancellation token. /// </param> /// <return> /// A response object containing the response body and response headers. /// </return> public async Task <HttpOperationResponse <object> > ExecuteMoveResponseWithHttpMessagesAsync(ExecuteMove body = default(ExecuteMove), Dictionary <string, List <string> > customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) { // Tracing bool _shouldTrace = ServiceClientTracing.IsEnabled; string _invocationId = null; if (_shouldTrace) { _invocationId = ServiceClientTracing.NextInvocationId.ToString(); Dictionary <string, object> tracingParameters = new Dictionary <string, object>(); tracingParameters.Add("body", body); tracingParameters.Add("cancellationToken", cancellationToken); ServiceClientTracing.Enter(_invocationId, this, "ExecuteMoveResponse", tracingParameters); } // Construct URL var _baseUrl = this.BaseUri.AbsoluteUri; var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "api/executemove").ToString(); // Create HTTP transport objects HttpRequestMessage _httpRequest = new HttpRequestMessage(); HttpResponseMessage _httpResponse = null; _httpRequest.Method = new HttpMethod("POST"); _httpRequest.RequestUri = new Uri(_url); // Set Headers if (customHeaders != null) { foreach (var _header in customHeaders) { if (_httpRequest.Headers.Contains(_header.Key)) { _httpRequest.Headers.Remove(_header.Key); } _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); } } // Serialize Request string _requestContent = null; if (body != null) { _requestContent = SafeJsonConvert.SerializeObject(body, this.SerializationSettings); _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); } // Set Credentials if (this.Credentials != null) { cancellationToken.ThrowIfCancellationRequested(); await this.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); } // Send Request if (_shouldTrace) { ServiceClientTracing.SendRequest(_invocationId, _httpRequest); } cancellationToken.ThrowIfCancellationRequested(); _httpResponse = await this.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); if (_shouldTrace) { ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); } HttpStatusCode _statusCode = _httpResponse.StatusCode; cancellationToken.ThrowIfCancellationRequested(); string _responseContent = null; if ((int)_statusCode != 200 && (int)_statusCode != 400) { var ex = new HttpOperationException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); if (_shouldTrace) { ServiceClientTracing.Error(_invocationId, ex); } _httpRequest.Dispose(); if (_httpResponse != null) { _httpResponse.Dispose(); } throw ex; } // Create Result var _result = new HttpOperationResponse <object>(); _result.Request = _httpRequest; _result.Response = _httpResponse; // Deserialize Response if ((int)_statusCode == 200) { _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); try { _result.Body = SafeJsonConvert.DeserializeObject <ExecuteMoveResponse>(_responseContent, this.DeserializationSettings); } catch (JsonException ex) { _httpRequest.Dispose(); if (_httpResponse != null) { _httpResponse.Dispose(); } throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); } } // Deserialize Response if ((int)_statusCode == 400) { _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); try { _result.Body = SafeJsonConvert.DeserializeObject <int?>(_responseContent, this.DeserializationSettings); } catch (JsonException ex) { _httpRequest.Dispose(); if (_httpResponse != null) { _httpResponse.Dispose(); } throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); } } if (_shouldTrace) { ServiceClientTracing.Exit(_invocationId, _result); } return(_result); }
public static ExecuteMoveResponse CalculateMoveResponse(ExecuteMove messagePayload) // Calculates { int[,] victoryConditions = new int[8, 3] // Defines all 8 possible winning combinations in Tic Tac Toe { { 0, 1, 2 }, { 0, 3, 6 }, { 0, 4, 8 }, { 1, 4, 7 }, { 2, 4, 6 }, { 2, 5, 8 }, { 3, 4, 5 }, { 6, 7, 8 } }; string[,] gameState = new string[8, 3] // Tracks player positions against possible victories { { "0", "1", "2" }, { "0", "3", "6" }, { "0", "4", "8" }, { "1", "4", "7" }, { "2", "4", "6" }, { "2", "5", "8" }, { "3", "4", "5" }, { "6", "7", "8" } }; //========== 1. SET UP THE GAME BOARD ========== // This section sets Human/Azure positions on an array called "gameState". It also checks if the Human has won or the game is tied. // (We can assume Azure has not won before making its move.) // Prepare known elements of Azure's response ExecuteMoveResponse response = new ExecuteMoveResponse() { azurePlayerSymbol = messagePayload.azurePlayerSymbol, humanPlayerSymbol = messagePayload.humanPlayerSymbol, gameBoard = messagePayload.gameBoard }; // Creates two lists: for gameBoard human and azure player positions List <int> humanPositions = new List <int>(); List <int> azurePositions = new List <int>(); for (int i = 0; i < messagePayload.gameBoard.Length; i++) { if (messagePayload.gameBoard[i] == messagePayload.humanPlayerSymbol) { humanPositions.Add(i); } else if (messagePayload.gameBoard[i] == messagePayload.azurePlayerSymbol) { azurePositions.Add(i); } } // Compare gameState to humanPositions. // Replace any values in gameState with humanSymbol if they match (indicating that the human owns that space) string humanSymbol = messagePayload.humanPlayerSymbol.ToString(); foreach (int i in humanPositions) { for (int row = 0; row < gameState.GetLength(0); row++) { for (int column = 0; column < gameState.GetLength(1); column++) { if (i.ToString() == gameState[row, column]) { gameState[row, column] = humanSymbol; } } // Check to see if the human has met any of the victory conditions if (gameState[row, 0] == humanSymbol && gameState[row, 1] == humanSymbol && gameState[row, 2] == humanSymbol) { response.winner = humanSymbol; // Set winPositions response.winPositions = new int[3]; for (int j = 0; j < 3; j++) { response.winPositions[j] = victoryConditions[row, j]; } return(response); } } } // Check for a tie game resulting from Player's move if (Array.IndexOf(messagePayload.gameBoard, '?') == -1) { response.winner = "tie"; return(response); } // Compare gameState to azurePositions. // Replace any values in gameState with azureSymbol if they match (indicating that Azure owns that space) string azureSymbol = messagePayload.azurePlayerSymbol.ToString(); foreach (int i in azurePositions) { for (int row = 0; row < gameState.GetLength(0); row++) { for (int column = 0; column < gameState.GetLength(1); column++) { if (i.ToString() == gameState[row, column]) { gameState[row, column] = azureSymbol; } } } } //========== 2. CALCULATE AZURE'S MOVE ========== // This section calculate's Azure's next move and checks if that move results in a win or tie. // Look for any possible winning moves or blocking moves for (int row = 0; row < gameState.GetLength(0); row++) { string[] gameStateRow = { gameState[row, 0], gameState[row, 1], gameState[row, 2] }; // Winning moves int?winningMove = WinBlock(gameStateRow, azureSymbol, humanSymbol); if (winningMove != null) { response.move = winningMove; response.gameBoard[(int)winningMove] = messagePayload.azurePlayerSymbol; response.winner = azureSymbol; response.winPositions = new int[3]; for (int j = 0; j < 3; j++) { response.winPositions[j] = victoryConditions[row, j]; } return(response); } // Blocking moves int?blockingMove = WinBlock(gameStateRow, humanSymbol, azureSymbol); if (blockingMove != null) { response.move = blockingMove; response.gameBoard[(int)blockingMove] = messagePayload.azurePlayerSymbol; } } // If no winning or blocking moves have been found, select the first available move from a predetermined order of priority if (response.move == null) { int[] movePriority = { 4, 8, 6, 2, 0, 7, 5, 3, 1 }; List <int> occupiedPositions = humanPositions.Concat(azurePositions).ToList(); for (int i = 0; i < movePriority.Length; i++) { if (occupiedPositions.IndexOf(movePriority[i]) == -1) { response.move = movePriority[i]; response.gameBoard[movePriority[i]] = messagePayload.azurePlayerSymbol; break; } } } // Check for a tie game resulting from Azure's move if (Array.IndexOf(messagePayload.gameBoard, '?') == -1) { response.winner = "tie"; return(response); } response.winner = "inconclusive"; return(response); }
/// <summary> /// Facilitates the game of Tic Tac Toe by accepting a JSON input representing /// the human player's move and returning a JSON object with Azure's move /// </summary> /// <param name='operations'> /// The operations group for this extension method. /// </param> /// <param name='body'> /// The input payload representing human player's move. /// </param> /// <param name='cancellationToken'> /// The cancellation token. /// </param> public static async Task <object> ExecuteMoveResponseAsync(this IRestClientSDKLibraryClient operations, ExecuteMove body = default(ExecuteMove), CancellationToken cancellationToken = default(CancellationToken)) { using (var _result = await operations.ExecuteMoveResponseWithHttpMessagesAsync(body, null, cancellationToken).ConfigureAwait(false)) { return(_result.Body); } }
/// <summary> /// Facilitates the game of Tic Tac Toe by accepting a JSON input representing /// the human player's move and returning a JSON object with Azure's move /// </summary> /// <param name='operations'> /// The operations group for this extension method. /// </param> /// <param name='body'> /// The input payload representing human player's move. /// </param> public static object ExecuteMoveResponse(this IRestClientSDKLibraryClient operations, ExecuteMove body = default(ExecuteMove)) { return(Task.Factory.StartNew(s => ((IRestClientSDKLibraryClient)s).ExecuteMoveResponseAsync(body), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult()); }