test_okey.erl 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. -module(test_okey).
  2. -include_lib("eunit/include/eunit.hrl").
  3. -include_lib("server/include/settings.hrl").
  4. -include_lib("server/include/requests.hrl").
  5. -include_lib("server/include/game_okey.hrl").
  6. %% debug
  7. -export([validate_hand/3,conductor/3]).
  8. -export([start/0, alt_play_3_players_1_bot/0, init_with_join_game/7]).
  9. -define(BT, 30000).
  10. -define(FLUSH_DELAY, 10).
  11. -define(SIM_DELAY, 10).
  12. -define(TCM, tc).
  13. -record(state, {
  14. conn,
  15. gid,
  16. uid,
  17. mode,
  18. hand,
  19. gosterge,
  20. pileh,
  21. acker_fun = fun(_Event) -> continue end,
  22. set_state :: #'OkeySetState'{}
  23. }).
  24. %% bot order
  25. -record(bo, {
  26. pid,
  27. event,
  28. event_no,
  29. order
  30. }).
  31. %% ===================================================================
  32. %% ===================================================================
  33. %% Tests
  34. %% ===================================================================
  35. %% ===================================================================
  36. game_creation_test_() ->
  37. {foreach,
  38. fun() -> tests:setup() end,
  39. fun(State) -> tests:cleanup(State) end,
  40. [
  41. % {timeout, 100, fun test_join_game_random_reveal/0},
  42. % {timeout, 100, fun test_match_me_random_reveal/0},
  43. % {timeout, 100, fun create_game_with_robots/0},
  44. % {timeout, 100, fun test_join_game_observer_settings/0},
  45. {timeout, 100, fun test_social_actions/0},
  46. fun() -> ok end
  47. ]
  48. }.
  49. game_ending_test_() ->
  50. {foreach,
  51. fun() -> tests:setup() end,
  52. fun(State) -> tests:cleanup(State) end,
  53. [
  54. % {timeout, 100, fun test_join_game_empty_pile/0},
  55. % {timeout, 100, fun test_join_game_countdown_random_reveal/0},
  56. fun() -> ok end
  57. ]
  58. }.
  59. replacement_test_() ->
  60. {foreach,
  61. fun() -> tests:setup() end,
  62. fun(State) -> tests:cleanup(State) end,
  63. [
  64. % {timeout, 100, fun alt_play_3_players_1_bot/0},
  65. % {timeout, 100, fun player_replacement/0},
  66. fun() -> ok end
  67. ]
  68. }.
  69. matchmaker_test_() ->
  70. {foreach,
  71. fun() -> tests:setup() end,
  72. fun(State) -> tests:cleanup(State) end,
  73. [
  74. % {timeout, 100, fun matchmaker_disconnect/0},
  75. fun() -> ok end
  76. ]
  77. }.
  78. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  79. %%% tests setup %%%
  80. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  81. %player_replacement() ->
  82. % MultiOwner = self(),
  83. % process_flag(trap_exit, true),
  84. % Clients = [ proc_lib:spawn_link(fun() ->
  85. % start_test_game_t(MultiOwner, join_game_player_replacement, empty_pile)
  86. % end) || _ <- lists:seq(1, 1) ],
  87. %
  88. % [ wait_for(C) || C <- Clients ].
  89. start() ->
  90. MultiOwner = self(),
  91. process_flag(trap_exit, true),
  92. Clients = [ proc_lib:spawn_link(fun() ->
  93. start_test_game_t(MultiOwner, join_game_robots, normal)
  94. end) || _ <- lists:seq(1, 1) ],
  95. [ wait_for(C) || C <- Clients ].
  96. alt_play_3_players_1_bot() ->
  97. MultiOwner = self(),
  98. process_flag(trap_exit, true),
  99. Clients = [ proc_lib:spawn_link(fun() ->
  100. start_test_game_t(MultiOwner, join_game_new, normal)
  101. end) || _ <- lists:seq(1, 1) ],
  102. [ wait_for(C) || C <- Clients ].
  103. %matchmaker_disconnect() ->
  104. % MultiOwner = self(),
  105. % process_flag(trap_exit, true),
  106. % Clients = [ proc_lib:spawn_link(fun() ->
  107. % start_test_game_t(MultiOwner, match_me_disconnect, normal)
  108. % end) || _ <- lists:seq(1, 1) ],%
  109. %
  110. % [ wait_for(C) || C <- Clients ].
  111. %test_join_game_random_reveal() ->
  112. % MultiOwner = self(),
  113. % process_flag(trap_exit, true),
  114. % Clients = [ proc_lib:spawn_link(fun() ->
  115. % start_test_game_t(MultiOwner, join_game, normal) end) || _ <- lists:seq(1, 1) ],
  116. % [ wait_for(C) || C <- Clients ].
  117. %test_join_game_countdown_random_reveal() ->
  118. % MultiOwner = self(),
  119. % process_flag(trap_exit, true),%
  120. %
  121. % Clients = [ proc_lib:spawn_link(fun() -> %
  122. % start_test_game_t(MultiOwner, join_game_countdown, normal)
  123. % end) || _ <- lists:seq(1, 1) ],
  124. %
  125. % [ wait_for(C) || C <- Clients ].
  126. %test_join_game_observer_settings() ->
  127. % MultiOwner = self(),
  128. % process_flag(trap_exit, true),
  129. % Clients = [ proc_lib:spawn_link(fun() ->
  130. % start_test_game_t(MultiOwner, join_game_observer, normal)
  131. % end) || _ <- lists:seq(1, 1) ],
  132. %
  133. % [ wait_for(C) || C <- Clients ].
  134. %test_join_game_empty_pile() ->
  135. % MultiOwner = self(),
  136. % process_flag(trap_exit, true),
  137. % Clients = [ proc_lib:spawn_link(fun() ->
  138. % start_test_game_t(MultiOwner, join_game, empty_pile)
  139. % end) || _ <- lists:seq(1, 1) ],
  140. %
  141. % [ wait_for(C) || C <- Clients ].
  142. test_social_actions() ->
  143. MultiOwner = self(),
  144. process_flag(trap_exit, true),
  145. Clients = [ proc_lib:spawn_link(fun() ->
  146. start_test_game_t(MultiOwner, test_social_actions, normal)
  147. end) || _ <- lists:seq(1, 1) ],
  148. [ wait_for(C) || C <- Clients ].
  149. start_test_game_t(MultiOwner, CreateMode, RevealMode) ->
  150. process_flag(trap_exit, true),
  151. Ids = [<<"radistao">>,<<"paul">>,<<"kunthar">>,<<"gleber">>],
  152. Host = localhost,
  153. Port = 9000,
  154. Owner = self(),
  155. Rematch = 0,
  156. case CreateMode of
  157. join_game_robots ->
  158. Robots = [robot,robot,robot],
  159. Humans = [<<"paul">>],%<<"radistao">>, <<"paul">>],
  160. {ok, GameId, _A} = game:create_table(game_okey, [{sets,2}, {rounds,20},{game_mode,color}], Robots ++ Humans),
  161. gas:info(?MODULE,"created table for Okey Game: gameid ~p",[{GameId,_A}]),
  162. Clients = [ proc_lib:spawn_link(fun() ->
  163. timer:sleep(crypto:rand_uniform(0, 10)),
  164. init_with_join_game(Owner, Host, Port, GameId, Id, Rematch, RevealMode)
  165. end) || Id <- Humans ],
  166. gas:info(?MODULE,"Human Pids: ~p",[Clients]),
  167. Orders = [];
  168. join_game ->
  169. {ok, GameId, _} = game:create_table(game_okey, [{game_mode, color}, {sets, 2}, {rounds, 2}], Ids),
  170. Orders = [],
  171. Clients = [ proc_lib:spawn_link(fun() ->
  172. timer:sleep(crypto:rand_uniform(0, 300)),
  173. init_with_join_game(Owner, Host, Port, GameId, Id, Rematch, RevealMode)
  174. end) || Id <- Ids ];
  175. join_game_new ->
  176. {ok, GameId, _} = game:create_table(game_okey, [{game_mode, color}, {sets, 2}, {rounds, 2}], Ids),
  177. Clients = [ proc_lib:spawn_link(fun() ->
  178. timer:sleep(crypto:rand_uniform(0, 300)),
  179. init_with_join_game(Owner, Host, Port, GameId, Id, Rematch, RevealMode)
  180. end) || Id <- Ids ],
  181. Orders = [#bo{pid = 2, event = game_ended, event_no = 2, order = stop}];
  182. join_game_countdown ->
  183. {ok, GameId, _} = game:create_table(game_okey, [{game_mode, countdown}, {gosterge_finish, true}], Ids),
  184. Orders = [#bo{pid = 2, event = game_ended, event_no = 10, order = stop}],
  185. Clients = [ proc_lib:spawn_link(fun() ->
  186. timer:sleep(crypto:rand_uniform(0, 300)),
  187. init_with_join_game(Owner, Host, Port, GameId, Id, Rematch, RevealMode) end) || Id <- Ids ];
  188. join_game_observer ->
  189. L = [{true, ok}, {false, {error, <<"this_game_is_private">>}}],
  190. lists:map(fun({Setting, ExpectedAnswer}) ->
  191. {ok, GameId, _} = game:create_table(game_okey, [{observers, Setting},
  192. {game_mode, standard}, {sets, 1}, {rounds, 1}], Ids),
  193. Bots = [ proc_lib:spawn_link(fun() ->
  194. timer:sleep(crypto:rand_uniform(0, 300)),
  195. init_with_join_game(Owner, Host, Port, GameId, Id, Rematch, RevealMode)
  196. end) || Id <- Ids ],
  197. Observers = [], % Observers = [<<"sustel">>, <<"kate">>],
  198. Bots2 = [ proc_lib:spawn_link(fun() ->
  199. timer:sleep(0),
  200. init_with_join_game_observe(Owner, Host, Port, GameId, Id, RevealMode, ExpectedAnswer)
  201. end) || Id <- Observers ],
  202. conductor([], Bots ++ Bots2)
  203. end, L),
  204. Orders = [],
  205. Clients = [];
  206. test_social_actions ->
  207. SenderFun = fun send_and_receive_social_action/2,
  208. ReceiverFun = fun receive_social_action/3,
  209. {ok, GameId, _} = game:create_table(game_okey, [{game_mode, countdown}, {gosterge_finish, true}], Ids),
  210. A = #bo{pid = 1, event = got_hand, event_no = 1, order = {do_and_continue, SenderFun, [<<"paul">>]}},
  211. B = #bo{pid = 2, event = got_hand, event_no = 1, order = {do_and_continue, ReceiverFun, [<<"radistao">>, <<"paul">>]}},
  212. C = #bo{pid = 3, event = got_hand, event_no = 1, order = {do_and_continue, ReceiverFun, [<<"radistao">>, <<"paul">>]}},
  213. D = #bo{pid = 4, event = got_hand, event_no = 1, order = {do_and_continue, ReceiverFun, [<<"radistao">>, <<"paul">>]}},
  214. Orders = [A, B, C, D],
  215. Clients = [ proc_lib:spawn_link(fun() -> init_with_join_game(Owner, Host, Port, Id, GameId,0, RevealMode)
  216. end) || Id <- Ids ]
  217. end,
  218. conductor(Orders, Clients),
  219. MultiOwner ! {self(), game_ended}.
  220. init_with_join_game(Owner, Host, Port, GameId, OwnId, Rematch, Mode) ->
  221. put(mode, Mode),
  222. log(started),
  223. S1 = ?TCM:connect(Host, Port),
  224. TT = ?TEST_TOKEN,
  225. #'PlayerInfo'{id = Id} = ?TCM:call_rpc(S1, #session_attach{token = TT}) ,
  226. log(connected),
  227. Stats = ?TCM:call_rpc(S1, #player_stats{game_type = <<"okey">>, player_id = Id}) ,
  228. #'PlayerOkeyStats'{} = Stats,
  229. ok = ?TCM:call_rpc(S1, #join_game{game = GameId}) ,
  230. State = #state{conn = S1, gid = GameId, uid = Id, acker_fun = standard_acker(Owner)},
  231. play_set(State, Rematch),
  232. log(finished),
  233. ok = ?TCM:flush_events(?FLUSH_DELAY),
  234. ok = ?TCM:close(S1).
  235. init_with_join_game_observe(_Owner, Host, Port, GameId, OwnId, Mode, EJoinResult) ->
  236. put(mode, Mode),
  237. log(started),
  238. S1 = ?TCM:connect(Host, Port),
  239. TT = ?TEST_TOKEN,
  240. #'PlayerInfo'{id = Id} = ?TCM:call_rpc(S1, #session_attach{token = TT}) ,
  241. log(connected),
  242. Stats = ?TCM:call_rpc(S1, #player_stats{game_type = <<"okey">>, player_id = Id}) ,
  243. #'PlayerOkeyStats'{} = Stats,
  244. JR = ?TCM:call_rpc(S1, #join_game{game = GameId}) ,
  245. gas:info(?MODULE,"JR: ~p, expected result: ~p", [JR, EJoinResult]),
  246. true = cmpr(EJoinResult, JR),
  247. log(finished),
  248. ok = ?TCM:flush_events(?FLUSH_DELAY),
  249. ok = ?TCM:close(S1).
  250. pickup_game(S0) ->
  251. Id = S0#state.uid,
  252. gas:info(?MODULE,"ID: ~p, waiting for #okey_game_info", [Id]),
  253. log(game_picked_up),
  254. GI = receive
  255. #'game_event'{event = <<"okey_game_info">>, args = Args0} ->
  256. {Keys,Values} = lists:unzip(Args0),
  257. A0 = list_to_tuple([okey_game_info|Values]),
  258. gas:info(?MODULE,"A0: ~p", [A0]),
  259. A0
  260. after ?BT -> erlang:error({server_timeout, "game_rematched"})
  261. end,
  262. gas:info(?MODULE,"ID: ~p, waiting for #okey_game_player_state", [Id]),
  263. GS = receive
  264. #'game_event'{event = <<"okey_game_player_state">>, args = Args} ->
  265. {Keys2,Values2} = lists:unzip(Args),
  266. A = list_to_tuple([okey_game_player_state|Values2]),
  267. gas:info(?MODULE,"A: ~p", [A]),
  268. A
  269. after ?BT -> erlang:error({server_timeout, "game_rematched"})
  270. end,
  271. gas:info(?MODULE,"picking up the game", []),
  272. NTI = GS#okey_game_player_state.next_turn_in,
  273. true = is_integer(NTI) orelse NTI =:= <<"infinity">>,
  274. SS = #'OkeySetState'{
  275. round_cur = GS#okey_game_player_state.current_round,
  276. round_max = GI#okey_game_info.rounds,
  277. set_cur = GI#okey_game_info.set_no,
  278. set_max = GI#okey_game_info.sets
  279. },
  280. Hand = GS#okey_game_player_state.tiles,
  281. State = S0#state{
  282. mode = GI#okey_game_info.game_type,
  283. set_state = SS,
  284. hand = Hand,
  285. gosterge = GS#okey_game_player_state.gosterge
  286. },
  287. Turn = GS#okey_game_player_state.whos_move,
  288. GameState = GS#okey_game_player_state.game_state,
  289. gas:info(?MODULE,"ID: ~p, picking up the game. Turn: ~p, GameState: ~p", [Id, Turn, GameState]),
  290. case {Turn, GameState} of
  291. {_, <<"game_finished">>} ->
  292. gas:info(?MODULE,"init bot finished", []),
  293. play_set(State, 0);
  294. {_, <<"do_okey_ready">>} ->
  295. gas:info(?MODULE,"init bot wait", []),
  296. loop_and_restart(State);
  297. {Id, <<"do_okey_take">>} ->
  298. gas:info(?MODULE,"init bot: move both", []),
  299. State1 = do_turn(State, 1),
  300. okey_client_loop(State1);
  301. {Id, <<"do_okey_discard">>} ->
  302. gas:info(?MODULE,"init bot: move discard only", []),
  303. {TryDiscard, _} = draw_random(Hand),
  304. Hand1 = do_discard(State, Hand, TryDiscard, 1),
  305. okey_client_loop(State#state{hand = Hand1});
  306. {_, <<"do_okey_challenge">>} ->
  307. gas:info(?MODULE,"init bot: challenge", []),
  308. do_challenge(State),
  309. okey_client_loop(State#state{hand = Hand});
  310. {_, _} ->
  311. gas:info(?MODULE,"init bot: not bot's move", []),
  312. okey_client_loop(State#state{hand = Hand})
  313. end.
  314. loop_and_restart(#state{set_state = #'OkeySetState'{round_max = MR, round_cur = RC}})
  315. when RC > MR, MR /= -1 ->
  316. ok;
  317. loop_and_restart(State) ->
  318. #state{set_state = #'OkeySetState'{round_cur = RC}} = State,
  319. {Hand0, Gosterge0} = get_hand(State),
  320. gas:info(?MODULE,"Human {Hand,Gosterge}: ~p",[{Hand0,Gosterge0}]),
  321. LoopRes = okey_client_loop(State#state{hand = Hand0, gosterge = Gosterge0}),
  322. gas:info(?MODULE,"Human LoopRes: ~p",[LoopRes]),
  323. case LoopRes of
  324. <<"done">> ->
  325. gas:info(?MODULE,"ID: ~p, next action done", [State#state.uid]),
  326. ok;
  327. <<"next_set">> ->
  328. say_ready(State),
  329. gas:info(?MODULE,"ID: ~p, next action next_set", [State#state.uid]),
  330. ok;
  331. <<"next_round">> ->
  332. say_ready(State),
  333. gas:info(?MODULE,"ID: ~p, next action next_round", [State#state.uid]),
  334. State1 = State#state{set_state = #'OkeySetState'{round_cur = RC + 1}},
  335. check_ack(State1, game_ended, fun loop_and_restart/1, [State1])
  336. end.
  337. play_set(State0, Rematch) ->
  338. % gas:info(?MODULE,"ID: ~p, sets: start", [State0#state.uid]),
  339. Id = State0#state.uid,
  340. State = init_okey_tour(State0),
  341. Rounds = (State#state.set_state)#'OkeySetState'.round_max,
  342. SetNo = (State#state.set_state)#'OkeySetState'.set_cur,
  343. Sets = (State#state.set_state)#'OkeySetState'.set_max,
  344. gas:info(?MODULE,"ID: ~p Start playing ~p/~p set of ~p rounds",[State0#state.uid,SetNo,Sets,Rounds]),
  345. loop_and_restart(State),
  346. gas:info(?MODULE,"ID: ~p Finish playing ~p/~p set of ~p rounds",[State0#state.uid,SetNo,Sets,Rounds]),
  347. case {SetNo, Rematch} of
  348. {X, 0} when X == Sets ->
  349. gas:info(?MODULE,"last set, no rematch", []),
  350. get_series_ended(Id),
  351. ok;
  352. {X, _} when X == Sets ->
  353. gas:info(?MODULE,"ID: ~p, last set, do rematch", [State0#state.uid]),
  354. play_set(State, Rematch-1);
  355. _ ->
  356. gas:info(?MODULE,"ID: ~p, play next set", [State0#state.uid]),
  357. play_set(State, Rematch)
  358. end.
  359. init_okey_tour(State) ->
  360. GameId = State#state.gid,
  361. receive
  362. #'game_event'{event = <<"okey_game_info">>, args = Args, game = GI} ->
  363. % gas:info(?MODULE,"CLIENT TOUR STARTED okey_game_info:~n ~p", [Args]),
  364. GT = proplists:get_value(game_type, Args),
  365. Rounds = proplists:get_value(rounds, Args),
  366. Sets = proplists:get_value(sets, Args),
  367. SetNo = proplists:get_value(set_no, Args),
  368. true = (GI =/= <<"undefined">>),
  369. true = (Sets =/= <<"undefined">>),
  370. true = (SetNo =/= <<"undefined">>),
  371. true = (GT =/= <<"undefined">>),
  372. true = (Rounds =/= <<"undefined">>),
  373. true = (GI =:= GameId),
  374. SS = #'OkeySetState'{
  375. round_cur = 1,
  376. round_max = Rounds,
  377. set_cur = SetNo,
  378. set_max = Sets
  379. },
  380. log(game_info),
  381. State#state{set_state = SS, mode = GT}
  382. after ?BT ->
  383. gas:info(?MODULE,"ERROR: ~p", [{server_timeout, "game_event:okey_game_info"}]),
  384. erlang:error({server_timeout, "game_event:okey_game_info"})
  385. end.
  386. get_series_ended(Id)->
  387. gas:info(?MODULE,"ID: ~p; waiting for okey_series_ended", [Id]),
  388. receive
  389. #game_event{event = <<"okey_series_ended">>} ->
  390. gas:info(?MODULE,"ID: ~p CLIENT SERIES ENDED", [Id]),
  391. log(tour_ended)
  392. after ?BT -> erlang:error({server_timeout, "okey_series_ended"})
  393. end.
  394. say_ready(State) ->
  395. S1 = State#state.conn,
  396. GameId = State#state.gid,
  397. <<"ok">> = ?TCM:call_rpc(S1, #game_action{game = GameId, action = okey_ready, args = []}) .
  398. get_hand(State) ->
  399. S1 = State#state.conn,
  400. GameId = State#state.gid,
  401. receive
  402. #'game_event'{event = <<"okey_game_started">>, args = Args} = _Msg ->
  403. log(game_started),
  404. MH = proplists:get_value(tiles, Args),
  405. G = proplists:get_value(gosterge, Args),
  406. CR = proplists:get_value(current_round, Args),
  407. CS = proplists:get_value(current_set, Args),
  408. ((MH == undefined) orelse (G == undefined)) andalso erlang:error(cant_get_params_of_hand),
  409. gas:info(?MODULE,"Human Round/Set: ~p/~p", [CR,CS]),
  410. HasGosterge = lists:member(G, MH),
  411. HasGostergeServer = ?TCM:call_rpc(S1, #game_action{game = GameId, action = okey_has_gosterge, args = []}),
  412. gas:info(?MODULE,"HasGostergeServer: ~p",[HasGostergeServer]),
  413. HasGosterge = HasGostergeServer,
  414. check_ack(State, got_hand, fun() -> ok end, []),
  415. {MH, G}
  416. after ?BT ->
  417. erlang:error({server_timeout, "game_event:okey_game_started"})
  418. end.
  419. check_ack(State = #state{acker_fun = F}, Event, ContinueFun, Args) ->
  420. A = F(Event),
  421. case A of
  422. continue ->
  423. apply(ContinueFun, Args);
  424. {sleep, T} ->
  425. timer:sleep(T),
  426. apply(ContinueFun, Args);
  427. {do_and_continue, What, With} ->
  428. apply(What, [State] ++ With),
  429. apply(ContinueFun, Args);
  430. stop ->
  431. gas:info(?MODULE,"ID: ~p, Pid: ~p, check_ack. STOPPING THE BOT!!!", [State#state.uid, self()]),
  432. erlang:error({terminate, acker_stop})
  433. end.
  434. validate_hand(S, GameId, Hand) ->
  435. case ?TCM:call_rpc(S, #game_action{
  436. game = GameId,
  437. action = okey_debug,
  438. args = []}) of
  439. {error, Reason} ->
  440. gas:info(?MODULE,"dying in fire. Reason: ~p", [Reason]),
  441. erlang:error(die_in_fire);
  442. ServerHand ->
  443. Res = game_okey:is_same_hand(ServerHand, Hand),
  444. case Res of
  445. false ->
  446. gas:info(?MODULE,"validation failed: ~p =/= ~p", [Hand, ServerHand]);
  447. _ ->
  448. ok
  449. end,
  450. Res
  451. end.
  452. okey_client_loop(State) ->
  453. Id = State#state.uid,
  454. receive
  455. #'game_event'{event = <<"okey_next_turn">>, args = Args} ->
  456. Timeout = crypto:rand_uniform(0, ?SIM_DELAY),
  457. State1 = case {proplists:get_value(player, Args), proplists:get_value(can_challenge, Args)} of
  458. {Id, false} ->
  459. do_turn(State, Timeout);
  460. {_OtherId, _} ->
  461. State
  462. end,
  463. okey_client_loop(State1);
  464. #'game_event'{event = <<"okey_player_ready">>} ->
  465. okey_client_loop(State);
  466. #'game_event'{event = <<"okey_player_has_gosterge">>, args = _Args} ->
  467. okey_client_loop(State);
  468. #'game_event'{event = <<"okey_tile_taken">>, args = Args} ->
  469. case proplists:get_value(revealed, Args) of
  470. null ->
  471. NewH = proplists:get_value(pile_height, Args),
  472. okey_client_loop(State#state{pileh = NewH});
  473. _ ->
  474. okey_client_loop(State)
  475. end;
  476. #'game_event'{event = <<"okey_tile_discarded">>} ->
  477. okey_client_loop(State);
  478. #'game_event'{event = <<"okey_revealed">>} ->
  479. do_challenge(State),
  480. okey_client_loop(State);
  481. #'game_event'{event = <<"okey_series_ended">>, args = Args} ->
  482. S = State#state.conn,
  483. ?TCM:rpc(S, #logout{}),
  484. NextAction = proplists:get_value(next_action, Args),
  485. NextAction;
  486. #'game_event'{event = <<"okey_round_ended">>, args = Args} ->
  487. okey_round_ended_checks(Args, State),
  488. GS = proplists:get_value(good_shot, Args),
  489. Reason = proplists:get_value(reason, Args),
  490. NextAction = proplists:get_value(next_action, Args),
  491. gas:info(?MODULE,"ID: ~p game ended, good_shot: ~p, reason: ~p", [Id, GS, Reason]),
  492. NextAction;
  493. #player_left{} ->
  494. okey_client_loop(State);
  495. #'game_event'{event = <<"okey_game_started">>, args = _Args} ->
  496. okey_client_loop(State);
  497. % erlang:error({protocol_breach, okey_series_ended});
  498. #'game_event'{event = <<"okey_game_info">>, args = _Args} ->
  499. okey_client_loop(State);
  500. % erlang:error({protocol_breach, okey_game_info});
  501. #'game_event'{event = <<"player_left">>} ->
  502. okey_client_loop(State);
  503. #'game_event'{args = Args} ->
  504. NextAction = proplists:get_value(next_action, Args),
  505. NextAction;
  506. _Msg ->
  507. gas:info(?MODULE,"the msg: ~p", [_Msg]),
  508. erlang:error({bot_received_unrecognized_message, _Msg})
  509. after ?BT ->
  510. log(server_timeouted),
  511. erlang:error({server_timeout, "okey_client_loop_timeout"})
  512. end.
  513. do_turn(State, Timeout) ->
  514. {Timeouted, Hand1} = do_take(State, Timeout),
  515. true = is_list(Hand1),
  516. {TryDiscard, WinningHand} = draw_random(Hand1),
  517. Pile0Height = State#state.pileh,
  518. FHand = case {Timeouted, is_revealing(Pile0Height)} of
  519. {false, true} ->
  520. do_reveal(State, WinningHand, TryDiscard);
  521. {false, false} ->
  522. do_discard(State, Hand1, TryDiscard, Timeout);
  523. {true, _} ->
  524. Hand1
  525. end,
  526. State#state{hand = FHand}.
  527. do_challenge(State) ->
  528. GameId = State#state.gid,
  529. S = State#state.conn,
  530. ZZZ = ?TCM:call_rpc(S, #game_action{
  531. game = GameId,
  532. action = okey_challenge,
  533. args = [ {challenge, random_bool(0.2)} ]}),
  534. gas:info(?MODULE,"ID: ~p challenge result: ~p", [State#state.uid, ZZZ]),
  535. ok.
  536. do_take(State, Timeout) ->
  537. S = State#state.conn,
  538. GameId = State#state.gid,
  539. Hand = State#state.hand,
  540. {is_list, true} = {is_list, is_list(Hand)},
  541. receive
  542. #'game_event'{event = <<"okey_turn_timeout">>, args = Args} ->
  543. % gas:info(?MODULE,"ID: ~p I timeouted on take", [Id]),
  544. TileT = proplists:get_value(<<"tile_taken">>, Args),
  545. TileD = proplists:get_value(<<"tile_discarded">>, Args),
  546. Hand1 = lists:delete(TileD, Hand),
  547. {true, [TileT | Hand1]}
  548. after Timeout ->
  549. Pile = crypto:rand_uniform(0, 2),
  550. case ?TCM:call_rpc(S, #game_action{
  551. game = GameId,
  552. action = okey_take,
  553. args = [ {pile, Pile} ]}) of
  554. #'OkeyPiece'{} = Tosh ->
  555. MyHand = [Tosh | Hand],
  556. % gas:info(?MODULE,"ID: ~p Take tosh in ~p pile! Get: ~p", [Id, Pile, Tosh]),
  557. {false, MyHand};
  558. {error, <<"cant_take_do_discard">>} ->
  559. % gas:info(?MODULE,"ID: ~p Has 15 items in hand. 15=~p", [Id, length(Hand)]),
  560. {false, Hand};
  561. {error, <<"game_has_already_ended">>} = Err ->
  562. case State#state.mode of
  563. <<"countdown">> ->
  564. {false, Hand};
  565. _ ->
  566. gas:info(?MODULE,"ID: ~p; mode:~p; failed take with msg ~p",
  567. [State#state.uid, State#state.mode, Err]),
  568. erlang:error(failed_take)
  569. end;
  570. Err ->
  571. gas:info(?MODULE,"ID: ~p failed take with msg ~p", [State#state.uid, Err]),
  572. erlang:error(failed_take)
  573. end
  574. end.
  575. do_discard(State, Hand, Item, Timeout) ->
  576. S = State#state.conn,
  577. GameId = State#state.gid,
  578. Id = State#state.uid,
  579. receive
  580. #'game_event'{event = <<"okey_tile_discarded">>, args = Args} ->
  581. case proplists:get_value(<<"player">>, Args) of
  582. Id ->
  583. Tile = proplists:get_value(<<"player">>, Args),
  584. lists:delete(Tile, Hand)
  585. end
  586. after Timeout ->
  587. Hand1 = lists:delete(Item, Hand),
  588. _Res = ?TCM:call_rpc(S, #game_action{game = GameId, action = <<"okey_discard">>,
  589. args = [ {tile, Item} ]}),
  590. Hand1
  591. end.
  592. do_reveal(State, Hand, Item) ->
  593. S = State#state.conn,
  594. GameId = State#state.gid,
  595. ?TCM:call_rpc(S, #game_action{
  596. game = GameId,
  597. action = okey_reveal,
  598. args = [ {discarded, Item},
  599. {hand, Hand}
  600. ]}),
  601. Hand.
  602. draw_random([One]) ->
  603. {One, []};
  604. draw_random(List) ->
  605. {is_list, true} = {is_list, is_list(List)},
  606. Pos = crypto:rand_uniform(1, length(List)),
  607. Joker = lists:nth(Pos, List),
  608. ResList = lists:delete(Joker, List),
  609. {Joker, ResList}.
  610. is_revealing(undefined) ->
  611. false;
  612. is_revealing(PileHeight) ->
  613. Mode = case get(mode) of
  614. undefined -> normal;
  615. A -> A
  616. end,
  617. MaxProb = case get(reveal_probability) of
  618. undefined -> 700.0;
  619. Value -> Value
  620. end,
  621. is_revealing(PileHeight, MaxProb, Mode).
  622. is_revealing(PileHeight, MaxProb, normal) ->
  623. HS = game_okey:hand_size(),
  624. MaxHeight = ((HS - 1) * 4 * 2 + 2.0) - (HS * 4),
  625. X = erlang:abs((MaxHeight - PileHeight) / MaxHeight),
  626. Prob = (X * X * X * X * X * X) * MaxProb,
  627. Point = crypto:rand_uniform(1, 1000),
  628. Prob > Point;
  629. is_revealing(_PileHeight, _, empty_pile) ->
  630. false.
  631. wait_for(_C) ->
  632. receive
  633. {_, game_ended} ->
  634. gas:info(?MODULE,"client: game ended");
  635. {'EXIT', _, normal} = M ->
  636. gas:info(?MODULE,"client: normal ending: ~p", [M]);
  637. {'EXIT', _C, Reason} = M ->
  638. gas:info(?MODULE,"client: FAILURE message: ~p", [{M,_C,Reason}]),
  639. erlang:error(Reason);
  640. M ->
  641. gas:info(?MODULE,"client: unrecognized result: ~p", [M]),
  642. erlang:error(M)
  643. end.
  644. %% checks
  645. okey_round_ended_checks(Args, State) ->
  646. Score = proplists:get_value(score, Args),
  647. Mode = State#state.mode,
  648. {newer_go_below_10, true} =
  649. {newer_go_below_10, Mode =/= <<"countdown">> orelse Score > -1}.
  650. %% Tests
  651. reveal_probability_loop(0) ->
  652. erlang:error(reveal_probability_loop_failed);
  653. reveal_probability_loop(N) ->
  654. case is_revealing(N) of
  655. true ->
  656. ok;
  657. false ->
  658. reveal_probability_loop(N-1)
  659. end.
  660. reveal_probability_loop_test() ->
  661. put(reveal_probability, 1000.0),
  662. reveal_probability_loop(50).
  663. log(Msg) ->
  664. ?TCM:log(Msg).
  665. random_bool(Prob) ->
  666. Point = crypto:rand_uniform(0, 1000),
  667. Prob*1000 > Point.
  668. standard_acker(Owner) ->
  669. Self = self(),
  670. Ref = make_ref(),
  671. fun(Event) ->
  672. E = {event, Ref, Self, Event},
  673. gas:info(?MODULE,"standard acker. ~p ! ~p", [Owner, E]),
  674. Owner ! E,
  675. receive
  676. {ARef, Res} when Ref == ARef ->
  677. gas:info(?MODULE,"standard acker got '~p' answer on question ~p", [Res,{Owner, E}]),
  678. Res
  679. end
  680. end.
  681. conductor(Orders, Clients) ->
  682. gas:info(?MODULE,"conductor init", []),
  683. Pairs = lists:zip(lists:seq(1, length(Clients)), Clients),
  684. Orders2 = lists:map(fun(O) ->
  685. {_, P} = lists:keyfind(O#bo.pid, 1, Pairs),
  686. O#bo{pid = P}
  687. end, Orders),
  688. conductor(Orders2, Clients, []).
  689. conductor(_Orders, [], _Events) ->
  690. gas:info(?MODULE,"conductor stop", []),
  691. ok;
  692. conductor(Orders, Clients, Events) ->
  693. receive
  694. {event, Ref, Pid, Event} = _E ->
  695. E2 = update_events(Events, Pid, Event),
  696. Z = match_event(Orders, E2, Pid, Event),
  697. case Z of
  698. #bo{order = Order} ->
  699. Pid ! {Ref, Order};
  700. _ ->
  701. Pid ! {Ref, continue}
  702. end,
  703. conductor(Orders, Clients, E2);
  704. {C, game_ended} ->
  705. gas:info(?MODULE,"conductor: game ended", []),
  706. conductor(Orders, lists:delete(C, Clients), Events);
  707. {'EXIT', C, normal} = M ->
  708. gas:info(?MODULE,"conductor: normal ending: ~p", [M]),
  709. conductor(Orders, lists:delete(C, Clients), Events);
  710. {'EXIT', C, {terminate, acker_stop}} = M ->
  711. gas:info(?MODULE,"conductor: removing client ~p because of ~p", [C, M]),
  712. conductor(Orders, lists:delete(C, Clients), Events);
  713. {'EXIT', _C, Reason} = M ->
  714. gas:info(?MODULE,"conductor: FAILURE message: ~p", [{M,_C,Reason}]),
  715. erlang:error(Reason);
  716. M ->
  717. gas:info(?MODULE,"conductor: unrecognized msg from client: ~p", [M]),
  718. erlang:error(M)
  719. end.
  720. update_events(Events, Pid, Event) ->
  721. F = [ X || {APid, AEvent, _} = X <- Events, Pid == APid, Event == AEvent ],
  722. case F of
  723. [] ->
  724. [{Pid, Event, 1} | Events];
  725. [{_,_,C} = X] ->
  726. L1 = lists:delete(X, Events),
  727. [{Pid, Event, C+1} | L1]
  728. end.
  729. match_event(Orders, Events, Pid, Event) ->
  730. Z = [ X || {APid, AEvent, _} = X <- Events, Pid == APid, Event == AEvent ],
  731. [{_, _, C}] = Z,
  732. Matched = [ Y || Y = #bo{pid = P, event = E} <- Orders, P == Pid, E == Event ],
  733. case Matched of
  734. [BO = #bo{event_no = EN} | _] when EN == C ->
  735. BO;
  736. _X ->
  737. false
  738. end.
  739. send_and_receive_social_action(State, Recipient) ->
  740. GID = State#state.gid,
  741. ?TCM:call_rpc(State#state.conn, #social_action{game = GID, type = 0, recipient = Recipient}),
  742. receive_social_action(State, State#state.uid, Recipient).
  743. receive_social_action(_State, Sender, Recipient) ->
  744. receive
  745. #social_event{type = Type, initiator = I, recipient = R} ->
  746. true = Type == 0,
  747. true = Sender == I,
  748. true = Recipient == R
  749. end.
  750. cmpr(X, Y) when element(1, X) == element(1, Y) ->
  751. true;
  752. cmpr(B, B) ->
  753. true;
  754. cmpr(_, _) ->
  755. false.