function ZYahtzee(np)
% Play text-based Yahtzee!
%
% ZYAHTZEE
% ZYAHTZEE(n), where n is the number of players.
%
% All standard Yahtzee rules apply.
%
% On your turn, you may roll the dice up to 3 times, although you may stop
% and score after your first or second roll. The 1st roll will be done
% automatically and displayed next to the "your roll" field. The 1st roll
% of each turn is of 5 6-sided dice. For your 2nd or 3rd rolls you may keep
% any or all of the dice of your previous roll. Select which rolls to keep
% by entering their die number(s) in the prompt. (You are now no longer
% required to enter spaces when deciding which dice to hold, i.e.,
% entering 125 will hold dice numbers 1, 2, and 5.)
%
% In addition to entering numbers in the prompt, you may enter "all" to
% hold all dice, enter to reroll all dice or "quit" to exit the game.
%
% Once you have held all dice or taken 3 rolls on this turn, you select the
% category to apply the roll to, and earn points accordingly. Your score
% sheet will be updated according to standard Yahtzee rules. You will be
% allowed 13 total turns per game.
%
% Good luck, and good dice.
%
% For more information see the official rules at:
% http://www.hasbro.com/common/instruct/Yahtzee.pdf
%
% Technical Note: You may wish to randomize the seed of MATLAB's random
% number generators prior to playing to avoid games that are the same, or
% even insert a randomization line at the beginning of this code.
%
%
% %%% ZCD June 2010 %%%
%

% Edits (ZCD) July 2010:
%   * fixed spelling error in game board
%   * do not accept bad die number inputs (instead of ignoring them)
%   * added statistics "easter egg"
%   * added multi-player versus capability
%   * automatically score final roll
% Edits (ZCD) August 2010:
%   * fixed improper Yahtzee points allocations in versus mode
%   * adjusted figure location on stats display
% Edits (ZCD) September 2010:
%   * added features to stats bonus display
%   * will save scores to appropriate directory (even when not in that dir)
% Edits (ZCD) February 2011:
%   * there is now no need to include spaces when entering held die numbers


% check inputs
if nargin == 0
    np=1;
elseif ~(np>0 && real(np) && numel(np)==1)
    error('Invalid input for number of players.')
end
% display variable for multiple players
pz = [np 1:np-1];

% set up initial points array
points = ones(13,np)*NaN;
% roll variable
roll = zeros(1,5);

% main game loop runs 13 rounds, with 3 throws per round
for r = 1:13
    % run one round loop per player
    for p = 1:np
    % when playing with multiple players, briefly show the previous
    % player's updated board
    if np>1 && ~(p==1 && r==1)
        clc
        dispPoints(points(:,pz(p)),zeros(1,5),0,pz(p));
        pause(1.5)
    end
        
    % initialize our rerolls variable
    rerolls = 1:5;
    % set up for a bonus yatzee
    bflag = 0; strbonus = '';
    
    for t = 1:3
        % throw the dice
        roll(rerolls) = randi(6,[1 length(rerolls)]);
        % show the user the game state
        dispPoints(points(:,p),roll,t,p);
        
        % --- do special yahtzee things ---
        % check for yahtzee
        fq = hist(roll,1:6);
        if any(fq==5)
            if isnan(points(13,p))
                points(13,p) = 50;
                dispPoints(points(:,p),roll,t,p);
                % be excited about the yahtzee
                disp(' ');disp('YAHTZEE!');disp(' ');pause(3);
                break;
            elseif points(13,p)>0
                points(13,p) = points(13,p)+100;
                dispPoints(points(:,p),roll,t,p);
                disp(' ');
                disp(['BONUS YAHTZEE' repmat('!!',[1 (points(13,p)-50)/100])]);
                disp(' ');pause(3);
                bflag = 1;
                strbonus = 'BONUS ';
                action = 'all';
            elseif points(13,p)==0
                bflag = 1;
                action = 'all';
                disp(' ')
                disp('Seems like you shouldn''t have taken a zero for Yahtzees.')
                disp(' ');pause(3);           
            end
        end        
        % there are special restrictions when the joker scoring
        % for bonus yahtzees are in effect
        if bflag
            % you must fill the top if its available
            if isnan(points(roll(1)))
                points(roll(1),p) = scoreLogic(roll,roll(1));
                break;
            end
        end
        % --- end special yahtzee things ---
        
        
        % obtain the user action
        if t<3 && ~bflag
            keepflag = 1;
            while keepflag
                action = input('Which die numbers do you keep?   ','s');
                % separate each character by a space
                sAction = blanks(2*length(action)); % long row of blanks
                sAction(1:2:end) = action;          % interleave with choies
                % check for bad inputs
                [actstr cf] = str2num(sAction);
                if ( isempty(setdiff(actstr,1:5)) && cf ) || any(strcmp(action,{'','all','quit'}))
                    % accept this input if it is a (legit die # AND we can
                    % do a succesful number conversion on it) OR its a
                    % special word ('' all or quit).
                    keepflag = 0;
                else
                    disp('invalid die number')
                end
            end
        end
        % take user action
        switch action
            case 'all'
                rerolls = [];
            case 'quit'
                return;
            otherwise
                % obtain which die indicies we are to reroll
                % rerolls = setxor(str2num(action),1:5);
                rerolls = setxor(str2num(sAction),1:5);
        end
        
        % get the score placement
        if t==3 || isempty(rerolls) || bflag
            catflag = 1;        % viable category choice
            % make sure this is an appropriate category
            while catflag
                % if this is the final turn, we know the category
                if sum(isnan(points(:,p))) == 1
                    catflag = 0;
                    catg = find(isnan(points(:,p)));
                    points(catg,p) = scoreLogic(roll,catg,bflag);
                    pause(1.5);
                else
                    % ask user for category
                    response = input(['Mark ' strbonus 'score in which category?   '],'s');
                    % the only valid string here is 'quit' which exits the
                    % game now
                    if strcmpi(response,'quit')
                        return;
                    else
                        catg = str2double(response);
                    end
                    % if we are in the right category range and do not yet have
                    % a score for that category we allow the assignment
                    if catg>0 && catg<14 && isnan(points(catg,p))
                        catflag = 0;
                        % get the category score
                        points(catg,p) = scoreLogic(roll,catg,bflag);
                    else
                        disp('invalid category')
                    end
                end
            end
            break;
        end
        
    end % throw loop
    end % player loop
