Code covered by the BSD License  

Highlights from
NIM game

image thumbnail
from NIM game by Apoio
A GUI implementation of the NIM Game

nim(arg)
function nim(arg)
%                  NIM GAME
%            by Apoio Matlab 2012
%    www.wix.com/apoiomatlab/apoiomatlab
%  Comments welcome: apoiomatlab@gmail.com

% Copyright (c) 2012, Apoio Matlab - All rights reserved.
% 
% Redistribution and use in source and binary forms, with or without 
% modification, are permitted and encouraged, provided that source code 
% retains this copyright notice and credits.

persistent b d nmc np nc p c
persistent chipW chipH chipS pileS buttW buttH buttS xoffset yoffset
persistent levelPopup reverseCheck cheatButton textBox helpBox

if nargin < 1,
   arg = 'start';
end

switch arg
    case 'start'
        xoffset = 50; yoffset = 100;
        chipW =  40; chipH = 10; chipS =  1; pileS = 10;  
        buttW = 100; buttH = 40; buttS = 20;  
        % figure definitons
        f=figure('Name','NIM Game','NumberTitle','off',...
            'Units','pixels', 'MenuBar', 'none','Resize','off',...
            'Position', [300 100 650 450],'Color',[0.8 0.8 0.8]);
        % button definitions
        closeButton=uicontrol(...
            'Style','push','String','Close','call','close',...
            'Position',[500 yoffset buttW buttH], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);  
        newButton=uicontrol(...
            'Style','push', 'String','New Game','call','nim(''newgame'')',...
            'Position',[500 yoffset+buttH+buttS buttW buttH], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);
        cheatButton=uicontrol(...
            'Style','push', 'String','Cheat','call','nim(''cheat'')',...
            'Position',[500 yoffset+2*buttH+2*buttS buttW buttH], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);
        helpButton=uicontrol(...
            'Style','push','String','Help','call','nim(''help'')', ...
            'Position',[500 yoffset+3*buttH+3*buttS buttW buttH], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);
        reverseCheck=uicontrol(...
            'Style','check', 'String','Reverse','call','nim(''newgame'')',...
            'Position',[500 yoffset+4*buttH+4*buttS buttW buttH/2], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);
        levelPopup=uicontrol(...
            'Style','popup', 'call','nim(''newgame'')',...
            'String',{'Easy','Medium','Hard','Very Hard','Impossible'},...
            'Position',[500 yoffset+4.5*buttH+5*buttS buttW buttH/2], ...
            'SelectionHighlight','off',...
            'BackgroundColor',[0.8 0.8 0.8]);
        creditsBox=uicontrol('Style','text','String','Apoio MatLab 2012','Units','pixels', ...
            'Position',[500 yoffset-80 buttW 40], ...
            'HorizontalAlignment','center','FontSize',12,'FontWeight','demi',...
            'BackgroundColor',[0.8 0.8 0.8],'ForegroundColor',[0 0 0]);
        % text box definitions
        textBox=uicontrol(...
            'Style','text','FontSize',20,...
            'Position',[xoffset 20 8*chipW+9*pileS 40], ...
            'HorizontalAlignment','center',...
            'BackgroundColor',[0.8 0.8 0.8],...
            'ForegroundColor',[0 0 1]);
        clear nc; nim('newgame')
    case 'newgame'
        set(helpBox,'String','')
        set(textBox,'String','Player Turn')
        set(cheatButton,'call','nim(''cheat'')')
        % delete existing chips if they exist
        if ~isempty(nc)
            for pp=1:np
                for cc=1:nc(pp)
                    delete(b(pp,cc))
                end
            end
        end
        % create new game                
        np=8;                % number of piles
        switch get(levelPopup,'Value')
            case 1
                nmc=10;              % max of chips per pile
                nc=randi(nmc,1,np);  % number of chips per pile
                nc([1,2,7,8])=0;     % remove piles 1, 2, 7, 8
            case 2
                nmc=15;              % max of chips per pile
                nc=randi(nmc,1,np);  % number of chips per pile
                nc([1,8])=0;         % remove piles 1, 8
            case 3
                nmc=20;              % max of chips per pile
                nc=randi(nmc,1,np);  % number of chips per pile
            case 4
                nmc=25;              % max of chips per pile
                nc=randi(nmc,1,np);  % number of chips per pile
            case 5
                nmc=31;              % max of chips per pile
                nc=randi(nmc,1,np);  % number of chips per pile
                if nimsum(nc)~=0     % make sure it is impossible
                    nc(8)=nimsum(nc(1:7));
                end        
        end
        % chips and numbers
        for p=1:np
            if nc(p)==0
                str='';
            else
                str=num2str(nc(p));
            end
            d(p)=uicontrol(...
                'Style','text', ...
                'BackgroundColor',[0.8 0.8 0.8],...
                'Position',[xoffset+(p-1)*(chipW+pileS)+pileS yoffset-30+chipS chipW 20], ...
                'String',str);    
            for c=1:nc(p)
                str='';
                if mod(c,5)==0
                    str='~';
                end
                b(p,c)=uicontrol( ...
                    'Style','push','String',str,'call','nim(''play'')',...
                    'Position',[xoffset+(p-1)*(chipW+pileS)+pileS yoffset+(c-1)*(chipH+chipS)+chipS chipW chipH], ...
                    'UserData',[p,c],'TooltipString',num2str(c), ...
                    'BackgroundColor','b','ForegroundColor','c',...
                    'SelectionHighlight','off');    
            end
        end   
    case 'remove'
        % disable callbacks
        set(cheatButton,'call','')
        for pp=1:np
            for cc=1:nc(pp)
                set(b(pp,cc),'call','')
            end
        end
        % highlight chips to remove
        for cc=nc(p)-c+1:nc(p)
            set(b(p,cc),'BackgroundColor','c')
        end
        pause(0.5)
        % delete chips
        for cc=nc(p)-c+1:nc(p)
            delete(b(p,cc))
            pause(0.05)
        end
        % remove chips
        nc(p)=nc(p)-c;
        % enable remaining chips
        for pp=1:np
            for cc=1:nc(pp)
                set(b(pp,cc),'call','nim(''play'')')
            end
        end
        set(cheatButton,'call','nim(''cheat'')')
        nim('display')
    case 'play'
        ud = get(gco,'UserData');p=ud(1);c=nc(p)-ud(2)+1;
        nim('continue')  
    case 'cheat'
        [p,c]=bestmove(nc,get(reverseCheck,'Value'));
        nim('continue')
    case 'continue'
        nim('remove') 
        if sum(nc)==0
            set(cheatButton,'call','')
            if get(reverseCheck,'value')
                set(textBox,'String','Computer Wins!')
            else
                set(textBox,'String','Player Wins!')
            end 
        else
            set(textBox,'String','Computer Turn')
            nim('computer') % finds computer move p and c
            nim('remove')   % removes c chips from pile p
            if sum(nc)==0
                set(cheatButton,'call','')
                if get(reverseCheck,'value')
                    set(textBox,'String','Player Wins!')
                else
                    set(textBox,'String','Computer Wins!')
                end 
            else
                set(textBox,'String','Player Turn')
            end
        end          
    case 'computer'
        l=get(levelPopup,'Value'); get(reverseCheck,'Value');
        if (l==1 &&  sum(nc)>10) || ( l==2 &&  sum(nc)>20) || ( l==3 &&  sum(nc)>50) 
            [p,c]=randommove(nc);
        else
            [p,c]=bestmove(nc,get(reverseCheck,'Value'));
        end
    case 'display'
        for p=1:np
            if nc(p)==0
                set(d(p),'string','')
            else
                set(d(p),'string',num2str(nc(p)))
            end
        end
    case 'help'
        set(textBox,'String','')
        NL = sprintf('\n');
        s1='NIM Game rules';
        s2='Player and Computer take turns removing chips.';
        s3='Each can take as many chips as wanted,';
        s4='from one pile only.';
        s5='Wins the player who takes the last chip on the board.';
        s6='Playing reverse, last chip taker looses.';
        s7='Clicking one chip removes it and the ones above it.';
        s8='The cheat button makes the best move for you.';
        s9='Credits to ApoioMatlab - 2012';
        s10=sprintf('http://www.wix.com/apoiomatlab/apoiomatlab');
        s11='Comments and suggestions welcome to:';
        s12='apoiomatlab@gmail.com';
        s13='Free redistribution keeping credits.';
        str = [s1,NL,NL,s2,NL,s3,NL,s4,NL,s5,NL,s6,NL,NL,s7,NL,s8,NL,NL,s9,NL,s10,NL,s11,NL,s12,NL,NL,s13];
        helpBox=uicontrol('Style','text','String',str,'Units','pixels', ...
            'Position',[xoffset yoffset-30 8*chipW+9*pileS 30*(chipH+chipS)], ...
            'HorizontalAlignment','center','FontSize',12,'FontWeight','demi',...
            'BackgroundColor',[0.8 0.8 0.8],'ForegroundColor',[0 0 1]);
