Converting TicTacToe C# Game To ASP.NET Core REST API

Introduction

As a continuation of my .NET learning journey, I decided to expand upon the Tic Tac Toe game I had originally written in C# by converting it into a REST API using ASP.NET Core. To start, I watched ASP.NET Core: The Big Picture by Roland Guijt, which provided an excellent overview of the framework. I then followed up with ASP.NET Core: Fundamentals by Gill Cleeren, which gave me a solid foundation to begin working on the project.

With this knowledge, I felt confident enough to start building the API. As I encountered more advanced or nuanced aspects of the framework, I conducted additional research to deepen my understanding. Pluralsight’s courses were instrumental in helping me grasp the core concepts of ASP.NET Core and enabled me to ask more informed questions during my learning process. This approach, combined with further research and discussions with experienced professionals, was key to the project's success.

A special thanks to Jeff Muller, a Senior Software Engineer I met at local meetup events, who generously answered my questions, reviewed my code, and provided valuable feedback as the project progressed.

Features

GET /GameBoard

The GET /GameBoard API endpoint provides read-only access to the current state of the Tic Tac Toe game board. This endpoint retrieves the game board represented as a two-dimensional rectangular array char[,] and returns it in a jagged array format char[][].

Endpoint Overview

  • URL: /GameBoard
  • Method: GET
  • Response Format: char[][] (Jagged Array)

Description

When a request is made to this endpoint, it invokes the GameBoard method of the TicTacToeController. This method fetches the current state of the game board from the Game object and converts it from a rectangular two-dimensional array char[,] to a jagged array char[][]. The jagged array is then returned in the response, allowing clients to view the current configuration of the game board.

Example Response

The response will be a JSON array representing the game board. For instance:

[
    ["X", "O", "\0"],
    ["\0", "X", "O"],
    ["O", "\0", "X"]
]

This JSON array corresponds to a 3x3 game board with the current state of each cell.

Code

Here's the code snippet for the GET /GameBoard endpoint:

[HttpGet("GameBoard")]
public ActionResult<char[][]> GameBoard()
{
    char[,] gameBoard = _game.ShowGameBoard();
    char[][] jaggedArray = ConvertToJaggedArray(gameBoard);
    return Ok(jaggedArray);
}

This method calls the ShowGameBoard method from the Game class to get the current game board and then converts it into a jagged array format before returning it in the response.

GET /Player/{playerNumber}/Username

The GET /Player/{playerNumber}/Username API endpoint allows clients to retrieve the username of a specific player in the Tic Tac Toe game. This endpoint accepts a path parameter playerNumber to specify which player's username to fetch.

Endpoint Overview

  • URL: /Player/{playerNumber}/Username
  • Method: GET
  • Path Parameter: playerNumber: Specifies the player number (either 1 or 2)
  • Response Format: string (Username)

Description

When a request is made to this endpoint, it invokes the GetPlayerUsername method of the TicTacToeController. The method fetches the username of the specified player based on the provided playerNumber parameter. If the playerNumber is invalid (i.e., not 1 or 2), the server will return a 400 Bad Request error.

The endpoint uses the following logic:

  • If the playerNumber is 1, the response will return the username of Player 1.
  • If the playerNumber is 2, the response will return the username of Player 2.
  • If the playerNumber is any other value, a 400 Bad Request error is returned with an error message.

Example Requests

  1. Request to get Player 1's username:

    // URL: /Player/1/Username 
    // Response:
    
    "PlayerOneUsername"
    

  2. Request to get Player 2's username:

    // URL: /Player/2/Username
    // Response:
    
    "PlayerTwoUsername"
    

  3. Invalid Request:

    // URL: /Player/3/Username
    // Response:
    
    {
    "error": "Invalid player number. Only 1 or 2 are allowed."
    }
    

Code

Here’s the code snippet for the GET /Player/{playerNumber}/Username endpoint:

[HttpGet("Player/{playerNumber}/Username")]
public ActionResult<string> PlayerUsername(int playerNumber)
{
    if (playerNumber == 1)
    {
        return Ok(_game.Player_1_Username);
    }
    else if (playerNumber == 2)
    {
        return Ok(_game.Player_2_Username);
    }
    else
    {
        return BadRequest("Invalid player number. Only 1 or 2 are allowed.");
    }
}

The GetPlayerUsername method takes the playerNumber as an input parameter. Based on the value of playerNumber, it returns either Player 1's or Player 2's username. If an invalid playerNumber is provided, a 400 Bad Request response is returned with an error message.

PUT /Player/{playerNumber}/Username

The PUT /Player/{playerNumber}/Username API endpoint allows clients to update the username of a specific player in the Tic Tac Toe game. This endpoint accepts a path parameter playerNumber to specify which player's username to update and a query parameter username that provides the new username.

