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, a400 Bad Request
error is returned with an error message.
Example Requests
-
Request to get Player 1's username:
// URL: /Player/1/Username // Response: "PlayerOneUsername"
-
Request to get Player 2's username:
// URL: /Player/2/Username // Response: "PlayerTwoUsername"
-
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, theusername
provided in the query is assigned to Player 1, and the updated username is returned in the response. - If the
playerNumber
is 2, theusername
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, a400 Bad Request
error is returned with an error message.
Example Requests
-
Request to update Player 1's username:
// URL: /Player/1/Username?username=NewPlayerOne // Response: "NewPlayerOne"
-
Request to update Player 2's username:
// URL: /Player/2/Username?username=NewPlayerTwo // Response: "NewPlayerTwo"
-
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, a400 Bad Request
error is returned with an error message.
Example Requests
-
Request to get Player 1's symbol:
// URL: /Player/1/Symbol // Response: "X"
-
Request to get Player 2's symbol:
// URL: /Player/2/Symbol // Response: "O"
-
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 providedsymbol
value. - If the
playerNumber
is 2, the method sets Player 2's symbol to the providedsymbol
value. - If the
playerNumber
is any other value, a400 Bad Request
error is returned with an error message.
Example Requests
-
Request to set Player 1's symbol to 'X':
// URL: /Player/1/Symbol?symbol=X // Response: "X"
-
Request to set Player 2's symbol to 'O':
// URL: /Player/2/Symbol?symbol=O // Response: "O"
-
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:
-
Check Player Setup:
If (either player’s symbol or username is not set) { returns "Please Finish Setting Up The Players" }
-
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" }
-
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 }
-
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
-
Request to make a valid move at coordinates (0, 1):
// URL: /MakeMove?x=0&y=1 // Response: "Move Accepted"
-
Request where player setup is incomplete:
// URL: /MakeMove?x=1&y=1 // Response: "Please Finish Setting Up The Players"
-
Request with invalid coordinates (e.g., out of bounds):
// URL: /MakeMove?x=3&y=3 // Response: "Player {playerNumber} Invalid Move"
-
Request with an occupied cell:
// URL: /MakeMove?x=0&y=0 // Response: "Player {playerNumber} Invalid Move"
-
Request resulting in a win:
// URL: /MakeMove?x=2&y=2 // Response: "Congratulations {winningPlayerUsername} You Have Won!"
-
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
-
Request when the game is ongoing:
// URL: /GameStatus // Response: "InProgress"
-
Request when the game has been won:
// URL: /GameStatus // Response: "Win"
-
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:
-
Update Game Status: Sets the game status to
Status.InProgress
, indicating that the game has been reset and is ready to start anew. -
Reset Current Player: Resets the current player to
1
, making Player 1 the starting player for the new game. -
Clear Game Board: Calls
ClearBoard
on thegame_board
object to remove all existing moves and reset the board to its initial state. -
Reset Players: Calls
ResetPlayer
on bothplayer_1
andplayer_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
- 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.