micropather实现A*算法

来源:互联网 发布:常州协同工作软件 编辑:程序博客网 时间:2024/06/04 08:57

MicroPather is a path finder and A* solver (astar or a-star) written in platform independent C++ that can be easily integrated into existing code. MicroPather focuses on being a path finding engine for video games but is a generic A* solver.

There is plenty of pathing code out there, but most if it seems to focus on teaching somewhat how to write an A* solver, rather than being utility code for pathing. MicroPather is firmly aimed at providing functionality, not at being a tutorial.

MicroPather's primary goal is to be easy to use:

  1. An easy API
  2. No requirements on the host program to change its data structures or objects
  3. No library or 'make' - just add 1 .cpp and 1 .h file to your project.

Enjoy, and thanks for checking out MicroPather!


官方网站可以下载其sdk和demo。

demo1的代码如下:

dungeon.cpp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#define USE_PATHER
  
#include <ctype.h>
#include <stdio.h>
#include <memory.h>
#include <math.h>
  
#include <vector>
#include <iostream>
  
#ifdef USE_PATHER
  
#include "micropather.h"
usingnamespacemicropather;
#endif
  
  
constintMAPX = 30;
constintMAPY = 10;
constchargMap[MAPX*MAPY+1] =
//"012345678901234567890123456789"
"     |      |                |"
"     |      |----+    |      +"
"---+ +---DD-+      +--+--+    "
"   |                     +-- +"
"        +----+  +---+         "
"---+ +  D    D            |   "
"   | |  +----+    +----+  +--+"
"   D |            |    |      "
"   | +-------+  +-+    |--+   "
"---+                   |     +";
  
classDungeon
#ifdef USE_PATHER
    :publicGraph
