updated server and update to readme by seoeun
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Andre Henriques 2024-01-20 22:47:44 +00:00
parent e3b2e60ddc
commit 3cbf7b99af
2 changed files with 50 additions and 46 deletions

View File

@ -1,13 +1,15 @@
# Application Coperative "Atomas" Game Andre Goncalves Henriques (URN: 6644818), Seoeun Lee (URN: 6595203)
The application that this project implements is a coperative version of the game [Atomas](https://play.google.com/store/apps/details?id=com.sirnic.atomas&hl=en&gl=US&pli=1). # Cooperative Application "Atomas" Game
Atomas is a simple single player mobile phone game which allows players to combine atomns to make new attoms with heigher atomic numbers.
The Server application inplements a coperative, and simplified version of this game that allows players to play a game of Atomas in the same board at the same time. The application that this project implements is a cooperative version of the game [Atomas](https://play.google.com/store/apps/details?id=com.sirnic.atomas&hl=en&gl=US&pli=1).
Atomas is a simple single player mobile phone game which allows players to combine atoms to make new atoms with higher atomic numbers.
The Server uses Paxos to guanratee that the only user can play at the time and that a player can not make a move on a diferent game state than another player. The Server application implements a cooperative and simplified version of this game that allows players to play a game of Atomas in the same board at the same time.
Ther `server.ex` file both provides some `Server` Module that runs this service and a `Client` Module that can be used to display some the game state in the terminal with nicer representation. The Server uses Paxos to guarantee that only one user can play at a time and that a player cannot make a move on a different game state than another player.
The `server.ex` file provides both `Server` Module that runs this service and a `Client` Module that can be used to display the game state in the terminal with nicer representation.
# API # API
@ -21,19 +23,19 @@ The server provides the following API:
## Important Concepts ## Important Concepts
The `game_id` is a number and is an identifier for the game, this can be a number because paxos guanratees that the there can not be two games with same number, as the server would have learned about that decision and whould have not incremented the number. The `game_id` is a number and is an identifier for the game. This can be a number that paxos guarantees that there cannot be two games with same number, as the server would have learned about that decision and would have not incremented the number.
The `game_state` is a representation of a game state that corresponds to an array of numbers, and some special atoms, where the number represents the atomic number of an atom the max length of this array is 16 when array gets bigger then that the game is over. The `game_state` is a representation of a game state that corresponds to an array of numbers, and some special atoms, where the number represents the atomic number. The maximum length of this array is 16, so when an array gets bigger than that the game is over.
## Start Game ## Start Game
The `:start_game` message causes the server to create a new game. The `:start_game` message causes the server to create a new game.
The `:start_game` message takes as arguments `participants` and `pid_to_inform`. The `:start_game` message takes the arguments `participants` and `pid_to_inform`.
- `participants` which is a list of names of the servers that are participating in the game. - `participants` is a list of names of the servers that are participating in the game.
- `pid_to_inform` which is a pid of the process that needs to be informed of the success or failure of this action. - `pid_to_inform` is a pid of the process that needs to be informed of the success or failure of this action.
For example to request the start of a game a user might do somethig like: For example, to request the start of a game, a user might do somethig like:
```elixir ```elixir
send(server_pid, {:start_game, [:p0, :p1], self()}) send(server_pid, {:start_game, [:p0, :p1], self()})
@ -56,17 +58,17 @@ This request will always create a new game (with the exception of crashes).
The `:get_game_state` message causes the server to return the current state of a game. The `:get_game_state` message causes the server to return the current state of a game.
The `:get_game_state` message takes as arguments `game_id`, and `pid_to_inform`. The `:get_game_state` message takes the arguments as `game_id` and `pid_to_inform`.
- `game_id` which is the game_id returned by the `:start_game` request - `game_id` is the game_id returned by the `:start_game` request
- `pid_to_inform` which is a pid of the process that needs to be informed of the success or failure of this action. - `pid_to_inform` is a pid of the process that needs to be informed of the success or failure of this action.
For example to request the current state of a agame: For example, to request the current state of a game:
```elixir ```elixir
send(server_pid, {:get_game_state, 1, self()}) send(server_pid, {:get_game_state, 1, self()})
# use recieve do to wait for the message # use 'recieve do' to wait for the message
# Or, assuming the server was started with the name :p0 # Or, assuming the server was started with the name ':p0'
Server.get_game_state(:p0, 1) Server.get_game_state(:p0, 1)
``` ```
@ -76,7 +78,7 @@ The server will answer with one of the following:
{:game_state, game_id, :not_playing} # When the player is not playing in the game {:game_state, game_id, :not_playing} # When the player is not playing in the game
{:game_state, game_id, :game_does_not_exist} # When the game was never created {:game_state, game_id, :game_does_not_exist} # When the game was never created
{:game_state, game_id, :game_finished, score} # When the game is already over {:game_state, game_id, :game_finished, score} # When the game is already over
{:game_state, game_id, game_state, hand} # When the player is in the game and the game has not finished {:game_state, game_id, game_state, hand} # When the player is in the game, and the game has started
# Returns the current game state of the game, # Returns the current game state of the game,
# and the current "atom" on the hand of the player # and the current "atom" on the hand of the player
``` ```
@ -85,12 +87,12 @@ The server will answer with one of the following:
The `:make_move` message causes the server to try to play the move that the player is requesting. The `:make_move` message causes the server to try to play the move that the player is requesting.
The `:make_move` message takes as arguments `game_id`, `move`, and `pid_to_inform`. The `:make_move` message takes the arguments as `game_id`, `move`, and `pid_to_inform`.
- `game_id` which is the game_id returned by the `:start_game` request - `game_id` is the game_id returned by the `:start_game` request
- `move` which is the position in the game state array the play wants to insert their "atom" - `move` is the position in the game state array where the play wants to insert their "atom"
- `pid_to_inform` which is a pid of the process that needs to be informed of the success or failure of this action. - `pid_to_inform` is a pid of the process that needs to be informed of the success or failure of this action.
For example to request the current state of a agame: For example, to request the current state of a agame:
```elixir ```elixir
send(server_pid, {:make_move, 1, 3, self()}) send(server_pid, {:make_move, 1, 3, self()})
@ -107,10 +109,10 @@ The server will answer with one of the following:
{:make_move, game_id, :game_does_not_exist} # When the game was never created {:make_move, game_id, :game_does_not_exist} # When the game was never created
{:make_move, game_id, :game_finished, score} # When the game is already over {:make_move, game_id, :game_finished, score} # When the game is already over
{:make_move, game_id, :invalid_move} # When the move requested by the player is an invalid move {:make_move, game_id, :invalid_move} # When the move requested by the player is an invalid move
{:make_move, game_id, :player_moved_before, game_state, hand} # When the player is in the game and the game has not finished, {:make_move, game_id, :player_moved_before, game_state, hand} # When the player is in the game and the game has started,
# but another player has played before this request was male # but another player has played the game before this request was made
# Returns the current game state of the game, # Returns the current game state of the game,
# and the current "atom" on the hand of the player # and the current "atom" on the player's hand
{:make_move, game_id, game_state, hand} # When the player is in the game and the game has not finished {:make_move, game_id, game_state, hand} # When the player is in the game and the game has not finished
# and the move is valid # and the move is valid
# Returns the current game state of the game, # Returns the current game state of the game,
@ -119,25 +121,27 @@ The server will answer with one of the following:
# Properties # Properties
- If a game exists then there was a process that create it. - If a game exists, then there was a process that creates it.
- Eventually all processes would will be in the same game state - Eventually, all processes will be in the same game state
- A player can not make a move in a game state that is not the last - A player cannot make a move in a game state unless it's not updated to the last game state
- If two players make a move at the same time for the same game only one of the moves happens and the other player eventually gets notified - If two players make a move at the same time in the same game, only one of the moves happens and the other player eventually gets notified
- If a player makes a valid move it will be evetually played
# Assumptions # Assumptions
- Processes won't be behave in a byzantine way - Processes won't behave in a byzantine way
- Only a minority of processes can fail - Only a minority of processes can fail
- Will be run in a partial synchronous system - Will run in a partial synchronous system
- When the game is created, the game state will have atoms in it, and the participants will have atoms in their hands
# Usage instructions # Usage instructions
There is two ways of interacting with the server via the API or the `Client` Module There are two ways of interacting with the server: via the API or the `Client` Module
## Notes ## Notes
By default all the logs are disabled to enable all the logs inside the paxos file there is a module variable called `@min_print_level`, By default, all the logs are disabled to enable all the logs inside the paxos file. There is a module variable called `@min_print_level`.
by setting it to 0 it enables all the logs Setting it to 0 enables all the logs
## API ## API
@ -156,7 +160,7 @@ procs = Enum.to_list(1..3) |> Enum.map(fn x -> :"p#{x}" end)
pids = procs |> Enum.map(fn x -> Server.start(x, procs) end) pids = procs |> Enum.map(fn x -> Server.start(x, procs) end)
``` ```
After the 3 instances have been started you can use Server helper functions to communicate with the Server: After the 3 instances have been started, you can use Server helper functions to communicate with the Server:
```elixir ```elixir
Server.start_game(:p1, [:p1, :p2]) Server.start_game(:p1, [:p1, :p2])
@ -166,19 +170,19 @@ Server.make_move(:p1, 1, 0)
## Using the Client ## Using the Client
Open 2 different terminals. One terminal will be the display game and the other will be used to control the game Open 2 different terminals. One terminal will display the game, and the other one will be used to control the game
In the first terminal start an iex session with a session name: In the first terminal, start an iex session with a session name:
``` ```
iex --sname c1 iex --sname c1
``` ```
In the second terminal start an iex session with a session name: In the second terminal, start an iex session with a session name:
``` ```
iex --sname c2 iex --sname c2
``` ```
On the first terminal connect to the second terminal: On the first terminal, connect the first terminal to the second terminal:
```elixir ```elixir
# In c1 # In c1
Node.connect(:"c2@<computer_name>") Node.connect(:"c2@<computer_name>")
@ -190,7 +194,7 @@ On both terminals compile paxos and server:
c "paxos.ex"; c "server.ex" c "paxos.ex"; c "server.ex"
``` ```
On the first terminal start the servers, create a game and enter display mode: Start the servers on the first terminal, create a game, and enter display mode:
```elixir ```elixir
# In c1 # In c1
procs = Enum.to_list(1..3) |> Enum.map(fn x -> :"p#{x}" end) procs = Enum.to_list(1..3) |> Enum.map(fn x -> :"p#{x}" end)
@ -221,11 +225,11 @@ You now should see something like:
Be Be
``` ```
The Center "Atom" is your hand, while the Letters arround are the game_state or board, The Center "Atom" is your hand, while the Letters arround are the "game_state" or "board",
the number represents where you can put the "Atom" in your hand the number represents where you can put the "Atom" in your hand
On the second terminal start enter control mode: Start control mode on the second terminal by entering:
```elixir ```elixir
# In c2 # In c2
Client.control_game(:p1, 1) Client.control_game(:p1, 1)
@ -236,6 +240,6 @@ You should see:
Type the number you want to play or q to exit: Type the number you want to play or q to exit:
``` ```
Keep your mouse in the second terminal, and insert the number, where you want to make your move, and hit enter! On the second terminal, insert the number in where you want to make your move and hit enter!
The map should update on the top screen and now you can play the game! The map should update on the top screen and now you can play the game!

View File

@ -153,7 +153,7 @@ defmodule Server do
not is_number(move) -> not is_number(move) ->
safecast(pid_to_inform, {:make_move, game_id, :invalid_move}) safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
state state
move >= length(game.game_state) -> move >= length(game.game_state) or move < 0 ->
safecast(pid_to_inform, {:make_move, game_id, :invalid_move}) safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
state state
true -> true ->