As promised, here’s the code and images for my minesweeper game.

Getting the game running is a little clunky, but everything should be here.

  1. Create a folder named “imgs”
  2. Save all of the images into the “imgs” folder, use all the original filenames
  3. Create three .m files for the code: “main.m”, “img_handle.m”, and “minesweeper.m”
  4. Copy and paste the code into these .m miles
  5. Run “main.m”

Your final minesweeper folder structure should look like this:

  • Minesweeper 2.0
    • img_handle.m
    • main.m
    • minesweeper.m
    • imgs
      • 0.png
      • 1.png
      • uncovered.png
      • win_mine.png

Feel free to edit the graphics to your liking.

Also, as I said before, the code is a little slow and I’d have to gut it and start over to improve performance. But again, it works, so I’m happy.

imgs

main.m

close all
clear
clc

% field_width = 8;
% field_height = 8;
% number_of_mines = 10;

field_width = 16;
field_height = 16;
number_of_mines = 30;

% field_width = 30;
% field_height = 16;
% number_of_mines = 99;

% ========================================================================

minesweeper(field_width, field_height, number_of_mines);

img_handle.m

classdef img_handle < hgsetget
    properties
        img_0
        img_1
        img_2
        img_3
        img_4
        img_5
        img_6
        img_7
        img_8
        click
        mine
        uncvr
        boom
        flag
        miss_flag
        win_mine
        state_happy
        state_click
        state_win
        state_dead
    end
    
    methods
        function obj = img_handle()
            obj.img_0 = imread('imgs/0.png');
            obj.img_1 = imread('imgs/1.png');
            obj.img_2 = imread('imgs/2.png');
            obj.img_3 = imread('imgs/3.png');
            obj.img_4 = imread('imgs/4.png');
            obj.img_5 = imread('imgs/5.png');
            obj.img_6 = imread('imgs/6.png');
            obj.img_7 = imread('imgs/7.png');
            obj.img_8 = imread('imgs/8.png');
            obj.click = imread('imgs/click.png');
            obj.mine  = imread('imgs/mine.png');
            obj.uncvr = imread('imgs/uncovered.png');
            obj.boom  = imread('imgs/boom.png');
            obj.flag  = imread('imgs/flag.png');
            obj.miss_flag = imread('imgs/miss_flag.png');
            obj.win_mine = imread('imgs/win_mine.png');
            obj.state_happy = imread('imgs/state_happy.png');
            obj.state_click = imread('imgs/state_click.png');
            obj.state_win = imread('imgs/state_win.png');
            obj.state_dead  = imread('imgs/state_dead.png');
        end
    end
end

minesweeper.m

