Code covered by the BSD License  

Highlights from
MATLAB Contest - Peg Solitaire

image thumbnail
from MATLAB Contest - Peg Solitaire by The MATLAB Contest Team
All the files needed to develop and score an entry for the MATLABĀ® Programming Contest.

solitaireGUIb(board,moves,delay)
function solitaireGUIb(board,moves,delay)
% SOLITAIREGUIB Play Peg Solitaire and analyze a list of moves.
% Note: Backwards compability version of SOLITAIREGUI (should run in 7.01
% R14 sp1).
%
% Use left-mouse button to drag and jump pegs.
%
% SOLITAIREGUI('English')   plays the English Peg Solitaire.
% SOLITAIREGUI('European')  plays the European (aka French) Peg Solitaire.
% SOLITAIREGUI(BOARD)       sets the GUI with a predifined board.
% SOLITAIREGUI([R,C,V])     sets the GUI with a random board with R rows, C
%                           columns, and peg weights between 1 and V.
% SOLITAIREGUI(BOARD,MOVES) passes to the GUI a four column matrix with a
%                           list of moves to analyze. 
% SOLITAIREGUI('English','EngOpt') analyze the optimal solution of the
%                           English Peg Solitaire.
% SOLITAIREGUI('English','EngNon') analyze the non-optimal solution of the
%                           English Peg Solitaire.
% SOLITAIREGUI(BOARD,MOVES,DELAY) shows a movie for a given solution with a
%                           pause of DELAY seconds between moves. If DELAY
%                           is too small some frames may be skipped,
%                           setting DELAY to zero assures that every frame
%                           is rendered.

% Copyright 2007 The MathWorks, Inc.

