第二章 趣味迷宫




这一章来谈一谈迷宫的走法,以及做一个简单的迷宫游戏,可以自己DIY迷宫地图
我们将迷宫问题难度上升一点,就是找一条最近的路径走出迷宫
一般针对最短路径问题,bfs是最简单的选择,A*是较好的选择,但是A*过程较繁琐,今天我们将采用IDA*算法来实现这个寻找迷宫出口问题
算法思想和流程:
IDA*算法简单的讲就是迭代+A*的实现,因实现过程非常简单,而且效率还很优,所以掌握IDA*势在必行
1、没有学习IDA*算法以前,觉得如果是最短路径问题,则dfs是无法实现的,bfs可以实现,但是IDA*告诉了我dfs怎么实现最短路径问题,而且实现的更好
2、、回顾下IDA*的步骤,没有了A*的优先队列,甚至连bfs广搜所需的简单队列和判重数组都不用,从一个阈值开始递归,这个值一般可以理解为递归的最大深度,若找不到目标点,则这个值进行扩大,列入++操作,再重新递归操作,找到则返回,返回的一定是最优值
3、回顾下IDA*的判断函数(IDA*的精髓啊):当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。
实现起来较容易,代码量也很少
//最近的路走出迷宫
//IDA*算法实现
#include <iostream>
using namespace std;

#define M 15//行
#define N 20//列
int map[][N]={
	{0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1},
	{1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1},
	{1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,1,0,1},
	{1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,0,1},
	{1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1},
	{1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1},
	{1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1},
	{1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1},
	{1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1},
	{1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1},
	{1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1},
	{1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1},
	{1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0}
};
int dir[][2]={-1,0,0,-1,1,0,0,1};
int starti=0,startj=0;//开始点坐标
int endi=M-1,endj=N-1;//结束点坐标
int maxDeep;//最大深度
int H(int i,int j){//估价函数
	return abs(endi-i)+abs(endj-j);
}

bool dfs(int curi,int curj,int depth){
	if(depth+H(curi,curj)>maxDeep)//IDA*估价剪枝
		return false;
	if(curi==endi&&curj==endj)
		return true;
	
	for(int i=0;i<4;i++){
		int ni=curi+dir[i][0];
		int nj=curj+dir[i][1];
		if(ni>=0&&ni<M&&nj>=0&&nj<N&&map[ni][nj]==0){
			map[ni][nj]=2;
			if(dfs(ni,nj,depth+1))return true;
			map[ni][nj]=0;
		}
	}
	return false;
}
int IDAstar(){
	maxDeep=H(starti,startj);
	map[starti][startj]=2;
	while(!dfs(starti,startj,0))maxDeep++;
	return maxDeep;
}
int main(){
    cout<<IDAstar();
    return 0;
}