end     % round loop


% --- End game operations ---
clc
gtotal = zeros(1,np);
for p=1:np
    % give final accounting to the player
    gtotal(p) = dispPoints(points(:,p),zeros(1,5),0,p);
end
for p=1:np
    % run score records
    highScores(gtotal(p),points(13,p),[np p])
end
if np>1
    % interleave player no and score
    c = [1:np;gtotal];
    s = sprintf('Player%d: %d,  ',c(:));
    disp(s(1:end-3)); disp(' ')
    disp(['PLAYER ' num2str(find(gtotal==max(gtotal))) ' WINS' repmat('!',[1 np])])
    disp(' ')   
end
% ---------------------------






    function score = scoreLogic(roll,cat,joker)
        score = 0;
        % how many points is this roll worth to this category?
        switch cat
            case {1 2 3 4 5 6}
                score = sum(roll==cat)*cat;
            case 7
                fq = hist(roll,1:6);
                if any(fq>2) || joker, score = sum(roll); end
            case 8
                fq = hist(roll,1:6);
                if any(fq>3) || joker, score = sum(roll); end
            case 9
                fq = hist(roll,1:6);
                if all(diff(sort(fq))==[0 0 0 2 1]) || joker
                    score = 25;
                end
            case 10
                if ~isempty( strfind(diff(unique(roll)),[1 1 1]) ) || joker
                    score = 30;
                end
            case 11
                if ~isempty( strfind(diff(unique(roll)),[1 1 1 1]) ) || joker
                    score = 40;
                end
            case 12
                score = sum(roll);
            case 13
                % case dealt with earlier in code
        end 
    end

    function gtotal = dispPoints(points,roll,t,p)
    % display the current game state
        % clear the command window
        if t~=0 clc; end
        
        % displays current points to the user
        slots = {'1: ones';'2: twos';'3: threes';'4: fours';'5: fives';...
            '6: sixes';'-->bonus (35/63)';'-->Top Subtotal';'7: set';'8: quads';...
            '9: boat';'10: 4-straight';'11: 5-straight';'12: Chance';...
            '13: YAHTZEE';'-->Bottom Subtotal';'-->Grand Total'};
        % top subtotal
        top = nansum(points(1:6));
        % do we have a bonus?
        if top<63, b=0; else b=35; end
        % bottom subtotal
        bot = nansum(points(7:13));
        
        gtotal = top+b+bot;
        
        % concatenate all our points information according to the
        % description in the slots variable
        disp(' ')
        disp(['PLAYER: ' num2str(p)])
        disp([slots,num2cell( [points(1:6);b;top+b;points(7:13);bot;gtotal] )])
        disp(' ')
        if t~=0
            disp(['Roll  ' num2str(t) '/3'])
            fprintf('Your Roll:    %d   %d   %d   %d   %d\n',roll)
            fprintf('Die Number:  (1) (2) (3) (4) (5)\n')
        end
    end



    function highScores(total,yah,np)
        dirFiles = dir(strrep(mfilename('fullpath'),mfilename,''));
        if ~any(strcmp({dirFiles.name},'YahtzeeRecords.mat'))
            scores = {'Player' 'Score' 'Date' 'Yahtzees' 'Rank'};
        else
            load('YahtzeeRecords')
        end
        
        name = inputdlg('Enter Your Name For Record Keeping',['Player ' num2str(np(2))]);
        if isempty(name), return; end
        name = name{1};
        if length(name)>15
            name = name(1:15);
        end
        
        if yah~=0, yah = mod(yah,99)-49; end
        
        scores(end+1,:) = {name total datestr(now,1) yah ''};
        
        sc = scores(2:end,2);
        [B IX] = sort([sc{:}]','descend');
        
        scores(2:end,:) = scores(IX+1,:);
        scores(2:end,end) = num2cell(1:size(scores,1)-1);
        save([strrep(mfilename('fullpath'),mfilename,'') 'YahtzeeRecords'],'scores');
        
        if size(scores,1)>10
            dispLengthTop = 10;
        else
            dispLengthTop = size(scores,1);
        end
        curRank = find(B==total,1,'last');
        if curRank>=dispLengthTop
            dispLengthNow = [curRank-3:curRank+3];
            if ~isempty(find(dispLengthNow==size(scores,1)))
                dispLengthNow = dispLengthNow(1:find(dispLengthNow==size(scores,1)));
            end
        else
            dispLengthNow = [];
        end
        
        disp(' ');disp(' ');
        disp(scores(1:dispLengthTop,:));
        if ~isempty(dispLengthNow)
            disp('     ...............................');
            disp(scores([1 dispLengthNow],:));
        end
        disp(' ');
        
        % occasionally do some interesting stats
        if rand>0.85 && size(scores,1)>5 && np(1)==1
            % print stats to screen
            disp(' '); disp('Your Statistics:'); disp(' ')
            tg = size(scores,1)-1;
            fprintf('Total games played: %g, Average games per day: %g\n\n',...
                [tg, tg/(etime(datevec(max(datenum(scores(2:end,3)))),...
                datevec(min(datenum(scores(2:end,3)))))/(60*60*24))]);
            disp('Correlation between scores and Yahtzees')
            rho = corrcoef([scores{2:end,2}],[scores{2:end,4}]);
            disp(rho(2))
            disp('Correlation between scores and the date')
            rho = corrcoef([scores{2:end,2}],datenum(scores(2:end,3)));
            disp(rho(2))
            disp('Correlation between scores and rank')
            rho = corrcoef([scores{2:end,2}],[scores{2:end,end}]);
            disp(rho(2))
            disp(' ')
            disp(['Mean of scores: ' num2str(mean([scores{2:end,2}]))])
            disp(['Standard deviation of scores: ' num2str(std([scores{2:end,2}]))])
            disp(' ')
            ny = repmat(unique([scores{2:end,4}]),[2 1]);
            eval(sprintf('disp([''Mean for %d Yahtzees: '' num2str( mean( [scores{[false %d==[scores{2:end,4}]],2}] ) )]), ',ny(:)'))
            disp(' ')
            eval(sprintf('disp( [''Best score for %d Yahtzees: '' num2str(max([scores{[false scores{2:end,4}]==%d,2}]))] ), ',ny(:)'))
            disp(' ')
            disp(['Average Yahtzee''s per game: ' num2str(sum([scores{2:end,4}])/scores{end,end})]);            
            disp(' ')            
            
            % print stats to figure
            hh = figure;
            subplot(3,1,1)
            hist([scores{2:end,2}])
            xlabel('Scores'); ylabel('Frequency')
            subplot(3,1,2)
            d = datenum(scores(2:end,3));
            du = unique(d);
            x = zeros(1,length(du)); xs=x;
            for i=1:length(du)
                x(i) = mean([scores{[false; d==du(i)],2}]);
                xs(i) = std([scores{[false; d==du(i)],2}]);
            end
            if any(xs)
                [bds ibds] = max(x(xs~=0)); bdd = du(xs~=0);
                fprintf(' Best day (with more than 1 game played), %s, has mean score of %g\n',datestr(bdd(ibds)),bds)
                [bds ibds] = min(x(xs~=0)); bdd = du(xs~=0);
                fprintf('Worst day (with more than 1 game played), %s, has mean score of %g\n',datestr(bdd(ibds)),bds)
            end
            ln = pinv([ones(length(x),1) (1:length(x))'])*x';
            plot(x,'-.sb','linewidth',2), hold on
            line(1:length(x),ln(1)+(1:length(x)).*ln(2),'color','r')
            set(gca,'Xtick',[])
            xlabel('Increasing Date (days) \rightarrow'); ylabel('Mean Score')
            legend('day score','linear fit','location','best')
            subplot(3,1,3)
            plist = unique(scores(2:end,1));
            x = zeros(1,length(plist));
            for i=1:length(plist)
                tmp = [scores{boolean([0; strcmpi(plist{i},scores(2:end,1))]),2}];
                x(i) = mean(tmp);
                plist{i} = [plist{i} ': ' num2str(length(tmp))];
            end
            [xb IX] = sort(x(1,:),'descend');
            if length(plist)<=6, lp=length(plist); else lp = 6; end
            bar(xb(1:lp),'r'); xlim([0.5 lp+0.5])
            set(gca,{'xtick','fontsize','xticklabel'},{1:lp,8,plist(IX(1:lp))})
            ylabel('Mean Score'); xlabel('Player: Games Played')
            disp(' ');
        end
    end

end
Sable
github-repos/MATLAB/Sable-mcbench-benchmarks/mcbench-benchmarks-ba13b2f0296ef49491b95e3f984c7c41fccdb6d8/27891-text-yahtzee/ZYahtzee.m
mcbench-benchmarks
github-repos/MATLAB/Sable-mcbench-benchmarks
ba13b2f0296ef49491b95e3f984c7c41fccdb6d8