Completed
Push — master ( 5d0f29...350887 )
by Kyle
01:25
created

_PathFinder._setup_map()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
1
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
from __future__ import absolute_import as _
3
4
import numpy as np
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
5
6
import tcod.map
7
8
from tcod.libtcod import lib, ffi
9
10
@ffi.def_extern()
11
def _pycall_path_old(x1, y1, x2, y2, handle):
12
    """libtcod style callback, needs to preserve the old userData issue."""
13
    func, userData = ffi.from_handle(handle)
14
    return func(x1, y1, x2, y2, userData)
15
16
17
@ffi.def_extern()
18
def _pycall_path_simple(x1, y1, x2, y2, handle):
19
    """Does less and should run faster, just calls the handle function."""
20
    return ffi.from_handle(handle)(x1, y1, x2, y2)
21
22
23
@ffi.def_extern()
24
def _pycall_path_swap_src_dest(x1, y1, x2, y2, handle):
25
    """A TDL function dest comes first to match up with a dest only call."""
26
    return ffi.from_handle(handle)(x2, y2, x1, y1)
27
28
29
@ffi.def_extern()
30
def _pycall_path_dest_only(x1, y1, x2, y2, handle):
0 ignored issues
show
Unused Code introduced by
The argument y1 seems to be unused.
Loading history...
Unused Code introduced by
The argument x1 seems to be unused.
Loading history...
31
    """A TDL function which samples the dest coordinate only."""
32
    return ffi.from_handle(handle)(x2, y2)
33
34
35
class _PathFinder(object):
36
    """
37
    .. versionadded:: 2.0
38
    """
39
40
    _C_ARRAY_CALLBACKS = {
41
        np.float32: ('float*', lib.PathCostArrayFloat32),
42
        np.bool_: ('int8_t*', lib.PathCostArrayInt8),
43
        np.int8: ('int8_t*', lib.PathCostArrayInt8),
44
        np.uint8: ('uint8_t*', lib.PathCostArrayUInt8),
45
        np.int16: ('int16_t*', lib.PathCostArrayInt16),
46
        np.uint16: ('uint16_t*', lib.PathCostArrayUInt16),
47
        np.int32: ('int32_t*', lib.PathCostArrayInt32),
48
        np.uint32: ('uint32_t*', lib.PathCostArrayUInt32),
49
        }
50
51
    def __init__(self, cost, diagonal=1.41,
52
                 width=None, height=None):
53
        """
54
55
        Args:
56
            cost (Union[tcod.map.Map,
57
                        Callable[float, int, int, int, int],
58
                        numpy.ndarray]):
59
            diagonal (float): Multiplier for diagonal movement.
60
                            A value of 0 disables diagonal movement entirely.
61
            width (int): The clipping width of this pathfinder.
62
                         Only needed if ``cost`` is a function call.
63
            height (int): The clipping height of this pathfinder.
64
                          Only needed if ``cost`` is a function call.
65
        """
66
        self.cost = cost
67
        self.width = width
68
        self.height = height
69
        self.diagonal = diagonal
70
        self.cdata = None
71
        self.handle = None
72
73
        if isinstance(cost, tcod.map.Map):
74
            self._setup_map(cost)
75
        elif callable(cost):
76
            self._setup_callback(lib._pycall_path_simple, cost)
77
        elif isinstance(cost, tuple) and len(cost) == 2 and callable(cost[0]):
78
            # hacked in support for old libtcodpy functions, don't abuse this!
79
            self._setup_callback(lib._pycall_path_old, cost)
80
        elif isinstance(cost, np.ndarray):
81
            self._setup_ndarray(cost)
82
        else:
83
            raise TypeError('cost must be a Map, function, or numpy array, '
84
                            'got %r' % (cost,))
85
86
    def _setup_map(self, cost):
87
        """Setup this pathfinder using libtcod's path_new_using_map."""
88
        self.width = cost.width
89
        self.height = cost.height
90
        self.cdata = ffi.gc(
91
            self._path_new_using_map(cost.cdata, self.diagonal),
92
            self._path_delete)
93
94
    def _setup_callback(self, c_call, args):
95
        """Setup this pathfinder using libtcod's path_new_using_function.
96
97
        The c_call will be called with (x1,y1,x2,y2,args).
98
        """
99
        self.handle = ffi.new_handle(args)
100
        self.cdata = ffi.gc(
101
                self._path_new_using_function(
102
                    self.width, self.height, c_call,
103
                    self.handle, self.diagonal),
104
                self._path_delete)
105
106
    def _setup_ndarray(self, cost):
107
        """Validate a numpy array and setup a C callback."""
108
        self.height, self.width = cost.shape # must be a 2d array
109
        if cost.dtype not in self._C_ARRAY_CALLBACKS:
110
            raise ValueError('dtype must be one of %r, dtype is %r' %
111
                             (self._C_ARRAY_CALLBACKS.keys(), cost.dtype))
112
        self.cost = cost = np.ascontiguousarray(cost)
113
        array_type, c_callback = self._C_ARRAY_CALLBACKS[cost.dtype]
114
        cost = ffi.cast(array_type, cost.ctypes.data)
115
        self._setup_callback(c_callback,
116
                             ffi.new('PathCostArray*', (self.width, cost)))
117
118
    _path_new_using_map = lib.TCOD_path_new_using_map
119
    _path_new_using_function = lib.TCOD_path_new_using_function
120
    _path_delete = lib.TCOD_path_delete
121
122
123
class AStar(_PathFinder):
124
    """
125
    .. versionadded:: 2.0
126
    """
127
128
    def get_path(self, start_x, start_y, goal_x, goal_y):
129
        """Return a list of (x, y) steps to reach the goal point, if possible.
130
131
        Args:
132
            start_x (int): Starting X position.
133
            start_y (int): Starting Y position.
134
            goal_x (int): Destination X position.
135
            goal_y (int): Destination Y position.
136
        Returns:
137
            List[Tuple[int, int]]:
138
                A list of points, or an empty list if there is no valid path.
139
        """
140
        lib.TCOD_path_compute(self.cdata, start_x, start_y, goal_x, goal_y)
141
        path = []
142
        x = ffi.new('int[2]')
143
        y = x + 1
144
        while lib.TCOD_path_walk(self.cdata, x, y, False):
145
            path.append((x[0], y[0]))
146
        return path
147
148
149
class Dijkstra(_PathFinder):
150
    """
151
    .. versionadded:: 2.0
152
    """
153
154
    _path_new_using_map = lib.TCOD_dijkstra_new
155
    _path_new_using_function = lib.TCOD_dijkstra_new_using_function
156
    _path_delete = lib.TCOD_dijkstra_delete
157
158
    def set_goal(self, x, y):
159
        """Set the goal point and recompute the Dijkstra path-finder.
160
        """
161
        lib.TCOD_dijkstra_compute(self.cdata, x, y)
162
163
    def get_path(self, x, y):
164
        """Return a list of (x, y) steps to reach the goal point, if possible.
165
        """
166
        lib.TCOD_dijkstra_path_set(self.cdata, x, y)
167
        path = []
168
        pointer_x = ffi.new('int[2]')
169
        pointer_y = pointer_x + 1
170
        while lib.TCOD_dijkstra_path_walk(self.cdata, pointer_x, pointer_y):
171
            path.append(pointer_x[0], pointer_y[0])
172
        return path
173