fixed compiler errors and updated read me
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
2d20363870
commit
e3b2e60ddc
@ -134,6 +134,11 @@ The server will answer with one of the following:
|
|||||||
|
|
||||||
There is two ways of interacting with the server via the API or the `Client` Module
|
There is two ways of interacting with the server via the API or the `Client` Module
|
||||||
|
|
||||||
|
## 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 setting it to 0 it enables all the logs
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
Start a new iex session and compilie `paxos.ex` and `server.ex`:
|
Start a new iex session and compilie `paxos.ex` and `server.ex`:
|
||||||
|
98
lib/paxos.ex
98
lib/paxos.ex
@ -21,10 +21,8 @@ defmodule Utils do
|
|||||||
"""
|
"""
|
||||||
@min_print_level 3
|
@min_print_level 3
|
||||||
|
|
||||||
@doc """
|
# This function works similiary with unicast but it allows both for a pid or an atom
|
||||||
This function works similiary with unicast but it allows both for a pid or an atom
|
# to be given as the first parameter
|
||||||
to be given as the first parameter
|
|
||||||
"""
|
|
||||||
def safecast(p, m) when p == nil, do: IO.puts('Trying to safecast #{m} with p as nil')
|
def safecast(p, m) when p == nil, do: IO.puts('Trying to safecast #{m} with p as nil')
|
||||||
def safecast(p, m) when is_pid(p), do: send(p, m)
|
def safecast(p, m) when is_pid(p), do: send(p, m)
|
||||||
def safecast(p, m) do
|
def safecast(p, m) do
|
||||||
@ -132,9 +130,7 @@ defmodule Paxos do
|
|||||||
|
|
||||||
create_log 0
|
create_log 0
|
||||||
|
|
||||||
@doc """
|
# This macro allows the state.instmap to be updated very easily
|
||||||
This macro allows the state.instmap to be updated very easily
|
|
||||||
"""
|
|
||||||
defmacrop set_instmap(do: expr) do
|
defmacrop set_instmap(do: expr) do
|
||||||
quote do
|
quote do
|
||||||
var!(map) = var!(state).instmap[var!(inst)]
|
var!(map) = var!(state).instmap[var!(inst)]
|
||||||
@ -171,9 +167,7 @@ defmodule Paxos do
|
|||||||
run(state)
|
run(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Guarantees that a specific state exists for a specific instance
|
||||||
Guarantees that a specific state exists for a specific instance
|
|
||||||
"""
|
|
||||||
defp has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil, action \\ nil) do
|
defp has_or_create(state, inst, value \\ nil, pid_to_inform \\ nil, action \\ nil) do
|
||||||
or_state state.instmap[inst] == nil do
|
or_state state.instmap[inst] == nil do
|
||||||
instmap =
|
instmap =
|
||||||
@ -197,11 +191,9 @@ defmodule Paxos do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Checks if an instance has finished or if it was aborted.
|
||||||
Checks if an instance has finished or if it was aborted.
|
# If the optional parameter ignore_aborted was set to true makes this function only check
|
||||||
If the optional parameter ignore_aborted was set to true makes this function only check
|
# if the the instance has finished
|
||||||
if the the instance has finished
|
|
||||||
"""
|
|
||||||
defp has_finished(state, inst, ignore_aborted \\ false) do
|
defp has_finished(state, inst, ignore_aborted \\ false) do
|
||||||
cond do
|
cond do
|
||||||
Map.has_key?(state.decided, inst) -> true
|
Map.has_key?(state.decided, inst) -> true
|
||||||
@ -211,21 +203,19 @@ defmodule Paxos do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# This is the run/recieve function
|
||||||
This is the run/recieve function
|
# All the messages that are handled by this function are:
|
||||||
All the messages that are handled by this function are:
|
# {:ele_trust, proc} ->
|
||||||
{:ele_trust, proc} ->
|
# {:propose, inst, value, pid_to_inform, action} ->
|
||||||
{:propose, inst, value, pid_to_inform, action} ->
|
# {:rb_deliver, proc, {:other_propose, inst, value}} ->
|
||||||
{:rb_deliver, proc, {:other_propose, inst, value}} ->
|
# {:rb_deliver, proc, {:prepare, proc, inst, ballot}} ->
|
||||||
{:rb_deliver, proc, {:prepare, proc, inst, ballot}} ->
|
# {:nack, inst, ballot} ->
|
||||||
{:nack, inst, ballot} ->
|
# {:rb_deliver, _proc, {:abort, inst, ballot}} ->
|
||||||
{:rb_deliver, _proc, {:abort, inst, ballot}} ->
|
# {:prepared, inst, ballot, accepted_ballot, accepted_value} ->
|
||||||
{:prepared, inst, ballot, accepted_ballot, accepted_value} ->
|
# {:rb_deliver, proc, {:accept, inst, ballot, value}} ->
|
||||||
{:rb_deliver, proc, {:accept, inst, ballot, value}} ->
|
# {:accepted, inst, ballot} ->
|
||||||
{:accepted, inst, ballot} ->
|
# {:get_value, inst, pid_to_inform} ->
|
||||||
{:get_value, inst, pid_to_inform} ->
|
# {:rb_deliver, _, {:decide, inst, value}} ->
|
||||||
{:rb_deliver, _, {:decide, inst, value}} ->
|
|
||||||
"""
|
|
||||||
runfn do
|
runfn do
|
||||||
# Handles leader elector
|
# Handles leader elector
|
||||||
{:ele_trust, proc} ->
|
{:ele_trust, proc} ->
|
||||||
@ -253,6 +243,8 @@ defmodule Paxos do
|
|||||||
%{map| ballot: Ballot.inc(map.ballot)}
|
%{map| ballot: Ballot.inc(map.ballot)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
state
|
||||||
|
|
||||||
not Map.has_key?(state.instmap, inst) ->
|
not Map.has_key?(state.instmap, inst) ->
|
||||||
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
EagerReliableBroadcast.broadcast(state.name, {:other_propose, inst, value})
|
||||||
state = has_or_create(state, inst, value, pid_to_inform, action)
|
state = has_or_create(state, inst, value, pid_to_inform, action)
|
||||||
@ -277,7 +269,7 @@ defmodule Paxos do
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Handles the sharing of a proposal value to other processes
|
# Handles the sharing of a proposal value to other processes
|
||||||
{:rb_deliver, proc, {:other_propose, inst, value}} ->
|
{:rb_deliver, _, {:other_propose, inst, value}} ->
|
||||||
cond do
|
cond do
|
||||||
has_finished(state, inst, true) ->
|
has_finished(state, inst, true) ->
|
||||||
EagerReliableBroadcast.broadcast(
|
EagerReliableBroadcast.broadcast(
|
||||||
@ -310,6 +302,7 @@ defmodule Paxos do
|
|||||||
set_instmap do
|
set_instmap do
|
||||||
%{ map | ballot: ballot }
|
%{ map | ballot: ballot }
|
||||||
end
|
end
|
||||||
|
state
|
||||||
|
|
||||||
Ballot.compare(ballot, &>/2, state.instmap[inst].ballot) ->
|
Ballot.compare(ballot, &>/2, state.instmap[inst].ballot) ->
|
||||||
safecast(proc,
|
safecast(proc,
|
||||||
@ -320,6 +313,7 @@ defmodule Paxos do
|
|||||||
set_instmap do
|
set_instmap do
|
||||||
%{ map | ballot: ballot }
|
%{ map | ballot: ballot }
|
||||||
end
|
end
|
||||||
|
state
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
safecast(proc, {:nack, inst, ballot})
|
safecast(proc, {:nack, inst, ballot})
|
||||||
@ -349,12 +343,13 @@ defmodule Paxos do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
state
|
||||||
true ->
|
true ->
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
|
|
||||||
# Handles an abort
|
# Handles an abort
|
||||||
{:rb_deliver, _proc, {:abort, inst, ballot}} ->
|
{:rb_deliver, _proc, {:abort, inst, _}} ->
|
||||||
cond do
|
cond do
|
||||||
has_finished(state, inst) ->
|
has_finished(state, inst) ->
|
||||||
state
|
state
|
||||||
@ -418,6 +413,7 @@ defmodule Paxos do
|
|||||||
accepted_ballot: ballot
|
accepted_ballot: ballot
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
state
|
||||||
else
|
else
|
||||||
log("#{state.name} -> #{proc} nack")
|
log("#{state.name} -> #{proc} nack")
|
||||||
safecast(proc, {:nack, inst, ballot})
|
safecast(proc, {:nack, inst, ballot})
|
||||||
@ -471,10 +467,8 @@ defmodule Paxos do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Does the logic to decide when to send the prepare messages
|
||||||
Does the logic to decide when to send the prepare messages
|
# Also sets the state nessesary to run the proposal
|
||||||
Also sets the state nessesary to run the proposal
|
|
||||||
"""
|
|
||||||
defp prepare(state, _) when state.leader != state.name, do: state
|
defp prepare(state, _) when state.leader != state.name, do: state
|
||||||
defp prepare(state, inst) do
|
defp prepare(state, inst) do
|
||||||
cond do
|
cond do
|
||||||
@ -505,15 +499,14 @@ defmodule Paxos do
|
|||||||
has_sent_accept: false
|
has_sent_accept: false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Processes the prepared messages and when a quorum is met the accept messages are send
|
||||||
Processes the prepared messages and when a quorum is met the accept messages are send
|
|
||||||
"""
|
|
||||||
defp prepared(state, _) when state.leader != state.name, do: state
|
defp prepared(state, _) when state.leader != state.name, do: state
|
||||||
defp prepared(state, inst) do
|
defp prepared(state, inst) do
|
||||||
if length(state.instmap[inst].prepared_values) >= floor(length(state.processes) / 2) + 1 and
|
or_state length(state.instmap[inst].prepared_values) >= floor(length(state.processes) / 2) + 1 and
|
||||||
not state.instmap[inst].has_sent_accept do
|
not state.instmap[inst].has_sent_accept do
|
||||||
{_, a_val} =
|
{_, a_val} =
|
||||||
Enum.reduce(state.instmap[inst].prepared_values, {Ballot.init(state.name, 0), nil}, fn {bal, val},
|
Enum.reduce(state.instmap[inst].prepared_values, {Ballot.init(state.name, 0), nil}, fn {bal, val},
|
||||||
@ -550,14 +543,11 @@ defmodule Paxos do
|
|||||||
has_sent_accept: true
|
has_sent_accept: true
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
else
|
|
||||||
state
|
state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Processes the accepted messages and when a qurum is met decide on the value
|
||||||
Processes the accepted messages and when a qurum is met decide on the value
|
|
||||||
"""
|
|
||||||
defp accepted(state, _) when state.leader != state.name, do: state
|
defp accepted(state, _) when state.leader != state.name, do: state
|
||||||
defp accepted(state, inst) do
|
defp accepted(state, inst) do
|
||||||
or_state state.instmap[inst].accepted >= floor(length(state.processes) / 2) + 1 do
|
or_state state.instmap[inst].accepted >= floor(length(state.processes) / 2) + 1 do
|
||||||
@ -598,9 +588,7 @@ defmodule Paxos do
|
|||||||
propose_loop(inst, t)
|
propose_loop(inst, t)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Waits the right message from the paxos replica
|
||||||
Waits the right message from the paxos replica
|
|
||||||
"""
|
|
||||||
defp propose_loop(inst, t) do
|
defp propose_loop(inst, t) do
|
||||||
receive do
|
receive do
|
||||||
{:timeout, ^inst} -> {:timeout}
|
{:timeout, ^inst} -> {:timeout}
|
||||||
@ -622,9 +610,7 @@ defmodule Paxos do
|
|||||||
get_decision_loop(inst, t)
|
get_decision_loop(inst, t)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Sends waits for the right message from the paxos replica
|
||||||
Sends waits for the right message from the paxos replica
|
|
||||||
"""
|
|
||||||
defp get_decision_loop(inst, t) do
|
defp get_decision_loop(inst, t) do
|
||||||
receive do
|
receive do
|
||||||
{:get_value_res, ^inst, v} ->
|
{:get_value_res, ^inst, v} ->
|
||||||
@ -661,9 +647,7 @@ defmodule Ballot do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Compare the name of 2 processes and select the lowest one
|
||||||
Compare the name of 2 processes and select the lowest one
|
|
||||||
"""
|
|
||||||
defp lexicographical_compare(a, b) do
|
defp lexicographical_compare(a, b) do
|
||||||
cond do
|
cond do
|
||||||
a == b -> 0
|
a == b -> 0
|
||||||
@ -672,9 +656,7 @@ defmodule Ballot do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Callculate the difference between w ballots
|
||||||
Callculate the difference between w ballots
|
|
||||||
"""
|
|
||||||
defp diff({name1, number1}, {name2, number2}) do
|
defp diff({name1, number1}, {name2, number2}) do
|
||||||
diff = number1 - number2
|
diff = number1 - number2
|
||||||
if diff == 0 do
|
if diff == 0 do
|
||||||
@ -701,9 +683,7 @@ defmodule EagerReliableBroadcast do
|
|||||||
require Utils
|
require Utils
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
@doc """
|
# Removes _br from the name of a process
|
||||||
Removes _br from the name of a process
|
|
||||||
"""
|
|
||||||
defp get_non_rb_name(name) do
|
defp get_non_rb_name(name) do
|
||||||
String.to_atom(String.replace(Atom.to_string(name), "_rb", ""))
|
String.to_atom(String.replace(Atom.to_string(name), "_rb", ""))
|
||||||
end
|
end
|
||||||
|
143
lib/server.ex
143
lib/server.ex
@ -121,9 +121,7 @@ defmodule Server do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Checks if the user can play the move before starting the requesting the user to play
|
||||||
Checks if the user can play the move before starting the requesting the user to play
|
|
||||||
"""
|
|
||||||
defp try_to_play_checks(state, game_id, move, pid_to_inform, repeat \\ false) do
|
defp try_to_play_checks(state, game_id, move, pid_to_inform, repeat \\ false) do
|
||||||
cond do
|
cond do
|
||||||
state.games[game_id] == :not_playing_in_game ->
|
state.games[game_id] == :not_playing_in_game ->
|
||||||
@ -165,9 +163,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Tries to propose to paxos the game action
|
||||||
Tries to propose to paxos the game action
|
|
||||||
"""
|
|
||||||
defp try_to_play(state, game_id, move, pid_to_inform) do
|
defp try_to_play(state, game_id, move, pid_to_inform) do
|
||||||
name = state.name
|
name = state.name
|
||||||
|
|
||||||
@ -209,9 +205,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Get the most recent game_state and return it to the player
|
||||||
Get the most recent game_state and return it to the player
|
|
||||||
"""
|
|
||||||
defp get_game_state(state, game_id, pid_to_inform, repeat \\ false) do
|
defp get_game_state(state, game_id, pid_to_inform, repeat \\ false) do
|
||||||
cond do
|
cond do
|
||||||
state.games[game_id] == :not_playing_in_game ->
|
state.games[game_id] == :not_playing_in_game ->
|
||||||
@ -240,9 +234,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# This generates a new hand based on the current game state
|
||||||
This generates a new hand based on the current game state
|
|
||||||
"""
|
|
||||||
defp get_hand_for_game_state(game_state) do
|
defp get_hand_for_game_state(game_state) do
|
||||||
r1 = Enum.random(0..100)
|
r1 = Enum.random(0..100)
|
||||||
cond do
|
cond do
|
||||||
@ -257,9 +249,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# This tries to create a game by sending the create message to paxos
|
||||||
This tries to create a game by sending the create message to paxos
|
|
||||||
"""
|
|
||||||
defp try_to_create_game(state, participants) do
|
defp try_to_create_game(state, participants) do
|
||||||
game_ids = Map.keys(state.games)
|
game_ids = Map.keys(state.games)
|
||||||
latest = Enum.at(Enum.sort(game_ids), length(game_ids) - 1)
|
latest = Enum.at(Enum.sort(game_ids), length(game_ids) - 1)
|
||||||
@ -281,9 +271,8 @@ defmodule Server do
|
|||||||
#
|
#
|
||||||
# Utils
|
# Utils
|
||||||
#
|
#
|
||||||
@doc """
|
|
||||||
Checks if a game has been finished
|
# Checks if a game has been finished
|
||||||
"""
|
|
||||||
defp is_finished(state, game) do
|
defp is_finished(state, game) do
|
||||||
case state.games[game] do
|
case state.games[game] do
|
||||||
{:finished, _} -> true
|
{:finished, _} -> true
|
||||||
@ -291,9 +280,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Gets up to the most recent instance
|
||||||
Gets up to the most recent instance
|
|
||||||
"""
|
|
||||||
defp qurey_status(state) do
|
defp qurey_status(state) do
|
||||||
v = Paxos.get_decision(state.paxos, state.instance, 100)
|
v = Paxos.get_decision(state.paxos, state.instance, 100)
|
||||||
or_state v != nil do
|
or_state v != nil do
|
||||||
@ -302,9 +289,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
# Sets the modified flag
|
||||||
Sets the modified flag
|
|
||||||
"""
|
|
||||||
defp set_modifed(state, game, val \\ false) do
|
defp set_modifed(state, game, val \\ false) do
|
||||||
or_state not is_finished(state, game) and state.games[game] != :not_playing_in_game and state.games[game] != nil do
|
or_state not is_finished(state, game) and state.games[game] != :not_playing_in_game and state.games[game] != nil do
|
||||||
%{state | games: Map.put(state.games, game, %{state.games[game] | modified: val})}
|
%{state | games: Map.put(state.games, game, %{state.games[game] | modified: val})}
|
||||||
@ -315,6 +300,7 @@ defmodule Server do
|
|||||||
# Apply Game States
|
# Apply Game States
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Calculates the index based on the incoming index and a move
|
||||||
defp get_index(indexed_game_state, spos, index) do
|
defp get_index(indexed_game_state, spos, index) do
|
||||||
index = spos + index
|
index = spos + index
|
||||||
len = length(indexed_game_state)
|
len = length(indexed_game_state)
|
||||||
@ -328,6 +314,9 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This funcion looks at pluses and blackholes and chekes if they can be merged
|
||||||
|
# This funcion will pick a state like this [ 1, 2, :+, 2, 1 ] and create a new state [ 1, {:merged, 3} , 1] and then call expand merge which will expand the merge
|
||||||
|
# Note by this point the state is indexed so it looks like [{1, 0}, {:+, 1}, {1, 2}]
|
||||||
defp simplify_game_state_pluses([], indexed_game_state), do: {false, indexed_game_state}
|
defp simplify_game_state_pluses([], indexed_game_state), do: {false, indexed_game_state}
|
||||||
defp simplify_game_state_pluses([{:+, i} | tl], indexed_game_state) do
|
defp simplify_game_state_pluses([{:+, i} | tl], indexed_game_state) do
|
||||||
before_i = get_index(indexed_game_state, i, -1)
|
before_i = get_index(indexed_game_state, i, -1)
|
||||||
@ -345,7 +334,7 @@ defmodule Server do
|
|||||||
list =
|
list =
|
||||||
indexed_game_state |>
|
indexed_game_state |>
|
||||||
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, n + 1}, i}, else: {x, ti} end) |>
|
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, n + 1}, i}, else: {x, ti} end) |>
|
||||||
Enum.filter(fn {x, ti} -> cond do
|
Enum.filter(fn {_, ti} -> cond do
|
||||||
b_i == ti -> false
|
b_i == ti -> false
|
||||||
a_i == ti -> false
|
a_i == ti -> false
|
||||||
true -> true
|
true -> true
|
||||||
@ -376,7 +365,7 @@ defmodule Server do
|
|||||||
list =
|
list =
|
||||||
indexed_game_state |>
|
indexed_game_state |>
|
||||||
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, trunc(:math.floor((a + b) / 2))}, i}, else: {x, ti} end) |>
|
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, trunc(:math.floor((a + b) / 2))}, i}, else: {x, ti} end) |>
|
||||||
Enum.filter(fn {x, ti} -> cond do
|
Enum.filter(fn {_, ti} -> cond do
|
||||||
b_i == ti -> false
|
b_i == ti -> false
|
||||||
a_i == ti -> false
|
a_i == ti -> false
|
||||||
true -> true
|
true -> true
|
||||||
@ -389,6 +378,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check if the item in an game is the substate merged
|
||||||
defp is_merged(item) do
|
defp is_merged(item) do
|
||||||
case item do
|
case item do
|
||||||
{:merged, _} -> true
|
{:merged, _} -> true
|
||||||
@ -396,6 +386,9 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This funcion expands merges
|
||||||
|
# With a state like this [1, {:merged, 3}, 1] it will create a game state like this [ {:merged, 4 } ]
|
||||||
|
# Note by this point the state is indexed so it looks like [{1, 0}, {:+, 1}, {1, 2}]
|
||||||
defp expand_merge(indexed_game_state) do
|
defp expand_merge(indexed_game_state) do
|
||||||
{{:merged, n}, i} = indexed_game_state |> Enum.find(fn {x, _} -> is_merged(x) end)
|
{{:merged, n}, i} = indexed_game_state |> Enum.find(fn {x, _} -> is_merged(x) end)
|
||||||
|
|
||||||
@ -412,7 +405,7 @@ defmodule Server do
|
|||||||
_ ->
|
_ ->
|
||||||
indexed_game_state |>
|
indexed_game_state |>
|
||||||
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, max(n, a) + 1}, i}, else: {x, ti} end) |>
|
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, max(n, a) + 1}, i}, else: {x, ti} end) |>
|
||||||
Enum.filter(fn {x, ti} -> cond do
|
Enum.filter(fn {_, ti} -> cond do
|
||||||
b_i == ti -> false
|
b_i == ti -> false
|
||||||
a_i == ti -> false
|
a_i == ti -> false
|
||||||
true -> true
|
true -> true
|
||||||
@ -429,6 +422,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This funcion removes previous indexes and reindexs the state
|
||||||
defp reindex(list, flat \\ true) do
|
defp reindex(list, flat \\ true) do
|
||||||
list = if flat do
|
list = if flat do
|
||||||
list |> Enum.map(fn {n, _} -> n end)
|
list |> Enum.map(fn {n, _} -> n end)
|
||||||
@ -439,17 +433,13 @@ defmodule Server do
|
|||||||
[list, 0..(length(list) - 1)] |> Enum.zip()
|
[list, 0..(length(list) - 1)] |> Enum.zip()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp remove_merged([], rec, _), do: rec
|
# Removes all merged from the array
|
||||||
|
defp remove_merged(l), do: l |> Enum.map(fn x -> case x do
|
||||||
defp remove_merged([{:merged, n} | tl], rec, add),
|
{:merged, n} -> n
|
||||||
do: if add, do: remove_merged(tl, rec ++ [n], false),
|
x -> x
|
||||||
else: remove_merged(tl, rec, false)
|
end end)
|
||||||
|
|
||||||
defp remove_merged([n | tl], rec, add), do:
|
|
||||||
remove_merged(tl, rec ++ [n], add)
|
|
||||||
|
|
||||||
defp remove_merged(list), do: remove_merged(list, [], true)
|
|
||||||
|
|
||||||
|
# This funcion recieves the game state after the move was done and tries to simplify the game
|
||||||
defp simplify_game_state(game_state) do
|
defp simplify_game_state(game_state) do
|
||||||
indexed_game_state =
|
indexed_game_state =
|
||||||
game_state |>
|
game_state |>
|
||||||
@ -475,6 +465,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This function tries to apply the make_move command
|
||||||
defp apply_game(state, {:make_move, game_id, player_name, pos_move, new_hand}) do
|
defp apply_game(state, {:make_move, game_id, player_name, pos_move, new_hand}) do
|
||||||
game = state.games[game_id]
|
game = state.games[game_id]
|
||||||
case game do
|
case game do
|
||||||
@ -507,6 +498,7 @@ defmodule Server do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This function tries to apply the start_game command
|
||||||
defp apply_game(state, {:start_game, game_id, participants, new_game_state, hand}) do
|
defp apply_game(state, {:start_game, game_id, participants, new_game_state, hand}) do
|
||||||
cond do
|
cond do
|
||||||
state.games[game_id] ->
|
state.games[game_id] ->
|
||||||
@ -535,17 +527,22 @@ defmodule Server do
|
|||||||
# Interface
|
# Interface
|
||||||
############
|
############
|
||||||
|
|
||||||
|
# handles responses from the start game request
|
||||||
create_loop :start_game do
|
create_loop :start_game do
|
||||||
{:start_game_ans, game_id} ->
|
{:start_game_ans, game_id} ->
|
||||||
log("Started a game #{game_id}")
|
log("Started a game #{game_id}")
|
||||||
{:start_game, game_id}
|
{:start_game, game_id}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Requests the server to start a game
|
||||||
|
"""
|
||||||
def start_game(name, participants) do
|
def start_game(name, participants) do
|
||||||
safecast(name, {:start_game, participants, self()})
|
safecast(name, {:start_game, participants, self()})
|
||||||
start_game_loop(nil, 10000)
|
start_game_loop(nil, 10000)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# handles responses from the get game state request
|
||||||
create_loop :get_game_state do
|
create_loop :get_game_state do
|
||||||
{:game_state, ^v, :not_playing} ->
|
{:game_state, ^v, :not_playing} ->
|
||||||
log("Not Playing in that game")
|
log("Not Playing in that game")
|
||||||
@ -561,11 +558,15 @@ defmodule Server do
|
|||||||
{:not_exists}
|
{:not_exists}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Requests the server to get the game state
|
||||||
|
"""
|
||||||
def get_game_state(name, game_id) do
|
def get_game_state(name, game_id) do
|
||||||
safecast(name, {:get_game_state, game_id, self()})
|
safecast(name, {:get_game_state, game_id, self()})
|
||||||
get_game_state_loop(game_id, 10000)
|
get_game_state_loop(game_id, 10000)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# handles responses from the make move request
|
||||||
create_loop :make_move do
|
create_loop :make_move do
|
||||||
{:make_move, ^v, :game_does_not_exist} ->
|
{:make_move, ^v, :game_does_not_exist} ->
|
||||||
log("Got game does not exist")
|
log("Got game does not exist")
|
||||||
@ -587,6 +588,9 @@ defmodule Server do
|
|||||||
{:state, game_state, hand}
|
{:state, game_state, hand}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Requests to make a move
|
||||||
|
"""
|
||||||
def make_move(name, game_id, move) do
|
def make_move(name, game_id, move) do
|
||||||
safecast(name, {:make_move, game_id, move, self()})
|
safecast(name, {:make_move, game_id, move, self()})
|
||||||
make_move_loop(game_id, 10000)
|
make_move_loop(game_id, 10000)
|
||||||
@ -596,22 +600,35 @@ defmodule Server do
|
|||||||
# Debug
|
# Debug
|
||||||
############
|
############
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Quicky creates some servers it's useful in testing
|
||||||
|
"""
|
||||||
def spinup(number_of_participants) do
|
def spinup(number_of_participants) do
|
||||||
procs = Enum.to_list(0..number_of_participants) |> Enum.map(fn n -> :"p#{n}" end)
|
procs = Enum.to_list(1..number_of_participants) |> Enum.map(fn n -> :"p#{n}" end)
|
||||||
Enum.map(procs, fn proc -> Server.start(proc, procs) end)
|
Enum.map(procs, fn proc -> Server.start(proc, procs) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Quicky kills some servers it's useful in testing
|
||||||
|
"""
|
||||||
def kill (pids) do
|
def kill (pids) do
|
||||||
pids |> Enum.map(fn m -> Process.exit(m, :kill) end)
|
pids |> Enum.map(fn m -> Process.exit(m, :kill) end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Client do
|
defmodule Client do
|
||||||
|
@moduledoc """
|
||||||
|
This module handles displaying the game state in a nice to understand
|
||||||
|
way
|
||||||
|
"""
|
||||||
import Utils
|
import Utils
|
||||||
require Utils
|
require Utils
|
||||||
|
|
||||||
create_log 3
|
create_log 3
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
This function plays the displays the game then waits for user input and displays the next state of the game
|
||||||
|
"""
|
||||||
def play_game(process, game_id) do
|
def play_game(process, game_id) do
|
||||||
game = Server.get_game_state(process, game_id)
|
game = Server.get_game_state(process, game_id)
|
||||||
|
|
||||||
@ -675,6 +692,11 @@ defmodule Client do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
This funcion waits for user input and then commands the server to act
|
||||||
|
|
||||||
|
Use the display_game funcion in a seperate terminal to see the game
|
||||||
|
"""
|
||||||
def control_game(process, game_id) do
|
def control_game(process, game_id) do
|
||||||
to_play = IO.gets("Type the number you want to play or q to exit: ")
|
to_play = IO.gets("Type the number you want to play or q to exit: ")
|
||||||
to_play = to_play |> String.trim("\"") |> String.trim()
|
to_play = to_play |> String.trim("\"") |> String.trim()
|
||||||
@ -707,10 +729,15 @@ defmodule Client do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
This funcion tries to display the most recent version of the game
|
||||||
|
|
||||||
|
Use control_game to control game
|
||||||
|
"""
|
||||||
def display_game(process, game_id, temp_game \\ [], temp_hand \\ []) do
|
def display_game(process, game_id, temp_game \\ [], temp_hand \\ []) do
|
||||||
game = Server.get_game_state(process, game_id)
|
game = Server.get_game_state(process, game_id)
|
||||||
|
|
||||||
cont = case game do
|
case game do
|
||||||
:timeout -> log("Could to not comunicate with the server")
|
:timeout -> log("Could to not comunicate with the server")
|
||||||
{:not_exists} -> log("Game does not exist")
|
{:not_exists} -> log("Game does not exist")
|
||||||
{:not_playing} -> log("Not Playing in that game")
|
{:not_playing} -> log("Not Playing in that game")
|
||||||
@ -733,43 +760,49 @@ defmodule Client do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_name(list) when is_list(list), do: list |> Enum.map(fn x -> to_name(x) end)
|
# This funcion recieves some objects and transforms them into user friendly text
|
||||||
def to_name(atom) when atom == :+, do: IO.ANSI.color_background(9) <> IO.ANSI.color(15) <> " + " <> IO.ANSI.reset()
|
defp to_name(list) when is_list(list), do: list |> Enum.map(fn x -> to_name(x) end)
|
||||||
def to_name(atom) when atom == :-, do: IO.ANSI.color_background(21) <> IO.ANSI.color(15) <> " - " <> IO.ANSI.reset()
|
defp to_name(atom) when atom == :+, do: IO.ANSI.color_background(9) <> IO.ANSI.color(15) <> " + " <> IO.ANSI.reset()
|
||||||
def to_name(atom) when atom == :b, do: IO.ANSI.color_background(232) <> IO.ANSI.color(15) <> " b " <> IO.ANSI.reset()
|
defp to_name(atom) when atom == :-, do: IO.ANSI.color_background(21) <> IO.ANSI.color(15) <> " - " <> IO.ANSI.reset()
|
||||||
def to_name(atom) when is_atom(atom), do: atom
|
defp to_name(atom) when atom == :b, do: IO.ANSI.color_background(232) <> IO.ANSI.color(15) <> " b " <> IO.ANSI.reset()
|
||||||
def to_name(i) do
|
defp to_name(atom) when is_atom(atom), do: atom
|
||||||
|
defp to_name(i) do
|
||||||
letter = [ "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" ] |> Enum.at(i - 1)
|
letter = [ "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" ] |> Enum.at(i - 1)
|
||||||
color = [46,45,138,19,11,140,8,47,57,159,72,48,55,35,251,188,107,110,21,11,156,134,128,179,140,234,14,90,206,7,249,209,253,123,192,165,234,136,198,208,43,34,215,127,23,250,177,237,124,202,229,
|
color = [46,45,138,19,11,140,8,47,57,159,72,48,55,35,251,188,107,110,21,11,156,134,128,179,140,234,14,90,206,7,249,209,253,123,192,165,234,136,198,208,43,34,215,127,23,250,177,237,124,202,229,
|
||||||
63,206,220,224,109,202,113,253,7,243,26,160,65,39,112,57,75, 252,82,213,186,68,243,134,100,226,48,90,134,208,102,25,106,72, 242,26,59,166,26,187,54,194,165,97,219,186,130,7,154,233,85, 130,67,43,200,90,60,148,49,161,110,247,116,223,159,132,132] |> Enum.at(i - 1) |> IO.ANSI.color()
|
63,206,220,224,109,202,113,253,7,243,26,160,65,39,112,57,75, 252,82,213,186,68,243,134,100,226,48,90,134,208,102,25,106,72, 242,26,59,166,26,187,54,194,165,97,219,186,130,7,154,233,85, 130,67,43,200,90,60,148,49,161,110,247,116,223,159,132,132] |> Enum.at(i - 1) |> IO.ANSI.color()
|
||||||
color <> String.pad_leading("#{letter}", 3, " ") <> IO.ANSI.reset()
|
color <> String.pad_leading("#{letter}", 3, " ") <> IO.ANSI.reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
def interpolate(list) do
|
# Adds the index as an indicator and pads the index
|
||||||
|
defp interpolate(list) do
|
||||||
[list, 0..length(list) - 1] |>
|
[list, 0..length(list) - 1] |>
|
||||||
Enum.zip() |>
|
Enum.zip() |>
|
||||||
Enum.reduce([], fn {v, i}, acc -> acc ++ [String.pad_leading("#{i}", 3, " "), v] end)
|
Enum.reduce([], fn {v, i}, acc -> acc ++ [String.pad_leading("#{i}", 3, " "), v] end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def grow_empty_list(t, i, acc) when i == 0, do: t ++ acc
|
# This grows a list with between spaces every item of the interpolated list
|
||||||
def grow_empty_list([], i, acc), do: grow_empty_list(acc, i, [])
|
# This allows the items to take as puch space in the possilble
|
||||||
def grow_empty_list([h | tl], i, acc), do:
|
defp grow_empty_list(t, i, acc) when i == 0, do: t ++ acc
|
||||||
|
defp grow_empty_list([], i, acc), do: grow_empty_list(acc, i, [])
|
||||||
|
defp grow_empty_list([h | tl], i, acc), do:
|
||||||
grow_empty_list(tl, i - 1, acc ++ [ h ++ [" "] ])
|
grow_empty_list(tl, i - 1, acc ++ [ h ++ [" "] ])
|
||||||
|
|
||||||
def fill(list) do
|
# This takes the list that was generated in the grow_empty_list function and merges it between every other item
|
||||||
|
defp fill(list) do
|
||||||
to_fill = 32 - length(list)
|
to_fill = 32 - length(list)
|
||||||
to_add = grow_empty_list(Enum.map(list, fn _ -> [] end), to_fill, [])
|
to_add = grow_empty_list(Enum.map(list, fn _ -> [] end), to_fill, [])
|
||||||
fill(list, to_add, [])
|
fill(list, to_add, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill([], _, acc), do: acc
|
defp fill([], _, acc), do: acc
|
||||||
def fill([hd | tail], [add_hd | add_tail], acc) do
|
defp fill([hd | tail], [add_hd | add_tail], acc) do
|
||||||
fill(tail, add_tail ,acc ++ [hd] ++ add_hd)
|
fill(tail, add_tail ,acc ++ [hd] ++ add_hd)
|
||||||
end
|
end
|
||||||
|
|
||||||
def printpt(game_state, hand), do: printpt(game_state, hand, 0)
|
# This functions prints the circle
|
||||||
def printpt(_, _, i) when i > 16, do: nil
|
defp printpt(game_state, hand), do: printpt(game_state, hand, 0)
|
||||||
def printpt(game_state, hand, i) do
|
defp printpt(_, _, i) when i > 16, do: nil
|
||||||
|
defp printpt(game_state, hand, i) do
|
||||||
res = case i do
|
res = case i do
|
||||||
0 ->
|
0 ->
|
||||||
" xxx \n" |>
|
" xxx \n" |>
|
||||||
|
Reference in New Issue
Block a user