matlab扫雷小游戏
游戏逻辑与 Windows 经典扫雷一致:左键翻开、右键插旗/取消插旗,自动递归展开空白区域,实时显示剩余雷数。
(界面基于 uifigure
+ uigridlayout
,MATLAB R2019b 及以上版本可直接运行;旧版可用 figure
+uicontrol
的兼容版本见文末)
一、运行方式
minesweeper(9,9,10); % 9×9、10颗雷
二、完整源码
function minesweeper(m,n,k)
% m 行、n 列、共 k 颗雷
%
% 用法示例:
% minesweeper(9,9,10) % 初级
% minesweeper(16,16,40) % 中级
% minesweeper(16,30,99) % 高级% ---------- 1. 参数初始化 ----------
if nargin<3, k=floor(m*n*0.15); end % 默认15%雷
if k>m*n-9, error('雷数太多!'); end
rng('shuffle');% 逻辑矩阵: -1 表示雷,>=0 表示周围雷数
board = zeros(m,n);
mineIdx = randperm(m*n,k);
board(mineIdx) = -1;% 计算每个非雷格子的周围雷数
for i = 1:mfor j = 1:nif board(i,j) ~= -1ii = max(i-1,1):min(i+1,m);jj = max(j-1,1):min(j+1,n);board(i,j) = sum(board(ii,jj)==-1);endend
end% 状态矩阵:0=未翻开,1=已翻开,2=插旗
state = zeros(m,n);
gameOver = false;
win = false;% ---------- 2. GUI ----------
fig = uifigure('Name','MATLAB 扫雷','Resize','off', ...'Position',[500 300 35*n+25 35*m+80]);
uigrid = uigridlayout(fig,[m+1 n],'RowSpacing',1,'ColumnSpacing',1);
uigrid.RowHeight = repmat({'1x'},m,1);
uigrid.ColumnWidth = repmat({'1x'},n,1);% 顶部信息
lbFlag = uilabel(fig,'Position',[10 35*m+30 150 25],'FontSize',12);
lbFlag.Text = sprintf('剩余雷数: %d',k);% 按钮网格
btn = gobjects(m,n);
for i = 1:mfor j = 1:nbtn(i,j) = uibutton(uigrid,'Text',' ','FontWeight','bold', ...'BackgroundColor',[.9 .9 .9], ...'FontSize',10,'Tag',[num2str(i),'-',num2str(j)]);btn(i,j).ButtonPushedFcn = @(src,evt) leftClick(src);btn(i,j).ContextMenu = contextMenuCreate(btn(i,j));end
end% ---------- 3. 事件函数 ----------function leftClick(src)if gameOver || win, return; end[i,j] = strtok(src.Tag,'-');i = str2double(i); j = str2double(j(2:end));if state(i,j)==2, return; end % 插旗格不响应左键reveal(i,j);checkWin;endfunction rightClick(src,~)if gameOver || win, return; end[i,j] = strtok(src.Tag,'-');i = str2double(i); j = str2double(j(2:end));if state(i,j)==1, return; end % 已翻开不能插旗if state(i,j)==0 % 未翻开→插旗state(i,j)=2;src.Text = '⚑';src.BackgroundColor = [1 .6 .6];elseif state(i,j)==2 % 插旗→取消state(i,j)=0;src.Text = ' ';src.BackgroundColor = [.9 .9 .9];endlbFlag.Text = sprintf('剩余雷数: %d',k-sum(state==2,'all'));endfunction reveal(i,j)if i<1 || i>m || j<1 || j>n || state(i,j)==1, return; endstate(i,j)=1;if board(i,j)==-1 % 踩到雷showAllMines;uialert(fig,'BOOM! 你踩到雷了','游戏结束');gameOver = true;return;end% 更新按钮文字和颜色btn(i,j).Text = num2str(board(i,j));btn(i,j).BackgroundColor = [1 1 1];if board(i,j)==0btn(i,j).Text = ' ';% 递归展开空白for di = -1:1, for dj = -1:1reveal(i+di,j+dj);end, endendendfunction showAllMinesfor i = 1:mfor j = 1:nif board(i,j)==-1btn(i,j).Text = '💣';btn(i,j).BackgroundColor = [1 0 0];endendendendfunction checkWinif gameOver, return; endif all(state(board~=-1)==1) % 所有非雷已翻开win = true;uialert(fig,'恭喜你,扫雷成功!','胜利');endendfunction cMenu = contextMenuCreate(btnObj)cMenu = uicontextmenu(fig);uimenu(cMenu,'Text','插旗/取消','Separator','on', ...'MenuSelectedFcn', @(src,evt) rightClick(btnObj,src));end
end
三、旧版 MATLAB 兼容版(无 uifigure
)
如果 MATLAB 版本 < R2019b,用传统 figure
+ uicontrol
实现,可替换“GUI”段为(其余逻辑不变):
fig = figure('Name','MATLAB 扫雷','NumberTitle','off','MenuBar','none', ...'Resize','off','Position',[500 300 35*n+25 35*m+45]);
btn = zeros(m,n);
for i = 1:mfor j = 1:nh = uicontrol('Style','pushbutton','String',' ','Units','pixels', ...'Position',[35*(j-1)+5 35*(m-i)+5 30 30], ...'FontWeight','bold','FontSize',10, ...'Callback',{@leftClick,i,j});btn(i,j) = h;end
end
% 其余回调函数需对应改为处理句柄,逻辑完全一致,这里略。
参考代码 matlab扫雷小游戏 www.youwenfan.com/contentcsf/22784.html
四、扩展玩法
- 计时器:使用
timer
对象记录用时并在标题栏实时刷新。 - 难度选择:在
uifigure
顶部加uidropdown
供用户选初级/中级/高级。 - 自定义雷区:加两个
uieditfield
输入行列与雷数,点击“开始”后动态创建按钮。 - AI 自动扫雷:把已翻开格子的数字与周围未知格关系建模为约束满足问题,用 MATLAB 的
intlinprog
或逻辑求解器推断必开/必旗格。