matlab 全局变量_十滴水的递归实现(matlab)

266081a3508917f20e3a318817ea1b5e.png

注1:本文仅讲述十滴水小游戏算法在matlab中的递归实现,求解算法和图像识别代码将分别讲述.游戏地址

注2:本文仅作留档

注3:写文章好几把累

Part Ⅰ :分析

1.为何使用递归?

一拍脑门想到的(不是

主要是之前写过一个递归走迷宫代码,它和十滴水有异曲同工之妙。

328d89b15f99a61a810df4e493a347d4.png

da7772f0886b2575bb110f0e0ddd902b.png

c6ccc5f511a75d341a612ed1564f2e68.png

638d968b704cd997d4dbca6a3d7627dc.png

书中的迷宫用矩阵图表示为这样:

34308553d90c1de01f80cf02b2ffda3b.png
暗红色为终点

我们定义十滴水的状态为:

1.初始状态:即最开始没有点击时的状态

e0fb5eabc4837cc74dcb4e380f32c77c.png

2.一般状态:可点击水滴时的状态(你可以发现在空中有水飞行时,水滴是点击不了的)

c2ee515f0f731f1fc35f9bde4f49bf33.png

定义funfun为十滴水在各个状态间转移的函数。(即运行一次会使十滴水从本状态跃迁到下一个状态)

共同之处在于:对funfun进行一次输入,此次输入会使整个盘面发生更新,使之跃迁到下一个状态(按照十滴水的游戏规则,点击一次后,会爆炸的爆炸,炸出水;不会爆炸的,加水;空白的,飞过;直到达到下一个状态)。走迷宫也是,运行一次会使整个盘面进行更新,使之跃迁到迷宫被走出的状态(所有按照规则可走的道路均被打上脚印)。

可以认为迷宫是十滴水的一种特殊情况。只有两个状态,初态,走出态(可走道路布满脚印)。

因此可以用递归模拟。

Part Ⅱ :代码及思路

  • 任务1:状态如何表示?
  • 任务2:点击操作如何模拟?
  • 任务3:炸出的水如何模拟?

①对于任务1,很简单,只需用矩阵刻画即可。

不妨设为map。

matlab提供了丰富的矩阵操作函数。可视化也十分方便。下为一局十滴水的初始状态。

704f4e54a26af6c6c9b0a409cabf8973.png
global map

map设为全局变量。其大小为6*6。每个位置只有5种状态,例map(1,1)只能为0,1,2,3,4中的任意一个。

设map(1,1)初始值为0,其状态转移用图表示为

5a10bf4294adb541b4167749336c8e86.png
只能按顺序依次变化,不能越级变化

可以看出map(1,1)==0是终态(吸收态),任意初始值都会在有限步之后到达0

②对于任务Ⅱ

funfun函数具有一个输入dro

funfun(dro)

dro为为一个二维向量表示坐标,形如[1,3],他表示点击(drop加水)的坐标。

加水即向盘面上的水滴加水(不考虑向无水的格子中加水,太憨了)

满足以下规则(注map(dro)表示map(dro(1),dro(2))):

  1. 如果map(dro)在1到3之间那么直接加一即可
  2. 如果map(dro)==4那么置0
 if map(pos(1),pos(2))<=3&&map(pos(1),pos(2))>=1
        map(pos(1),pos(2))=map(pos(1),pos(2))+1;
    elseif map(pos(1),pos(2))==4
        map(pos(1),pos(2))=0;
    end

③对于任务Ⅲ:

这个解释起来比较麻烦

首先考察飞行的水,某一时刻他有位置、运动的方向和速度等性质,显然面向对象是描述它的最佳方案,我们定义一个public class shui{}(不是

害,递归递归。

速度其实可以不用管,因为同一时刻,同一个位置处比如[1,2]上不可能有两个运动方向相同的水。所以对于飞行的水必然是可以任意的空间顺序来处理的,仅需在递归时指定一个空间操作的步长为1即可。(此处可能有点难以理解,我也很难解释)

所以仅需考虑运动方向。可以借用单位向量的概念来描述。再定义另一个位置向量pos(position)作为funfun的输入。即:

funfun(pos,dro)

其作用为:

  1. 保存水滴炸裂的位置,即飞行水的源头。

此时dro的作用就改变了,它表示的是飞行水的当前位置。

运动方向为

sign(dro-pos)

飞行水的规则为

  1. 当飞行水遇到不为零的map(dro)时执行任务Ⅱ的操作(operate),即map(dro)不为0时,使用任务Ⅱ的规则
  2. 当飞行水遇到为0的map(dro)时执行fun(pos,dro+sign(dro-pos)),即向运动方向运动再一个步长。

那么任务Ⅱ的逻辑需作出一些改变:具体来说就是何时向盘面上的水滴加水。

我们重新定义任务Ⅱ

  1. 仅当pos==dro时执行原任务Ⅱ
  2. 对于飞行的水来说就是当它在飞行过程中遇到map(dro)不为零时执行原任务Ⅱ,否则继续飞行。
  3. 关于水滴爆裂,也需对任务Ⅱ逻辑进行一些添加,添加爆裂产生飞行水的操作。

改动后的任务Ⅱ:

if 

任务Ⅲ:

if 

检查递归是否有出口(注上方代码第二个if就是飞行水的位置限制):

当它既不属于点击操作(pos==dro),飞行水也全部消失时,递归就达到了出口。

应该没问题。

试运行。

252615a5a4fbeb0bc2b77ed1bcafedfa.gif

没问题。

实现完整代码:

function

总代码:

%%
%点击运行
%直接运行
%预存若干局面以及随机局面
%取消14行,15行注释为随机局面
%取消16行为图像识别
%取消其他矩阵的注释为已有局面
%97行pause()为单步运行,pause(0.5)为间隔半秒运行
function testtenwater

global map%图
    
% rng(s)
% p=randi(5,6)-1;
% map=p;
%map=csvread('a.csv');
%map=cell2mat(map)
%map=
%  map=[
%      0 4 3 0 0 3
%      4 1 3 3 2 3
%      3 1 2 3 2 4
%      4 1 2 4 4 2
%      0 2 1 2 3 0
%      0 2 3 2 2 0
%  ]
map=[
    0 3 3 1 1 4
    4 2 2 1 1 4
    3 2 0 4 0 3
    0 4 2 3 3 4
    1 2 0 2 3 3
    0 2 2 1 0 2

]
% map=[
% 1 4 2 4 2 0
% 2 0 0 2 2 3
% 0 3 2 4 1 1
% 3 3 1 0 4 0
% 3 0 3 2 3 3
% 2 4 4 2 2 1
% 
% ]
% map=[
% 3 4 2 2 2 0
% 2 1 3 0 1 2
% 3 0 3 3 3 0
% 2 4 4 1 0 0
% 1 3 2 4 4 2
% 3 3 1 0 4 1
% ]
% map=[
%     3 0 3 3 2 0
%     3 1 1 4 4 2
%     2 3 0 4 1 2
%     4 2 3 4 3 1
%     1 0 2 2 0 4
%     1 2 0 0 3 0
%     
%     ]
h=6;
imagesc(map)%作图
%%
%test
% dx
% dy
% mm=[dx ;dy' ;y 0 0 0 0 0;x 0 0 0 0 0];
% flag=[-1,1];
% for ii=1:2
%     for jj=1:2
%     [aa(ii,jj),fl(ii,jj)]=mysumUAD(mm(ii,:),mm(ii+2,1),flag(jj));
%     end
% end

%%
%求解算法
pic_num = 1;
for ii=100:-1:1
    b=jisuan2();
    b(map==0)=0;
    [i,j]=ind2sub([6,6],find(b==max(max(b)))); %单下标转换
    cc=randi(length(i),1); %随机选取周围水最多的点
    figure(1);
     imagesc( map)
     colorbar
     drawnow;
   
     if sum(map>0)==0
        disp(ii)
        break
     end
    
    pos=[i(cc),j(cc)];
     disp(pos)
     text(pos(2),pos(1),'click')
   pause()%去掉其中的数字可以单步运行
    funfun(pos,pos);
    
     F=getframe(gcf);
    I=frame2im(F);
    [I,ch]=rgb2ind(I,256);
    if pic_num == 1
        imwrite(I,ch,'test.gif','gif', 'Loopcount',inf,'DelayTime',0.5);
    else
        imwrite(I,ch,'test.gif','gif','WriteMode','append','DelayTime',0.5);
    end
    pic_num = pic_num + 1;
    
    
    
    
    
    
    
    
    
    
end

end
%%
%计算map中所有的周围水
function b=jisuan2()
for ii=1:6
    for jj=1:6
        b(ii,jj)=jisuan1([ii,jj]);
    end
end
end
%%
%周围水计算程序
%按照水最多来点
function a=jisuan1(pos) 
global map
x=pos(1);
y=pos(2);
dx=map(x,:);
dy=map(:,y);
mm=[dx ;dy' ;y 0 0 0 0 0;x 0 0 0 0 0];
flag=[-1,1];
for ii=1:2
    for jj=1:2
    [aa(ii,jj),fl(ii,jj)]=mysumUAD(mm(ii,:),mm(ii+2,1),flag(jj));
    end
end
temp=sum(sum(fl));%统计周围有几个方向有水滴
a=sum(sum(aa))+4*map(x,y);%统计总的水滴数(中间水滴数权重为4,其余方向为1)
switch temp
    %周围四个方向有水滴的优先
    case 4
        a=a*4;
    case 3
        a=a*3;
    case 2
        a=a*2;
    case 1
        a=a;
end
end
%%
function [c,fl]=mysumUAD(dx,y,flag)
c=0;
%flag=1up
%flag=-1down
%fl返回up或down方向是否有水滴
fl=1;
mm=y+flag;
switch flag
    case 1 
        pp=6;
    case -1
        pp=1;
end
for yy=mm:flag:pp
    c=c+dx(yy);
    if c>0
        break;
    end
end
 if (mm>pp&&flag>0)||(mm<pp&&flag<0)
     fl=0;
 elseif (c==0)&&(yy==pp)
    fl=0;
 end

return
end
%%
%矩阵实现十滴水
%待


%%
%递归实现十滴水
function funfun(pos,dro)
global map
if sum(pos==dro)==2
    if map(pos(1),pos(2))<=3&&map(pos(1),pos(2))>=1
        map(pos(1),pos(2))=map(pos(1),pos(2))+1;
    elseif map(pos(1),pos(2))==4
        map(pos(1),pos(2))=0;
        funfun(pos,dro-[0,1]);
        funfun(pos,dro-[1,0]);
         funfun(pos,dro+[0,1]);
        funfun(pos,dro+[1,0]);
    end
end
if sum(sum(pos==dro))<2
    if dro(1)>=1&&dro(1)<=6&&dro(2)>=1&&dro(2)<=6
    if map(dro(1),dro(2))==0
        funfun(pos,dro+sign(dro-pos))
    else
        funfun(dro,dro);
    end
    end
end

end
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值