Endpoint Overview

  • URL: /Player/{playerNumber}/Username
  • Method: PUT
  • Path Parameter: playerNumber: Specifies the player number (either 1 or 2)
  • Query Parameter: username: The new username to assign to the specified player.
  • Response Format: string (Updated Username)

Description

When a request is made to this endpoint, it invokes the PlayerUsername method of the TicTacToeController. The method takes the playerNumber as a path parameter and the username as a query parameter to update the respective player's username. If the playerNumber is invalid (i.e., not 1 or 2), the server will return a 400 Bad Request error.

The endpoint uses the following logic:

  • If the playerNumber is 1, the username provided in the query is assigned to Player 1, and the updated username is returned in the response.
  • If the playerNumber is 2, the username provided in the query is assigned to Player 2, and the updated username is returned in the response.
  • If the playerNumber is any other value, a 400 Bad Request error is returned with an error message.

Example Requests

  1. Request to update Player 1's username:

    // URL: /Player/1/Username?username=NewPlayerOne
    // Response:
    
    "NewPlayerOne"
    

  2. Request to update Player 2's username:

    // URL: /Player/2/Username?username=NewPlayerTwo
    // Response:
    
    "NewPlayerTwo"
    

  3. Invalid Request:

    // URL: /Player/3/Username?username=InvalidPlayer
    // Response:
    
    {
    "error": "Invalid player number. Only 1 or 2 are allowed."
    }
    

Code

Here’s the code snippet for the PUT /Player/{playerNumber}/Username endpoint:

[HttpPut("Player/{playerNumber}/Username")]
public ActionResult<string> PlayerUsername(int playerNumber, [FromQuery] string username)
{
    if (playerNumber == 1)
    {
        _game.Player_1_Username = username;
        return Ok(_game.Player_1_Username);
    }
    else if (playerNumber == 2)
    {
        _game.Player_2_Username = username;
        return Ok(_game.Player_2_Username);
    }
    else
    {
        return BadRequest("Invalid player number. Only 1 or 2 are allowed.");
    }
}

The PlayerUsername method takes the playerNumber as a path parameter and the username from the query string. Based on the playerNumber, it updates either Player 1's or Player 2's username and returns the updated value. If an invalid playerNumber is provided, a 400 Bad Request response is returned with an error message.

GET /Player/{playerNumber}/Symbol

The GET /Player/{playerNumber}/Symbol API endpoint allows clients to retrieve the game symbol (either 'X' or 'O') assigned to a specific player in the Tic Tac Toe game. This endpoint accepts a path parameter playerNumber to specify which player's symbol to fetch.

Endpoint Overview

  • URL: /Player/{playerNumber}/Symbol
  • Method: GET
  • Path Parameter: playerNumber: Specifies the player number (either 1 or 2)
  • Response Format: char (Symbol)

Description

When a request is made to this endpoint, it invokes the PlayerSymbol method of the TicTacToeController. The method retrieves the symbol of the specified player based on the provided playerNumber parameter. If the playerNumber is invalid (i.e., not 1 or 2), the server will return a 400 Bad Request error.

The endpoint uses the following logic:

  • If the playerNumber is 1, the response will return the symbol of Player 1.
  • If the playerNumber is 2, the response will return the symbol of Player 2.
  • If the playerNumber is any other value, a 400 Bad Request error is returned with an error message.

Example Requests

  1. Request to get Player 1's symbol:

    // URL: /Player/1/Symbol
    // Response:
    
    "X"
    

  2. Request to get Player 2's symbol:

    // URL: /Player/2/Symbol
    // Response:
    
    "O"
    

  3. Invalid Request:

    // URL: /Player/3/Symbol
    // Response:
    
    {
    "error": "Invalid player number. Only 1 or 2 are allowed."
    }
    

Code

Here’s the code snippet for the GET /Player/{playerNumber}/Symbol endpoint:

[HttpGet("Player/{playerNumber}/Symbol")]
public ActionResult<char> PlayerSymbol(int playerNumber)
{
    if (playerNumber == 1)
    {
        return Ok(_game.Player_1_Symbol);
    }
    else if (playerNumber == 2)
    {
        return Ok(_game.Player_2_Symbol);
    }
    else
    {
        return BadRequest("Invalid player number. Only 1 or 2 are allowed.");
    }
}

The PlayerSymbol method takes the playerNumber as an input parameter. Based on the value of playerNumber, it returns either Player 1's or Player 2's symbol ('X' or 'O'). If an invalid playerNumber is provided, a 400 Bad Request response is returned with an error message.

PUT /Player/{playerNumber}/Symbol

