Bootstrap

天神降临,大家过来膜拜吧! FLASH AS 3.0 A星(A*, A star) 寻路算法--史上最快,极限优化挑战!


天神降临,大家过来膜拜吧!

 oh yeah!

转载请声明出处,例子代码可以免费随意使用,但请保留或注明作者信息.

 这里的算法说是终极优化, 我挑战了一下,

http://eidiot.net/2007/04/17/a-star-pathfinding/

最终结果比较他快三倍, 我站在高高处,藐视了 一下作者. 偷笑

优化思路:

             a.开包(open list)算法优化: 

                线性查找-->

                二叉堆(binary heap 容器:Array->Vector)->

                二分排序插入/删除(容器:Array)
          b. 障碍点容器优化: 

                       dictionary(1维) ->dictionary(2维)--  array(1维)->array(2维)->bitmapdata->bytearray(8位对齐)->byteArrayarray(32位对齐)->vector. <int>
          c. 闭包(close list)容器优化:

                     dictionary(1维) -> array(1维)->bitmapdata->vector. <int>
           d. 过程生成的路点对象内存管理优化:

                           AS动态对象(Object)->非动态对象(自定义PathNode对象)->使用bytearray自己管理内存分配对象-->使用Vector自已管理内存分配对象.
           e. 评估函数:

                        标准(F = G+H, G=Parent.G+DIR_G, H = (ABS(X1-X)+ABS(Y1-Y) )*10 )

             

上述优化基本上是  左->右 == 慢->快

 

测试结果部分

 

单位:毫秒

地图大小:500x500地图:

 

 

从41条测试线路的平均值结果,  足足快3倍, 比较未优化但是快了25倍以上


未优化                                                                             10886 ms
所谓终极优化                                                                1361.414634  ms
二分插入排序 ByteArray做内存管理 (32bit对齐 )   479.195122 ms
二分插入排序Vector做内存管理                                  447.2439024 ms

 

 

startX

startY

endX

endY

距离

未优化

所谓

终极

优化

二分插入排序ByteArray做内存管理(32bit对齐)

二分插入排序

Vector做内存管理

36

32

113

125

120.7394

17

8

10

11

113

125

34

29

124.3262

16

7

11

11

34

29

469

463

614.4762

9133

1338

544

493

469

463

152

155

441.9876

超时

8536

2810

2560

152

155

169

165

19.72308

17763

2743

957

894

169

165

465

463

420.0238

1159

219

101

97

465

463

165

143

438.6342

超时

8385

2594

2470

165

143

220

146

55.08176

19685

2958

1031

965

220

146

229

139

11.40175

725

234

124

116

229

139

111

477

358.0056

827

168

72

70

111

477

327

350

250.5694

1705

364

164

158

327

350

319

340

12.80625

210

71

38

38

319

340

464

477

199.4843

51

20

18

17

464

477

71

26

598.2057

20912

2370

801

774

71

26

465

468

592.1149

6158

922

362

348

465

468

119

188

445.1022

超时

7749

2499

2372

119

188

219

314

160.8602

3625

804

309

286

219

314

79

401

164.8302

28

11

10

12

79

401

229

309

175.9659

141

35

24

23

229

309

91

432

184.8594

41

13

13

12

91

432

65

446

29.52965

2

1

7

6

65

446

227

310

211.5183

100

21

16

14

227

310

62

444

212.5582

78

15

13

14

62

444

354

26

509.8902

644

89

38

36

354

26

479

27

125.004

32

14

14

13

479

27

27

469

632.193

352

48

29

29

27

469

10

18

451.3203

3733

936

384

354

10

18

473

463

642.1791

9152

1331

534

493

473

463

100

260

424.6622

5947

782

268

255

100

260

234

152

172.1046

2182

531

241

226

234

152

248

135

22.02272

超时

0

113

0

234

152

249

137

21.2132

598

193

113

104

249

137

120

234

161.4001

5200

951

401

377

120

234

154

203

46.01087

1557

418

214

201

154

203

404

237

252.3014

4173

882

357

335

404

237

11

15

451.3679

8876

1331

572

543

11

15

230

409

450.7738

