This repository has been archived on 2024-01-29. You can view files and clone it, but cannot push or open issues or pull requests.
distributed_system_coursework/lib/server.ex
Andre Henriques 8c674626f8
All checks were successful
continuous-integration/drone/push Build is passing
added some docs
2024-01-19 18:23:08 +00:00

847 lines
27 KiB
Elixir

defmodule ServerMacros do
@moduledoc """
This module defines some helper macros that are used within the server macro
"""
@doc """
This macro creates a wait loop to wait for the messages that are receive match what is inside the
loop
"""
defmacro create_loop(name, do: match_exp) do
function_name = :"#{name}_loop"
ast1 = quote do
{:timeout} -> :timeout
end
ast2 = quote do
value ->
# TODO check spelling
log("Disreguarding: #{inspect(value)}")
# Process.send_after(self(), value, t + 2000)
unquote(function_name)(v, t)
end
ast3 = ast1 ++ match_exp ++ ast2
quote do
defp unquote(function_name)(v, t) do
var!(v) = v
receive do
unquote(ast3)
after
t -> :timeout
end
end
end
end
@doc """
this function tries to propose and on failure it calls the else block and
on success tries to match with the expressions on the do block
"""
defmacro try_propose(val, do: ready, else: recal_do) do
ast1 = quote do
{:timeout} -> unquote(recal_do)
{:abort} -> unquote(recal_do)
end
ast2 = quote do
{:decision, v} ->
var!(state) = apply_game(var!(state), v)
unquote(recal_do)
v ->
raise "Unknown message on try_propose #{inspect(v)}"
end
ast3 = ast1 ++ ready ++ ast2
quote do
v = Paxos.propose(var!(state).paxos, var!(state).instance, unquote(val), 1000)
case v do
unquote(ast3)
end
end
end
end
defmodule Server do
@moduledoc """
Contains the to run the server Code
"""
require ServerMacros
import ServerMacros
require Utils
import Utils
create_log 2
@doc """
Contains the start code for the server
"""
def start(name, participants) do
log("#{name} Starting server")
pid = spawn(Server, :init, [name, participants])
register_name(name, pid, false)
end
@doc """
Initializes the state and starts the paxos inspect and then it calls the run fn
"""
def init(name, participants) do
paxos = Paxos.start(alter_name(name, "_paxos"), Enum.map(participants, fn name -> alter_name(name, "_paxos") end), true)
state = %{
name: name,
procs: participants,
games: %{},
paxos: paxos,
instance: 0,
}
run(state)
end
runfn do
{:start_game, participants, pid_to_inform} ->
or_state is_list(participants) do
{state, game_id} = try_to_create_game(state, participants)
state = set_modifed(state, game_id)
safecast(pid_to_inform, {:start_game_ans, game_id})
state
end
{:get_game_state, game_id, pid_to_inform} ->
state = get_game_state(state, game_id, pid_to_inform)
set_modifed(state, game_id)
{:make_move, game_id, move, pid_to_inform} ->
try_to_play_checks(state, game_id, move, pid_to_inform)
end
@doc """
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
cond do
state.games[game_id] == :not_playing_in_game ->
safecast(pid_to_inform, {:make_move, game_id, :not_playing})
state
state.games[game_id] == nil ->
if repeat do
safecast(pid_to_inform, {:make_move, game_id, :game_does_not_exist})
state
else
state = qurey_status(state)
try_to_play_checks(state, game_id, move, pid_to_inform, true)
end
true ->
state = qurey_status(state)
game = state.games[game_id]
cond do
is_finished(state, game_id) ->
{_, score} = state.games[game_id]
safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
state
game.modified ->
safecast(pid_to_inform, {:make_move, game_id, :player_moved_before, game.game_state, game.hand})
set_modifed(state, game_id)
true ->
cond do
not is_number(move) ->
safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
state
move >= length(game.game_state) ->
safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
state
true ->
try_to_play(state, game_id, move, pid_to_inform)
end
end
end
end
@doc """
Tries to propose to paxos the game action
"""
defp try_to_play(state, game_id, move, pid_to_inform) do
name = state.name
new_hand = if state.games[game_id].hand[state.name] == :- do
Enum.at(state.games[game_id].game_state, move)
else
get_hand_for_game_state(state.games[game_id].game_state)
end
try_propose {:make_move, game_id, name, move, new_hand}
do
{:decision, {:make_move, ^game_id, ^name, ^move, ^new_hand}} ->
state = apply_game(state, {:make_move, game_id, name, move, new_hand})
if is_finished(state, game_id) do
{_, score} = state.games[game_id]
safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
else
game = state.games[game_id]
safecast(pid_to_inform, {:make_move, game_id, game.game_state, game.hand[state.name]})
end
set_modifed(state, game_id)
{:decision, {:make_move, ^game_id, new_name, new_move, new_new_hand}} ->
state = apply_game(state, {:make_move, game_id, new_name, new_move, new_new_hand})
if is_finished(state, game_id) do
{_, score} = state.games[game_id]
safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
else
game = state.games[game_id]
safecast(pid_to_inform, {:make_move, game_id, :player_moved_before, game.game_state, game.hand})
end
set_modifed(state, game_id)
else
try_to_play(state, game_id, move, pid_to_inform)
end
end
@doc """
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
cond do
state.games[game_id] == :not_playing_in_game ->
safecast(pid_to_inform, {:game_state, game_id, :not_playing})
state
state.games[game_id] == nil ->
if repeat do
safecast(pid_to_inform, {:game_state, game_id, :game_does_not_exist})
state
else
state = qurey_status(state)
get_game_state(state, game_id, pid_to_inform, true)
end
true ->
state = qurey_status(state)
if is_finished(state, game_id) do
{_, score} = state.games[game_id]
safecast(pid_to_inform, {:game_state, game_id, :game_finished, score})
else
game = state.games[game_id]
safecast(pid_to_inform, {:game_state, game_id, game.game_state, game.hand[state.name]})
end
state
end
end
@doc """
This generates a new hand based on the current game state
"""
defp get_hand_for_game_state(game_state) do
r1 = Enum.random(0..100)
cond do
r1 <= 1 -> :b
r1 <= 5 -> :-
r1 <= 20 -> :+
true ->
mx = game_state |> Enum.filter(fn m -> m != :+ end) |> Enum.max()
mn = max(mx - 20, 1)
mx = max(mx - 2, 4)
Enum.random(mn..mx)
end
end
@doc """
This tries to create a game by sending the create message to paxos
"""
defp try_to_create_game(state, participants) do
game_ids = Map.keys(state.games)
latest = Enum.at(Enum.sort(game_ids), length(game_ids) - 1)
new_game_id = if latest do latest else 0 end + 1
new_game_state = Enum.to_list(0..Enum.random(3..8)) |> Enum.map(fn _ -> Enum.random(1..4) end)
hand = Enum.reduce(participants, %{}, fn p, acc -> Map.put(acc, p, get_hand_for_game_state(new_game_state)) end)
try_propose {:start_game, new_game_id, participants, new_game_state, hand}
do
{:decision, {:start_game, ^new_game_id, ^participants, ^new_game_state, ^hand}} ->
state = apply_game(state, {:start_game, new_game_id, participants, new_game_state, hand})
{state, new_game_id}
else
try_to_create_game(state, participants)
end
end
#
# Utils
#
@doc """
Checks if a game has been finished
"""
defp is_finished(state, game) do
case state.games[game] do
{:finished, _} -> true
_ -> false
end
end
@doc """
Gets up to the most recent instance
"""
defp qurey_status(state) do
v = Paxos.get_decision(state.paxos, state.instance, 100)
or_state v != nil do
state = apply_game(state, v)
qurey_status(state)
end
end
@doc """
Sets the modified flag
"""
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
%{state | games: Map.put(state.games, game, %{state.games[game] | modified: val})}
end
end
#
# Apply Game States
#
defp get_index(indexed_game_state, spos, index) do
index = spos + index
len = length(indexed_game_state)
cond do
index < 0 ->
len - 1
index >= len ->
rem(index, len)
true ->
index
end
end
defp simplify_game_state_pluses([], indexed_game_state), do: {false, indexed_game_state}
defp simplify_game_state_pluses([{:+, i} | tl], indexed_game_state) do
before_i = get_index(indexed_game_state, i, -1)
after_i = get_index(indexed_game_state, i, 1)
if before_i != after_i do
{b, b_i} = Enum.at(indexed_game_state, before_i)
{a, a_i} = Enum.at(indexed_game_state, after_i)
if b == a do
case b do
:+ -> simplify_game_state_pluses(tl, indexed_game_state)
:b -> simplify_game_state_pluses(tl, indexed_game_state)
n ->
list =
indexed_game_state |>
Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, n + 1}, i}, else: {x, ti} end) |>
Enum.filter(fn {x, ti} -> cond do
b_i == ti -> false
a_i == ti -> false
true -> true
end
end) |>
reindex()
{true, expand_merge(list)}
end
else
simplify_game_state_pluses(tl, indexed_game_state)
end
else
simplify_game_state_pluses(tl, indexed_game_state)
end
end
defp simplify_game_state_pluses([{:b, i} | tl], indexed_game_state) do
before_i = get_index(indexed_game_state, i, -1)
after_i = get_index(indexed_game_state, i, 1)
if before_i != after_i do
{b, b_i} = Enum.at(indexed_game_state, before_i)
{a, a_i} = Enum.at(indexed_game_state, after_i)
a = if is_atom(a) do 1 else a end
b = if is_atom(b) do 1 else b end
list =
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.filter(fn {x, ti} -> cond do
b_i == ti -> false
a_i == ti -> false
true -> true
end
end) |>
reindex()
{true, expand_merge(list)}
else
simplify_game_state_pluses(tl, indexed_game_state)
end
end
defp is_merged(item) do
case item do
{:merged, _} -> true
_ -> false
end
end
defp expand_merge(indexed_game_state) do
{{:merged, n}, i} = indexed_game_state |> Enum.find(fn {x, _} -> is_merged(x) end)
b_i = get_index(indexed_game_state, i, - 1)
a_i = get_index(indexed_game_state, i, + 1)
if b_i != a_i do
{b, b_i} = Enum.at(indexed_game_state, b_i)
{a, a_i} = Enum.at(indexed_game_state, a_i)
if a == b do
case b do
:+ -> indexed_game_state
{:merged, _} -> 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.filter(fn {x, ti} -> cond do
b_i == ti -> false
a_i == ti -> false
true -> true
end
end) |>
reindex() |>
expand_merge()
end
else
indexed_game_state
end
else
indexed_game_state
end
end
defp reindex(list, flat \\ true) do
list = if flat do
list |> Enum.map(fn {n, _} -> n end)
else
list
end
[list, 0..(length(list) - 1)] |> Enum.zip()
end
defp remove_merged([], rec, _), do: rec
defp remove_merged([{:merged, n} | tl], rec, add),
do: if add, do: remove_merged(tl, rec ++ [n], false),
else: remove_merged(tl, rec, false)
defp remove_merged([n | tl], rec, add), do:
remove_merged(tl, rec ++ [n], add)
defp remove_merged(list), do: remove_merged(list, [], true)
defp simplify_game_state(game_state) do
indexed_game_state =
game_state |>
reindex(false)
{repeat, indexed_game_state} =
indexed_game_state |>
Enum.filter(fn x -> case x do
{:+, _} -> true
{:b, _} -> true
_ -> false
end
end) |>
simplify_game_state_pluses(indexed_game_state)
if repeat do
indexed_game_state |>
Enum.map(fn {v, _} -> v end) |>
remove_merged() |>
simplify_game_state()
else
indexed_game_state |> Enum.map(fn {v, _} -> v end)
end
end
defp apply_game(state, {:make_move, game_id, player_name, pos_move, new_hand}) do
game = state.games[game_id]
case game do
{:finished, _} ->
raise "Game already finished"
:not_playing_in_game ->
%{state | instance: state.instance + 1 }
game ->
game_state = game.game_state
game_state = if game.hand[player_name] == :- do
List.delete_at(game_state, pos_move)
else
{b, e} = Enum.split(game_state, pos_move)
b ++ [ game.hand[player_name] ] ++ e
end
game_state = simplify_game_state(game_state)
hand = Map.put(game.hand, player_name, new_hand)
game = %{game| hand: hand, game_state: game_state, modified: true }
if length(game.game_state) > 16 do
%{state| games: Map.put(state.games, game_id, {
:finished,
game.game_state |>
Enum.filter(fn x -> not is_atom(x) end) |>
Enum.sum()}), instance: state.instance + 1
}
else
%{state| games: Map.put(state.games, game_id, game), instance: state.instance + 1}
end
end
end
defp apply_game(state, {:start_game, game_id, participants, new_game_state, hand}) do
cond do
state.games[game_id] ->
raise "Game Already Exists"
state.name in participants ->
%{state |
games: Map.put(state.games, game_id, %{
game_state: new_game_state,
participants: participants,
hand: hand,
modified: true,
}),
instance: state.instance + 1
}
true ->
%{state |
games: Map.put(state.games, game_id, :not_playing_in_game),
instance: state.instance + 1,
}
end
end
defp apply_game(_, _), do: raise "Do not know how to apply game state"
############
# Interface
############
create_loop :start_game do
{:start_game_ans, game_id} ->
log("Started a game #{game_id}")
{:start_game, game_id}
end
def start_game(name, participants) do
safecast(name, {:start_game, participants, self()})
start_game_loop(nil, 10000)
end
create_loop :get_game_state do
{:game_state, ^v, :not_playing} ->
log("Not Playing in that game")
{:not_playing}
{:game_state, ^v, :game_finished, score} ->
log("Game finsihed, #{score}")
{:game_finished, score}
{:game_state, ^v, game_state, hand} ->
log("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}")
{:state, game_state, hand}
{:game_state, ^v, :game_does_not_exist} ->
log("Got game does not exist")
{:not_exists}
end
def get_game_state(name, game_id) do
safecast(name, {:get_game_state, game_id, self()})
get_game_state_loop(game_id, 10000)
end
create_loop :make_move do
{:make_move, ^v, :game_does_not_exist} ->
log("Got game does not exist")
{:not_exists}
{:make_move, ^v, :not_playing} ->
log("Not Playing in that game")
{:not_playing}
{:make_move, ^v, :game_finished, score} ->
log("Game finsihed, #{score}")
{:game_finished, score}
{:make_move, ^v, :player_moved_before, game_state, hand} ->
log("Player moved_before, #{inspect(game_state)} #{inspect(hand)}")
{:player_moved_before, game_state, hand}
{:make_move, ^v, :invalid_move} ->
log("Invalid Move")
{:invalid_move}
{:make_move, ^v, game_state, hand} ->
log("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}")
{:state, game_state, hand}
end
def make_move(name, game_id, move) do
safecast(name, {:make_move, game_id, move, self()})
make_move_loop(game_id, 10000)
end
############
# Debug
############
def spinup(number_of_participants) do
procs = Enum.to_list(0..number_of_participants) |> Enum.map(fn n -> :"p#{n}" end)
Enum.map(procs, fn proc -> Server.start(proc, procs) end)
end
def kill (pids) do
pids |> Enum.map(fn m -> Process.exit(m, :kill) end)
end
end
defmodule Client do
import Utils
require Utils
create_log 3
def play_game(process, game_id) do
game = Server.get_game_state(process, game_id)
cont = case game do
:timeout ->
log("Could to not comunicate with the server")
true
{:not_exists} ->
log("Game does not exist")
true
{:not_playing} ->
log("Not Playing in that game")
true
{:game_finished, score} ->
log("Game finsihed, #{score}")
true
{:game_does_not_exist} ->
log("Got game does not exist")
true
{:state, game_state, hand} ->
game_state |>
to_name() |>
interpolate() |>
fill() |>
printpt(to_name(hand))
false
end
if not cont do
to_play = IO.gets("Type the number you want to play or q to exit: ")
to_play = to_play |> String.trim("\"") |> String.trim()
case to_play do
"q" ->
log("Bye Bye")
v ->
try do
{n, _} = Integer.parse(v)
res = Server.make_move(process, game_id, n)
case res do
:timeout ->
log("Could to not comunicate with the server")
{:not_exists} -> log("Game does not exist")
{:not_playing} -> log("Not Playing in that game")
{:game_finished, score} -> log("Game finsihed, #{score}")
{:game_does_not_exist} -> log("Got game does not exist")
{:player_moved_before, _, _} ->
log("Player Moved before you did please check the map and try again")
play_game(process, game_id)
{:invalid_move} -> raise "Invalid Move"
{:state, _, _} -> play_game(process, game_id)
end
rescue
_ ->
log("Please provide a valid number")
play_game(process, game_id)
end
end
end
end
def control_game(process, game_id) do
to_play = IO.gets("Type the number you want to play or q to exit: ")
to_play = to_play |> String.trim("\"") |> String.trim()
case to_play do
"q" ->
log("Bye Bye")
v ->
try do
{n, _} = Integer.parse(v)
res = Server.make_move(process, game_id, n)
case res do
:timeout -> log("Could to not comunicate with the server")
{:not_exists} -> control_game(process, game_id)
{:not_playing} -> control_game(process, game_id)
{:game_finished, _} -> control_game(process, game_id)
{:player_moved_before, _, _} ->
log("Player Moved before you did please check the map and try again")
control_game(process, game_id)
{:invalid_move} -> raise "Invalid Move"
{:state, _, _} -> control_game(process, game_id)
end
rescue
_ ->
log("Please provide a valid number")
control_game(process, game_id)
end
end
end
def display_game(process, game_id, temp_game \\ [], temp_hand \\ []) do
game = Server.get_game_state(process, game_id)
cont = case game do
:timeout -> log("Could to not comunicate with the server")
{:not_exists} -> log("Game does not exist")
{:not_playing} -> log("Not Playing in that game")
{:game_finished, score} -> log("Game finsihed, #{score}")
{:game_does_not_exist} -> log("Got game does not exist")
{:state, game_state, hand} ->
if temp_game != game_state or temp_hand != hand do
# IO.ANSI.clear()
game_state |>
to_name() |>
interpolate() |>
fill() |>
printpt(to_name(hand))
Process.sleep(1000)
display_game(process, game_id, game_state, hand)
else
Process.sleep(1000)
display_game(process, game_id, temp_game, temp_hand)
end
end
end
def 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(9) <> IO.ANSI.color(15) <> " + " <> IO.ANSI.reset()
def to_name(atom) when atom == :-, do: IO.ANSI.color_background(21) <> 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()
def to_name(atom) when is_atom(atom), do: atom
def 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)
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()
color <> String.pad_leading("#{letter}", 3, " ") <> IO.ANSI.reset()
end
def interpolate(list) do
[list, 0..length(list) - 1] |>
Enum.zip() |>
Enum.reduce([], fn {v, i}, acc -> acc ++ [String.pad_leading("#{i}", 3, " "), v] end)
end
def grow_empty_list(t, i, acc) when i == 0, do: t ++ acc
def grow_empty_list([], i, acc), do: grow_empty_list(acc, i, [])
def grow_empty_list([h | tl], i, acc), do:
grow_empty_list(tl, i - 1, acc ++ [ h ++ [" "] ])
def fill(list) do
to_fill = 32 - length(list)
to_add = grow_empty_list(Enum.map(list, fn _ -> [] end), to_fill, [])
fill(list, to_add, [])
end
def fill([], _, acc), do: acc
def fill([hd | tail], [add_hd | add_tail], acc) do
fill(tail, add_tail ,acc ++ [hd] ++ add_hd)
end
def printpt(game_state, hand), do: printpt(game_state, hand, 0)
def printpt(_, _, i) when i > 16, do: nil
def printpt(game_state, hand, i) do
res = case i do
0 ->
" xxx \n" |>
String.replace("xxx", Enum.at(game_state, 1))
1 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 0)) |>
String.replace("yyy", Enum.at(game_state, 2))
2 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 31)) |>
String.replace("yyy", Enum.at(game_state, 3))
3 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 30)) |>
String.replace("yyy", Enum.at(game_state, 4))
4 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 29)) |>
String.replace("yyy", Enum.at(game_state, 5))
5 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 28)) |>
String.replace("yyy", Enum.at(game_state, 6))
6 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 27)) |>
String.replace("yyy", Enum.at(game_state, 7))
7 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 26)) |>
String.replace("yyy", Enum.at(game_state, 8))
8 ->
" xxx zzz yyy \n" |>
String.replace("xxx", Enum.at(game_state, 25)) |>
String.replace("yyy", Enum.at(game_state, 9)) |>
String.replace("zzz", hand)
9 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 24)) |>
String.replace("yyy", Enum.at(game_state, 10))
10 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 23)) |>
String.replace("yyy", Enum.at(game_state, 11))
11 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 22)) |>
String.replace("yyy", Enum.at(game_state, 12))
12 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 21)) |>
String.replace("yyy", Enum.at(game_state, 13))
13 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 20)) |>
String.replace("yyy", Enum.at(game_state, 14))
14 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 19)) |>
String.replace("yyy", Enum.at(game_state, 15))
15 ->
" xxx yyy \n" |>
String.replace("xxx", Enum.at(game_state, 18)) |>
String.replace("yyy", Enum.at(game_state, 16))
16 ->
" xxx \n" |>
String.replace("xxx", Enum.at(game_state, 17))
end
IO.write("#{res}")
printpt(game_state, hand, i + 1)
end
end