【数据结构】安卓平台下深度优先搜索的应用--走迷宫

在RPG游戏中,我们常常可以看到怪物很聪明,自己能找到玩家,游戏中有地图,怪物在地图中以特定的算法搜索以找到玩家。今天我们来看看一种基本的图搜索算法——深度优先搜索算法


深度优先搜索算法讲的是

1。从某个点开始,遍历所有与该点相连的点

2。如果找到一个点并且该点没有访问过,则把这个点设为已访问

3。对后面的点执行1,2操作。

4。如果某个点所连接的所有点已被访问过,回溯,直到回溯到某个点所连的点有未访问过的。执行1,2操作。

5。当所有点被访问过,结束搜索。

显然,这是一个递归的过程。递归的结束条件是搜索到达目的地的时候。深度优先搜索是往深里搜的算法,就是一条路走到底,直到无路可走了再回来,回到有岔路的地方并且这条岔路没走过,然后走岔路,以此走下去。直到到达目的地……

昨天心血来潮,撸了一个例子,用View绘图。

效果图:


这个例子按深度优先算法生成迷宫,然后再按深度优先搜索遍历迷宫,运行例子后可以看到小球在走迷宫,从入口找到出口。。。其中Maze.java是迷宫类,MainView是负责绘图的类。

MainActivity.java
package com.luoye.maze;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends Activity {

    static  Context context;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(1);
        setContentView(new MainView(this));
        showToast(this,"点击屏幕让小球走迷宫");
        context=this;
    }

    static Toast toast;
    public static void showToast(Context context,String text)
    {
        if(toast==null)
        {
            toast=Toast.makeText(context,text,Toast.LENGTH_LONG);
        }else
        {
            toast.setText(text);
        }
        toast.show();
    }



}

MainView.java
package com.luoye.maze;
import android.content.*;
import android.os.Handler;
import android.os.Message;
import android.view.*;
import android.graphics.*;


/**
 *主视图类
 *落叶似秋编写
 *2016-3-11
 */
public class MainView extends View implements Runnable
{

	boolean isFirst=true;//标记是否是第一次走完
	final  int SLEEP=10;
	Paint wallPaint;//墙画笔
	Paint manPaint;//人画笔
	boolean isDraw=false;//标记是否刷新画布
	boolean canTouch=true;//标记是否可以触摸屏幕
	Maze maze;
	int circleX,circleY;//实时记录人的位置
	byte[][] data;//迷宫数据矩阵
	Rect rect;
	final int[][] direct={
		{-1,0},//左
		{0,1},//上
		{1,0},//右
		{0,-1}//下
	};//方向,相对于当前位置的偏移

	public MainView(Context context)
	{
		super(context);
		wallPaint = new Paint();
		wallPaint.setColor(Color.BLACK);
		wallPaint.setAntiAlias(true);
		wallPaint.setDither(true);

		manPaint = new Paint();
		manPaint.setColor(Color.RED);
		manPaint.setAntiAlias(true);
		manPaint.setDither(true);
		maze = new Maze(0, 0, 35, 61);//迷宫长和宽都必须为奇数。

		init();

	}

	private void init()
	{
		data = maze.generate().clone();//生成迷宫
		circleX = (2) * 20 + maze.mx;
		circleY = (1) * 20 + maze.my;
	}


	@Override
	protected void onDraw(Canvas canvas)
	{
		// TODO: Implement this method
		canvas.drawARGB(255, 255, 255, 255);
		maze.draw(canvas, wallPaint);
		drawMan(canvas, circleX, circleY);//这句的位置很重要
	}
	Handler handler=new Handler() {
		@Override
		public void handleMessage(Message msg)
		{
			if (msg.what == 0)
			{
				if (isDraw)
				{
					invalidate();//刷新画布
				}
				else
				{
					if (isFirst)
					{
						isFirst = false;
						MainActivity.showToast(MainActivity.context, "找到出口了");
					}
				}
			}
			else if (msg.what == 1)
			{
				MainActivity.showToast(MainActivity.context, "点击屏幕重新开始");
			}
		}
	};
	@Override
	public void run()
	{
		// TODO: Implement this method
		//从开始位置搜索
		dfs(2, 1);
		canTouch = true;
		handler.sendEmptyMessage(1);
		
	}


	/**
	 * 画人
	 * @param canvas
	 * @param x
	 * @param y
	 */
	private void drawMan(Canvas canvas, int x, int y)
	{
		canvas.drawCircle(x + 10, y + 10, 10, manPaint);
		
	}


