% File: genetic.m
% Author: Corey Hebert
% Purpose: use a genetic algorithm for discarding in the game of cribbage 
% 
% Note: modified version of discard_plot.m
%
% Inputs: 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 lost by the network will prompt changes in the values
%       of the cards which were chosen for discard (those with lowest value).
%
% 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 = genetic (epochs, cycles)

hand = zeros(52,1);
choice = zeros(6,1);

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


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

for c = 1:cycles

    win_inputs = [];
    win_outputs = [];
    scores = randn(6,1); % randomly intitialize weights    
    
    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

    for b = 1:epochs

        % use network to select player outputs
        player_choice = scores;
        % 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;

        new_scores = zeros(6,1);

        % compare scores, if the player didn't win, evolve
        if (player_score < random_score)
            for i = 1:6
                if(player_choice(i,1) == 0)
                    pm = rand;
                    if (rand > .5)
                        new_scores(i,1) = scores(i,1) + rand/log(b+1);
                    else
                        new_scores(i,1) = scores(i,1) - rand/log(b+1);
                    end
                else
                    new_scores(i,1) = scores(i,1);
                end
            end
        end

        % pick a winner out of the evolutions
        if (player_score < random_score)
            for n = 1:10
                pc = scores;
                % random outputs for random player
                rc = new_scores;

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

                % convert net output to "valid" discard options by dropping 
                % 2 lowest values to 0 and keeping 4 highest as 1
                rc_sort = sort(rc);
                for i = 1:6
                    if (rc_sort(1,1) == rc(i,1) || rc_sort(2,1) == rc(i,1))
                        rc(i,1) = 0;
                    else
                        rc(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) = pc(count,1);
                        kept(i,2) = rc(count,1);
                        count = count + 1;
                   end
                end
                % score hands
                ps = score(kept(:,1), cut_card, 0);
                rs = score(kept(:,2), cut_card, 0);

                pave = pave + ps;
                rave = rave + rs;

            end

            pave = pave/10;
            rave = rave/10;

            if (pave < rave)
                for h = 1:6
                    scores(h,1) = new_scores(h,1);
                end
            end

        end
    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

        % use network to select player outputs
        player_choice = scores;
        % 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
