-module(game). -author('Maxim Sokhatsky '). -compile(export_all). -include_lib("server/include/requests.hrl"). -include_lib("db/include/journal.hrl"). -include_lib("db/include/table.hrl"). -include_lib("db/include/tournaments.hrl"). -include_lib("db/include/scoring.hrl"). -include_lib("stdlib/include/qlc.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). online() -> [X||X<-qlc:e(gproc:table()),element(1,X)=={p,l,broadcast}]. get_all_games_ids() -> [GameId || {_, _, #game_table{id = GameId, game_type = game_okey}} <- qlc:e(gproc:table())]. destroy_game(Pid,Sup) -> game_sup:stop_game(Sup,Pid). gen_game_id() -> PoolNum = wf:config(nsx_idgen,game_pool,5000000) div 1000000, PoolNumStr = integer_to_list(PoolNum), PoolNum*1000000 + 200 + kvs:next_id(game_table, 1). %% 200 is reserved for lucky games and for already created games create_game(GameId, GameFSM, Params) -> {ok, Pid} = create_game_monitor(GameId, GameFSM, Params), {ok, GameId, Pid}. %% rank_table(GameId) -> {ok, {LastTourNum, TourResult}} | {error, Reason} %% TourResult = {UserId, Pos, Points, Status} rank_table(GameId) -> case get_relay_mod_pid(GameId) of {Module, Pid} -> Module:system_request(Pid, last_tour_result); undefined -> {error, not_found} end. get_lucky_pid(Sup) -> [X]=game:get_lucky_table(Sup), X#game_table.game_process. get_relay_pid(GameId) -> case get_tables(GameId) of [] -> undefined; [#game_table{game_process = P} | _] -> gas:info(?MODULE,"GameRelay: ~p",[P]), P end. get_relay_mod_pid(GameId) -> case get_tables(GameId) of [] -> undefined; [#game_table{game_process = P, game_module = M} | _] -> gas:info(?MODULE,"GameRelay: ~p",[{M,P}]), {M,P} end. get_relay(GameId) -> gen_server:call(?MODULE, {get_relay, GameId}). game_requirements(GameAtom) -> GameAtom:get_requirements(). game_requirements(game_tavla,paired) -> paired_tavla:get_requirements(); game_requirements(GameAtom,_) -> GameAtom:get_requirements(). counter(Game) -> PL = supervisor:count_children(case Game of game_okey -> okey_sup; game_tavla -> tavla_sup; _ -> game_sup end), Res = proplists:get_value(active, PL, 0), case Game of game_okey -> Res; game_tavla -> Res; _ -> 0 end. game_sup_domain(Module, Params) -> case Module of tavla_paired -> tavla_sup; standalone -> case proplists:get_value(game, Params) of game_okey -> okey_sup; game_tavla -> tavla_sup; _ -> game_sup end; elimination -> case proplists:get_value(game_type, Params) of game_okey -> okey_sup; game_tavla -> tavla_sup; _ -> game_sup end; _ -> game_sup end. create_game_monitor(Topic, GameFSM, Params) -> Sup = game_sup_domain(GameFSM, Params), gas:info(?MODULE,"Create Root Game Process (Game Monitor2): ~p Params: ~p Sup: ~p",[GameFSM, Params,Sup]), RelayInit = Sup:start_game(GameFSM,[Topic,Params],Topic), gas:info(?MODULE,"RelayInit ~p",[RelayInit]), RelayInit. get_lucky_table(Game) -> Lucky = true, Check = fun(undefined, _Value) -> true; (Param, Value) -> Param == Value end, Cursor = fun() -> qlc:cursor(qlc:q([V || {{_,_,_K},_,V=#game_table{game_type=G, feel_lucky = L}} <- gproc:table(props), Check(Game, G), Check(Lucky, L)])) end, Tables = qlc:next_answers(Cursor(), 1), Tables. get_tournament(TrnId) -> Check = fun(undefined, _Value) -> true; (Param, Value) -> Param == Value end, Cursor = fun() -> qlc:cursor(qlc:q([V || {{_,_,_K},_, V = #game_table{trn_id=TId}} <- gproc:table(props), Check(TrnId, TId)])) end, Table = case qlc:next_answers(Cursor(), 1) of [T] -> X = T#game_table.id, X; _ -> [] end, % gas:info(?MODULE,"~w:get_tournament Table = ~p", [?MODULE, Table]), Table. %% stress_test(NumberOfRooms) -> %% OkeyPlayers = [begin %% {ok,GameId,A} = game:create_table(game_okey,[{table_name,"okey maxim and alice + 2 robots"}, %% {speed,normal}, %% {rounds,80}, %% {sets,1}, %% {game_mode,standard}, %% {owner,"kate"}],[<<"maxim">>,<<"alice">>,robot,robot]), %% %% Clients = [ proc_lib:spawn_link(fun() -> %% test_okey:init_with_join_game(self(), '127.0.0.1', ?LISTEN_PORT, GameId, Id, 1, normal) %% end) || Id <- [<<"maxim">>,<<"alice">>] ], %% %% {ok,GameId,A} %% %% %% end ||X<-lists:seq(1,NumberOfRooms)], %% [{ok,OP1,_}|_] = OkeyPlayers, %% [{ok,OP2,_}|_] = lists:reverse(OkeyPlayers), %% gas:info(?MODULE,"Okey bot rooms runned (STRESS): ~p~n",[{OP1,OP2}]). create_standalone_game(Game, Params, Users) -> gas:info(?MODULE,"create_standalone_game/3 Params:~p", [Params]), case Game of game_okey -> #pointing_rule{quota = Quota, kakush_winner = KakushForWinners, kakush_other = KakushForLoser, game_points = WinGamePoints } = proplists:get_value(pointing_rules, Params), GameId = proplists:get_value(game_id, Params), TableName = proplists:get_value(table_name, Params), MulFactor = proplists:get_value(double_points, Params, 1), SlangAllowed = proplists:get_value(slang, Params, false), ObserversAllowed = proplists:get_value(observers, Params, false), Speed = proplists:get_value(speed, Params, normal), GameMode = proplists:get_value(game_mode, Params), Rounds = case GameMode of countdown -> undefined; _ -> proplists:get_value(rounds, Params, undefined) end, GostergeFinishAllowed = proplists:get_value(gosterge_finish, Params, false), BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of true -> enabled; false -> disabled end, TableParams = [ {table_name, TableName}, {mult_factor, MulFactor}, {slang_allowed, SlangAllowed}, {observers_allowed, ObserversAllowed}, {tournament_type, standalone}, {round_timeout, infinity}, %% {round_timeout, 30 * 1000}, {set_timeout, infinity}, %% {set_timeout, 10 * 60 *1000}, {speed, Speed}, {game_type, GameMode}, {rounds, Rounds}, {reveal_confirmation, true}, {next_series_confirmation, no_exit}, {pause_mode, normal}, {social_actions_enabled, true}, {gosterge_finish_allowed, GostergeFinishAllowed} ], create_game(GameId, standalone, [{game, Game}, {game_mode, GameMode}, {game_name, TableName}, {seats, 4}, {registrants, Users}, {initial_points, 0}, {quota_per_round, Quota}, {kakush_for_winners, KakushForWinners}, {kakush_for_loser, KakushForLoser}, {win_game_points, WinGamePoints}, {mul_factor, MulFactor}, {table_module, okey_table}, {bot_module, okey_bot}, {bots_replacement_mode, BotsReplacementMode}, {table_params, TableParams}, {common_params, Params} ]); game_tavla -> #pointing_rule{quota = Quota, kakush_winner = KakushForWinners, kakush_other = KakushForLoser, game_points = WinGamePoints } = proplists:get_value(pointing_rules, Params), GameId = proplists:get_value(game_id, Params), TableName = proplists:get_value(table_name, Params), MulFactor = proplists:get_value(double_points, Params, 1), SlangAllowed = proplists:get_value(slang, Params, false), ObserversAllowed = proplists:get_value(observers, Params, false), Speed = proplists:get_value(speed, Params, normal), GameMode = proplists:get_value(game_mode, Params), Rounds = case GameMode of _ -> proplists:get_value(rounds, Params, undefined) end, BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of true -> enabled; false -> disabled end, TableParams = [ {table_name, TableName}, {mult_factor, MulFactor}, {slang_allowed, SlangAllowed}, {observers_allowed, ObserversAllowed}, {tournament_type, standalone}, {round_timeout, infinity}, %% {round_timeout, 30 * 1000}, {set_timeout, infinity}, %% {set_timeout, 10 * 60 *1000}, {speed, Speed}, {game_mode, GameMode}, {rounds, Rounds}, {next_series_confirmation, no_exit}, {pause_mode, normal}, {social_actions_enabled, true}, {tables_num, 1} ], create_game(GameId, standalone, [{game, Game}, {game_mode, GameMode}, {game_name, TableName}, {seats, 2}, {registrants, Users}, {initial_points, 0}, {quota_per_round, Quota}, {kakush_for_winners, KakushForWinners}, {kakush_for_loser, KakushForLoser}, {win_game_points, WinGamePoints}, {mul_factor, MulFactor}, {table_module, tavla_table}, {bot_module, tavla_bot}, {bots_replacement_mode, BotsReplacementMode}, {table_params, TableParams}, {common_params, Params} ]) end. create_paired_game(Game, Params, Users) -> gas:info(?MODULE,"create_paired_game/3 Params:~p", [Params]), case Game of game_tavla -> #pointing_rule{quota = Quota, kakush_winner = KakushForWinners, kakush_other = KakushForLoser, game_points = WinGamePoints } = proplists:get_value(pointing_rules, Params), GameId = proplists:get_value(game_id, Params), TableName = proplists:get_value(table_name, Params), MulFactor = proplists:get_value(double_points, Params, 1), SlangAllowed = proplists:get_value(slang, Params, false), ObserversAllowed = proplists:get_value(observers, Params, false), Speed = proplists:get_value(speed, Params, normal), GameMode = proplists:get_value(game_mode, Params), Rounds = case GameMode of _ -> proplists:get_value(rounds, Params, undefined) end, BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of true -> enabled; false -> disabled end, TablesNum = length(Users) div 2 + length(Users) rem 2, TableParams = [ {table_name, TableName}, {mult_factor, MulFactor}, {slang_allowed, SlangAllowed}, {observers_allowed, ObserversAllowed}, {tournament_type, paired}, {round_timeout, infinity}, {set_timeout, infinity}, {speed, Speed}, {game_mode, GameMode}, {rounds, Rounds}, {next_series_confirmation, no_exit}, {pause_mode, disabled}, {social_actions_enabled, true}, {tables_num, TablesNum} ], create_game(GameId, tavla_paired, [{game, Game}, {game_mode, GameMode}, {game_name, TableName}, {tables_num, TablesNum}, {registrants, Users}, {quota_per_round, Quota}, {kakush_for_winners, KakushForWinners}, {kakush_for_loser, KakushForLoser}, {win_game_points, WinGamePoints}, {mul_factor, MulFactor}, {table_module, tavla_table}, {bot_module, tavla_bot}, {bots_replacement_mode, BotsReplacementMode}, {table_params, TableParams}, {common_params, Params} ]) end. create_elimination_trn(GameType, Params, Registrants) -> gas:info(?MODULE,"create_elimination_trn/3 Params:~p", [Params]), TrnId = proplists:get_value(trn_id, Params), QuotaPerRound = proplists:get_value(quota_per_round, Params), PlayersNumber = proplists:get_value(players_number, Params), Tours = proplists:get_value(tours, Params), GameMode = proplists:get_value(game_mode, Params), Speed = proplists:get_value(speed, Params), Awards = proplists:get_value(awards, Params), RegistrantsNum = length(Registrants), if RegistrantsNum =/= PlayersNumber -> gas:error(?MODULE,"create_elimination_trn/3 Error: Wrong number of the registrants: ~p (required: ~p). ", [RegistrantsNum, PlayersNumber]), exit(wrong_registrants_number); true -> do_nothing end, {ok, Plan} = matrix:get_plan(GameType, QuotaPerRound, PlayersNumber, Tours), case GameType of game_okey -> Rounds = 10, {ok, SetTimeout} = nsm_db:get(config,"games/okey/trn/elim/tour_time_limit/"++integer_to_list(Tours), 35*60*1000), TableParams = [ {table_name, ""}, {tournament_type, elimination}, {round_timeout, infinity}, {set_timeout, SetTimeout}, {speed, Speed}, {game_type, GameMode}, {rounds, Rounds}, {gosterge_finish_allowed, undefined}, {reveal_confirmation, true}, {next_series_confirmation, no}, {pause_mode, disabled}, {observers_allowed, false}, {slang_allowed, false}, {social_actions_enabled, false}, {mult_factor, 1} ], create_game(TrnId, elimination, [{game_type, GameType}, {game_mode, GameMode}, {registrants, Registrants}, {plan, Plan}, {quota_per_round, QuotaPerRound}, {rounds_per_tour, Rounds}, {tours, Tours}, {players_per_table, 4}, {speed, Speed}, {awards, Awards}, {trn_id, TrnId}, {table_module, okey_table}, {demo_mode, false}, {table_params, TableParams} ]); game_tavla -> Rounds = 3, {ok, SetTimeout} = nsm_db:get(config,"games/tavla/trn/elim/tour_time_limit/"++integer_to_list(Tours), 35*60*1000), TableParams = [ {table_name, ""}, {tournament_type, elimination}, {round_timeout, infinity}, {set_timeout, SetTimeout}, {speed, Speed}, {game_mode, GameMode}, {rounds, Rounds}, {next_series_confirmation, no}, {pause_mode, disabled}, {slang_allowed, false}, {observers_allowed, false}, {social_actions_enabled, false}, {mult_factor, 1}, {tables_num, 1} ], create_game(TrnId, elimination, [{game_type, GameType}, {game_mode, GameMode}, {registrants, Registrants}, {plan, Plan}, {quota_per_round, QuotaPerRound}, {rounds_per_tour, Rounds}, {tours, Tours}, {players_per_table, 2}, {speed, Speed}, {awards, Awards}, {trn_id,TrnId}, {table_module, tavla_table}, {demo_mode, false}, {table_params, TableParams} ]) end. start_tournament(TrnId,NumberOfTournaments,NumberOfPlayers,_Quota,_Tours,_Speed,GiftIds) -> gas:info(?MODULE,"START TOURNAMENT: ~p",[{TrnId,NumberOfTournaments,NumberOfPlayers,_Quota,_Tours,_Speed,GiftIds}]), {ok,Tournament} = nsm_db:get(tournament,TrnId), RealPlayersUnsorted = nsm_tournaments:joined_users(TrnId), if NumberOfPlayers - length(RealPlayersUnsorted) > 300 -> nsm_db:put(Tournament#tournament{status=canceled}), wf:send([tournament, TrnId, cancel], {TrnId}), error; true -> #tournament{quota = QuotaPerRound, tours = Tours, game_type = GameType, game_mode = GameMode, speed = Speed} = Tournament, RealPlayersPR = lists:keysort(#play_record.other, RealPlayersUnsorted), gas:info(?MODULE,"Head: ~p",[hd(RealPlayersPR)]), RealPlayers = [list_to_binary(Who)||#play_record{who=Who}<-RealPlayersPR, Who /= undefined], %% Registrants = case NumberOfPlayers > length(RealPlayers) of %% true -> nsm_db:put(Tournament#tournament{status=canceled}), RealPlayers; %% false -> [lists:nth(N,RealPlayers)||N<-lists:seq(1,NumberOfPlayers)] end, RealPlayersNumber = length(RealPlayers), Registrants = if NumberOfPlayers == RealPlayersNumber -> RealPlayers; NumberOfPlayers > RealPlayersNumber -> RealPlayers ++ [list_to_binary(anonymous:ima_gio(N)) || N <- lists:seq(1, NumberOfPlayers-RealPlayersNumber)]; true -> lists:sublist(RealPlayers, NumberOfPlayers) end, gas:info(?MODULE,"Registrants: ~p",[Registrants]), OkeyTournaments = [begin Params = [{trn_id, TrnId}, {quota_per_round, QuotaPerRound}, {players_number, NumberOfPlayers}, {tours, Tours}, {game_mode, GameMode}, {speed, Speed}, {awards, GiftIds}], {ok,GameId,A} = create_elimination_trn(GameType, Params, Registrants), nsm_db:put(Tournament#tournament{status=activated,start_time=time()}), wf:send([tournament, TrnId, activate], {TrnId}), {ok,GameId,A} end || _ <-lists:seq(1,NumberOfTournaments)], [{ok,OP1,_}|_] = OkeyTournaments, [{ok,OP2,_}|_] = lists:reverse(OkeyTournaments), gas:info(?MODULE,"Okey tournaments runned: ~p~n",[{OP1,OP2}]), OP1 end. get_tables(Id) -> qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{id = _Id}} <- gproc:table(props), Id == _Id ])). qlc_id(Id) -> qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{gameid = _GameId, id = _Id, owner = _Owner, creator = _Creator}} <- gproc:table(props), Id == _Id])). qlc_id_creator(Id,Creator,Owner) -> qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{gameid = _GameId, id = _Id, owner = _Owner, creator = _Creator}} <- gproc:table(props), Id == _Id, Creator == _Creator, Owner ==_Owner])). campaigns(reveal) -> campaigns(reveal,{2014,4,27},{2014,7,30}); campaigns(series) -> campaigns(series,{2014,4,27},{2014,7,30}). campaigns(series,From,To) -> {atomic,Res}=mnesia:transaction(fun() -> mnesia:select(series_event, ets:fun2ms(fun (#series_event{date=T}=S) when T > From, T < To -> S end)) end), lists:keysort(2,lists:foldr(fun series_aggregate/2,[],Res)); campaigns(reveal,From,To) -> {atomic,Res}=mnesia:transaction(fun() -> mnesia:select(reveal_event, ets:fun2ms(fun (#reveal_event{date=T}=S) when T > From, T < To -> S end)) end), lists:keysort(2,lists:foldr(fun reveal_aggregate/2,[],Res)). series_aggregate(#series_event{score = Count, user = Item}, Acc) -> case lists:keyfind(Item,1,Acc) of {Item,Sum} -> lists:keyreplace(Item,1,Acc,{Item,Count+Sum}); false -> [{Item,Count}|Acc] end. reveal_aggregate(#reveal_event{score = Count, user = Item}, Acc) -> case lists:keyfind(Item,1,Acc) of {Item,Sum} -> lists:keyreplace(Item,1,Acc,{Item,Count+Sum}); false -> [{Item,Count}|Acc] end. get_player_info(_,User) -> Okey = okey_scoring, Scoring = [ begin case kvs:get(series_log,{M,S,R,User}) of {ok,#series_log{type=M,speed=S,rounds=R,stats=Res}} -> Win = case lists:keyfind(winner,1,Res) of {_,Num1} -> Num1; _ -> 0 end, Los = case lists:keyfind(looser,1,Res) of {_,Num2} -> Num2; _ -> 0 end, [{lists:concat([wf:to_list(M)," ",wf:to_list(S)," ",wf:to_list(R)]), lists:concat([Win,"/",Los])}]; _ -> [] end end || M <- Okey:modes(), S <- Okey:speeds(), R <- Okey:rounds() ], Games=lists:flatten(Scoring), Reveals = case kvs:get(reveal_log,User) of {ok,R} -> R; _ -> #reveal_log{} end, Protocol = case kvs:get(protocol_log,User) of {ok,P} -> P; _ -> #protocol_log{} end, #stats_event{ player_id=User, games=Games, reveals=Reveals#reveal_log.stats, protocol=Protocol#protocol_log.stats, score=Reveals#reveal_log.score}. plist_setkey(Name,Pos,List,New) -> case lists:keyfind(Name,Pos,List) of false -> [New|List]; Element -> lists:keyreplace(Name,Pos,List,New) end.