55030

7070

2308

2179

230

409

240

97

312.1602

1541

325

161

152

240

97

25

19

228.7116

1819

375

181

167

25

19

148

156

184.1141

29

10

12

9

148

156

169

188

38.27532

23085

3540

1179

1103

平均ms

       

10886

1361.415

479.2

447

 

代码:FindPathImplVetorMem.as


/**
@author aerror
*/
package 
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	public class FindPathImplVetorMem  
	{
		public static const BLOCK_TYPE_NULL:int = 0;
		public static const BLOCK_TYPE_STATIC:int = 1;
		public static const BLOCK_TYPE_ENTRY:int = 2;
		public static const BLOCK_TYPE_ITEM:int = 4;
		
		private var obstacles:Vector.<int>;
		private var iwidth:uint;
		private var iheight:uint;
		private var nearby:Vector.<int>;
		private var nearbyStraight:Vector.<int>;
		private var nearbyCross:Vector.<int>;
		
		private static const STATE_NULL:uint = 0; 
		private static const STATE_CLOSE:uint = 1;
		
		/**
		 * 
		 * 
		 * */
		public function FindPathImplVetorMem()
		{
			
			nearbyStraight = new Vector.<int>();
			nearbyStraight.push(
				-1,-1,14,
				0,-1,10,
				1,-1,14,
				1,0,10,
				1,1,14,
				0,1,10,
				-1,1,14,
				-1,0,10
			);
			
			nearbyCross = new Vector.<int>();
			nearbyCross.push(
				-1,-1,10,
				0,-1,14,
				1,-1,10,
				1,0,14,
				1,1,10,
				0,1,14,
				-1,1,10,
				-1,0,14
			);
			nearby = nearbyStraight;
		}
		
		public function initSys():void
		{
			
		}
		
		public function clearBlock():void
		{
			if(obstacles!=null)
			{
				//obstacles.dispose();
				obstacles = null;
			}
		}
		
		public function initMap(_mapCol:int, _mapRow:int, bytes:ByteArray):void
		{
			clearBlock();
			this.iwidth = _mapCol;
			this.iheight = _mapRow;
			obstacles = new Vector.<int>(iwidth*iheight);
			
			var num:int = bytes.length/6;
			for(var i:int=0;i<num;i++)
			{
				var x:int= bytes.readUnsignedShort();
				var y:int= bytes.readUnsignedShort();
				var t:int = bytes.readUnsignedShort();
				obstacles[x+y*_mapRow] = t;
				
				if(t==0)
				{
					trace("BLOCK_TYPE_NULL inited");
					return;
				}
			}
			
			
		}
		
		public function checkBlock(cellX:int, cellY:int, cellType:int):Boolean
		{
			
			if(obstacles!=null &&  obstacles[cellX + cellY*iwidth] ==cellType)
			{
				return true;
			}
			return false;
		}
		public function addBlock(cellX:int, cellY:int,blockType:int):void
		{
			
			if(obstacles==null)
			{
				return;	
			}
			obstacles[cellX + cellY*iwidth] = blockType;
		}
		
		public function removeBlock(cellX:int, cellY:int,blockType:int):void
		{
			if(obstacles==null )
			{
				return;	
			}
			obstacles[cellX + cellY*iwidth] = BLOCK_TYPE_NULL;
		}
		
		
		
		public function findPath(startX:int, startY:int, endX:int, endY:int, distance:int ):ByteArray
		{
			var ret:ByteArray =  new ByteArray();
			ret.endian = Endian.LITTLE_ENDIAN;
		
			
			var width:uint = iwidth;
			var height:uint = iheight;
			var nodelist:PathNodeFactoryVector = new PathNodeFactoryVector(width * height);
			
			var key:uint;
			
			
			var opneListSortedArr:Array = new Array();
			var opneNum:int=0;
			var minIndex:uint = 0;
			
			var stateMap:Vector.<uint>= new Vector.<uint>(width * height);
			
			var startPoint:uint = nodelist.createNode(startX,startY,0,0,PathNodeFactoryVector.NULL);
			key = x + width * y;
			opneListSortedArr.push(startPoint);
			stateMap[key]=startPoint;
			opneNum = 1;
			minIndex = 0;
			
			var found_end_point:uint = PathNodeFactoryVector.NULL;
			
			var x:int;
			var y:int;
			var g:int;
			var h:Number;
			var f:int;
			var i:int;
			var min:uint = PathNodeFactoryVector.NULL;
		
			
			while(true)
			{
				
				var min_key:uint; 
				var min_x:uint;
				var min_y:uint;
				var min_f:uint;
				var min_g:uint;
				
				//find the min g+h  point
				//
				if(opneNum >0)
				{
					//remove from openlist;
					min = opneListSortedArr.shift();

					min_x    = nodelist.getNodeX(min);
					min_y    = nodelist.getNodeY(min);
					min_g    = nodelist.getNodeG(min);
					min_key  = min_x + width * min_y;
					opneNum--;

					
				}
				else
				{
					trace("FIND PATH FAILED");
					break;
				}
				
				//add to close list;
				stateMap[min_key] = STATE_CLOSE;// = min;
				
				
				
				//add nearby point to openlist
				for( i  = 0;i<8;i++)
				{
					x = min_x + nearby[i*3];
					y = min_y + nearby[i*3+1];
					
					key = x + width * y;
					
					//calc h
					//
					h = ((endX<x)?(x-endX):(endX-x)) +
						((endY<y)?(y-endY):(endY-y));
					//we find the path when openlist contains the endpoint
					//
					if(h<=distance)
					{
						found_end_point = nodelist.createNode(x,y,0,0,min);
						break;
					}
					h = h*10;
					
				
					if(x >= width || x < 0 //out of bounds ?
						|| y >= height || y < 0//out of bounds ?
						|| obstacles[key]!=BLOCK_TYPE_NULL //is obstacle ?
					)
					{
						continue;
					}
					
					// if in close list?
					var state:uint = stateMap[key];
					if(state==STATE_CLOSE)
					{
						continue;
					}
					
					//calc g
					//
					g = (nearby[i*3+2]+min_g);
					f = g+h;
					
					
					//check if new point or old
					//
					var newPt:uint;
					//the old point
					//key key its g value,update if new g is less
					//
					if(state==STATE_NULL)
					{
						//the new point
						//
						newPt =  nodelist.createNode(x,y,g,f,min);						
						stateMap[key] = newPt;
						nodelist.sortInsert(opneListSortedArr,newPt,0,opneNum);
						
						
						opneNum++;
					}
					else if(g<nodelist.getNodeG(state)) 
					{
						newPt = nodelist.createNode(x,y,g,f,min);
						nodelist.sortRemove(opneListSortedArr,state,0,opneNum);
					
						stateMap[key] = newPt;
						nodelist.sortInsert(opneListSortedArr,newPt,0,opneNum-1);
					}
					
				}
				
				
				//just break the upper loop
				//
				if(found_end_point !=PathNodeFactoryVector.NULL)
				{
					break;
				}
				
			}
			
			//stateMap.dispose();
			//stateMap =null;
			
			//encode it to bytearray. :(
			//
			if(found_end_point !=PathNodeFactoryVector.NULL)
			{
				var pt:uint = found_end_point; 
				var num:int=0;
				while(pt!=PathNodeFactoryVector.NULL)
				{
					num++;
					pt =nodelist.getNodeParent(pt);
				}
				ret.length = num*4;
				pt  = found_end_point;
				i =0;
				while(pt!=PathNodeFactoryVector.NULL)
				{
					ret.position = (num-i-1)*4;
					ret.writeShort(nodelist.getNodeX(pt));
					ret.writeShort(nodelist.getNodeY(pt));
					i++;
					pt = nodelist.getNodeParent(pt);
				}
				
				ret.position = 0;
			}
			
			//clear used memory
			//
			trace("used length :" + nodelist.getUsedLength() + "open num" + opneListSortedArr.length);
			nodelist.clear();
			stateMap.length = 0;
			opneListSortedArr.length =0;
			return ret;
		}
		
		
	}
}



