<commit_before>-module(game).

-behaviour(gen_fsm).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("tarabish_constants.hrl").
-include("tarabish_types.hrl").

-include_lib("eunit/include/eunit.hrl").

%% --------------------------------------------------------------------
%% External exports
-export([start/1, determine_dealer/2]).

%% gen_fsm callbacks
-export([init/1, state_name/2, state_name/3, handle_event/3,
	 handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).

%% states:
-export([wait_trump/3, wait_card/3]).

%% from table:
-export([call_trump/3, play_card/3]).

-record(state, {table,
                score,    % Two scores { , } (0,2 -- 1,3)

                % Set per hand:
                hands,    % What the players are holding{[], [], [], []}
                deck,     % What's left of the deck
                dealer,   % Which seat is dealing
                trick,    % Trick number (for runs/done)
                trump,    % Trump for this hand
                hscore,   % Score for this hand (for bait, etc) { , }
                caller,   % 0 or 1 for who called trump

                % Set per trick:
                order,    % The deal/play order for this hand
                inplay,   % Current cards on the table as [(Card, Seat),]
                ledin     % Suit that started the thing
              }).

%% ====================================================================
%% External functions
%% ====================================================================
start(TablePid) ->
  gen_fsm:start(?MODULE, [TablePid], []).

call_trump(Game, Seat, Suit) ->
  gen_fsm:sync_send_event(Game, {call_trump, Seat, Suit}).

play_card(Game, Seat, Card) ->
  gen_fsm:sync_send_event(Game, {play_card, Seat, Card}).

%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, StateName, StateData}          |
%%          {ok, StateName, StateData, Timeout} |
%%          ignore                              |
%%          {stop, StopReason}
%% --------------------------------------------------------------------
% TODO: monitor table
init([Table]) ->

  % TODO: use crypto:rand_bytes instead of random for shuffle
  % seed random number generator
  {A1,A2,A3} = now(),
  random:seed(A1, A2, A3),

  % in 0, 1, 2, 3
  Dealer = determine_dealer(Table, deck:shuffle(deck:new())),

  DealerEvent = #event{type=?tarabish_EventType_DEALER, seat=Dealer},
  table:broadcast(Table, DealerEvent),

  State = new_hand(Dealer, #state{table=Table, score={0,0}}),

  {ok, wait_trump, State}.


% Handle force the dealer:
wait_trump({call_trump, Seat, ?tarabish_PASS}, _From,
    #state{order = [Seat|[]]} = State) ->
  {reply, {error, forced_to_call}, wait_trump, State};

% Other player passes
wait_trump({call_trump, Seat, ?tarabish_PASS = Suit}, _From,
    #state{order=[Seat|Rest]} = State) ->

  Event = #event{type=?tarabish_EventType_CALL_TRUMP, seat=Seat, suit=Suit},
  table:broadcast(State#state.table, Event),

  AskTrumpEvent = #event{type=?tarabish_EventType_ASK_TRUMP, seat=hd(Rest)},
  table:broadcast(State#state.table, AskTrumpEvent),

  {reply, ok, wait_trump, State#state{order=Rest}};

% Non pass:
wait_trump({call_trump, Seat, Suit}, _From,
    #state{order=[Seat|_Rest]} = State) ->

  Event = #event{type=?tarabish_EventType_CALL_TRUMP, seat=Seat, suit=Suit},
  table:broadcast(State#state.table, Event),
  State1 = deal3(State),
  State2 = new_trick(State1#state.dealer + 1,
      State1#state{trick=1, trump=Suit, caller=(Seat rem 2)}),

  AskCardEvent = #event{type=?tarabish_EventType_ASK_CARD,
    seat=hd(State2#state.order)},
  table:broadcast(State#state.table, AskCardEvent),

  {reply, ok, wait_card, State2};

wait_trump(_Event, _From, State) ->
  {reply, {error, invalid}, wait_trump, State}.

process_hand(#state{hscore={S1, S2}, caller=Caller} = State) when S1 == S2 ->
  % Half bait
  process_hand(S1 * Caller, S2 * ((Caller + 1) rem 2), ?tarabish_BaitType_HALF, State);

process_hand(#state{hscore={S1, S2}, caller=0} = State) when S1 < S2 ->
  % bait
  process_hand(0, S1 + S2, ?tarabish_BaitType_FULL, State);

process_hand(#state{hscore={S1, S2}, caller=1} = State) when S2 < S1 ->
  % bait
  process_hand(S1 + S2, 0, ?tarabish_BaitType_FULL, State);

process_hand(#state{hscore={S1, S2}} = State) ->
  % normal
  process_hand(S1, S2, ?tarabish_BaitType_NONE, State).

process_hand(Score1, Score2, BaitType,
  #state{score=Score, caller=_Caller, dealer=Dealer, table=Table} = State) ->

  % TODO: score to 500
  HandScoreList = [Score1, Score2],
  ScoreList = tuple_to_list(Score),
  NewScoresList = lists:zipwith(fun(X, Y) -> X + Y end, HandScoreList, ScoreList),

  Event = #event{type=?tarabish_EventType_HAND_DONE,
                 hand_score=HandScoreList,
                 score=NewScoresList,
                 bait=BaitType},
  table:broadcast(Table, Event),

  State1 = new_hand(Dealer + 1, State#state{score=list_to_tuple(NewScoresList)}),
  {reply, ok, wait_trump, State1}.

process_trick(LastWin, #state{trick=9} = State) ->
  % TODO: count up score
  % TODO: add 20's and 50's
  % TODO: bait
  State1 = add_hscore(LastWin, 10, State),

  process_hand(State1);

%  Event = #event{type=?tarabish_EventType_ASK_CARD, seat=LastWin},
%  table:broadcast(State#state.table, Event),

%  NewOrder = create_order(LastWin),
%  {reply, ok, wait_card, State#state{order=NewOrder, inplay=[]}};

process_trick(LastWin, #state{trick=Trick} = State) ->
  Event = #event{type=?tarabish_EventType_ASK_CARD, seat=LastWin},
  table:broadcast(State#state.table, Event),

  {reply, ok, wait_card,
    new_trick(LastWin, State#state{trick = Trick + 1})}.

% First card of the trick
process_card(Seat, Card, Rest, #state{ledin=?tarabish_NONE} = State) ->
  process_card(Seat, Card, Rest, State#state{ledin=Card#card.suit});

process_card(Seat, Card, [], State) ->

  InPlay = lists:reverse([{Card, Seat}|State#state.inplay]),<commit_msg>Remove completed TODOs.<commit_after>-module(game).

-behaviour(gen_fsm).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("tarabish_constants.hrl").
-include("tarabish_types.hrl").

-include_lib("eunit/include/eunit.hrl").

%% --------------------------------------------------------------------
%% External exports
-export([start/1, determine_dealer/2]).

%% gen_fsm callbacks
-export([init/1, state_name/2, state_name/3, handle_event/3,
	 handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).

%% states:
-export([wait_trump/3, wait_card/3]).

%% from table:
-export([call_trump/3, play_card/3]).

-record(state, {table,
                score,    % Two scores { , } (0,2 -- 1,3)

                % Set per hand:
                hands,    % What the players are holding{[], [], [], []}
                deck,     % What's left of the deck
                dealer,   % Which seat is dealing
                trick,    % Trick number (for runs/done)
                trump,    % Trump for this hand
                hscore,   % Score for this hand (for bait, etc) { , }
                caller,   % 0 or 1 for who called trump

                % Set per trick:
                order,    % The deal/play order for this hand
                inplay,   % Current cards on the table as [(Card, Seat),]
                ledin     % Suit that started the thing
              }).

%% ====================================================================
%% External functions
%% ====================================================================
start(TablePid) ->
  gen_fsm:start(?MODULE, [TablePid], []).

call_trump(Game, Seat, Suit) ->
  gen_fsm:sync_send_event(Game, {call_trump, Seat, Suit}).

play_card(Game, Seat, Card) ->
  gen_fsm:sync_send_event(Game, {play_card, Seat, Card}).

%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, StateName, StateData}          |
%%          {ok, StateName, StateData, Timeout} |
%%          ignore                              |
%%          {stop, StopReason}
%% --------------------------------------------------------------------
% TODO: monitor table
init([Table]) ->

  % TODO: use crypto:rand_bytes instead of random for shuffle
  % seed random number generator
  {A1,A2,A3} = now(),
  random:seed(A1, A2, A3),

  % in 0, 1, 2, 3
  Dealer = determine_dealer(Table, deck:shuffle(deck:new())),

  DealerEvent = #event{type=?tarabish_EventType_DEALER, seat=Dealer},
  table:broadcast(Table, DealerEvent),

  State = new_hand(Dealer, #state{table=Table, score={0,0}}),

  {ok, wait_trump, State}.


% Handle force the dealer:
wait_trump({call_trump, Seat, ?tarabish_PASS}, _From,
    #state{order = [Seat|[]]} = State) ->
  {reply, {error, forced_to_call}, wait_trump, State};

% Other player passes
wait_trump({call_trump, Seat, ?tarabish_PASS = Suit}, _From,
    #state{order=[Seat|Rest]} = State) ->

  Event = #event{type=?tarabish_EventType_CALL_TRUMP, seat=Seat, suit=Suit},
  table:broadcast(State#state.table, Event),

  AskTrumpEvent = #event{type=?tarabish_EventType_ASK_TRUMP, seat=hd(Rest)},
  table:broadcast(State#state.table, AskTrumpEvent),

  {reply, ok, wait_trump, State#state{order=Rest}};

% Non pass:
wait_trump({call_trump, Seat, Suit}, _From,
    #state{order=[Seat|_Rest]} = State) ->

  Event = #event{type=?tarabish_EventType_CALL_TRUMP, seat=Seat, suit=Suit},
  table:broadcast(State#state.table, Event),
  State1 = deal3(State),
  State2 = new_trick(State1#state.dealer + 1,
      State1#state{trick=1, trump=Suit, caller=(Seat rem 2)}),

  AskCardEvent = #event{type=?tarabish_EventType_ASK_CARD,
    seat=hd(State2#state.order)},
  table:broadcast(State#state.table, AskCardEvent),

  {reply, ok, wait_card, State2};

wait_trump(_Event, _From, State) ->
  {reply, {error, invalid}, wait_trump, State}.

process_hand(#state{hscore={S1, S2}, caller=Caller} = State) when S1 == S2 ->
  % Half bait
  process_hand(S1 * Caller, S2 * ((Caller + 1) rem 2), ?tarabish_BaitType_HALF, State);

process_hand(#state{hscore={S1, S2}, caller=0} = State) when S1 < S2 ->
  % bait
  process_hand(0, S1 + S2, ?tarabish_BaitType_FULL, State);

process_hand(#state{hscore={S1, S2}, caller=1} = State) when S2 < S1 ->
  % bait
  process_hand(S1 + S2, 0, ?tarabish_BaitType_FULL, State);

process_hand(#state{hscore={S1, S2}} = State) ->
  % normal
  process_hand(S1, S2, ?tarabish_BaitType_NONE, State).

process_hand(Score1, Score2, BaitType,
  #state{score=Score, caller=_Caller, dealer=Dealer, table=Table} = State) ->

  % TODO: score to 500
  HandScoreList = [Score1, Score2],
  ScoreList = tuple_to_list(Score),
  NewScoresList = lists:zipwith(fun(X, Y) -> X + Y end, HandScoreList, ScoreList),

  Event = #event{type=?tarabish_EventType_HAND_DONE,
                 hand_score=HandScoreList,
                 score=NewScoresList,
                 bait=BaitType},
  table:broadcast(Table, Event),

  State1 = new_hand(Dealer + 1, State#state{score=list_to_tuple(NewScoresList)}),
  {reply, ok, wait_trump, State1}.

process_trick(LastWin, #state{trick=9} = State) ->
  % TODO: add 20's and 50's
  State1 = add_hscore(LastWin, 10, State),

  process_hand(State1);

process_trick(LastWin, #state{trick=Trick} = State) ->
  Event = #event{type=?tarabish_EventType_ASK_CARD, seat=LastWin},
  table:broadcast(State#state.table, Event),

  {reply, ok, wait_card,
    new_trick(LastWin, State#state{trick = Trick + 1})}.

% First card of the trick
process_card(Seat, Card, Rest, #state{ledin=?tarabish_NONE} = State) ->
  process_card(Seat, Card, Rest, State#state{ledin=Card#card.suit});

process_card(Seat, Card, [], State) ->

  InPlay = lists:reverse([{Card, Seat}|State#state.inplay]),
{'fraction_non_alphanumeric': 0.23849680694285247, 'fraction_numerical': 0.014900933355166202, 'mean_word_length': 3.014130791981597, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3350913', 'n_tokens_mistral': 4243, 'n_tokens_neox': 4039, 'n_words': 825}