利用MarkovJunior方法生成迷宫和图形的MATLAB演示[迷宫生成、贪吃蛇、地图生成、图案生成]

利用MarkovJunior方法生成迷宫和图形的MATLAB演示[迷宫生成、贪吃蛇、地图生成、图案生成]

  • 0 前言
  • 1 介绍MarkovJunior
  • 2 迷宫生成
    • 2.1 深度优先迷宫生成
    • 2.2 广度优先迷宫生成
  • 3 其它生成图案
    • 3.1 地牢地图
    • 3.2 贪吃蛇
    • 3.3 植物花

惯例声明:本人没有相关的工程应用经验,只是纯粹对相关算法感兴趣才写此博客。所以如果有错误,欢迎在评论区指正,不胜感激。本文主要关注于算法的实现,对于实际应用等问题本人没有任何经验,所以也不再涉及。

0 前言

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

1 介绍MarkovJunior

MarkovJunior会根据一系列的规则,来进行图形的生成。常见规则为替换,比如将某个像素点替换成另一个像素点,或者将某个图案(像素块)替换为另一个大小相同图案。

这种像素的替换是随机的,所以生成的图案通常是随机图案,具有无限的可能。

不同规则之间还有各种逻辑关系,常见的为:
1当执行完上面规则后,再执行下一条规则
2当上面规则无解时,执行下一条规则
3与其它几条规则一起,随机选择一条规则进行执行

有了这些规则,就可以生成复杂而又实用的随机图案。

下图给出了用MarkovJunior生成的一些示例:
请添加图片描述
实际MarkovJunior的语言给的非常的简洁,但是核心部分查找像素、替换像素、以及逻辑和循环部分。这些功能在matlab中都可以

2 迷宫生成

首先搞两个简单的迷宫生成算法。

2.1 深度优先迷宫生成

深度优先迷宫算法的思路为规划一条路到头,如果生成不下去了,再后退开辟另一条路。和下面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

2.2 广度优先迷宫生成

广度优先则是在路径规划时,在各个可能的分岔口都尽量分岔,最终用分岔填满整个空间。和深度优先迷宫算法相比,其分岔多,但是每个分岔路径都很短,迷宫最终解法的路径长度也通常较短。

对于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

3 其它生成图案

3.1 地牢地图

地牢地图的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

3.2 贪吃蛇

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

3.3 植物花

地牢地图的MarkovJunior规则见下图,图左侧为生成规则表,代表每个规则的先后顺序以及执行条件。请添加图片描述

MATLAB运行的效果如下:
请添加图片描述

这里由于查找和替换的图形不是正方的矩形,而是其它形状的图形,所以后置函数和前面的代码有所改动。这里引入了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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/98549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小文智能GPT助手介绍

如何使用小文交互的GPT助手,让AI更加智能,适用更多场景? 在小文智能最新推出的4.0版本,有一个新功能,叫做GPT助手。GPT助手,顾名思义,即在小文智能的场景中,接入ChatGPT&#xff0c…

com.google.guava:guava 组件安全漏洞及健康分析

