function sudokon(g)
%SUDOKON play sudoku onscreen, starting with the g , if given
%
% SUDOKON(G) generates a sudoku grid from the given matrix G which is
% expected to be a 9-by-9 matrix with entries from {0,1,2,...,9}.
%
% The object of the game is to select, for each cell not already containing
% a single, bold, number, a number from 1:9 in such a way that, in each row,
% in each column, and in each of the nine 3-by-3 cell-boxes, each of the
% numbers 1:9 appears, hence appears exactly once.
% In the default mode, each of the cells still to be filled in this way shows
% all the choices still possible for it under these constraints.
%
% To select one of these numbers for a cell, left-click on it.
%
% If there is only one choice showing, then left-clicking anywhere in that
% cell will select that number for the cell.
%
% If there are no such cells, you need to use reasoning to reduce the number
% of choices. When you come to the conclusion that one of the choices showing
% is impossible, delete it by right-clicking on it.
%
% To undo the most recent change in a cell, click on it (making sure not to
% click on any of the choices showing). This will bring up an 'undo' button,
% and clicking it will undo the most recent change in that cell as well as
% all other changes made since then.
% To undo the most recent change in a cell that left it with just one choice
% you must right-click it (since a left-click would make that single choice
% your choice for this cell).
%
% If the cell is already settled, then a (left or right) click on it brings
% up the 'undo' button.
%
% If you are in the clueless mode (see the 'hint?' button described below),
% then the cells still not settled are blank and clicking one such generates
% a keyboard of all nine numbers. Clicking on one makes the corresponding
% number your choice for this cell provided the choice isn't obviously wrong.
%
% SUDOKON(N) with N from 1:12 supplies a puzzle from a list of puzzles, with
% the same N providing seemingly different puzzles, but their difficulty
% increasing with N.
%
% SUDOKON without any input expects you to enter a puzzle directly into the
% sudoku board, by left-clicking on available choices.
%
% The GUI has the following permanent buttons:
%
% 'hint?': cycles through the following three modes:
% (1) showing nothing in cells not yet settled (the clueless mode);
% (2) showing the choices still possible in cells not yet settled;
% (3) showing (if left-clicked) the choice to be selected at some
% cells based on the present state of the board, or
% (if right-clicked) some choices that can safely be deleted from
% some cells; either way, detailed hints are printed in the
% command window, provided '...choices; print' (see 'check...')
% is set.
% 'check...': checks whatever is indicated on the button to the right of
% it, and that button cycles through the following four modes:
% (1) '...choices made' tells you whether or not all selections
% (and deletions) made so far are correct;
% (2) '...choices; print' checks the same thing but also prints in
% the command window various details (such as the order in which
% choices were found, and by what rules, to obtain a complete
% solution, starting at the current board);
% (3) '...# solutions' tells you the number of solutions;
% (4) '...all solutions' prints, in the command window, all
% solutions (if any).
% 'save': saves the current board in a file.
% 'load': brings back a board saved earlier.
% 'print': sends the sudoku board in its current state to the printer,
% to an eps file, or to the command window.
% 'permute': permutes the current problem into one of the same difficulty,
% thus providing a large source of sudoku problems.
% Copyright 2006-2010 Carl de Boor
% All commercial rights and uses, including the name `sudokon', reserved for
% Carl de Boor.
% 8-29jan06, 8-13feb06, 22feb06, 24-25feb06, 1-2mar06, 3-7mar06, 23mar06,
% 13jun06, 14jul06, 19sep06, 16oct06, 26nov06, 06feb07, 14feb07, 23feb07,
% 15mar07, 15apr07, 22apr07, 12jun07, 13feb08, 04jun08, 26jul08, 17-22jan09,
% 27-31jul10, 08-13dec10
if ~nargin||~ischar(g)
action = 'start';
else
action = g;
temp = get(gcf,'Userdata');
if ~isempty(temp)
[hf,p,dg,gr,hc,hst] = deal(temp{:});
end
end
w = [.09,.0866666666];
switch action
case 'start'
% set up the display
running = findobj(allchild(0),'name','sudokon');
if ~isempty(running), delete(running), end
hf = figure('Toolbar','none','menubar','none',...
'name','sudokon',...
'numbertitle','off',...
'DockControls','off',...
'tag','sudoku',...
'Units','characters',...
'Resize', 'off',...
'DeleteFcn','sudokon ''closefigure''',...
'Position',[10,10,135,40]);
set(hf,'Units', 'normalized') %temporary use of character units ensures
%same appearance over platforms and screens
hc = zeros(85,4);
ismall = .01; ilarge = .02;
w3 = 3*(w+ismall)+ilarge;
xoff = .03; yoff=.94; % 3*w3(2) + yoff = 1
A = ['1 2 3';'4 5 6';'7 8 9'];
k = 0;
for j1=0:2
for j2=1:3
for i1=0:2
for i2=1:3
k = k+1;
x = xoff+j1*w3(1)+(j2-1)*(w(1)+ismall);
y = yoff-i1*w3(2)-i2*(w(2)+ismall);
hc(k,[2 3]) = [x,y]+w/2;
hc(k,1) = uicontrol('Parent',hf,...
'BackgroundColor',[1 1 1],...
'ForegroundColor','k',...
'FontName','FixedWidth',...
'Fontsize',10,'Fontweight','normal','Style','text',...
'ButtonDownFcn','sudokon ''works''',...
'Enable','inactive',...
'Units','normalized',...
'Position', [x+.006,y,w(1)-.012,w(2)],...
'string',A,'Userdata',k);
end
end
end
end
% set up an axes, to plot the sudoku grid when printing
axes('Parent',hf,...
'ButtonDownFcn','sudokon ''works''',...
'Position',[0 0 1 1]);
% it seems best to pick the grid directly from the cells just set up
temp = 1:12; temp([1 5 9 12]) = [];
xx = zeros(12,1); xx(temp) = (hc(10:9:73,2)+hc(1:9:64,2))/2;
ofn = [1 5 9]; ofs = [1 4 7];
xx(ofn) = 2*hc(ofs*9-8,2) - xx(ofn+1);
xx(ofn+3) = 2*hc(ofs*9+10,2) - xx(ofn+2);
yy = zeros(12,1); yy(temp) = (hc(2:9,3)+hc(1:8,3))/2;
yy(ofn) = 2*hc(ofs,3) - yy(ofn+1);
yy(ofn+3) = 2*hc(ofs+2,3) - yy(ofn+2);
temp = reshape(repmat(1:12,3,1),1,36);
hc(83,4) = line('Xdata',[repmat([xx([1 end]);NaN],12,1); xx(temp)],...
'Ydata',[yy(temp);repmat([yy([1 end]);NaN],12,1)],...
'Linewidth',.5,'Visible','off');
axis off
% set up the background button for doing nothing
hc(82,4) = uicontrol('Parent',hf,...
'Style','Pushbutton',...
'Tooltipstring','don''t do anything',...
'Units','normalized',...
'Callback','sudokon ''donot''',...
'Visible','off');
% set up the button for undo'ing the most recent change in a cell
% and all other changes since then elsewhere
% ( hc(82,1:2) have become unused )
hc(82,3) = uicontrol('Parent',hf,...
'Style','Pushbutton',...
'Tooltipstring',...
'return to situation just before the last action on this cell',...
'Units','normalized',...
'String','undo',...
'Callback','sudokon ''undo''',...
'Visible','off');
% 'FontName','FixedWidth',...
% start the list of possibilities
p = ones(82,9);
% ... and the 3x3 key board
for j=1:9
p(82,j) = uicontrol('Parent',hf,...
'Units','normalized',...
'Visible','off',...
'String',num2str(j),...
'Tooltipstring','click to delete',...
'Style','Pushbutton');
end
% ... and the 7 buttons
for j=1:4
if j<4, hc(83,j) = uicontrol('Parent',hf,...
'Units','normalized',...
'BackgroundColor',[1 1 1],...
'FontName','FixedWidth',...
'Style','Pushbutton');
end
hc(84,j) = uicontrol('Parent',hf,...
'Units','normalized',...
'BackgroundColor',[1 1 1],...
'FontName','FixedWidth',...
'Style','Pushbutton');
end
set(hc(83,1),'ButtonDownFcn','sudokon ''hints''',...
'Enable','inactive',...
'Tooltipstring','cycles through levels of help',...
'Position', [hc(1,2)-w(1)/2,.97,w(1),.029],...
'string','hint?');
set(hc(83,2),'Callback','sudokon ''prints''',...
'Tooltipstring','prints current sudoku board',...
'Position', [hc(19,2)-w(1)/2,.9375,w(1),.029],...
'string','print');
set(hc(83,3),'Callback','sudokon ''checks''',...
'Tooltipstring','checks consistency of choices made so far',...
'Userdata','click on empty cell for selection',...
'Position', [hc(10,2)-w(1)/2,.97,w(1),.029],...
'string','check...');
set(hc(84,1),'Callback','sudokon ''save''',...
'Userdata',0,...
'Position', [hc(1,2)-w(1)/2,.9375,w(1),.029],...
'Tooltipstring','saves current board in a file','String','save')
set(hc(84,2),'Callback','sudokon ''load''',...
'Userdata','left/right-click to select/delete',...
'Position', [hc(10,2)-w(1)/2,.9375,w(1),.029],...
'Tooltipstring','brings back a board saved earlier','String','load')
set(hc(84,3),'Callback','sudokon ''prefer''',...
'Userdata',1,...
'Position', [hc(19,2)-w(1)*.51,.97,w(1)*2.4,.029],...
'Tooltipstring','sets what to check for',...
'HorizontalAlignment','left',...
'String','...choices made')
% Horizontal Alignment will not work in a Windows environment
set(hc(84,4),'Callback','sudokon ''permute''',...
'Tooltipstring','permutes the current problem',...
'Position', [hc(28,2)-w(1)/2,.9375,w(1),.029],...
'string','permute');
hc(85,1) = uicontrol('Parent',hf,...
'BackgroundColor',[1 1 1],...
'FontName','FixedWidth',...
'Style','Text',...
'Visible','on',...
'Fontweight','bold',...
'Units','normalized',...
'Position', [(hc(46,2)+hc(55,2))/2-.2,.94,.5,.057],...
'Userdata','The current selection is inconsistent',...
'String',get(hc(84,2),'Userdata'));
% 'Position', [(hc(46,2)+hc(55,2))/2-.3,.94,.6,.057],...
% if hc(85,2), then choices to be removed rather than singleton
% suggestions are shown when hints are asked for and,
% if there is printing, more details are printed.
% initiate the solution matrix
dg = zeros(9,9);
% construct the 27 groups
gr2 = repmat((1:9).',1,9) + repmat(0:9:80,9,1); % its columns are the columns
gr1 = gr2.'; % its columns are the rows
gr3 = repmat([1:3, 10:12, 19:21].',1,9) + ...
repmat([0 3 6 27 30 33 54 57 60],9,1);
% its columns are the 3-by-3 cells
gr = [gr1 gr2 gr3]; % to be used as i, j+9, c+18
% initiate history
hst = zeros(0,3);
% generate the list of given singletons
if nargin
if numel(g)==1
g = examples(g);
else % check that the given input is 9x9 with range 1:9
if ~isequal(size(g),[9,9])
delete(findobj(allchild(0),'name','sudokon'))
error('the input matrix should be of size [9,9]')
end
if issparse(g)
g = full(g);
end
d = unique(g);
d(d==0|d==1|d==2|d==3|d==4|d==5|d==6|d==7|d==8|d==9)=[];
if ~isempty(d)
delete(findobj(allchild(0),'name','sudokon'))
error('the entries of the input matrix should all be from 0:9')
end
end
s = find(g);
if hc(85,3), fprintf('Insert the given values\n')
end
[dg,p,hc] = rm(s,g(s),dg,p,gr,hc);
hst = [zeros(length(s),1),s,g(s)];
end
% prevent access to uicontrols and figure by others
set(hf,'HandleVisibility','callback')
case 'checks'
% get(hc(84,3),'Userdata') action
% 1 check correctness
% 2 check correctness and print detail
% 3 check number of solutions
% 4 print all solutions (if any)
set(hc(85,1),'Visible','off')
switch get(hc(84,3),'Userdata')
case {1,2}
ok = checkit(dg,p,gr,hc,action);
if ~ok, set(hc(85,1),'String',get(hc(85,1),'Userdata'))
else
set(hc(85,1),'String','All choices made so far are correct')
end
case 3
pp = p; pp(82,1) = 0;
%[~,~,pp] = checkit(dg,pp,gr,hc,'allsols');
[ignored,ignored,pp] = checkit(dg,pp,gr,hc,'allsols');
if pp(82,1)==1, mess = sprintf('There is exactly one solution.');
elseif pp(82,1)<0
mess = sprintf('There are at least %g solutions.',-pp(82,1));
else mess = sprintf('There are exactly %g solutions.',pp(82,1));
end
set(hc(85,1),'String',mess)
case 4
pp = p; pp(82,1) = 0; checkit(dg,pp,gr,hc,'ap');
end
set(hc(85,1),'Visible','on')
case 'hints'
if hc(85,4)>0 % we are in the end game, so, simply turn on the cells not
% yet selected.
if hc(85,4)>1 % if this is the second request for hints, finish all
% selections.
kk = find(~hc(1:81,4)); dd = kk; hc(kk,4) = 1; lkk = numel(kk);
for j=1:lkk
dd(j) = find(p(kk(j),:));
set(hc(kk(j),1),'Visible','on','Fontsize',30,'Fontweight','bold',...
'String',dd(j))
end
hst = [hst; ones(lkk,1),kk,dd];
set(hc(85,1),'Visible','on', 'String','Congratulations!')
else
hc(85,4) = 0; update(hc,p), hc(85,4) = 2;
set(hc(85,1),'Visible','on', 'String',get(hc(84,2),'Userdata'))
end
else
if hc(85,4)<0
hc(85,4) = 0; update(hc,p)
set(hc(85,1),'Visible','on','String',get(hc(84,2),'Userdata'))
if isequal(get(p(82,1),'Visible'),'on')
set([p(82,:), hc(82,4)],'Visible','off');
end
else
strings = get(hc(85,1),'String');
if iscell(strings), strings = strings{1}; end
if (strings(1)=='E'||strings(1)=='N')
update(hc,p)
set(hc(~hc(1:81,4),1),'String',''), hc(85,4) = -1;
set(hc(85,1),'Visible','on','String',get(hc(83,3),'Userdata'))
else
hc(85,2) = 0;
if isequal(get(gcf,'SelectionType'),'alt')
hc(85,2) = 1;
end
switch checkit(dg,p,gr,hc,action)
case 0
messge = get(hc(85,1),'Userdata');
case 2
messge = {'Every circle indicates';'a removable choice'};
case 1
messge = 'Every star indicates a correct choice';
case -1
messge = 'No obvious correct choices';
end
set(hc(85,1),'String',messge,'Visible','on')
end
end
end
case 'prints'
button = questdlg('Where should the board be sent to?','Send to ...',...
'Printer','File','Command Window','Printer');
if isempty(button), button = 'N'; end
switch button(1)
case {'P','F'}
set([reshape(hc(83:84,1:4),1,8),hc(85,1)],'Visible','off')
set(hc(83,4),'Visible','on')
if button(1)=='F'
curbd = get(hc(84,1),'Userdata');
[pn,mfname,coru] = getfilename('.eps',curbd);
if ~isempty(mfname)
eval(['print -deps ',pn,mfname,'.eps'])
set(hc(84,1),'Userdata',curbd+1);
fprintf(['sudokon ',coru,'ated the eps-file ',mfname,...
' in the directory ',strrep(pn(1:end-1),'\','\\'),'\n']);
end
else
print
end
set([reshape(hc(83:84,1:4),1,8),hc(85,1)],'Visible','on')
set(hc(83,4),'Visible','off')
case 'C'
border = ' +-------+-------+-------+';
tline = repmat(' | . . . | . . . | . . . |',3,1);
printg = [border;repmat([tline;border],3,1)]; dotsp = find(printg=='.');
numbers = find(dg); strdg = num2str(dg); dotsd = find(strdg~=' ');
printg(dotsp(numbers)) = strdg(dotsd(numbers));
disp(printg)
end
case 'save' % saves the current history in the current directory
% for later recall via the command load
curbd = get(hc(84,1),'Userdata');
[pn,mfname,coru] = getfilename('.mat',curbd);
if isempty(mfname), return, end
save([pn mfname],'hst')
set(hc(84,1),'Userdata',curbd+1);
fprintf(['sudokon ',coru,'ated the MAT-file ',mfname,...
' in the directory ',strrep(pn(1:end-1),'\','\\'),'\n']);
case 'load' % restores the board to the end of the history brought
% back from
% get a file name
curbd = get(hc(84,1),'Userdata'); % used in serializing exported data
mfname = ['board',num2str(curbd),'.mat']; curdir = pwd;
if ~(curdir(end)==filesep), curdir = [curdir filesep]; end
getfiletitle = 'Which saved board would you like to load?';
[mfname,pn] = uigetfile([curdir mfname], getfiletitle);
if isequal(mfname,0)||isequal(pn,0)
% the user hit Cancel, so give up on this
return
end
load([pn mfname])
% check out that this is actually a workable history
accepted = 0;
q = unique(hst(:,3));
q(q==0|q==1|q==2|q==3|q==4|q==5|q==6|q==7|q==8|q==9)=[];
if isempty(q)
q = unique(hst(:,1));
q(q==0|q==1|q==2)=[];
if isempty(q)
q = unique(hst(:,2));
if ~any(q-fix(q))&&q(1)>0&&q(end)<82
accepted = 1;
[dg,p,hc] = bringback(hst,p,gr,hc);
mfname(end-3:end) = [];
set(hc(85,1),'Visible','on','String',['back to ',mfname])
end
end
end
if ~accepted
set(hc(85,1),'Visible','on','String',['the file ''',...
mfname,''' does not appear to be generated by sudokon.'])
end
case 'prefer' % makes choices for what exactly check? does
% Userdata action
% 1 check correctness
% 2 check correctness and print detail
% 3 check number of solutions
% 4 print all solutions (if any)
temp = get(hc(84,3),'Userdata') + 1; if temp>4, temp = 1; end
hc(85,3) = 0;
switch temp
case 1
set(hc(83,3),'Tooltipstring','checks consistency of choices made so far')
set(hc(84,3),'String','...choices made')
case 2
set(hc(84,3),'String','...choices; print')
set(hc(83,3),'Tooltipstring',...
'checks consistency of choices made so far and prints details')
hc(85,3) = 1;
case 3
set(hc(84,3),'String','...# solutions')
set(hc(83,3),'Tooltipstring','checks number of solutions')
case 4
set(hc(84,3),'String','...all solutions')
set(hc(83,3),'Tooltipstring','prints out all solutions (if any)')
end
set(hc(84,3),'Userdata',temp)
case 'works' % act on the selected cell
answer = get(gcf,{'CurrentPoint','SelectionType'});
k = get(gco,'Userdata');
if hc(k,4) % the cell has been settled, so, show the undo button
setdonot(hc(82,4),hc(k,2:3))
set(hc(82,3),'Position',[hc(k,2:3)-w/2,w(1),w(2)/3],...
'Visible','on','Userdata',k)
set(hc(85,1),'visible','off')
else
switch hc(85,4)
% hc(85,4) gives the 5 modes: clueless (-1), some help (0),
% no help but only singletons (1), help with singletons (2), done (3).
case {0,2} % some help, hence this is a click on digit or else undo wish
set(hc(85,1),'String',get(hc(84,2),'Userdata'))
d = find(p(k,:));
if length(d)==1
% we are in the help mode and are looking at a singleton cell
% that has not yet been settled.
if isequal(answer{2},'normal')
hst(end+1,:) = [1,k,d];
set(hc(82,3:4),'Visible','off')
[dg,p,hc] = rm(k,d,dg,p,gr,hc);
else % anything other than a left click brings up the undo button
setdonot(hc(82,4),hc(k,2:3))
set(hc(82,3),'Visible','on',...
'Position',[hc(k,2:3)-w/2,w(1),w(2)/3],'Userdata',k)
set(hc(85,1),'Visible','off')
end
else
% check which digit was clicked
dc = ...
find(histc(answer{1}(1), [-inf, hc(k,2)+[-w(1)/6,w(2)/6], inf]))+...
3*(3-...
find(histc(answer{1}(2), [-inf, hc(k,3)+[-w(1)/6,w(2)/6], inf])));
% ... and act accordingly
if p(k,dc) % the digit is one of the possible choices
switch answer{2} % which should be 'normal' or 'alt'
case 'normal' % a left click, hence select this digit
hst(end+1,:) = [1,k,dc];
[dg,p,hc] = rm(k,dc,dg,p,gr,hc);
case 'alt' % a right click, hence delete this digit
p(k,dc) = 0; hst(end+1,:) = [2,k,dc];
text = get(hc(k,1),'string');
temp = [1 7 13 2 8 14 3 9 15]; text(temp(dc))=' ';
set(hc(k,1),'string',text,...
'Fontweight','normal','Foregroundcolor','k')
otherwise % put up the undo button
setdonot(hc(82,4),hc(k,2:3))
set(hc(82,3),'Visible','on',...
'Position',[hc(k,2:3)-w/2,w(1),w(2)/3],'Userdata',k)
set(hc(85,1),'Visible','off')
end
else % an empty space was clicked, hence put up undo button
setdonot(hc(82,4),hc(k,2:3))
set(hc(82,3),'Visible','on','Position',...
[hc(k,2:3)-w/2,w(1),w(2)/3],'Userdata',k)
set(hc(85,1),'Visible','off')
end
end
case {-1,1} % no help, hence show the keyboard
setdonot(hc(82,4),hc(k,2:3))
keyboard(k,hc,p,w);
set(hc(85,1),'String','click digit to select','Visible','on')
set(p(82,:),'Tooltipstring','click to select',...
'Callback','sudokon ''finis''');
otherwise
error('something wrong in sudokon')
end
end
case 'finis' % make the selected digit the choice for this cell
stored = get(gco,'Userdata');
k = stored(1); d = stored(2);
if ~p(k,d) % we are in the clueless state
% but will refuse any inconsistent choice
set(hc(85,1),'Visible','on',...
'String',['your choice of ',num2str(d),' is inconsistent'])
else
set([p(82,:),hc(82,3:4)],'Visible','off')
hst(end+1,:) = [1,k,d];
[dg,p,hc] = rm(k,d,dg,p,gr,hc);
set(hc(85,1),'String',get(hc(83,3),'Userdata'))
end
case 'undo' % return to the stage before the last change made to this cell
set(hc(82,3:4),'Visible','off')
k = get(gco,'Userdata');
last = find(hst(:,2)==k,1,'last');
if ~hst(last,1) % this is one of the original givens, hence merely start
% from scratch
last = find(hst(:,1),1);
end
hst(last:end,:) = [];
[dg,p,hc] = bringback(hst,p,gr,hc);
case 'donot' % do nothing
set([p(82,:),hc(82,3:4)],'Visible','off')
set(hc(85,1),'Visible','on')
case 'closefigure'
delete(findobj('Tag','sudokon Message Box'))
return
case 'permute'
sudokon(permute_it(dg))
return
end
if hc(85,4)<3&&all(hc(1:81,4)) % we just finished
hc(85,4) = 3;
set(hc(85,1),'Visible','on', 'String','Congratulations!')
for j=1:6
set(hc(85,1),'Visible','off'), pause(.1)
set(hc(85,1),'Visible','on'), pause(.1)
end
end
set(hf,'Userdata',{hf,p,dg,gr,hc,hst})
function [ok,dg,p] = checkit(dg,p,gr,hc,action)
%CHECKIT check for inconsistencies and/or new singletons
switch action(1)
case 'h'
ok = -1; ntries = 3; nrep = 4; pstart = p;
case {'c','a'}
ntries = numel(find(dg==0)); nrep = 1; hc(85,2) = 0;
% made sure that an earlier right click on hints doesn't generate
% detailed printout now
end
if hc(85,3)
if action(1)=='h', fprintf('\n detailed hints\n')
else counts = zeros(1,6);
fprintf(['\n',action,'\n rcb, bc, br, two, three ',...
'count occurrences of the following:\n rare digit ',...
'removed from box(rcb), column(bc), row(br);\n all other choices removed',...
' from only pair(two)/triple(three)\n',...
' that can contain a certain pair/triple of digits.\n']);
end
end
for tries=1:ntries
for rep=1:nrep
for r=1:27
tt = sum(p(gr(:,r),:));
if ~all(tt), ok = 0; return, end %inconcistency: some digit is excluded
sd = find(tt==1); %These are the digits that occur exactly once in this
% group, hence the corresponding k should be a singleton.
for d = sd
k = gr(p(gr(:,r),d)==1,r);
if ~dg(k) % this should be a singleton
if hc(85,3)&&action(1)=='h'
dd = 1:9; dd(d) = [];
if any(p(k,dd))||r<10
fprintf(' ** select cell %g|%g for digit %g\n',cellid(k),d)
end
end
p(k,:) = 0;
% also remove this digit from all the other cells in the two
% other groups containing this cell
j = ceil(k/9); i = k - 9*(j-1); % get corresp. col and row index
c = ceil(i/3)+ 3*(ceil(j/3)-1); % get corresp. box index
% recall that gr = [gr1 gr2 gr3], to be used as i, j+9, c+18
p(unique(gr(:,[i j+9 c+18])),d) = 0; p(k,d) = 1;
end
end
tt = sum(p(gr(:,r),:));
sp2 = find(tt==2); % these are the 'rare' digits in this group;
% if their sites also belong to another group,
% then this digit should be removed from any other site
% in that other group
sp = [sp2 find(tt==3)]; lsp = numel(sp);
kk = cell(1,lsp);
for l = 1:lsp
d = sp(l);
k = gr(p(gr(:,r),d)==1,r); kk{1,l} = k;
j = ceil(k/9); i = k - 9*(j-1); % get corresp. col and row index
c = ceil(i/3)+ 3*(ceil(j/3)-1); % get corresp. box index
p(k,d) = 0;
if r<19, % the group is a column or row; the only possible other
% group is a box
if ~diff(c)
if any(p(gr(:,18+c),d))
if hc(85,3)
if action(1)=='h'
if r<10
fprintf([' ** %g occurs in row %g only ',...
'in box %g|%g; remove it elsewhere in box\n'],...
d,r,ceil(i(1)/3),ceil(j(1)/3))
else
fprintf([' ** %g occurs in col %g only ',...
'in box %g|%g; remove it elsewhere in box\n'],...
d,r-9,ceil(i(1)/3),ceil(j(1)/3))
end
else counts(2) = counts(2)+1;
end
end
p(gr(:,18+c),d) = 0;
end
end
else % the group is a box; depending on the exact location of
% the sites, the other group may be a row or a column
if ~diff(i), % the group is a row
if any(p(gr(:,i),d))
if hc(85,3)
if action(1)=='h'
fprintf([' ** %g occurs in box %g|%g only',...
' in row %g; remove it elsewhere in row\n'],...
d,ceil(i(1)/3),ceil(j(1)/3),i(1))
else counts(4) = counts(4)+1;
end
end
p(gr(:,i),d) = 0;
end
elseif ~diff(j), % the group is a column
if any(any(p(gr(:,j+9),d)))
if hc(85,3)
if action(1)=='h'
fprintf([' ** %g occurs in box %g|%g only',...
' in col %g; remove it elsewhere in col\n'],...
d,ceil(i(1)/3),ceil(j(1)/3),j(1))
else counts(3) = counts(3)+1;
end
end
p(gr(:,j+9),d) = 0;
end
end
end
p(k,d) = 1;
end
% if two share the two same sites (or three the
% same three), remove all others from those sites.
for i=1:lsp-1
for j=i+1:lsp
kkk = unique([kk{i}(:);kk{j}(:)]);
switch numel(kkk)
case 2
dd = 1:9; dd(sp([i j])) = [];
if any(any(p(kkk,dd)))
if hc(85,3)
if action(1)=='h', cid = cellid(kkk).';
fprintf([' ** remove all but %g and %g from cells',...
' %g|%g and %g|%g\n'],sort(sp([i j])),cid(:))
else counts(5)=counts(5)+1;
end
end
p(kkk,dd) = 0;
end
case 3
for jj=j+1:lsp
kkkk = unique([kkk;kk{jj}(:)]);
if numel(kkkk)==3
dd = 1:9; dd(sp([i j jj])) = [];
if any(any(p(kkkk,dd)))
if hc(85,3)
if action(1)=='h', cid = cellid(kkkk).';
fprintf([' ** remove all but %g, %g, ',...
'and %g from cells %g|%g, %g|%g, and ',...
'%g|%g\n'],sort(sp([i j jj])),cid(:))
else counts(6)=counts(6)+1;
end
end
p(kkkk,dd) = 0;
end
end
end
end
end
end
tc = sum(p(gr(:,r),:),2);
if ~all(tc), ok = 0; return, end %inconcistency: some cell is now empty
sc = tc==1&dg(gr(:,r))==0; %these are the singleton cells in the
% group not yet settled.
for k=gr(sc,r).'
d = find(p(k,:)==1);
% remove this digit from all the other cells in the three
% groups containing this cell
j = ceil(k/9); i = k - 9*(j-1); % get corresp. col and row index
c = ceil(i/3)+ 3*(ceil(j/3)-1); % get corresp. box index
% recall that gr = [gr1 gr2 gr3], to be used as i, j+9, c+18
kk = unique(gr(:,[i j+9 c+18])); p(k,d) = 0;
if any(any(p(kk,d)))
p(kk,d) = 0;
if hc(85,3)&&action(1)=='h'
fprintf(' ** select cell %g|%g for digit %g\n',cellid(k),d)
end
end
p(k,d) = 1;
end
sc = find(tc==2); % these are the cells in the group having exactly
% two choices left. If these choices are the same
% for two such cells, then the two choices should
% be removed from all other cells of any group
% containing these two cells.
% If we are looking for good hints, append the cells in the
% group having exactly three choices left.
if action(1)=='h', sc = [sc; find(tc==3)]; end
lsc3 = numel(sc); kk = gr(sc,r);
for i=1:lsc3-1
for j=i+1:lsc3
ddd = unique([find(p(kk(i),:)),find(p(kk(j),:))]);
if numel(ddd)==2
kkk = 1:9; kkk(sc([i j])) = [];
if hc(85,3)&&action(1)=='h'&&any(any(p(gr(kkk,r),ddd)))
fprintf([' ** %g and %g are the only choices in two ',...
'cells of ', getgroup(r),';\n'],ddd)
fprintf([' remove these digits from all other cells',...
' in ', getgroup(r),'.\n'])
end
p(gr(kkk,r),ddd) = 0;
elseif action(1)=='h'&&numel(ddd)<5
for jj=j+1:lsc3
dddd = unique([ddd,find(p(kk(jj),:))]);
if numel(dddd)==3
kkk = 1:9; kkk(sc([i j jj])) = [];
if hc(85,3)&&action(1)=='h'&&any(any(p(gr(kkk,r),dddd)))
fprintf([' ** %g, %g and %g are the only choices ',...
'in three cells of ',getgroup(r),';\n'],dddd)
fprintf([' remove these digits from all other',...
' cells in ', getgroup(r),'.\n'])
end
p(gr(kkk,r),dddd) = 0;
elseif numel(dddd)==4
for jjj=jj+1:lsc3
if all(ismember(find(p(kk(jjj),:)),dddd))
kkk = 1:9; kkk(sc([i j jj jjj])) = [];
if hc(85,3)&&action(1)=='h'&&any(any(p(gr(kkk,r),dddd)))
fprintf([' ** %g, %g, %g, and %g are the only '...
'choices in four cells of ',getgroup(r),';\n'],dddd)
fprintf([' remove these digits from all ',...
'other cells in ',getgroup(r),'.\n'])
end
p(gr(kkk,r),dddd) = 0;
end
end
end
end
end
end
end
end % for r
% here is the list of newly found singletons
ns = find(dg(:)==0&sum(p(1:81,:),2)==1);
% terminate this loop as soon as there are newly found singletons (since
% this loop is repeated only in case we are looking for hints and, in that
% case, it is easier to understand a suggestion resulting from just the
% first pass through this loop).
if ~isempty(ns), break, end
end % for rep
if hc(85,3)&&~(action(1)=='h')
counts(1) = counts(1)+1;
fprintf('%g: %grcb, %gbc, %gbr, %gtwo, %gthree',counts),
end
if isempty(ns) % try for x-wings
rbeg = 1; rend = 9; kadd = 9;
for rs=1:2
for r=rbeg:rend-1
sp2 = find(sum(p(gr(:,r),:))==2);
% these are the 'rare' digits in this group;
for l=1:numel(sp2)
d = sp2(l); k = find(p(gr(:,r),d)==1);
for s=r+1:rend
if p(gr(k(1),s),d) && p(gr(k(2),s),d)
temp = 1:9; temp(k) = [];
if ~any(p(gr(temp,s),d)) % can erase d from columns k
if hc(85,3), temp1 = p(gr(:,kadd+k),d); end
p(gr(:,kadd+k),d) = 0; p(gr(k,[r,s]),d) = 1;
if hc(85,3)&&any(temp1-p(gr(:,kadd+k),d))
if rs==1
fprintf('\n X-wing for %g at (%g,%g),(%g,%g)',...
d,r,k(1),s,k(2))
fprintf(['; remove the digit %g elsewhere in',...
'\n columns %g and %g'],...
d,k)
else
fprintf('\n X-wing for %g at (%g,%g),(%g,%g)',...
d,k(1),r-9,k(2),s-9)
fprintf(['; remove the digit %g elsewhere in',...
'\n in rows %g and %g.'],...
d,k)
end
end
break
end
end
end
end
end
rbeg = 10; rend = 18; kadd = 0;
end
ns = find(dg(:)==0&sum(p(1:81,:),2)==1);
if ~isempty(ns)&&action(1)=='h'&&hc(85,3)
for k=ns.'
fprintf('\n ** select cell %g|%g for digit %g',...
cellid(k),find(p(k,:)==1))
end
fprintf('\n')
end
end % for Xwings
switch action(1)
case 'h'
isemptyns=false; if isempty(ns), isemptyns=true; end
if hc(85,3), fprintf('\n'), end
nc = find(sum(pstart-p,2));
if isempty(nc)&&isemptyns, break, end
ok = 1;
if isemptyns||hc(85,2), ok = 2; ns = nc; hc(85,2) = 1; end
for k=ns(:)' % replace each new singleton by a star
% or each deletable choice by a circle
A = get(hc(k,1),'String');
temp = [1 7 13 2 8 14 3 9 15];
if hc(85,2), A(temp(p(k,:)~=pstart(k,:))) = 'o';
else A(temp(p(k,:)==1)) = '*';
end
set(hc(k,1),'String',A,'Fontweight','bold','Foregroundcolor','r')
end
if ~isemptyns, break, end
case {'c','a'}
if isempty(ns), break, end
lns = length(ns); dns = zeros(lns,1);
for r=1:lns
dns(r) = find(p(ns(r),:));
end
if hc(85,3)
j = ceil(ns/9); i = ns - 9*(j-1); % get corresp. col and row index
ij = [i(:)'; j(:)'];
fprintf(['\n insert singleton at%2g|%1g%2g|%1g%2g|%1g%2g|',...
'%1g%2g|%1g%2g|%1g%2g|%1g%2g|%1g%2g|%1g%2g|%1g'],ij(:))
fprintf(['\n with value %3g%4g%4g%4g%4g%4g%4g%4g',...
'%4g%4g'],dns)
fprintf('\n')
end
[dg,p,hc] = rm(ns,dns,dg,p,gr,hc,action);
end % for switch action(1)
if all(dg), break, end
end % for tries
if action(1)=='c' || action(1)=='a'
sz = find(dg==0);
if isempty(sz), ok = 1;
if action(1)=='a'
p(82,1) = p(82,1)+1;
if p(82,1)==1
set(hc(85,1),'Visible','On',...
'String', 'There is one solution.');
else
set(hc(85,1),'Visible','On',...
'String', sprintf('There are %g solutions.',p(82,1)));
end
pause(.01)
if action(2)=='p', fprintf('\n solution %g :\n',p(82,1)), disp(dg),
end
ok = 0;
if p(82,1)==50
button = questdlg('Continue?',...
'50 solutions found so far; continue?',...
'No','Yes','No');
if button(1)=='N', ok = 1; p(82,1) = -p(82,1); return, end
end
end
else % at some locations, we still don't have a singleton
% so, find a nonsingleton solution with the smallest number
% of possibilities and run through each of them until the one
% that works is found. Note that this could lead to further
% branchings (and should be set up that way). The trick is to
% interpret any inconsistency found merely as a wrong choice
% made along the way, calling for a different choice at an
% earlier point.
% find the first digit appearing the least in settled cells,
% [nm,dig] = min(sum(p(dg>0,:))); sz(~p(sz,dig)) = [];
% find the first digit appearing the most in unsettled cells,
%[~,dig] = max(sum(p(sz,:))); sz(~p(sz,dig)) = [];
[ignored,dig] = max(sum(p(sz,:))); sz(~p(sz,dig)) = [];
% then determine the first shortest nonsingleton containing it:
[nm,ni] = min(sum(p(sz,:),2)); k = sz(ni);
j = ceil(k/9); i = k - 9*(j-1); cc = find(p(k,:)>0);
% cc(cc==dig)=[]; cc = [dig,cc]; % this attempt didn't work at all
if hc(85,3)
fprintf(['\ntry the %g remaining possibilities at location %g|%g:'...
' %g,%g,%g,%g'],nm,i,j,cc)
end
for nn=1:nm
if hc(85,3)
fprintf('\n try possibility %g, namely %g, at location %g|%g',...
nn,cc(nn),i,j)
end
[ndg,np,hc] = rm(k,cc(nn),dg,p,gr,hc,action);
[ok,ndg,np] = checkit(ndg,np,gr,hc,action);
if action(1)=='a', p(82,1) = np(82,1); end
if ok, dg = ndg; p = np; return, end
end
end
end
function g = examples(n)
%EXAMPLES provide a sample puzzle
switch n
case 1
mssg = 'this is a simple puzzle, rating 2';
g = [
8 0 0 5 0 0 3 2 0
7 0 3 1 0 0 4 0 0
1 2 0 0 0 9 0 0 8
0 9 0 0 0 0 0 1 0
6 5 0 0 9 3 0 8 0
0 8 0 6 2 0 0 9 3
0 0 6 0 0 8 1 0 2
2 0 0 4 0 0 0 6 7
0 7 4 0 0 6 0 0 5 ] ; % 2 inserts, 1|0|0|2|4; 0
case 2
mssg = 'this is a randomly made up puzzle, rating 3';
g=[0 0 0 0 0 5 3 0 2
7 1 0 3 0 0 8 0 0
0 6 0 0 0 2 0 0 0
0 3 0 0 0 0 0 4 0
2 0 0 4 0 3 0 0 9
0 0 9 0 7 0 0 5 0
9 0 0 6 0 0 0 0 0
0 0 1 0 3 7 0 0 8
0 4 0 0 8 0 0 0 1 ]; % 3 inserts: 2two, 9|4|5|4|6; 0
case 3
mssg = 'this is a medium-simple puzzle, rating 4';
g = [
0 7 8 0 1 0 9 0 4
0 0 0 0 0 0 0 0 0
3 0 2 0 0 0 6 7 0
0 4 0 1 0 9 0 0 7
0 0 6 2 0 8 4 0 0
0 2 0 5 0 3 0 0 8
0 0 1 0 0 0 0 0 0
9 0 0 0 0 0 2 1 0
0 6 7 0 9 0 5 0 3 ]; % 4 inserts: 2two, 8|2|2|4|2;
case 4
mssg = 'this is a medium-hard puzzle, rating 5';
g=[3 9 0 2 0 0 8 0 0
0 0 0 7 0 0 0 2 9
7 0 0 3 0 0 0 5 0
0 0 0 4 1 2 0 0 0
0 0 0 0 0 0 9 3 0
0 5 6 0 0 0 0 0 0
4 1 0 0 0 6 0 0 0
0 7 0 0 0 4 0 0 2
0 0 3 0 0 7 0 8 4]; % 5 inserts: 2two, 12|2|1|3|1;
case 5
mssg = 'this is a medium-hard puzzle, rating 5';
g=[0 8 3 1 0 0 0 5 0
0 0 0 0 0 0 2 6 0
5 0 0 9 4 0 0 0 0
0 0 0 2 0 3 0 0 0
9 7 0 0 0 0 0 0 0
0 0 0 0 0 0 0 7 8
0 4 6 0 0 0 0 0 0
0 0 0 0 8 1 0 0 9
0 3 0 0 0 4 6 1 0 ] ; % 5 inserts, 11|3|2|5|3
case 6
mssg = 'this is a hard puzzle, rating 7';
g=[8 0 0 0 9 0 0 0 0
0 0 0 6 2 0 0 0 0
0 0 0 0 0 1 0 3 9
0 0 0 1 3 0 0 8 0
2 9 7 0 5 0 0 0 0
0 8 0 0 0 0 5 0 0
0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 7 0 1
0 0 3 0 0 7 6 0 5 ] ; % 7 inserts: 2two, 10|5|4|4|2
case 7
mssg = 'this is a hard puzzle, rating 7';
g=[4 6 0 0 0 1 0 0 0
0 0 2 0 9 6 0 0 0
0 3 0 0 0 0 0 6 8
0 0 0 6 0 7 0 0 0
0 0 0 0 0 0 0 3 7
5 1 0 0 0 0 0 0 0
0 0 0 7 1 0 9 0 0
8 4 0 0 0 0 0 5 0
0 0 0 3 0 0 0 2 4 ]; % 7 inserts: 2two, 12|5|4|3|3
case 8
mssg = 'this is a very hard puzzle, rating 8';
g=[0 1 0 0 2 0 0 0 4
0 9 0 0 0 0 5 0 0
0 0 2 0 0 3 0 0 0
2 0 7 1 0 0 8 0 0
0 0 0 0 9 0 2 0 5
0 0 0 7 3 0 0 0 1
1 0 0 0 0 7 0 0 0
0 0 4 0 0 0 6 0 0
0 0 8 0 0 6 0 9 0 ] ; % 8 inserts: 3two, 11|2|1|5|5
case 9
mssg = 'this is a very, very hard puzzle (one fourer), rating 9';
g=[ 3 5 0 0 0 0 6 0 1
2 0 0 0 0 0 5 0 8
0 0 9 0 0 0 0 7 0
5 0 0 2 0 0 0 0 0
0 0 0 0 3 4 0 0 0
8 1 0 0 0 5 0 0 9
0 0 0 0 0 1 8 0 0
0 0 0 0 2 0 0 0 7
1 0 6 3 9 0 0 0 0 ]; % 9 inserts: 1two, 6|3|3|5|5
case 10
mssg = 'this puzzle requires use of the X-wing, rating 9';
g=[0 6 5 0 2 0 0 0 9
0 0 0 4 0 0 0 0 0
7 2 0 0 0 5 0 3 0
0 0 9 0 0 0 3 8 0
2 0 0 0 0 0 0 0 7
0 5 8 0 0 0 2 0 0
0 1 0 7 0 0 0 9 6
0 0 0 0 0 6 0 0 0
6 0 0 0 9 0 8 5 0 ] ; % 9|2|1|4|6
case 11
mssg = 'this puzzle is surprisingly hard, yet rating 5';
g=[0 0 0 0 0 8 0 0 0
6 4 1 3 0 0 0 8 0
0 8 0 0 7 0 1 3 0
4 2 0 0 6 7 0 0 0
0 0 0 2 0 0 4 0 0
0 0 7 0 0 0 0 0 6
0 9 0 0 0 0 0 0 0
5 0 0 0 0 1 0 4 9
0 0 3 4 8 9 0 1 0 ]; % 5 inserts: 12|2|0|5|8
case 12
mssg = 'this puzzle is the hardest here; must guess with only 34 cells settled; rating 12';
g=[0 0 0 1 0 2 3 0 4
0 0 0 0 0 0 0 0 0
0 0 0 5 0 6 1 0 7
6 0 1 0 0 0 7 0 8
0 0 0 0 0 0 0 0 0
8 0 5 0 0 0 4 0 3
5 0 6 2 0 1 0 0 0
0 0 0 0 0 0 0 0 0
2 0 4 6 0 3 0 0 0]; % 3+3+2+1+1+5 inserts: 2two
otherwise
mssg = 'interesting puzzle, rating 7';
g=[0 0 0 0 2 7 0 0 0
1 0 5 0 0 0 0 0 0
0 0 0 0 0 0 0 4 2
0 0 0 0 3 4 0 0 0
9 0 3 7 8 6 0 0 0
0 8 0 0 0 0 4 6 0
0 3 2 0 0 0 5 0 0
0 0 0 3 4 2 0 7 8
0 0 0 0 1 9 0 0 0]; % 7 inserts: 1two, 14|2|3|6|8
end
% now randomize the puzzle, for greater variety.
g = permute_it(g);
temp = msgbox(mssg); set(temp,'Tag','sudokon Message Box')
function keyboard(k,hc,p,w)
%KEYBOARD generate a keyboard on cell k
set([hc(82,3),p(82,:)],'Visible','off')
x = [-1 0 1 -1 0 1 -1 0 1]*(w(1)/3)+(hc(k,2)-w(1)/6);
y = [1 1 1 0 0 0 -1 -1 -1]*(w(2)/3)+(hc(k,3)-w(2)/6);
hh = 1:9; if ~hc(85,4), hh(~p(k,:)) = []; end
for j=hh
set(p(82,j),'Visible','on',...
'Position',[x(j),y(j),w/3],...
'Userdata',[k,j]);
end
function g = permute_it(g)
%PERMUTE_IT permutes g while preserving all groups
% This means choosing, for both rows and columns, a permutation that maps a
% 3-group to a 3-group:
rows = [randperm(3) randperm(3) randperm(3)] + ...
reshape(repmat(3*(randperm(3)-1),3,1),1,9);
cols = [randperm(3) randperm(3) randperm(3)] + ...
reshape(repmat(3*(randperm(3)-1),3,1),1,9);
% ... as well as an arbitrary 9-permutation for permuting the 9 digits
perm9 = [1, randperm(9)+1];
% ... to give the acceptable variation
g(rows,cols) = perm9(g+1)-1;
% ... further `changed' by, possibly, switching to the transposed
perm2 = randperm(2)-1; if perm2(1), g = g.'; end
%function [dg,p,hc] = rm(s,ds,dg,p,gr,hc,~)
function [dg,p,hc] = rm(s,ds,dg,p,gr,hc,action)
%RM remove the value ds(j) of the singleton s(j) from all of its groups
for r=1:length(s)
k = s(r);
j = ceil(k/9); i = k - 9*(j-1); % get corresp. col and row index
c = ceil(i/3)+ 3*(ceil(j/3)-1); % get corresp. cell index
d = ds(r); % get the given digit
% zero out that digit in the three groups that k belongs to
p(unique(gr(:,[i j+9 c+18])),d) = 0;
% also zero out all other digits at k, but reintroduce d at k
p(k,:) = 0; p(k,d) = 1;
% update dg
dg(k) = d; hc(k,4) = 1;
if nargin==6 % update the display
set(hc(k,1),'Foregroundcolor','k','Fontsize',30,'Fontweight','bold',...
'String',d)
update(hc,p)
% if this is the ninth of this digit being settled, change their color to
% blue
cds = find(p(1:81,d));
if numel(cds)==9&&all(hc(cds,4)==1)
set(hc(cds,1),'ForegroundColor','b'), end
if ~hc(85,4)&&all(sum(p(1:81,:),2)==1) % there are only singletons left,
% so, for greater pleasure, turn off all cells not yet settled,
% being prepared to turn them on again when a hint is asked for
hc(85,4) = 1;
set(hc(85,1),'Visible','on',...
'String',{'Only one choice left for each cell;'; ...
get(hc(83,3),'Userdata')})
set(hc(~hc(1:81,4),1),'String','')
end
end
end
function update(hc,p)
%UPDATE update display
if ~hc(85,4) % if we are in help mode
C = repmat(' ',3,5);
for k=1:81
if ~hc(k,4) % only those not yet settled
A = '123456789'; A(~p(k,:)) = ' '; C(:,[1 3 5]) = reshape(A,3,3)';
set(hc(k,1),'String',C,'Fontweight','normal','Foregroundcolor','k')
end
end
end
function [dg,p,hc] = bringback(hst,p,gr,hc)
%BRINGBACK restore to the end of the given HST
p(1:81,:) = 1;
dg = zeros(9,9);
A = reshape('147258369',3,3);
set(hc(1:81,1),'Visible','on','Fontsize',10,'Fontweight','normal',...
'ForegroundColor','k','String',A)
hc([1:81,85],4) = 0;
% enter the givens, if any
s = find(~hst(:,1));
if ~isempty(s)
[dg,p,hc] = rm(hst(s,2),hst(s,3),dg,p,gr,hc);
end
s = find(hst(:,1)==1);
if ~isempty(s)
[dg,p,hc] = rm(hst(s,2),hst(s,3),dg,p,gr,hc);
end
s = find(hst(:,1)==2);
p(hst(s,2)+(hst(s,3)-1)*82) = 0;
update(hc,p)
if ~hc(85,4)
set(hc(85,1),'Visible','on','String',get(hc(84,2),'Userdata'))
end
function setdonot(handle,xy)
%SETDONOT set the do-nothing uicontrol
W = .11;
set(handle,'Position',[xy-W/2,W,W],'Visible','on');
function [pn,mfname,coru] = getfilename(suffix,curbd)
%GETFILENAME get a filename (including directory) for storing stuff
mfname = ['board',num2str(curbd+1),suffix]; coru = ''; curdir = pwd;
if ~(curdir(end)==filesep), curdir = [curdir filesep]; end
getfiletitle = ['Choose a Name for the ',suffix,'-File'];
while isempty(coru)
[mfname,pn] = uiputfile([curdir mfname], getfiletitle);
if isequal(mfname,0)||isequal(pn,0)
% the user hit Cancel, so give up on this
mfname = ''; return
else
checked = 0;
if length(mfname)>2&&isequal(mfname(end-3:end),suffix)
% strip off terminal suffix
mfname(end-3:end) = []; checked = 1;
end
fullfilename = [pn mfname,suffix];
if ~exist(fullfilename, 'file')
coru = 'cre';
else
if checked
anss = 'Yes';
else
anss = questdlg([which(fullfilename),' already exists.';...
' Do you want to replace it?'],...
[suffix,'-file already exists ...'], 'No','Yes','No');
end
if isequal(anss,'Yes')
coru = 'upd';
end
end
end
end
function groupr = getgroup(r)
%GETGROUP gets text as to what group exactly r represents
if r<10, groupr = ['row ',num2str(r)];
elseif r<19, groupr = ['column ',num2str(r-9)];
else r = r-18; j = ceil(r/3); i = r-3*(j-1);
groupr = ['box ',num2str(i),'|',num2str(j)];
end
function cij = cellid(k)
%CIJ returns the row(s) and column(s) of the given cell(s) k
j = ceil(k/9); i = k - 9*(j-1); % get corresp. col and row index
cij = [i,j];