Browse Source

Merge branch 'master' of github.com:kakaranet/games

Roman Dayneko 11 years ago
parent
commit
f6c16e5933

+ 66 - 0
apps/server/include/game_state.hrl

@@ -0,0 +1,66 @@
+-define(PLAYER,
+         id              :: pos_integer(), %% Player Id
+         seat_num        :: integer(),
+         user_id         :: binary(),
+         is_bot          :: boolean(),
+         info            :: #'PlayerInfo'{},
+         connected       :: boolean()
+        ).
+
+-define(GAME_STATE, 
+         game_id              :: pos_integer(),
+         table_id             :: pos_integer(),
+         table_name           :: string(),
+         players,             %% The register of table players
+         parent               :: {atom(), pid()},
+         parent_mon_ref       :: reference(),
+         relay                :: pid(),
+         mult_factor          :: integer(),
+         slang_flag           :: boolean(),
+         observer_flag        :: boolean(),
+         tournament_type      :: atom(), %%  standalone | elimination | pointing | lucky
+         speed                :: slow | normal | fast,
+         game_mode            :: standard | color | evenodd | countdown,
+         social_actions_enabled :: boolean(),
+         tournament_table     :: list(), %% [{TurnNum, TurnRes}], TurnRes = [{PlayerId, Points, Status}]
+         % game tree
+         rounds               :: undefined | integer(), %% Not defined for countdown game type
+         tour                 :: undefined | integer(),
+         tours                :: undefined | integer(),
+         cur_round            :: integer(),
+         next_series_confirmation :: yes_exit | no_exit | no,
+         % timeouts
+         turn_timeout         :: integer(),
+         ready_timeout        :: integer(),
+         round_timeout        :: infinity | integer(),
+         set_timeout          :: infinity | integer(),
+         timeout_timer        :: undefined | reference(),
+         timeout_magic        :: term(),
+         round_timer          :: undefined | reference(),
+         set_timer            :: undefined | reference(),
+         wait_list            :: list(),
+         % pause
+         pause_mode           :: disabled | normal,
+         paused_statename     :: atom(), %% For storing a statename before pause
+         paused_timeout_value :: integer(), %% For storing remain timeout value
+         % scoring
+         scoring_state        :: term()).
+
+-record(okey_state, {?GAME_STATE,
+         reveal_confirmation_timeout    :: integer(),
+         reveal_confirmation  :: boolean(),
+         gosterge_finish_allowed :: undefined | boolean(), %% Only defined for countdown game type
+         %% Dynamic parameters
+         desk_rule_pid        :: undefined | pid(),
+         start_seat           :: integer(), %% The player who moves first
+         reveal_confirmation_list :: list(), %% {SeatNum, Answer}
+         desk_state %% OKEY DESK
+        }).
+
+-record(tavla_state, {?GAME_STATE,
+         tables_num           :: integer(), %% For paired mode >= 1, otherwise  = 1
+         %% Dynamic parameters
+         desk_rule_pid        :: undefined | pid(),
+         start_color, %% The player who moves first
+         desk_state %% TAVLA DESK
+        }).

+ 2 - 2
apps/server/src/game_manager.erl

@@ -208,7 +208,7 @@ create_standalone_game(Game, Params, Users) ->
                           {kakush_for_loser, KakushForLoser},
                           {win_game_points, WinGamePoints},
                           {mul_factor, MulFactor},
-                          {table_module, game_okey_ng_table_trn},
+                          {table_module, game_okey_table},
                           {bot_module, game_okey_bot},
                           {bots_replacement_mode, BotsReplacementMode},
                           {table_params, TableParams},
@@ -385,7 +385,7 @@ create_elimination_trn(GameType, Params, Registrants) ->
                          {speed, Speed},
                          {awards, Awards},
                          {trn_id, TrnId},
-                         {table_module, game_okey_ng_table_trn},
+                         {table_module, game_okey_table},
                          {demo_mode, false},
                          {table_params, TableParams}
                         ]);

+ 1 - 1
apps/server/src/gas.erl

@@ -1,7 +1,7 @@
 -module(gas).
 -compile(export_all).
 
-%-define(ALLOWED, [gas,nsg_trn_lucky,game_session,game_manager,game_okey_ng_table_trn]).
+%-define(ALLOWED, [gas,nsg_trn_lucky,game_session,game_manager,game_okey_table]).
 -define(ALLOWED, [gas,auth_server]).
 
 log(Module, String, Args, Fun) ->

+ 1 - 1
apps/server/src/lucky_sup.erl

@@ -40,7 +40,7 @@ init([]) ->
                   {mode, normal}, % Common table for several real players
                   {seats, 4},
 %%%                  {quota_per_round, Quota},
-                  {table_module, game_okey_ng_table_trn},
+                  {table_module, game_okey_table},
                   {bot_module, game_okey_bot},
                   {table_params, OkeyTableParams}
                  ],

+ 1 - 1
apps/server/src/okey/game_okey_ng_desk.erl → apps/server/src/okey/game_okey_desk.erl

@@ -50,7 +50,7 @@
 %%  {reveal, SeatNum, TashPlaces, DicardedTash}
 %%  {gosterge_finish, SeatNum}
 
--module(game_okey_ng_desk).
+-module(game_okey_desk).
 
 -behaviour(gen_fsm).
 %% --------------------------------------------------------------------

+ 0 - 584
apps/server/src/okey/game_okey_ng_scoring.erl

@@ -1,584 +0,0 @@
-%% Author: serge
-%% Created: Oct 23, 2012
-%% Description:
--module(game_okey_ng_scoring).
-
-
-%%
-%% Include files
-%%
--include_lib("eunit/include/eunit.hrl").
--include_lib("server/include/log.hrl").
-
-%%
-%% Exported Functions
-%%
--export([
-         init/3,
-         last_round_result/1,
-         chanak/1,
-         round_finished/6
-        ]).
-
--type normal_tash() :: {integer(), integer()}.
--type tash() :: false_okey | normal_tash().
-
--define(MODE_STANDARD, standard).
--define(MODE_EVENODD, evenodd).
--define(MODE_COLOR, color).
--define(MODE_COUNTDOWN, countdown).
-
--define(COUNTDOWN_MAX_POINTS, 10).
-
--define(ACH_CHANAK_WINNER, 0). %% Not defined in the points matrix
-
--define(ACH_GOSTERGE_SHOWN, 1).
--define(ACH_WIN_REVEAL, 2).
--define(ACH_WIN_REVEAL_WITH_OKEY, 3).
--define(ACH_WIN_REVEAL_WITH_PAIRS, 4).
--define(ACH_WIN_REVEAL_WITH_OKEY_PAIRS, 5).
--define(ACH_8_TASHES, 6).
--define(ACH_WIN_REVEAL_WITH_COLOR, 7).
--define(ACH_WIN_REVEAL_WITH_COLOR_OKEY, 8).
--define(ACH_WIN_REVEAL_WITH_COLOR_PAIRS, 9).
--define(ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS, 10).
--define(ACH_FAIL_REVEAL, 11).
--define(ACH_CAUGHT_BLUFF, 12).
--define(ACH_EMPTY_BOX, 13).
--define(ACH_REJECT_GOOD_HAND, 14).
--define(ACH_GOSTERGE_WINNER, 15).
-
-
--record(state,
-        {mode             :: standard | evenodd | color | countdown,
-         seats_num        :: integer(),
-         rounds_num       :: undefined | pos_integer(),
-         last_round_num   :: integer(),
-         round_score      :: list(),    %% [{SeatNum, DeltaPoints}]
-         round_achs       :: list(),    %% [{SeatNum, [{AchId, Points}]}]
-         total_score      :: list(),    %% [{SeatNum, Points}]
-         finish_info      :: term(),    %% FinishInfo
-         chanak           :: integer()  %% Defined only for evenodd and color mode
-        }).
-
-%%
-%% API Functions
-%%
-
-%% @spec init(Mode, SeatsInfo, RoundsNum) -> ScoringState
-%% @doc Initialises scoring state.
-%% @end
-%% Types:
-%%     Mode = standard | evenodd | color | countdown
-%%     SeatsInfo = [{SeatNum, Points}]
-%%       SeatNum = integer()
-%%       Points = integer()
-%%     RoundsNum = undefined | pos_integer()
-
-init(Mode, SeatsInfo, RoundsNum) ->
-    gas:info(?MODULE,"OKEY_NG_SCORING init Mode: ~p SeatsInfo = ~p RoundsNum = ~p", [Mode, SeatsInfo, RoundsNum]),
-    true = lists:member(Mode, [?MODE_STANDARD, ?MODE_EVENODD, ?MODE_COLOR, ?MODE_COUNTDOWN]),
-    true = if Mode == ?MODE_COUNTDOWN -> RoundsNum == undefined;
-              true -> is_integer(RoundsNum) orelse RoundsNum == undefined end,
-    SeatsNum = length(SeatsInfo),
-    true = lists:seq(1, SeatsNum) == lists:sort([SeatNum || {SeatNum, _} <- SeatsInfo]),
-    #state{mode = Mode,
-           seats_num = SeatsNum,
-           rounds_num = RoundsNum,
-           last_round_num = 0,
-           chanak = 0,
-           round_score = undefined,
-           finish_info = undefined,
-           round_achs = undefined,
-           total_score = SeatsInfo
-          }.
-
-
-%% @spec last_round_result(ScoringState) -> {FinishInfo, RoundScore, AchsPoints, TotalScore} |
-%%                                          no_rounds_played
-%% @end
-%% Types:
-%%     FinishInfo =  tashes_out | timeout | set_timeout |
-%%                   {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} |
-%%                   {fail_reveal, Revealer} |
-%%                   {gosterge_finish, Winner}
-%%     RoundScore = [{SeatNum, DeltaPoints}]
-%%     AchsPoints = [{SeatNum, [{AchId, Points}]}]
-%%     TotalScore = [{SeatNum, Points}]
-
-last_round_result(#state{last_round_num = 0}) -> no_rounds_played;
-
-last_round_result(#state{round_score = RoundScore,
-                         round_achs = RoundAchs,
-                         total_score = TotalScore,
-                         finish_info = FinishInfo
-                        }) ->
-    {FinishInfo, RoundScore, RoundAchs, TotalScore}.
-
-%% @spec chanak(ScoringState) -> Chanak
-%% @end
-%% Types: Chanak = integer()
-
-chanak(#state{chanak = Chanak}) ->
-    Chanak.
-
-%% @spec round_finished(ScoringState, FinishReason, Hands, Gosterge, WhoHasGosterge, Has8Tashes) ->
-%%                                    {NewScoringState, GameIsOver}
-%% @end
-%% Types:
-%%     FinishReason = tashes_out |
-%%                    {reveal, SeatNum, Tashes, Discarded, ConfirmationList} |
-%%                    {gosterge_finish, SeatNum}
-%%       Tashes = [Row1, Row2]
-%%         Row1 = Row2 = [null | tash()]
-%%       Discaded = tash()
-%%       ConfirmationList = [SeatNum, boolean()]
-%%     Hands = [{SeatNum, Hand}]
-%%       Hand = [tash()]
-%%     Gosterge = normal_tash()
-%%     WhoHasGosterge = undefined | SeatNum
-%%     Has8Tahses = [SeatNum]
-%%     GameisOver = boolean() 
-
-round_finished(#state{mode = GameMode, seats_num = SeatsNum,
-                      last_round_num = LastRoundNum,
-                      total_score = TotalScore, chanak = Chanak} = State,
-               FinishReason, Hands, Gosterge, WhoHasGosterge, Has8Tashes) ->
-    ScoringMode = get_scoring_mode(GameMode, Gosterge),
-    PointingRules = get_pointing_rules(ScoringMode),
-    ChanakRefillingPoints = get_chanak_refilling_points(ScoringMode),
-    Seats = lists:seq(1, SeatsNum),
-    FinishInfo = finish_info(GameMode, FinishReason, Gosterge),
-    PlayersAchs = players_achivements(GameMode, Seats, Hands, WhoHasGosterge, Has8Tashes, FinishInfo),
-    {ChanakWinPoints, NewChanak} = calc_chanak(GameMode, WhoHasGosterge, PlayersAchs,
-                                               ChanakRefillingPoints, PointingRules, Chanak),
-    PlayersAchsRecs = [{SeatNum, get_achivements_points(PointingRules, Achivements)}
-                       || {SeatNum, Achivements} <- PlayersAchs],
-    RoundAchs = merge_points(PlayersAchsRecs, ChanakWinPoints),
-    RoundScore = [{SeatNum, sum_achivements_points(AchPoints)}
-                   || {SeatNum, AchPoints} <- RoundAchs],
-    RoundNum = LastRoundNum + 1,
-    NewTotalScore = add_delta(TotalScore, RoundScore),
-    NewState = State#state{last_round_num = RoundNum,
-                           round_score = RoundScore,
-                           total_score = NewTotalScore,
-                           round_achs = RoundAchs,
-                           finish_info = FinishInfo,
-                           chanak = NewChanak},
-    {NewState, detect_game_finish(NewState)}.
-
-%%
-%% Local Functions
-%%
-
-merge_points(PlayersAchsRecs, ChanakWinPoints) ->
-    F = fun({SeatNum, ChanakPoints}, Acc) ->
-                ChanakRec = {?ACH_CHANAK_WINNER, ChanakPoints},
-                case lists:keyfind(SeatNum, 1, Acc) of
-                    {_, AchsRecs} -> lists:keyreplace(SeatNum, 1, Acc,
-                                                      {SeatNum, [ChanakRec | AchsRecs]});
-                    false ->
-                        [{SeatNum, [ChanakRec]}]
-                end
-        end,
-    lists:foldl(F, PlayersAchsRecs, ChanakWinPoints).
-
-%% calc_chanak(GameMode, WhoHasGosterge, PlayersAchs, Chanak) -> {ChanakWinPoints, NewChanak}
-calc_chanak(GameMode, WhoHasGosterge, PlayersAchs, RefillingPoints, PointingRules, Chanak) ->
-    if GameMode == ?MODE_EVENODD;
-       GameMode == ?MODE_COLOR ->
-           GostergePoints = if WhoHasGosterge == undefined ->
-                                   lists:nth(?ACH_GOSTERGE_SHOWN, PointingRules);
-                               true -> 0
-                            end,
-           case find_chanak_winners(PlayersAchs) of
-               [] ->
-                   {[], Chanak + GostergePoints};
-               ChanakWinners ->
-                   WinnersNum = length(ChanakWinners),
-                   PerWinnerPoints = Chanak div WinnersNum,
-                   RestPoints = Chanak rem WinnersNum,
-                   ChanakWinPoints = [{SeatNum, PerWinnerPoints} || SeatNum <- ChanakWinners],
-                   {ChanakWinPoints, RestPoints + GostergePoints + RefillingPoints}
-           end;
-       true -> {[], 0}
-    end.
-
-
-find_chanak_winners(PlayersAchs) ->
-    [SeatNum || {SeatNum, Achs} <- PlayersAchs, is_chanak_winner(Achs)].
-
-
-is_chanak_winner(Achs) ->
-    F = fun(Ach) -> lists:member(Ach, chanak_win_achievements())
-        end,
-    lists:any(F, Achs).
-
-
-detect_game_finish(#state{mode = GameMode, last_round_num = RoundNum, finish_info = FinishInfo,
-                          rounds_num = MaxRoundNum, total_score = TotalScore}) ->
-    if GameMode == ?MODE_COUNTDOWN ->
-           lists:any(fun({_, Points}) -> Points >= ?COUNTDOWN_MAX_POINTS end, TotalScore);
-       FinishInfo == set_timeout ->
-           true;
-       true ->
-           RoundNum == MaxRoundNum
-    end.
-
-
-players_achivements(Mode, Seats, Hands, WhoHasGosterge, Has8Tashes, FinishInfo) ->
-    case FinishInfo of
-        tashes_out ->
-            [begin
-                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats];
-        timeout ->
-            [begin
-                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats];
-        set_timeout ->
-            [begin
-                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats];
-        {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} ->
-            [begin
-                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
-                 Achivements = player_achivements_win_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes,
-                                                             Revealer, WrongRejects, RevealWithOkey,
-                                                             RevealWithPairs, RevealWithColor),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats];
-        {fail_reveal, Revealer} ->
-            [begin
-                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
-                 Achivements = player_achivements_fail_reveal(Mode, SeatNum, WhoHasGosterge,
-                                                              Has8Tashes, Revealer),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats];
-        {gosterge_finish, _Winner} ->
-            [begin
-                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
-                 Achivements = player_achivements_gosterge_finish(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
-                 {SeatNum, Achivements}
-             end || SeatNum <- Seats]
-    end.
-
-
-%% finish_info(GameMode, FinishReason, Gosterge) ->
-%%      tashes_out |
-%%      timeout |
-%%      set_timeout |
-%%      {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} |
-%%      {fail_reveal, Revealer} |
-%%      {gosterge_finish, Winner}
-finish_info(GameMode, FinishReason, Gosterge) ->
-    case FinishReason of
-        tashes_out -> tashes_out;
-        timeout -> timeout;
-        set_timeout -> set_timeout;
-        {reveal, Revealer, Tashes, Discarded, ConfirmationList} ->
-            {RightReveal, RevealWithPairs, WithColor} = check_reveal(Tashes, Gosterge),
-            WinReveal = RightReveal orelse lists:all(fun({_, Answer}) -> Answer == true end, ConfirmationList),
-            if WinReveal ->
-                   RevealWithColor = case GameMode of
-                                         ?MODE_STANDARD -> false;
-                                         ?MODE_EVENODD -> WithColor;
-                                         ?MODE_COLOR -> WithColor;
-                                         ?MODE_COUNTDOWN -> false
-                                     end,
-                   Okey = gosterge_to_okey(Gosterge),
-                   RevealWithOkey = Discarded == Okey,
-                   WrongRejects = if RightReveal ->
-                                         [S || {S, Answer} <- ConfirmationList, Answer==false];
-                                     true -> []
-                                  end,
-                   {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs};
-               true ->
-                   {fail_reveal, Revealer}
-            end;
-        {gosterge_finish, Winner} when GameMode == ?MODE_COUNTDOWN ->
-            {gosterge_finish, Winner}
-    end.
-
-
-%% @spec get_achivements_points(PointingRules, Achivements) -> AchsPoints
-%% @end
-get_achivements_points(PointingRules, Achivements) ->
-    [{Ach, lists:nth(Ach, PointingRules)} || Ach <- Achivements].
-
-%% @spec sum_achivements_points(AchPoints) -> integer()
-%% @end
-sum_achivements_points(AchPoints) ->
-    lists:foldl(fun({_, P}, Acc)-> Acc + P end, 0, AchPoints).
-
-%% @spec add_delta(TotalScore, RoundScores) -> NewTotalScore
-%% @end
-add_delta(TotalScore, RoundScores) ->
-    [{SeatNum, proplists:get_value(SeatNum, TotalScore) + Delta}
-     || {SeatNum, Delta} <- RoundScores].
-
-
-%% @spec gosterge_to_okey(GostergyTash) -> OkeyTash
-%% @end
-gosterge_to_okey({Color, Value}) ->
-    if Value == 13 -> {Color, 1};
-       true -> {Color, Value + 1}
-    end.
-
-%% @spec check_reveal(TashPlaces, Gosterge) -> {RightReveal, WithPairs, SameColor}
-%% @end
-check_reveal([TopRow, BottomRow], Gosterge) ->
-    FlatList = TopRow ++ [null | BottomRow],
-    Okey = gosterge_to_okey(Gosterge),
-    Normalized = [case E of
-                      Okey -> okey;
-                      false_okey -> Okey;
-                       _ -> E
-                  end || E <- FlatList],
-    Sets = split_by_delimiter(null, Normalized),
-    ProperHand = lists:all(fun(S) -> is_set(S) orelse is_run(S) end, Sets),
-    Pairs = lists:all(fun(S) -> is_pair(S) end, Sets),
-    [Color | Rest] = [C || {C, _} <- Normalized],
-    SameColor = lists:all(fun(C) -> C==Color end, Rest),
-    {ProperHand orelse Pairs, Pairs, ProperHand andalso SameColor}.
-
-%% @spec split_by_delimiter(Delimiter, List) -> ListOfList
-%% @end
-split_by_delimiter(Delimiter, Hand) -> split_by_delimiter(Delimiter, Hand, []).
-split_by_delimiter(_, [], Acc) -> lists:reverse(Acc);
-split_by_delimiter(Delimiter, [Delimiter | Hand], Acc) -> split_by_delimiter(Delimiter, Hand, Acc);
-split_by_delimiter(Delimiter, Hand, Acc) ->
-    {L, Rest} = lists:splitwith(fun(X) -> X =/= Delimiter end, Hand),
-    split_by_delimiter(Delimiter, Rest, [L | Acc]).
-
-%% @spec is_set(Set) -> boolean()
-%% @end
-is_set(Set) when
-  length(Set) < 3;
-  length(Set) > 4 -> false;
-is_set(Set) ->
-    Normals = [ X || X <- Set, X =/= okey ],
-    {_, Value} = hd(Normals),
-    SameValue = lists:all(fun({_, V}) -> V == Value end, Normals),
-    UniqColors = length(Normals) == length(lists:usort([C || {C, _} <- Normals])),
-    SameValue andalso UniqColors.
-
-
-%% @spec is_run(Set) -> boolean()
-%% @end
-is_run(Set) when length(Set) < 3 -> false;
-is_run(Set) ->
-    {Okeys, Normals} = lists:partition(fun(X)-> X == okey end, Set),
-    {Color, _} = hd(Normals),
-    {Colors, Values} = lists:unzip(Normals),
-    SameColor = lists:all(fun(C) -> C == Color end, Colors),
-    SortedValues = lists:sort(Values),
-    NormalizedValues = if hd(SortedValues)==1 -> tl(SortedValues) ++ [14]; true -> false end,
-    OkeysNum = length(Okeys),
-    Check1 = check_run(SortedValues, OkeysNum),
-    Check2 = check_run(NormalizedValues, OkeysNum),
-    SameColor andalso (Check1 orelse Check2).
-
-
-check_run(false, _) -> false;
-check_run([First | Rest], OkeysNum) ->
-    check_run(First, Rest, OkeysNum).
-
-
-check_run(Cur, [Cur | _], _OkeysNum) -> false;
-check_run(Cur, [Next | Rest], OkeysNum) when Next == Cur + 1 ->
-    check_run(Cur+1, Rest, OkeysNum);
-check_run(_Cur, [_Next | _Rest], 0) -> false;
-check_run(Cur, [Next | Rest], OkeysNum) ->
-    check_run(Cur+1, [Next | Rest], OkeysNum - 1);
-check_run(_Cur, [], _OkeysNum) -> true.
-
-%% @spec is_pair(Set) -> boolean()
-%% @end
-is_pair([_A, okey]) -> true;
-is_pair([okey, _B]) -> true;
-is_pair([A, A]) -> true;
-is_pair(_) -> false.
-
-
-player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes) ->
-    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, no_winner, undefined,
-                       undefined, undefined, undefined, undefined, undefined).
-
-player_achivements_win_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes,
-                              Revealer, WrongRejects, RevealWithOkey,
-                              RevealWithPairs, RevealWithColor) ->
-    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, reveal,
-                       Revealer, WrongRejects, true, RevealWithOkey,
-                       RevealWithPairs, RevealWithColor).
-
-player_achivements_fail_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes, Revealer) ->
-    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, reveal,
-                       Revealer, [], false, false, false, false).
-
-player_achivements_gosterge_finish(Mode, SeatNum, WhoHasGosterge, Has8Tashes) ->
-    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, gosterge_finish,
-                       undefined, [], false, false, false, false).
-
-%% player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, FinishType, Revealer, WrongRejects,
-%%                    WinReveal, RevealWithOkey, RevealWithPairs, WithColor) -> [{AchId}]
-player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, FinishType, Revealer, WrongRejects,
-                   WinReveal, RevealWithOkey, RevealWithPairs, WithColor) ->
-    L=[
-       %% 1
-       {?ACH_GOSTERGE_SHOWN, SeatNum == WhoHasGosterge},
-       %% 2
-       {?ACH_WIN_REVEAL, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso (not RevealWithPairs) andalso (not WithColor)},
-       %% 3
-       {?ACH_WIN_REVEAL_WITH_OKEY, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso (not RevealWithPairs) andalso (not WithColor)},
-       %% 4
-       {?ACH_WIN_REVEAL_WITH_PAIRS, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso RevealWithPairs andalso (not WithColor)},
-       %% 5
-       {?ACH_WIN_REVEAL_WITH_OKEY_PAIRS, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso RevealWithPairs andalso (not WithColor)},
-       %% 6
-       {?ACH_8_TASHES, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso lists:member(SeatNum, Has8Tashes)},
-       %% 7
-       {?ACH_WIN_REVEAL_WITH_COLOR, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso (not RevealWithPairs) andalso WithColor},
-       %% 8
-       {?ACH_WIN_REVEAL_WITH_COLOR_OKEY, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso (not RevealWithPairs) andalso WithColor},
-       %% 9
-       {?ACH_WIN_REVEAL_WITH_COLOR_PAIRS, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso RevealWithPairs andalso WithColor},
-       %% 10
-       {?ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso RevealWithPairs andalso WithColor},
-       %% 11
-       {?ACH_FAIL_REVEAL, FinishType == reveal andalso SeatNum == Revealer andalso (not WinReveal)},
-       %% 12 AKA others_on_wrong_reveal
-       {?ACH_CAUGHT_BLUFF, FinishType == reveal andalso SeatNum =/= Revealer andalso (not WinReveal)},
-       %% 13
-       {?ACH_EMPTY_BOX, false}, %% XXX: By the last information it is no deduction from players to the chanak
-       %% 14
-       {?ACH_REJECT_GOOD_HAND, FinishType == reveal andalso lists:member(SeatNum, WrongRejects)},
-       %% 15
-       {?ACH_GOSTERGE_WINNER, Mode == ?MODE_COUNTDOWN andalso FinishType == gosterge_finish andalso SeatNum == WhoHasGosterge}
-      ],
-    [Ach || {Ach, true} <- L].
-
-chanak_win_achievements() ->
-    [?ACH_WIN_REVEAL_WITH_OKEY,
-     ?ACH_WIN_REVEAL_WITH_PAIRS,
-     ?ACH_WIN_REVEAL_WITH_OKEY_PAIRS,
-     ?ACH_8_TASHES,
-     ?ACH_WIN_REVEAL_WITH_COLOR,
-     ?ACH_WIN_REVEAL_WITH_COLOR_OKEY,
-     ?ACH_WIN_REVEAL_WITH_COLOR_PAIRS,
-     ?ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS].
-
-get_pointing_rules(ScoringMode) ->
-    {_, Rules} = lists:keyfind(ScoringMode, 1, points_matrix()),
-    Rules.
-
-%% TODO: Check the table
-points_matrix() ->
-    [%%          1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 <--- achievement number
-     {standard, [1,  3,  6,  6, 12,  0,  0,  0,  0,  0, -9,  3,  0, -1,  0]},
-     {odd,      [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  0, -1,  0]},
-     {even,     [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
-     {ybodd,    [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  0, -1,  0]},
-     {ybeven,   [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
-     {rbodd,    [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
-     {rbeven,   [4, 12, 24, 24, 48, 48, 96,192,192,384,-36, 12,  0, -1,  0]},
-     {countdown,[1,  2,  4,  4,  8,  0,  0,  0,  0,  0, -2,  0,  0, -1,  1]}
-    ].
-
-get_chanak_refilling_points(ScoringMode) ->
-    {_, RefillingPoints} = lists:keyfind(ScoringMode, 1, chanak_refilling_table()),
-    RefillingPoints.
-
-chanak_refilling_table() ->
-    [
-     {standard, 0},
-     {odd,      8},
-     {even,     8},
-     {ybodd,   16},
-     {ybeven,  16},
-     {rbodd,   16},
-     {rbeven,  16},
-     {countdown,0}
-    ].
-%%===================================================================
-
-%% @spec get_scoring_mode(GameMode, Gosterge) ->  ScoringMode
-%% @end
-get_scoring_mode(?MODE_STANDARD, _) ->  standard;
-get_scoring_mode(?MODE_COUNTDOWN, _) -> countdown;
-get_scoring_mode(?MODE_COLOR, {Color, Val}) when (Val rem 2) == 0 -> get_scoring_mode_c_even(b2c(Color));
-get_scoring_mode(?MODE_COLOR, {Color, Val}) when (Val rem 2) == 1 -> get_scoring_mode_c_odd(b2c(Color));
-get_scoring_mode(?MODE_EVENODD, {_Color, Val}) when (Val rem 2) == 0 -> even;
-get_scoring_mode(?MODE_EVENODD, {_Color, Val}) when (Val rem 2) == 1 -> odd.
-
-get_scoring_mode_c_odd(C) when C == yellow; C == blue -> ybodd;
-get_scoring_mode_c_odd(C) when C == black; C == red -> rbodd.
-get_scoring_mode_c_even(C) when C == yellow; C == blue -> ybeven;
-get_scoring_mode_c_even(C) when C == black; C == red -> rbeven.
-
-b2c(1) -> red;
-b2c(2) -> blue;
-b2c(3) -> yellow;
-b2c(4) -> black.
-
-
-%% Tests
-test_test_() ->
-    [{"is_pair",
-      [?_assertEqual(true,  is_pair([{1,3}, {1,3}])),
-       ?_assertEqual(true,  is_pair([{4,13}, {4,13}])),
-       ?_assertEqual(false, is_pair([{4,12}, {4,13}])),
-       ?_assertEqual(false, is_pair([{1,1}, {4,8}])),
-       ?_assertEqual(true,  is_pair([okey, {3,8}])),
-       ?_assertEqual(true,  is_pair([{2,3}, okey])),
-       ?_assertEqual(true,  is_pair([okey, okey])),
-       ?_assertEqual(false, is_pair([{2,4}, {4,2}, {3,3}])),
-       ?_assertEqual(false, is_pair([{2,4}])),
-       ?_assertEqual(false, is_pair([okey])),
-       ?_assertEqual(false, is_pair([okey, okey, {2,6}]))
-      ]},
-     {"is_set",
-      [?_assertEqual(true,  is_set([{1,3}, {3,3}, {2,3}])),
-       ?_assertEqual(true,  is_set([{4,8}, okey, {2,8}])),
-       ?_assertEqual(true,  is_set([{4,3}, okey, okey])),
-       ?_assertEqual(true,  is_set([{4,13}, {1,13}, {3,13}, {2,13}])),
-       ?_assertEqual(true,  is_set([okey, {1,13}, {3,13}, {2,13}])),
-       ?_assertEqual(true,  is_set([okey, okey, {3,13}, {2,13}])),
-       ?_assertEqual(false, is_set([{2,6}])),
-       ?_assertEqual(false, is_set([okey])),
-       ?_assertEqual(false, is_set([{3,4}, {2,6}])),
-       ?_assertEqual(false, is_set([{2,3}, {4,3}])),
-       ?_assertEqual(false, is_set([okey, okey])),
-       ?_assertEqual(false, is_set([{3,4}, {1,4}, {3,4}])),
-       ?_assertEqual(false, is_set([{3,4}, {1,4}, {2,5}])),
-       ?_assertEqual(false, is_set([{3,4}, okey, {2,5}])),
-       ?_assertEqual(false, is_set([{2,5}, {3,5}, {4,5}, {1,6}])),
-       ?_assertEqual(false, is_set([{2,5}, {3,5}, {4,5}, {2,5}])),
-       ?_assertEqual(false, is_set([{2,3}, {3,3}, {4,3}, {1,3}, {3,1}])),
-       ?_assertEqual(false, is_set([{2,3}, okey, {4,3}, {1,3}, {3,3}]))
-      ]},
-     {"is_run",
-      [?_assertEqual(false, is_run([{1,3}])),
-       ?_assertEqual(false, is_run([okey])),
-       ?_assertEqual(false, is_run([{2,2}, {2,3}])),
-       ?_assertEqual(false, is_run([okey, {2,3}])),
-       ?_assertEqual(false, is_run([{4,1}, {2,3}])),
-       ?_assertEqual(true,  is_run([{4,4}, {4,6}, {4,5}])),
-       ?_assertEqual(true,  is_run([okey, {4,6}, {4,5}])),
-       ?_assertEqual(true,  is_run([okey, {4,6}, okey])),
-       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,13}])),
-       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,11}, {1,13}])),
-       ?_assertEqual(true,  is_run([{2,4}, {2,6}, {2,7}, {2,5}])),
-       ?_assertEqual(false, is_run([{2,4}, {1,6}, {2,7}, {2,5}])),
-       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,11}, {1,10}, {1,13}])),
-       ?_assertEqual(false, is_run([{1,12}, {1,1}, {1,11}, {1,11}, {1,13}])),
-       ?_assertEqual(false, is_run([{1,12}, {1,1}, {1,2}, {1,11}, {1,13}])),
-       ?_assertEqual(true,  is_run([{3,6}, {3,8}, okey, {3,5}, okey])),
-       ?_assertEqual(true,  is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}])),
-       ?_assertEqual(false, is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}, {3,2}])),
-       ?_assertEqual(false, is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}, {1,2}]))
-      ]}
-    ].

