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

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

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

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
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,生成贪吃蛇的代码如下所示: ```java import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Snake extends JFrame implements ActionListener { private final int WIDTH = 300; private final int HEIGHT = 300; private final int DOT_SIZE = 10; private final int ALL_DOTS = 900; private final int RAND_POS = 29; private final int DELAY = 140; private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private boolean leftDirection = false; private boolean rightDirection = true; private boolean upDirection = false; private boolean downDirection = false; private boolean inGame = true; private Timer timer; private Image ball; private Image apple; private Image head; public Snake() { setTitle("Snake"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(WIDTH, HEIGHT); setResizable(false); setLocationRelativeTo(null); setVisible(true); initGame(); } private void initGame() { dots = 3; for (int i = 0; i < dots; i++) { x[i] = 50 - i * 10; y[i] = 50; } locateApple(); timer = new Timer(DELAY, this); timer.start(); } @Override public void paint(Graphics g) { super.paint(g); if (inGame) { g.drawImage(apple, apple_x, apple_y, this); for (int i = 0; i < dots; i++) { if (i == 0) { g.drawImage(head, x[i], y[i], this); } else { g.drawImage(ball, x[i], y[i], this); } } Toolkit.getDefaultToolkit().sync(); } else { gameOver(g); } } private void gameOver(Graphics g) { String message = "Game Over"; Font font = new Font("Helvetica", Font.BOLD, 14); FontMetrics metrics = getFontMetrics(font); g.setColor(Color.black); g.setFont(font); g.drawString(message, (WIDTH - metrics.stringWidth(message)) / 2, HEIGHT / 2); } private void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } private void move() { for (int i = dots; i > 0; i--) { x[i] = x[(i - 1)]; y[i] = y[(i - 1)]; } if (leftDirection) { x[0] -= DOT_SIZE; } if (rightDirection) { x[0] += DOT_SIZE; } if (upDirection) { y[0] -= DOT_SIZE; } if (downDirection) { y[0] += DOT_SIZE; } } private void checkCollision() { for (int i = dots; i > 0; i--) { if ((i > 4) && (x[0] == x[i]) && (y[0] == y[i])) { inGame = false; } } if (y[0] >= HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] >= WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; } if (!inGame) { timer.stop(); } } private void locateApple() { int r = (int) (Math.random() * RAND_POS); apple_x = r * DOT_SIZE; r = (int) (Math.random() * RAND_POS); apple_y = r * DOT_SIZE; } @Override public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(); checkCollision(); move(); } repaint(); } public static void main(String[] args) { new Snake(); } } ``` 这里我们使用了 Java 的图形界面组件库 Swing 和 AWT 来实现贪吃蛇游戏。该游戏含有基本的贪吃蛇的操作,包括蛇的移动、吃苹果、碰撞检测等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值