The PUT /Player/{playerNumber}/Symbol API endpoint allows clients to set or update the symbol (either 'X' or 'O') assigned to a specific player in the Tic Tac Toe game. This endpoint accepts a path parameter playerNumber to specify which player's symbol to update, and a query parameter symbol to indicate the new symbol.

Endpoint Overview

  • URL: /Player/{playerNumber}/Symbol
  • Method: PUT
  • Path Parameter: playerNumber: Specifies the player number (either 1 or 2)
  • Query Parameter: symbol: The symbol to assign to the player (either o, O, x, or X)
  • Response Format: char (Updated Symbol)

Description

When a request is made to this endpoint, it invokes the PlayerSymbol method of the TicTacToeController. The method updates the symbol for the specified player based on the playerNumber parameter and the provided symbol value. If the playerNumber is invalid (i.e., not 1 or 2), the server returns a 400 Bad Request error.

The endpoint uses the following logic:

  • If the playerNumber is 1, the method sets Player 1's symbol to the provided symbol value.
  • If the playerNumber is 2, the method sets Player 2's symbol to the provided symbol value.
  • If the playerNumber is any other value, a 400 Bad Request error is returned with an error message.

Example Requests

  1. Request to set Player 1's symbol to 'X':

    // URL: /Player/1/Symbol?symbol=X
    // Response:
    
    "X"
    

  2. Request to set Player 2's symbol to 'O':

    // URL: /Player/2/Symbol?symbol=O
    // Response:
    
    "O"
    

  3. Invalid Request (invalid player number):

    // URL: /Player/3/Symbol?symbol=X
    // Response:
    
    {
      "error": "Invalid player number. Only 1 or 2 are allowed."
    }
    

Code

Here’s the code snippet for the PUT /Player/{playerNumber}/Symbol endpoint:

[HttpPut("Player/{playerNumber}/Symbol")]
public ActionResult<char> PlayerSymbol(int playerNumber, [FromQuery] char symbol)
{
    if (playerNumber == 1)
    {
        _game.Player_1_Symbol = symbol;
        return Ok(_game.Player_1_Symbol);
    }
    else if (playerNumber == 2)
    {
        _game.Player_2_Symbol = symbol;
        return Ok(_game.Player_2_Symbol);
    }
    else
    {
        return BadRequest("Invalid player number. Only 1 or 2 are allowed.");
    }
}

The PlayerSymbol method takes the playerNumber as a path parameter and the symbol as a query parameter. It updates the symbol for the specified player and returns the updated symbol. If the playerNumber is invalid, the method returns a 400 Bad Request response with an error message.

PUT /MakeMove

The PUT /MakeMove API endpoint allows clients to make a move on the Tic Tac Toe game board. This endpoint accepts two query parameters, x and y, which represent the coordinates on the game board where the move is to be made.

Endpoint Overview

  • URL: /MakeMove
  • Method: PUT
  • Query Parameters: x: The row index (0-based) y: The column index (0-based)
  • Response Format: string (Move Result)

Description

When a request is made to this endpoint, it invokes the MakeMove method of the TicTacToeController. The method processes the move based on the provided x and y coordinates and returns a message indicating the result of the move. The result can indicate whether the move was accepted, if it was invalid (e.g., if the cell is already occupied or the coordinates are out of bounds), or if the game has concluded.

The endpoint uses the following logic:

  1. Check Player Setup:

    If (either player’s symbol or username is not set)
    {
       returns "Please Finish Setting Up The Players"
    }
    

  2. Validate Move:

    If (the move is out of bounds or if the cell at the specified coordinates is already occupied)
    {
       returns "Player {playerNumber} Invalid Move"
    }
    

  3. Process Move:

    If (the move is valid)
    {
       places the current player's symbol at the specified coordinates
    
       switches to the other player
    
       updates the game status
    }
    

  4. Check Game Conclusion:

    If (the game has concluded by win))
    {
       returns "Congratulations {username} You Have Won!"
    } 
    else If (the game concluded by draw)
    {
       returns "The Game Was a Draw"
    } 
    else 
    {
       returns "Move Accepted"
    }
    

Example Requests

  1. Request to make a valid move at coordinates (0, 1):

    // URL: /MakeMove?x=0&y=1
    // Response:
    
    "Move Accepted"
    

  2. Request where player setup is incomplete:

    // URL: /MakeMove?x=1&y=1
    // Response:
    
    "Please Finish Setting Up The Players"
    

  3. Request with invalid coordinates (e.g., out of bounds):

    // URL: /MakeMove?x=3&y=3
    // Response:
    
    "Player {playerNumber} Invalid Move"
    

  4. Request with an occupied cell:

    // URL: /MakeMove?x=0&y=0
    // Response:
    
    "Player {playerNumber} Invalid Move"
    

  5. Request resulting in a win:

    // URL: /MakeMove?x=2&y=2
    // Response:
    
    "Congratulations {winningPlayerUsername} You Have Won!"
    

  6. Request resulting in a draw:

    // URL: /MakeMove?x=2&y=1
    // Response:
    
    "The Game Was a Draw"
    