if nargin == 0 || ischar(board)
   if nargin == 0 || strcmpi(board,'english')
       board = (magic(7)~=25)-[1;1;0;0;0;1;1]*[2 2 0 0 0 2 2]; 
   else
       board = (magic(7)~=25)-2*(([1 2 3 4 3 2 1]'*[1 2 3 4 3 2 1])<=2);
   end
elseif isstruct(board)
   board = board.board; 
elseif numel(board)==3 
   m = board(1); n = board(2);
   board = floor((board(3)+2)*rand(m,n)-1);
   if all(board(:)~=0), board(ceil(n*m*rand))=0; end
end
if (nargin>1 && ischar(moves)) || nargin<2
    if nargin>1 && strcmpi(moves,'engopt')
      moves = [2 4 4 4;3 6 3 4;1 5 3 5;4 5 2 5;6 5 4 5;5 7 5 5;5 4 5 6
               3 7 5 7;5 7 5 5;3 3 3 5;3 1 3 3;5 2 5 4;5 4 5 6;5 6 3 6
               3 6 3 4;3 4 3 2;7 3 5 3;4 3 6 3;7 5 7 3;7 3 5 3;5 1 3 1
               3 1 3 3;1 3 1 5;1 5 3 5;3 5 5 5;2 3 4 3;4 3 6 3;6 3 6 5
               6 5 4 5;4 5 4 3;4 3 4 1];
    elseif nargin>1 && strcmpi(moves,'engnon')
      moves = [2 4 4 4;3 6 3 4;4 4 2 4;3 2 3 4;5 3 3 3;3 4 3 2;1 3 3 3
               1 5 1 3;2 5 2 3;3 2 3 4;1 3 3 3;3 4 3 2;5 1 5 3;3 1 5 1
               3 2 5 2;6 3 4 3;5 1 5 3;4 3 6 3;7 3 5 3;6 5 6 3;7 5 7 3
               5 4 5 2;7 3 5 3;5 2 5 4;4 5 6 5;5 7 5 5;3 7 5 7;5 4 5 6
               5 7 5 5;6 5 4 5;4 6 4 4];
    else
      moves = zeros(0,4);
    end
end
[m,n] = size(board);
tb = @(p) all([p>0;p<=[n;m];~rem(p,1)]);
if size(moves,2)~=4 || ~isnumeric(moves) || ~isreal(moves)
    error('MOVES must be a numeric and real matrix with 4 columns')
end
moves = round(moves(1:min(nnz(board>0),end),:));
sm=[]; rp=[]; pt = sum(board(board(:)>0));
nf = round(min(9,max(3,1500/nnz(board(:)>=1))));
[x,y,z] = ellipsoid(0,0,0,.40,.40,.40,nf*2);
x = x(nf+1:end,:);z = z(nf+1:end,:);y = y(nf+1:end,:);
S = struct('A',board,'PP',zeros(m,n,'uint16'),'collected',0,'lifted',0,...
           'score',pt,'listCtr',0,'lastPeg',[NaN;NaN]);
S.PP(S.A>0) = 1:nnz(S.A>0);
[hf,ha,sh,ht,hl] = drawGUI; printStats
if nargin<3 % interacting with the GUI by dragging pegs
    set(hf,'WindowButtonDownFcn',@myClick,'WindowButtonUpFcn',@myRelease);
else        % automatically step through the moves
    ctr = 1; t0 = clock;
    while S.listCtr < size(moves,1);
        if etime(clock,t0)<delay*ctr, pause(delay*ctr-etime(clock,t0)); end
        if delay<eps, pause(eps); end
        myDo; 
        ctr = ctr+1;
    end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function validPick = doMove(f,t)
  validPick = tb(f) && S.A(f(2),f(1))>0;
  S.undo = S;
  if validPick
    if ~isequal(S.lastPeg,f)
      S.lifted = S.lifted + S.A(f(2),f(1));
      S.score = S.score + S.A(f(2),f(1));
    end
    S.lastPeg = f;
    mp = (f+t)/2;
    validMove = tb(t) && S.A(t(2),t(1))==0 && all(sort(abs(f-t))==[0;2]);
    if validMove && S.A(mp(2),mp(1))>0
      set(sh(S.PP(f(2),f(1))),'XData',x+t(1),'YData',y+t(2));
      set(sh(S.PP(mp(2),mp(1))),'Visible','off');
      S.lastPeg = t;
      S.collected = S.collected + S.A(mp(2),mp(1));
      S.score = S.score - S.A(mp(2),mp(1));
      S.A(t(2),t(1)) = S.A(f(2),f(1));
      S.A([f(2) mp(2)],[f(1) mp(1)]) = 0;
      S.PP(t(2),t(1)) = S.PP(f(2),f(1));
      S.PP([f(2) mp(2)],[f(1) mp(1)]) = 0;    
    end
    printStats
  else
    S.lastPeg = [0;0];  
  end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function printStats
  set(ht(1),'string',sprintf('Collected: %0.5g of %0.5g',S.collected,pt))
  set(ht(2),'string',sprintf('Lifted: %0.5g',S.lifted))
  set(ht(3),'string',sprintf('Score: %0.5g',S.score))
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function myRelease(h,e) 
  if ~strcmp(get(hf,'SelectionT'),'normal') || isempty(sm), return; end
  set(hf,'WindowButtonMotionFcn',{});
  cp = (get(ha,'CurrentPoint').*[1 0 0;0 1 0])*[1 1 0]'-rp;
  if diff(abs(cp))>0 
    cp = min(max(rp + [0;2*sign(cp(2))],[1;1]),[n;m]); % up/down dir
  elseif diff(abs(cp))<0
    cp = min(max(rp + [2*sign(cp(1));0],[1;1]),[n;m]); % left/rigth dir
  else
    cp = rp; % can't find direction, make it an invalid move
  end
  set(sh(sm),'XData',x+rp(1),'YData',y+rp(2));
  if doMove(rp,cp)
    moves = [moves(1:S.listCtr,:);[rp(2) rp(1) cp(2) cp(1)]];
    S.listCtr = S.listCtr + 1;
    set(hl,'string',[blanks(size(num2str(moves),2));num2str(moves)],...
           'Value',S.listCtr+1,'ListboxTop',max(1,S.listCtr-12))
  end
  sm = [];
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function myClick(h,e) 
  cp = round(get(ha,'CurrentPoint').*[1 0 0;0 1 0])*[1 1 0]';
  if strcmp(get(hf,'SelectionT'),'normal') && tb(cp) && S.A(cp(2),cp(1))>0
    rp = cp;
    sm = S.PP(cp(2),cp(1));
    set(hf,'WindowButtonMotionFcn',@myMotion);
  end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function myMotion(h,e) 
  cpf = min(max(get(ha,'CurrentPoint').*[1 0 0;0 1 0]*[1 1 0]',1),[n;m]);
  set(sh(sm),'XData',x+cpf(1),'YData',y+cpf(2))
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function myUndo(h,e) 
  if isfield(S,'undo')
    S = S.undo;
    set(hl,'Value',S.listCtr+1,'ListboxTop',max(1,S.listCtr-12))
    printStats
    set(sh,'Vis','off')
    [i,j] = find(S.PP);
%     arrayfun(@(h,i,j) set(h,'Vis','on','XDa',x+j,'Yda',y+i),...
%              sh(S.PP(S.PP>0)),i,j)   
    u = sh(S.PP(S.PP>0));
    for k = 1:numel(i)
        set(u(k),'Vis','on','XDa',x+j(k),'Yda',y+i(k))
    end
  end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function myDo(h,e)
  if S.listCtr<size(moves,1) 
    doMove(moves(S.listCtr+1,[2 1])',moves(S.listCtr+1,[4 3])');
    S.listCtr = S.listCtr + 1;
    set(hl,'Value',S.listCtr+1,'ListboxTop',max(1,S.listCtr-12))
  end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [hf,ha,sh,ht,hl] = drawGUI
  hf = findall(0,'Tag','SolitaireGUI');
  if isempty(hf)
      hf = figure('MenuBar','none','Name','The Solitaire MATLAB Contest',...
                  'NumberT','off','color','w','Rend','zbuf',...
                  'BackingS','on','Tag','SolitaireGUI');
  else
      figure(hf); clf
  end
  ha = axes('Pos',[.18 .12 .80 .85],'Nex','add','box','off','yd','rev',...
            'DataAspectRatio',[1 1 1]);
  text((1:m)*0-.2,1:m,num2str((1:m)'),'hor','right','fontS',8)
  text(1:n,(1:n)*0+m+1.2,num2str((1:n)'),'hor','cen','ver','top','fontS',8)
%   ht = cellfun(@(f,p) uicontrol('Sty','Tex','Fore',f,'Back',[1 1 .83],...
%          'Units','Poi','Pos',p,'FontS',10),{[0,.4,0],[.6,0,0],[0,0,.5]},...
%          {[90 5 120 12],[220 5 80 12],[310 5 80 12]}); 
  ht(1) = uicontrol('Sty','Tex','Fore',[0,.4,0],'Back',[1 1 .83],...
                    'Units','Poi','Pos',[90 5 120 12],'FontS',10);
  ht(2) = uicontrol('Sty','Tex','Fore',[.6,0,0],'Back',[1 1 .83],...
                    'Units','Poi','Pos',[220 5 80 12],'FontS',10);
  ht(3) = uicontrol('Sty','Tex','Fore',[0,0,.5],'Back',[1 1 .83],...
                    'Units','Poi','Pos',[310 5 80 12],'FontS',10);
  hl = uicontrol('Sty','lis','Pos',[5 50 90 360],...
                 'String',[blanks(size(num2str(moves),2));num2str(moves)]);
  uicontrol('String','<','Call',@myUndo,'Pos',[10 10 30 30]);
  uicontrol('String','>','Call',@myDo,'Pos',[60 10 30 30]);
  set(ha,'Ylim',[-.2,m+1.2],'Xlim',[-.2,n+1.2],'Visible','off')
  [i,j] = find(S.A>0);
  %sh = arrayfun(@(l,k) surf(x+k,y+l,z+S.A(l,k)-.4,'Visible','off'),i,j);
  sh = zeros(size(i));
  for k =1:numel(i)
      sh(k) = surf(x+j(k),y+i(k),z+S.A(i(k),j(k))-.4,'Visible','off');
  end
  set(sh,'Visible','on','normalmode','manual','cdatamode','manual','clipping','off')
  shading flat; lightangle(120,120); lighting flat;
  c = [zeros(m+2,1) [zeros(1,n);S.A>=0;zeros(1,n)] zeros(m+2,1)];
  c = contourc(c,[0 0]);
  
  %c = cellfun(@(c) c(2:end)',mat2cell(c,[1 1],c(2,c(1,:)<.5)+1),'Un',0);
  cc = mat2cell(c,[1 1],c(2,c(1,:)<.5)+1);
  for i =1 :numel(cc)
     cc{i} = cc{i}(2:end)'; 
  end
  
  % mf = @(g,h) arrayfun(@(h) all(inpolygon(c{1,g},c{2,g},c{1,h},c{2,h})),h);
  % c = c(:,~(sum(bsxfun(mf,1:size(c,2),(1:size(c,2))'))>1))';
  MF = true(size(cc,2));
  for i1 = 1:size(cc,2)
      for i2 = 1:size(cc,2)
          MF(i1,i2) = MF(i1,i2) & all(inpolygon(cc{1,i1},cc{2,i1},cc{1,i2},cc{2,i2}));
      end
  end
  c = cc(:,~sum(MF)>1)';
  
%   arrayfun(@(i) patch(c{i,1}-1,c{i,2}-1,c{i,1}>inf,[.85,1,.9],...
%                    'EdgeC',[0 .5 0],'FaceL','none','lineW',3),1:size(c,1));
  for i = 1:1:size(c,1)
     patch(c{i,1}-1,c{i,2}-1,c{i,1}>inf,[.85,1,.9],...
                   'EdgeC',[0 .5 0],'FaceL','none','lineW',3);
  end
  
  [i,j] = find(S.A>=0);
%   patch(bsxfun(@plus,j(:)',.1*sin(-2*pi:pi/4:2*pi)'),...
%         bsxfun(@plus,i(:)',.1*cos(-2*pi:pi/4:2*pi)'),...
%         zeros(17,numel(i)),'faceC',[0 .5 0],'FaceL','none');
  patch(repmat(j(:)',17,1)+repmat(.1*sin(-2*pi:pi/4:2*pi)',1,numel(j)),...
        repmat(i(:)',17,1)+repmat(.1*cos(-2*pi:pi/4:2*pi)',1,numel(i)),...
        zeros(17,numel(i)),'faceC',[0 .5 0],'FaceL','none');

  hc = colorbar('Ycolor',[.5 .5 .5],'Xcolor',[.5 .5 .5],...
                'Ylim',[min(max(S.A(:))-0.5,1),max(S.A(:))]);
  ylabel(hc,'Peg Values')
  set(ha,'CliMmode','manual')
  
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end % of solitaireGUI and nested functions

Contact us