420 likes | 816 Views
MATLAB Minesweeper. Development of a MATLAB GUI. Start with an empty function. function MineSweeper_GUI () end % of MineSweeper ()
E N D
MATLAB Minesweeper Development of a MATLAB GUI
Start with an empty function function MineSweeper_GUI() end % of MineSweeper() While not strictly required, a function that creates the GUI provides flexibility for your program. If you have multiple frames in your interface, functions permit separation of code by allowing a main program to instantiate the frames independently.
Create a basic figure function MineSweeper_GUI() fh= figure('Position', [400, 400, 250, 300]); % Adjust appearance set(fh, 'Resize', 'off'); set(fh, 'MenuBar', 'none'); set(fh, 'NumberTitle', 'off'); set(fh, 'Name', 'MATLAB MineSweeper'); end % of MineSweeper_GUI() These are common attributes, although not every GUI will necessarily set them.
Provide the controls % Text will be provided and covered up by a button. The button % will disappear when clicked. % Control to hold text (to display after button is pushed) tbh = uicontrol('Units', 'normalized', 'Style', 'text'); set(tbh, 'String', '8', 'FontSize', 16); set(tbh, 'Position', [0, 0, 0.1, 0.1]); Now that we have one box, let’s make the code repeat it.
Provide the rest of textboxes c = 1; for xpos = 1:9 for ypos = 1:9 % Control to hold text (to display after button is pushed) tb(c) = uicontrol('Style', 'text', 'Units', 'normalized'); set(tb(c), 'String', '8', 'FontSize', 16); set(tb(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1),0.1,0.1]); c = c+1; end % of for ypos end % of for xpos (We assume a 9x9 grid – this can become a feature selected by the user) These all show the number ‘8’. We will change that after we have a visually complete GUI – then we can make it fully functional.
Provide pushbuttons to cover % Cover with buttons c = 1; for xpos = 1:9 for ypos = 1:9 % These are drawn on the figure, not any panel u(c)=uicontrol('Style', 'pushbutton', 'Units', 'normalized'); set(u(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1), 0.1, 0.1]); c = c + 1; end end We now have a visually-complete game. Now we can focus on the functionality.
Add button functionality When actions are to be performed as a result of clicking on a control, MATLAB places a call to a “callback function”. It is the responsibility of the programmer to provide that function, and to tell MATLAB how to call that function. The syntax for this is a little different from typical function usage.
Defining a callback function To define a callback function, simply make a function in the same file as the GUI frame. The name can be any legal function name. The first two parameters of the function are required by MATLAB – you can add any others you wish afterwards: function my_callback(H, E, a, b, c) . . . end % of my_callback()
Our callback function % Callback (cb) for PushButtons (pb) function cb_pb(H, E, bh, th, c, sz) % What string is "under" the button that was pressed? s = get(th(c), 'String'); if strcmp(s, '9') hit_mine(c, sz, bh, th); elseif length(s)==0 || strcmp(s, ' ') hit_empty(c, sz, bh, th); else delete(bh(c)); uicontrol(th(c)); % set focus to current text end % of if end % of cb_pb
Tell MATLAB to use the callback function % Because clicking a button can mean deleting other buttons in this % program, we want to send the entire array of button handles and text % handles to the callback functions. That is why this loop is AFTER % the original creation loop - if we had tried to set the callbacks in % the creation loop, the early buttons would know nothing about buttons % defined later. for c = 1:n^2 % Handle a left-click set(u(c), 'callback', {@cb_pb, u, th, cnt, n}); end % of for c
function MineSweeper_demo() clc; n=9; fh = figure('Position', [400, 400, 250, 300]); % Adjust appearance set(fh, 'Resize', 'off'); set(fh, 'MenuBar', 'none'); set(fh, 'NumberTitle', 'off'); set(fh, 'Name', 'MATLAB MineSweeper'); c = 1; for xpos = 1:9 for ypos = 1:9 % Control to hold text (to display after button is pushed) tb(c) = uicontrol('Style', 'text', 'Units', 'normalized'); set(tb(c), 'String', '8', 'FontSize', 16); set(tb(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1),0.1,0.1]); c = c+1; end % of for ypos end % of for xpos % Cover with buttons c = 1; for xpos = 1:9 for ypos = 1:9 % These are drawn on the figure, not any panel u(c)=uicontrol('Style', 'pushbutton', 'Units', 'normalized'); set(u(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1), 0.1, 0.1]); c = c + 1; end end % Attach callback function for c = 1:n^2 % Handle a left-click set(u(c), 'callback', {@cb_pb, u, tb, c, n}); end % of for c end % of MineSweeper_demo() % Callback function function cb_pb(H, E, bh, th, c, sz) % What string is "under" the button? s = get(th(c), 'String'); if strcmp(s, '9') hit_mine(c, sz, bh, th); elseif length(s)==0 || strcmp(s, ' ') hit_empty(c, sz, bh, th); else delete(bh(c)); uicontrol(th(c)); % set focus to current text end end % of cb_pb()
Finishing off the code At this point, most of the GUI code is complete. We now provide code that will perform the tasks desired upon certain events. If a mine is beneath a clicked button: hit_mine(c, sz, bh, th); If a space is beneath a clicked button: hit_empty(c, sz, bh, th);
hit_mine() % What to do if a mine is hit function hit_mine(i, sz, bh, th) % Get rid of ALL buttons and show the mines for r=1:sz for c=1:sz % Since using vectors to hold handles v = (c-1)*sz + r; % Is there still a button here? if ishandle(bh(v)) % What is the textbox string? ts = get(th(v), 'String'); % Change textbox to show an image of a mine exploding if strcmp(ts, '9') mineimg = imread('mine10.jpg'); set(bh(v), 'cdata', mineimg); else % Get rid of the button delete(bh(v)); end end end % of for c end % of for r
Add a grid for testing hit_mine() function MineSweeper_demo() % ----------------------------- % Testing values n = 9; for i=1:n^2 M(i) = num2str(randi(9,1)); r = randi(2)-1; if r M(i) = ' '; end end % ------------------------------ And we will change this line: set(tb(c), 'String', '8', 'FontSize', 16); To display the testing values: set(tb(c), 'String', num2str(M(c)), 'FontSize', 16);
hit_empty() function hit_empty(pos, sz, bh, th) % Find all of the surrounding empties and remove the buttons recursive_remove(pos, pos, sz, bh, th, 1); end % of hit_empty() (see next slide)
recursive_remove() % Remove all buttons over empty strings and over numbers that touch one of % those empty strings (but don't remove over mines) function recursive_remove(from, to, sz, bh, th, level) if ~(to<=0 || to>sz^2 ||(mod(from, sz)==1 && mod(to, sz)==0) ||(mod(from, sz)==0 && mod(to, sz)==1) to_s = get(th(to), 'String'); from_s = get(th(from), 'String'); % Empty or contains a number (but not a mine) if strcmp(to_s, ' ') || (~strcmp(to_s, ' ') && str2num(to_s)>0 && str2num(to_s)<9) % Does the button still exist? if ishandle(bh(to)) % Remove the button delete(bh(to)); % If it's empty, recurse to check the surrounding buttons if strcmp(to_s, ' ') level = level + 1; recursive_remove(to, to-sz-1, sz, bh, th, level); recursive_remove(to, to-sz, sz, bh, th, level); recursive_remove(to, to-sz+1, sz, bh, th, level); recursive_remove(to, to-1, sz, bh, th, level); recursive_remove(to, to+1, sz, bh, th, level); recursive_remove(to, to+sz-1, sz, bh, th, level); recursive_remove(to, to+sz, sz, bh, th, level); recursive_remove(to, to+sz+1, sz, bh, th, level); level = level - 1; end end % of if end % of else end % of recursive_remove()
Last Details One other property of the game (other than scoring) involves allowing the user to mark a location as “containing a mine” or “might contain a mine”. We will do this by allowing the user to right-click on a button and placing “?” for “might be a mine”, or “!” for “containing a mine”.
Right-click Callbacks A separate callback function must be placed for handling “alternative clicks”. Note the added line: for c = 1:n^2 % Handle a left-click set(u(c), 'callback', {@cb_pb, u, tb, c, n}); % Handle a right-click set(u(cnt), 'ButtonDownFcn', {@right_click, c, u}); end % of for c
right_click() % This function is executed when a button is right-clicked function right_click(h, e, pos, bh) % The button we pressed is in a figure. Only the figure knows the type of % click that was made: fh = ancestor(h, 'figure') clickType = get(fh, 'SelectionType') % 'Alt' is a right-click % % This will allow using the right-click to cycle through the options bs = get(h, 'String'); if strcmp(clickType, 'alt') if strcmp(bs, '!') % Option 1: Clear the button label set(bh(pos), 'String', ''); end if strcmp(bs, '') % Option 2: Put a query (?) on the button - user isn't sure set(bh(pos), 'String', '?'); set(bh(pos), 'FontSize', 16, 'ForegroundColor', [1, 0, 0]); end if strcmp(bs, '?') % Option 3 : Put a bang (!) on the button - user thinks this is a mine set(bh(pos), 'String', '!'); set(bh(pos), 'FontSize', 16, 'ForegroundColor', [1, 0, 0]); end end % of if end % of right_click()
Source Code Function: http://kindy.egr115.com/MineSweeper.zip