惯例声明:本人没有相关的工程应用经验,只是纯粹对相关算法感兴趣才写此博客。所以如果有错误,欢迎在评论区指正,不胜感激。本文主要关注于算法的实现,对于实际应用等问题本人没有任何经验,所以也不再涉及。
MarkovJunior号称是一个概率编程语言,基于马尔科夫算法来生成各种图形结构。
本文根据其思想,利用MATLAB实现了其中的部分示例。
如果对其它迷宫算法感兴趣的,可参见:
利用matlab创建与解决迷宫[深度优先、Prim、递归分割、Wilson]
https://blog.csdn.net/weixin_42943114/article/details/104172146
本文参考:
[1] mxgmn / MarkovJunio - github
https://github.com/mxgmn/MarkovJunior
[2] 1行代码生成随机迷宫,这个概率编程语言登GitHub热榜,作者曾开发著名WFC算法
https://zhuanlan.zhihu.com/p/525217024?utm_id=0
MarkovJunior会根据一系列的规则,来进行图形的生成。常见规则为替换,比如将某个像素点替换成另一个像素点,或者将某个图案(像素块)替换为另一个大小相同图案。
这种像素的替换是随机的,所以生成的图案通常是随机图案,具有无限的可能。
不同规则之间还有各种逻辑关系,常见的为:
1当执行完上面规则后,再执行下一条规则
2当上面规则无解时,执行下一条规则
3与其它几条规则一起,随机选择一条规则进行执行
有了这些规则,就可以生成复杂而又实用的随机图案。
下图给出了用MarkovJunior生成的一些示例:
实际MarkovJunior的语言给的非常的简洁,但是核心部分查找像素、替换像素、以及逻辑和循环部分。这些功能在matlab中都可以
首先搞两个简单的迷宫生成算法。
深度优先迷宫算法的思路为规划一条路到头,如果生成不下去了,再后退开辟另一条路。和下面2.2节介绍的广度优先算法比,由于其追求单条路径的深度,所以通常每条路径都很长,分岔较少。
对于MarkovJunior语言,深度优先迷宫算法规则只有两个:一个是前进,将迷宫中的’兰蓝蓝’替换为’绿绿兰’;另一个是后退,将迷宫中的’绿绿兰’替换为绿黄黄’。
比如下图,初始定义一个绿点,然后根据规则1就可以一直延伸下去路径。
但是如果遇到走不通的地方,就需要根据规则2进行后退。等到后退到可以执行规则1时,便继续执行规则1。
最终当迷宫全部被黄色填满,规则1和规则2都无法执行时,便结束。最终生成的迷宫如下:
对应的MATLAB代码见下。其实实际代码很短,大部分都是逻辑的while、if之类的代码和绘图代码。主要的查找和替换代码打包成function函数即可。
clear
clc
close all
%MarkovJunior算法-模拟深度优先算法
%初始迷宫
X=20;
Y=20;
Maze=uint8(zeros(2*Y+1,2*X+1));
SizeMaze=size(Maze);
Maze(2,2)=1;%1代表不是墙wall,可以行走的区域room
%1优先:检测迷宫中的[1,0,0],替换为[2,2,1]。2次优先:检测迷宫中的[1,2,2],替换为[3,3,1]
figure(1)
imagesc(Maze)
caxis([0,3])
axis off
pause(0.01)
while true
%检测
%生成4个方向,然后随机排序4个,匹配检测
Block1=[1,0,0];Block2=[2,2,1];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
Block1=[1,2,2];Block2=[3,3,1];
[indx_k,Block_Dir]=FindBlock2(Maze,[1,2,2]);
if isempty(indx_k)
break
end
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
%绘图
figure(1)
imagesc(Maze)
caxis([0,3])
axis off
pause(0.01)
end
%后置函数
function Mat=ReplaceBlock(Mat,indx,Block,Block_Dir)
%根据索引替换
Block_k=rot90(Block,Block_Dir);
Size_Mat=size(Mat);
Size_Block=size(Block_k);
[Mr,Mc]=ind2sub(Size_Mat,indx);
Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1)=Block_k;
end
function [indx_Rand,Block_Dir]=FindBlock2(Mat,Block)
%根据Block找到Mat矩阵中的索引。
%2维空间中Block会随机旋转
%找到所有,然后随机选择一个。
Block1=Block;
Block2=rot90(Block,1);
Block3=rot90(Block,2);
Block4=rot90(Block,3);
indx1=FindBlockSame(Mat,Block1);
indx2=FindBlockSame(Mat,Block2);
indx3=FindBlockSame(Mat,Block3);
indx4=FindBlockSame(Mat,Block4);
%随机一个
RandIndx=[[indx1,0*ones(size(indx1,1),1)];
[indx2,1*ones(size(indx2,1),1)];
[indx3,2*ones(size(indx3,1),1)];
[indx4,3*ones(size(indx4,1),1)];
];
N_Rand=size(RandIndx,1);
if isempty(RandIndx)
indx_Rand=[];Block_Dir=[];
else
Rand_i=randi(N_Rand,1);
indx_Rand=RandIndx(Rand_i,1);
Block_Dir=RandIndx(Rand_i,2);
end
end
function indx=FindBlockSame(Mat,Block)
%查找Block相同部分的Mat,所在位置
Size_Mat=size(Mat);
Size_Block=size(Block);
Block_indx=zeros(Size_Mat);
for kr=1:Size_Mat(1)-Size_Block(1)+1
for kc=1:Size_Mat(2)-Size_Block(2)+1
Mat_k=Mat(kr:kr+Size_Block(1)-1,kc:kc+Size_Block(2)-1);
IsSame=isequal(Mat_k,Block);
if IsSame
Block_indx(kr,kc)=1;
end
end
end
indx=find(Block_indx);
end
广度优先则是在路径规划时,在各个可能的分岔口都尽量分岔,最终用分岔填满整个空间。和深度优先迷宫算法相比,其分岔多,但是每个分岔路径都很短,迷宫最终解法的路径长度也通常较短。
对于MarkovJunior语言,广度优先迷宫算法规则只有一个,就是前进并添加节点,将’兰蓝蓝’替换为’兰黄兰’。
最终生成的迷宫如下:
MATLAB代码如下:
clear
clc
close all
%MarkovJunior算法-模拟广度优先算法
%初始迷宫
X=15;
Y=15;
Maze=uint8(zeros(2*Y+1,2*X+1));
SizeMaze=size(Maze);
Maze(2,2)=1;%1代表不是墙wall,可以行走的区域room
figure(1)
imagesc(Maze)
caxis([0,3])
axis off
pause(0.01)
%1优先:检测迷宫中的[1,0,0],替换为[1,2,1]。
while true
%检测
%生成4个方向,然后随机排序4个,匹配检测
Block1=[1,0,0];Block2=[1,2,1];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,2])
pause(0.01)
end
Maze(Maze>1)=1;
figure(1)
imagesc(Maze)
caxis([0,2])
pause(0.01)
figure(2)
Maze2=Maze;Maze(Maze>1)=1;
imagesc(Maze)
%% 后置函数
function Mat=ReplaceBlock(Mat,indx,Block,Block_Dir)
%根据索引替换
Block_k=rot90(Block,Block_Dir);
Size_Mat=size(Mat);
Size_Block=size(Block_k);
[Mr,Mc]=ind2sub(Size_Mat,indx);
Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1)=Block_k;
end
function [indx_Rand,Block_Dir]=FindBlock2(Mat,Block)
%根据Block找到Mat矩阵中的索引。
%2维空间中Block会随机旋转
%找到所有,然后随机选择一个。
Block1=Block;
Block2=rot90(Block,1);
Block3=rot90(Block,2);
Block4=rot90(Block,3);
indx1=FindBlockSame(Mat,Block1);
indx2=FindBlockSame(Mat,Block2);
indx3=FindBlockSame(Mat,Block3);
indx4=FindBlockSame(Mat,Block4);
%随机一个
RandIndx=[[indx1,0*ones(size(indx1,1),1)];
[indx2,1*ones(size(indx2,1),1)];
[indx3,2*ones(size(indx3,1),1)];
[indx4,3*ones(size(indx4,1),1)];
];
N_Rand=size(RandIndx,1);
if isempty(RandIndx)
indx_Rand=[];Block_Dir=[];
else
Rand_i=randi(N_Rand,1);
indx_Rand=RandIndx(Rand_i,1);
Block_Dir=RandIndx(Rand_i,2);
end
end
function indx=FindBlockSame(Mat,Block)
%查找Block相同部分的Mat,所在位置
Size_Mat=size(Mat);
Size_Block=size(Block);
Block_indx=zeros(Size_Mat);
for kr=1:Size_Mat(1)-Size_Block(1)+1
for kc=1:Size_Mat(2)-Size_Block(2)+1
Mat_k=Mat(kr:kr+Size_Block(1)-1,kc:kc+Size_Block(2)-1);
IsSame=isequal(Mat_k,Block);
if IsSame
Block_indx(kr,kc)=1;
end
end
end
indx=find(Block_indx);
end
地牢地图的MarkovJunior规则见下图,图左侧为生成规则表,代表每个规则的先后顺序以及执行条件。
利用MATLAB最终实现的效果如下:
MATLAB代码见下面。这里由于规则非常多,所以对于的代码明显要比之前更长。
clear
clc
close all
rng(12)
%MarkovJunior算法-NystromDungeon
%初始迷宫
X=20;
Y=20;
Maze=uint8(zeros(2*Y+1,2*X+1));
SizeMaze=size(Maze);
Maze(2,2)=1;%1代表不是墙wall,可以行走的区域room
colormap([0,0,0;0.4,0.1,0.1;1,1,1;0,1,0;1,0,0])
figure(1)
imagesc(Maze)
caxis([0,4])
axis off
pause(0.01)
%1检测迷宫中的[1,0,0],替换为[1,2,1]
while true
Block1=[1,0,0];Block2=[1,0,1];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
%2生成房间
while true
Block1=zeros(9,11);Block1(1:2:end,1:2:end)=1;
Block2=2*ones(9,11);
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
end
%3生成路径
%[4,0,1]替换为[3,3,4];[3,3,4]替换为[4,2,2];[1]替换为[4]
t=0;
while true
Block1=[4,0,1];Block2=[3,3,4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
Block1=[3,3,4];Block2=[4,2,2];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
Block1=[1];Block2=[4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
end
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
t=t+1;
end
%4删除指引点
Block1=[4];Block2=[3];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
while true
Block1=[4];Block2=[2];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
end
%5打上格点
while true
Block1=[3,2,2];Block2=[3,2,3];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
Block1=[3,0,2];Block2=[3,2,3];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
end
%6开放一些路径
for k=1:3
Block1=[3,0,3];Block2=[3,2,3];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
end
while true
Block1=[3];Block2=[2];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
%7删除分支小路
while true
Block1=[0,0,0;0,2,0];Block2=[0,0,0;0,0,0];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
end
figure(1)
imagesc(Maze)
caxis([0,4])
pause(0.01)
figure(2)
Maze2=Maze;Maze(Maze>1)=1;
imagesc(Maze)
%% 后置函数
function Mat=ReplaceBlock(Mat,indx,Block,Block_Dir)
%根据索引替换
Block_k=rot90(Block,Block_Dir);
Size_Mat=size(Mat);
Size_Block=size(Block_k);
[Mr,Mc]=ind2sub(Size_Mat,indx);
Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1)=Block_k;
end
function [indx_Rand,Block_Dir]=FindBlock2(Mat,Block)
%根据Block找到Mat矩阵中的索引。
%2维空间中Block会随机旋转
%找到所有,然后随机选择一个。
Block1=Block;
Block2=rot90(Block,1);
Block3=rot90(Block,2);
Block4=rot90(Block,3);
indx1=FindBlockSame(Mat,Block1);
indx2=FindBlockSame(Mat,Block2);
indx3=FindBlockSame(Mat,Block3);
indx4=FindBlockSame(Mat,Block4);
%随机一个
RandIndx=[[indx1,0*ones(size(indx1,1),1)];
[indx2,1*ones(size(indx2,1),1)];
[indx3,2*ones(size(indx3,1),1)];
[indx4,3*ones(size(indx4,1),1)];
];
N_Rand=size(RandIndx,1);
if isempty(RandIndx)
indx_Rand=[];Block_Dir=[];
else
Rand_i=randi(N_Rand,1);
indx_Rand=RandIndx(Rand_i,1);
Block_Dir=RandIndx(Rand_i,2);
end
end
function indx=FindBlockSame(Mat,Block)
%查找Block相同部分的Mat,所在位置
Size_Mat=size(Mat);
Size_Block=size(Block);
Block_indx=zeros(Size_Mat);
for kr=1:Size_Mat(1)-Size_Block(1)+1
for kc=1:Size_Mat(2)-Size_Block(2)+1
Mat_k=Mat(kr:kr+Size_Block(1)-1,kc:kc+Size_Block(2)-1);
IsSame=isequal(Mat_k,Block);
if IsSame
Block_indx(kr,kc)=1;
end
end
end
indx=find(Block_indx);
end
MarkovJunior不仅可以生成静态的地图,还可以运行贪吃蛇,同样规则并不太多,规则见下图,图左侧为生成规则表,代表每个规则的先后顺序以及执行条件。
利用MATLAB最终实现的效果如下:
实现代码见下。
这里其实不是所有随机情况都能成功吃掉所有食物,因为每次蛇的运行路径是随机的,不能控制,所以也存在自己把自己围到角里无法下一步的情况。
clear
clc
close all
%MarkovJunior算法-贪吃蛇
%有概率死掉,不是必胜方法
rng(3)
X=9;
Y=9;
Maze=uint8(zeros(2*Y+1,2*X+1));
SizeMaze=size(Maze);
Maze(2,2)=1;%1代表空Room
Maze(2*randi([2,X-1],1),2*randi([2,Y-1],1))=2;%2代表得分点
mcp=[0,0,0;0.7,0.7,0.3;1,1,1;0,1,0;1,0,0;1,0,1];%黑灰白绿红紫
colormap(mcp);Ncolor=size(mcp,1);
figure(1)
imagesc(Maze)
caxis([0,3])
axis off
pause(0.01)
%1生成网格点。检测迷宫中的[1,0,0],替换为[1,2,1]。
while true
Block1=[1,0,0];Block2=[1,0,1];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
figure(1)
imagesc(Maze);caxis([0,Ncolor-1]);pause(0.01)
%2生成蛇。
while true
Block1=[2,0,1];Block2=[5,3,4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
for k=1:2
Block1=[4,0,1];Block2=[3,3,4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Maze);caxis([0,Ncolor-1]);pause(0.01)
end
%2生成计分点
for k=1:10
Block1=[1];Block2=[2];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
end
%然后按照序号进行替换
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
figure(1)
imagesc(Maze);caxis([0,Ncolor-1]);pause(0.01)
%3贪吃蛇运动
for k=1:100
%吃计分点
Block1=[4,0,2];Block2=[3,3,4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
%向前进一格
Block1=[4,0,1];Block2=[3,3,4];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
if isempty(indx_k)
break
else
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
Block1=[5,3,3];Block2=[1,0,5];
[indx_k,Block_Dir]=FindBlock2(Maze,Block1);
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
else
Maze=ReplaceBlock(Maze,indx_k,Block2,Block_Dir);
end
figure(1)
imagesc(Maze);caxis([0,Ncolor-1]);pause(0.01)
end
figure(1)
imagesc(Maze)
caxis([0,Ncolor-1])
pause(0.01)
%% 后置函数
function Mat=ReplaceBlock(Mat,indx,Block,Block_Dir)
%根据索引替换
Block_k=rot90(Block,Block_Dir);
Size_Mat=size(Mat);
Size_Block=size(Block_k);
[Mr,Mc]=ind2sub(Size_Mat,indx);
Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1)=Block_k;
end
function [indx_Rand,Block_Dir]=FindBlock2(Mat,Block)
%根据Block找到Mat矩阵中的索引。
%2维空间中Block会随机旋转
%找到所有,然后随机选择一个。
Block1=Block;
Block2=rot90(Block,1);
Block3=rot90(Block,2);
Block4=rot90(Block,3);
indx1=FindBlockSame(Mat,Block1);
indx2=FindBlockSame(Mat,Block2);
indx3=FindBlockSame(Mat,Block3);
indx4=FindBlockSame(Mat,Block4);
%随机一个
RandIndx=[[indx1,0*ones(size(indx1,1),1)];
[indx2,1*ones(size(indx2,1),1)];
[indx3,2*ones(size(indx3,1),1)];
[indx4,3*ones(size(indx4,1),1)];
];
N_Rand=size(RandIndx,1);
if isempty(RandIndx)
indx_Rand=[];Block_Dir=[];
else
Rand_i=randi(N_Rand,1);
indx_Rand=RandIndx(Rand_i,1);
Block_Dir=RandIndx(Rand_i,2);
end
end
function indx=FindBlockSame(Mat,Block)
%查找Block相同部分的Mat,所在位置
Size_Mat=size(Mat);
Size_Block=size(Block);
Block_indx=zeros(Size_Mat);
for kr=1:Size_Mat(1)-Size_Block(1)+1
for kc=1:Size_Mat(2)-Size_Block(2)+1
Mat_k=Mat(kr:kr+Size_Block(1)-1,kc:kc+Size_Block(2)-1);
IsSame=isequal(Mat_k,Block);
if IsSame
Block_indx(kr,kc)=1;
end
end
end
indx=find(Block_indx);
end
地牢地图的MarkovJunior规则见下图,图左侧为生成规则表,代表每个规则的先后顺序以及执行条件。
这里由于查找和替换的图形不是正方的矩形,而是其它形状的图形,所以后置函数和前面的代码有所改动。这里引入了nan来删除非矩形形状的图形的非像素点,然后用isequaln来进行判断。
而且这里和之前不同,也不涉及匹配图形的旋转,所以关于旋转角度也做了一些函数修改。
最终实现代码见下。
clear
clc
close all
%MarkovJunior算法-花
rng(3)
X=30;
Y=60;
Draw=zeros(Y,X);
SizeMaze=size(Draw);
Draw(Y-1:Y,:)=1;%1代表土地
Draw(Y-2,:)=2;%2代表绿地
mcp=[0.39,0.98,0.96;0.65,0.50,0.25;
0.67,0.99,0.38;0.09,0.59,0.17;
0.9,0,0;1,1,0];%蓝褐绿墨红黄
colormap(mcp);Ncolor=size(mcp,1);
figure(1)
imagesc(Draw)
caxis([0,5])
set(gcf,'Position',[713,342,335,420])
axis off
pause(0.01)
while true
%生成叶子,生成方式由下面四种方式共同决定
%Rand_k=randi([1,4],1);
Rand_k = randperm(5);
for k1=1:5
switch Rand_k(k1)
case 1 %直线生长
Block1=[0,0,0;0,0,0;0,4,0];
Block2=[nan,nan,nan;nan,4,nan;nan,3,nan];
case 2 %斜向生长
Block1=[0,0,0;0,0,0;0,0,0;4,0,0;nan,nan,0];
Block2=[nan,nan,nan;nan,4,nan;nan,3,nan;3,3,nan;nan,nan,nan];
case 3 %斜向生长
Block1=[0,0,0;0,0,0;0,0,0;0,0,4;0,nan,nan];
Block2=[nan,nan,nan;nan,4,nan;nan,3,nan;nan,3,3;nan,nan,nan];
case 4 %双斜向生长
Block1=[0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,4,0,0;0,nan,nan,nan,0];
Block2=[0,0,0,0,0;0,4,0,4,0;0,3,0,3,0;0,3,3,3,0;nan,nan,nan,nan,nan];
case 5 %开花
Block1=[0,0,0;0,4,0;0,3,0;0,3,0];
Block2=[0,5,0;5,3,5;0,5,0;nan,nan,nan];
end
[indx_k,Block_Dir]=FindBlock2(Draw,Block1);
if ~isempty(indx_k)
break
end
end
%如果没有树枝,就再从地里长一根
if isempty(indx_k)
Block1=[0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;2,2,2,2,2;1,1,1,1,1];
Block2=[0,0,0,0,0;0,0,4,0,0;0,0,3,0,0;2,2,3,2,2;1,1,3,1,1];
[indx_k,Block_Dir]=FindBlock2(Draw,Block1);
if isempty(indx_k)
break
end
end
%然后按照序号进行替换
Draw=ReplaceBlock(Draw,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Draw)
caxis([0,Ncolor-1]);set(gcf,'Position',[713,342,335,420]);axis off;pause(0.01)
end
%其余的都开花
while true
Block1=[nan,nan,nan;nan,4,nan;nan,nan,nan];
Block2=[nan,5,nan;5,3,5;nan,5,nan];
[indx_k,Block_Dir]=FindBlock2(Draw,Block1);
if isempty(indx_k)
break
end
Draw=ReplaceBlock(Draw,indx_k,Block2,Block_Dir);
figure(1)
imagesc(Draw);caxis([0,Ncolor-1]);axis off;pause(0.01)
end
figure(1)
imagesc(Draw);caxis([0,Ncolor-1]);axis off;pause(0.01)
%% 后置函数 和上面几个后置函数相比有所改动
function Mat=ReplaceBlock(Mat,indx,Block,Block_Dir)
%根据索引替换
Block_k=rot90(Block,Block_Dir);
Size_Mat=size(Mat);
Size_Block=size(Block_k);
[Mr,Mc]=ind2sub(Size_Mat,indx);
Mat_k=Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1);
%如果Block含有nan,则不替换
indx_nan=~isnan(Block_k);
Mat_k(indx_nan)=Block_k(indx_nan);
Mat(Mr:Mr+Size_Block(1)-1,Mc:Mc+Size_Block(2)-1)=Mat_k;
end
function [indx_Rand,Block_Dir]=FindBlock2(Mat,Block)
%根据Block找到Mat矩阵中的索引。
%2维空间中Block会随机旋转
%找到所有,然后随机选择一个。
%这里不需要旋转了,改动了一下
Block1=Block;
indx1=FindBlockSame(Mat,Block1);
RandIndx=[indx1,0*ones(size(indx1,1),1)];
N_Rand=size(RandIndx,1);
if isempty(RandIndx)
indx_Rand=[];Block_Dir=[];
else
Rand_i=randi(N_Rand,1);
indx_Rand=RandIndx(Rand_i,1);
Block_Dir=RandIndx(Rand_i,2);
end
end
function indx=FindBlockSame(Mat,Block)
%查找Block相同部分的Mat,所在位置
Size_Mat=size(Mat);
Size_Block=size(Block);
Block_indx=zeros(Size_Mat);
for kr=1:Size_Mat(1)-Size_Block(1)+1
for kc=1:Size_Mat(2)-Size_Block(2)+1
Mat_k=Mat(kr:kr+Size_Block(1)-1,kc:kc+Size_Block(2)-1);
Mat_k(isnan(Block))=nan;%如果输入的Block里有nan的,也把对应比对位置替换为nan
IsSame=isequaln(Mat_k,Block);
if IsSame
Block_indx(kr,kc)=1;
end
end
end
indx=find(Block_indx);
end