123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- -module(game_tavla_bot).
- -author('Maxim Sokhatsky <maxim@synrc.com>').
- -behaviour(gen_server).
- -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
- -export([start/3, start_link/3, robot_init/1, init_state/2, join_game/1, get_session/1,
- send_message/2, call_rpc/2, do_skip/2, do_rematch/1, first_move_table/0, follow_board/3 ]).
- -include_lib("server/include/log.hrl").
- -include_lib("server/include/conf.hrl").
- -include_lib("server/include/basic_types.hrl").
- -include_lib("server/include/requests.hrl").
- -include_lib("server/include/game_tavla.hrl").
- -include_lib("server/include/game_okey.hrl").
- -include_lib("server/include/settings.hrl").
- -record(state, {
- moves = 0 :: integer(),
- %% started = false :: boolean(),
- %% next_initiated = false :: boolean(),
- table_id :: integer(),
- is_robot = true :: boolean(),
- board :: list(tuple('Color'(), integer) | null),
- user :: #'PlayerInfo'{},
- player_color :: integer(),
- players,
- uid :: 'PlayerId'(),
- owner :: pid(),
- owner_mon :: 'MonitorRef'(),
- session :: pid(),
- gid :: 'GameId'(),
- bot :: pid(),
- conn :: pid(),
- hand :: list(),
- running_requests = dict:new() :: any(),
- delay :: integer(),
- mode :: atom(),
- request_id = 0,
- confirmation :: yes_exit | no_exit | no}).
- % gen_server
- send_message(Pid, Message) -> gen_server:call(Pid, {send_message, Message}).
- call_rpc(Pid, Message) -> gen_server:call(Pid, {call_rpc, Message}).
- get_session(Pid) -> gen_server:call(Pid, get_session).
- init_state(Pid, Situation) -> gen_server:cast(Pid, {init_state, Situation}).
- join_game(Pid) -> gen_server:cast(Pid, join_game).
- start(Owner, PlayerInfo, GameId) -> gen_server:start(?MODULE, [Owner, PlayerInfo, GameId], []).
- start_link(Owner, PlayerInfo, GameId) -> gen_server:start_link(?MODULE, [Owner, PlayerInfo, GameId], []).
- init([Owner, PlayerInfo, GameId]) ->
- {ok, SPid} = game_session:start_link(self()),
- game_session:bot_session_attach(SPid, PlayerInfo),
- UId = PlayerInfo#'PlayerInfo'.id,
- ?INFO("BOTMODULE ~p started with game_session pid ~p", [UId,SPid]),
- {ok, #state{user = PlayerInfo, uid = UId, owner = Owner, gid = GameId, session = SPid}}.
- handle_call({send_message, Msg0}, _From, State) ->
- BPid = State#state.bot,
- Msg = flashify(Msg0),
- BPid ! Msg,
- {reply, ok, State};
- handle_call({call_rpc, Msg}, From, State) ->
- RR = State#state.running_requests,
- Id = State#state.request_id + 1,
- Self = self(),
- RR1 = dict:store(Id, From, RR),
- proc_lib:spawn_link(fun() ->
- Res = try
- Answer = game_session:process_request(State#state.session, "TAVLA BOT", Msg),
- {reply, Id, Answer}
- catch
- _Err:Reason -> {reply, Id, {error, Reason}}
- end,
- gen_server:call(Self, Res)
- end),
- {noreply, State#state{running_requests = RR1, request_id = Id}};
- handle_call({reply, Id, Answer}, _From, State) ->
- RR = State#state.running_requests,
- From = dict:fetch(Id, RR),
- gen_server:reply(From, Answer),
- {reply, ok, State};
- handle_call(get_session, _From, State) ->
- {reply, State#state.session, State};
- % {ok, SPid} = game_session:start_link(self()),
- % game_session:bot_session_attach(SPid, State#state.user),
- % {reply, State#state.session, State#state{session = SPid}};
- handle_call(Request, _From, State) ->
- Reply = ok,
- ?INFO("unknown call: ~p", [Request]),
- {reply, Reply, State}.
- handle_cast(join_game, State) ->
- Mon = erlang:monitor(process, State#state.owner),
- UId = State#state.uid,
- GId = State#state.gid,
- ?INFO("Init State User ~p",[State#state.user]),
- BPid = proc_lib:spawn_link(game_tavla_bot, robot_init, [#state{gid = GId, uid = UId, conn = self(), table_id = State#state.table_id, user = State#state.user}]),
- BPid ! join_game,
- {noreply, State#state{bot = BPid, owner_mon = Mon}};
- handle_cast(Msg, State) ->
- ?INFO("unknown cast: ~p", [Msg]),
- {noreply, State}.
- handle_info({'DOWN', Ref, process, _, Reason}, State = #state{owner_mon = OMon}) when OMon == Ref ->
- ?INFO("relay goes down with reason ~p so does bot", [Reason]),
- {stop, Reason, State};
- handle_info({send_message,M}, State) ->
- {noreply, State};
- handle_info(Info, State) ->
- {stop, {unrecognized_info, Info}, State}.
- terminate(_Reason, _State) -> ok.
- code_change(_OldVsn, State, _Extra) -> {ok, State}.
- % loops
- robot_init(State) ->
- ?INFO("Robot Init User Info ~p",[State#state.user]),
- robot_init_loop(State).
- robot_init_loop(State) -> % receiving messages from relay
- S = State#state.conn,
- Id = State#state.uid,
- GameId = State#state.gid,
- receive
- join_game ->
- case call_rpc(S, #join_game{game = GameId}) of
- #'TableInfo'{game = _Atom} -> tavla_client_loop(State);
- _Err -> ?INFO("ID: ~p failed take with msg ~p", [Id, _Err]),
- erlang:error(robot_cant_join_game)
- end
- end.
- tavla_client_loop(State) -> % incapsulate tavla protocol
- timer:sleep(300),
- S = State#state.conn,
- GameId = State#state.gid,
- Id = State#state.uid,
- MyColor = State#state.player_color,
- GameMode = State#state.mode,
- receive
- #game_event{event = <<"tavla_next_turn">>, args = Params} = Msg ->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- case check_can_roll(GameMode) of
- true ->
- case fix_color(proplists:get_value(color, Params)) of
- MyColor ->
- ?INFO("TAVLABOT ~p Doing roll", [Id]),
- roll_action(State,TableId),
- tavla_client_loop(State);
- _ -> tavla_client_loop(State)
- end;
- false ->
- ?INFO("TAVLABOT ~p Ignoring tavla_next_turn (paired mode). Server rolls automaticly.", [Id]),
- tavla_client_loop(State)
- end;
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_moves">>, args = Params} = Msg ->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- PlayerColor = fix_color(proplists:get_value(color, Params)),
- From = proplists:get_value(from, Params),
- To = proplists:get_value(to, Params),
- %% ?INFO("board before moves: ~p", [State#state.board]),
- Board = reverse_board(State#state.board, PlayerColor),
- FromR=rel(From,PlayerColor), ToR=rel(To,PlayerColor),
- NewBoard = reverse_board(follow_board(Board,FromR,ToR,PlayerColor), PlayerColor),
- %% ?INFO("board after moves: ~p", [NewBoard]),
- tavla_client_loop(State#state{board = NewBoard,moves = State#state.moves + 1});
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_turn_timeout">>, args = Params} = Msg ->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- PlayerColor = fix_color(proplists:get_value(color, Params)),
- Moves = ext_to_moves(proplists:get_value(moves, Params)),
- %% ?INFO("board before moves: ~p", [State#state.board]),
- Board = reverse_board(State#state.board, PlayerColor),
- F = fun({From, To}, B) ->
- FromR = rel(From, PlayerColor), ToR = rel(To, PlayerColor),
- follow_board(B, FromR, ToR, PlayerColor)
- end,
- NewBoard = reverse_board(lists:foldl(F, Board, Moves), PlayerColor),
- %% ?INFO("board after moves: ~p", [NewBoard]),
- tavla_client_loop(State#state{board = NewBoard, moves = State#state.moves + length(Moves)});
- _ -> tavla_client_loop(State) end;
- %% #game_event{event = <<"tavla_vido_request">>, args = Params} ->
- %% TableId = proplists:get_value(table_id, Params, 0),
- %% case TableId == State#state.table_id of true ->
- %%
- %% % ?INFO("tavla moves: ~p",[Params]),
- %% To = proplists:get_value(from, Params),
- %% vido(State,To,TableId),
- %% tavla_client_loop(State);
- %%
- %% _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_surrender_request">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- % ?INFO("tavla moves: ~p",[Params]),
- To = proplists:get_value(from, Params),
- surrender(State,To,TableId),
- tavla_client_loop(State);
- _ -> tavla_client_loop(State) end;
- %% #game_event{event = <<"tavla_ack">>, args = Params} ->
- %% TableId = proplists:get_value(table_id, Params, 0),
- %% case TableId == State#state.table_id of true ->
- %% _To = proplists:get_value(from, Params),
- %% _Type = proplists:get_value(type, Params),
- %% tavla_client_loop(State);
- %%
- %% _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_game_player_state">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- PlayersColors = proplists:get_value(players_colors, Params),
- WhosMove = proplists:get_value(whos_move, Params),
- BoardRaw = proplists:get_value(board, Params),
- Dice = proplists:get_value(dice, Params),
- GameState = proplists:get_value(game_state, Params),
- Paused = proplists:get_value(paused, Params),
- case Paused of
- true -> wait_for_resume();
- false -> ok
- end,
- FoundMyself = lists:keyfind(Id, #tavla_color_info.name, PlayersColors),
- PlayerColor = fix_color(FoundMyself#tavla_color_info.color),
- ?INFO("TAVLABOT ~p BoardRaw: ~p", [Id, BoardRaw]),
- Board = if BoardRaw == null -> null;
- true -> ext_to_board(BoardRaw)
- end,
- State1 = State#state{board = Board, moves = 0, player_color=PlayerColor},
- ?INFO("TAVLABOT ~p player color: ~p",[Id, PlayerColor]),
- MyMove = lists:member(fix_color(PlayerColor), WhosMove),
- case {MyMove, GameState} of
- {true, <<"first_move_competition">>} ->
- roll_action(State1, TableId),
- tavla_client_loop(State1);
- {true, <<"waiting_for_roll">>} ->
- roll_action(State1, TableId),
- tavla_client_loop(State1);
- {true, <<"waiting_for_move">>} ->
- do_move(State1, Dice, TableId, PlayerColor),
- tavla_client_loop(State1#state{moves = 1});
- {_, <<"initializing">>} ->
- tavla_client_loop(State1);
- {_, <<"finished">>} ->
- tavla_client_loop(State1);
- {false, _} ->
- tavla_client_loop(State1)
- end;
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_game_started">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- Players = proplists:get_value(players, Params),
- BoardRaw = proplists:get_value(board, Params),
- Competition = proplists:get_value(do_first_move_competition_roll, Params),
- FoundMyself = lists:keyfind(Id, #tavla_color_info.name, Players),
- PlayerColor = fix_color(FoundMyself#tavla_color_info.color),
- ?INFO("TAVLABOT ~p game_started, color: ~p",[Id, PlayerColor]),
- ?INFO("TAVLABOT ~p game_started, BoardRaw: ~p",[Id, BoardRaw]),
- Board = if BoardRaw == null -> null;
- true -> ext_to_board(BoardRaw)
- end,
- if Competition -> roll_action(State,TableId); true -> do_nothing end,
- tavla_client_loop(State#state{board = Board, moves=0, player_color=PlayerColor});
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_won_first_move">>, args = Params} = Msg ->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- State2 = case fix_color(proplists:get_value(color, Params)) of
- MyColor ->
- ?INFO("TAVLABOT ~p : I won the first move order.", [Id]),
- case proplists:get_value(reroll, Params) of
- false ->
- Dice = proplists:get_value(dice, Params),
- ?INFO("TAVLABOT ~p Reroll is not needed. Doing the first move with dice:~p",[Id, Dice]),
- do_move(State,Dice,TableId,MyColor), State#state{moves = State#state.moves + 1};
- true ->
- ?INFO("TAVLABOT ~p Reroll needed. So doing it.",[Id]),
- roll_action(State, TableId)
- end;
- _ -> ?INFO("TAVLABOT ~p tavla_won_first_move: Ignore rolls (another color)",[Id]), State
- end,
- tavla_client_loop(State2);
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_rolls">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- State2 = case fix_color(proplists:get_value(color, Params)) of
- MyColor ->
- Dice = proplists:get_value(dices, Params),
- case Dice of
- [_A,_B] ->
- ?INFO("TAVLABOT ~p Doing moves with dice: ~p",[Id, Dice]),
- do_move(State,Dice,TableId,MyColor), State#state{moves = State#state.moves + 1};
- [_C] ->
- ?INFO("TAVLABOT ~p Ignore the roll (first move competition)",[Id]),
- State
- end;
- _ -> ?INFO("TAVLABOT ~p Ignore the roll (another color)",[Id]), State
- end,
- tavla_client_loop(State2);
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"player_left">>, args = Params} = Msg ->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- Replaced = proplists:get_value(bot_replaced, Params, false) orelse
- proplists:get_value(human_replaced, Params, false),
- case Replaced of
- false ->
- call_rpc(S, #logout{});
- true ->
- tavla_client_loop(State)
- end;
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_game_info">>, args = Args} = Msg->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- User = State#state.user,
- Mode = proplists:get_value(game_mode, Args),
- TableId = proplists:get_value(table_id, Args),
- ?INFO("TAVLABOT game_info ~p ~p",[self(),User]),
- Players = proplists:get_value(players, Args),
- SeriesConfirmMode = proplists:get_value(series_confirmation_mode, Args),
- ?INFO("TAVLABOT players: ~p",[Players]),
- Delay = get_delay(fast),
- CatchTID = case User == undefined of
- false -> FoundMyself = lists:keyfind(User#'PlayerInfo'.id,#'PlayerInfo'.id,Players),
- case FoundMyself of false -> State#state.table_id; _ -> TableId end;
- true -> ?INFO("ERROR USER in ~p is not set!",[self()]), undefined
- end,
- tavla_client_loop( State#state{table_id = CatchTID, delay = Delay, mode = Mode,
- players = Players, confirmation = SeriesConfirmMode});
- #game_event{event = <<"tavla_series_ended">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- tavla_client_loop(State);
- _ -> tavla_client_loop(State) end;
- #game_event{event = <<"tavla_game_ended">>, args = Params} = Msg->
- TableId = proplists:get_value(table_id, Params, 0),
- case TableId == State#state.table_id of true ->
- ?INFO("TAVLABOT ~p : Received message: ~p", [Id, Msg]),
- say_ready(State,TableId),
- tavla_client_loop(State);
- _ -> tavla_client_loop(State) end;
- Msg ->
- ?INFO("TAVLABOT ~p Received UNKNOWN message: ~p",[Id, Msg]),
- tavla_client_loop(State)
- end.
- fix_color(Color) -> case Color of 1 -> 2; 2 -> 1 end.
- check_can_roll(Mode) ->
- case Mode of
- <<"paired">> -> false;
- _ -> true
- end.
- % logic
- follow_board(Board,Moves,PlayerColor) ->
- lists:foldl(fun ({From,To}, Acc) -> follow_board(Acc,From,To,PlayerColor) end, Board, Moves).
- follow_board(Board,From,To,PlayerColor) -> % track the moves to keep board consistent
- FromCell = lists:nth(From + 1,Board),
- {FromColor,_FromCount} = case FromCell of
- null -> {0,0};
- _Else -> _Else
- end,
- BoardWithKicks = [ case No of
- From -> case Cell of
- {_Color,1} -> {{0,0},null};
- {Color,Count} -> {{0,0},{Color,Count-1}};
- _ -> ?INFO("Board: ~p From ~p To ~p",[Board,From,To]),
- ?INFO("follow_board: cant move from empty slot"), exit(self(),kill), {{0,0},null}
- end;
- To -> case Cell of
- null -> case FromColor of 0 -> {{0,0},null}; _ -> {{0,0},{FromColor,1}} end;
- {0,0} -> case FromColor of 0 -> {{0,0},null}; _ -> {{0,0},{FromColor,1}} end;
- {Color,Count} ->
- case Color =:= FromColor of
- true -> {{0,0},{Color,Count+1}};
- false -> case Count of
- 1 when FromColor =/= 0 -> {{Color,1},{FromColor,1}};
- _ -> ?INFO("Board: ~p From ~p To ~p",[Board,From,To]),
- ?INFO("follow_board: cant kick tower"), exit(self(),kill), {{0,0},{Color,Count}}
- end
- end
- end;
- _ -> {{0,0},Cell}
- end || {Cell,No} <- lists:zip(Board,lists:seq(0,27)) ],
- {KickColor,KickAmount} = lists:foldl(
- fun({{KC,KA},_},{Col,Sum}) ->
- case KC of
- 0 -> {Col,Sum};
- _ -> {KC,Sum+KA}
- end
- end,{0,0}, BoardWithKicks),
- %% ?INFO("Kick: ~p",[{KickColor,KickAmount}]),
- NewBoard = [ case {No,KickColor} of
- {25,_} when KickColor =/= 0 -> case Cell of
- null -> {KickColor,KickAmount};
- {_Color,Sum} -> {KickColor,KickAmount+Sum}
- end;
- _ -> Cell
- end || {{{_KC,_KA},Cell},No} <- lists:zip(BoardWithKicks,lists:seq(0,27))],
- NewBoard.
- all_in_home(Board,Color) ->
- Lates = lists:foldr(fun (A,Acc) ->
- case A of
- {null,_No} -> Acc;
- {{C,_Count},No} -> NotInHome = (((No > 0)and(No < 19)) or (No == 26)),
- case {C, NotInHome} of
- {Color,true} -> Acc + 1;
- _ -> Acc
- end
- end
- end,0,lists:zip(Board,lists:seq(0,27))),
- Lates =:= 0.
- make_decision(Board,Dices2,Color,TableId) -> % produces tavla moves
- [X,Y] = Dices2,
- Dices = case X =:= Y of true -> [X,X,X,X]; false -> Dices2 end,
- Decision = first_available_move(Board,Dices,Color,TableId),
- ?INFO("Decision: ~p",[Decision]),
- Decision.
- %% norm([A,B]) -> case A > B of true -> {A,B}; false -> {B,A} end.
- first_move_table() -> [{{6,6},[{13,7},{13,7},{24,18},{24,18}]}, % based on
- {{6,5},[{24,13}]},
- {{6,4},[{24,18},{13,9}]},
- {{6,3},[{24,18},{13,10}]},
- {{6,2},[{24,18},{13,11}]},
- {{6,1},[{13,7},{8,7}]},
- {{5,5},[{13,3},{13,3}]},
- {{5,4},[{24,20},{13,8}]},
- {{5,3},[{8,3},{5,3}]},
- {{5,2},[{24,22},{13,8}]},
- {{5,1},[{24,23},{13,8}]},
- {{4,4},[{24,20},{24,20},{13,9},{13,9}]},
- {{4,3},[{24,21},{13,9}]},
- {{4,2},[{8,4},{6,4}]},
- {{4,1},[{24,23},{13,9}]},
- {{3,3},[{24,21},{24,21},{13,10},{13,10}]},
- {{3,2},[{24,21},{13,11}]},
- {{3,1},[{8,5},{6,5}]},
- {{2,2},[{13,11},{13,11},{6,4},{6,4}]},
- {{2,1},[{13,11},{6,5}]},
- {{1,1},[{8,7},{8,7},{6,5},{6,5}]}
- ].
- %make_first_move(Dices,TableId,PlayerColor) ->
- % {_,Moves} = lists:keyfind(norm(Dices),1,first_move_table()),
- % case PlayerColor of
- % 1 -> [ #'TavlaAtomicMove'{table_id = TableId,from=25-From,to=25-To} || {From,To} <- Moves];
- % 2 -> [ #'TavlaAtomicMove'{table_id = TableId,from=From,to=To} || {From,To} <- Moves]
- % end.
- tactical_criteria(Board,Color) ->
- case all_in_home(Board,Color) of
- true -> finish;
- false -> case lists:nth(27,Board) of
- {Color,Count} -> kicks;
- _ -> race
- end
- end.
- rel(X,C) -> case C of 2 -> case X of 0->27;27->0;26->25;25->26;_->25-X end;
- 1 -> X end.
- reverse_board(Board,PlayerColor) ->
- case PlayerColor of
- 1 -> Board;
- 2 -> [BE|Rest] = Board, [WE,WK,BK|Rest1] = lists:reverse(Rest), [WE] ++ Rest1 ++ [WK,BK,BE]
- end.
- %% first_available_move(RealBoard,Dice,Color,TableId) -> Moves
- first_available_move(RealBoard,Dice,Color,TableId) ->
- RelativeBoard = reverse_board(RealBoard,Color),
- F = fun(Die, {MovesAcc, BoardAcc, FailedDiceAcc}) ->
- %% ?INFO("board: ~p", [BoardAcc]),
- Tactic = tactical_criteria(BoardAcc, Color),
- %% ?INFO("tactical criteria: ~p", [Tactic]),
- case find_move(Color, Die, Tactic, BoardAcc) of
- {Move, NewBoard} -> {[Move | MovesAcc], NewBoard, FailedDiceAcc};
- false -> {MovesAcc, BoardAcc, [Die | FailedDiceAcc]}
- end
- end,
- {List, Board, FailedDice} = lists:foldl(F, {[], RelativeBoard, []}, Dice),
- %% ?INFO("moves found: ~p",[{List, FailedDice}]),
- [#'TavlaAtomicMove'{from=rel(From,Color), to=rel(To,Color)} || {From,To} <- lists:reverse(List)] ++
- if length(FailedDice) == 0; length(FailedDice) == length(Dice) -> [];
- true -> first_available_move(reverse_board(Board,Color),FailedDice,Color,TableId) end.
- %% find_move(Die, Tactic, Board) -> {Move, NewBoard} | false
- find_move(Color, Die, Tactic, Board) ->
- OppColor = case Color of 1 -> 2; 2 -> 1 end,
- find_move(Color, OppColor, Die, Tactic, Board, lists:sublist(lists:zip(Board,lists:seq(0,27)),2,24)).
- find_move(_Color, _OppColor, _Die, _Tactic, _Board, []) -> false;
- find_move(Color, OppColor, Die, Tactic, Board, [{Cell, No} | Rest]) ->
- %% ?INFO("checking pos: ~p", [{Cell, No, Die, Tactic}]),
- case {Cell, Tactic} of
- {null,kicks} when No == Die -> ?INFO("found kick to empty: ~p",[{26,No}]), {{26,No}, follow_board(Board,26,No,Color)};
- {{OppColor,1},kicks} when No == Die -> ?INFO("found kick kick: ~p",[{26,No}]), {{26,No}, follow_board(Board,26,No,Color)};
- {{Color,_},kicks} when No == Die -> ?INFO("found kick over own: ~p",[{26,No}]), {{26,No}, follow_board(Board,26,No,Color)};
- {{Color,_},finish} when No + Die >= 25 -> ?INFO("found finish move: ~p",[{No,27}]), {{No,27}, follow_board(Board,No,27,Color)};
- {{Color,_},Tactic} when (Tactic==race orelse Tactic==finish) andalso (No + Die < 25) ->
- case lists:nth(No+Die+1, Board) of
- null -> ?INFO("found race to empty: ~p",[{No,No+Die}]), {{No,No+Die}, follow_board(Board,No,No+Die,Color)};
- {Color,_} -> ?INFO("found race over own: ~p",[{No,No+Die}]), {{No,No+Die}, follow_board(Board,No,No+Die,Color)};
- {OppColor,1} -> ?INFO("found race kick: ~p",[{No,No+Die}]), {{No,No+Die}, follow_board(Board,No,No+Die,Color)};
- _ -> find_move(Color, OppColor, Die, Tactic, Board, Rest)
- end;
- _ -> find_move(Color, OppColor, Die, Tactic, Board, Rest)
- end.
- %% make_end_series(Color,TableId,Board) ->
- %% lists:foldl(fun (A,Acc) -> ?INFO("~p / ~p~n",[A,Acc]), {Cell,No} = A, case Cell of null -> Acc;
- %% {Color,Count} -> Acc ++
- %% [ #'TavlaAtomicMove'{from=No, to=27} || X <- lists:seq(1,Count)]; _ -> Acc end
- %% end, [], lists:sublist(lists:zip(Board,lists:seq(0,27)),1,27) ).
- % actions
- do_rematch(State) ->
- S = State#state.conn,
- GameId = State#state.gid,
- ok = call_rpc(S, #rematch{game = GameId}).
- say_ready(State,TableId) ->
- S = State#state.conn,
- GameId = State#state.gid,
- ok = call_rpc(S, #game_action{game = GameId, action = tavla_ready, args = [{table_id,TableId}]}).
- vido(State,To,TableId) ->
- S = State#state.conn,
- GameId = State#state.gid,
- ok = call_rpc(S, #game_action{game = GameId,
- action = tavla_vido_answer,
- args = [{table_id,TableId},{from,State#state.uid},{to,To},{answer,false}]}).
- surrender(State,To,TableId) ->
- S = State#state.conn,
- GameId = State#state.gid,
- ok = call_rpc(S, #game_action{game = GameId,
- action = tavla_surrender_answer,
- args = [{table_id,TableId},{from,State#state.uid},{to,To},{answer,true}]}).
- roll_action(State,TableId) ->
- S = State#state.conn,
- GameId = State#state.gid,
- ok = call_rpc(S, #game_action{game = GameId, action = tavla_roll, args = [{table_id,TableId}]}).
- do_skip(State,TableId) ->
- S = State#state.conn,
- GameId = State#state.gid,
- call_rpc(S, #game_action{
- game = GameId,
- action = tavla_skip,
- args = [{table_id,TableId}]}).
- do_move(State, Dices,TableId,PlayerColor) ->
- Delay = State#state.delay,
- simulate_delay(take, Delay),
- S = State#state.conn,
- GameId = State#state.gid,
- Id = State#state.uid,
-
- % Decision = case State#state.moves > 5 of
- % false -> make_decision(State#state.board, Dices, PlayerColor,TableId);
- % true -> make_end_series(PlayerColor,TableId,State#state.board)
- % end,
- Decision = make_decision(State#state.board, Dices, PlayerColor,TableId),
- case Decision of
- [] ->
- ?INFO("TAVLABOT ~p : No moves available. Do nothing", [Id]),
- no_moves;
- _ ->
- ?INFO("TAVLABOT ~p : Moves: ~p", [Id, Decision]),
- Resp = call_rpc(S, #game_action{game = GameId,
- action = tavla_move,
- args = [{table_id, TableId},{player, Id},{moves, Decision }]}),
- ?INFO("TAVLABOT_DBG ~p : Server response: ~p", [Id, Resp]),
- {ok, Decision, Resp}
- end.
- flashify(R) when is_tuple(R) ->
- [RecName | Rest] = tuple_to_list(R),
- Rest1 = lists:map(fun
- (X) -> flashify(X)
- end, Rest),
- list_to_tuple([RecName | Rest1]);
- flashify([{Key, _Value} | _] = P) when is_atom(Key) ->
- lists:map(fun
- ({K, V}) when is_atom(K) -> {K, flashify(V)}
- end, P);
- flashify(A) when A == true -> A;
- flashify(A) when A == false -> A;
- flashify(A) when A == null -> A;
- flashify(A) when A == undefined -> A;
- flashify(A) when is_atom(A) ->
- list_to_binary(atom_to_list(A));
- flashify(Other) ->
- Other.
- time_to_sleep(_, Delay) ->
- erlang:trunc((Delay / 3) * 2).
- simulate_delay(Action, Delay) ->
- TheDelay = time_to_sleep(Action, Delay),
- receive
- #game_paused{action = <<"pause">>} ->
- wait_for_resume()
- after TheDelay ->
- ok
- end.
- wait_for_resume() ->
- receive
- #game_paused{action = <<"resume">>} ->
- ok
- end.
- ext_to_board(ExtBoard) ->
- [case Cell of
- null -> null;
- #tavla_checkers{color = C, number = N} -> {fix_color(C), N}
- end || Cell <- ExtBoard].
- ext_to_moves(ExtMoves) ->
- [{From, To} || #'TavlaAtomicMoveServer'{from = From, to = To} <- ExtMoves].
- get_delay(fast) -> {ok, Val} = nsm_db:get(config,"games/tavla/robot_delay_fast", 6000), Val;
- get_delay(normal) -> {ok, Val} = nsm_db:get(config,"games/tavla/robot_delay_normal", 9000), Val;
- get_delay(slow) -> {ok, Val} = nsm_db:get(config,"games/tavla/robot_delay_slow", 15000), Val.
|