Code covered by the BSD License

# MATLAB Contest - Peg Solitaire

09 May 2007 (Updated 06 Aug 2009)

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')
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```