在这里我们将构造一个基于HT for Web的HTML5+javaScript来实现汉诺塔游戏。
汉诺塔的游戏规则及递归算法分析请参考http://en.wikipedia.org/wiki/Tower_of_Hanoi。
知道了汉诺塔的规则和算法,现在就开始创建元素。用HT for Web(http://www.hightopo.com)现有的3D模板创建底盘和3根柱子不是问题,问题是要创建若干个中空的圆盘。一开始的想法是:创建一个圆柱体,将圆柱体的上下两端隐藏,设置柱面的宽度来实现圆盘的效果,经过多次尝试并查阅相关api文档,发现柱面是没有厚度的,改方法不可行。
后来在HT for Web自定义3D模型的WebGL应用(http://www.hightopo.com/blog/381.html)受到启发,圆盘的形成就是在xy平面上的一个矩形,根据y轴旋转一周产生的,通过查阅相关文档,最总决定采用ht.Default.createRingModel方法来创建圆盘模型,然后在创建node的时候通过shape3d属性引用创建好的模型。
在逻辑实现上,采用了栈的先进后出的原理,对圆柱上的圆盘做顺序控制,确保每次移动的圆盘都是最小的圆盘。
在算法上,采用的是递归算法,通过递归算法,将搬迁过程一步一步记录下来,再采用堆的原理一步一步地执行搬迁过工作。
所有代码和运行效果如下:http://v.youku.com/v_show/id_XODcwMTk4MDI4.html
1 var barNum = 5, // 圆盘个数 2 cylinderHeight = barNum * 20 + 40, // 圆柱高度 3 barrelMinORadius = 50, // 圆盘最大外半径 4 barrelIRadius = 10, // 圆盘内半径 5 poorRadius = 20, // 圆盘外半径差值 6 barrelMaxORadius = barrelMinORadius + barNum * poorRadius, 7 barrelHeight = 20, // 圆盘高 8 barPadding = 20, // 柱体之间的间隙 9 floorX = barrelMaxORadius * 6 + barPadding * 4, // 底盘长 10 floorY = 20, // 底盘高 11 floorZ = 2 * barrelMaxORadius + barPadding * 2, // 底盘宽 12 // 柱体集 13 positions = [ 14 { 15 barrels: [], 16 position: [-(2*barrelMaxORadius + barPadding), cylinderHeight / 2 + 1, 0] 17 },{ 18 barrels: [], 19 position: [0, cylinderHeight / 2 + 1, 0] 20 },{ 21 barrels: [], 22 position: [(2*barrelMaxORadius + barPadding), cylinderHeight / 2 + 1, 0] 23 } 24 ], 25 runOrder = [], // 圆盘移动顺序集 26 // 动画参数 27 params = { 28 delay: 10, 29 duration: 500, 30 easing: Easing['easeBoth'] 31 }; 32 33 /** 34 * 初始化程序 35 * */ 36 function init(){ 37 dataModel = new ht.DataModel(); 38 g3d = new ht.graph3d.Graph3dView(dataModel); 39 view = g3d.getView(); 40 view.className = 'main'; 41 document.body.appendChild(view); 42 window.addEventListener('resize', function (e) { 43 g3d.invalidate(); 44 }, false); 45 46 g3d.setEye([0, cylinderHeight * 2, floorX * sin(2*PI/360*60)]); 47 48 // 初始化节点 49 initNodes(); 50 51 moveAnimation(); 52 } 53 54 /** 55 * 构造游戏移动队列 56 * diskQuantity:圆盘个数 57 * positionA:起点 58 * positionB:中转点 59 * positionC:终点 60 * */ 61 function buildRunOrder(diskQuantity, positionA, positionB, positionC){ 62 if (diskQuantity == 1) { 63 runOrder.push([positionA, positionC]); 64 } else { 65 buildRunOrder(diskQuantity - 1, positionA, positionC, positionB); 66 buildRunOrder(1, positionA, positionB, positionC); 67 buildRunOrder(diskQuantity - 1, positionB, positionA, positionC); 68 } 69 } 70 71 /** 72 * 移动动画 73 * positionA:起点 74 * positionC:终点 75 * */ 76 function moveAnimation(positionA, positionC){ 77 if(!positionA){ 78 var poses = runOrder.shift(); 79 if(!poses){ 80 setTimeout(reset, 500); 81 }else{ 82 moveAnimation(positions[poses[0]], positions[poses[1]]); 83 } 84 }else { 85 var barrel = positionA.barrels.pop(); 86 var position = positionC.cylinder.p3(), 87 barPos = barrel.getPosition3d(); 88 position[1] = position[1] + floorY + barrelHeight * positionC.barrels.length - cylinderHeight / 2; 89 setPolylinePoints(polyline, barPos, position); 90 params.action = function (v, t) { 91 var length = g3d.getLineLength(polyline), 92 offset = g3d.getLineOffset(polyline, length * v), 93 point = offset.point, 94 px = point.x, 95 py = point.y, 96 pz = point.z; 97 barrel.p3(px, py, pz); 98 }; 99 params.finishFunc = function () {100 positionC.barrels.push(barrel);101 var poses = runOrder.shift();102 if (!poses) {103 moveAnimation();104 } else {105 moveAnimation(positions[poses[0]], positions[poses[1]]);106 }107 };108 anim = ht.Default.startAnim(params);109 }110 }111 112 /**113 * 重置游戏114 * */115 function reset(){116 if(positions[0].barrels.length == 0){117 positions[0].barrels = positions[2].barrels;118 }119 positions[2].barrels = [];120 for(var i = 0, len = positions[0].barrels.length; i < len; i++){121 var pos = positions[0].cylinder.p3();122 pos[1] = pos[1] + floorY + i * barrelHeight - cylinderHeight / 2;123 positions[0].barrels[i].p3(pos);124 }125 buildRunOrder(barNum, 0, 1, 2);126 setTimeout(moveAnimation, 500);127 }128 129 /**130 * 初始化节点131 * */132 function initNodes(){133 // 底盘134 floor = createNode([0, floorY / 2, 0], [floorX, floorY, floorZ]).s({135 'shape3d': 'box',136 '3d.movable': false137 });138 139 // 创建柱子140 for(var i = 0, len = 3; i < len; i++){141 positions[i].cylinder = createNode(positions[i].position, [20, cylinderHeight, 20], floor).s({142 'shape3d': 'cylinder',143 'shape3d.color': '#E5BB77',144 '3d.movable': false145 });146 }147 148 // 创建圆盘149 createBarrels(barNum, positions[0].cylinder);150 151 // 创建圆盘运行轨迹152 polyline = new ht.Polyline();153 polyline.setSegments([1, 2, 4, 2]);154 polyline.s({155 'shape.background': null,156 'shape.border.color': 'rgba(0,0,0,0)',157 'shape.border.gradient.color': 'rgba(0,0,0,0)',158 'shape.border.pattern': [20, 10],159 'shape3d.resolution': 50160 });161 dataModel.add(polyline);162 }163 164 /**165 * 设置路线节点166 * */167 function setPolylinePoints(polyline, from, to){168 polyline.setPoints([169 {x: from[0], y: from[2], e: from[1]},170 {x: from[0], y: from[2], e: cylinderHeight},171 {x: from[0], y: from[2], e: cylinderHeight + 60},172 {x: to[0], y: to[2], e: cylinderHeight + 60},173 {x: to[0], y: to[2], e: cylinderHeight},174 {x: to[0], y: to[2], e: to[1]}175 ]);176 return polyline;177 }178 179 /**180 * 创建圆盘181 * barNum:圆盘个数182 * host:吸附节点183 * */184 function createBarrels(barNum, host){185 // 圆盘初始x位置186 var pos = host.p3();187 188 for(var i = barNum, j = 0; i > 0; i--, j++){189 pos[1] = barrelHeight * j + floorY;190 positions[0].barrels.push(createBarrel(pos, [1, barrelHeight, 1], barrelMinORadius + i*poorRadius, barrelIRadius, host).s({191 'shape3d.color': randomColor(),192 '3d.movable': false193 }));194 }195 }196 197 /**198 * 创建节点199 * p3:节点位置200 * s3:节点大小201 * host:吸附节点202 * */203 function createNode(p3, s3, host){204 var node = new ht.Node();205 node.p3(p3);206 node.s3(s3);207 node.setHost(host);208 node.s({209 'wf.visible': 'selected',210 'wf.color': '#FF6B10',211 'wf.width': 2,212 'wf.short': true213 });214 dataModel.add(node);215 return node;216 }217 218 /**219 * 创建空心圆柱220 * p3:圆桶位置221 * s3:圆桶大小222 * oRadius:圆桶外径223 * iRadius:圆桶内径224 * host:吸附节点225 * */226 function createBarrel(p3, s3, oRadius, iRadius, host){227 return createNode(p3, s3, host).s({228 'shape3d': ht.Default.createRingModel([229 oRadius, 1,230 oRadius, 0,231 iRadius, 0,232 iRadius, 1,233 oRadius, 1234 ], null, 20, false, false, 70)235 });236 }
新闻热点
疑难解答