组件简介 维护者google组织许可证类型Apache-2.0首次发布2010 年 4 月 26 日最新发布时间2023 年 8 月 1 日GitHub Star48189GitHub Fork10716依赖包28,694依赖存储库219,576 Guava 是 Google 的一组核心 Java 库,其中包括新的集合类型(例如 multimap 和…

MongoDB实验——在Java应用程序中操作 MongoDB 数据

在Java应用程序中操作 MongoDB 数据 1. 启动MongoDB Shell 2. 切换到admin数据库,使用root账户 3.开启Eclipse,创建Java Project项目,命名为MongoJava File --> New --> Java Project 4.在MongoJava项目下新建包,包名为mo…

算法——排序

排序 下面的代码会用到宏定义,因为再C中没有swap交换函数,所以对于swap的宏定义代码如下: #define swap(a, b) {\__typeof(a) __a a; a b; b __a;\ } 稳定排序: 1.插入排序: 插入排序会将数组,分位两个部…

C语言每日一练--Day(17)

本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字:数对 截取字符串 💓博主csdn个人主页:小小unico…

Linux——守护进程

简述 不受用户登录、注销影响的进程称为守护进程 特点 后台运行:守护进程在后台默默地执行任务,不与用户交互。它不会向终端输出信息,也不会从终端接收输入。 无终端关联:守护进程通常与任何终端会话(比如SSH会话&…

软件测试/测试开发丨Pytest和Allure报告 学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/26755 Pytest 命名规则 类型规则文件test_开头 或者 _test 结尾类Test 开头方法/函数test_开头注意:测试类中不可以添加__init__构造函数 注…

cvat 安装部署

官网地址: https://github.com/opencv/cvat/tree/masterhttps://github.com/opencv/cvat/tree/master 1.从官网上下载源码地址。 2.配置环境变量 vim /etc/profile source /etc/profile 或者执行: export CVAT_HOSTyour-ip-address 3.执行命令 …

无涯教程-Android - 应用组件

应用程序组件是Android应用程序的基本组成部分,这些组件需要在应用程序清单文件 AndroidManifest.xml 注册,该文件描述了应用程序的每个组件以及它们如何交互。 Android应用程序可以使用以下四个主要组件- Sr.NoComponents & 描述1 Activities 它们…

BMC相关知识

简介 BMC(Baseboard Management Controller),基板管理控制器,普通PC没有,服务器产品必备。BMC是一个独立的系统,只要通电即可运行,服务器无需开机,不依赖其它软硬件,如O…

ChatGPT 总结数据分析的所有知识点

ChatGPT功能非常多,特别是对某个行业,某个方向,某个技术进行总结那是相当专业的。 如下图。 直接用一个指令便总结出来数据分析当中的所有知识点内容。 AIGC ChatGPT ,BI商业智能, 可视化Tableau, PowerBI, FineReport, 数据库Mysql Oracle, Office, Python ,ETL Ex…

打造个人的NAS云存储-通过Nextcloud搭建私有云盘实现公网远程访问

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…

EXCEL中点击单元格,所在行和列都改变颜色

在日常工作中,尤其是办公室工作人群,尝尝需要处理大量的数据,在对数据进行修改时,时长发生看错行的事情,导致数据越改越乱,因此,我常用的一种方法就是选中单元格时,所在行、列标记为…

suricata安装与配置

一、功能介绍 1、概述 suricata来源于经典的nids系统snort,是一套基于网络流量的威胁检测引擎,整合了ids,ips,network security monitoring(NSM)和PCAP processing等功能。 2、IDS功能 通过监听网卡流量并匹配规则引擎进行入侵实时监测和…

【分享】小型园区组网场景

小型园区组网图 在小型园区中,S2700&S3700通常部署在网络的接入层,S5700&S6700通常部署在网络的核心,出口路由器一般选用AR系列路由器。 接入交换机与核心交换机通过Eth-Trunk组网保证可靠性。 每个部门业务划分到一个VLAN中&#…

如何高效进行测试用例评审

1.用例评审的目的 为了减少测试人员执行阶段做无效工作,执行无效case,提交无效缺陷(可以友情提醒研发同学,讲到自己负责的相关模块时,注意下是否存在异议点)为了避免三方(产品、研发、测试&…

使用C语言计算1/1-1/2+1/3-1/4+...+1/99-1/100

观察算式,发现分子都是1,分母从1~100,所以可以使用for循环产生1~100之间的数。 另一个问题是,如何产生正负交替的符号?很简单,这个符号本质上就是往每一项前面乘一个系数:一或者负一。所以只需…

【数据结构练习】单链表OJ题(二)

目录 一、相交链表二、环形链表1三、环形链表2四、链表分割五、复制带随机指针的链表 一、相交链表 题目: 示例: 注意:不能根据节点的值来比较是否相交,而是根据节点在内存中是否指向相同的位置。 例如以上图: 链表…

无涯教程-Android - RadioButton函数

RadioButton有两种状态:选中或未选中,这允许用户从一组中选择一个选项。 Radio Button 示例 本示例将带您完成一些简单的步骤,以展示如何使用Linear Layout和RadioButton创建自己的Android应用程序。 以下是修改后的主要Activity文件 src/MainActivity.java 的内容。 packa…

数学建模:数据的预处理

🔆 文章首发于我的个人博客:欢迎大佬们来逛逛 文章目录 数据预处理数据变换数据清洗缺失值处理异常值处理 数据预处理 数据变换 常见的数据变换的方式:通过某些简单的函数进行数据变换。 x ′ x 2 x ′ x x ′ log ⁡ ( x ) ∇ f ( x k )…