EdgeCostCallback   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 15
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 10
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 3 1
1
"""
2
3
Example::
4
5
    >>> import numpy as np
6
    >>> import tcod.path
7
    >>> dungeon = np.array(
8
    ...     [
9
    ...         [1, 0, 1, 1, 1],
10
    ...         [1, 0, 1, 0, 1],
11
    ...         [1, 1, 1, 0, 1],
12
    ...     ],
13
    ...     dtype=np.int8,
14
    ...     )
15
    ...
16
    >>> height, width = dungeon.shape
17
18
    # Create a pathfinder from a numpy array.
19
    # This is the recommended way to use the tcod.path module.
20
    >>> astar = tcod.path.AStar(dungeon)
21
    >>> print(astar.get_path(0, 0, 4, 2))
22
    [(0, 1), (1, 2), (2, 1), (3, 0), (4, 1), (4, 2)]
23
    >>> astar.cost[0, 1] = 1 # You can access the map array via this attribute.
24
    >>> print(astar.get_path(0, 0, 4, 2))
25
    [(1, 0), (2, 0), (3, 0), (4, 1), (4, 2)]
26
27
    # Create a pathfinder from an edge_cost function.
28
    # Calling Python functions from C is known to be very slow.
29
    >>> def edge_cost(my_x, my_y, dest_x, dest_y):
30
    ...     return dungeon[dest_y, dest_x]
31
    ...
32
    >>> dijkstra = tcod.path.Dijkstra(
33
    ...     tcod.path.EdgeCostCallback(edge_cost, width, height),
34
    ...     )
35
    ...
36
    >>> dijkstra.set_goal(0, 0)
37
    >>> print(dijkstra.get_path(4, 2))
38
    [(1, 0), (2, 0), (3, 0), (4, 1), (4, 2)]
39
"""
40
41
from __future__ import absolute_import
42
43
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...
44
45
from tcod.libtcod import lib, ffi
46
47
48
@ffi.def_extern()
49
def _pycall_path_old(x1, y1, x2, y2, handle):
0 ignored issues
show
Coding Style Naming introduced by
The name x1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name x2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
50
    """libtcodpy style callback, needs to preserve the old userData issue."""
51
    func, userData = ffi.from_handle(handle)
0 ignored issues
show
Coding Style Naming introduced by
The name userData does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
52
    return func(x1, y1, x2, y2, userData)
53
54
55
@ffi.def_extern()
56
def _pycall_path_simple(x1, y1, x2, y2, handle):
0 ignored issues
show
Coding Style Naming introduced by
The name x1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name x2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
57
    """Does less and should run faster, just calls the handle function."""
58
    return ffi.from_handle(handle)(x1, y1, x2, y2)
59
60
61
@ffi.def_extern()
62
def _pycall_path_swap_src_dest(x1, y1, x2, y2, handle):
0 ignored issues
show
Coding Style Naming introduced by
The name x1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name x2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
63
    """A TDL function dest comes first to match up with a dest only call."""
64
    return ffi.from_handle(handle)(x2, y2, x1, y1)
65
66
67
@ffi.def_extern()
68
def _pycall_path_dest_only(x1, y1, x2, y2, handle):
0 ignored issues
show
Coding Style Naming introduced by
The name x1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y1 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name x2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y2 does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Unused Code introduced by
The argument x1 seems to be unused.
Loading history...
Unused Code introduced by
The argument y1 seems to be unused.
Loading history...
69
    """A TDL function which samples the dest coordinate only."""
70
    return ffi.from_handle(handle)(x2, y2)
71
72
def _get_pathcost_func(name):
73
    """Return a properly cast PathCostArray callback."""
74
    return ffi.cast('TCOD_path_func_t', ffi.addressof(lib, name))
75
76
77
class _EdgeCostFunc(object):
78
    """
79
    Args:
80
        userdata (Any): Custom userdata to send to the C call.
81
        width (int): The maximum width of this callback.
82
        height (int): The maximum height of this callback.
83
    """
84
    _CALLBACK_P = lib._pycall_path_old
85
86
    def __init__(self, userdata, width, height):
87
        self._userdata = userdata
88
        self.width = width
89
        self.height = height
90
91
    def get_tcod_path_ffi(self):