PathNodeFactoryVector.as

/**
@author aerror
*/

package 
{
	public class PathNodeFactoryVector
	{
		public static const NULL:uint = 0;
		private var mem:Vector.<uint>;
		private var writePosition:uint =0;
		public function PathNodeFactoryVector(max:uint)
		{
			mem  = new Vector.<uint>(max*4+4);	
			writePosition = 4;
			
		}
		
		
		public function getNodeX(id:uint):uint
		{
			return mem[id]&0xffff;
		}
		
		public function getNodeY(id:uint):uint
		{
			return mem[id]>>16 &0xffff;
		}
		
		public function getNodeG(id:uint):uint
		{
			return mem[id+1];
		}
		
		
		public function getNodeF(id:uint):uint
		{
			return mem[id+2];
		}
		
		
		public function getNodeParent(id:uint):uint
		{
			return mem[id+3];	
		}
		
		
		
		
		public function createNode(_x:uint,_y:uint,_g:uint, _f:uint,_linkParent:uint):uint
		{
			mem[writePosition + 0] = (_x | _y<<16 );	
			mem[writePosition + 1]= _g;
			mem[writePosition + 2]= _f;
			mem[writePosition + 3]= _linkParent;
			writePosition += 4;
			return writePosition-4;
		}
		
		
		public function checkOK(arr:Array):Boolean
		{
			for(var i:int=0;i < arr.length-1;i++)
			{
				if(getNodeF(arr[i]) >getNodeF(arr[i+1]))
				{
					return false;
				}
			}
			
			return true;
		}
		
		public function sortInsert(arr:Array, newPt:uint, begin:int, end:int):void
		{
			//optimize for empty array
			if(arr.length==0)
			{
				arr.push(newPt);
				return;
			}
			
			var new_F:uint = getNodeF(newPt);
			var cur_F:uint = getNodeF(arr[begin]);
			//optimize for sorted inserting with less
			if(new_F <= cur_F )
			{
				arr.splice(0,0,newPt);
				return;
			}
			//optimize for sorted inserting with larger
			cur_F = getNodeF(arr[end-1]);
			if(new_F  >cur_F)
			{
				arr.splice(end,0,newPt);	
				return;
			}
			else if(new_F == cur_F)
			{
				arr.splice(end-1,0,newPt);	
				return;
			}
			
			while(true)
			{
				if(begin==end)
				{
					cur_F = getNodeF(arr[begin]);
					if(new_F > cur_F)
						arr.splice(begin+1,0,newPt);
					else
						arr.splice(begin,0,newPt);
					return ;
				}
				
				
				
				//find a place
				var mid:int = begin+(end-begin)/2;
				cur_F =getNodeF(arr[mid]);
				if(new_F < cur_F)
				{ 
					end = mid;
				}
				else if(new_F > cur_F) 
				{
					begin = mid +1; 	
				}
				else 
				{
					arr.splice(mid,0,newPt);
					return; 
				}
			}
			
		}
		
		
		
					
		public function sortRemove(arr:Array, oldPt:uint, begin:int, end:int):void
		{
			var old_f:uint = getNodeF(oldPt);
			var i:int;
			
			while(true)
			{
				
				//find a place
				var mid:int = begin+(end-begin)/2;
				var cur_f:uint = getNodeF(arr[mid]);
				if(old_f < cur_f)
				{ 
					end = mid;
				}
				else if(old_f > cur_f)
				{
					begin = mid +1; 	
				}
				else
				{
					for(i =mid; i<end ; i++)
					{
						if(oldPt==arr[i])
						{
							arr.splice(i,1);
							return ;		
						}
					}
					
					for(i = mid-1; i>=0 ; i--)
					{
						if(oldPt==arr[i])
						{
							arr.splice(i,1);
							return ;		
						}
					}
					
					return ;
				}
			}
			
		}
		
		public function clear():void
		{
			this.mem.length = 0;
		}
		public function getUsedLength():uint
		{
			return this.writePosition;
		}
		
		public function dumpArray(opneListSortedArr:Array):void
		{
			for(var j:int =0;j<opneListSortedArr.length;j++)
			{
				trace(getNodeF(opneListSortedArr[j]));
			}
		}
	}
	

}



;