game_stats.erl 10 KB

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