game_stats.erl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. -module(game_stats).
  2. -behaviour(gen_server).
  3. -export([start_link/0, add_game/1, get_skill/1, get_game_points/2, get_player_stats/1,
  4. init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3,
  5. assign_points/2, is_feel_lucky/1, charge_quota/1]).
  6. -include_lib("server/include/basic_types.hrl").
  7. -include_lib("server/include/game_okey.hrl").
  8. -include_lib("server/include/game_tavla.hrl").
  9. -include_lib("server/include/log.hrl").
  10. -include_lib("server/include/games.hrl").
  11. -include_lib("db/include/transaction.hrl").
  12. -include_lib("db/include/scoring.hrl").
  13. -record(raw_result,
  14. {player_id :: binary(),
  15. winner :: boolean(),
  16. score :: integer(),
  17. pos :: integer()
  18. }).
  19. -record(result,
  20. {player_id :: string(),
  21. robot :: boolean(),
  22. winner :: boolean(),
  23. score :: integer(),
  24. pos :: integer(),
  25. kakush_points :: integer(),
  26. game_points :: integer()
  27. }).
  28. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  29. add_game(Game) -> gen_server:cast(?MODULE, {add_game, Game}).
  30. get_skill(UserId) when is_binary(UserId) -> get_skill(binary_to_list(UserId));
  31. get_skill(UserId) -> {ok, crypto:rand_uniform(1,1000)}.
  32. get_game_points(GameType, UserId) when is_binary(UserId) -> get_game_points(GameType, binary_to_list(UserId));
  33. get_game_points(GameType, UserId) -> {ok, [{game_points,crypto:rand_uniform(1,1000)},
  34. {finished_with_okey,crypto:rand_uniform(1,1000)},
  35. {finished_with_8_tashes,crypto:rand_uniform(1,1000)}]}.
  36. get_player_stats(UserId) when is_binary(UserId) -> get_player_stats(binary_to_list(UserId));
  37. get_player_stats(UserId) -> {ok, [{total_games,crypto:rand_uniform(1,10)},
  38. {total_wins,crypto:rand_uniform(1,10)},
  39. {total_loses,crypto:rand_uniform(1,10)},
  40. {overal_success_ratio,crypto:rand_uniform(1,100)},
  41. {average_play_time,crypto:rand_uniform(1000,5000)}]}.
  42. init([]) ->
  43. wf:reg(stats),
  44. {ok, no_state}.
  45. handle_call(Request, From, State) ->
  46. error_logger:error_msg("unknown call ~p ~p ~n", [Request, From]),
  47. {noreply, State}.
  48. handle_cast({add_game, Game}, State) -> {noreply, State};
  49. handle_cast(Msg, State) -> error_logger:error_msg("unknown cast ~p ~n", [Msg]), {noreply, State}.
  50. handle_info({stats,Route,Message}, State) ->
  51. wf:info("Stats: Route: ~p Message: ~p~n",[Route,Message]),
  52. handle_stats(Route,Message),
  53. {noreply, State};
  54. handle_info(Info, State) -> error_logger:error_msg("unknown info ~p~n", [Info]), {noreply, State}.
  55. handle_stats([tournament,T,cancel],Message) -> ok;
  56. handle_stats([tournament,T,activate],Message) -> ok;
  57. handle_stats([personal_score,user,U,add],Message) -> ok;
  58. handle_stats([system,game_end_note,U,add],Message) -> ok;
  59. handle_stats([system,tournament_tour_note,T],Message) -> ok;
  60. handle_stats([system,tournament_ends_note,T],Message) -> ok;
  61. handle_stats([system,game_ends_note,T],Message) -> ok;
  62. handle_stats(Route,Message) -> ok.
  63. terminate(_Reason, _State) -> ok.
  64. code_change(_OldVsn, State, _Extra) -> {ok, State}.
  65. is_feel_lucky(GameInfo) ->
  66. proplists:get_value(lucky, GameInfo,false).
  67. %game_info_to_ti(GameInfo) ->
  68. % #ti_game_event{game_name = okey,
  69. % game_mode = proplists:get_value(mode, GameInfo),
  70. % id = proplists:get_value(id, GameInfo),
  71. % double_points = proplists:get_value(double_points, GameInfo)
  72. % }.
  73. charge_quota(GameInfo) ->
  74. PR0 = proplists:get_value(pointing_rules, GameInfo),
  75. PRLucky = proplists:get_value(pointing_rules_lucky, GameInfo),
  76. Players = proplists:get_value(initial_players, GameInfo),
  77. Double = proplists:get_value(double_points, GameInfo),
  78. % TI = game_info_to_ti(GameInfo),
  79. PR = pointing_rules:double_points(PR0, Double),
  80. [begin
  81. UId = user_id_to_string(U#'PlayerInfo'.id),
  82. Amount = case is_feel_lucky(GameInfo) of
  83. true -> PRLucky#pointing_rule.quota;
  84. _ -> PR#pointing_rule.quota
  85. end,
  86. kvs:add(#transaction{id=kvs:next_id(transaction,1),feed_id={quota,UId},comment=game_start})
  87. end || U <- Players].
  88. assign_points(#'TavlaGameResults'{players = Results}, GameInfo) ->
  89. ConvertedResults = [ #raw_result{winner = Winner == <<"true">>, player_id = PlayerId, score = Score,
  90. pos = if Winner == <<"true">> -> 1; true -> 2 end}
  91. || #'TavlaPlayerScore'{winner = Winner, player_id = PlayerId, score = Score} <- Results],
  92. assign_points(ConvertedResults, GameInfo);
  93. assign_points(#'OkeyGameResults'{series_results = Results}, GameInfo) ->
  94. ConvertedResults = [ #raw_result{winner = Winner == <<"true">>, player_id = PlayerId, score = Score, pos = Pos}
  95. || #'OkeySeriesResult'{winner = Winner, player_id = PlayerId, score = Score, place = Pos} <- Results],
  96. assign_points(ConvertedResults, GameInfo);
  97. assign_points(RawResults, GameInfo) ->
  98. GameId = proplists:get_value(id, GameInfo),
  99. PR0 = proplists:get_value(pointing_rules, GameInfo),
  100. PRLucky = proplists:get_value(pointing_rules_lucky, GameInfo),
  101. Players = proplists:get_value(initial_players, GameInfo),
  102. Double = proplists:get_value(double_points, GameInfo),
  103. % TI = game_info_to_ti(GameInfo),
  104. PR1 = pointing_rules:double_points(PR0, Double),
  105. PlayersIds = [if Robot -> user_id_to_string(UserId);
  106. true -> "(robot)"
  107. end || #'PlayerInfo'{id = UserId, robot = Robot} <- Players],
  108. #pointing_rule{
  109. kakush_winner = KakushWinner,
  110. kakush_other = KakushOther,
  111. game_points = WinGamePoints
  112. } = case is_feel_lucky(GameInfo) of true -> PRLucky; false -> PR1 end,
  113. Bots = [UserId || #raw_result{player_id = UserId} <- RawResults, is_bot(UserId, Players)],
  114. Paids = [UserId || #raw_result{player_id = UserId} <- RawResults, is_paid(UserId)],
  115. Winners = [UserId || #raw_result{player_id = UserId, winner = true} <- RawResults],
  116. TotalNum = length(RawResults),
  117. PaidsNum = length(Paids),
  118. WinnersNum = length(Winners),
  119. KakushPerWinner = round((KakushWinner * PaidsNum div TotalNum) / WinnersNum),
  120. KakushPerLoser = KakushOther * PaidsNum div TotalNum,
  121. gas:info(?MODULE,"GAME_STATS <~p> KakushWinner: ~p KakushOther: ~p", [GameId, KakushWinner, KakushOther]),
  122. gas:info(?MODULE,"GAME_STATS <~p> KakushPerWinner: ~p KakushPerLoser: ~p", [GameId, KakushPerWinner, KakushPerLoser]),
  123. Results = [begin
  124. Robot = lists:member(UserId, Bots),
  125. Paid = lists:member(UserId, Paids),
  126. {KakushPoints, GamePoints} = calc_points(KakushPerWinner, KakushPerLoser,
  127. WinGamePoints, Paid, Robot, Winner),
  128. #result{player_id = user_id_to_string(UserId), robot = Robot, winner = Winner,
  129. pos = Pos, kakush_points = KakushPoints, game_points = GamePoints}
  130. end || #raw_result{player_id = UserId, winner = Winner, pos = Pos} <- RawResults],
  131. gas:info(?MODULE,"GAME_STATS <~p> Results: ~p", [GameId, Results]),
  132. [begin
  133. if not Robot ->
  134. SR = #scoring_record{
  135. game_id = proplists:get_value(id, GameInfo),
  136. who = UserId,
  137. all_players = PlayersIds,
  138. game_type = PR0#pointing_rule.game_type,
  139. game_kind = PR0#pointing_rule.game,
  140. % condition, % where'd I get that?
  141. score_points = GamePoints,
  142. score_kakaush = KakushPoints,
  143. % custom, % no idea what to put here
  144. timestamp = erlang:now()
  145. },
  146. Route = [feed, user, UserId, scores, 0, add], % maybe it would require separate worker for this
  147. wf:send(Route, [SR]),
  148. {Wins, Loses} = if Winner-> {1, 0};
  149. true -> {0, 1}
  150. end,
  151. % haven't found a way to properly get average time
  152. wf:send([personal_score, user, UserId, add],
  153. {_Games = 1, Wins, Loses, _Disconnects = 0, GamePoints, 0});
  154. true -> do_nothing % no statistics for robots
  155. end,
  156. if not Robot ->
  157. if KakushPoints /= 0 ->
  158. kvs:add(#transaction{id=kvs:next_id(transaction,1),feed_id={kakush,UserId},comment=game_end});
  159. % ok = nsm_accounts:transaction(UserId, ?CURRENCY_KAKUSH, KakushPoints, TI#ti_game_event{type = game_end});
  160. true -> ok
  161. end,
  162. if GamePoints /= 0 ->
  163. kvs:add(#transaction{id=kvs:next_id(transaction,1),feed_id={game_points,UserId},comment=game_end});
  164. % ok = nsm_accounts:transaction(UserId, ?CURRENCY_GAME_POINTS, GamePoints, TI#ti_game_event{type = game_end});
  165. true -> ok
  166. end;
  167. true -> do_nothing %% no points for robots
  168. end
  169. end || #result{player_id = UserId, robot = Robot, winner = Winner,
  170. kakush_points = KakushPoints, game_points = GamePoints} <- Results],
  171. GameName = proplists:get_value(name, GameInfo, ""),
  172. GameType = proplists:get_value(game_type, GameInfo),
  173. GameEndRes = [{if Robot -> "robot"; true -> UserId end, Robot, Pos, KPoints, GPoints}
  174. || #result{player_id = UserId, robot = Robot, pos = Pos,
  175. kakush_points = KPoints, game_points = GPoints} <- Results],
  176. gas:info(?MODULE,"GAME_STATS <~p> Notificaton: ~p", [GameId, {{GameName, GameType}, GameEndRes}]),
  177. wf:send(["system", "game_ends_note"], {{GameName, GameType}, GameEndRes}).
  178. is_bot(UserId, Players) ->
  179. case lists:keyfind(UserId, #'PlayerInfo'.id, Players) of
  180. #'PlayerInfo'{robot = Robot} -> Robot;
  181. _ -> true % If UserId is not found then the player is a replaced bot.
  182. end.
  183. is_paid(UserId) -> true. %nsm_accounts:user_paid(UserId).
  184. user_id_to_string(UserId) -> binary_to_list(UserId).
  185. calc_points(KakushPerWinner, KakushPerLoser, WinGamePoints, Paid, Robot, Winner) ->
  186. if Robot -> {0, 0};
  187. not Paid andalso Winner -> {0, WinGamePoints};
  188. not Paid -> {0, 0};
  189. Paid andalso Winner -> {KakushPerWinner, WinGamePoints};
  190. Paid -> {KakushPerLoser, 0}
  191. end.