下面我们的主要任务就是做一个这样的游戏了,可以模拟动态的走出这个迷宫,还可以自己DIY通路和障碍,动手开始吧
HTML代码如下,只要复制粘贴,保存html打开就能见到后面的效果了
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
<style type="text/css">
body,div,h2,ul,li{margin:0;padding:0;}
body{font:12px/1.5 Arail;}
.box{width:860px;margin:10px auto;background:#eee;border:1px solid #b8b8b8;overflow:hidden}
.title{height:30px;line-height:30px;font-size:14px;padding:0 15px 0 35px;border-bottom:1px solid #b8b8b8;background:#fafafa url(http://js.alixixi.com/img/mm/ico.gif) 5px 50% no-repeat;}
.title span{float:left;}
.title a{float:right;color:#06f;outline:none;}
.title a:hover{color:red;}
    .box .tool {
        width:300px;
        float:left;
    }
    .box .content {
        width:480px;
        margin-left:310px;
    }
    .tool div {
        margin:10px auto;
        height:40px;
        text-align:center;
    }
    .content .tag {
        height:30px;
        line-height:30px;
    }
    .content td {
        width:10px;
        height:10px;
        font-size:xx-large;
        line-height:10px;
        border:1px solid #c3c3c3;
        background:#fff;
        text-align:center;
    }
</style>
<script type="text/javascript">
    //一些常量
    
    var matrix = [
	[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
	[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1],
	[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
	[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1],
	[1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
	[1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
	[1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
    ];
    var M = 15, N = 20;
    var dir = [[-1, 0], [0, -1], [1, 0], [0, 1]];
    var starti = 0, startj = 0;//开始点坐标
    var endi = M - 1, endj = N - 1;//结束点坐标
    function H(i,j){//估价函数
        return Math.abs(endi - i) + Math.abs(endj - j);
    }

    var BLACK="#000";
    var BLUE="#06f";
    var WHITE="#fff";
</script>
<script type="text/javascript">
    //一些公共函数
    //获取ID
    var $ = function (id) { return typeof id === "string" ? document.getElementById(id) : id };
    //获取tagName
    var $$ = function (tagName, oParent) { return (oParent || document).getElementsByTagName(tagName) };
    //获取class
    var $$$ = function (sClass, oParent) {
        var aClass = [],
        i = 0,
        reClass = new RegExp("(\\s|^)" + sClass + "($|\\s)"),
        aElement = $$("*", oParent);
        for (i = 0; i < aElement.length; i++) reClass.test(aElement[i].className) && aClass.push(aElement[i]);
        return aClass
    };
</script>
<script type="text/javascript">
    //创建数独对象
    var Maze = function () { this.initialize.apply(this, arguments) };
    Maze.prototype = {
        initMatrix: function (matrix,Map) {
            for (var i = 0; i < M; i++) {
                matrix[i] = new Array(N);
                for (var j = 0; j < N; j++) {
                    matrix[i][j] = Map[i][j];
                }
            }
        },
        renewMatrix: function (matrix,Map) {
            for (var i = 0; i < M; i++) {
                for (var j = 0; j < N; j++) {
                    matrix[i][j] = Map[i][j];
                }
            }
        },
        initialize: function (obj ,Map) {
            var oThis = this;
            this.Parent = $(obj);//获得table对象
            this.Table = $$("table", this.Parent)[0];
            this.tag = $$$("tag", this.Parent)[0];
            this.ButtonA = $$$("answer", this.Parent)[0];
            this.ButtonB = $$$("restore", this.Parent)[0];
            this.ButtonC = $$$("show", this.Parent)[0];
            this.ButtonD = $$$("diy", this.Parent)[0];

            this.matrix = new Array(M);//存放数独矩阵
            this.diyMatrix = new Array(M);//diy迷宫
            this.Td = [];//table中的所有td列矩阵

            this.maxDeep = 0;
            this.stime = null;//计时

            this.initMatrix(this.matrix, Map);
            this.initMatrix(this.diyMatrix, Map);

            this.dom = document.documentElement || document.body;
            this.ButtonA.onclick = function () {
                oThis.tag.innerText = "运行";
                oThis.initOperation();
                oThis.show();
                oThis.answer();
                oThis.print();
            }
            this.ButtonB.onclick = function () {
                oThis.tag.innerText = "还原迷宫";
                oThis.initOperation();
                oThis.show();
            }
            this.ButtonC.onclick = function () {
                oThis.tag.innerText = "显示答案";
                oThis.initOperation();
                oThis.answer();
                oThis.show();
            }
            this.ButtonD.onclick = function () {
                oThis.tag.innerText = "DIY迷宫";
                oThis.initOperation();
                oThis.show();
                oThis.diy();
            }
            this.create(Map);
        },
        initOperation: function () {//初始化操作
            var oThis = this;
            oThis.renewMatrix(oThis.matrix, oThis.diyMatrix);
            clearInterval(oThis.stime);
        },
        create: function (Map) {
            var oThis = this;
            var aFrag = document.createDocumentFragment();
            var i = 0, j = 0;
            for (i = 0; i < M; i++) {
                var tr = document.createElement("tr");
                oThis.Td.push([]);
                for (j = 0; j < N; j++) {
                    var td = document.createElement("td");
                    if (Map[i][j] == 1) td.style.backgroundColor = BLACK;
                    oThis.Td[i].push(td);
                    
                    tr.appendChild(td);
                }
                aFrag.appendChild(tr);
            }
            this.Table.appendChild(aFrag);
        },
        print: function () {
            var oThis = this;
            var curi = starti, curj = startj;
            var preDir = -3;//访问的上一个方向
            
            this.stime = setInterval(function () {
                oThis.Td[curi][curj].style.backgroundColor = BLUE;
                if (curi == endi && curj == endj) clearTimeout(oThis.stime);
                for (var i = 0; i < 4; i++) {
                    var ni = curi + dir[i][0];
                    var nj = curj + dir[i][1];
                    if (ni >= 0 && ni < M && nj >= 0 && nj < N && Math.abs(i - preDir) != 2 && oThis.matrix[ni][nj] == 2) {
                        curi = ni, curj = nj;
                        preDir = i;
                        break;
                    }
                }
            }, 100);
        },
        show: function () {
            for (var i = 0; i < M; i++)
                for (var j = 0; j < N; j++) {
                    if (this.matrix[i][j] == 2)
                        this.Td[i][j].style.backgroundColor = BLUE;
                    else if (this.matrix[i][j] == 1)
                        this.Td[i][j].style.backgroundColor = BLACK;
                    else this.Td[i][j].style.backgroundColor = WHITE;
                }
        },
        find:function(obj){
            for (var i = 0; i < M; i++)
                for (var j = 0; j < N; j++)
                    if (this.Td[i][j] == obj)
                        return { i: i, j: j };
        },
        diy: function () {
            var oThis=this;
            for (var i = 0; i < M; i++) {
                for (var j = 0; j < N; j++) {
                    this.Td[i][j].onclick = function () {
                        var pos = oThis.find(this);
                        var i=pos.i,j=pos.j;
                        if (oThis.diyMatrix[i][j] == 0) {
                            oThis.diyMatrix[i][j] = 1;
                            this.style.backgroundColor = BLACK;
                        } else {
                            oThis.diyMatrix[i][j] = 0;
                            this.style.backgroundColor = WHITE;
                        }
                    }
                }
            }
        },
        answer: function () {
            this.IDAstar();
            //this.show();
        },
        dfs:function(curi, curj, depth) {
        
            if (depth + H(curi, curj) > this.maxDeep)//IDA*估价剪枝
                return false;
            if (curi == endi && curj == endj)
                return true;

            for (var i = 0; i < 4; i++) {
                var ni = curi + dir[i][0];
                var nj = curj + dir[i][1];
                if (ni >= 0 && ni < M && nj >= 0 && nj < N && this.matrix[ni][nj] == 0) {
                    this.matrix[ni][nj] = 2;
                    if (this.dfs(ni, nj, depth + 1) == true) return true;
                    this.matrix[ni][nj] = 0;
                }
            }
            return false;
        },
        IDAstar:function(){
            this.maxDeep = H(starti, startj);
            this.matrix[starti][startj]=2;
            while (this.dfs(starti, startj, 0) == false) {
                if (this.maxDeep > 100) break;
                this.maxDeep = this.maxDeep + 1;
            }
        }
    };
    window.onload = function () {
        var Box = $$$("box");
        //创建实例
        new Maze(Box[0], matrix);
    };
</script>
</head>
<body>
<div class="box">
    <h2 class="title"><span>迷宫模拟</span></h2>

    <div class="tool">
        <div><button class="answer">运行</button></div>
        <div><button class="restore">还原迷宫</button></div>
        <div><button class="show">显示答案</button></div>
        <div><button class="diy">DIY迷宫</button></div>
    </div>

    <div class="content">
        <div class="tag">默认迷宫</div>
        <table class="sudoku">
            
        </table>
    </div>
</div>

</body>
</html>



效果如下:
初始状态:

点击运行:
蓝色条会一直动
点击显示答案会将最近的路径显示出来
点击DIY迷宫可以自己DIY,然后再点击运行或者显示答案,会重新计算结果


后记:
我一直觉得编程没有那么枯燥,也可以是玩出来的



王川
2014/2/13
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值