This project is a full implementation of the Tic Tac Toe game but playable through a REST API. All the game data is saved in the database to make this challenge more fun. Run it now and play an honest game 🎮!
It's written in C# and uses ASP.NET Web API. Some more details:
- CliFx is used to start the application. You can use it to start your worker, seed your database, and many more.
- AutoMapper is used by the Core layer to return DTO objects to the Api layer.
- Serilog handles everything concerning logging.
- Npgsql is the data provider for PostgreSQL that is used by Entity Framework.
- DrfLikePaginations handles the pagination. It supports Cursor and Offset paginations.
First run the project through the command below. It will generate any migrations that are missing, apply them, and run the project afterwards (check out the script responsible for that):
docker-compose up app-development
Then you can create players, a board, and play your game! Below you can check some HTTPie commands to play with the API.
# First create a player for you
http POST :8000/api/v1/players name=Jafar
# Then to someone who can play with you, let's say a computer
http POST :8000/api/v1/players name=Rose computer:=true
# You can list them with pagination!
http GET :8000/api/v1/players
# You can filter also! Know this project: https://github.com/willianantunes/drf-like-paginations/
http GET :8000/api/v1/players computer==false
# In order to have a game, you first need to create a board
http POST :8000/api/v1/boards firstPlayerId=1 secondPlayerId=2
# Then you are good to go!
# If you are lucky, you can win with three movements like the following:
http GET :8000/api/v1/games/play BoardId==1 PlayerId==1 MovementPosition==1
http GET :8000/api/v1/games/play BoardId==1 PlayerId==1 MovementPosition==2
http GET :8000/api/v1/games/play BoardId==1 PlayerId==1 MovementPosition==3
# Get the game details given its ID:
http GET :8000/api/v1/games/1
Sample GIF that shows a winning game with 4 movements (positions 1, 7, 8, and 9):
This project has 4 layers:
- API: ASP.NET Core stuff.
- Core: All business logic.
- Infrastructure: The database is fully configured in this layer.
- EntryCommands: It's like the
Startup.cs
.
It's important to mention that I didn't use the standard way to start the project, given from the webapi boilerplate project. CliFx
plays an essential role as the command handler of the program. The idea was to mimic how Django works because it's easy to understand and test.
About the entities:
You can check how I configured these 5 tables using the EF Fluent API in AppDbContext class.
To run all tests, you can simply execute the command:
docker-compose up tests
It will wait until the database is up and execute all the tests using the project's actual database (PostgreSQL).
To allow each integration test to be executed isolated without worries of race conditions or dirty data, I create one dedicated database per method test. You can check how I handled it by looking at the class IntegrationTestsFixture.
If you look at the PositionDecider class, you'll notice that I simply choose a random available position from the list. During the tests, I used an even simpler version (CustomPositionDecider class) that merely select the first item from the ordered list. I could create a test that asserts a winning game with three movements only (7, 8, and 9) on a 3x3 board with this approach (see it in this test in GameServiceITests class).
The test plan jmeter-test-plan.jmx can be opened with JMeter 5.4.1. The Thread Group
basically has the following configurations:
In order to run it, first you should apply the migrations with the command:
docker-compose up apply-migrations
As the database is ready to be used, you can run the production version of the Tic Tac Toe program:
docker-compose up app
Then you can open a new terminal and finally run:
docker-compose up performance-test
When the performance testing is concluded, you can open the file tests-jmeter/index.html
and run PerformanceTestingResultTest.cs
. Check out this statistics:
The output with regards to APDEX:
The best way to learn a new language, apart from reading documentation and books, is by coding, focusing on practice instead of theory, at least that's what I believe. But, of course, this is even better when you have some programming background. The problem is, though, which project can we do to learn a new language? I don't think a CRUD application is good because it doesn't provide challenges concerning abstractions, data structures, iteration statements, conditionals, and many more. Now a project related to an actual game can really bring you benefits, but not a too complicated one because, in the end, it consumes lots of time.
I think Hangman and Tic Tac Toe are good examples to start with. It's a good idea to start with a console application, then evolve it to be more challenging, like including a REST API with a Database, thus bringing a taste about how an actual project is in a company. So, to learn C#, I followed this path. I suggest it to everyone!
I use this project to test some features that I'm interested in. Then I can use them as a reference for future projects. Below you can check some of them:
- MassTransit and its integration with ASP.NET Core.
- Serilog.
- Liveness and readiness concepts.
- Pagination.
- Integration tests with a real database.
It supposes the queue is named tic-tac-toe-player
. The player name is 'Iago'. Change it if you want.
curl -i -u guest:guest 'http://localhost:15672/api/exchanges/%2F/amq.default/publish' \
-H 'Content-Type: text/plain;charset=UTF-8' \
--data-raw '{"vhost":"/","name":"amq.default","properties":{"delivery_mode":1,"headers":{},"content_type":"application/json"},"routing_key":"tic-tac-toe-player","delivery_mode":"1","payload":"{\"name\":\"Iago\"}","payload_encoding":"string","headers":{},"props":{"content_type":"application/json"}}'
Look at the class PlayerConsumer to see how the message is handled.
The image built from Dockerfile is the one you should use in production. When you access the container through the command docker-compose run app bash
and execute ls -l
you'll see something like the following:
-rwxr-xr-x 1 appuser appuser 48192516 Nov 12 22:21 TicTacToeCSharpPlayground
-rw-r--r-- 1 appuser appuser 44120 Nov 12 22:21 TicTacToeCSharpPlayground.pdb
-rw-rw-r-- 1 appuser appuser 815 Nov 12 22:14 appsettings.json
-rw-r--r-- 1 appuser appuser 23304 Nov 12 22:21 packages.lock.json
drwxr-xr-x 2 appuser appuser 4096 Nov 12 23:11 scripts
Files in folder scripts
:
-rwxrwxr-x 1 appuser appuser 985 Sep 14 2022 build-production.sh
-rwxrwxr-x 1 appuser appuser 198 Nov 12 23:09 start-consumer.sh
-rwxrwxr-x 1 appuser appuser 163 Nov 12 23:09 start-web.sh