"The simpler the food, the harder it is to prepare well" - Joël Robuchon.
Welcome to the TicTacToe Game Processor, an Elixir Phoenix API that manages a TicTacToe game. The App allows any type of client and provides a common interface to play a game.
Deployment has been done with one of the most popular PaaS Heroku using the container stack.
Note : It might take up to 20 secs to get the API response first time, because Heroku unloads applications from the memory after some inactivity time and In-memory state such as those in Agents, GenServers, and ETS will be lost every 24 hours means a started game if not completed within 24 hours will be vanished. See Dyno Sleeping.
https://tictac9.herokuapp.com/api/v1/swagger
- Install Elixir from the Elixir downloads page.
- Install dependencies with
mix deps.get - Start Phoenix endpoint with
mix phx.server - The game processor should be accessible now in
localhost:4000.
- Make sure Docker Deamon is up and running by verify either of the two commands:
docker run hello-worldordocker info - Navigate to the app root directory
- To run local docker image ,unfortunately
prod.secret.exsrequire commenting line no 29 and uncomment line 28 to get the default port 😞 ( Due to heroku port accessing, working on finding a solution for this) - build the image by running
docker build -t tictac .don't forget to add a dot docker run -p 4000:4000 tictac- The game processor should be accessible now in
localhost:4000.
Game Processor provides an endpoint to start a game. You need to do a POST request to the following endpoint:
http://localhost:4000/api/v1/game/create
An example response will be in the form of a unique gameId in UUID form:
{
"game_id": "fdaee005-b595-4fc9-b30b-8a223da0ea57"
}
Game Processor provides a way to get the player to move.
You need to do a POST operation to the following endpoint:
http://localhost:4000/api/v1/game/move
After this, you need to provide the specific parameters to move API as below in the request body.
{
"game_params" :{
"game_id" : "080440c6-13b3-4e54-92d5-346dbc5b6511",
"col" : 2,
"row" : 3,
"player" : "o"
}
}
game_id: Unique game Id generated by create game APIcol: Board column number for a move, it should be within 1..3 range onlyrow: Board row number for a move, it should be within 1..3 range onlyplayer: valid players are X and O, either uppercase or lower
lib/tictactoe_web/controllers/fallback_controller.ex contains all the errors thrown by the game processor in case of an invalid move
game-Id: A valid state game-id required for a moveplayer: player passed must be X or Ocell coordinates: row and column range should be 1..3cell occupied: row and column should be empty for a proper moveplayer-turn: The same player is not allowed in 2 consecutive moves
A valid move response will contain the response.
{
"board": {
"{\"col\":1,\"row\":1}": "empty",
"{\"col\":1,\"row\":2}": "empty",
"{\"col\":1,\"row\":3}": "empty",
"{\"col\":2,\"row\":1}": "empty",
"{\"col\":2,\"row\":2}": "empty",
"{\"col\":2,\"row\":3}": "o",
"{\"col\":3,\"row\":1}": "empty",
"{\"col\":3,\"row\":2}": "empty",
"{\"col\":3,\"row\":3}": "empty"
},
"game_id": "1266eec6-8f95-4c3b-9473-46e3a41e955e",
"player": "x",
"winner": false
}
- The current board after the move.
- The player that performed the move as a value of cell coordinate value.
- The current game Id.
- Next player turn.
If a move encounters a win or tie, an appropriate message would be displayed to encourage the players 🎉
We need something fault-tolerant to ensure an error occurred in a game would be handled appropriately and not prevent the rest of the running games.
We need a tool that makes it easy to track the state of a game. By leveraging Dynamic Supervisors and GenServers, we can concurrently maintain lots and lots of games having an in-memory game state, gracefully handle any errors that might occur for a given game deployment and track each game process.
GameSupervisor : A DynamicSupervisor which starts with no children. Children GameProcessors are started on demand. GameSupervisor allows players to start games, and it also automatically restarts them when a game process crashes.
GameProcessor : Its a GenServer to keep each game’s state. With our GenServer callbacks in place, we can spawn a process that maintains a game’s state.
GameRegistry :
We can’t name a process with a string; we need to use a Registry to link string names to game PID's. Elixir’s GenServer implementation has a built-in way of referring to processes in a registry through it’s :via-tuples
State : Managing and manipulating a perticular game state , exe :initial,:playing,:game_over etc , leveraging the power of Elixir pattern maching
GameStore : In-memory Map to store a game state, can be easily replaced by a persistance data store like postgres
- More test cases for sure 😔
- MiniMax Algorithm for a computer player
- A frontend client in Angular,React or Vue
- Pub Sub design for real time game state update using web sockets
- Phoenix live view dashboard
- Further code optimization and modularization
- Detailed module and function documentation
- Elixir in Action: https://www.manning.com/books/elixir-in-action-second-edition
- Alchemist Camp: https://www.youtube.com/channel/UCp5Nix6mJCoLkH_GqcRRp1A
- Elixir School: https://elixirschool.com/
- jessejanderson: https://gist.github.com/jessejanderson/26ef3cf17017e38af7e76bd2dcc6cfb0
- Official website: https://www.phoenixframework.org/
- Guides: https://hexdocs.pm/phoenix/overview.html
- Docs: https://hexdocs.pm/phoenix
- Forum: https://elixirforum.com/c/phoenix-forum
- Source: https://github.com/phoenixframework/phoenix
Bored of being bored because being bored is boring.
Utilizing the App's modular design like State, GameProcessor, GameUtils etc., a CLI module allows you to play Tictactoe in the command line.
- open terminal in app's root dir
- type
iex -s mix - import CLI module
import Tictactoe.Game.Cli - just type
play
Note : As this module does not use GenServer for state management, any error will restart the game.