	/**
	 * 深度优先搜索出口
	 * @param x
	 * @param y
	 */
	private void dfs(int x, int y)
	{


		int offsetX=0;
		int offsetY=0;
		//标记为访问过
		data[x][y] = Maze.VISITED;
		//每走到一个点就遍历四个方向
		for (int i=0;i < 4;i++)
		{
			if (isDraw == false)
				return;
			offsetX = direct[i][0];
			offsetY = direct[i][1];

			if (data[x + offsetX][y + offsetY] == Maze.SPACE)
			{

				try
				{
					Thread.sleep(SLEEP);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}

				circleX = (x + offsetX) * 20 + maze.mx;
				circleY = (y + offsetY) * 20 + maze.my;
				//通知刷新画布
				handler.sendEmptyMessage(0);
				//深度优先搜索下一位置**************
				dfs(x + offsetX, y + offsetY);
				//回溯--------------------------
				try
				{
					Thread.sleep(SLEEP);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}

				circleX = (x + offsetX) * 20 + maze.mx;
				circleY = (y + offsetY) * 20 + maze.my;
				//通知刷新画布
				handler.sendEmptyMessage(0);
			}
		}
		//如果到达终点
		if (x == maze.width - 3 && y == maze.height - 2)
		{
			handler.sendEmptyMessage(0);
			isDraw = false;
			isFirst = true;

			return;
		}

		try
		{
			Thread.sleep(SLEEP);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}

		//通知刷新画布
		handler.sendEmptyMessage(0);
	}

	/**
	 * 屏幕点击事件
	 * @param event
	 * @return
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		if (event.getAction() == MotionEvent.ACTION_DOWN)
		{
			if (canTouch)
			{
				canTouch = false;
				if (!isFirst)
				{
					data = maze.generate().clone();
				}
				circleX = (2) * 20 + maze.mx;
				circleY = (1) * 20 + maze.my;
				isFirst = false;
				new Thread(this).start();
				isDraw = true;
			}
		}
		return super.onTouchEvent(event);
	}
}

Maze.java

package com.luoye.dfsmaze;
import java.util.*;
import android.graphics.*;

/*
*迷宫类
*/
public  class Maze
{

	public static final int WALL = 0;//墙
	public static final int SPACE = 1;//空
	public static final int VISITED=2;//标记已访问过
	public static final int MOVE=3;//迷宫中可以动的物体
	public static final int EMPTY=4;
	private byte[][] data;//数据
	public int width;//迷宫宽
	public int height;//迷宫高
	private Random rand = new Random();
	public int mx,my;
	final int[] upx = { 1, -1, 0, 0 };//分别代表了左右上下4个方向
	final int[] upy = { 0, 0, 1, -1 };
	public Maze(int mx,int my,int width, int height)
	{
		this.width = width;
		this.height = height;
		data = new byte[width][];
		this.mx=mx;
		this.my=my;
	}

	/**
	 *打洞
	 *从入口打洞,随机选择一个方向,如果那个方向前面两格都是墙,则打通
	 *这里也是用了深度优先搜索算法
	 *
	 */
	private void carve(int x, int y)
	{
		int dir = rand.nextInt(4);//0~3
		int count = 0;
		while (count < 4)
		{
			final int x1 = x + upx[dir];
			final int y1 = y + upy[dir];
			final int x2 = x1 + upx[dir];//加强,与x1同个方向
			final int y2 = y1 + upy[dir];//加强,与y1同一个方向
			if (data[x1][y1] == WALL && data[x2][y2] == WALL)//如果为都为墙。表示打中空格
			{
				data[x1][y1] = SPACE;
				data[x2][y2] = SPACE;
				carve(x2, y2);//条件符合,开挖
			}
			else
			{
				dir = (dir + 1) % 4;//产生一个不同与dir的,又在0~3范围内的数(即产生下一个方向)
				count += 1;
			}
		}
	}

	/**
	 *生成
	 *没什么好说的
	 */
	public byte[][] generate()
	{
		//都填为墙
		for (int x = 0; x < width; x++)
		{
			data[x] = new byte[height];
			for (int y = 0; y < height; y++)
			{
				data[x][y] = WALL;
			}
		}
		//第一行和最后一行清空
		for (int x = 0; x < width; x++)
		{
			data[x][0] = EMPTY;
			data[x][height - 1] = EMPTY;
		}
		//第一列和最后列填充为空
		for (int y = 0; y < height; y++)
		{
			data[0][y] = EMPTY;
			data[width - 1][y] = EMPTY;
		}

		//入口下来那一格打空
		data[2][2] = SPACE;
		//开始打洞
		carve(2, 2);

		//开入口
		data[2][1] = SPACE;
		//开出口
		data[width - 3][height - 2] = SPACE;
		
		return data;
	}

	/**
	 *画迷宫
	 *Paint传进来就可以自定义方块颜色
	 */
	public void draw(Canvas canvas,Paint paint)
	{
		int x1=mx,y1=my;
		final int rectW=20,rectH=20;
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				if (data[x][y] == WALL)
				{
					canvas.drawRect(x1,y1,x1+rectW,y1+rectH,paint);
					x1=x1+rectW;
				}
				else
				{
					//如果是空白,直接不画,右移一格就行
					x1=x1+rectW;
					
				}
			}
			y1=y1+rectH;//下一行
			x1=mx;//定位到一行开始
		}
	}


}

	

例子下载地址: https://github.com/luoyesiqiu/MazeGame
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值