+ 553 - 1114
apps/server/src/okey/game_okey_scoring.erl

@@ -1,1145 +1,584 @@
-%%----------------------------------------------------------------------
-%% @author Paul Peregud <paulperegud@gmail.com>
-%% @copyright Paynet Internet ve Bilisim Hizmetleri A.S. All Rights Reserved.
-%% @doc
-%% Scoring for game_okey. Implemented in different thread to isolate complexity.
-%% @end
-%%-------------------------------------------------------------------
+%% Author: serge
+%% Created: Oct 23, 2012
+%% Description:
 -module(game_okey_scoring).
--behaviour(gen_server).
 
--export([start_link/1]).
--export([standings/1]).
--export([add_event/3]).
--export([get_chanak_points/1, show_gosterge/4, stats/2, finish_round/4, finish_round/7]).
--export([replace_uid/3, reset_scoring/1, new_chanak/1, set_chanak_points/2]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
-         terminate/2, code_change/3]).
 
--include_lib("server/include/log.hrl").
--include_lib("server/include/requests.hrl").
--include_lib("server/include/game_okey.hrl").
--include_lib("server/include/basic_types.hrl").
--include_lib("db/include/config.hrl").
--include_lib("db/include/scoring.hrl").
+%%
+%% Include files
+%%
 -include_lib("eunit/include/eunit.hrl").
