Function If Return End If Return End Switch Case

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 13

function npuzzle(cmd)

if ~nargin
npuzzle('init')
return;
end
if ~(ischar(cmd)||isscalar(cmd))
return;
end
switch cmd
%------------------------------------------------------------------
case 'init'
% MAIN FIGURE
scrsz = get(0,'ScreenSize');
hFigure = figure( ...
'Name','N-Puzzle', ...
'Menubar','none',...
'NumberTitle','off', ...

'KeyPressFcn','npuzzle(double(get(gcbf,''Currentcharacter'')))', ...
'Units','pixels', ...
'Tag','npuzzle', ...
'Position',[(scrsz(3)-510)/2 (scrsz(4)-500)/2 510 500], ...
'Color',[.95 .95 .95], ...
'Colormap',[1 1 1;.93 .97 .93;1 1 .6;.6 .8 1;.6 1 .6;1 .6 .
6], ...
'Visible','on');
% MENU
FileMenu = uimenu(hFigure,'Label','File');
uimenu(FileMenu,'Label','New
Game','Accelerator','N','Callback','npuzzle(''NewGame'')');
NewBoard = uimenu(FileMenu,'Label','New Board');
uimenu(NewBoard,'Label','3
(2x2)','Callback','npuzzle(''three'')');
uimenu(NewBoard,'Label','8
(3x3)','Callback','npuzzle(''eight'')');
uimenu(NewBoard,'Label','15
(4x4)','Callback','npuzzle(''fifteen'')');
uimenu(NewBoard,'Label','24
(5x5)','Callback','npuzzle(''twentyfour'')');
uimenu(NewBoard,'Label','35
(6x6)','Callback','npuzzle(''thirtyfive'')');
uimenu(NewBoard,'Label','48
(7x7)','Callback','npuzzle(''fourtyeight'')');

uimenu(NewBoard,'Label','Custom ...','Separator','on','Callback','npuzzle
(''CustomBoard'')');
uimenu(FileMenu,'Label','Import
Game ...','Callback','npuzzle(''ImportGame'')');
uimenu(FileMenu,'Label','Solve
Game','Separator','on','Callback','npuzzle(''Solve'')');

uimenu(FileMenu,'Label','Exit','Accelerator','Q','Separator','on','Callba
ck','delete(gcf)');
EditMenu = uimenu(hFigure,'Label','Edit');
uimenu(EditMenu,'Label','Undo','Accelerator','Z','Callback','npuzzle(''Un
do'')','Tag','Undo');

uimenu(EditMenu,'Label','Redo','Accelerator','R','Callback','npuzzle(''Re
do'')','Tag','Redo');
HelpMenu = uimenu(hFigure,'Label','Help');

uimenu(HelpMenu,'Label','Help','Accelerator','H','Callback','helpwin
npuzzle');
uimenu(HelpMenu,'Label','Simulate
History','Callback','npuzzle(''SimulateHistory'')','Separator','on');

uimenu(HelpMenu,'Label','About','Callback','npuzzle(''About'')','Separato
r','on');
% MATRIX AXES
axes( ...
'Parent',hFigure, ...
'Units','normalized', ...
'Clipping','on', ...
'Position',[0.02 0.02 0.96 0.96]);
axis('square')
% Start a new board with N=16
startgame(16)

%------------------------------------------------------------------
case 'Surface'
ud = get(gca,'UserData');
set(gcf,'Name',[num2str(ud.N-1) '-Puzzle'])
EdgeColor = [.6 .6 .6];
FaceColor = [1 1 1];
FontSize = .5/ud.n;
HoleColor = [.3 .3 .3];
delete(findobj(gca,'Tag','Surface'))
delete(findobj(gca,'Tag','Hole'))
arrayfun(@(x)delete(findobj(gca,'Tag',num2str(x))),1:1000)
% MATRIX SURFACE
CData = zeros(ud.n+1,ud.n+1);
surface( ...
zeros(size(CData)),CData, ...
'Parent',gca, ...
'ButtonDownFcn','npuzzle(''ButtonDown'');', ...
'EdgeColor',EdgeColor, ...
'FaceColor',FaceColor, ...
'LineWidth',1,...
'Tag','Surface')
axis off
hold on
% TEXT FIELDS
arrayfun(@(x)text( ...
'Position',[mod(x-1,ud.n)+1 ud.n+1-ceil(x/ud.n)]+.5, ...
'String','', ...
'FontUnits','normalized', ...
'FontSize',FontSize, ...
'Tag',num2str(x), ...
'ButtonDownFcn','npuzzle(''ButtonDown'');', ...
'HorizontalAlignment','center'), ...
1:ud.N)
% EMPTY FIELD
fill(1,1,HoleColor, ...
'ButtonDownFcn','npuzzle(''ButtonDown'');', ...
'Tag','Hole')

%------------------------------------------------------------------

%------------------------------------------------------------------
case 'eight'
startgame(9)

%------------------------------------------------------------------

%------------------------------------------------------------------
case 'fourtyeight'
startgame(49)

%------------------------------------------------------------------
case 'CustomBoard'
prompt = {'Input Size of Board (N):'};
name = 'Custom Board Size';
options.Resize = 'on';
answer = inputdlg(prompt,name,1,{'15'},options);
if ~isempty(answer)
N = str2double(answer{1})+1;
n = round(sqrt(N));
if n^2==N
startgame(N)
else
errordlg('Input Size (N) Not Accepted','Custom Board
Size')
end
end

%------------------------------------------------------------------
case 'NewGame'
ud = get(gca,'UserData');
board = shuffleboard(1:ud.N,10000); % Shuffle the board
ud.board = board;
ud.boardhistory = ud.board;
ud.histpos = 1;
ud.starttime = clock;
ud.endtime = [];
set(gca,'UserData',ud);
set(findobj(gcf,'Tag','Undo'),'Enable','off')
set(findobj(gcf,'Tag','Redo'),'Enable','off')
drawboard()
drawpos()

%------------------------------------------------------------------
case 'ImportGame'
prompt = {'Input Game as a Vector or a Matrix:'};
name = 'Import Game';
options.Resize = 'on';
answer = inputdlg(prompt,name,1,{''},options);
if ~isempty(answer)&&~isempty(answer{1})
board = str2num(answer{1})';
board = board(:)';
N = length(board);
n = round(sqrt(N));
if ~isempty(board)&&n^2==N&&~ismember(0,ismember(1:N,board))
ud = get(gca,'UserData');
ud.board = board;
ud.N = N;
ud.n = n;
ud.currpos = 1;
ud.boardhistory = ud.board;
ud.histpos = 1;
ud.starttime = clock;
ud.endtime = [];
set(gca,'UserData',ud);
set(findobj(gcf,'Tag','Undo'),'Enable','off')
set(findobj(gcf,'Tag','Redo'),'Enable','off')
npuzzle('Surface')
drawboard()
drawpos()
else
errordlg('Input Game Not Accepted','Import Game')
end
end

%------------------------------------------------------------------
case 'SimulateHistory'
set(gcf,'Color',[.7 0
0],'Pointer','watch','CloseRequestFcn','','KeyPressFcn','')
ud = get(gca,'UserData');
for i=1:ud.histpos
hole = find(ud.boardhistory(i,:)==ud.N);
x = mod(hole-1,ud.n)+1;
y = ud.n+1-ceil(hole/ud.n);
delete(findobj(gca,'tag','currpos'))
plot(x+[0 .995 .995 0 0]+0.005,y+[0 0 .995 .995 0],'r', ...
'LineWidth',2, ...
'Tag','currpos')
pause(.2)
arrayfun(@(x)set(findobj(gca,'tag',num2str(x)), ...
'String',num2str(ud.boardhistory(i,x))), ...
1:ud.N)
set(findobj(gca,'tag','Hole'), ...
'XData',x+[0 .99 .99 0 0]+0.005,...
'YData',y+[0 0 .99 .99 0]+0.005)
pause(.3)
end
set(gcf,'Color',[.95 .95 .
95],'Pointer','arrow','CloseRequestFcn','closereq','KeyPressFcn','npuzzle
(double(get(gcbf,''Currentcharacter'')))')
drawpos()
%------------------------------------------------------------------
case 'Solve'
ud = get(gca,'UserData');
if sum(ud.board(:)'==1:ud.N)~=ud.N % if not solved
set(gcf,'Color',[.7 0
0],'Pointer','watch','CloseRequestFcn','','KeyPressFcn','')
pause(.2)
boardhistory = solve(ud.board);
ud.boardhistory =
[ud.boardhistory(1:ud.histpos,:);boardhistory];
ud.board = 1:ud.N;
set(gca,'UserData',ud);
set(findobj(gcf,'Tag','Redo'),'Enable','off')
set(findobj(gcf,'Tag','Undo'),'Enable','on')
ud = get(gca,'UserData');
for i=ud.histpos:size(ud.boardhistory,1)
hole = find(ud.boardhistory(i,:)==ud.N);
x = mod(hole-1,ud.n)+1;
y = ud.n+1-ceil(hole/ud.n);
delete(findobj(gca,'tag','currpos'))
plot(x+[0 .995 .995 0 0]+0.005,y+[0 0 .995 .995 0],'r',
...
'LineWidth',2, ...
'Tag','currpos')
pause(.01)
arrayfun(@(x)set(findobj(gca,'tag',num2str(x)), ...
'String',num2str(ud.boardhistory(i,x))), ...
1:ud.N)
set(findobj(gca,'tag','Hole'), ...
'XData',x+[0 .99 .99 0 0]+0.005,...
'YData',y+[0 0 .99 .99 0]+0.005)
pause(.03)
end
ud.histpos = size(ud.boardhistory,1);
set(gca,'UserData',ud);
pause(.2)
set(gcf,'Color',[.95 .95 .
95],'Pointer','arrow','CloseRequestFcn','closereq','KeyPressFcn','npuzzle
(double(get(gcbf,''Currentcharacter'')))')
drawpos()
end

%------------------------------------------------------------------
case 'Undo'
ud = get(gca,'UserData');
if ud.histpos>1
ud.histpos = ud.histpos-1;
ud.board = ud.boardhistory(ud.histpos,:);
set(gca,'UserData',ud);
drawboard()
set(findobj(gcf,'Tag','Redo'),'Enable','on')
end
if ud.histpos<=1
set(findobj(gcf,'Tag','Undo'),'Enable','off')
end
%------------------------------------------------------------------
case 'Redo'
ud = get(gca,'UserData');
if ud.histpos<size(ud.boardhistory,1)
ud.histpos = ud.histpos+1;
ud.board = ud.boardhistory(ud.histpos,:);
set(gca,'UserData',ud);
drawboard()
set(findobj(gcf,'Tag','Undo'),'Enable','on')
end
if size(ud.boardhistory,1)<=ud.histpos
set(findobj(gcf,'Tag','Redo'),'Enable','off')
end

%------------------------------------------------------------------
case 'ButtonDown' % MouseClick
ud = get(gca,'UserData');
n = sqrt(ud.N);
pos = get(gca,'CurrentPoint');
ud.currpos = sub2ind([n n],floor(pos(1,1)),ud.n+1-
floor(pos(1,2)));
set(gca,'UserData',ud);
drawpos()
% If not left-click, treat as space
if ~strcmpi('normal',get(gcf,'SelectionType'))
npuzzle(32)
end

%------------------------------------------------------------------
case 28 % left
ud = get(gca,'UserData');
if ud.currpos~=1
ud.currpos = ud.currpos-1;
set(gca,'UserData',ud);
drawpos()
end

%------------------------------------------------------------------
case 29 % right
ud = get(gca,'UserData');
if ud.currpos~=ud.N
ud.currpos = ud.currpos+1;
set(gca,'UserData',ud);
drawpos()
end

%------------------------------------------------------------------
case 30 % up
ud = get(gca,'UserData');
if ud.currpos>ud.n
ud.currpos = ud.currpos-ud.n;
set(gca,'UserData',ud);
drawpos()
end
%------------------------------------------------------------------
case 31 % down
ud = get(gca,'UserData');
if ud.currpos<=ud.N-ud.n
ud.currpos = ud.currpos+ud.n;
set(gca,'UserData',ud);
drawpos()
end

%------------------------------------------------------------------
case 32 % space
ud = get(gca,'UserData');
n = sqrt(ud.N);
hole = find(ud.board==ud.N);
bx = mod(hole-1,n)+1;
by = ud.n+1-ceil(hole/n);
cx = mod(ud.currpos-1,n)+1;
cy = ud.n+1-ceil(ud.currpos/n);
if (bx==cx&&abs(by-cy)==1)||(by==cy&&abs(bx-cx)==1)
ud.board(hole) = ud.board(ud.currpos);
ud.board(ud.currpos) = ud.N;
ud.histpos = ud.histpos+1;
ud.boardhistory(ud.histpos,:) = ud.board;
if size(ud.boardhistory,1)>ud.histpos
ud.boardhistory(ud.histpos+1:end,:) = [];
end
set(findobj(gcf,'Tag','Redo'),'Enable','off')
set(findobj(gcf,'Tag','Undo'),'Enable','on')
ud.currpos = hole;
set(gca,'UserData',ud);
drawboard()
drawpos()
if isempty(ud.endtime)
checksolution()
end
end

%------------------------------------------------------------------

end
end
%% FUNCTION: DRAWBOARD
function drawboard()
ud = get(gca,'UserData');
arrayfun(@(x)set(findobj(gca,'tag',num2str(x)), ...
'String',num2str(ud.board(x))), ...
1:ud.N)
hole = find(ud.board==ud.N);
x = mod(hole-1,ud.n)+1;
y = ud.n+1-ceil(hole/ud.n);
set(findobj(gca,'Tag','Hole'), ...
'XData',x+[0 .99 .99 0 0]+0.005,...
'YData',y+[0 0 .99 .99 0]+0.005)
end
%% FUNCTION: DRAWPOS
function drawpos()
ud = get(gca,'UserData');
delete(findobj(gca,'tag','currpos'))
x = mod(ud.currpos-1,ud.n)+1;
y = ud.n+1-ceil(ud.currpos/ud.n);
plot(x+[0 .995 .995 0 0]+0.005,y+[0 0 .995 .995 0],'r', ...
'LineWidth',2, ...
'Tag','currpos')
end
%% FUNCTION: SHUFFLEBOARD
function board = shuffleboard(board,times)
N = length(board);
n = sqrt(N);
hole = find(board==N);
for i=1:times
move = [1 -1 n -n];
x = mod(hole-1,n)+1;
y = ceil(hole/n);
switch x
case 1
move(2) = 1;
case n
move(1) = -1;
end
switch y
case 1
move(4) = n;
case n
move(3) = -n;
end
newhole = hole + move(ceil(rand*4));
board(hole) = board(newhole);
hole = newhole;
end
board(newhole) = N;
end
%% FUNCTION: CHECKSOLUTION
function checksolution()
ud = get(gca,'UserData');
if ~ismember(0,ud.board==1:ud.N)
ud.endtime = clock;
set(gca,'UserData',ud);
ico = ones(13)*3; % create simple icon matrix
ico(:,1:4:13) = 1;
ico(1:4:13,:) = 1;
ico(10:12,10:12) = 2;
map = [0 0 0;.5 .5 .6;1 1 1];
msgbox(sprintf([...
'CONGRATULATIONS!\n\n'...
'Start time: %s\n'...
'End time: %s\n\n'...
'Elapsed time: %d seconds\n'...
'Number of moves: %d'],...
datestr(ud.starttime,13),...
datestr(ud.endtime,13),...
round(etime(ud.endtime,ud.starttime)),...
ud.histpos),...
'Finished N-Puzzle','custom',ico,map)
end
end
%% FUNCTION: STARTGAME
function startgame(N)
ud = get(gca,'UserData');
ud.N = N;
ud.n = sqrt(N);
ud.currpos = 1;
set(gca,'UserData',ud);
npuzzle('Surface')
npuzzle('NewGame')
end
%% FUNCTION: SOLVE
function solution = solve(board)
board = board(:)';
solution = [];
N = length(board);
n = sqrt(N);
tilePos = @(tile,board)find(board==tile);
holePos = @(board)tilePos(N,board);
column = @(pos)mod(pos-1,n)+1;
row = @(pos)ceil(pos/n);
for cr=1:n-2 % for all rows except last two rows
for cc=1:n-1 % for all columns except last column
currentTile = cc+(cr-1)*n;
% if tile not at correct position
if tilePos(currentTile,board)~=currentTile %
% if current tile is at bottom row, move it up one step
if row(tilePos(currentTile,board)) == n
% move hole to position above it
moveHoleToRowColumn(n-
1,column(tilePos(currentTile,board)))
% pop the position up one step by moving hole down
moveHole('down')
end
% move hole to bottom or side of current tile
while row(holePos(board))<n &&
row(holePos(board))<row(tilePos(currentTile,board))
moveHole('down');
end
if tilePos(currentTile,board)==currentTile
continue
end
% if current tile is not at correct column
while column(tilePos(currentTile,board)) ~= cc
% move hole to bottom or under tile
while row(holePos(board))<n &&
row(holePos(board))<=row(tilePos(currentTile,board))
moveHole('down');
end
if tilePos(currentTile,board)==currentTile
continue
end
% move hole to left/right of current tile
if column(tilePos(currentTile,board)) <= cc
while
column(holePos(board))<column(tilePos(currentTile,board))+1
moveHole('right');
end
while
column(holePos(board))>column(tilePos(currentTile,board))+1
moveHole('left');
end
elseif column(tilePos(currentTile,board)) > cc
while
column(holePos(board))>column(tilePos(currentTile,board))-1
moveHole('left');
end
while
column(holePos(board))<column(tilePos(currentTile,board))-1
moveHole('right');
end
end
% move hole up to same row of current tile
if row(holePos(board)) > row(tilePos(currentTile,board))
while row(holePos(board)) >
row(tilePos(currentTile,board))
moveHole('up');
end
end
if column(holePos(board)) >
column(tilePos(currentTile,board))
moveHole('left');
else
moveHole('right');
end
end
% now tile is supposed to be at correct column
% if current tile is not at correct row
while row(tilePos(currentTile,board)) ~= cr
moveHoleToRowColumn(row(tilePos(currentTile,board))
+1,column(tilePos(currentTile,board))+1)
moveHole('up','up','left','down')
end
end
end
currentTile = cr*n;
% if tile not at correct position
if tilePos(currentTile,board)~=currentTile
% if current tile is at bottom row
if row(tilePos(currentTile,board)) == n
% move hole to position above it
moveHoleToRowColumn(n-1,column(tilePos(currentTile,board)))
% pop the position up one step
moveHole('down')
end
% move hole to bottom or side of current tile
if row(holePos(board))~=n &&
row(holePos(board))<=row(tilePos(currentTile,board))
while row(holePos(board))<n &&
row(holePos(board))<row(tilePos(currentTile,board))
moveHole('down');
end
end
if tilePos(currentTile,board)==currentTile
continue
end
% if current tile is not at correct column
while column(tilePos(currentTile,board)) ~= n
% move hole to bottom or under tile
while row(holePos(board))<n &&
row(holePos(board))<=row(tilePos(currentTile,board))
moveHole('down');
end
if tilePos(currentTile,board)==currentTile
continue
end
% move hole to left/right of current tile
if column(tilePos(currentTile,board)) <= cc
while
column(holePos(board))<column(tilePos(currentTile,board))+1
moveHole('right');
end
while
column(holePos(board))>column(tilePos(currentTile,board))+1
moveHole('left');
end
elseif column(tilePos(currentTile,board)) > cc
while
column(holePos(board))>column(tilePos(currentTile,board))-1
moveHole('left');
end
while
column(holePos(board))<column(tilePos(currentTile,board))-1
moveHole('right');
end
end
% move hole up to same row of current tile
while row(holePos(board)) > row(tilePos(currentTile,board))
moveHole('up');
end
if column(holePos(board)) >
column(tilePos(currentTile,board))
moveHole('left');
else
moveHole('right');
end
end
% move last tile to under its position
while row(tilePos(currentTile,board)) > cr+1
moveHoleToRowColumn(row(tilePos(currentTile,board))+1,n-1)
moveHole('up','up','right','down')
end
% move hole to underneath it
moveHoleToRowColumn(cr+2,n)

moveHole('up','up','left','down','right','down','left','up','up','right',
'down')
end
end

for cc=1:n-2 % for all columns except last two columns


cr = n; % last row
currentTile = cc+(cr-1)*n;
% if tile not at correct position
if tilePos(currentTile,board)~=currentTile %
% if tile is not at last row, make it
if row(tilePos(currentTile,board)) ~= n
moveHoleToRowColumn(n,column(tilePos(currentTile,board)))
moveHole('up')
end
while column(tilePos(currentTile,board)) ~= cc
moveHoleToRowColumn(n-1,column(tilePos(currentTile,board)))
moveHole('left','down','right')
end
end
currentTile = cc+(cr-2)*n;
% if tile not at correct position
if tilePos(currentTile,board)~=currentTile
% move hole toright if it might disturb other pieces
if column(holePos(board))==cc
moveHole('right');
end
if tilePos(currentTile,board)==currentTile
continue
end
% tile is not at row n-1, make it
if row(tilePos(currentTile,board)) ~= n-1
moveHoleToRowColumn(n-1,column(tilePos(currentTile,board)))
moveHole('down')
end
% move last tile to under its position
while column(tilePos(currentTile,board)) ~= cc+1
moveHoleToRowColumn(n,column(tilePos(currentTile,board)))
moveHole('left','up','right')
end
% move hole to right side of it
moveHoleToRowColumn(n,cc+2);moveHole('up')

moveHole('left','left','down','right','up','right','down','left','left','
up','right')
end
end
% Last 2x2 square
count = 0;
while holePos(board)~=n*n || tilePos((n-1)*n,board)~=(n-1)*n
if row(holePos(board))==n
if column(holePos(board))==n
moveHole('up')
else
moveHole('right')
end
else
if column(holePos(board))==n
moveHole('left')
else
moveHole('down')
end
end
count = count+1;
if count>10 % unsolvable
break
end
end
function swap(x,y)
xp = (board==x);
yp = (board==y);
board(xp) = y;
board(yp) = x;
end
function moveHole(varargin)
hp = find(board==N);
for ii=1:nargin
switch varargin{ii}
case 'left'
hp = hp-1;
case 'right'
hp = hp+1;
case 'up'
hp = hp-n;
case 'down'
hp = hp+n;
end
swap(N,board(hp))
solution = [solution;board];
end
end
function moveHoleToRowColumn(x,y)
while row(holePos(board)) < x
moveHole('down')
end
while row(holePos(board)) > x
moveHole('up')
end
while column(holePos(board)) < y
moveHole('right')
end
while column(holePos(board)) > y
moveHole('left')
end
end
end

You might also like