classdef minesweeper
    properties
        x_len
        y_len
        x_pos
        y_pos
        state
        show
        solution
        num_mines
        initialized
        
        fh
    end
    
    methods
        function obj = minesweeper(varargin)
            if nargin == 1
                obj = varargin{1};
            elseif nargin == 3
                obj.x_len = varargin{1};
                obj.y_len = varargin{2};
                obj.num_mines = varargin{3};
                
                pos = get(0, 'ScreenSize');
                obj.x_pos = pos(3)/4;
                obj.y_pos = pos(4)/2;
            else
                obj.x_len = 8;
                obj.y_len = 8;
                obj.num_mines = 10;
            end
            obj.state = 'state_happy';
            obj.initialized = 0;
            
            obj.solution = zeros(obj.x_len, obj.y_len);
            obj.show = cell(obj.x_len, obj.y_len);
            for jj = 1:obj.y_len
                for ii = 1:obj.x_len
                    obj.show{ii, jj} = 'uncvr';
                end
            end
            
            obj.fh = img_handle();
            disp(obj)
        end
        
        function obj = initialize(obj, x, y)
            % Place mines
            found = 0;
            while ~found
                mines = reshape(...
                    randperm(obj.x_len * obj.y_len), ...
                    obj.x_len, ...
                    obj.y_len);
                mine_mask = mines > obj.x_len*obj.y_len - obj.num_mines;
                if ~mine_mask(x, y)
                    found = 1;
                end
            end
            
            % Write numbers
            obj.solution = conv2(...
                double(mine_mask), ...
                [1 1 1; 1 1 1; 1 1 1], ...
                'same');
            obj.solution(mine_mask) = 9;
            
            obj.initialized = 1;
        end
        
        function obj = reveal(obj, x, y)
            obj = open(obj, x - 1, y    );
            obj = open(obj, x + 1, y    );
            obj = open(obj, x    , y - 1);
            obj = open(obj, x    , y + 1);
            
            obj = open(obj, x - 1, y + 1);
            obj = open(obj, x - 1, y - 1);
            obj = open(obj, x + 1, y + 1);
            obj = open(obj, x + 1, y - 1);
        end
        
        function obj = open(obj, x, y)
            bound_check = x < 1 || y < 1 || ...
                x > obj.x_len || y > obj.y_len;
            if bound_check
                return
            end
            
            switch obj.state
                case 'state_happy'
                    if strcmp(obj.show{x, y}, 'flag')
                        image(16*x, 16*y, get(obj.fh, 'flag'))
                        return
                    elseif ~strcmp(obj.show{x, y}, 'uncvr')
                        image(16*x, 16*y, get(obj.fh, obj.show{x, y}))
                        return
                    end
                        
                    
                    switch obj.solution(x, y)
                        case 0
                            obj.show{x, y} = 'img_0';
                            obj = reveal(obj, x, y);
                        case 1
                            obj.show{x, y} = 'img_1';
                        case 2
                            obj.show{x, y} = 'img_2';
                        case 3
                            obj.show{x, y} = 'img_3';
                        case 4
                            obj.show{x, y} = 'img_4';
                        case 5
                            obj.show{x, y} = 'img_5';
                        case 6
                            obj.show{x, y} = 'img_6';
                        case 7
                            obj.show{x, y} = 'img_7';
                        case 8
                            obj.show{x, y} = 'img_8';
                        case 9
                            obj.show{x, y} = 'boom';
                            obj.state = 'state_dead';
                    end
                case 'state_dead'
                    if obj.solution(x, y) == 9 && ...
                       ~strcmp(obj.show{x, y}, 'boom')
                        
                        obj.show{x, y} = 'mine';
                        
                    elseif strcmp(obj.show{x, y}, 'flag')
                        obj.show{x, y} = 'miss_flag';
                    end
                case 'state_win'
                    if obj.solution(x, y) == 9
                        obj.show{x, y} = 'win_mine';
                    end
            end
            
            image(16*x, 16*y, get(obj.fh, obj.show{x, y}))
        end
        
        function disp(obj) %#ok<MCHV2>
            img_flag = get(obj.fh, 'flag');
            img_uncvr = get(obj.fh, 'uncvr');
            img_click = get(obj.fh, 'click');
            img_state_happy = get(obj.fh, 'state_happy');
            img_state_click = get(obj.fh, 'state_click');
            img_state_dead  = get(obj.fh, 'state_dead');
            img_state_win   = get(obj.fh, 'state_win');
            
            set(gcf, ...
                'name', 'Minesweeper 2.0', ...
                'Position', [obj.x_pos obj.y_pos ...
                32*(obj.x_len+2) 32*(obj.y_len+2)])
            hold on
            grid = repmat(img_uncvr, [obj.y_len obj.x_len 1]);
            image(16, 16, grid)
            
            image(8*obj.x_len, -32, img_state_happy)
            
            set(gca, ...
                'Units', 'pixels', ...
                'DataAspectRatio', [1 1 1], ...
                'Position', [32 32 32*obj.x_len 32*obj.y_len], ...
                'XLim', [0 16*(obj.x_len+1)], ...
                'YLim', [-48 16*(obj.y_len+1)], ...
                'YDir', 'Reverse', ...
                'Visible', 'off')
            set(gcf, ...
                'MenuBar', 'none', ...
                'Renderer', 'OpenGL', ...
                'RendererMode', 'manual', ...
                'WindowButtonDownFcn', @click)
            axis equal
            
            function click(src, evnt) %#ok<INUSD>
                set(gcf, ...
                    'WindowButtonMotionFcn', @motion, ...
                    'WindowButtonUpFcn', @up)
                
                pt = get(gca, 'CurrentPoint');
                x = floor(pt(1, 1)/16);
                y = floor(pt(1, 2)/16);
                x_prev = x;
                y_prev = y;

                if (x >= obj.x_len/2) && (x <= obj.x_len/2 + 1) && ...
                   (y >= -2) && (y <= -1)

                    clf
                    set(gcf, 'WindowButtonMotionFcn', '')
                    set(gcf, 'WindowButtonUpFcn', '')
                    pos = get(gcf, 'Position');
                    obj.x_pos = pos(1);
                    obj.y_pos = pos(2);
                    minesweeper(obj);
                    return
                end

                if x < 1 || y < 1 || ...
                   x > obj.x_len || y > obj.y_len

                    set(gcf, 'WindowButtonMotionFcn', '')
                    set(gcf, 'WindowButtonUpFcn', '')
                    return
                end

                if any(strcmp(obj.state, {'state_win', 'state_dead'}))
                    set(gcf, ...
                        'WindowButtonMotionFcn', '', ...
                        'WindowButtonUpFcn', '')
                    return
                end

                click_type = get(gcf, 'SelectionType');
                if ~strcmp(obj.show{x, y}, 'flag') && ...
                        ~strcmp(click_type, 'open')
                    
                    image(8*obj.x_len, -32, img_state_click)
                    image(x*16, y*16, img_click)
                end

                function motion(src, evnt) %#ok<INUSD>
                    pt = get(gca, 'CurrentPoint');
                    x = floor(pt(1, 1)/16);
                    y = floor(pt(1, 2)/16);

                    if (x >= 1 && x < obj.x_len+1) && ...
                       (y >= 1 && y < obj.y_len+1)

                        if (x_prev ~= x) || (y_prev ~= y)
                            image(x_prev*16, y_prev*16, ...
                                get(obj.fh, obj.show{x_prev, y_prev}))

                            if ~strcmp(obj.show{x, y}, 'flag')
                                image(x*16, y*16, img_click)
                            end
                        end
                        x_prev = x;
                        y_prev = y;
                    end
                end

                function up(src, evnt) %#ok<INUSD>
                    if (x >= 1 && x <= obj.x_len) && ...
                       (y >= 1 && y <= obj.y_len)

                        if ~obj.initialized
                            obj = initialize(obj, x, y);
                        end

                        if strcmp(click_type, 'normal')
                            obj = open(obj, x, y);
                            image(x*16, y*16, ...
                                get(obj.fh, obj.show{x, y}))
                        elseif strcmp(click_type, 'alt')
                            if strcmp(obj.show{x, y}, 'uncvr')
                                obj.show{x, y} = 'flag';
                            elseif strcmp(obj.show{x, y}, 'flag')
                                obj.show{x, y} = 'uncvr';
                            end
                            image(x*16, y*16, get(obj.fh, obj.show{x, y}))
                        elseif strcmp(click_type, 'open')
                            if ~strcmp(obj.show{x, y}, 'flag')
                                obj = reveal(obj, x, y);
                                image(x_prev*16, y_prev*16, ...
                                    get(obj.fh, obj.show{x, y}))
                            end
                        end
                        
                        switch obj.show{x, y}
                            case 'uncvr'
                                image(16*x, 16*y, img_uncvr)
                            case 'flag'
                                image(16*x, 16*y, img_flag)
                            otherwise
                                image(x*16, y*16, ...
                                    get(obj.fh, obj.show{x, y}))
                        end
                        
                        % Check for the win
                        if sum(sum(strcmp(obj.show, 'uncvr'))) + ...
                                sum(sum(strcmp(obj.show, 'flag'))) == ...
                                obj.num_mines
                            obj.state = 'state_win';
                        end
                        
                        state_img = img_state_happy;
                        switch obj.state
                            case 'state_win'
                                for jj = 1:obj.y_len
                                    for ii = 1:obj.x_len
                                        obj = open(obj, ii, jj);
                                    end
                                end
                                state_img = img_state_win;
                                
                            case 'state_dead'
                                for jj = 1:obj.y_len
                                    for ii = 1:obj.x_len
                                        if obj.solution(ii, jj) == 9 ...
                                           || strcmp(obj.show{ii, jj}, ...
                                           'flag')
                                            
                                            obj = open(obj, ii, jj);
                                        end
                                    end
                                end
                                state_img = img_state_dead;
                        end
                        
                        image(8*obj.x_len, -32, state_img)
                    else
                        image(x_prev*16, y_prev*16, ...
                            get(obj.fh, obj.show{x_prev, y_prev}))
                    end

                    set(gcf, 'WindowButtonMotionFcn', '')
                end
            end
            
            drawnow
        end
    end
end
Advertisements