- Easy - it will just be a random move
- Medium - a simple move that will try to get three in a row or block the opponent should he or she have 2 in a row
- Difficult - will make a perfect move that will guarantee a tie or victory.
But first lets create our game controller, under the controllers folder create a folder called v1 and add a GameController.cs file
with that created lets setup our controller with a simple hello world api end point
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace src.controllers.v1 {
[ApiController]
[Route("api/v1/[Controller]")]
public class GameController : ControllerBase {
private readonly ILogger<GameController> _logger;
public GameController(ILogger<GameController> logger){
this._logger = logger;
}
[HttpGet("HelloWorld")]
public ActionResult<string> HelloWorld() => Ok($"hello world");
}
}
now with that ready lets run our project and test it, in our terminal in the source folder type in
dotnet run
this will run our api in our localhost on port 5001, now navigate in your browser to
notice that the api/v1/game comes from the route attribute on our class, whereas the helloword comes from the HttpGet attribute on our endpoint, now using our browser works well enough for the simplest of get calls, however it becomes a huge pain in the arse for calls post request with body payloads, so lets use Postman for testing our endpoint.
so to get started create a new collection in postman to store all of our tic tac toe endpoints, click the new button and select collection.
Next give your collection a name, a description if you feel like it, generally i find if your name is well thought out you don't really need a description, but hey maybe you want to clarify that it's X's and O's. also make your life simpler and under the variables section specify a url variable with our domain and port number https://localhost:5001 and a version variable with the remainder of our url /api/v1 excluding the controller and endpoint of course since those will not be repetitive.
with that created add our helloworld endpoint to your newly created tic tac toe collection
now with that created lets go and create our other endpoints, namely RandomMove, GoodMove and ExcellentMove, respectively, easy, medium and difficult; however before we create our endpoints we must ask ourselves one very important question, and that is which verb should we use? a POST or a GET? now generally when yo use a POST you are sending data to be saved to some sort of Repository, and when you call a GET you are usually getting some sort of response from a Repository, now you may have noticed that we do not have a repository, at no point did we set up a database or datatable or anything that we could store data in and as you may know REST services are stateless, that means that any request response combination has any prior knowledge of a previous request response combination.
So from the stateless nature of REST services and the fact that we have no repository we know one thing for sure, and that is that we are going to have to pass the "Game" in its entirety to our service, now to choose GET vs POST, well to be honest there is no perfect choice, technically we are POSTing our entire game but not storing it, and we are GETing a responding move. In my opinion we can go either way but because GET calls are cached I am going to opt for using the GET verb, now the main reason is because I have already decided that I am going to use a query parameters to transfer my game data and not the request body, had i decided to use the request body i would have opted for a POST verb.
So you may ask what the difference between a query parameter is vs a request body, well if you think of your rest call as a letter, the URL you are sending it to is the address on the envelop and query parameters would be data appended to the address, whereas the request body would be the letter inside the envelope.
so what data should we send in our query parameters? well if you think of a tic tac toe game it really is just a series of X's and O's placed inside of a 3 by 3 grid,
So if that is all that is we don't actually have to serialize the whole board, all we need to do is pass the sequence of moves and their addresses to our api, so in the above example lets say that
- X took the center square
- O took the top left corner
- X took the center right corner
- O took the bottom right corner
how would we pass this information to our API? well we are missing a key concept and that is addresses on our grid, again we have a choice to make, do we use x,y coordinates? it seems logical right we have a table lets split number our cells
top row 0,0; 1,0; 2,0
mid row 0,1; 1,1; 2,1
low row 0,2; 1,2; 2,2
then we could do something like send 11X, 00O, 21X, 22O as query parameters
or if we wanted to use a POST verb with a body we could send some json
[
{
"symbol": "X"
"col": 1
"row": 1
},
{
"symbol": "O"
"col": 0
"row": 0
}
{
"symbol": "X"
"col": 2
"row": 1
}
{
"symbol": "O"
"col": 2
"row": 2
}
]
now to be frank for such small payloads, it makes no difference it really is a matter of preference, however if we were going to send huge payloads over mobile service providers in countries without modern infrastructure then every symbol matters, each bit counts so i propose something different, lets use a simpler grid system
now all we have to do is send 4X, 0O, 8X, 5O; so with that laid out we can create our three GET calls and for now they will just return the strings; RandomMove, GoodMove and Expert move.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace src.controllers.v1 {
[ApiController]
[Route("api/v1/[controller]")]
public class GameController : ControllerBase {
private readonly ILogger<GameController> _logger;
public GameController(ILogger<GameController> logger){
this._logger = logger;
}
[HttpGet("HelloWorld")]
public ActionResult<string> HelloWorld() => Ok($"hello world");
[HttpGet("RandomMove")]
public ActionResult<string> RandomMove([FromQuery] string[] m){
return "Random Move";
}
[HttpGet("SimpleMove")]
public ActionResult<string> SimpleMove([FromQuery] string[] m){
return "Simple Move";
}
[HttpGet("ExpertMove")]
public ActionResult<string> ExpertMove([FromQuery] string[] m){
return "Expert Move";
}
}
}
now with those created lets run our application with dotnet run and test our responses in Postman
each call returning the corresponding value, in the next post lets get these returning actual valid moves, but fist lets check in our code.
To check in our code we first have to ask ourselves will an existing consumer of our code be affected by our changes? the answer is no because we have not modified anything that we return so we definitely are not pushing a major version. Are we publishing any bug fixes? also no thus there are no patches being pushed.
we are merely extending an existing version, thus i think we can safely say that this is a minor version.
lets do a git status
now we can see four changes, one we modified our git ignore; what i did was just append it with
#ignore files from repo
*.ignore
this just allows me to append any file with .ignore so for example myfile.cs.ignore and it wont be brought into our git repo;
next i remove the default WetherForecastController, because it's not needed
I add our new GameController.cs and remove the default UnitTest file because again its not needed.
next lets commit our changes
git commit -m 'Removed otb files, crated game controller with placeholder endpoints'
now lets annotate tag our version as a minor upgrade
git tag -a v1.1.0 -m 'removed otb example classes & created GameController.cs'
with that done lets push our changes up to our repo
git push
now if we go to our devops, we wont see or tag, because we have to explicitly push it
now with that done we can see our new tag locally using git tag -n and remotely
super, next lets start writing some actual code.