#endif
{
private:
    Dungeon(constDungeon& );
    voidoperator=(constDungeon& );
  
    // 寻路的起始点位置
    intplayerX, playerY;
  
    // 寻路的最终路径
    std::vector<void*> path;
  
    // 门是否打开
    booldoorsOpen;
    // 
    boolshowConsidered;
  
    MicroPather* pather;
  
public:
    Dungeon() : playerX( 0 ), playerY( 0 ), doorsOpen(false), showConsidered(false), pather( 0 )
    {
        // this : The "map" that implements the Graph callbacks. 
        // 20 : How many states should be internally allocated at a time. 
        // This can be hard to get correct. The higher the value, 
        // the more memory MicroPather will use.
        pather =newMicroPather(this, 20 );  // Use a very small memory block to stress the pather
    }
  
    virtual~Dungeon() 
    {
        deletepather;
    }
  
    intX() {returnplayerX; }
    intY() {returnplayerY; }
  
    // 校验和,用于debug
    unsigned Checksum() {returnpather->Checksum(); }
  
    voidClearPath()
    {
#ifdef USE_PATHER
        path.resize( 0 );
#endif
    }
  
    // 
    voidToggleTouched() 
    {   
        showConsidered = !showConsidered; 
        // Reset() Should be called whenever the cost between states or 
        // the connection between states changes. 
        // Also frees overhead memory used by MicroPather, 
        // and calling will free excess memory. 
        pather->Reset();                  
    }
  
    // 打开或者关闭Door
    voidToggleDoor() 
    
        doorsOpen = !doorsOpen; 
#ifdef USE_PATHER
        pather->Reset();
#endif  
    }
  
    // 目标点(nx, ny)是否可到达(" "或者"D")
    intPassable(intnx, int ny ) 
    {
        if( nx >= 0 && nx < MAPX 
            && ny >= 0 && ny < MAPY )
        {
            intindex = ny * MAPX + nx;
            charc = gMap[index];
            if( c ==' ' )
                return1;
            elseif( c == 'D')
                return2;
        }       
        return0;
    }
  
    // 计算起始位置到(nx,ny)的路径及其代价
    intSetPos(int nx, int ny ) 
    {
        intresult = 0;
        if( Passable( nx, ny ) == 1 )
        {
#ifdef USE_PATHER
            floattotalCost;
            if( showConsidered )
                pather->Reset();
            /*
            int micropather::MicroPather::Solve  
            ( void * startState,  
              void *  endState,  
              std::vector< void * > *  path,  
              float *  totalCost   
            
            Solve for the path from start to end.
            Parameters:
            startState:  Input, the starting state for the path.  
            endState:  Input, the ending state for the path.  
            path:  Output, a vector of states that define the path. Empty if not found.  
            totalCost:  Output, the cost of the path, if found.  
  
            Returns:
            Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME. 
            */
            result = pather->Solve( XYToNode( playerX, playerY ), XYToNode( nx, ny ), &path, &totalCost );
  
            if( result == MicroPather::SOLVED ) 
            {
                // 将玩家位置设置为(nx, ny)
                playerX = nx;
                playerY = ny;
            }
            printf("Pather returned %d\n", result );
#else
            playerX = nx;
            playerY = ny;
#endif
        }
        returnresult;
    }
  
    voidPrint() 
    {
        charbuf[ MAPX + 1 ];
        std::vector<void*> stateVec;
  
        if( showConsidered )
            pather->StatesInPool(&stateVec);
        printf(" doors %s\n", doorsOpen ?"open": "closed");
        printf(" 0         10        20\n");
        printf(" 012345678901234567890123456789\n");
        for(intj=0; j<MAPY; ++j ) 
        {
            // 按行复制, 并输出
            memcpy(buf, &gMap[MAPX * j], MAPX + 1);
            buf[MAPX] = 0;
#ifdef USE_PATHER
            unsigned k;
            // Wildly inefficient demo code.
            for( k=0; k<path.size(); ++k )
            {
                intx, y;
                NodeToXY( path[k], &x, &y );
                if( y == j )
                    buf[x] ='0'+ k % 10;
            }
            if( showConsidered )
            {
                for( k=0; k<stateVec.size(); ++k ) 
                {
                    intx, y;
                    NodeToXY( stateVec[k], &x, &y );
                    if( y == j )
                        buf[x] ='x';
                }     
            }       
#endif
            // Insert the player
            if( j == playerY )
                buf[playerX] ='i';
            printf("%d%s\n", j % 10, buf );
        }
    }
  
#ifdef USE_PATHER
    voidNodeToXY(void* node,int* x,int* y ) 
    {
        intindex = (int)node;
        *y = index / MAPX;
        *x = index - *y * MAPX;
    }
  
    void* XYToNode(intx, int y )
    {
        return(void*) ( y*MAPX + x );
    }
  
    // 最小代价
    virtualfloatLeastCostEstimate(void* nodeStart,void* nodeEnd ) 
    {
        intxStart, yStart, xEnd, yEnd;
        NodeToXY( nodeStart, &xStart, &yStart );
        NodeToXY( nodeEnd, &xEnd, &yEnd );
  
        /* Compute the minimum path cost using distance measurement. It is possible
        to compute the exact minimum path using the fact that you can move only 
        on a straight line or on a diagonal, and this will yield a better result.
        */
        intdx = xStart - xEnd;
        intdy = yStart - yEnd;
        return(float)sqrt( (double)(dx*dx) + (double)(dy*dy) );
    }
  
    // Return the exact cost from the given state to all its neighboring states. 
    // This may be called multiple times, or cached by the solver.
    virtualvoidAdjacentCost( void* node, std::vector< StateCost > *neighbors ) 
    {
        intx, y;
        // 在X,Y轴上8个方向   E   SE  S   SW     W    NW  N   NE
        constintdx[8] =     { 1,  1,  0, -1,    -1,  -1,  0,  1 };
        constintdy[8] =     { 0,  1,  1,  1,     0,  -1, -1, -1 };
        // X,Y轴上8个方向的代价
        constfloatcost[8] = { 1.0f, 1.41f, 1.0f, 1.41f, 1.0f, 1.41f, 1.0f, 1.41f };
  
        NodeToXY( node, &x, &y );
  
        // 得到与该点相邻的8个方向的点的坐标;计算是否可通过;将代价push到vector中
        for(inti=0; i<8; ++i ) 
        {
            intnx = x + dx[i];
            intny = y + dy[i];
  
            intpass = Passable( nx, ny );
            if( pass > 0 ) 
            {
                if( pass == 1 || doorsOpen ) 
                {
                    // Normal floor
                    StateCost nodeCost = 
                    
                        // (nx, ny)点的索引号
                        XYToNode( nx, ny ), 
                        // node到该点的代价
                        cost[i] 
                    };
                    neighbors->push_back( nodeCost );
                }
                else 
                {
                    // 若不可pass则代价为FLT_MAX
                    StateCost nodeCost = { XYToNode( nx, ny ), FLT_MAX };
                    neighbors->push_back( nodeCost );
                }
            }
        }
    }
  
    virtualvoidPrintStateInfo(void* node ) 
    {
        intx, y;
        NodeToXY( node, &x, &y );
        printf("(%d,%d)", x, y );
    }
  