92
        """
93
94
        Returns:
95
            Tuple[CData, CData]: A cffi (callback, userdata) pair.
96
97
98
        """
99
        return (self._CALLBACK_P, ffi.new_handle(self._userdata),
100
                self.width, self.height)
101
102
    def __repr__(self):
103
        return '%s(%r, width=%r, height=%r)' % (
104
            self.__class__.__name__,
105
            self._userdata, self.width, self.height,
106
            )
107
108
109
class EdgeCostCallback(_EdgeCostFunc):
110
    """Calculate cost from an edge-cost callback.
111
112
    Args:
113
        callback (Callable[[int, int, int, int], float]):
114
            A callback which can handle the following parameters:
115
            `(source_x:int, source_y:int, dest_x:int, dest_y:int) -> float`
116
        width (int): The maximum width of this callback.
117
        height (int): The maximum height of this callback.
118
    """
119
    _CALLBACK_P = lib._pycall_path_simple
120
121
    def __init__(self, callback, width, height):
122
        self.callback = callback
123
        super(EdgeCostCallback, self).__init__(callback, width, height)
124
125
class NodeCostArray(np.ndarray):
126
    """Calculate cost from a numpy array of nodes.
127
128
    Args:
129
        array (numpy.ndarray): A numpy array with the cost of each node.
130
    """
131
132
    _C_ARRAY_CALLBACKS = {
133
        np.float32: ('float*', _get_pathcost_func('PathCostArrayFloat32')),
134
        np.bool_: ('int8_t*', _get_pathcost_func('PathCostArrayInt8')),
135
        np.int8: ('int8_t*', _get_pathcost_func('PathCostArrayInt8')),
136
        np.uint8: ('uint8_t*', _get_pathcost_func('PathCostArrayUInt8')),
137
        np.int16: ('int16_t*', _get_pathcost_func('PathCostArrayInt16')),
138
        np.uint16: ('uint16_t*', _get_pathcost_func('PathCostArrayUInt16')),
139
        np.int32: ('int32_t*', _get_pathcost_func('PathCostArrayInt32')),
140
        np.uint32: ('uint32_t*', _get_pathcost_func('PathCostArrayUInt32')),
141
        }
142
143
    def __new__(cls, array):
144
        """Validate a numpy array and setup a C callback."""
145
        self = np.ascontiguousarray(array).view(cls)
146
        return self
147
148
    @property
149
    def width(self):
0 ignored issues
show
Coding Style introduced by
This method 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...
150
        return self.shape[1]
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named shape.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
151
152
    @property
153
    def height(self):
0 ignored issues
show
Coding Style introduced by
This method 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...
154
        return self.shape[0]
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named shape.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
155
156
    def __repr__(self):
157
        return '%s(%r)' % (self.__class__.__name__,
158
                           repr(self.view(np.ndarray)))
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named view.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
159
160
    def get_tcod_path_ffi(self):
0 ignored issues
show
Coding Style introduced by
This method 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...
161
        if len(self.shape) != 2:
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named shape.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
162
            raise ValueError('Array must have a 2d shape, shape is %r' %
163
                             (self.shape,))
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named shape.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
164
        if self.dtype.type not in self._C_ARRAY_CALLBACKS:
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named dtype.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
165
            raise ValueError('dtype must be one of %r, dtype is %r' %
166
                             (self._C_ARRAY_CALLBACKS.keys(), self.dtype.type))
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named dtype.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
167
168
        array_type, callback = \
169
            self._C_ARRAY_CALLBACKS[self.dtype.type]
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named dtype.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
170
        userdata = ffi.new(
171
            'PathCostArray*',
172
            (self.width, ffi.cast(array_type, self.ctypes.data)),
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named ctypes.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
173
        )
174
        return callback, userdata, self.width, self.height
175
176
177
class _PathFinder(object):
178
    """A class sharing methods used by AStar and Dijkstra."""
179
180
    def __init__(self, cost, diagonal=1.41):
181
        self.cost = cost
182
        self.diagonal = diagonal
183
        self._path_c = None
184
        self._callback = self._userdata = None
185
186
        if hasattr(self.cost, 'map_c'):
187
            self.width, self.height = self.cost.width, self.cost.height
188
            self._path_c = ffi.gc(
189
                self._path_new_using_map(self.cost.map_c, diagonal),
0 ignored issues
show
Bug introduced by
The Instance of NodeCostArray does not seem to have a member named map_c.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
190
                self._path_delete,
191
                )
192
            return
193
194
        if not hasattr(self.cost, 'get_tcod_path_ffi'):
195
            assert not callable(self.cost), \
196
                "Any callback alone is missing width & height information. " \
197
                "Wrap your callback in tcod.path.EdgeCostCallback"
198
            self.cost = NodeCostArray(self.cost)
199
200
        self._callback, self._userdata, self.width, self.height = \
201
            self.cost.get_tcod_path_ffi()
202
        self._path_c = ffi.gc(
203
            self._path_new_using_function(
204
                self.width,
205
                self.height,
206
                self._callback,
207
                self._userdata,
208
                diagonal
209
                ),
210
            self._path_delete,
211
            )
212
213
    def __repr__(self):
214
        return '%s(cost=%r, diagonal=%r)' % (self.__class__.__name__,
215
                                             self.cost, self.diagonal)
216
217
    def __getstate__(self):
218
        state = self.__dict__.copy()
219
        del state['_path_c']
220
        del state['width']
221
        del state['height']
222
        del state['_callback']
223
        del state['_userdata']
224
        return state
225
226
    def __setstate__(self, state):
227
        self.__dict__.update(state)
228
        self.__init__(self.cost, self.diagonal)
229
230
    _path_new_using_map = lib.TCOD_path_new_using_map
231
    _path_new_using_function = lib.TCOD_path_new_using_function
232
    _path_delete = lib.TCOD_path_delete
233
234
235
class AStar(_PathFinder):
236
    """
237
    Args:
238
        cost (Union[tcod.map.Map, numpy.ndarray, Any]):
239
        diagonal (float): Multiplier for diagonal movement.
240
            A value of 0 will disable diagonal movement entirely.
241
    """
242
243
    def get_path(self, start_x, start_y, goal_x, goal_y):
244
        """Return a list of (x, y) steps to reach the goal point, if possible.
245
246
        Args:
247
            start_x (int): Starting X position.
248
            start_y (int): Starting Y position.
249
            goal_x (int): Destination X position.
250
            goal_y (int): Destination Y position.
251
        Returns:
252
            List[Tuple[int, int]]:
253
                A list of points, or an empty list if there is no valid path.
254
        """
255
        lib.TCOD_path_compute(self._path_c, start_x, start_y, goal_x, goal_y)
256
        path = []
257
        x = ffi.new('int[2]')
0 ignored issues
show
Coding Style Naming introduced by
The name x does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
258
        y = x + 1
0 ignored issues
show
Coding Style Naming introduced by
The name y does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
259
        while lib.TCOD_path_walk(self._path_c, x, y, False):
260
            path.append((x[0], y[0]))
261
        return path
262
263
264
class Dijkstra(_PathFinder):
265
    """
266
    Args:
267
        cost (Union[tcod.map.Map, numpy.ndarray, Any]):
268
        diagonal (float): Multiplier for diagonal movement.
269
            A value of 0 will disable diagonal movement entirely.
270
    """
271
272
    _path_new_using_map = lib.TCOD_dijkstra_new
273
    _path_new_using_function = lib.TCOD_dijkstra_new_using_function
274
    _path_delete = lib.TCOD_dijkstra_delete
275
276
    def set_goal(self, x, y):
0 ignored issues
show
Coding Style Naming introduced by
The name x does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
277
        """Set the goal point and recompute the Dijkstra path-finder.
278
        """
279
        lib.TCOD_dijkstra_compute(self._path_c, x, y)
280
281
    def get_path(self, x, y):
0 ignored issues
show
Coding Style Naming introduced by
The name x does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name y does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
282
        """Return a list of (x, y) steps to reach the goal point, if possible.
283
        """
284
        lib.TCOD_dijkstra_path_set(self._path_c, x, y)
285
        path = []
286
        pointer_x = ffi.new('int[2]')
287
        pointer_y = pointer_x + 1
288
        while lib.TCOD_dijkstra_path_walk(self._path_c, pointer_x, pointer_y):
289
            path.append((pointer_x[0], pointer_y[0]))
290
        return path
291