--include_lib("server/include/game_tavla.hrl").
-
-
-
--record(okey_stats_entry, {
-          uid   :: integer(),
-          % move_no   :: integer(),
-          reason    :: atom()
-         }).
--record(state, {
-          %% table data
-          game_pid    :: pid(),
-          relay_pid   :: pid(),
-          mode        :: atom(), %% standard | oddeven | color | countdown
-          %% set statistics
-          game_no = 0               :: integer(),
-          chanak = 0                :: integer(),
-          set = []                  :: list(tuple('PlayerId()', integer())),
-          round_max                 :: integer(),
-          round_cur                 :: integer(),
-          set_max                   :: integer(),
-          set_cur                   :: integer(),
-          history = []              :: list(#'OkeyGameResults'{}),
-
-          %% round statistics
-          stats = []                :: list(list(#okey_stats_entry{})),
-          gosterge_shown = false    :: boolean(),
-          gosterge_finish = false   :: boolean(),
-
-          game_info = []            :: [{atom(), any()}]
-         }).
-
-%%%===================================================================
-%%% API
-%%%===================================================================
-
-%%--------------------------------------------------------------------
-%% @doc
-%% Starts the server
+-include_lib("server/include/log.hrl").
+
 %%
-%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
-%% @end
-%%--------------------------------------------------------------------
-start_link(Params) ->
-    gen_server:start_link(?MODULE, [Params], []).
+%% Exported Functions
+%%
+-export([
+         init/3,
+         last_round_result/1,
+         chanak/1,
+         round_finished/6
+        ]).
+
+-type normal_tash() :: {integer(), integer()}.
+-type tash() :: false_okey | normal_tash().
+
+-define(MODE_STANDARD, standard).
+-define(MODE_EVENODD, evenodd).
+-define(MODE_COLOR, color).
+-define(MODE_COUNTDOWN, countdown).
+
+-define(COUNTDOWN_MAX_POINTS, 10).
+
+-define(ACH_CHANAK_WINNER, 0). %% Not defined in the points matrix
+
+-define(ACH_GOSTERGE_SHOWN, 1).
+-define(ACH_WIN_REVEAL, 2).
+-define(ACH_WIN_REVEAL_WITH_OKEY, 3).
+-define(ACH_WIN_REVEAL_WITH_PAIRS, 4).
+-define(ACH_WIN_REVEAL_WITH_OKEY_PAIRS, 5).
+-define(ACH_8_TASHES, 6).
+-define(ACH_WIN_REVEAL_WITH_COLOR, 7).
+-define(ACH_WIN_REVEAL_WITH_COLOR_OKEY, 8).
+-define(ACH_WIN_REVEAL_WITH_COLOR_PAIRS, 9).
+-define(ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS, 10).
+-define(ACH_FAIL_REVEAL, 11).
+-define(ACH_CAUGHT_BLUFF, 12).
+-define(ACH_EMPTY_BOX, 13).
+-define(ACH_REJECT_GOOD_HAND, 14).
+-define(ACH_GOSTERGE_WINNER, 15).
+
+
+-record(state,
+        {mode             :: standard | evenodd | color | countdown,
+         seats_num        :: integer(),
+         rounds_num       :: undefined | pos_integer(),
+         last_round_num   :: integer(),
+         round_score      :: list(),    %% [{SeatNum, DeltaPoints}]
+         round_achs       :: list(),    %% [{SeatNum, [{AchId, Points}]}]
+         total_score      :: list(),    %% [{SeatNum, Points}]
+         finish_info      :: term(),    %% FinishInfo
+         chanak           :: integer()  %% Defined only for evenodd and color mode
+        }).
 
-add_event(Srv, UId, Reason) ->
-    gen_server:cast(Srv, {add_event, UId, Reason}).
+%%
+%% API Functions
+%%
 
-reset_scoring(Srv) ->
-    gen_server:cast(Srv, reset_scoring).
+%% @spec init(Mode, SeatsInfo, RoundsNum) -> ScoringState
+%% @doc Initialises scoring state.
+%% @end
+%% Types:
+%%     Mode = standard | evenodd | color | countdown
+%%     SeatsInfo = [{SeatNum, Points}]
+%%       SeatNum = integer()
+%%       Points = integer()
+%%     RoundsNum = undefined | pos_integer()
+
+init(Mode, SeatsInfo, RoundsNum) ->
+    gas:info(?MODULE,"OKEY_NG_SCORING init Mode: ~p SeatsInfo = ~p RoundsNum = ~p", [Mode, SeatsInfo, RoundsNum]),
+    true = lists:member(Mode, [?MODE_STANDARD, ?MODE_EVENODD, ?MODE_COLOR, ?MODE_COUNTDOWN]),
+    true = if Mode == ?MODE_COUNTDOWN -> RoundsNum == undefined;
+              true -> is_integer(RoundsNum) orelse RoundsNum == undefined end,
+    SeatsNum = length(SeatsInfo),
+    true = lists:seq(1, SeatsNum) == lists:sort([SeatNum || {SeatNum, _} <- SeatsInfo]),
+    #state{mode = Mode,
+           seats_num = SeatsNum,
+           rounds_num = RoundsNum,
+           last_round_num = 0,
+           chanak = 0,
+           round_score = undefined,
+           finish_info = undefined,
+           round_achs = undefined,
+           total_score = SeatsInfo
+          }.
+
+
+%% @spec last_round_result(ScoringState) -> {FinishInfo, RoundScore, AchsPoints, TotalScore} |
+%%                                          no_rounds_played
+%% @end
+%% Types:
+%%     FinishInfo =  tashes_out | timeout | set_timeout |
+%%                   {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} |
+%%                   {fail_reveal, Revealer} |
+%%                   {gosterge_finish, Winner}
+%%     RoundScore = [{SeatNum, DeltaPoints}]
+%%     AchsPoints = [{SeatNum, [{AchId, Points}]}]
+%%     TotalScore = [{SeatNum, Points}]
+
+last_round_result(#state{last_round_num = 0}) -> no_rounds_played;
+
+last_round_result(#state{round_score = RoundScore,
+                         round_achs = RoundAchs,
+                         total_score = TotalScore,
+                         finish_info = FinishInfo
+                        }) ->
+    {FinishInfo, RoundScore, RoundAchs, TotalScore}.
+
+%% @spec chanak(ScoringState) -> Chanak
+%% @end
+%% Types: Chanak = integer()
 
-replace_uid(Srv, UId1, UId2) ->
-    gen_server:cast(Srv, {replace_uid, UId1, UId2}).
+chanak(#state{chanak = Chanak}) ->
+    Chanak.
 
-show_gosterge(Srv, Uid, Gosterge, Hand) ->
-    gen_server:call(Srv, {show_gosterge, Uid, Gosterge, Hand}).
--spec finish_round(pid(), #'OkeyGameResults'{}, [{'PlayerId'(),[#'OkeyPiece'{}|null]}], #'OkeyPiece'{}) -> #'OkeyGameResults'{}.
-finish_round(Srv, Results, Hands, Gosterge) ->
-    finish_round(Srv, Results, Hands, Gosterge, undefined, undefined, undefined).
--spec finish_round(pid(), #'OkeyGameResults'{}, [{'PlayerId'(),[#'OkeyPiece'{}|null]}], #'OkeyPiece'{},
-                  'PlayerId'()|undefined, #'OkeyPiece'{}|undefined, [#'OkeyPiece'{}|null]|undefined) -> #'OkeyGameResults'{}.
-finish_round(Srv, Results, Hands, Gosterge, Winner, LastRevealTash, RevealHand) ->
-    gen_server:call(Srv, {finish_round, Results, Hands, Gosterge, Winner, LastRevealTash, RevealHand}).
+%% @spec round_finished(ScoringState, FinishReason, Hands, Gosterge, WhoHasGosterge, Has8Tashes) ->
+%%                                    {NewScoringState, GameIsOver}
+%% @end
+%% Types:
+%%     FinishReason = tashes_out |
+%%                    {reveal, SeatNum, Tashes, Discarded, ConfirmationList} |
+%%                    {gosterge_finish, SeatNum}
+%%       Tashes = [Row1, Row2]
+%%         Row1 = Row2 = [null | tash()]
+%%       Discaded = tash()
+%%       ConfirmationList = [SeatNum, boolean()]
+%%     Hands = [{SeatNum, Hand}]
+%%       Hand = [tash()]
+%%     Gosterge = normal_tash()
+%%     WhoHasGosterge = undefined | SeatNum
+%%     Has8Tahses = [SeatNum]
+%%     GameisOver = boolean() 
+
+round_finished(#state{mode = GameMode, seats_num = SeatsNum,
+                      last_round_num = LastRoundNum,
+                      total_score = TotalScore, chanak = Chanak} = State,
+               FinishReason, Hands, Gosterge, WhoHasGosterge, Has8Tashes) ->
+    ScoringMode = get_scoring_mode(GameMode, Gosterge),
+    PointingRules = get_pointing_rules(ScoringMode),
+    ChanakRefillingPoints = get_chanak_refilling_points(ScoringMode),
+    Seats = lists:seq(1, SeatsNum),
+    FinishInfo = finish_info(GameMode, FinishReason, Gosterge),
+    PlayersAchs = players_achivements(GameMode, Seats, Hands, WhoHasGosterge, Has8Tashes, FinishInfo),
+    {ChanakWinPoints, NewChanak} = calc_chanak(GameMode, WhoHasGosterge, PlayersAchs,
+                                               ChanakRefillingPoints, PointingRules, Chanak),
+    PlayersAchsRecs = [{SeatNum, get_achivements_points(PointingRules, Achivements)}
+                       || {SeatNum, Achivements} <- PlayersAchs],
+    RoundAchs = merge_points(PlayersAchsRecs, ChanakWinPoints),
+    RoundScore = [{SeatNum, sum_achivements_points(AchPoints)}
+                   || {SeatNum, AchPoints} <- RoundAchs],
+    RoundNum = LastRoundNum + 1,
+    NewTotalScore = add_delta(TotalScore, RoundScore),
+    NewState = State#state{last_round_num = RoundNum,
+                           round_score = RoundScore,
+                           total_score = NewTotalScore,
+                           round_achs = RoundAchs,
+                           finish_info = FinishInfo,
+                           chanak = NewChanak},
+    {NewState, detect_game_finish(NewState)}.
 
-get_chanak_points(Srv) ->
-    gen_server:call(Srv, get_chanak_points).
+%%
+%% Local Functions
+%%
 
-set_chanak_points(Srv, Points) ->
-    gen_server:call(Srv, {set_chanak_points, Points}).
+merge_points(PlayersAchsRecs, ChanakWinPoints) ->
+    F = fun({SeatNum, ChanakPoints}, Acc) ->
+                ChanakRec = {?ACH_CHANAK_WINNER, ChanakPoints},
+                case lists:keyfind(SeatNum, 1, Acc) of
+                    {_, AchsRecs} -> lists:keyreplace(SeatNum, 1, Acc,
+                                                      {SeatNum, [ChanakRec | AchsRecs]});
+                    false ->
+                        [{SeatNum, [ChanakRec]}]
+                end
+        end,
+    lists:foldl(F, PlayersAchsRecs, ChanakWinPoints).
+
+%% calc_chanak(GameMode, WhoHasGosterge, PlayersAchs, Chanak) -> {ChanakWinPoints, NewChanak}
+calc_chanak(GameMode, WhoHasGosterge, PlayersAchs, RefillingPoints, PointingRules, Chanak) ->
+    if GameMode == ?MODE_EVENODD;
+       GameMode == ?MODE_COLOR ->
+           GostergePoints = if WhoHasGosterge == undefined ->
+                                   lists:nth(?ACH_GOSTERGE_SHOWN, PointingRules);
+                               true -> 0
+                            end,
+           case find_chanak_winners(PlayersAchs) of
+               [] ->
+                   {[], Chanak + GostergePoints};
+               ChanakWinners ->
+                   WinnersNum = length(ChanakWinners),
+                   PerWinnerPoints = Chanak div WinnersNum,
+                   RestPoints = Chanak rem WinnersNum,
+                   ChanakWinPoints = [{SeatNum, PerWinnerPoints} || SeatNum <- ChanakWinners],
+                   {ChanakWinPoints, RestPoints + GostergePoints + RefillingPoints}
+           end;
+       true -> {[], 0}
+    end.
 
-standings(#'OkeyGameResults'{} = R) ->
-    standings0(R).
 
-stats(Srv, Uid) ->
-    gen_server:call(Srv, {stats, Uid}).
+find_chanak_winners(PlayersAchs) ->
+    [SeatNum || {SeatNum, Achs} <- PlayersAchs, is_chanak_winner(Achs)].
 
-%%%===================================================================
-%%% gen_server callbacks
-%%%===================================================================
 
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Initiates the server
-%%
-%% @spec init(Args) -> {ok, State} |
-%%                     {ok, State, Timeout} |
-%%                     ignore |
-%%                     {stop, Reason}
-%% @end
-%%--------------------------------------------------------------------
-init([Params]) ->
-    gas:info(?MODULE,"Scoring Params: ~p", [Params]),
-    Mode = proplists:get_value(game_mode, Params),
-    {is_mode, true} = {is_mode, is_mode(Mode)},
-    Sets = proplists:get_value(sets, Params),
-    Rounds = proplists:get_value(rounds, Params),
-    GF = proplists:get_value(gosterge_finish, Params),
-    %% game info filled in game_okey:init/1
-    GameInfo = proplists:get_value(game_info, Params),
-    game_stats:charge_quota(GameInfo),
-    {ok, #state{mode = Mode, gosterge_finish = GF, set_max = Sets,
-                set_cur = 1, round_max = Rounds, round_cur = 1,
-                game_info = GameInfo}}.
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling call messages
-%%
-%% @spec handle_call(Request, From, State) ->
-%%                                   {reply, Reply, State} |
-%%                                   {reply, Reply, State, Timeout} |
-%%                                   {noreply, State} |
-%%                                   {noreply, State, Timeout} |
-%%                                   {stop, Reason, Reply, State} |
-%%                                   {stop, Reason, State}
-%% @end
-%%--------------------------------------------------------------------
-handle_call({show_gosterge, UId, Gosterge, Hand}, _From, #state{gosterge_shown = false} = State) ->
-    #state{stats = Stats, mode = Mode, gosterge_finish = GF} = State,
-    case {lists:member(Gosterge, Hand), Mode, GF} of
-        {true, countdown, true} ->
-            Stats2 = [#okey_stats_entry{uid = UId,
-                                        reason = gosterge} | Stats],
-            {[gosterge], GP} = score_single([gosterge], achievements(countdown, with_scores)),
-            Best = get_last_score(UId, State) + GP,
-            case Best of
-                X when X >= 10 ->
-                    Res0 = hd(State#state.history),
-                    {RoundRes, GameRes} = countdown_points_reset0(UId, Res0),
-                    {reply, {true, RoundRes, GameRes},
-                     State#state{gosterge_shown = true, stats = Stats2}};
-                _ ->
-                    {reply, true, State#state{gosterge_shown = true, stats = Stats2}}
-            end;
-        {true, _, _} ->
-            Stats2 = [#okey_stats_entry{uid = UId,
-                                        reason = gosterge} | Stats],
-            {reply, true, State#state{gosterge_shown = true, stats = Stats2}};
-        {false, _, _} ->
-            {reply, false, State}
-    end;
-handle_call({show_gosterge, _Uid, _Gosterge, _Hand}, _From, #state{gosterge_shown = true} = State) ->
-    {reply, false, State};
-
-handle_call({finish_round, _, _, _, _, _, _} = Msg, From, State = #state{set = []}) ->
-    Value = State#state.round_cur,
-    Stats = State#state.stats,
-    Counters = [ {UId, Value} || #okey_stats_entry{uid = UId, reason = Reason} <- Stats, Reason == started ],
-    true = (4 =:= length(Counters)),
-    State1 = State#state{set = Counters},
-    handle_call(Msg, From, State1);
-handle_call({finish_round, #'OkeyGameResults'{} = Res0, Hands, Gosterge, undefined, undefined, undefined}, _From, State0) ->
-    #state{mode = Mode0, stats = Stats0} = State0,
-    Mode = game_okey:get_scoring_mode(Mode0, Gosterge),
-    gas:info(?MODULE,"scoring mode: ~p", [Mode]),
-    {State1, Stats1} = refill_chanak(Mode, State0, Stats0),
-    gas:info(?MODULE,"chanak value: ~p", [State1#state.chanak]),
-    Stats = check_hands_for_8_tashes(Hands, Stats1),
-    common_round_finish(Stats, Res0, Mode, State1);
-handle_call({finish_round, #'OkeyGameResults'{} = Res0, Hands, Gosterge, Winners, LastRevealTash, RevealHand}, _From, State) ->
-    #state{mode = Mode0, stats = Stats0} = State,
-    Mode = game_okey:get_scoring_mode(Mode0, Gosterge),
-    gas:info(?MODULE,"scoring mode: ~p", [Mode]),
-    {State1, Stats1} = refill_chanak(Mode, State, Stats0),
-    gas:info(?MODULE,"chanak value: ~p", [State1#state.chanak]),
-    Stats = check_hands_for_8_tashes(Hands, Stats1),
-    [ gas:info(?MODULE,"Player ~p ended with ~p", [binary_to_list(Player),Ending]) || {_,Player,Ending}<-Stats],
-    Okey = game_okey:get_okey(Gosterge),
-    Stats2 = case Winners of
-                 [Winner] ->
-                     A = if_list(with_okey(Okey, LastRevealTash),
-                                 #okey_stats_entry{uid = Winner#okey_player.player_id, reason = okey}),
-                     B = if_list(with_even_tashes(Gosterge, RevealHand),
-                                 #okey_stats_entry{uid = Winner#okey_player.player_id, reason = even}),
-                     C = if_list(with_color(Gosterge, RevealHand),
-                                 #okey_stats_entry{uid = Winner#okey_player.player_id, reason = color}),
-                     gas:info(?MODULE,"A: ~p, B: ~p, C: ~p", [A, B, C]),
-                     lists:flatten([A, B, C| Stats]);
-                 _ ->
-                     Stats
-             end,
-    common_round_finish(Stats2, Res0, Mode, State1);
-handle_call(get_chanak_points, _From, State) ->
-    {reply, State#state.chanak, State};
-handle_call({set_chanak_points, Points}, _From, State) ->
-    {reply, ok, State#state{chanak = Points}};
-handle_call(_Request, _From, State) ->
-    {reply, {error, {unknown_call, _Request}}, State}.
-
-
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling cast messages
-%%
-%% @spec handle_cast(Msg, State) -> {noreply, State} |
-%%                                  {noreply, State, Timeout} |
-%%                                  {stop, Reason, State}
-%% @end
-%%--------------------------------------------------------------------
-handle_cast({add_event, UId, Reason}, State) ->
-    #state{stats = Stats} = State,
-    Stats2 = [#okey_stats_entry{uid = UId, reason = Reason} | Stats],
-    {noreply, State#state{stats = Stats2}};
-
-handle_cast({replace_uid, UId1, UId2}, State) ->
-    #state{stats = Stats, history = History} = State,
-    Stats2 = lists:map(fun
-                           (#okey_stats_entry{uid = UId} = X) when UId == UId1 ->
-                               X#okey_stats_entry{uid = UId2};
-                           (X) ->
-                               X
-                       end, Stats),
-    Set2 = lists:map(fun
-                          ({UId, Val}) when UId == UId1 ->
-                              {UId2, Val};
-                          (X) ->
-                              X
-                      end, Stats),
-    F2 = fun
-             (#'OkeyGameR'{player_id = X} = R) when X == UId1 ->
-                 R#'OkeyGameR'{player_id = UId2};
-             (R) ->
-                 R
-         end,
-    F1 = fun(#'OkeyGameResults'{results = RRs} = R) ->
-                 RRs2 = lists:map(F2, RRs),
-                 R#'OkeyGameResults'{results = RRs2}
-         end,
-    History2 = lists:map(F1, History),
-    {noreply, State#state{stats = Stats2, set = Set2, history = History2}};
-
-handle_cast(update_to_next_round_or_set, State) ->
-    {noreply, update_to_next_round_or_set(State)};
-
-handle_cast(reset_scoring, State) ->
-    {noreply, reset_scoring0(State)};
-
-handle_cast(_Msg, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling all non call/cast messages
-%%
-%% @spec handle_info(Info, State) -> {noreply, State} |
-%%                                   {noreply, State, Timeout} |
-%%                                   {stop, Reason, State}
-%% @end
-%%--------------------------------------------------------------------
-% handle_info(die, State) ->
-%     {stop, normal, State};
-handle_info(Info, State) ->
-    gas:info(?MODULE,"unrecognized info: ~p", [Info]),
-    {stop, {error, unrecognized_info}, State}.
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any
-%% necessary cleaning up. When it returns, the gen_server terminates
-%% with Reason. The return value is ignored.
-%%
-%% @spec terminate(Reason, State) -> void()
-%% @end
-%%--------------------------------------------------------------------
-terminate(_Reason, _State) ->
-    ok.
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Convert process state when code is changed
-%%
-%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% @end
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-
-get_last_score(UId, State) ->
-    case State#state.history of
-        [] ->
-            0;
-        [H | _] ->
-            RRs = H#'OkeyGameResults'.results,
-            R = lists:keyfind(UId, #'OkeyGameR'.player_id, RRs),
-            R#'OkeyGameR'.score
-    end.
+is_chanak_winner(Achs) ->
+    F = fun(Ach) -> lists:member(Ach, chanak_win_achievements())
+        end,
+    lists:any(F, Achs).
 
-gosterge_finish_round_result(UId, RoundScore, #'OkeyGameResults'{results = RRs0} = Res0) ->
-    RRs1 = lists:map(fun(X) ->
-                             X#'OkeyGameR'{
-                               score_delta = 0,
-                               breakdown = [],
-                               skill_delta = 0
-                              }
-                     end, RRs0),
-    {value, R, RRs2} = lists:keytake(UId, #'OkeyGameR'.player_id, RRs1),
-    R1 = R#'OkeyGameR'{
-           winner = true,
-           good_shot = true,
-           skill_delta = 1,
-           score_delta = RoundScore,
-           breakdown = [#'OkeyScoringDetail'{reason = [gosterge],
-                                             score = RoundScore}]
-          },
-    Res0#'OkeyGameResults'{results = [R1 | RRs2]}.
-
-gosterge_finish_game_result(UId, GameScore, Res0) ->
-    #'OkeyGameResults'{results = RRs1} = strip_gamepoints(Res0),
-    {value, R, RRs2} = lists:keytake(UId, #'OkeyGameR'.player_id, RRs1),
-    R1 = R#'OkeyGameR'{
-           winner = true,
-           good_shot = true,
-           skill_delta = 1,
-           score_delta = GameScore,
-           score = GameScore,
-           breakdown = [#'OkeyScoringDetail'{reason = [gosterge_winner],
-                                             score = GameScore}]
-          },
-    Res0#'OkeyGameResults'{results = [R1 | RRs2]}.
-
-positions_arrange(Rs0) ->
-    Rs = lists:reverse(lists:keysort(#'OkeyGameR'.score, Rs0)),
-    [#'OkeyGameR'{score = MaxScore} | _] = Rs,
-    positions_arrange(1, MaxScore, Rs).
-
-positions_arrange(_, _, []) ->
-    [];
-positions_arrange(P, Score, [R | Rest]) ->
-    case R of
-        #'OkeyGameR'{score = Score} ->
-            [{P, R} | positions_arrange(P, Score, Rest) ];
-        #'OkeyGameR'{score = LowerScore} ->
-            [{P+1, R} | positions_arrange(P+1, LowerScore, Rest) ]
-    end.
 
-standings0(#'OkeyGameResults'{results = Rs0}) ->
-    Rs = positions_arrange(Rs0),
-    [ begin
-          #'OkeyGameR'{player_id = UId, score = Score} = R,
-          #'OkeySeriesResult'{player_id = UId, place = Position, score = Score, 
-                     winner = case Position =:= 1 of true -> <<"true">>; false -> <<"none">> end }
-      end
-      || {Position, R} <- Rs ].
-
-strip_gamepoints(#'OkeyGameResults'{results = R} = Res) ->
-    R2 = [ strip_gamepoints(R0) || R0 <- R],
-    Res#'OkeyGameResults'{results = R2};
-strip_gamepoints(#'OkeyGameR'{} = R) ->
-    R#'OkeyGameR'{
-      winner = false,
-      good_shot = false,
-      score_delta = 0,
-      skill_delta = 0,
-      score = 0,
-      breakdown = []
-     }.
-
-countdown_points_reset(false, Res) ->
-    {Res, Res};
-countdown_points_reset({true, PlayerId}, Res) ->
-    countdown_points_reset0(PlayerId, Res).
-
-countdown_points_reset0(PlayerId, Res) ->
-    {[gosterge], RoundScore} = score_single([gosterge], achievements(countdown, with_scores)),
-    {[gosterge_winner], GameScore} = score_single([gosterge_winner], achievements(countdown, with_scores)),
-    RoundRes = gosterge_finish_round_result(PlayerId, RoundScore, Res),
-    GameRes = gosterge_finish_game_result(PlayerId, GameScore, Res),
-    {RoundRes, GameRes}.
-
-update_history(Res, State) ->
-    Spin = fun(#'OkeyGameResults'{results = List}) ->
-                   lists:map(fun(X) -> {X#'OkeyGameR'.score_delta, X#'OkeyGameR'.score} end, List)
-           end,
-    case State#state.history of
-        [] ->
-            Res1 = trim_to_10(Res, State#state.mode),
-            gas:info(?MODULE,"old entry: none", []),
-            gas:info(?MODULE,"new entry: ~p", [Spin(Res1)]),
-            gas:info(?MODULE,"cmb entry: ~p", [Spin(Res1)]),
-            {State#state{history = [Res1]}, Res1};
-        [H|Rest] ->
-            C = combine(Res, H, State#state.mode),
-            gas:info(?MODULE,"old entry: ~p", [Spin(H)]),
-            gas:info(?MODULE,"new entry: ~p", [Spin(Res)]),
-            gas:info(?MODULE,"cmb entry: ~p", [Spin(C)]),
-            {State#state{history = [C, H | Rest]}, C}
+detect_game_finish(#state{mode = GameMode, last_round_num = RoundNum, finish_info = FinishInfo,
+                          rounds_num = MaxRoundNum, total_score = TotalScore}) ->
+    if GameMode == ?MODE_COUNTDOWN ->
+           lists:any(fun({_, Points}) -> Points >= ?COUNTDOWN_MAX_POINTS end, TotalScore);
+       FinishInfo == set_timeout ->
+           true;
+       true ->
+           RoundNum == MaxRoundNum
     end.
 
-combine(Base = #'OkeyGameResults'{}, History = #'OkeyGameResults'{}, Mode) ->
-    BaseR = Base#'OkeyGameResults'.results,
-    HistoryR = History#'OkeyGameResults'.results,
-    ComulativeRes = [ combine(B, H, Mode) || B = #'OkeyGameR'{player_id = UIdB} <- BaseR,
-                                             H = #'OkeyGameR'{player_id = UIdH} <- HistoryR,
-                                             UIdB == UIdH ],
-    Base#'OkeyGameResults'{results = ComulativeRes};
-combine(B = #'OkeyGameR'{}, H = #'OkeyGameR'{}, Mode) when Mode == countdown->
-    trim_to_10(combine(B, H), Mode);
-combine(B = #'OkeyGameR'{}, H = #'OkeyGameR'{}, _Mode) ->
-    combine(B, H).
-combine(B = #'OkeyGameR'{}, H = #'OkeyGameR'{}) ->
-    BPD = B#'OkeyGameR'.score_delta,
-    HPD = H#'OkeyGameR'.score,
-    B#'OkeyGameR'{score = BPD + HPD}.
-
-trim_to_10(Results = #'OkeyGameResults'{}, Mode) when Mode == countdown ->
-    RRs = Results#'OkeyGameResults'.results,
-    RRs1 = [ trim_to_10(R, Mode) || R <- RRs ],
-    Results#'OkeyGameResults'{results = RRs1};
-trim_to_10(Results = #'OkeyGameResults'{}, _Mode)  ->
-    Results;
-trim_to_10(S = #'OkeyGameR'{score = Score}, Mode) when Score < 0, Mode == countdown ->
-    S#'OkeyGameR'{score = 0};
-trim_to_10(S, _Mode) ->
-    S.
-
-get_set_state(State) ->
-    #'OkeySetState'{round_cur = State#state.round_cur,
-                    round_max = State#state.round_max,
-                    set_cur = State#state.set_cur,
-                    set_max = State#state.set_max}.
-
-
-get_max_countdown_counter(#state{history = [L|_]} = _State) ->
-    #'OkeyGameResults'{results = Rs} = L,
-    MaxP = lists:last(lists:keysort(#'OkeyGameR'.score, Rs)),
-    {MaxP#'OkeyGameR'.player_id, MaxP#'OkeyGameR'.score}.
-
-%% begin CODE for countdown
-check_set_ending(#state{mode = countdown, round_cur = RCur,
-                        set_cur = SetCur, set_max = SetMax} = State) ->
-    {A, B} = get_max_countdown_counter(State),
-    gas:info(?MODULE,"SetCur: ~p, SetMax: ~p", [SetCur, SetMax]),
-    gas:info(?MODULE,"coundown best score: ~p", [{A, B}]),
-    case {A, B} of
-        {PlayerId, Points} when Points >= 10 ->
-            Acc =
-                case SetCur of
-                    SetMax -> done; %% it was last set
-                    _ ->      next_set %% there are few more sets left
-                end,
-            %% PlayerResults2 = avard_countdown_bonus(PlayerId, PlayerResults),
-            {State#state{round_cur = 1,
-                         set_cur = SetCur + 1,
-                         stats = [],
-                         gosterge_shown = false},
-             Acc,
-             {true, PlayerId}};
-        {_PlayerId, Points} when Points < 10 ->
-            {State#state{round_cur = RCur + 1, stats = [], gosterge_shown = false},
-             next_round,
-             false}
-    end;%% end CODE for countdown
-
-
-check_set_ending(State = #state{round_cur = RCur0, round_max = RMax0,
-                                set_cur = SetCur0, set_max = SetMax0})
-  when (RCur0 /= RMax0) ->
-    gas:info(?MODULE,"check_set_ending A, next_round. debug: ~p", [{RCur0, RMax0, SetCur0, SetMax0}]),
-    State1 = update_to_next_round_or_set(State),
-    {State1, next_round, false};
-check_set_ending(State = #state{set_cur = SetCur0, set_max = SetMax0,
-                                round_cur = RCur0, round_max = RMax0})
-  when (RCur0 == RMax0) andalso (SetCur0 < SetMax0)->
-    gas:info(?MODULE,"check_set_ending A, next_set. debug: ~p", [{RCur0, RMax0, SetCur0, SetMax0}]),
-    State1 = update_to_next_round_or_set(State),
-    {State1, next_set, false};
-check_set_ending(State = #state{set_cur = SetCur, set_max = SetMax,
-                                round_cur = RCur, round_max = RMax}) ->
-    gas:info(?MODULE,"check_set_ending B, done. debug: ~p", [{RCur, RMax, SetCur, SetMax}]),
-    State1 = update_to_next_round_or_set(State),
-    {State1, done, false}.
-
-update_to_next_round_or_set(State = #state{mode = Mode}) when Mode == standard; Mode == evenodd; Mode == color ->
-    NextRound = State#state.round_cur + 1,
-    CurrentSet = State#state.set_cur,
-    gas:info(?MODULE,"update_to_next_round_or_set. {set_cur, round_cur}: ~p", [{State#state.set_cur, State#state.round_cur}]),
-    case NextRound > State#state.round_max of
-        true ->
-            %% new set
-            gas:info(?MODULE,"next set", []),
-            State#state{round_cur = 1, set_cur = CurrentSet + 1, stats = [], gosterge_shown = false};
-        false ->
-            gas:info(?MODULE,"next round", []),
-            %% new round
-            State#state{round_cur = NextRound, stats = [], gosterge_shown = false}
-    end;
-update_to_next_round_or_set(State) -> %% case when Mode = countdown
-    State#state{stats = [], gosterge_shown = false}.
-
-
-check_hands_for_8_tashes(Hands, Stats) ->
-    lists:foldl(fun({UId, H}, Acc) ->
-                        case with_8_tashes(H) of
-                            true ->
-                                A = #okey_stats_entry{uid = UId, reason = with_8_tashes},
-                                [A | Acc];
-                            false ->
-                                Acc
-                        end
-                end, Stats, Hands).
-
-reset_scoring0(State) ->
-    gas:info(?MODULE,"External reset scoring. set 1, round 1", []),
-    State#state{round_cur = 1, set_cur = 1, stats = [], gosterge_shown = false, history = []}.
-
-common_round_finish(Stats, Res0, Mode, State1) ->
-    Res2 = analyze_game(Stats, Res0, Mode),
-    {State2, Res4} = update_set(Mode, State1, Res2),
-    {State3, Res5} = update_history(Res4, State2),
-    {State4, IsLast, CountdownReset} = check_set_ending(State3),
-    RoundResults = translate_reasons(Res5),
-    {_, GameResults0} = countdown_points_reset(CountdownReset, Res5),
-    case IsLast of
-        done ->
-            GameResults = translate_reasons(GameResults0),
-            %game_stats:assign_points(GameResults, State1#state.game_info),
-            {reply, {ok, {done, GameResults},                 RoundResults}, State4#state{history = []}};
-        next_set ->
-            {reply, {ok, {next_set,   get_set_state(State4)}, RoundResults}, State4#state{history = []}};
-        next_round ->
-            {reply, {ok, {next_round, get_set_state(State4)}, RoundResults}, State4}
-    end.
 
-%% REWRITE THIS PART!!!
-update_set(Mode, State, R) when Mode =/= standard ->
-    #'OkeyGameResults'{results = Res} = R,
-    %% find reveal, if available
-    FilterFun = fun(#'OkeyScoringDetail'{reason = X}) ->
-                        length(X) > 1 andalso lists:member(reveal, X) %% reason is [reveal, ..]
-                end,
-    CW = find_first_ogr(FilterFun, Res),
-    case CW of
-        null ->
-            update_set2(State, R);
-        _ ->
-            update_set_state(State, R, CW)
-    end;
-update_set(_, State, R) ->
-    {State, R}.
-
-update_set2(State = #state{}, R) ->
-    #'OkeyGameResults'{results = Res} = R,
-    %% find with_8_tashes, if available
-    FilterFun = fun(#'OkeyScoringDetail'{reason = X}) ->
-                        lists:member(with_8_tashes, X) %% reason is [with_8_tashes, ..]
-                end,
-    CW = find_first_ogr(FilterFun, Res),
-    case CW of
-        null ->
-            {State, R};
-        _ ->
-            update_set_state(State, R, CW)
+players_achivements(Mode, Seats, Hands, WhoHasGosterge, Has8Tashes, FinishInfo) ->
+    case FinishInfo of
+        tashes_out ->
+            [begin
+                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats];
+        timeout ->
+            [begin
+                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats];
+        set_timeout ->
+            [begin
+                 Achivements = player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats];
+        {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} ->
+            [begin
+                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
+                 Achivements = player_achivements_win_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes,
+                                                             Revealer, WrongRejects, RevealWithOkey,
+                                                             RevealWithPairs, RevealWithColor),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats];
+        {fail_reveal, Revealer} ->
+            [begin
+                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
+                 Achivements = player_achivements_fail_reveal(Mode, SeatNum, WhoHasGosterge,
+                                                              Has8Tashes, Revealer),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats];
+        {gosterge_finish, _Winner} ->
+            [begin
+                 {_, _Hand} = lists:keyfind(SeatNum, 1, Hands),
+                 Achivements = player_achivements_gosterge_finish(Mode, SeatNum, WhoHasGosterge, Has8Tashes),
+                 {SeatNum, Achivements}
+             end || SeatNum <- Seats]
     end.
 
-find_first_ogr(FilterFun, Res) ->
-    lists:foldl(fun
-                    (#'OkeyGameR'{breakdown = BR} = OGR, null) ->
-                        P = lists:filter(FilterFun, BR),
-                        case P of
-                            [] -> null;
-                            _ -> OGR
-                        end;
-                    (_, Acc) ->
-                             Acc
-                end, null, Res).
-
-update_set_state(State, R = #'OkeyGameResults'{}, CW) ->
-    Entries0 = R#'OkeyGameResults'.results,
-    Entries1 = lists:delete(CW, Entries0),
-    
-    Reward = State#state.chanak,
-    #'OkeyGameR'{breakdown = BR, score = OrgScore, score_delta = OrgDelta} = CW,
-    gas:info(?MODULE,"chanak points: ~p", [Reward]),
-    BR1 = [#'OkeyScoringDetail'{reason = <<"chanak points">>, score = Reward} | BR],
-
-    Entries2 = [CW#'OkeyGameR'{breakdown = BR1,
-                               score = OrgScore + Reward,
-                               score_delta = OrgDelta + Reward} | Entries1],
-    {State#state{chanak = 0}, R#'OkeyGameResults'{results = Entries2}}.
-
-refill_chanak(Mode, State = #state{gosterge_shown = X}, Stats0) when X == false, Mode =/= standard, Mode =/= countdown ->
-    Chanak = State#state.chanak,
-    L = [ #okey_stats_entry{uid = UId, reason = box_empty} || {UId, _} <- State#state.set ],
-    Achievements = achievements(Mode, with_scores),
-    {_, Deduction} = lists:keyfind([box_empty], 1, Achievements),
-    Stats = %L ++ 
-            Stats0,
-    NewChanak = Chanak + Deduction,
-    {State#state{chanak = NewChanak, stats = Stats}, Stats};
-refill_chanak(_, State, Stats) ->
-    {State, Stats}.
-
-new_chanak(Mode) ->
-    Achievements = achievements(Mode, with_scores),
-    {_, Deduction} = lists:keyfind([gosterge_winner], 1, Achievements),
-    Deduction.
-
-analyze_game(Stats, #'OkeyGameResults'{results = PlayerResults0} = Res, Mode) ->
-    PlayerEvents = dict:from_list([ {Id, []} || #'OkeyGameR'{player_id = Id} <- PlayerResults0 ]),
-%    gas:info(?MODULE,"PlayerEvents: ~p", [PlayerEvents]),
-    PlayerEvents1 = analyze_game0(Stats, PlayerEvents, fun describe_achievement/2),
-%    gas:info(?MODULE,"PlayerEvents1: ~p", [PlayerEvents1]),
-    Check = [ check_stats(X) ||  {_Id, X} <- dict:to_list(PlayerEvents1) ],
-    case lists:any(fun(Bool) -> Bool end, Check) of
-        false ->
-%            gas:info(?MODULE,"PlayerEvents: ~p", [PlayerEvents1]),
-            erlang:error(incomplete_stats_detected);
-        true -> ok
-    end,
-    PEL = dict:to_list(PlayerEvents1),
-    PlayerEvents2 = [ {Id, ordsets:from_list(X) } ||  {Id, X} <- PEL ],
-    AMode = achievements(Mode),
-    Achievements = ordsets:from_list(AMode),
-    Reduced = [ {Id, reduce_ach(match_ach(PE, Achievements))} || {Id, PE} <- PlayerEvents2 ],
-    Scored = [ score_ach(R, RA, Mode)
-               || {{_Id, RA}, R} <- lists:zip(lists:keysort(1, Reduced),
-                                              lists:keysort(#'OkeyGameR'.player_id, PlayerResults0)) ],
-    Res#'OkeyGameResults'{results = Scored}.
-
-match_ach(MyList, OffList) ->
-    lists:filter(fun(X) -> ordsets:is_subset(X, MyList) end, OffList).
-
-
-reduce_ach(L) ->
-    reduce_ach(L, L, []).
-reduce_ach([], _L, Acc) ->
-    Acc;
-reduce_ach([H | T], L, Acc) ->
-    case lists:any(fun
-                       (X) when X =:= H -> false;
-                       (X) -> ordsets:is_subset(H, X)
-                   end, L) of
-        true ->
-            reduce_ach(T, L, Acc);
-        false ->
-            reduce_ach(T, L, [H | Acc])
-    end.
 
-score_ach(R, [], _Mode) ->
-    R;
-score_ach(#'OkeyGameR'{score_delta = PD} = R, [H0|T] = _RA, Mode) ->
-    Achievements = achievements(Mode, with_scores),
-    case score_single(H0, Achievements) of
-        false ->
-            score_ach(R, T, Mode);
-        {H, Score} ->
-            BD = R#'OkeyGameR'.breakdown,
-            A = #'OkeyScoringDetail'{reason = H, score = Score},
-            R1 = R#'OkeyGameR'{score_delta = PD + Score,
-                               score = PD + Score,
-                               breakdown = [A | BD]},
-            score_ach(R1, T, Mode)
+%% finish_info(GameMode, FinishReason, Gosterge) ->
+%%      tashes_out |
+%%      timeout |
+%%      set_timeout |
+%%      {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs} |
+%%      {fail_reveal, Revealer} |
+%%      {gosterge_finish, Winner}
+finish_info(GameMode, FinishReason, Gosterge) ->
+    case FinishReason of
+        tashes_out -> tashes_out;
+        timeout -> timeout;
+        set_timeout -> set_timeout;
+        {reveal, Revealer, Tashes, Discarded, ConfirmationList} ->
+            {RightReveal, RevealWithPairs, WithColor} = check_reveal(Tashes, Gosterge),
+            WinReveal = RightReveal orelse lists:all(fun({_, Answer}) -> Answer == true end, ConfirmationList),
+            if WinReveal ->
+                   RevealWithColor = case GameMode of
+                                         ?MODE_STANDARD -> false;
+                                         ?MODE_EVENODD -> WithColor;
+                                         ?MODE_COLOR -> WithColor;
+                                         ?MODE_COUNTDOWN -> false
+                                     end,
+                   Okey = gosterge_to_okey(Gosterge),
+                   RevealWithOkey = Discarded == Okey,
+                   WrongRejects = if RightReveal ->
+                                         [S || {S, Answer} <- ConfirmationList, Answer==false];
+                                     true -> []
+                                  end,
+                   {win_reveal, Revealer, WrongRejects, RevealWithColor, RevealWithOkey, RevealWithPairs};
+               true ->
+                   {fail_reveal, Revealer}
+            end;
+        {gosterge_finish, Winner} when GameMode == ?MODE_COUNTDOWN ->
+            {gosterge_finish, Winner}
     end.
 
-score_single(Entry, Achievements) ->
-    H = lists:usort(ordsets:to_list(Entry)),
-    lists:keyfind(H, 1, Achievements).
-
-translate_reasons(#'OkeyGameResults'{results = R} = Res) ->
-    R2 = [ translate_reasons(X) || X  <- R ],
-    Res#'OkeyGameResults'{results = R2};
-translate_reasons(#'OkeyGameR'{breakdown = R} = Res) ->
-    R2 = [ translate_reasons(X) || X  <- R ],
-    Res#'OkeyGameR'{breakdown = R2};
-translate_reasons(#'OkeyScoringDetail'{reason = R} = Res) when is_binary(R) ->
-    Res;
-translate_reasons(#'OkeyScoringDetail'{reason = R} = Res) ->
-    Descs = lists:zip(achievements(), achievements_desc()),
-    {R, Reason} = lists:keyfind(R, 1, Descs),
-    Res#'OkeyScoringDetail'{reason = Reason}.
-
-analyze_game0([], Players, _F) ->
-    Players;
-analyze_game0([#okey_stats_entry{uid = UId, reason = Reason} | Rest], Players, F) ->
-    Player = dict:fetch(UId, Players),
-    Player2 = F(Reason, Player),
-    analyze_game0(Rest, dict:store(UId, Player2, Players), F).
-
-describe_achievement(Event, List) ->
-    [Event | List].
-
-with_okey(Okey, Okey) ->
-    true;
-with_okey(_Okey, _LastTash) ->
-    false.
-
-with_even_tashes(Gosterge, Hand0) ->
-    Okey = game_okey:get_okey(Gosterge),
-    Hand = game_okey:normalize_hand(null, Hand0),
-    Rev2 = lists:map(fun(J) when J == Okey -> okey;
-                        (Other) -> Other
-                     end, Hand),
-    Rev3 = lists:map(fun(?FALSE_OKEY) -> Okey;
-                        (Other) -> Other
-                     end, Rev2),
-    Sets = game_okey:split_by_delimiter(null, Rev3),
-    Res = lists:all(fun(S) ->
-                      game_okey:is_pair(S)
-              end, Sets),
-    Res andalso gas:info(?MODULE,"Detected even tashes, gosterge: ~p~nhand ~p", [Gosterge, Hand0]),
-    Res.
-
-with_8_tashes(Hand0) ->
-    Hand = game_okey:normalize_hand(null, Hand0),
-    Sets = game_okey:split_by_delimiter(null, Hand),
-    Pairs0 = lists:filter(fun
-                              ([A, A]) -> true;
-                              (_) -> false
-                          end, Sets),
-    Singles = lists:map(fun(L) -> hd(L) end, Pairs0),
-    case length(Singles) of
-        X when X > 3 ->
-            check_combinations(Singles);
-        _ ->
-            false
-    end.
 
-with_color(Gosterge, Hand) ->
-    Okey = game_okey:get_okey(Gosterge),
-    Tashes0 = lists:flatten(game_okey:split_by_delimiter(null, game_okey:normalize_hand(null, Hand))),
-    Tashes1 = lists:filter(fun(J) when J == Okey -> false;
-                              (_) -> true
-                           end, Tashes0),
-    Tashes = lists:map(fun(J) when J == ?FALSE_OKEY -> Okey;
-                          (J) -> J
-                       end, Tashes1),
-    TheC = (hd(Tashes))#'OkeyPiece'.color,
-    lists:all(fun
-                  (#'OkeyPiece'{color = C}) when C == TheC -> true;
-                  (#'OkeyPiece'{color = _}) -> false
-              end, Tashes).
-
-check_combinations(Singles) ->
-    Combs = kakamath:combination(4, Singles),
-    lists:any(fun(List) ->
-                      same_values(List)
-              end, Combs).
-
-same_values(List) ->
-    Val = (hd(List))#'OkeyPiece'.value,
-    lists:all(fun
-                  (#'OkeyPiece'{value = V}) when V == Val -> true;
-                  (_) -> false
-              end, List).
-
-
-if_list(true, X) ->
-    [X];
-if_list(false, _) ->
-    [].
-
-is_mode(Mode) ->
-    lists:member(Mode, [standard, evenodd, color, countdown]).
-
-achievements(Mode, with_scores) ->
-    GP = game_points(),
-    {Mode, Points} = lists:keyfind(Mode, 1, GP),
-    Res0 = lists:zip(achievements(), Points),
-    Res1 = [ P || {_, Score} = P <- Res0, Score =/= 0 ],
-    Res1.
-achievements(Mode) ->
-    {Ach, _Scores} = lists:unzip(achievements(Mode, with_scores)),
-    Ach.
-
-achievements() ->
-   A = [
-        [gosterge],                  %% 1
-        [reveal],                    %% 2
-        [reveal, okey],              %% 3
-        [reveal, even],              %% 4
-        [reveal, even, okey],        %% 5
-        [no_reveal, with8tashes],    %% 6
-        [reveal, color],             %% 7
-        [reveal, color, okey],       %% 8
-        [reveal, color, even],       %% 8
-        [reveal, color, even, okey], %% 10
-        [wrong_reveal],              %% 11
-        [caught_bluff],              %% 12 %% AKA others_on_wrong_reveal
-        [box_empty],                 %% 13
-        [rejected_good_hand],        %% 14
-        [gosterge_winner]            %% 15
-       ],%% note: those points are added
-    [ lists:usort(X) || X <- A ].
-achievements_desc() ->
-    [
-     <<"gosterge shown">>,                            %% 1
-     <<"proper reveal">>,                             %% 2
-     <<"reveal with okey">>,                          %% 3
-     <<"reveal with even tashes">>,                   %% 4
-     <<"reveal with even tashes and okey">>,          %% 5
-     <<"8 tashes of value in hand">>,                 %% 6
-     <<"reveal with color">>,                         %% 7
-     <<"reveal with color and okey">>,                %% 8
-     <<"reveal with color and even tashes">>,         %% 9
-     <<"reveal with color, even tashes and okey">>,   %% 10
-     <<"reveal with wrong hand">>,                    %% 11
-     <<"caught bluff">>,                              %% 12 %% AKA others_on_wrong_reveal
-     <<"deduction to empty box">>,                    %% 13
-     <<"rejected good hand">>,                        %% 14
-     <<"gosterge winner">>                            %% 15
-    ].%% note: those points are added
-
-
-%% note - all penalties/deductions should be expressed here as negative numbers
-game_points() ->
-    [%%          1   2   3   4   5   6   7   8   9  10  11  12  13  14, 15 <--- achievement number
-     {standard, [1,  3,  6,  6, 12,  0,  0,  0,  0,  0, -9,  3,  1,  0,  1]},
-     {odd,      [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  1,  0,  8]},
-     {even,     [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  2,  0,  8]},
-     {ybodd,    [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  1,  0, 16]},
-     {ybeven,   [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  2,  0, 16]},
-     {rbodd,    [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  2,  0, 16]},
-     {rbeven,   [4, 12, 24, 24, 48, 48, 96,192,192,384,-36, 12,  4,  0, 16]},
-     {countdown,[1,  2,  4,  4,  8,  0,  0,  0,  0,  0, -2,  0,  1,  0,  1]}
-    ].%% note: those points are added
-
-
-%% debug
-check_stats(List) ->
-    lists:member(reveal, List)
-        orelse lists:member(wrong_reveal, List)
-        orelse lists:member(disconnected, List)
-        orelse lists:member(out_of_tashes, List).
-
-%%%===================================================================
-%%% Tests
-%%%===================================================================
-
-detecting_8_tashes_test() ->
-    H = [ {true, [#'OkeyPiece'{color = 1, value = 2},
-                  #'OkeyPiece'{color = 1, value = 2},
-                  null,
-                  #'OkeyPiece'{color = 2, value = 2},
-                  #'OkeyPiece'{color = 2, value = 2},
-                  null,
-                  #'OkeyPiece'{color = 3, value = 2},
-                  #'OkeyPiece'{color = 3, value = 2},
-                  null,
-                  #'OkeyPiece'{color = 4, value = 1},
-                  #'OkeyPiece'{color = 4, value = 3},
-                  null,
-                  #'OkeyPiece'{color = 4, value = 2},
-                  #'OkeyPiece'{color = 4, value = 2},
-                  null,
-                  #'OkeyPiece'{color = 2, value = 12},
-                  #'OkeyPiece'{color = 3, value = 7},
-                  #'OkeyPiece'{color = 2, value = 7},
-                  #'OkeyPiece'{color = 2, value = 12}
-                 ], #'OkeyPiece'{color = 2, value = 12}},
-          {false, [#'OkeyPiece'{color = 1, value = 2},
-                   #'OkeyPiece'{color = 1, value = 2},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 2},
-                   #'OkeyPiece'{color = 2, value = 2},
-                   null,
-                   #'OkeyPiece'{color = 3, value = 2},
-                   #'OkeyPiece'{color = 3, value = 2},
-                   null,
-                   #'OkeyPiece'{color = 4, value = 1},
-                   #'OkeyPiece'{color = 4, value = 3},
-                   null,
-                   #'OkeyPiece'{color = 4, value = 2},
-                   #'OkeyPiece'{color = 2, value = 12},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 12},
-                   #'OkeyPiece'{color = 3, value = 7},
-                   #'OkeyPiece'{color = 2, value = 7},
-                   #'OkeyPiece'{color = 2, value = 12}
-                  ], #'OkeyPiece'{color = 2, value = 12}},
-          {false, [#'OkeyPiece'{color = 1, value = 2},
-                   #'OkeyPiece'{color = 1, value = 2},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 2},
-                   ?FALSE_OKEY,
-                   null,
-                   #'OkeyPiece'{color = 3, value = 2},
-                   #'OkeyPiece'{color = 3, value = 2},
-                   null,
-                   #'OkeyPiece'{color = 4, value = 1},
-                   #'OkeyPiece'{color = 4, value = 3},
-                   null,
-                   #'OkeyPiece'{color = 4, value = 2},
-                   #'OkeyPiece'{color = 2, value = 12},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 12},
-                   #'OkeyPiece'{color = 3, value = 7},
-                   #'OkeyPiece'{color = 2, value = 7},
-                   #'OkeyPiece'{color = 2, value = 12}
-                  ], #'OkeyPiece'{color = 2, value = 2}},
-          {false, [#'OkeyPiece'{color = 1, value = 2},
-                  #'OkeyPiece'{color = 1, value = 2},
-                  #'OkeyPiece'{color = 2, value = 5},
-                  #'OkeyPiece'{color = 2, value = 5},
-                  null,
-                  null,
-                  #'OkeyPiece'{color = 3, value = 7},
-                  #'OkeyPiece'{color = 3, value = 7},
-                  null,
-                  #'OkeyPiece'{color = 4, value = 7},
-                  #'OkeyPiece'{color = 4, value = 7},
-                  null,
-                  #'OkeyPiece'{color = 2, value = 1},
-                  #'OkeyPiece'{color = 2, value = 1},
-                  null,
-                  #'OkeyPiece'{color = 2, value = 12},
-                  #'OkeyPiece'{color = 3, value = 7},
-                  #'OkeyPiece'{color = 2, value = 7},
-                  #'OkeyPiece'{color = 2, value = 12}
-                 ], #'OkeyPiece'{color = 2, value = 12}},
-          {false, [#'OkeyPiece'{color = 1, value = 2},
-                   #'OkeyPiece'{color = 1, value = 3},
-                   #'OkeyPiece'{color = 1, value = 4},
-                   #'OkeyPiece'{color = 1, value = 6},
-                   null,
-                   #'OkeyPiece'{color = 1, value = 5},
-                   #'OkeyPiece'{color = 2, value = 5},
-                   #'OkeyPiece'{color = 3, value = 5},
-                   #'OkeyPiece'{color = 4, value = 5},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 1},
-                   #'OkeyPiece'{color = 2, value = 13},
-                   #'OkeyPiece'{color = 2, value = 12},
-                   null,
-                   #'OkeyPiece'{color = 3, value = 7},
-                   #'OkeyPiece'{color = 2, value = 7},
-                   #'OkeyPiece'{color = 2, value = 12}
-                  ], #'OkeyPiece'{color = 2, value = 12}}
-         ],
-    HS = lists:zip(lists:seq(1, length(H)), H),
-    lists:map(fun({Num, {ProperResult, Hand, _}}) ->
-                      Z = {Num, with_8_tashes(Hand), Hand},
-                      {Num, ProperResult, Hand} = Z
-              end, HS).
-
-detecting_color_test() ->
-    H = [ {true, [#'OkeyPiece'{color = 1, value = 1},
-                  #'OkeyPiece'{color = 1, value = 2},
-                  #'OkeyPiece'{color = 1, value = 3},
-                  #'OkeyPiece'{color = 1, value = 4},
-                  null,
-                  #'OkeyPiece'{color = 1, value = 5},
-                  #'OkeyPiece'{color = 1, value = 6},
-                  #'OkeyPiece'{color = 1, value = 7},
-                  #'OkeyPiece'{color = 1, value = 8},
-                  null,
-                  #'OkeyPiece'{color = 1, value = 8},
-                  #'OkeyPiece'{color = 1, value = 9},
-                  #'OkeyPiece'{color = 1, value = 11},
-                  #'OkeyPiece'{color = 1, value = 12},
-                  #'OkeyPiece'{color = 1, value = 13},
-                  #'OkeyPiece'{color = 2, value = 12}
-                 ], #'OkeyPiece'{color = 2, value = 11}},
-          {false, [#'OkeyPiece'{color = 1, value = 1},
-                   #'OkeyPiece'{color = 1, value = 2},
-                   #'OkeyPiece'{color = 1, value = 3},
-                   #'OkeyPiece'{color = 1, value = 4},
-                   null,
-                   #'OkeyPiece'{color = 2, value = 5},
-                   #'OkeyPiece'{color = 2, value = 6},
-                   #'OkeyPiece'{color = 2, value = 7},
-                   #'OkeyPiece'{color = 2, value = 8},
-                   null,
-                   #'OkeyPiece'{color = 1, value = 8},
-                   #'OkeyPiece'{color = 1, value = 9},
-                   #'OkeyPiece'{color = 1, value = 11},
-                   #'OkeyPiece'{color = 1, value = 12},
-                   #'OkeyPiece'{color = 1, value = 13},
-                   #'OkeyPiece'{color = 2, value = 12}
-                  ], #'OkeyPiece'{color = 2, value = 11}},
-          {false, [[null,
-                    {'OkeyPiece',1,2},
-                    {'OkeyPiece',3,2},
-                    {'OkeyPiece',1,3},
-                    {'OkeyPiece',3,5},
-                    {'OkeyPiece',4,5},
-                    {'OkeyPiece',1,7},
-                    {'OkeyPiece',3,7},
-                    {'OkeyPiece',4,7},
-                    {'OkeyPiece',2,8},
-                    {'OkeyPiece',4,8},
-                    {'OkeyPiece',3,9},
-                    {'OkeyPiece',3,10},
-                    {'OkeyPiece',1,11}],
-                   [{'OkeyPiece',1,12},
-                    null,null,null,null,null,null,null,null,null,
-                    null,null,null,null]], {'OkeyPiece',1,0}}
-
-        ],
-    HS = lists:zip(lists:seq(1, length(H)), H),
-    lists:map(fun({Num, {ProperResult, Hand, Gosterge}}) ->
-                      Z = {Num, with_color(Gosterge, Hand), Hand, Gosterge},
-                      {Num, ProperResult, Hand, Gosterge} = Z
-              end, HS).
-
-reduce_ach_test() ->
-    H0 = [
-          { [[a]],
-            [[a],[]] },
-
-          { [[a,b]],
-            [[a],[b],[a,b]] },
-
-          { [[a,b],[a,c]],
-            [[a],[b],[a,b],[a,c]] },
-
-          { [[a,b],[c]],
-            [[a],[],[c],[b],[a,b]] }
-         ],
-    H = [ { [ ordsets:from_list(AX) || AX <- A ], [ ordsets:from_list(BX) || BX <- B ]} || {A, B} <- H0 ],
-    HS = lists:zip(lists:seq(1, length(H)), H),
-    lists:map(fun({Num, {Ideal0, Fresh}}) ->
-                      Reduced = reduce_ach(Fresh),
-                      Z = {Num, ordsets:from_list(Reduced), Fresh},
-                      Ideal = ordsets:from_list(Ideal0),
-                      {Num, Ideal, Fresh} = Z
-              end, HS).
-
-insert_at_random_test() ->
-    A = put_some_nulls(20, lists:seq(1, 10)),
-    gas:info(?MODULE,"insert_at_random_test: ~p", [A]).
-
-
-positions_arrange_test() ->
-    [{1, #'OkeyGameR'{player_id = 1}},
-     {2, #'OkeyGameR'{player_id = 2}},
-     {3, #'OkeyGameR'{player_id = 3}},
-     {4, #'OkeyGameR'{player_id = 4}}]
-        = positions_arrange([#'OkeyGameR'{score = 40, player_id = 1},
-                          #'OkeyGameR'{score = 30, player_id = 2},
-                          #'OkeyGameR'{score = 20, player_id = 3},
-                          #'OkeyGameR'{score = 10, player_id = 4}]),
-    [{1, #'OkeyGameR'{player_id = 2}},
-     {1, #'OkeyGameR'{player_id = 1}},
-     {2, #'OkeyGameR'{player_id = 3}},
-     {3, #'OkeyGameR'{player_id = 4}}]
-        = positions_arrange([#'OkeyGameR'{score = 40, player_id = 1},
-                          #'OkeyGameR'{score = 40, player_id = 2},
-                          #'OkeyGameR'{score = 20, player_id = 3},
-                          #'OkeyGameR'{score = 10, player_id = 4}]),
-    [{1, #'OkeyGameR'{player_id = 4}},
-     {1, #'OkeyGameR'{player_id = 3}},
-     {1, #'OkeyGameR'{player_id = 2}},
-     {1, #'OkeyGameR'{player_id = 1}}]
-        = positions_arrange([#'OkeyGameR'{score = 10, player_id = 1},
-                          #'OkeyGameR'{score = 10, player_id = 2},
-                          #'OkeyGameR'{score = 10, player_id = 3},
-                          #'OkeyGameR'{score = 10, player_id = 4}]),
-
-    [{1, #'OkeyGameR'{player_id = 1}},
-     {2, #'OkeyGameR'{player_id = 2}},
-     {3, #'OkeyGameR'{player_id = 3}},
-     {4, #'OkeyGameR'{player_id = 4}}]
-        = positions_arrange([#'OkeyGameR'{score = 30, player_id = 2},
-                          #'OkeyGameR'{score = 20, player_id = 3},
-                          #'OkeyGameR'{score = 40, player_id = 1},
-                          #'OkeyGameR'{score = 10, player_id = 4}]).
-
-score_ach_test() ->
-    %% ok = app_util:start(kakaconfig),
-    %% {ok, Srv} = ?MODULE:start_link([{game_mode, standard}, {sets, 2}, {rounds, 2}]),
-    %% Players = [<<"gleber">>, <<"paul">>, <<"kunthar">>, <<"radistao">>],
-    %% Events = [gosterge, reveal, okey, even, odd, no_reveal, with8tashes, color, wrong_reveal, caught_bluff, box_empty, out_of_tashes],
-    %% lists:map(fun(UId) ->
-    %%                   Event = started,
-    %%                   ?MODULE:add_event(Srv, UId, Event)
-    %%           end, Players),
-    %% lists:map(fun(_) ->
-    %%                   {UId, _} = kakamath:draw_random(Players),
-    %%                   {Event, _} = kakamath:draw_random(Events),
-    %%                   ?MODULE:add_event(Srv, UId, Event)
-    %%           end, lists:seq(1, crypto:rand_uniform(10, 30))),
-    %% {Gosterge, Hands0, _Pile0} = game_okey:hand_out_pieces(),
-    %% Hands = [ {UId, put_some_nulls(21, H)} || {UId, H} <- lists:zip(Players, Hands0) ],
-    %% ?MODULE:finish_round(Srv, [<<"paul">>], Hands, Gosterge).
-    ok.
-
-put_some_nulls(Target, List) ->
-    lists:foldl(fun(_, Acc) ->
-                        insert_at_random(null, Acc)
-                end, List, lists:seq(length(List), Target)).
-
-insert_at_random(Element, List) ->
-    L1 = length(List)+1,
-    R = crypto:rand_uniform(0, L1),
-    case R of
-        0 -> [Element | List];
-        L1 -> List ++ [Element];
-        _ -> lists:sublist(List, R) ++ [Element] ++ lists:nthtail(R, List)
-    end.
+%% @spec get_achivements_points(PointingRules, Achivements) -> AchsPoints
+%% @end
+get_achivements_points(PointingRules, Achivements) ->
+    [{Ach, lists:nth(Ach, PointingRules)} || Ach <- Achivements].
 
+%% @spec sum_achivements_points(AchPoints) -> integer()
+%% @end
+sum_achivements_points(AchPoints) ->
+    lists:foldl(fun({_, P}, Acc)-> Acc + P end, 0, AchPoints).
 
+%% @spec add_delta(TotalScore, RoundScores) -> NewTotalScore
+%% @end
+add_delta(TotalScore, RoundScores) ->
+    [{SeatNum, proplists:get_value(SeatNum, TotalScore) + Delta}
+     || {SeatNum, Delta} <- RoundScores].
 
-        % [gosterge],                  %% 1
-        % [reveal],                    %% 2
-        % [reveal, okey],              %% 3
-        % [reveal, even],              %% 4
-        % [reveal, even, okey],        %% 5
-        % [no_reveal, with8tashes],    %% 6
-        % [reveal, color],             %% 7
-        % [reveal, color, even],       %% 8
-        % [reveal, color, okey],       %% 9
-        % [reveal, color, even, okey], %% 10
-        % [wrong_reveal],              %% 11
-        % [caught_bluff],              %% 12 %% AKA others_on_wrong_reveal
-        % [box_empty]                  %% 13
 
+%% @spec gosterge_to_okey(GostergyTash) -> OkeyTash
+%% @end
+gosterge_to_okey({Color, Value}) ->
+    if Value == 13 -> {Color, 1};
+       true -> {Color, Value + 1}
+    end.
 
+%% @spec check_reveal(TashPlaces, Gosterge) -> {RightReveal, WithPairs, SameColor}
+%% @end
+check_reveal([TopRow, BottomRow], Gosterge) ->
+    FlatList = TopRow ++ [null | BottomRow],
+    Okey = gosterge_to_okey(Gosterge),
+    Normalized = [case E of
+                      Okey -> okey;
+                      false_okey -> Okey;
+                       _ -> E
+                  end || E <- FlatList],
+    Sets = split_by_delimiter(null, Normalized),
+    ProperHand = lists:all(fun(S) -> is_set(S) orelse is_run(S) end, Sets),
+    Pairs = lists:all(fun(S) -> is_pair(S) end, Sets),
+    [Color | Rest] = [C || {C, _} <- Normalized],
+    SameColor = lists:all(fun(C) -> C==Color end, Rest),
+    {ProperHand orelse Pairs, Pairs, ProperHand andalso SameColor}.
+
+%% @spec split_by_delimiter(Delimiter, List) -> ListOfList
+%% @end
+split_by_delimiter(Delimiter, Hand) -> split_by_delimiter(Delimiter, Hand, []).
+split_by_delimiter(_, [], Acc) -> lists:reverse(Acc);
+split_by_delimiter(Delimiter, [Delimiter | Hand], Acc) -> split_by_delimiter(Delimiter, Hand, Acc);
+split_by_delimiter(Delimiter, Hand, Acc) ->
+    {L, Rest} = lists:splitwith(fun(X) -> X =/= Delimiter end, Hand),
+    split_by_delimiter(Delimiter, Rest, [L | Acc]).
+
+%% @spec is_set(Set) -> boolean()
+%% @end
+is_set(Set) when
+  length(Set) < 3;
+  length(Set) > 4 -> false;
+is_set(Set) ->
+    Normals = [ X || X <- Set, X =/= okey ],
+    {_, Value} = hd(Normals),
+    SameValue = lists:all(fun({_, V}) -> V == Value end, Normals),
+    UniqColors = length(Normals) == length(lists:usort([C || {C, _} <- Normals])),
+    SameValue andalso UniqColors.
+
+
+%% @spec is_run(Set) -> boolean()
+%% @end
+is_run(Set) when length(Set) < 3 -> false;
+is_run(Set) ->
+    {Okeys, Normals} = lists:partition(fun(X)-> X == okey end, Set),
+    {Color, _} = hd(Normals),
+    {Colors, Values} = lists:unzip(Normals),
+    SameColor = lists:all(fun(C) -> C == Color end, Colors),
+    SortedValues = lists:sort(Values),
+    NormalizedValues = if hd(SortedValues)==1 -> tl(SortedValues) ++ [14]; true -> false end,
+    OkeysNum = length(Okeys),
+    Check1 = check_run(SortedValues, OkeysNum),
+    Check2 = check_run(NormalizedValues, OkeysNum),
+    SameColor andalso (Check1 orelse Check2).
+
+
+check_run(false, _) -> false;
+check_run([First | Rest], OkeysNum) ->
+    check_run(First, Rest, OkeysNum).
+
+
+check_run(Cur, [Cur | _], _OkeysNum) -> false;
+check_run(Cur, [Next | Rest], OkeysNum) when Next == Cur + 1 ->
+    check_run(Cur+1, Rest, OkeysNum);
+check_run(_Cur, [_Next | _Rest], 0) -> false;
+check_run(Cur, [Next | Rest], OkeysNum) ->
+    check_run(Cur+1, [Next | Rest], OkeysNum - 1);
+check_run(_Cur, [], _OkeysNum) -> true.
+
+%% @spec is_pair(Set) -> boolean()
+%% @end
+is_pair([_A, okey]) -> true;
+is_pair([okey, _B]) -> true;
+is_pair([A, A]) -> true;
+is_pair(_) -> false.
+
+
+player_achivements_no_winner(Mode, SeatNum, WhoHasGosterge, Has8Tashes) ->
+    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, no_winner, undefined,
+                       undefined, undefined, undefined, undefined, undefined).
+
+player_achivements_win_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes,
+                              Revealer, WrongRejects, RevealWithOkey,
+                              RevealWithPairs, RevealWithColor) ->
+    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, reveal,
+                       Revealer, WrongRejects, true, RevealWithOkey,
+                       RevealWithPairs, RevealWithColor).
+
+player_achivements_fail_reveal(Mode, SeatNum, WhoHasGosterge, Has8Tashes, Revealer) ->
+    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, reveal,
+                       Revealer, [], false, false, false, false).
+
+player_achivements_gosterge_finish(Mode, SeatNum, WhoHasGosterge, Has8Tashes) ->
+    player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, gosterge_finish,
+                       undefined, [], false, false, false, false).
+
+%% player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, FinishType, Revealer, WrongRejects,
+%%                    WinReveal, RevealWithOkey, RevealWithPairs, WithColor) -> [{AchId}]
+player_achivements(Mode, SeatNum, WhoHasGosterge, Has8Tashes, FinishType, Revealer, WrongRejects,
+                   WinReveal, RevealWithOkey, RevealWithPairs, WithColor) ->
+    L=[
+       %% 1
+       {?ACH_GOSTERGE_SHOWN, SeatNum == WhoHasGosterge},
+       %% 2
+       {?ACH_WIN_REVEAL, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso (not RevealWithPairs) andalso (not WithColor)},
+       %% 3
+       {?ACH_WIN_REVEAL_WITH_OKEY, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso (not RevealWithPairs) andalso (not WithColor)},
+       %% 4
+       {?ACH_WIN_REVEAL_WITH_PAIRS, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso RevealWithPairs andalso (not WithColor)},
+       %% 5
+       {?ACH_WIN_REVEAL_WITH_OKEY_PAIRS, FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso RevealWithPairs andalso (not WithColor)},
+       %% 6
+       {?ACH_8_TASHES, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso lists:member(SeatNum, Has8Tashes)},
+       %% 7
+       {?ACH_WIN_REVEAL_WITH_COLOR, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso (not RevealWithPairs) andalso WithColor},
+       %% 8
+       {?ACH_WIN_REVEAL_WITH_COLOR_OKEY, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso (not RevealWithPairs) andalso WithColor},
+       %% 9
+       {?ACH_WIN_REVEAL_WITH_COLOR_PAIRS, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso (not RevealWithOkey) andalso RevealWithPairs andalso WithColor},
+       %% 10
+       {?ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS, (Mode == ?MODE_EVENODD orelse Mode == ?MODE_COLOR) andalso FinishType == reveal andalso SeatNum == Revealer andalso WinReveal andalso RevealWithOkey andalso RevealWithPairs andalso WithColor},
+       %% 11
+       {?ACH_FAIL_REVEAL, FinishType == reveal andalso SeatNum == Revealer andalso (not WinReveal)},
+       %% 12 AKA others_on_wrong_reveal
+       {?ACH_CAUGHT_BLUFF, FinishType == reveal andalso SeatNum =/= Revealer andalso (not WinReveal)},
+       %% 13
+       {?ACH_EMPTY_BOX, false}, %% XXX: By the last information it is no deduction from players to the chanak
+       %% 14
+       {?ACH_REJECT_GOOD_HAND, FinishType == reveal andalso lists:member(SeatNum, WrongRejects)},
+       %% 15
+       {?ACH_GOSTERGE_WINNER, Mode == ?MODE_COUNTDOWN andalso FinishType == gosterge_finish andalso SeatNum == WhoHasGosterge}
+      ],
+    [Ach || {Ach, true} <- L].
+
+chanak_win_achievements() ->
+    [?ACH_WIN_REVEAL_WITH_OKEY,
+     ?ACH_WIN_REVEAL_WITH_PAIRS,
+     ?ACH_WIN_REVEAL_WITH_OKEY_PAIRS,
+     ?ACH_8_TASHES,
+     ?ACH_WIN_REVEAL_WITH_COLOR,
+     ?ACH_WIN_REVEAL_WITH_COLOR_OKEY,
+     ?ACH_WIN_REVEAL_WITH_COLOR_PAIRS,
+     ?ACH_WIN_REVEAL_WITH_COLOR_OKEY_PAIRS].
+
+get_pointing_rules(ScoringMode) ->
+    {_, Rules} = lists:keyfind(ScoringMode, 1, points_matrix()),
+    Rules.
+
+%% TODO: Check the table
+points_matrix() ->
+    [%%          1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 <--- achievement number
+     {standard, [1,  3,  6,  6, 12,  0,  0,  0,  0,  0, -9,  3,  0, -1,  0]},
+     {odd,      [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  0, -1,  0]},
+     {even,     [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
+     {ybodd,    [1,  3,  6,  6, 12, 12, 24, 48, 48, 96, -9,  3,  0, -1,  0]},
+     {ybeven,   [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
+     {rbodd,    [2,  6, 12, 12, 24, 24, 48, 96, 96,192,-18,  6,  0, -1,  0]},
+     {rbeven,   [4, 12, 24, 24, 48, 48, 96,192,192,384,-36, 12,  0, -1,  0]},
+     {countdown,[1,  2,  4,  4,  8,  0,  0,  0,  0,  0, -2,  0,  0, -1,  1]}
+    ].
+
+get_chanak_refilling_points(ScoringMode) ->
+    {_, RefillingPoints} = lists:keyfind(ScoringMode, 1, chanak_refilling_table()),
+    RefillingPoints.
+
+chanak_refilling_table() ->
+    [
+     {standard, 0},
+     {odd,      8},
+     {even,     8},
+     {ybodd,   16},
+     {ybeven,  16},
+     {rbodd,   16},
+     {rbeven,  16},
+     {countdown,0}
+    ].
+%%===================================================================
+
+%% @spec get_scoring_mode(GameMode, Gosterge) ->  ScoringMode
+%% @end
+get_scoring_mode(?MODE_STANDARD, _) ->  standard;
+get_scoring_mode(?MODE_COUNTDOWN, _) -> countdown;
+get_scoring_mode(?MODE_COLOR, {Color, Val}) when (Val rem 2) == 0 -> get_scoring_mode_c_even(b2c(Color));
+get_scoring_mode(?MODE_COLOR, {Color, Val}) when (Val rem 2) == 1 -> get_scoring_mode_c_odd(b2c(Color));
+get_scoring_mode(?MODE_EVENODD, {_Color, Val}) when (Val rem 2) == 0 -> even;
+get_scoring_mode(?MODE_EVENODD, {_Color, Val}) when (Val rem 2) == 1 -> odd.
+
+get_scoring_mode_c_odd(C) when C == yellow; C == blue -> ybodd;
+get_scoring_mode_c_odd(C) when C == black; C == red -> rbodd.
+get_scoring_mode_c_even(C) when C == yellow; C == blue -> ybeven;
+get_scoring_mode_c_even(C) when C == black; C == red -> rbeven.
+
+b2c(1) -> red;
+b2c(2) -> blue;
+b2c(3) -> yellow;
+b2c(4) -> black.
+
+
+%% Tests
+test_test_() ->
+    [{"is_pair",
+      [?_assertEqual(true,  is_pair([{1,3}, {1,3}])),
+       ?_assertEqual(true,  is_pair([{4,13}, {4,13}])),
+       ?_assertEqual(false, is_pair([{4,12}, {4,13}])),
+       ?_assertEqual(false, is_pair([{1,1}, {4,8}])),
+       ?_assertEqual(true,  is_pair([okey, {3,8}])),
+       ?_assertEqual(true,  is_pair([{2,3}, okey])),
+       ?_assertEqual(true,  is_pair([okey, okey])),
+       ?_assertEqual(false, is_pair([{2,4}, {4,2}, {3,3}])),
+       ?_assertEqual(false, is_pair([{2,4}])),
+       ?_assertEqual(false, is_pair([okey])),
+       ?_assertEqual(false, is_pair([okey, okey, {2,6}]))
+      ]},
+     {"is_set",
+      [?_assertEqual(true,  is_set([{1,3}, {3,3}, {2,3}])),
+       ?_assertEqual(true,  is_set([{4,8}, okey, {2,8}])),
+       ?_assertEqual(true,  is_set([{4,3}, okey, okey])),
+       ?_assertEqual(true,  is_set([{4,13}, {1,13}, {3,13}, {2,13}])),
+       ?_assertEqual(true,  is_set([okey, {1,13}, {3,13}, {2,13}])),
+       ?_assertEqual(true,  is_set([okey, okey, {3,13}, {2,13}])),
+       ?_assertEqual(false, is_set([{2,6}])),
+       ?_assertEqual(false, is_set([okey])),
+       ?_assertEqual(false, is_set([{3,4}, {2,6}])),
+       ?_assertEqual(false, is_set([{2,3}, {4,3}])),
+       ?_assertEqual(false, is_set([okey, okey])),
+       ?_assertEqual(false, is_set([{3,4}, {1,4}, {3,4}])),
+       ?_assertEqual(false, is_set([{3,4}, {1,4}, {2,5}])),
+       ?_assertEqual(false, is_set([{3,4}, okey, {2,5}])),
+       ?_assertEqual(false, is_set([{2,5}, {3,5}, {4,5}, {1,6}])),
+       ?_assertEqual(false, is_set([{2,5}, {3,5}, {4,5}, {2,5}])),
+       ?_assertEqual(false, is_set([{2,3}, {3,3}, {4,3}, {1,3}, {3,1}])),
+       ?_assertEqual(false, is_set([{2,3}, okey, {4,3}, {1,3}, {3,3}]))
+      ]},
+     {"is_run",
+      [?_assertEqual(false, is_run([{1,3}])),
+       ?_assertEqual(false, is_run([okey])),
+       ?_assertEqual(false, is_run([{2,2}, {2,3}])),
+       ?_assertEqual(false, is_run([okey, {2,3}])),
+       ?_assertEqual(false, is_run([{4,1}, {2,3}])),
+       ?_assertEqual(true,  is_run([{4,4}, {4,6}, {4,5}])),
+       ?_assertEqual(true,  is_run([okey, {4,6}, {4,5}])),
+       ?_assertEqual(true,  is_run([okey, {4,6}, okey])),
+       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,13}])),
+       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,11}, {1,13}])),
+       ?_assertEqual(true,  is_run([{2,4}, {2,6}, {2,7}, {2,5}])),
+       ?_assertEqual(false, is_run([{2,4}, {1,6}, {2,7}, {2,5}])),
+       ?_assertEqual(true,  is_run([{1,12}, {1,1}, {1,11}, {1,10}, {1,13}])),
+       ?_assertEqual(false, is_run([{1,12}, {1,1}, {1,11}, {1,11}, {1,13}])),
+       ?_assertEqual(false, is_run([{1,12}, {1,1}, {1,2}, {1,11}, {1,13}])),
+       ?_assertEqual(true,  is_run([{3,6}, {3,8}, okey, {3,5}, okey])),
+       ?_assertEqual(true,  is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}])),
+       ?_assertEqual(false, is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}, {3,2}])),
+       ?_assertEqual(false, is_run([{3,6}, {3,8}, okey, {3,5}, {3,9}, {1,2}]))
+      ]}
+    ].

+ 78 - 129
apps/server/src/okey/game_okey_ng_table_trn.erl → apps/server/src/okey/game_okey_table.erl

@@ -4,7 +4,7 @@
 %%%
 %%% Created : Oct 15, 2012
 %%% -------------------------------------------------------------------
--module(game_okey_ng_table_trn).
+-module(game_okey_table).
 
 -behaviour(gen_fsm).
 %% --------------------------------------------------------------------
@@ -14,6 +14,7 @@
 -include_lib("server/include/basic_types.hrl").
 -include_lib("server/include/settings.hrl").
 -include_lib("server/include/game_okey.hrl").
+-include_lib("server/include/game_state.hrl").
 -include_lib("server/include/requests.hrl").
 
 %% --------------------------------------------------------------------
@@ -47,59 +48,7 @@
          finish_info        :: term()
         }).
 
--record(state,
-        {%% Fixed parameters
-         game_id              :: pos_integer(),
-         table_id             :: pos_integer(),
-         table_name           :: string(),
-         parent               :: {atom(), pid()},
-         relay                :: pid(),
-         mult_factor          :: integer(),
-         slang_flag           :: boolean(),
-         observer_flag        :: boolean(),
-         tournament_type      :: atom(), %%  standalone | elimination | pointing | lucky
-         speed                :: slow | normal | fast,
-         turn_timeout         :: integer(),
-         reveal_confirmation_timeout    :: integer(),
-         ready_timeout        :: integer(),
-         round_timeout        :: infinity | integer(),
-         set_timeout          :: infinity | integer(),
-         game_mode            :: standard | color | evenodd | countdown,
-         rounds               :: undefined | integer(), %% Not defined for countdown game type
-         reveal_confirmation  :: boolean(),
-         next_series_confirmation :: yes_exit | no_exit | no,
-         pause_mode           :: disabled | normal,
-         gosterge_finish_allowed :: undefined | boolean(), %% Only defined for countdown game type
-         social_actions_enabled :: boolean(),
-         tour                 :: undefined | integer(),
-         tours                :: undefined | integer(),
-         %% Dynamic parameters
-         desk_rule_pid        :: undefined | pid(),
-         players,             %% The register of table players
-         tournament_table     :: list(), %% [{TurnNum, TurnRes}], TurnRes = [{PlayerId, Points, Status}]
-         start_seat           :: integer(), %% The player who moves first
-         cur_round            :: integer(),
-         desk_state           :: #desk_state{}, %% For tracking current state of a game on the table
-         scoring_state        :: term(), %% generated by a scoring module
-         reveal_confirmation_list :: list(), %% {SeatNum, Answer}
-         wait_list            :: list(),
-         timeout_timer        :: undefined | reference(),
-         timeout_magic        :: term(),
-         round_timer          :: undefined | reference(),
-         set_timer            :: undefined | reference(),
-         paused_statename     :: atom(), %% For storing a statename before pause
-         paused_timeout_value :: integer() %% For storing remain timeout value
-        }).
-
--record(player,
-        {
-         id              :: pos_integer(), %% Player Id
-         seat_num        :: integer(),
-         user_id         :: binary(),
-         is_bot          :: boolean(),
-         info            :: #'PlayerInfo'{},
-         connected       :: boolean()
-        }).
+-record(player, {?PLAYER}).
 
 -define(STATE_WAITING_FOR_START, state_waiting_for_start).
 -define(STATE_PLAYING, state_playing).
@@ -111,8 +60,8 @@
 -define(HAND_SIZE, 14).
 -define(SEATS_NUM, 4).
 -define(RELAY, relay_ng).
--define(DESK, game_okey_ng_desk).
--define(SCORING, game_okey_ng_scoring).
+-define(DESK, game_okey_desk).
+-define(SCORING, game_okey_scoring).
 
 %% ====================================================================
 %% External functions
@@ -175,14 +124,14 @@ init([GameId, TableId, Params]) ->
     {ok, Relay} = ?RELAY:start(RelayParams),
 
 %%    {ok, ObserverPid} = game_observer:mypid(),
-%%    [begin gas:info(?MODULE,"OKEY_NG_TABLE_TRN subscribe observer to player ~p", [PI]), 
+%%    [begin gas:info(?MODULE,"OKEY_TABLE subscribe observer to player ~p", [PI]), 
 %%               ?RELAY:subscribe(Relay, ObserverPid, PlayerId, observer) end|| {PlayerId, #'PlayerInfo'{robot = false}, _, _} = PI <- PlayersInfo],
 
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN_DBG <~p,~p> Set timeout: ~p, round timeout: ~p.", [GameId, TableId, SetTimeout, RoundTimeout]),
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN_DBG <~p,~p> PlayersInfo: ~p.", [GameId, TableId, PlayersInfo]),
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Started.", [GameId, TableId]),
     parent_notify_table_created(Parent, TableId, Relay),
-    {ok, ?STATE_WAITING_FOR_START, #state{game_id = GameId,
+    {ok, ?STATE_WAITING_FOR_START, #okey_state{game_id = GameId,
                                           table_id = TableId,
                                           table_name = TableName,
                                           parent = Parent,
@@ -214,13 +163,13 @@ init([GameId, TableId, Params]) ->
                                          }}.
 
 handle_event({parent_message, Message}, StateName,
-             #state{game_id = GameId, table_id = TableId} = StateData) ->
+             #okey_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received message from the parent: ~p.",
           [GameId, TableId, Message]),
     handle_parent_message(Message, StateName, StateData);
 
 handle_event({relay_message, Message}, StateName,
-             #state{game_id = GameId, table_id = TableId} =  StateData) ->
+             #okey_state{game_id = GameId, table_id = TableId} =  StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received message from the relay: ~p.",
           [GameId, TableId, Message]),
     handle_relay_message(Message, StateName, StateData);
@@ -229,7 +178,7 @@ handle_event(_Event, StateName, StateData) ->
     {next_state, StateName, StateData}.
 
 handle_sync_event({player_action, PlayerId, Action}, From, StateName,
-                  #state{players = Players} = StateData) ->
+                  #okey_state{players = Players} = StateData) ->
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             handle_player_action(Player, Action, From, StateName, StateData);
@@ -242,42 +191,42 @@ handle_sync_event(_Event, _From, StateName, StateData) ->
     {reply, Reply, StateName, StateData}.
 
 handle_info({timeout, Magic}, ?STATE_PLAYING,
-            #state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
+            #okey_state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Move timeout. Do an automatic move(s).", [GameId, TableId]),
     do_timeout_moves(StateData);
 
 handle_info({round_timeout, Round}, ?STATE_PLAYING,
-            #state{cur_round = Round, desk_state = DeskState, game_id = GameId,
+            #okey_state{cur_round = Round, desk_state = DeskState, game_id = GameId,
                    table_id = TableId, timeout_timer = TRef} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Time to finish round ~p because the round timeout.", [GameId, TableId, Round]),
     if TRef =/= undefined -> erlang:cancel_timer(TRef);
        true -> do_nothing
     end,
-    finalize_round(StateData#state{desk_state = DeskState#desk_state{finish_reason = timeout}});
+    finalize_round(StateData#okey_state{desk_state = DeskState#desk_state{finish_reason = timeout}});
 
 handle_info(set_timeout, StateName,
-            #state{cur_round = Round, desk_state = DeskState, game_id = GameId,
+            #okey_state{cur_round = Round, desk_state = DeskState, game_id = GameId,
                    table_id = TableId, timeout_timer = TRef} = StateData) when
   StateName =/= ?STATE_SET_FINISHED ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Time to finish round ~p and the set because the set timeout.", [GameId, TableId, Round]),
     if TRef =/= undefined -> erlang:cancel_timer(TRef);
        true -> do_nothing
     end,
-    finalize_round(StateData#state{desk_state = DeskState#desk_state{finish_reason = set_timeout}});
+    finalize_round(StateData#okey_state{desk_state = DeskState#desk_state{finish_reason = set_timeout}});
 
 handle_info({timeout, Magic}, ?STATE_REVEAL_CONFIRMATION,
-            #state{timeout_magic = Magic, wait_list = WL, game_id = GameId, table_id = TableId,
+            #okey_state{timeout_magic = Magic, wait_list = WL, game_id = GameId, table_id = TableId,
                    reveal_confirmation_list = CList} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Time to check reveal confirmation responses.", [GameId, TableId]),
     NewCList = lists:foldl(fun(SeatNum, Acc) -> [{SeatNum, false} | Acc] end, CList, WL),
-    finalize_round(StateData#state{reveal_confirmation_list = NewCList});
+    finalize_round(StateData#okey_state{reveal_confirmation_list = NewCList});
 
-handle_info(Info, StateName, #state{game_id = GameId, table_id = TableId} = StateData) ->
+handle_info(Info, StateName, #okey_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Unexpected message(info) received at state <~p>: ~p.",
           [GameId, TableId, StateName, Info]),
     {next_state, StateName, StateData}.
 
-terminate(Reason, StateName, #state{game_id = GameId, table_id = TableId, relay = Relay}) ->
+terminate(Reason, StateName, #okey_state{game_id = GameId, table_id = TableId, relay = Relay}) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Shutting down at state: <~p>. Reason: ~p",
           [GameId, TableId, StateName, Reason]),
     relay_stop(Relay),
@@ -293,17 +242,17 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
 %% handle_parent_message(Msg, StateName, StateData)
 
 handle_parent_message({register_player, RequestId, UserInfo, PlayerId, SeatNum}, StateName,
-                      #state{table_id = TableId, players = Players,
+                      #okey_state{table_id = TableId, players = Players,
                              parent = Parent, relay = Relay} = StateData) ->
     #'PlayerInfo'{id = UserId, robot = IsBot} = UserInfo,
     NewPlayers = reg_player(PlayerId, SeatNum, UserId, IsBot, UserInfo, _Connected = false, Players),
     relay_register_player(Relay, UserId, PlayerId),
     %% TODO: Send notificitations to gamesessions (we have no such notification)
     parent_confirm_registration(Parent, TableId, RequestId),
-    {next_state, StateName, StateData#state{players = NewPlayers}};
+    {next_state, StateName, StateData#okey_state{players = NewPlayers}};
 
 handle_parent_message({replace_player, RequestId, UserInfo, PlayerId, SeatNum}, StateName,
-                      #state{game_id = GameId, table_id = TableId, players = Players,
+                      #okey_state{game_id = GameId, table_id = TableId, players = Players,
                              parent = Parent, relay = Relay} = StateData) ->
     #'PlayerInfo'{id = UserId, robot = IsBot} = UserInfo,
     #player{id = OldPlayerId} = get_player_by_seat_num(SeatNum, Players),
@@ -314,10 +263,10 @@ handle_parent_message({replace_player, RequestId, UserInfo, PlayerId, SeatNum},
     ReplaceMsg = create_player_left(SeatNum, UserInfo, Players),
     relay_publish_ge(Relay, ReplaceMsg, StateData),
     parent_confirm_replacement(Parent, TableId, RequestId),
-    {next_state, StateName, StateData#state{players = NewPlayers2}};
+    {next_state, StateName, StateData#okey_state{players = NewPlayers2}};
 
 handle_parent_message(start_round, StateName,
-                      #state{game_id = GameId, game_mode = GameMode, cur_round = CurRound,
+                      #okey_state{game_id = GameId, game_mode = GameMode, cur_round = CurRound,
                              gosterge_finish_allowed = GostergeFinishAllowed,
                              start_seat = LastStartSeat, players = Players,
                              relay = Relay, turn_timeout = TurnTimeout,
@@ -360,7 +309,7 @@ handle_parent_message(start_round, StateName,
                         end;
                     true -> SetTRef
                  end,
-    NewStateData = StateData#state{cur_round = NewCurRound,
+    NewStateData = StateData#okey_state{cur_round = NewCurRound,
                                    start_seat = StartSeat,
                                    desk_rule_pid = Desk,
                                    desk_state = DeskState,
@@ -383,7 +332,7 @@ handle_parent_message(start_round, StateName,
     {next_state, ?STATE_PLAYING, NewStateData};
 
 handle_parent_message(show_round_result, StateName,
-                      #state{relay = Relay, scoring_state = ScoringState,
+                      #okey_state{relay = Relay, scoring_state = ScoringState,
                              game_id = GameId, table_id = TableId} = StateData) ->
     {FinishInfo, RoundScore, AchsPoints, TotalScore} = ?SCORING:last_round_result(ScoringState),
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> RoundScore: ~p Total score: ~p.", [GameId, TableId, RoundScore, TotalScore]),
@@ -408,48 +357,48 @@ handle_parent_message(show_round_result, StateName,
                                                           AchsPoints, StateData)
           end,
     relay_publish_ge(Relay, Msg, StateData),
-    {next_state, StateName, StateData#state{}};
+    {next_state, StateName, StateData#okey_state{}};
 
 %% Results = [{PlayerId, Position, Score, Status}] Status = winner | loser | eliminated | none
 handle_parent_message({show_series_result, Results}, StateName,
-                      #state{game_id = GameId, relay = Relay, players = Players,
+                      #okey_state{game_id = GameId, relay = Relay, players = Players,
                              next_series_confirmation = Confirm} = StateData) ->
     Msg = create_okey_series_ended(Results, Players, Confirm),
     relay_publish_ge(Relay, Msg, StateData),
-    {next_state, StateName, StateData#state{}};
+    {next_state, StateName, StateData#okey_state{}};
 
 %% Results = [{UserId, Position, Score, Status}] Status = active | eliminated
 handle_parent_message({tour_result, TourNum, Results}, StateName,
-                      #state{game_id = GameId, relay = Relay, tournament_table = TTable} = StateData) ->
+                      #okey_state{game_id = GameId, relay = Relay, tournament_table = TTable} = StateData) ->
     NewTTable = [{TourNum, Results} | TTable],
     Msg = create_okey_tour_result(TourNum, Results),
     relay_publish_ge(Relay, Msg, StateData),
-    {next_state, StateName, StateData#state{tournament_table = NewTTable}};
+    {next_state, StateName, StateData#okey_state{tournament_table = NewTTable}};
 
 handle_parent_message({playing_tables_num, Num}, StateName,
-                      #state{relay = Relay} = StateData) ->
+                      #okey_state{relay = Relay} = StateData) ->
 %%XXX    Msg = create_okey_playing_tables(Num),
 %%    relay_publish_ge(Relay, Msg),
     {next_state, StateName, StateData};
 
 handle_parent_message(rejoin_players, StateName,
-                      #state{game_id = GameId, relay = Relay,
+                      #okey_state{game_id = GameId, relay = Relay,
                              players = Players} = StateData) ->
     [relay_unregister_player(Relay, P#player.id, {rejoin, GameId}) || P <- players_to_list(Players)],
-    {next_state, StateName, StateData#state{players = players_init()}};
+    {next_state, StateName, StateData#okey_state{players = players_init()}};
 
 
 handle_parent_message(disconnect_players, StateName,
-                      #state{relay = Relay, players = Players} = StateData) ->
+                      #okey_state{relay = Relay, players = Players} = StateData) ->
     [relay_unregister_player(Relay, P#player.id, game_over) || P <- players_to_list(Players)],
-    {next_state, StateName, StateData#state{players = players_init()}};
+    {next_state, StateName, StateData#okey_state{players = players_init()}};
 
 
 handle_parent_message(stop, _StateName, StateData) ->
     {stop, normal, StateData};
 
 handle_parent_message(Message, StateName,
-                      #state{game_id = GameId, table_id = TableId} = StateData) ->
+                      #okey_state{game_id = GameId, table_id = TableId} = StateData) ->
     ?ERROR("OKEY_NG_TABLE_TRN <~p,~p> Unexpected parent message received in state <~p>: ~p. State: ~p. Stopping.",
            [GameId, TableId, StateName, Message, StateName]),
     {stop, unexpected_parent_message, StateData}.
@@ -460,32 +409,32 @@ handle_parent_message(Message, StateName,
 %% handle_relay_message(Msg, StateName, StateData)
 
 handle_relay_message({player_connected, PlayerId} = Msg, StateName,
-                     #state{parent = Parent, game_id = GameId,
+                     #okey_state{parent = Parent, game_id = GameId,
                             table_id = TableId, players = Players} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received nofitication from the relay: ~p", [GameId, TableId, Msg]),
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             NewPlayers = store_player_rec(Player#player{connected = true}, Players),
             parent_send_player_connected(Parent, TableId, PlayerId),
-            {next_state, StateName, StateData#state{players = NewPlayers}};
+            {next_state, StateName, StateData#okey_state{players = NewPlayers}};
         error ->
             {next_state, StateName, StateData}
     end;
 
 
 handle_relay_message({player_disconnected, PlayerId}, StateName,
-                     #state{parent = Parent, table_id = TableId, players = Players} = StateData) ->
+                     #okey_state{parent = Parent, table_id = TableId, players = Players} = StateData) ->
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             NewPlayers = store_player_rec(Player#player{connected = false}, Players),
             parent_send_player_disconnected(Parent, TableId, PlayerId),
-            {next_state, StateName, StateData#state{players = NewPlayers}};
+            {next_state, StateName, StateData#okey_state{players = NewPlayers}};
         error ->
             {next_state, StateName, StateData}
     end;
 
 handle_relay_message({subscriber_added, PlayerId, SubscrId} = Msg, StateName,
-                     #state{relay = Relay, game_id = GameId,
+                     #okey_state{relay = Relay, game_id = GameId,
                             table_id = TableId, tournament_table = TTable,
                             players = Players} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received nofitication from the relay: ~p", [GameId, TableId, Msg]),
@@ -515,7 +464,7 @@ handle_relay_message({subscriber_added, PlayerId, SubscrId} = Msg, StateName,
     end,
     {next_state, StateName, StateData};
 
-handle_relay_message(Message, StateName, #state{game_id = GameId, table_id = TableId} = StateData) ->
+handle_relay_message(Message, StateName, #okey_state{game_id = GameId, table_id = TableId} = StateData) ->
     ?ERROR("OKEY_NG_TABLE_TRN <~p,~p> Unknown relay message received in state <~p>: ~p. State: ~p. Stopping.",
            [GameId, TableId, StateName, Message]),
     {next_state, StateName, StateData}.
@@ -527,7 +476,7 @@ handle_relay_message(Message, StateName, #state{game_id = GameId, table_id = Tab
 handle_player_action(#player{id = PlayerId, seat_num = SeatNum, user_id = UserId},
                      {submit, #game_action{action = Action, args = Args} = GA}, From,
                      StateName,
-                     #state{game_id = GameId, table_id = TableId} = StateData) ->
+                     #okey_state{game_id = GameId, table_id = TableId} = StateData) ->
     {Keys,Values} = lists:unzip(Args),
     ExtAction = list_to_tuple([Action|Values]),
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Player <~p> (~p)~n submit the game action: ~p.",
@@ -537,7 +486,7 @@ handle_player_action(#player{id = PlayerId, seat_num = SeatNum, user_id = UserId
 handle_player_action(#player{id = PlayerId, user_id = UserId},
                      {signal, {pause_game, _}=Signal}, _From,
                      StateName,
-                     #state{table_id = TableId, game_id = GameId, timeout_timer = TRef,
+                     #okey_state{table_id = TableId, game_id = GameId, timeout_timer = TRef,
                             pause_mode = PauseMode, relay = Relay} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received signal from player <~p> : ~p. PauseMode: ~p",
           [GameId, TableId, PlayerId, Signal, PauseMode]),
@@ -552,7 +501,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
                                  T -> T
                              end,
                    relay_publish(Relay, create_game_paused_pause(UserId, GameId)),
-                   {reply, 0, ?STATE_PAUSE, StateData#state{paused_statename = StateName,
+                   {reply, 0, ?STATE_PAUSE, StateData#okey_state{paused_statename = StateName,
                                                             paused_timeout_value = Timeout,
                                                             timeout_magic = undefined}};
                true ->
@@ -564,7 +513,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
 handle_player_action(#player{id = PlayerId, user_id = UserId},
                      {signal, {resume_game, _}=Signal}, _From,
                      StateName,
-                     #state{table_id = TableId, game_id = GameId, pause_mode = PauseMode,
+                     #okey_state{table_id = TableId, game_id = GameId, pause_mode = PauseMode,
                             relay = Relay, paused_statename = ResumedStateName,
                             paused_timeout_value = Timeout} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received signal from player <~p> : ~p. PauseMode: ~p",
@@ -576,7 +525,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
             if StateName == ?STATE_PAUSE ->
                    relay_publish(Relay, create_game_paused_resume(UserId, GameId)),
                    {Magic, TRef} = start_timer(Timeout),
-                   {reply, 0, ResumedStateName, StateData#state{timeout_timer = TRef,
+                   {reply, 0, ResumedStateName, StateData#okey_state{timeout_timer = TRef,
                                                                 timeout_magic = Magic}};
                true ->
                    {reply, {error, game_is_not_paused}, StateName, StateData}
@@ -586,7 +535,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
 
 handle_player_action(#player{id = PlayerId},
                      {signal, Signal}, _From, StateName,
-                     #state{table_id = TableId, game_id = GameId} = StateData) ->
+                     #okey_state{table_id = TableId, game_id = GameId} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Received signal from player <~p> : ~p. Ignoring.",
           [GameId, TableId, PlayerId, Signal]),
     {reply, ok, StateName, StateData};
@@ -655,7 +604,7 @@ do_action(_SeatNum, #okey_reveal{}, _From, StateName, StateData) ->
 
 
 do_action(SeatNum, #okey_challenge{challenge = Challenge}, From,
-          ?STATE_REVEAL_CONFIRMATION = StateName, #state{reveal_confirmation_list = CList,
+          ?STATE_REVEAL_CONFIRMATION = StateName, #okey_state{reveal_confirmation_list = CList,
                                                          wait_list = WL,
                                                          timeout_timer = TRef} = StateData) ->
     case lists:member(SeatNum, WL) of
@@ -666,11 +615,11 @@ do_action(SeatNum, #okey_challenge{challenge = Challenge}, From,
             if NewWL == [] ->
                    gen_fsm:reply(From, ok),
                    erlang:cancel_timer(TRef),
-                   finalize_round(StateData#state{timeout_timer = undefined,
+                   finalize_round(StateData#okey_state{timeout_timer = undefined,
                                                   reveal_confirmation_list = NewCList});
                true ->
                    {reply, ok, StateName,
-                    StateData#state{reveal_confirmation_list = NewCList,
+                    StateData#okey_state{reveal_confirmation_list = NewCList,
                                     wait_list = NewWL}}
             end;
         false ->
@@ -690,7 +639,7 @@ do_action(_SeatNum, _UnsupportedAction, _From, StateName, StateData) ->
 
 
 %%===================================================================
-do_timeout_moves(#state{desk_rule_pid = Desk, desk_state = DeskState} = StateData) ->
+do_timeout_moves(#okey_state{desk_rule_pid = Desk, desk_state = DeskState} = StateData) ->
     #desk_state{cur_seat = CurSeatNum,
                 hands = Hands,
                 state = DeskStateName} = DeskState,
@@ -721,7 +670,7 @@ do_timeout_moves(#state{desk_rule_pid = Desk, desk_state = DeskState} = StateDat
 %%===================================================================
 
 do_game_action(SeatNum, GameAction, From, StateName,
-               #state{desk_rule_pid = Desk} = StateData) ->
+               #okey_state{desk_rule_pid = Desk} = StateData) ->
     gas:info(?MODULE,"OKEY_NG_TABLE_TRN do_game_action SeatNum: ~p  GameAction: ~p", [SeatNum, GameAction]),
     case desk_player_action(Desk, SeatNum, GameAction) of
         {ok, Events} ->
@@ -746,7 +695,7 @@ do_game_action(SeatNum, GameAction, From, StateName,
     end.
 
 
-process_game_events(Events, #state{desk_state = DeskState, players = Players,
+process_game_events(Events, #okey_state{desk_state = DeskState, players = Players,
                                    game_id = GameId, relay = Relay, timeout_timer = OldTRef,
                                    round_timeout = RoundTimeout, round_timer = RoundTRef,
                                    turn_timeout = TurnTimeout} = StateData) ->
@@ -756,24 +705,24 @@ process_game_events(Events, #state{desk_state = DeskState, players = Players,
         state_finished ->
             if is_integer(RoundTimeout) -> erlang:cancel_timer(RoundTRef); true -> do_nothing end,
             erlang:cancel_timer(OldTRef),
-            on_game_finish(StateData#state{desk_state = NewDeskState});
+            on_game_finish(StateData#okey_state{desk_state = NewDeskState});
         state_take ->
             case [E || {next_player, _} = E <- Events] of %% Find a next player event
                 [] ->
-                    {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState}};
+                    {next_state, ?STATE_PLAYING, StateData#okey_state{desk_state = NewDeskState}};
                 [_|_] ->
                     erlang:cancel_timer(OldTRef),
                     {Magic, TRef} = start_timer(TurnTimeout),
-                    {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState,
+                    {next_state, ?STATE_PLAYING, StateData#okey_state{desk_state = NewDeskState,
                                                                  timeout_timer = TRef,
                                                                  timeout_magic = Magic}}
             end;
         state_discard ->
-            {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState}}
+            {next_state, ?STATE_PLAYING, StateData#okey_state{desk_state = NewDeskState}}
     end.
 
 
-on_game_finish(#state{desk_state = DeskState,
+on_game_finish(#okey_state{desk_state = DeskState,
                       reveal_confirmation = RevealConfirmation,
                       reveal_confirmation_timeout = Timeout} = StateData) ->
     #desk_state{finish_reason = FinishReason,
@@ -784,7 +733,7 @@ on_game_finish(#state{desk_state = DeskState,
            WL = [SeatNum || {SeatNum, _} <- Hands, SeatNum =/= Revealer],
            {Magic, TRef} = start_timer(Timeout),
            {next_state, ?STATE_REVEAL_CONFIRMATION,
-            StateData#state{reveal_confirmation_list = [],
+            StateData#okey_state{reveal_confirmation_list = [],
                             wait_list = WL,
                             timeout_timer = TRef,
                             timeout_magic = Magic}};
@@ -794,7 +743,7 @@ on_game_finish(#state{desk_state = DeskState,
 
 %%===================================================================
 
-finalize_round(#state{desk_state = #desk_state{finish_reason = FinishReason,
+finalize_round(#okey_state{desk_state = #desk_state{finish_reason = FinishReason,
                                                finish_info = FinishInfo,
                                                hands = Hands,
                                                gosterge = Gosterge,
@@ -830,11 +779,11 @@ finalize_round(#state{desk_state = #desk_state{finish_reason = FinishReason,
     if GameOver ->
            gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Set is over.", [GameId, TableId]),
            parent_send_game_res(Parent, TableId, NewScoringState, RoundScorePl, TotalScorePl),
-           {next_state, ?STATE_SET_FINISHED, StateData#state{scoring_state = NewScoringState}};
+           {next_state, ?STATE_SET_FINISHED, StateData#okey_state{scoring_state = NewScoringState}};
        true ->
            gas:info(?MODULE,"OKEY_NG_TABLE_TRN <~p,~p> Round is over.", [GameId, TableId]),
            parent_send_round_res(Parent, TableId, NewScoringState, RoundScorePl, TotalScorePl),
-           {next_state, ?STATE_FINISHED, StateData#state{scoring_state = NewScoringState}}
+           {next_state, ?STATE_FINISHED, StateData#okey_state{scoring_state = NewScoringState}}
     end.
 
 
@@ -844,7 +793,7 @@ finalize_round(#state{desk_state = #desk_state{finish_reason = FinishReason,
 handle_desk_events([], DeskState, _Players, _Relay, _StateData) ->
     DeskState;
 
-handle_desk_events([Event | Events], DeskState, Players, Relay, #state{} = StateData) ->
+handle_desk_events([Event | Events], DeskState, Players, Relay, #okey_state{} = StateData) ->
     #desk_state{cur_seat = CurSeatNum,
                 hands = Hands,
                 discarded = Discarded,
@@ -1009,21 +958,21 @@ init_players([{PlayerId, UserInfo, SeatNum, _StartPoints} | PlayersInfo], Player
     init_players(PlayersInfo, NewPlayers).
 
 %%=================================================================
-send_to_subscriber_ge(Relay, SubscrId, Msg, #state{game_id = GameId} = _StateData) ->
+send_to_subscriber_ge(Relay, SubscrId, Msg, #okey_state{game_id = GameId} = _StateData) ->
     [Name|List] = tuple_to_list(Msg),
     Event = #game_event{game = GameId, event = Name, args = lists:zip(known_records:fields(Name),List) },
     gas:info(?MODULE,"SEND SUB ~p",[Event]),
     game_observer:log_event(Event),
     ?RELAY:table_message(Relay, {to_subscriber, SubscrId, Event}).
 
-send_to_client_ge(Relay, PlayerId, Msg, #state{game_id = GameId} = _StateData) ->
+send_to_client_ge(Relay, PlayerId, Msg, #okey_state{game_id = GameId} = _StateData) ->
     [Name|List] = tuple_to_list(Msg),
     Event = #game_event{game = GameId, event = Name, args = lists:zip(known_records:fields(Name),List) },
     gas:info(?MODULE,"SEND CLIENT ~p",[Event]),
     game_observer:log_event(Event),
     ?RELAY:table_message(Relay, {to_client, PlayerId, Event}).
 
-relay_publish_ge(Relay, Msg, #state{game_id = GameId} = _StateData) ->
+relay_publish_ge(Relay, Msg, #okey_state{game_id = GameId} = _StateData) ->
     [Name|List] = tuple_to_list(Msg),
     Event = #game_event{game = GameId, event = Name, args = lists:zip(known_records:fields(Name),List) },
     gas:info(?MODULE,"RELAY PUBLISH ~p",[Event]),
@@ -1074,7 +1023,7 @@ desk_player_action(Desk, SeatNum, Action) ->
 
 %%===================================================================
 
-create_okey_game_info(#state{table_name = TName, mult_factor = MulFactor,
+create_okey_game_info(#okey_state{table_name = TName, mult_factor = MulFactor,
                              slang_flag = SlangFlag, observer_flag = ObserverFlag,
                              speed = Speed, turn_timeout = TurnTimeout,
                              reveal_confirmation_timeout = RevealConfirmationTimeout,
@@ -1115,7 +1064,7 @@ create_okey_game_info(#state{table_name = TName, mult_factor = MulFactor,
 
 
 create_okey_game_player_state(_PlayerId, ?STATE_WAITING_FOR_START,
-                              #state{cur_round = CurRound, scoring_state = ScoringState,
+                              #okey_state{cur_round = CurRound, scoring_state = ScoringState,
                                      set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     Chanak = ?SCORING:chanak(ScoringState),
     SetTimeout = if SetTimeout1 == infinity -> null;
@@ -1135,7 +1084,7 @@ create_okey_game_player_state(_PlayerId, ?STATE_WAITING_FOR_START,
                             set_timeout = SetTimeout};
 
 create_okey_game_player_state(PlayerId, ?STATE_PLAYING,
-                              #state{timeout_timer = TRef, cur_round = CurRound,
+                              #okey_state{timeout_timer = TRef, cur_round = CurRound,
                                      players = Players, desk_state = DeskState,
                                      scoring_state = ScoringState, round_timer = RoundTRef,
                                      round_timeout = RoundTimeout1, set_timer = SetTRef,
@@ -1179,7 +1128,7 @@ create_okey_game_player_state(PlayerId, ?STATE_PLAYING,
                             set_timeout = SetTimeout};
 
 create_okey_game_player_state(PlayerId, ?STATE_REVEAL_CONFIRMATION,
-                              #state{timeout_timer = TRef, cur_round = CurRound,
+                              #okey_state{timeout_timer = TRef, cur_round = CurRound,
                                      players = Players, desk_state = DeskState,
                                      scoring_state = ScoringState,
                                      set_timeout = SetTimeout1, set_timer = SetTRef}) ->
@@ -1219,7 +1168,7 @@ create_okey_game_player_state(PlayerId, ?STATE_REVEAL_CONFIRMATION,
                             set_timeout = SetTimeout};
 
 create_okey_game_player_state(_PlayerId, ?STATE_FINISHED,
-                              #state{cur_round = CurRound, scoring_state = ScoringState,
+                              #okey_state{cur_round = CurRound, scoring_state = ScoringState,
                                      set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     Chanak = ?SCORING:chanak(ScoringState),
     SetTimeout = if SetTimeout1 == infinity -> null;
@@ -1239,7 +1188,7 @@ create_okey_game_player_state(_PlayerId, ?STATE_FINISHED,
                             set_timeout = SetTimeout};
 
 create_okey_game_player_state(PlayerId, ?STATE_PAUSE,
-                              #state{paused_statename = PausedStateName,
+                              #okey_state{paused_statename = PausedStateName,
                                      paused_timeout_value = Timeout
                                      } = StateData) ->
     Msg = create_okey_game_player_state(PlayerId, PausedStateName, StateData),
@@ -1248,7 +1197,7 @@ create_okey_game_player_state(PlayerId, ?STATE_PAUSE,
 
 
 create_okey_game_started(SeatNum, DeskState, CurRound,
-                         #state{scoring_state = ScoringState, round_timeout = RoundTimeout1,
+                         #okey_state{scoring_state = ScoringState, round_timeout = RoundTimeout1,
                                 set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     Chanak = ?SCORING:chanak(ScoringState),
     #desk_state{hands = Hands,
@@ -1325,7 +1274,7 @@ create_okey_tile_discarded(SeatNum, Tash, Timeouted, Players) ->
                          timeouted = Timeouted}.
 
 create_okey_round_ended_reveal(Revealer, RevealerWin, WrongRejects, RoundScore, TotalScore, PlayersAchsPoints,
-                        #state{players = Players}) ->
+                        #okey_state{players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      WinnerStatus = if SeatNum == Revealer -> RevealerWin;
@@ -1358,7 +1307,7 @@ create_okey_round_ended_reveal(Revealer, RevealerWin, WrongRejects, RoundScore,
                       next_action = next_round}. %%XXX
 
 create_okey_round_ended_tashes_out(RoundScore, TotalScore, PlayersAchsPoints,
-                                   #state{players = Players}) ->
+                                   #okey_state{players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      {_, Score} = lists:keyfind(SeatNum, 1, TotalScore),
@@ -1385,7 +1334,7 @@ create_okey_round_ended_tashes_out(RoundScore, TotalScore, PlayersAchsPoints,
                       next_action = next_round}. %%XXX
 
 create_okey_round_ended_gosterge_finish(Winner, RoundScore, TotalScore, PlayersAchsPoints,
-                                        #state{players = Players}) ->
+                                        #okey_state{players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      WinnerStatus = SeatNum == Winner,

+ 1 - 1
apps/server/src/okey_sup.erl

@@ -74,7 +74,7 @@ okey_standalone_specs(GamesNum, VirtUsersPerTable) ->
                                  {kakush_for_loser, 1},
                                  {win_game_points, 1},
                                  {mul_factor, 1},
-                                 {table_module, game_okey_ng_table_trn},
+                                 {table_module, game_okey_table},
                                  {bot_module, game_okey_bot},
                                  {bots_replacement_mode, enabled},
                                  {table_params, TableParams},

+ 99 - 148
apps/server/src/tavla/game_tavla_ng_table.erl

@@ -14,6 +14,7 @@
 -include_lib("server/include/basic_types.hrl").
 -include_lib("server/include/settings.hrl").
 -include_lib("server/include/game_tavla.hrl").
+-include_lib("server/include/game_state.hrl").
 -include_lib("server/include/requests.hrl").
 
 %% --------------------------------------------------------------------
@@ -45,58 +46,8 @@
          finish_info        :: undefined | {black | white, normal | mars}
         }).
 
--record(state,
-        {%% Fixed parameters
-         game_id              :: pos_integer(),
-         table_id             :: pos_integer(),
-         table_name           :: string(),
-         parent               :: {atom(), pid()},
-         relay                :: pid(),
-         mult_factor          :: integer(),
-         slang_flag           :: boolean(),
-         observer_flag        :: boolean(),
-         tournament_type      :: atom(), %%  standalone | elimination | pointing | lucky
-         game_mode            :: normal | kakara,
-         speed                :: slow | normal | fast,
-         turn_timeout         :: integer(),
-         ready_timeout        :: integer(),
-         round_timeout        :: infinity | integer(),
-         set_timeout          :: infinity | integer(),
-         rounds               :: undefined | integer(), %% Not defined for countdown game type
-         next_series_confirmation :: yes_exit | no_exit | no,
-         pause_mode           :: disabled | normal,
-         social_actions_enabled :: boolean(),
-         tour                 :: undefined | integer(),
-         tours                :: undefined | integer(),
-         parent_mon_ref       :: reference(),
-         tables_num           :: integer(), %% For paired mode >= 1, otherwise  = 1
-         %% Dynamic parameters
-         desk_rule_pid        :: undefined | pid(),
-         players,             %% The register of table players
-         tournament_table     :: list(), %% [{TurnNum, TurnRes}], TurnRes = [{PlayerId, Points, Status}]
-         start_color          :: color(), %% The player who moves first
-         cur_round            :: integer(),
-         desk_state           :: #desk_state{}, %% For tracking current state of a game on the table
-         scoring_state        :: term(), %% generated by a scoring module
-         wait_list            :: list(),
-         timeout_timer        :: undefined | reference(),
-         timeout_magic        :: term(),
-         round_timer          :: undefined | reference(),
-         set_timer            :: undefined | reference(),
-         paused_statename     :: atom(), %% For storing a statename before pause
-         paused_timeout_value :: integer() %% For storing remain timeout value
-        }).
 
--record(player,
-        {
-         id              :: pos_integer(), %% Player Id
-         color           :: undefined | color(),
-         seat_num        :: integer(),
-         user_id         :: binary(),
-         is_bot          :: boolean(),
-         info            :: #'PlayerInfo'{},
-         connected       :: boolean()
-        }).
+-record(player,{?PLAYER, color :: undefined | color()}).
 
 -define(STATE_WAITING_FOR_START, state_waiting_for_start).
 -define(STATE_FIRST_MOVE_COMPETITION, state_first_move_competition).
@@ -180,7 +131,7 @@ init([GameId, TableId, Params]) ->
     parent_notify_table_created(Parent, TableId, Relay),
     {_, ParentPid} = Parent,
     ParentMonRef = erlang:monitor(process, ParentPid),
-    {ok, ?STATE_WAITING_FOR_START, #state{game_id = GameId,
+    {ok, ?STATE_WAITING_FOR_START, #tavla_state{game_id = GameId,
                                           table_id = TableId,
                                           table_name = TableName,
                                           parent = Parent,
@@ -211,13 +162,13 @@ init([GameId, TableId, Params]) ->
                                          }}.
 
 handle_event({parent_message, Message}, StateName,
-             #state{game_id = GameId, table_id = TableId} = StateData) ->
+             #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> Received message from the parent: ~p.",
           [GameId, TableId, Message]),
     handle_parent_message(Message, StateName, StateData);
 
 handle_event({relay_message, Message}, StateName,
-             #state{game_id = GameId, table_id = TableId} =  StateData) ->
+             #tavla_state{game_id = GameId, table_id = TableId} =  StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> Received message from the relay: ~p.",
           [GameId, TableId, Message]),
     handle_relay_message(Message, StateName, StateData);
@@ -226,7 +177,7 @@ handle_event(_Event, StateName, StateData) ->
     {next_state, StateName, StateData}.
 
 handle_sync_event({player_action, PlayerId, Action}, From, StateName,
-                  #state{players = Players} = StateData) ->
+                  #tavla_state{players = Players} = StateData) ->
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             handle_player_action(Player, Action, From, StateName, StateData);
@@ -239,43 +190,43 @@ handle_sync_event(_Event, _From, StateName, StateData) ->
     {reply, Reply, StateName, StateData}.
 
 handle_info({timeout, Magic}, ?STATE_FIRST_MOVE_COMPETITION,
-            #state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
+            #tavla_state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> First move competition timeout. Do an automatic rolls.", [GameId, TableId]),
     do_first_move_competition_timeout_rolls(StateData);
 
 handle_info({timeout, Magic}, ?STATE_PLAYING,
-            #state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
+            #tavla_state{timeout_magic = Magic, game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Move timeout. Do an automatic action(s).", [GameId, TableId]),
     do_timeout_moves(StateData);
 
 handle_info({round_timeout, Round}, ?STATE_PLAYING,
-            #state{cur_round = Round, desk_state = DeskState, game_id = GameId,
+            #tavla_state{cur_round = Round, desk_state = DeskState, game_id = GameId,
                    table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Time to finish round ~p because of the "
           "round timeout.", [GameId, TableId, Round]),
-    finalize_round(StateData#state{desk_state = DeskState#desk_state{finish_reason = timeout}});
+    finalize_round(StateData#tavla_state{desk_state = DeskState#desk_state{finish_reason = timeout}});
 
 handle_info(set_timeout, ?STATE_PLAYING,
-            #state{cur_round = Round, desk_state = DeskState, game_id = GameId,
+            #tavla_state{cur_round = Round, desk_state = DeskState, game_id = GameId,
                    table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Time to finish round ~p and the set because of "
           "the set timeout.", [GameId, TableId, Round]),
-    finalize_round(StateData#state{desk_state = DeskState#desk_state{finish_reason = set_timeout}});
+    finalize_round(StateData#tavla_state{desk_state = DeskState#desk_state{finish_reason = set_timeout}});
 
 handle_info(set_timeout, ?STATE_SET_FINISHED = StateName,
-            #state{game_id = GameId, table_id = TableId} = StateData) ->
+            #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Time to finish the set because of the set timeout. "
           "But the set is finished already. Ignoring", [GameId, TableId]),
     {next_state, StateName, StateData};
 
 handle_info(set_timeout, StateName,
-            #state{game_id = GameId, table_id = TableId, scoring_state = ScoringState,
+            #tavla_state{game_id = GameId, table_id = TableId, scoring_state = ScoringState,
                    parent = Parent, players = Players, timeout_timer = TRef} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Time to finish the set because of the set "
           "timeout at state <~p>. The set is over", [GameId, TableId, StateName]),
     if TRef =/= undefined -> erlang:cancel_timer(TRef);
        true -> do_nothing end,
-    NewStateData = StateData#state{timeout_timer = undefined,
+    NewStateData = StateData#tavla_state{timeout_timer = undefined,
                                    timeout_magic = undefined},
     {_, RoundScore, _, TotalScore} = ?SCORING:last_round_result(ScoringState),
     RoundScorePl = [{get_player_id_by_seat_num(SeatNum, Players), Points} || {SeatNum, Points} <- RoundScore],
@@ -284,18 +235,18 @@ handle_info(set_timeout, StateName,
     {next_state, ?STATE_SET_FINISHED, NewStateData};
 
 handle_info({'DOWN', MonitorRef, _Type, _Object, Info}, _StateName,
-             #state{game_id = GameId, table_id = TableId, parent_mon_ref = MonitorRef
+             #tavla_state{game_id = GameId, table_id = TableId, parent_mon_ref = MonitorRef
                    } = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> The parent is died with reason: ~p. Stopping",
           [GameId, TableId, Info]),
     {stop, parent_died, StateData};
 
-handle_info(Info, StateName, #state{game_id = GameId, table_id = TableId} = StateData) ->
+handle_info(Info, StateName, #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> Unexpected message(info) received at state <~p>: ~p.",
           [GameId, TableId, StateName, Info]),
     {next_state, StateName, StateData}.
 
-terminate(Reason, StateName, #state{game_id = GameId, table_id = TableId, relay = Relay}) ->
+terminate(Reason, StateName, #tavla_state{game_id = GameId, table_id = TableId, relay = Relay}) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> Shutting down at state: <~p>. Reason: ~p",
           [GameId, TableId, StateName, Reason]),
     relay_stop(Relay),
@@ -311,7 +262,7 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
 %% handle_parent_message(Msg, StateName, StateData)
 
 handle_parent_message({register_player, RequestId, UserInfo, PlayerId, SeatNum}, StateName,
-                      #state{table_id = TableId, players = Players,
+                      #tavla_state{table_id = TableId, players = Players,
                              parent = Parent, relay = Relay} = StateData) ->
     #'PlayerInfo'{id = UserId, robot = IsBot} = UserInfo,
     Color = if SeatNum == 1 -> ?WHITE;
@@ -320,10 +271,10 @@ handle_parent_message({register_player, RequestId, UserInfo, PlayerId, SeatNum},
     relay_register_player(Relay, UserId, PlayerId),
     %% TODO: Send notificitations to gamesessions (we have no such notification)
     parent_confirm_registration(Parent, TableId, RequestId),
-    {next_state, StateName, StateData#state{players = NewPlayers}};
+    {next_state, StateName, StateData#tavla_state{players = NewPlayers}};
 
 handle_parent_message({replace_player, RequestId, UserInfo, PlayerId, SeatNum}, StateName,
-                      #state{table_id = TableId, players = Players,
+                      #tavla_state{table_id = TableId, players = Players,
                              parent = Parent, relay = Relay} = StateData) ->
     #'PlayerInfo'{id = UserId, robot = IsBot} = UserInfo,
     #player{id = OldPlayerId} = get_player_by_seat_num(SeatNum, Players),
@@ -336,10 +287,10 @@ handle_parent_message({replace_player, RequestId, UserInfo, PlayerId, SeatNum},
     ReplaceMsg = create_player_left(SeatNum, UserInfo, Players),
     publish_ge(ReplaceMsg, StateData),
     parent_confirm_replacement(Parent, TableId, RequestId),
-    {next_state, StateName, StateData#state{players = NewPlayers2}};
+    {next_state, StateName, StateData#tavla_state{players = NewPlayers2}};
 
 handle_parent_message(start_round, ?STATE_WAITING_FOR_START,
-                      #state{game_id = GameId, table_id = TableId, turn_timeout = TurnTimeout, game_mode = GameMode,
+                      #tavla_state{game_id = GameId, table_id = TableId, turn_timeout = TurnTimeout, game_mode = GameMode,
                              round_timeout = RoundTimeout, set_timeout = SetTimeout} = StateData) ->
     CurRound = 1,
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Recieved the directive to start new round (~p)", [GameId, TableId, CurRound]),
@@ -361,7 +312,7 @@ handle_parent_message(start_round, ?STATE_WAITING_FOR_START,
     SetTRef = if is_integer(SetTimeout) -> erlang:send_after(SetTimeout, self(), set_timeout);
                  true -> undefined
               end,
-    NewStateData = StateData#state{desk_state = DeskState,
+    NewStateData = StateData#tavla_state{desk_state = DeskState,
                                    cur_round = CurRound,
                                    timeout_timer = TRef,
                                    timeout_magic = Magic,
@@ -373,7 +324,7 @@ handle_parent_message(start_round, ?STATE_WAITING_FOR_START,
     {next_state, ?STATE_FIRST_MOVE_COMPETITION, NewStateData};
 
 handle_parent_message(start_round, ?STATE_FINISHED,
-                      #state{game_id = GameId, table_id = TableId, cur_round = CurRound,
+                      #tavla_state{game_id = GameId, table_id = TableId, cur_round = CurRound,
                              start_color = LastStartColor, turn_timeout = TurnTimeout,
                              round_timeout = RoundTimeout, set_timeout = SetTimeout,
                              set_timer = SetTRef, game_mode = GameMode} = StateData) ->
@@ -404,7 +355,7 @@ handle_parent_message(start_round, ?STATE_FINISHED,
                         end;
                     true -> SetTRef
                  end,
-    NewStateData = StateData#state{cur_round = NewCurRound,
+    NewStateData = StateData#tavla_state{cur_round = NewCurRound,
                                    start_color = StartColor,
                                    desk_rule_pid = Desk,
                                    desk_state = DeskState,
@@ -423,7 +374,7 @@ handle_parent_message(start_round, ?STATE_FINISHED,
     {next_state, ?STATE_PLAYING, NewStateData};
 
 handle_parent_message(show_round_result, StateName,
-                      #state{scoring_state = ScoringState,
+                      #tavla_state{scoring_state = ScoringState,
                              game_id = GameId, table_id = TableId} = StateData) ->
     {FinishInfo, RoundScore, AchsPoints, TotalScore} = ?SCORING:last_round_result(ScoringState),
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> RoundScore: ~p Total score: ~p.", [GameId, TableId, RoundScore, TotalScore]),
@@ -442,22 +393,22 @@ handle_parent_message(show_round_result, StateName,
                                                 StateData)
           end,
     publish_ge(Msg, StateData),
-    {next_state, StateName, StateData#state{}};
+    {next_state, StateName, StateData#tavla_state{}};
 
 %% Results = [{PlayerId, Position, Score, Status}] Status = winner | loser | eliminated | none
 handle_parent_message({show_series_result, Results}, StateName,
                       StateData) ->
     Msg = create_tavla_series_ended(Results, StateData),
     publish_ge(Msg, StateData),
-    {next_state, StateName, StateData#state{}};
+    {next_state, StateName, StateData#tavla_state{}};
 
 %% Results = [{UserId, Position, Score, Status}] Status = active | eliminated
 handle_parent_message({tour_result, TourNum, Results}, StateName,
-                      #state{tournament_table = TTable} = StateData) ->
+                      #tavla_state{tournament_table = TTable} = StateData) ->
     NewTTable = [{TourNum, Results} | TTable],
     Msg = create_tavla_tour_result(TourNum, Results, StateData),
     publish_ge(Msg, StateData),
-    {next_state, StateName, StateData#state{tournament_table = NewTTable}};
+    {next_state, StateName, StateData#tavla_state{tournament_table = NewTTable}};
 
 handle_parent_message({playing_tables_num, Num}, StateName,
                       StateData) ->
@@ -467,19 +418,19 @@ handle_parent_message({playing_tables_num, Num}, StateName,
     {next_state, StateName, StateData};
 
 handle_parent_message(rejoin_players, StateName,
-                      #state{game_id = GameId, relay = Relay,
+                      #tavla_state{game_id = GameId, relay = Relay,
                              players = Players} = StateData) ->
     [relay_unregister_player(Relay, P#player.id, {rejoin, GameId}) || P <- players_to_list(Players)],
-    {next_state, StateName, StateData#state{players = players_init()}};
+    {next_state, StateName, StateData#tavla_state{players = players_init()}};
 
 
 handle_parent_message(disconnect_players, StateName,
-                      #state{relay = Relay, players = Players} = StateData) ->
+                      #tavla_state{relay = Relay, players = Players} = StateData) ->
     [relay_unregister_player(Relay, P#player.id, game_over) || P <- players_to_list(Players)],
-    {next_state, StateName, StateData#state{players = players_init()}};
+    {next_state, StateName, StateData#tavla_state{players = players_init()}};
 
 handle_parent_message({send_table_state, DestTableId, PlayerId, Ref}, StateName,
-                      #state{game_id = GameId, table_id = TableId, parent = Parent} = StateData) ->
+                      #tavla_state{game_id = GameId, table_id = TableId, parent = Parent} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  Received request to send the table state events for player "
           "<~p> (~p) at table ~p. Processing.",
           [GameId, TableId, PlayerId, Ref, DestTableId]),
@@ -495,24 +446,24 @@ handle_parent_message(stop, _StateName, StateData) ->
 
 handle_parent_message({action, {competition_rolls, Die1, Die2} = Action},
                       ?STATE_FIRST_MOVE_COMPETITION = StateName,
-                      #state{game_id = GameId, table_id = TableId,
+                      #tavla_state{game_id = GameId, table_id = TableId,
                              desk_state = DeskState} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  Parent action received in state <~p>: ~p. Processing.",
           [GameId, TableId, StateName, Action]),
 %%    publish_ge(create_tavla_rolls_die(?WHITE, Die1, StateData), StateData),
 %%    publish_ge(create_tavla_rolls_die(?BLACK, Die2, StateData), StateData),
     NewDeskState = DeskState#desk_state{dice = {Die1, Die2}},
-    do_start_game(StateData#state{desk_state = NewDeskState});
+    do_start_game(StateData#tavla_state{desk_state = NewDeskState});
 
 handle_parent_message({action, {rolls, Color, Die1, Die2} = Action},
                       ?STATE_PLAYING = StateName,
-                      #state{game_id = GameId, table_id = TableId,
+                      #tavla_state{game_id = GameId, table_id = TableId,
                              relay = Relay, turn_timeout = TurnTimeout} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  Parent action received in state <~p>: ~p. Processing.",
           [GameId, TableId, StateName, Action]),
     relay_publish_ge(Relay, create_tavla_next_turn(Color, StateData)),
     {TRef, Magic} = start_timer(TurnTimeout),
-    NewStateData1 = StateData#state{timeout_timer = TRef, timeout_magic = Magic},
+    NewStateData1 = StateData#tavla_state{timeout_timer = TRef, timeout_magic = Magic},
     case do_parent_game_action(Color, {roll, Die1, Die2}, NewStateData1) of
         {ok, NewStateName, NewStateData2} ->
             {next_state, NewStateName, NewStateData2};
@@ -521,27 +472,27 @@ handle_parent_message({action, {rolls, Color, Die1, Die2} = Action},
     end;
 
 handle_parent_message({action, Action}, StateName,
-                      #state{game_id = GameId, table_id = TableId} = StateData) ->
+                      #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  Parent action received in state <~p>: ~p. Ignoring.",
           [GameId, TableId, StateName, Action]),
     {next_state, StateName, StateData};
 
 handle_parent_message({game_event, GameEvent}, StateName,
-                      #state{game_id = GameId, table_id = TableId, relay = Relay} = StateData) ->
+                      #tavla_state{game_id = GameId, table_id = TableId, relay = Relay} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  A game event received from the parent in state <~p>: ~p. Publish it.",
           [GameId, TableId, StateName, GameEvent]),
     relay_publish_ge(Relay, GameEvent),
     {next_state, StateName, StateData};
 
 handle_parent_message({table_state_event, _PlayerId, SubscrId, StateEvent}, StateName,
-                      #state{game_id = GameId, table_id = TableId, relay = Relay} = StateData) ->
+                      #tavla_state{game_id = GameId, table_id = TableId, relay = Relay} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p>  A table state event received from the parent in state <~p>: ~p.",
           [GameId, TableId, StateName, StateEvent]),
     send_to_subscriber_ge(Relay, SubscrId, StateEvent),
     {next_state, StateName, StateData};
 
 handle_parent_message(Message, StateName,
-                      #state{game_id = GameId, table_id = TableId} = StateData) ->
+                      #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     ?ERROR("TAVLA_NG_TABLE <~p,~p> Unexpected parent message received in state <~p>: ~p. Stopping.",
            [GameId, TableId, StateName, Message]),
     {stop, unexpected_parent_message, StateData}.
@@ -552,32 +503,32 @@ handle_parent_message(Message, StateName,
 %% handle_relay_message(Msg, StateName, StateData)
 
 handle_relay_message({player_connected, PlayerId} = Msg, StateName,
-                     #state{parent = Parent, game_id = GameId,
+                     #tavla_state{parent = Parent, game_id = GameId,
                             table_id = TableId, players = Players} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Received nofitication from the relay: ~p", [GameId, TableId, Msg]),
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             NewPlayers = store_player_rec(Player#player{connected = true}, Players),
             parent_send_player_connected(Parent, TableId, PlayerId),
-            {next_state, StateName, StateData#state{players = NewPlayers}};
+            {next_state, StateName, StateData#tavla_state{players = NewPlayers}};
         error ->
             {next_state, StateName, StateData}
     end;
 
 
 handle_relay_message({player_disconnected, PlayerId}, StateName,
-                     #state{parent = Parent, table_id = TableId, players = Players} = StateData) ->
+                     #tavla_state{parent = Parent, table_id = TableId, players = Players} = StateData) ->
     case get_player(PlayerId, Players) of
         {ok, Player} ->
             NewPlayers = store_player_rec(Player#player{connected = false}, Players),
             parent_send_player_disconnected(Parent, TableId, PlayerId),
-            {next_state, StateName, StateData#state{players = NewPlayers}};
+            {next_state, StateName, StateData#tavla_state{players = NewPlayers}};
         error ->
             {next_state, StateName, StateData}
     end;
 
 handle_relay_message({subscriber_added, PlayerId, SubscrId} = Msg, StateName,
-                     #state{relay = Relay, game_id = GameId, game_mode = GameMode,
+                     #tavla_state{relay = Relay, game_id = GameId, game_mode = GameMode,
                             table_id = TableId, tournament_table = TTable,
                             players = Players, parent = Parent} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Received nofitication from the relay: ~p", [GameId, TableId, Msg]),
@@ -609,7 +560,7 @@ handle_relay_message({subscriber_added, PlayerId, SubscrId} = Msg, StateName,
     end,
     {next_state, StateName, StateData};
 
-handle_relay_message(Message, StateName, #state{game_id = GameId, table_id = TableId} = StateData) ->
+handle_relay_message(Message, StateName, #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     ?ERROR("TAVLA_NG_TABLE <~p,~p> Unknown relay message received in state <~p>: ~p. State: ~p. Stopping.",
            [GameId, TableId, StateName, Message]),
     {next_state, StateName, StateData}.
@@ -621,7 +572,7 @@ handle_relay_message(Message, StateName, #state{game_id = GameId, table_id = Tab
 handle_player_action(#player{id = PlayerId, seat_num = SeatNum, user_id = UserId},
                      {submit, #game_action{action = Action, args = Args} = GA}, From,
                      StateName,
-                     #state{game_id = GameId, table_id = TableId} = StateData) ->
+                     #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     {Keys,Values} = lists:unzip(Args),
     ExtAction = list_to_tuple([Action|Values]),
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Player <~p> (~p) submit the game action: ~p.",
@@ -631,7 +582,7 @@ handle_player_action(#player{id = PlayerId, seat_num = SeatNum, user_id = UserId
 handle_player_action(#player{id = PlayerId, user_id = UserId},
                      {signal, {pause_game, _}=Signal}, _From,
                      StateName,
-                     #state{table_id = TableId, game_id = GameId, timeout_timer = TRef,
+                     #tavla_state{table_id = TableId, game_id = GameId, timeout_timer = TRef,
                             pause_mode = PauseMode, relay = Relay} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Received signal from player <~p> : ~p. PauseMode: ~p",
           [GameId, TableId, PlayerId, Signal, PauseMode]),
@@ -645,7 +596,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
                                  T -> T
                              end,
                    relay_publish(Relay, create_game_paused_pause(UserId, GameId)),
-                   {reply, 0, ?STATE_PAUSE, StateData#state{paused_statename = StateName,
+                   {reply, 0, ?STATE_PAUSE, StateData#tavla_state{paused_statename = StateName,
                                                             paused_timeout_value = Timeout,
                                                             timeout_magic = undefined}};
                true ->
@@ -657,7 +608,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
 handle_player_action(#player{id = PlayerId, user_id = UserId},
                      {signal, {resume_game, _}=Signal}, _From,
                      StateName,
-                     #state{table_id = TableId, game_id = GameId, pause_mode = PauseMode,
+                     #tavla_state{table_id = TableId, game_id = GameId, pause_mode = PauseMode,
                             relay = Relay, paused_statename = ResumedStateName,
                             paused_timeout_value = Timeout} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Received signal from player <~p> : ~p. PauseMode: ~p",
@@ -669,7 +620,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
             if StateName == ?STATE_PAUSE ->
                    relay_publish(Relay, create_game_paused_resume(UserId, GameId)),
                    {TRef, Magic} = start_timer(Timeout),
-                   {reply, 0, ResumedStateName, StateData#state{timeout_timer = TRef,
+                   {reply, 0, ResumedStateName, StateData#tavla_state{timeout_timer = TRef,
                                                                 timeout_magic = Magic}};
                true ->
                    {reply, {error, game_is_not_paused}, StateName, StateData}
@@ -679,7 +630,7 @@ handle_player_action(#player{id = PlayerId, user_id = UserId},
 
 handle_player_action(#player{id = PlayerId},
                      {signal, Signal}, _From, StateName,
-                     #state{table_id = TableId, game_id = GameId} = StateData) ->
+                     #tavla_state{table_id = TableId, game_id = GameId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Received signal from player <~p> : ~p. Ignoring.",
           [GameId, TableId, PlayerId, Signal]),
     {reply, ok, StateName, StateData};
@@ -693,7 +644,7 @@ handle_player_action(_Player, _Message, _From, StateName, StateData) ->
 %%===================================================================
 
 do_action(SeatNum, #tavla_roll{}, From, ?STATE_FIRST_MOVE_COMPETITION = StateName,
-          #state{game_id = GameId, table_id = TableId,
+          #tavla_state{game_id = GameId, table_id = TableId,
                  desk_state = DeskState, players = Players} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> Action tavla_roll{} Deskstate: ~p.",
           [GameId, TableId, DeskState]),
@@ -708,16 +659,16 @@ do_action(SeatNum, #tavla_roll{}, From, ?STATE_FIRST_MOVE_COMPETITION = StateNam
            NewDeskState = DeskState#desk_state{dice = NewDice},
            if WhiteDie =/= undefined andalso BlackDie =/= undefined ->
                   gen_fsm:reply(From, ok),
-                  do_start_game(StateData#state{desk_state = NewDeskState});
+                  do_start_game(StateData#tavla_state{desk_state = NewDeskState});
               true ->
-                  {reply, ok, StateName, StateData#state{desk_state = NewDeskState}}
+                  {reply, ok, StateName, StateData#tavla_state{desk_state = NewDeskState}}
            end;
        true ->
            {reply, {error, already_rolled}, StateName, StateData}
     end;
 
 do_action(SeatNum, #tavla_roll{}, From, ?STATE_PLAYING = StateName,
-          #state{game_mode = standard,
+          #tavla_state{game_mode = standard,
                  desk_state = DeskState, players = Players} = StateData) ->
     #desk_state{state = DeskStateName,
                 cur_color = CurColor} = DeskState,
@@ -738,7 +689,7 @@ do_action(_SeatNum, #tavla_roll{}, _From, StateName, StateData) ->
 
 
 do_action(SeatNum, #tavla_move{moves = ExtMoves}, From, ?STATE_PLAYING = StateName,
-          #state{desk_state = DeskState, players = Players} = StateData) ->
+          #tavla_state{desk_state = DeskState, players = Players} = StateData) ->
     #desk_state{state = DeskStateName,
                 cur_color = CurColor} = DeskState,
     #player{color = Color} = get_player_by_seat_num(SeatNum, Players),
@@ -765,21 +716,21 @@ do_action(_SeatNum, #tavla_ready{}, _From, StateName, StateData) ->
     {reply, ok, StateName, StateData};
 
 do_action(SeatNum, #tavla_surrender{}, From, ?STATE_PLAYING,
-          #state{desk_state = DeskState, players = Players} = StateData) ->
+          #tavla_state{desk_state = DeskState, players = Players} = StateData) ->
     #player{color = Color} = get_player_by_seat_num(SeatNum, Players),
     #desk_state{board = Board} = DeskState,
     gen_fsm:reply(From, ok),
     Condition = surrender_condition(Color, Board),
     NewDeskState = DeskState#desk_state{finish_reason = surrender,
                                         finish_info = {Color, Condition}},
-    finalize_round(StateData#state{desk_state = NewDeskState});
+    finalize_round(StateData#tavla_state{desk_state = NewDeskState});
 
 do_action(_SeatNum, _UnsupportedAction, _From, StateName, StateData) ->
     {reply, {error, invalid_action}, StateName, StateData}.
 
 
 %%===================================================================
-do_first_move_competition_timeout_rolls(#state{desk_state = DeskState} = StateData) ->
+do_first_move_competition_timeout_rolls(#tavla_state{desk_state = DeskState} = StateData) ->
     {WhiteDie, BlackDie} = DeskState#desk_state.dice,
     FinWhiteDie = if WhiteDie == undefined ->
                          NewWhiteDie = random_die(),
@@ -794,9 +745,9 @@ do_first_move_competition_timeout_rolls(#state{desk_state = DeskState} = StateDa
                      true -> BlackDie
                   end,
     NewDeskState = DeskState#desk_state{dice = {FinWhiteDie, FinBlackDie}},
-    do_start_game(StateData#state{desk_state = NewDeskState}).
+    do_start_game(StateData#tavla_state{desk_state = NewDeskState}).
 
-do_start_game(#state{desk_state = DeskState, turn_timeout = TurnTimeout} = StateData) ->
+do_start_game(#tavla_state{desk_state = DeskState, turn_timeout = TurnTimeout} = StateData) ->
     {WhiteDie, BlackDie} = Dice = DeskState#desk_state.dice,
     StartColor = if WhiteDie >= BlackDie -> ?WHITE;
                     true -> ?BLACK
@@ -819,14 +770,14 @@ do_start_game(#state{desk_state = DeskState, turn_timeout = TurnTimeout} = State
     Msg = create_won_first_move(StartColor, Dice, _Reroll = false, StateData),
     publish_ge(Msg, StateData),
     {TRef, Magic} = start_timer(TurnTimeout),
-    {next_state, ?STATE_PLAYING, StateData#state{start_color = StartColor,
+    {next_state, ?STATE_PLAYING, StateData#tavla_state{start_color = StartColor,
                                                  desk_rule_pid = Desk,
                                                  desk_state = NewDeskState2,
                                                  timeout_timer = TRef,
                                                  timeout_magic = Magic
                                                 }}.
 
-do_timeout_moves(#state{desk_rule_pid = Desk, desk_state = DeskState,
+do_timeout_moves(#tavla_state{desk_rule_pid = Desk, desk_state = DeskState,
                         game_id = GameId, table_id = TableId,
                         players = Players, game_mode = GameMode} = StateData) ->
     #desk_state{state = DeskStateName,
@@ -853,7 +804,7 @@ do_timeout_moves(#state{desk_rule_pid = Desk, desk_state = DeskState,
                                   _ -> E
                               end || E <- Events2],
                     NewDeskState = DeskState#desk_state{pips_list = NewPipsList},
-                    process_game_events(Events, StateData#state{desk_state = NewDeskState});
+                    process_game_events(Events, StateData#tavla_state{desk_state = NewDeskState});
                 _ -> %% Only rolls_timeout
                     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> No moves can be done for the dice ~p for player <~p> (~p)",
                           [GameId, TableId, {Die1, Die2}, CurColor, UserId]),
@@ -880,7 +831,7 @@ do_timeout_moves(#state{desk_rule_pid = Desk, desk_state = DeskState,
 %%===================================================================
 
 do_parent_game_action(Color, GameAction,
-               #state{desk_rule_pid = Desk} = StateData) ->
+               #tavla_state{desk_rule_pid = Desk} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE do_parent_game_action Color: ~p  GameAction: ~p", [Color, GameAction]),
     case desk_player_action(Desk, Color, GameAction) of
         {ok, Events} ->
@@ -892,7 +843,7 @@ do_parent_game_action(Color, GameAction,
 
 
 do_game_action(Color, GameAction, From, StateName,
-               #state{game_id = GameId, table_id = TableId, desk_rule_pid = Desk} = StateData) ->
+               #tavla_state{game_id = GameId, table_id = TableId, desk_rule_pid = Desk} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE <~p,~p> Appling the action: Color <~p>  GameAction: ~p",
           [GameId, TableId, Color, GameAction]),
     case desk_player_action(Desk, Color, GameAction) of
@@ -909,7 +860,7 @@ do_game_action(Color, GameAction, From, StateName,
     end.
 
 %% process_game_events(Events, StateData) -> {next_state, StateName, NewStateData}
-process_game_events(Events, #state{desk_state = DeskState, timeout_timer = OldTRef,
+process_game_events(Events, #tavla_state{desk_state = DeskState, timeout_timer = OldTRef,
                                    round_timeout = RoundTimeout, round_timer = RoundTRef,
                                    turn_timeout = TurnTimeout, game_mode = GameMode} = StateData) ->
     NewDeskState = handle_desk_events(Events, DeskState, StateData), %% Track the desk and send game events to clients
@@ -918,20 +869,20 @@ process_game_events(Events, #state{desk_state = DeskState, timeout_timer = OldTR
         state_finished ->
             if is_integer(RoundTimeout) -> erlang:cancel_timer(RoundTRef); true -> do_nothing end,
             erlang:cancel_timer(OldTRef),
-            on_game_finish(StateData#state{desk_state = NewDeskState});
+            on_game_finish(StateData#tavla_state{desk_state = NewDeskState});
         _ ->
             case [E || {next_player, _} = E <- Events] of %% Find a next player event
                 [] ->
-                    {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState}};
+                    {next_state, ?STATE_PLAYING, StateData#tavla_state{desk_state = NewDeskState}};
                 [_|_] when GameMode == paired ->
                     erlang:cancel_timer(OldTRef),
-                    {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState,
+                    {next_state, ?STATE_PLAYING, StateData#tavla_state{desk_state = NewDeskState,
                                                                  timeout_timer = undefined,
                                                                  timeout_magic = undefined}};
                 [_|_] ->
                     erlang:cancel_timer(OldTRef),
                     {TRef, Magic} = start_timer(TurnTimeout),
-                    {next_state, ?STATE_PLAYING, StateData#state{desk_state = NewDeskState,
+                    {next_state, ?STATE_PLAYING, StateData#tavla_state{desk_state = NewDeskState,
                                                                  timeout_timer = TRef,
                                                                  timeout_magic = Magic}}
             end
@@ -943,7 +894,7 @@ on_game_finish(StateData) ->
 
 %%===================================================================
 
-finalize_round(#state{desk_state = #desk_state{finish_reason = FinishReason,
+finalize_round(#tavla_state{desk_state = #desk_state{finish_reason = FinishReason,
                                                finish_info = FinishInfo},
                       scoring_state = ScoringState, timeout_timer = TimeoutTRef,
                       round_timer = RoundTRef, parent = Parent, players = Players,
@@ -969,7 +920,7 @@ finalize_round(#state{desk_state = #desk_state{finish_reason = FinishReason,
                  {surrender, SeatNum, Condition}
          end,
     {NewScoringState, GameOver} = ?SCORING:round_finished(ScoringState, FR),
-    NewStateData = StateData#state{scoring_state = NewScoringState,
+    NewStateData = StateData#tavla_state{scoring_state = NewScoringState,
                                    timeout_timer = undefined,
                                    timeout_magic = undefined,
                                    round_timer = undefined},
@@ -996,7 +947,7 @@ handle_desk_events([], DeskState, _StateData) ->
     DeskState;
 
 handle_desk_events([Event | Events], DeskState,
-                    #state{game_id = GameId, table_id = TableId} = StateData) ->
+                    #tavla_state{game_id = GameId, table_id = TableId} = StateData) ->
     gas:info(?MODULE,"TAVLA_NG_TABLE_DBG <~p,~p> handle_desk_events/3 Event: ~p", [GameId, TableId, Event]),
     #desk_state{board = Board,
                 pips_list = OldPipsList} = DeskState,
@@ -1207,7 +1158,7 @@ parent_publish_ge({ParentMod, ParentPid}, TableId, GameEvent) ->
 desk_player_action(Desk, Color, Action) ->
     ?DESK:player_action(Desk, Color, Action).
 
-publish_ge(GameEvent, #state{relay = Relay, parent = Parent, table_id = TableId,
+publish_ge(GameEvent, #tavla_state{relay = Relay, parent = Parent, table_id = TableId,
                              game_mode = GameMode}) ->
     case GameMode of
         paired ->
@@ -1222,7 +1173,7 @@ publish_ge(GameEvent, #state{relay = Relay, parent = Parent, table_id = TableId,
 
 %%===================================================================
 
-create_tavla_game_info(#state{table_name = TName, mult_factor = MulFactor,
+create_tavla_game_info(#tavla_state{table_name = TName, mult_factor = MulFactor,
                               slang_flag = SlangFlag, observer_flag = ObserverFlag,
                               speed = Speed, turn_timeout = TurnTimeout,
                               ready_timeout = ReadyTimeout, game_mode = GameMode,
@@ -1262,7 +1213,7 @@ create_tavla_game_info(#state{table_name = TName, mult_factor = MulFactor,
                     }.
 
 create_tavla_game_player_state(_PlayerId, ?STATE_WAITING_FOR_START,
-                               #state{table_id = TableId, cur_round = CurRound, players = Players,
+                               #tavla_state{table_id = TableId, cur_round = CurRound, players = Players,
                                       set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     Colors = players_ext_color_info(Players),
     SetTimeout = if SetTimeout1 == infinity -> null;
@@ -1281,7 +1232,7 @@ create_tavla_game_player_state(_PlayerId, ?STATE_WAITING_FOR_START,
                              set_timeout = SetTimeout};
 
 create_tavla_game_player_state(_PlayerId, ?STATE_FIRST_MOVE_COMPETITION,
-                               #state{timeout_timer = TRef, cur_round = CurRound,
+                               #tavla_state{timeout_timer = TRef, cur_round = CurRound,
                                       players = Players, desk_state = DeskState,
                                       round_timer = RoundTRef,
                                       round_timeout = RoundTimeout1, set_timer = SetTRef,
@@ -1315,7 +1266,7 @@ create_tavla_game_player_state(_PlayerId, ?STATE_FIRST_MOVE_COMPETITION,
 
 
 create_tavla_game_player_state(_PlayerId, ?STATE_PLAYING,
-                               #state{timeout_timer = TRef, cur_round = CurRound,
+                               #tavla_state{timeout_timer = TRef, cur_round = CurRound,
                                       players = Players, desk_state = DeskState,
                                       round_timer = RoundTRef,
                                       round_timeout = RoundTimeout1, set_timer = SetTRef,
@@ -1347,7 +1298,7 @@ create_tavla_game_player_state(_PlayerId, ?STATE_PLAYING,
                              set_timeout = SetTimeout};
 
 create_tavla_game_player_state(_PlayerId, ?STATE_SET_FINISHED,
-                               #state{table_id = TableId, cur_round = CurRound,
+                               #tavla_state{table_id = TableId, cur_round = CurRound,
                                       players = Players}) ->
     Colors = players_ext_color_info(Players),
     #tavla_game_player_state{table_id = TableId,
@@ -1363,7 +1314,7 @@ create_tavla_game_player_state(_PlayerId, ?STATE_SET_FINISHED,
                              set_timeout = null};
 
 create_tavla_game_player_state(_PlayerId, ?STATE_FINISHED,
-                               #state{table_id = TableId, cur_round = CurRound, players = Players,
+                               #tavla_state{table_id = TableId, cur_round = CurRound, players = Players,
                                       set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     Colors = players_ext_color_info(Players),
     SetTimeout = if SetTimeout1 == infinity -> null;
@@ -1382,7 +1333,7 @@ create_tavla_game_player_state(_PlayerId, ?STATE_FINISHED,
                              set_timeout = SetTimeout};
 
 create_tavla_game_player_state(PlayerId, ?STATE_PAUSE,
-                               #state{paused_statename = PausedStateName,
+                               #tavla_state{paused_statename = PausedStateName,
                                       paused_timeout_value = Timeout
                                      } = StateData) ->
     Msg = create_tavla_game_player_state(PlayerId, PausedStateName, StateData),
@@ -1391,7 +1342,7 @@ create_tavla_game_player_state(PlayerId, ?STATE_PAUSE,
 
 
 create_tavla_game_started(DeskState, DoFirstMoveCompetitionRoll,
-                         #state{table_id = TableId, cur_round = CurRound,
+                         #tavla_state{table_id = TableId, cur_round = CurRound,
                                 round_timeout = RoundTimeout1, players = Players,
                                 set_timeout = SetTimeout1, set_timer = SetTRef}) ->
     #desk_state{board = Board} = DeskState,
@@ -1410,7 +1361,7 @@ create_tavla_game_started(DeskState, DoFirstMoveCompetitionRoll,
                         set_timeout = SetTimeout,
                         do_first_move_competition_roll = DoFirstMoveCompetitionRoll}.
 
-create_won_first_move(Color, Dice,Reroll, #state{table_id = TableId, players = Players}) ->
+create_won_first_move(Color, Dice,Reroll, #tavla_state{table_id = TableId, players = Players}) ->
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),
     #tavla_won_first_move{table_id = TableId,
                           color = color_to_ext(Color),
@@ -1418,11 +1369,11 @@ create_won_first_move(Color, Dice,Reroll, #state{table_id = TableId, players = P
                           dice = dice_to_ext(Dice),
                           reroll = Reroll}.
 
-create_tavla_next_turn(Color, #state{table_id = TableId, players = Players}) ->
+create_tavla_next_turn(Color, #tavla_state{table_id = TableId, players = Players}) ->
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),
     #tavla_next_turn{table_id = TableId, color = color_to_ext(Color), player = UserId}.
 
-create_tavla_rolls_die(Color, Die, #state{table_id = TableId, players = Players}) ->
+create_tavla_rolls_die(Color, Die, #tavla_state{table_id = TableId, players = Players}) ->
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),
     #tavla_rolls{table_id = TableId,
                  player = UserId,
@@ -1430,7 +1381,7 @@ create_tavla_rolls_die(Color, Die, #state{table_id = TableId, players = Players}
                  dices = [die_to_ext(Die)]
                 }.
 
-create_tavla_rolls_dice(Color, Dice, #state{table_id = TableId, players = Players}) ->
+create_tavla_rolls_dice(Color, Dice, #tavla_state{table_id = TableId, players = Players}) ->
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),
     #tavla_rolls{table_id = TableId,
                  player = UserId,
@@ -1438,7 +1389,7 @@ create_tavla_rolls_dice(Color, Dice, #state{table_id = TableId, players = Player
                  dices = dice_to_ext(Dice)
                 }.
 
-create_tavla_moves(Color, From, To, Type, Pips, #state{table_id = TableId, players = Players}) ->
+create_tavla_moves(Color, From, To, Type, Pips, #tavla_state{table_id = TableId, players = Players}) ->
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),
     #tavla_moves{table_id = TableId,
                  color = color_to_ext(Color),
@@ -1458,7 +1409,7 @@ create_player_left(SeatNum, UserInfo, Players) ->
 
 
 create_tavla_round_ended_win(Winner, RoundScore, TotalScore, _PlayersAchsPoints,
-                             #state{table_id = TableId, players = Players}) ->
+                             #tavla_state{table_id = TableId, players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      WinnerStatus = if SeatNum == Winner -> <<"true">>;
@@ -1481,7 +1432,7 @@ create_tavla_round_ended_win(Winner, RoundScore, TotalScore, _PlayersAchsPoints,
                       results =  Results}.
 
 create_tavla_round_ended_surrender(Surrender, RoundScore, TotalScore, _PlayersAchsPoints,
-                                   #state{table_id = TableId, players = Players}) ->
+                                   #tavla_state{table_id = TableId, players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      WinnerStatus = if SeatNum =/= Surrender -> <<"true">>;
@@ -1504,7 +1455,7 @@ create_tavla_round_ended_surrender(Surrender, RoundScore, TotalScore, _PlayersAc
                       results =  Results}.
 
 create_tavla_round_ended_draw(Reason, RoundScore, TotalScore, _PlayersAchsPoints,
-                             #state{table_id = TableId, players = Players}) ->
+                             #tavla_state{table_id = TableId, players = Players}) ->
     PlResults = [begin
                      #player{user_id = UserId} = get_player_by_seat_num(SeatNum, Players),
                      {_, Score} = lists:keyfind(SeatNum, 1, TotalScore),
@@ -1526,7 +1477,7 @@ create_tavla_round_ended_draw(Reason, RoundScore, TotalScore, _PlayersAchsPoints
                       winner = null,
                       results =  Results}.
 
-create_tavla_series_ended(Results, #state{table_id = TableId, players = Players}) ->
+create_tavla_series_ended(Results, #tavla_state{table_id = TableId, players = Players}) ->
     Standings = [begin
                      #player{user_id = UserId} = fetch_player(PlayerId, Players),
                      Winner = case Status of    %% TODO: Implement in the client support of all statuses
@@ -1539,7 +1490,7 @@ create_tavla_series_ended(Results, #state{table_id = TableId, players = Players}
     #tavla_series_ended{table_id = TableId,
                         standings = Standings}.
 
-create_tavla_tour_result(TourNum, Results, #state{table_id = TableId}) ->
+create_tavla_tour_result(TourNum, Results, #tavla_state{table_id = TableId}) ->
     Records = [#tavla_tour_record{player_id = UserId, place = Position,
                                   score = Score, status = Status}
                || {UserId, Position, Score, Status} <- Results],
@@ -1547,7 +1498,7 @@ create_tavla_tour_result(TourNum, Results, #state{table_id = TableId}) ->
                        tour_num = TourNum,
                        records = Records}.
 
-create_tavla_turn_timeout(Color, Dice, Moves, #state{table_id = TableId, players = Players}) ->
+create_tavla_turn_timeout(Color, Dice, Moves, #tavla_state{table_id = TableId, players = Players}) ->
     DiceExt = if Dice == undefined -> null;
                  true -> dice_to_ext(Dice) end,
     [#player{user_id = UserId}] = find_players_by_color(Color, Players),

+ 1 - 1
apps/server/src/tavla/game_tavla_ng_trn_paired.erl

@@ -119,7 +119,7 @@
 -define(STATE_SHOW_TOUR_RESULT, state_show_tour_result).
 -define(STATE_FINISHED, state_finished).
 
--define(TAB_MOD, game_okey_ng_table_trn).
+-define(TAB_MOD, game_okey_table).
 
 -define(TABLE_STATE_INITIALIZING, initializing).
 -define(TABLE_STATE_READY, ready).