Code

Here’s the code snippet for the PUT /MakeMove endpoint:

[HttpPut("MakeMove")]
public ActionResult<string> MakeMove([FromQuery] int x, [FromQuery] int y)
{
    return Ok(_game.MakeMove(x, y));
}

The MakeMove method takes x and y as query parameters, processes the move using the game logic, and returns the result of the move as a string. This response provides information about the success or failure of the move, including game conclusion status.

GET /GameStatus

The GET /GameStatus API endpoint allows clients to retrieve the current status of the Tic Tac Toe game. This endpoint provides information about whether the game is ongoing, has concluded with a win, or ended in a draw.

Endpoint Overview

  • URL: /GameStatus
  • Method: GET
  • Response Format: string (Game Status)

Description

When a request is made to this endpoint, it invokes the GameStatus method of the TicTacToeController. The method returns the current status of the game as a string. The status can be one of the following:

  • "Win": Indicates that the game has concluded with a win for one of the players.
  • "Draw": Indicates that the game has ended in a draw with no winner.
  • "InProgress": Indicates that the game is still ongoing and no conclusion has been reached yet.

The status is retrieved from the Game_Status property of the _game object, which is an enumeration of type Status with possible values Win, Draw, or InProgress.

Example Requests

  1. Request when the game is ongoing:

    // URL: /GameStatus
    // Response:
    
    "InProgress"
    

  2. Request when the game has been won:

    // URL: /GameStatus
    // Response:
    
    "Win"
    

  3. Request when the game ended in a draw:

    // URL: /GameStatus
    // Response:
    
    "Draw"
    

Code

Here’s the code snippet for the GET /GameStatus endpoint:

[HttpGet("GameStatus")]
public ActionResult<string> GameStatus()
{
    return Ok(_game.Game_Status.ToString());
}

The GameStatus method returns the current game status as a string by converting the Game_Status enum value to its string representation. This allows clients to easily determine the current state of the game.

PUT /ResetGame

The PUT /ResetGame API endpoint allows clients to reset the Tic Tac Toe game to its initial state. This endpoint is used to start a new game by clearing the current board, resetting player data, and updating the game status.

Endpoint Overview

  • URL: /ResetGame
  • Method: PUT
  • Response Format: string (Reset Result)

Description

When a request is made to this endpoint, it invokes the ResetGame method of the TicTacToeController. The method performs the following actions to reset the game:

  1. Update Game Status: Sets the game status to Status.InProgress, indicating that the game has been reset and is ready to start anew.

  2. Reset Current Player: Resets the current player to 1, making Player 1 the starting player for the new game.

  3. Clear Game Board: Calls ClearBoard on the game_board object to remove all existing moves and reset the board to its initial state.

  4. Reset Players: Calls ResetPlayer on both player_1 and player_2 to reset player-specific data such as symbols and usernames.

The method returns "Game Reset" to indicate that the game has been successfully reset.

Example Request

  1. Request to reset the game:
    // URL: /ResetGame
    // Response:
    
    "Game Reset"
    

Code

Here’s the code snippet for the PUT /ResetGame endpoint:

[HttpPut("ResetGame")]
public ActionResult<string> ResetGame()
{
    return Ok(_game.ResetGame());
}

The ResetGame method is called to reset the game state, and the result is returned as a string indicating the completion of the reset operation.

Reflection

Transitioning my Tic Tac Toe console application from C# to a REST API with ASP.NET Core has been an eye-opening experience. Converting a simple console project into a web-based API not only tested my technical skills but also provided valuable insights into modern web development practices.

Experience and Growth

The journey began with a familiar console project, which I used as a foundation to explore the world of REST APIs. The process of adapting the game’s logic and functionality to fit the RESTful architecture taught me a lot about API design principles, such as handling HTTP methods, managing endpoints, and ensuring data integrity through proper validation.

Working on this project, I gained a deeper understanding of ASP.NET Core’s features and how they can be leveraged to create efficient and scalable APIs. The hands-on experience of building endpoints and managing game states translated well into practical skills that I can apply in future projects.

Future Aspirations

Looking ahead, I am excited about the prospect of building a React front end to this project to interface with the API endpoints. Integrating a modern front-end framework with my existing API will provide a complete, user-friendly interface and enrich the overall user experience.

Sources: