From 2a8dfb787e69dffa1318b48cf63f58bbf31de72b Mon Sep 17 00:00:00 2001 From: Andre Henriques Date: Wed, 17 Jan 2024 19:05:18 +0000 Subject: [PATCH] app finished! --- lib/paxos.ex | 7 +- lib/server.ex | 242 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 198 insertions(+), 51 deletions(-) diff --git a/lib/paxos.ex b/lib/paxos.ex index b69b994..ec001a8 100644 --- a/lib/paxos.ex +++ b/lib/paxos.ex @@ -11,10 +11,7 @@ defmodule Utils do end end - def alter_name(name, part) do - String.to_atom(Atom.to_string(name) <> part) - end - + def alter_name(name, part), do: :"#{name}#{part}" def beb_broadcast(m, dest), do: for(p <- dest, do: safecast(p, m)) def register_name(name, pid, link \\ true) do @@ -46,7 +43,7 @@ defmodule Utils do IO.puts(msg) end end - + defmacro or_state(val, do: expr) do quote do case unquote(val) do diff --git a/lib/server.ex b/lib/server.ex index f65fc4e..4b2cf0d 100644 --- a/lib/server.ex +++ b/lib/server.ex @@ -1,6 +1,6 @@ defmodule ServerMacros do - def create_create_loop(name, do: match_exp, else: process_exp, after: after_exp) do + def create_create_loop(name, do: match_exp, else: process_exp) do function_name = :"#{name}_loop" ast1 = quote do @@ -8,21 +8,14 @@ defmodule ServerMacros do end ast2 = quote do value -> - log("Got unexpected value: #{inspect(value)}") - Process.send_after(self(), value, t + 2000) + # 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 - after_exp = if after_exp == nil do - quote do - t -> :timeout - end - else - after_exp - end - quote do def unquote(function_name)(v, t) do var!(v) = v @@ -30,22 +23,18 @@ defmodule ServerMacros do receive do unquote(ast3) after - unquote(after_exp) + t -> :timeout end end end end def create_create_loop(name, do: exp, else: else_exp) do - create_create_loop(name, do: exp, else: else_exp, after: nil) - end - - def create_create_loop(name, do: exp, after: after_exp) do - create_create_loop(name, do: exp, else: nil, after: after_exp) + create_create_loop(name, do: exp, else: else_exp) end def create_create_loop(name, do: exp) do - create_create_loop(name, do: exp, else: nil, after: nil) + create_create_loop(name, do: exp, else: nil) end defmacro create_loop(name, clauses) do @@ -140,7 +129,7 @@ defmodule Server do state = qurey_status(state) game = state.games[game_id] cond do - is_finished(game, game_id) -> + is_finished(state, game_id) -> {_, score} = state.games[game_id] safecast(pid_to_inform, {:make_move, game_id, :game_finished, score}) state @@ -155,8 +144,7 @@ defmodule Server do def try_to_play(state, game_id, move, pid_to_inform) do name = state.name - # TODO create new hand - new_hand = 2 + new_hand = get_hand_for_game_state(state.games[game_id].game_state) try_propose {:make_move, game_id, name, move, new_hand} do {:decision, {:make_move, ^game_id, ^name, ^move, ^new_hand}} -> @@ -206,20 +194,36 @@ defmodule Server do true -> state = qurey_status(state) - safecast(pid_to_inform, {:game_state, game_id, state.games[game_id].game_state, state.games[game_id].hand[state.name]}) + 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 + def get_hand_for_game_state(game_state) do + r1 = Enum.random(0..100) + if r1 <= 10 do + :+ + else + 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 + def 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 - # TODO: randomize game state - new_game_state = [1, 1] - # TODO: randomize Initial hand value - hand = Enum.reduce(participants, %{}, fn p, acc -> Map.put(acc, p, 1) end) + 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 @@ -252,7 +256,7 @@ defmodule Server do def set_modifed(state, game, val \\ false) do or_state not is_finished(state, game) do - %{state | games: Map.put(state.games, game, %{state.games | modified: val})} + %{state | games: Map.put(state.games, game, %{state.games[game] | modified: val})} end end @@ -260,34 +264,174 @@ defmodule Server do # Apply Game States # + def 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 + + def simplify_game_state_pluses([], indexed_game_state), do: {false, indexed_game_state} + def 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) + 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 + + def is_merged(item) do + case item do + {:merged, _} -> true + _ -> false + end + end + + def 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, 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() |> + expand_merge() + end + else + indexed_game_state + end + else + indexed_game_state + end + end + + def 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 + + def remove_merged([], rec, _), do: rec + + def remove_merged([{:merged, n} | tl], rec, add) do + if add do + remove_merged(tl, rec ++ [n], false) + else + remove_merged(tl, rec, false) + end + end + + def remove_merged([n | tl], rec, add), do: + remove_merged(tl, rec ++ [n], add) + + def remove_merged(list) do + log("#{inspect(list)}") + remove_merged(list, [], true) + end + def simplify_game_state(game_state) do - # TODO actualy do this - game_state + log("game_state: #{inspect(game_state)}") + indexed_game_state = + game_state |> + reindex(false) + + {repeat, indexed_game_state} = + indexed_game_state |> + Enum.filter(fn x -> case x do + {:+, _} -> true + _ -> false + end + end) |> + simplify_game_state_pluses(indexed_game_state) + + log("game_state2: #{inspect(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 def 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 + raise "Game already finished" :not_playing_in_game -> %{state | instance: state.instance + 1 } game -> game_state = game.game_state {b, e} = Enum.split(game_state, pos_move) game_state = b ++ [ game.hand[player_name] ] ++ e - game_state = simplefy_game_state(game_state) + game_state = simplify_game_state(game_state) hand = Map.put(game.hand, player_name, new_hand) game = %{game| hand: hand, game_state: game_state } - # TODO decide if it's ending state - %{state| games: Map.put(state.games, game_id, game)} + + if length(game.game_state) > 15 do + %{state| games: Map.put(state.games, game_id, {:finished, Enum.sum(game.game_state)}), instance: state.instance + 1} + else + %{state| games: Map.put(state.games, game_id, game), instance: state.instance + 1} + end end end def apply_game(state, {:start_game, game_id, participants, new_game_state, hand}) do cond do state.games[game_id] -> - raise :game_already_exists + raise "Game Already Exists" state.name in participants -> %{state | games: Map.put(state.games, game_id, %{ @@ -306,7 +450,7 @@ defmodule Server do end end - def apply_game(_, _), do: raise :do_not_know_how_to_apply_game_state + def apply_game(_, _), do: raise "Do not know how to apply game state" ############ # Interface @@ -320,47 +464,53 @@ defmodule Server do def start_game(name, participants) do safecast(name, {:start_game, participants, self()}) - start_game_loop(nil, 1000) + start_game_loop(nil, 10000) end create_loop :get_game_state do {:game_state, ^v, :not_playing} -> - IO.puts("Not Playing in that game") + log("Not Playing in that game") {:not_playing} {:game_state, ^v, game_state, hand} -> - IO.puts("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}") + log("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}") {:state, game_state, hand} + {: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_does_not_exist} -> - IO.puts("Got 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, 1000) + get_game_state_loop(game_id, 10000) end create_loop :make_move do {:make_move, ^v, :game_does_not_exist} -> - IO.puts("Got game does not exist") + log("Got game does not exist") {:not_exists} {:make_move, ^v, :not_playing} -> - IO.puts("Not Playing in that game") + log("Not Playing in that game") {:not_playing} {:make_move, ^v, :game_finished, score} -> - IO.puts("Game finsihed, #{score}") + log("Game finsihed, #{score}") {:game_finished, score} {:make_move, ^v, :player_moved_before, game_state, hand} -> - IO.puts("Player moved_before, #{inspect(game_state)} #{inspect(hand)}") + log("Player moved_before, #{inspect(game_state)} #{inspect(hand)}") {:player_moved_before, game_state, hand} {:make_move, ^v, game_state, hand} -> - IO.puts("Got game state, #{inspect(game_state)}, hand: #{inspect(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, 1000) + make_move_loop(game_id, 10000) end