% File: discard_plot.m
% Author: Corey Hebert
% Purpose: develop a neural network for discarding cards in the game of 
%          cribbage; allow average scores to be return for evaluation purposes
% 
% Inputs: style   (the type of network to create)
%         Si      (size of the ith layer, dimension indicates the number
%                  of layers)
%         TFi     (transfer function for the ith layer)
%         BTF     (Backpropigation network training function)
%         BLF     (Backpropigation weight/bias learning function)
%         epochs  (how many epochs to use training the network)
%         cycles  (how many batches to play)
%
% Spec: network will be trained via self play, in batches of 10. That is,
%       10 hands will be played between the network player and a random
%       player. Hands won by the network will be passed to the network
%       as training data (52 bit hand input, 6 bit discard target).
%
% Uses: score.m   (scores a hand, according to the rules of cribbage, 
%                  given a cut card)
%       cut.m     (generates a cut card, given the cards already dealt)


function averages = discard_plot (style, Si, TFi, BTF, BLF, epochs, cycles)

% create a basic network, seed with 10 random inputs
initial_input = [];
initial_output = [];
hand = zeros(52,1);
choice = zeros(6,1);

% how frequently to save averages
if(cycles < 1000)
    freq = 1;
else
    freq = cycles/1000;
end

% mins and maxes for inputs (each should be 0 or 1)
minmax = [zeros(52,1), ones(52,1)];

for i = 1:1000
    % crossvalind('Holdout', x, y) will select x-floor(x*y) items by
    % "holding out" floor(x*y) elements (we're just catching "Train")
    select = crossvalind('Holdout', 52, .89);     % 6 card hand
    for x = 1:52  % crossvalind outputs logicals, convert to ints
        if(select(x,1))
            hand(x,1) = 1;
        else
            hand(x,1) = 0;
        end    
    end

    initial_input = [initial_input, hand];      % append to input matrix
    select = crossvalind('Holdout', 6, .34);    % 4 cards to keep
    for x = 1:6    
        if(select(x,1))
            choice(x,1) = 1;
        else
            choice(x,1) = 0;
        end
    end

    initial_output = [initial_output, choice];  % append to output matrix
end

switch style
    case 'newff'
        % note: online documention differs from built in help
        %      (7.5.0.338 (R2007b) version used)
        net = newff(initial_input, initial_output, Si, TFi, BTF, BLF, 'mse');
    case 'trial'
        fprintf('"trial" version used!\n');
        net = newff(minmax, [75 6], {'tansig' 'purelin'});
    case 'newcf'
        net = newcf(initial_input, initial_output, Si, TFi, BTF, BLF, 'mse');
    case 'newpnn'  % may has training issues
        net = newpnn(initial_input, initial_output);
    case 'newrb'   % may have training issues
        net = newrb(initial_input, initial_output);
    otherwise
        fprintf('Invalid network supplied');
end

net.trainParam.epochs = epochs;
net = train(net, initial_input, initial_output);


% begin training cycles
averages = [];
p_ave = 0;
r_ave = 0;

for c = 1:cycles

    win_inputs = [];
    win_outputs = [];
    
    for b = 1:10  % batch loop

        hand = zeros(52,1);
        
        % create a 6 card hand (both players will play this hand)
        select = crossvalind('Holdout', 52, .89);
        for x = 1:52
            if(select(x,1))
                hand(x,1) = 1;
            else
                hand(x,1) = 0;
            end
        end

        % use network to select player outputs
        player_choice = sim(net, hand);
        % random outputs for random player
        random_choice = zeros(6,1);
        select2 = crossvalind('Holdout', 6, .34);
        for x = 1:6
            if (select2(x,1))
                random_choice(x,1) = 1;
            else
                random_choice(x,1) = 0;
            end
        end        

        % convert net output to "valid" discard options by dropping 2 
        % lowest values to 0 and keeping 4 highest as 1
        pc_sort = sort(player_choice);
        for i = 1:6
            if (pc_sort(1,1) == player_choice(i,1) || pc_sort(2,1) == player_choice(i,1))
                player_choice(i,1) = 0;
            else
                player_choice(i,1) = 1;
            end
        end

        % get cut card
        cut_card = cut(hand);
        
        % determine which cards were kept
        count = 1;
        kept = zeros(52,2);
        for i = 1: 52
            if (hand(i,1) == 1)
                 kept(i,1) = player_choice(count,1);
                 kept(i,2) = random_choice(count,1);
                 count = count + 1;
            end
        end

        % score hands
        player_score = score(kept(:,1), cut_card, 0);
        random_score = score(kept(:,2), cut_card, 0);
        
        p_ave = p_ave + player_score;
        r_ave = r_ave + random_score;

        % compare scores, keep move if network player won this hand
        if (player_score > random_score)
            win_inputs = [win_inputs, hand];
            win_outputs = [win_outputs, player_choice];
        end
    end
    
    % train network based on wins (if the network won any)
    if (size(win_inputs) ~= 0)
        net.trainParam.epochs = epochs;
        net = train(net, win_inputs, win_outputs);
    end

    % score evaluator, columns are averages based on set of trials:
    %               network player score, random player score, difference
    p_ave = 0;
    r_ave = 0;
    parameter = 1000;

    % save only 1 out of every freq because of limits in matlab's matrix size
    % at most, this will be 1000 values
    if (mod(c, freq) == 0)
        for p = 1:parameter

            % create a 6 card hand (both players will play this hand)
            select = crossvalind('Holdout', 52, .89);
            for x = 1:52
                if(select(x,1))
                    hand(x,1) = 1;
                else
                    hand(x,1) = 0;
                end
            end

            % use network to select player outputs
            player_choice = sim(net, hand);
            % random outputs for random player
            random_choice = zeros(6,1);
            select2 = crossvalind('Holdout', 6, .34);
            for x = 1:6
                if (select2(x,1))
                    random_choice(x,1) = 1;
                else
                    random_choice(x,1) = 0;
                end
            end

            % convert net output to "valid" discard options by dropping 2 
            % lowest values to 0 and keeping 4 highest as 1
            pc_sort = sort(player_choice);
            for i = 1:6
                if (pc_sort(1,1) == player_choice(i,1) || pc_sort(2,1) == player_choice(i,1))
                    player_choice(i,1) = 0;
                else
                    player_choice(i,1) = 1;
                end
            end

            % get cut card
            cut_card = cut(hand);

            % determine which cards were kept
            count = 1;
            kept = zeros(52,2);
            for i = 1: 52
            if (hand(i,1) == 1)
                kept(i,1) = player_choice(count,1);
                kept(i,2) = random_choice(count,1);
                count = count + 1;
            end
        end

        % score hands
        player_score = score(kept(:,1), cut_card, 0);
        random_score = score(kept(:,2), cut_card, 0);

        p_ave = p_ave + player_score;
        r_ave = r_ave + random_score;

    end

    p_ave = p_ave/1000;
    r_ave = r_ave/1000;

    temp = [p_ave, r_ave, p_ave-r_ave];

    averages = [averages; temp];
end
    
end