#endif
};
  
intmain(int /*argc*/,constchar**/*argv*/)
{
    {
        Dungeon test;
        constintNUM_TEST = 5;
        inttx[NUM_TEST]    = { 24, 25, 10, 6,  0   }; // x of test
        intty[NUM_TEST]    = { 9,  9,  5,  5,  0   }; // y of test
        intdoor[NUM_TEST]  = { 0,  0,  0,  1,  0   }; // toggle door? (before move)
        unsigned check[NUM_TEST] = { 139640, 884, 0, 129313, 2914 };
        for(inti=0; i<NUM_TEST; ++i )
        {
            // (25,9)到(10, 5)时, (10, 5)正好被不通路包围的中心,这个不通路有2扇门,
            // 此时2扇门没打开,所以(25,9)到(10, 5)不通!
            // (10,5)到(6,5)之间正好有一扇门,若门关闭则此路不通,门打开则可以通
            if( door[i] )
                test.ToggleDoor();
            int_result = test.SetPos( tx[i], ty[i] );
            if( _result == MicroPather::SOLVED ) 
            {
                // Return the "checksum" of the last path returned by Solve(). 
                // Useful for debugging, and a quick way to see if 2 paths are the same. 
                unsigned checkNum = test.Checksum();
  
                if( checkNum == check[i] )
                    printf("Test %d to (%d,%d) ok\n", i, tx[i], ty[i] );
                else
                    printf("Test %d to (%d,%d) BAD CHECKSUM\n", i, tx[i], ty[i] );
            }
            elseif(_result == MicroPather::NO_SOLUTION)
            {
                printf("Test %d to (%d,%d) no solution\n", i, tx[i], ty[i] );
            }
            elseif(_result == MicroPather::START_END_SAME)
            {
                printf("Test %d to (%d,%d) start end same\n", i, tx[i], ty[i] );
            }
        }
    }
  
    Dungeon dungeon;
    booldone =false;
    charbuf[ 256 ];
    while( !done ) 
    {
        dungeon.Print();
  
        printf("\n# # to move, q to quit, r to redraw, d to toggle doors, t for touched\n");
        std::cin.getline( buf, 256 );
        if( *buf )
        {
            if( buf[0] =='q'
                done =true;
            elseif( buf[0] == 'd'
            {
                dungeon.ToggleDoor();
                dungeon.ClearPath();
            }
            elseif( buf[0] == 't'
                dungeon.ToggleTouched();      
            elseif( buf[0] == 'r'
                dungeon.ClearPath();
            elseif( isdigit( buf[0] ) ) 
            {
                intx, y;
                sscanf( buf,"%d %d", &x, &y );// sleazy, I know
                dungeon.SetPos( x, y );
            
        }
        else            
            dungeon.ClearPath();
    }
    return0;
}

原创粉丝点击