end % switch

function [p,c]=bestmove(nc,r)
if winingposition(nc,r)
    np=length(nc); [aux1,aux2]=sort(nc);
    if r==0 || aux1(np-1)>1
        n=nimsum(nc);
        pp=find(nimsum(nc,n)<=nc); 
        p=randi(length(pp));      
        p=pp(p);                  
        c=nc(p)-nimsum(n,nc(p));
    else
        if aux1(np)==1
            [p,c]=randommove(nc);
        else
            p=aux2(np);
            if mod(sum(aux1(1:np-1)),2)==0
                c=nc(p)-1;
            else
                c=nc(p);
            end
        end   
    end    
else
    [p,c]=randommove(nc);
end
       
function [p,c]=randommove(nc)
pp=find(nc~=0);   
p=randi(length(pp)); 
p=pp(p);             
c=randi(nc(p));

function w=winingposition(nc,r)
[aux1,aux2]=sort(nc);
np=length(nc); w=0;
if (r==0 && nimsum(nc)~=0)  || (r==1 && ...
        ( (aux1(np-1)>1  && nimsum(nc)~=0       ) || ...
          (aux1(np-1)==1 && aux1(np)>1          ) || ...
          (aux1(np)==1   && mod(sum(aux1),2)==0 ) || aux1(np-1)==0    ) )
    w=1;
end

function y=nimsum(varargin);
if nargin==1
    y=0;
    for i=1:length(varargin{1})
        y=nimsum(y,varargin{1}(i));
    end 
else
    y=bitxor(uint8(varargin{1}), uint8(varargin{2}));    
end

Contact us