Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pgu/algo.py
blob: dedb020095ce09b2e93c2fb79365dc2694260492 (plain)
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
"""Some handy algorithms for use in games, etc.

<p>please note that this file is alpha, and is subject to modification in
future versions of pgu!</p>
"""
print 'pgu.algo','This module is alpha, and is subject to change.'

#def dist(a,b):
#    return abs(a[0]-b[0]) + abs(a[1]-b[1])
    
class node:
    def __init__(self,prev,pos,dest):
        self.prev,self.pos,self.dest = prev,pos,dest
        if self.prev == None: self.g = 0
        else: self.g = self.prev.g + 1
        self.h = dist(pos,dest)
        self.f = self.g+self.h


def astar(start,end,layer,_dist):
    """uses the a* algorithm to find a path
    
    <pre>astar(start,end,layer,dist): return [list of positions]</pre>
    
    <dl>
    <dt>start<dd>start position
    <dt>end<dd>end position
    <dt>layer<dd>a grid where zero cells are open and non-zero cells are walls
    <dt>dist<dd>a distance function dist(a,b)
    </dl>
    
    <p>returns a list of positions from start to end</p>
    """
    global dist
    dist = _dist
    if layer[start[1]][start[0]]: return [] #start is blocked
    if layer[end[1]][end[0]]: return [] #end is blocked
    w,h = len(layer[0]),len(layer)
    if start[0] < 0 or start[1] < 0 or start[0] >= w or start[1] >= h: return [] #start outside of layer
    if end[0] < 0 or end[1] < 0 or end[0] >= w or end[1] >= h: return [] #end outside of layer

    opens = []
    open = {}
    closed = {}
    cur = node(None,start,end)
    open[cur.pos] = cur
    opens.append(cur)
    while len(open):
        cur = opens.pop(0)
        if cur.pos not in open: continue
        del open[cur.pos]
        closed[cur.pos] = cur
        if cur.pos == end: break
        for dx,dy in [(0,-1),(1,0),(0,1),(-1,0)]:#(-1,-1),(1,-1),(-1,1),(1,1)]:
            x,y = pos = cur.pos[0]+dx,cur.pos[1]+dy
            if layer[y][x]: continue
            #check for blocks of diagonals
            if layer[cur.pos[1]+dy][cur.pos[0]]: continue
            if layer[cur.pos[1]][cur.pos[0]+dx]: continue
            new = node(cur,pos,end)
            if pos in open and new.f >= open[pos].f: continue
            if pos in closed and new.f >= closed[pos].f: continue
            if pos in open: del open[pos]
            if pos in closed: del closed[pos]
            open[pos] = new
            lo = 0
            hi = len(opens)
            while lo < hi:
                mid = (lo+hi)/2
                if new.f < opens[mid].f: hi = mid
                else: lo = mid + 1
            opens.insert(lo,new)
    
    if cur.pos != end: 
        return []
                    
    path = []
    while cur.prev != None:
        path.append(cur.pos)
        cur = cur.prev
    path.reverse()
    return path
    

def getline(a,b):
    """returns a path of points from a to b
    
    <pre>getline(a,b): return [list of points]</pre>
    
    <dl>
    <dt>a<dd>starting point
    <dt>b<dd>ending point
    </dl>
    
    <p>returns a list of points from a to b</p>
    """
           
    path = []
    
    x1,y1 = a
    x2,y2 = b
    dx,dy = abs(x2-x1),abs(y2-y1)

    if x2 >= x1: xi1,xi2 = 1,1
    else: xi1,xi2 = -1,-1
    
    if y2 >= y1: yi1,yi2 = 1,1
    else: yi1,yi2 = -1,-1
    
    if dx >= dy:
        xi1,yi2 = 0,0
        d = dx
        n = dx/2
        a = dy
        p = dx
    else:
        xi2,yi1 = 0,0
        d = dy
        n = dy/2
        a = dx
        p = dy
        
    x,y = x1,y1
    c = 0
    while c <= p:
        path.append((x,y))
        n += a
        if n > d: 
            n -= d
            x += xi1
            y += yi1
        x += xi2
        y += yi2
        c += 1
    return path