Completed
Push — master ( ec79aa...19501a )
by Kyle
57s
created

BSP.split_recursive()   B

Complexity

Conditions 1

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
c 2
b 0
f 1
dl 0
loc 26
rs 8.8571
1
'''
2
The following example shows how to travrse the BSP tree using Python.  This
3
assumes `create_room` and `connect_rooms` will be replaced by custom code.
4
5
Example::
6
7
    import tcod.bsp
8
9
    def create_room(node):
10
        """Initialize the room at this current node."""
11
        print('Create a room for %s.' % node)
12
13
    def connect_rooms(node):
14
        """Connect two fully initialized rooms."""
15
        node1, node2 = node.children
16
        print('Connect the rooms:\\n%s\\n%s' % (node1, node2))
17
18
    def traverse(node):
19
        """Traverse a BSP tree dispatching nodes to the correct calls."""
20
        # For nodes without children, node.children is an empty tuple.
21
        for child in node.children:
22
            traverse(child)
23
24
        if node.children:
25
            connect_rooms(node)
26
        else:
27
            create_room(node)
28
29
    bsp = tcod.bsp.BSP(x=0, y=0, width=80, height=60)
30
    bsp.split_recursive(
31
        depth=5,
32
        min_width=3,
33
        min_height=3,
34
        max_horizontal_ratio=1.5,
35
        max_vertical_ratio=1.5,
36
        )
37
    traverse(bsp)
38
'''
39
from __future__ import absolute_import as _
40
41
from tcod.libtcod import lib, ffi
42
43
44
class BSP(object):
45
    """A binary space partitioning tree which can be used for simple dungeon
46
    generation.
47
48
    Attributes:
49
        x (int): Rectangle left coordinate.
50
        y (int): Rectangle top coordinate.
51
        width (int): Rectangle width.
52
        height (int): Rectangle height.
53
        level (int): This nodes depth.
54
        position (int): The integer of where the node was split.
55
        horizontal (bool): This nodes split orientation.
56
        parent (Optional[BSP]): This nodes parent or None
57
        children (Optional[Tuple[BSP, BSP]]):
58
            A tuple of (left, right) BSP instances, or
59
            None if this BSP has no children.
60
61
    Args:
62
        x (int): Rectangle left coordinate.
63
        y (int): Rectangle top coordinate.
64
        width (int): Rectangle width.
65
        height (int): Rectangle height.
66
67
    .. versionchanged:: 2.0
68
       You can create BSP's with this class contructor instead of using
69
       :any:`bsp_new_with_size`.
70
    """
71
72
    def __init__(self, x, y, width, height):
73
        self.x = x
0 ignored issues
show
Coding Style Naming introduced by
The name x does not conform to the attribute 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...
74
        self.y = y
0 ignored issues
show
Coding Style Naming introduced by
The name y does not conform to the attribute 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...
75
        self.width = width
76
        self.height = height
77
78
        self.level = 0
79
        self.position = 0
80
        self.horizontal = False
81
82
        self.parent = None
83
        self.children = ()
84
85
    @property
86
    def w(self):
0 ignored issues
show
Coding Style Naming introduced by
The name w does not conform to the attribute 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 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...
87
        return self.width
88
    @w.setter
89
    def w(self, value):
0 ignored issues
show
Coding Style Naming introduced by
The name w does not conform to the attribute 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 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...
90
        self.width = value
91
92
    @property
93
    def h(self):
0 ignored issues
show
Coding Style Naming introduced by
The name h does not conform to the attribute 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 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...
94
        return self.height
95
    @h.setter
96
    def h(self, value):
0 ignored issues
show
Coding Style Naming introduced by
The name h does not conform to the attribute 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 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...
97
        self.height = value
98
99
    def _as_cdata(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...
100
        cdata = ffi.gc(lib.TCOD_bsp_new_with_size(self.x, self.y,
101
                                                  self.width, self.height),
102
                       lib.TCOD_bsp_delete)
103
        cdata.level = self.level
104
        return cdata
105
106
    def __str__(self):
107
        """Provide a useful readout when printed."""
108
        status = 'leaf'
109
        if self.children:
110
            status = ('split at position=%i,horizontal=%r' %
111
                      (self.position, self.horizontal))
112
113
        return ('<%s(x=%i,y=%i,width=%i,height=%i)level=%i,%s>' %
114
                (self.__class__.__name__,
115
                 self.x, self.y, self.width, self.height, self.level, status))
116
117
    def _unpack_bsp_tree(self, cdata):
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...
118
        self.x = cdata.x
119
        self.y = cdata.y
120
        self.width = cdata.w
121
        self.height = cdata.h
122
        self.level = cdata.level
123
        self.position = cdata.position
124
        self.horizontal = bool(cdata.horizontal)
125
        if lib.TCOD_bsp_is_leaf(cdata):
126
            return
127
        self.children = (BSP(0, 0, 0, 0), BSP(0, 0, 0, 0))
128
        self.children[0].parent = self
129
        self.children[0]._unpack_bsp_tree(lib.TCOD_bsp_left(cdata))
130
        self.children[1].parent = self
131
        self.children[1]._unpack_bsp_tree(lib.TCOD_bsp_right(cdata))
132
133
    def split_once(self, horizontal, position):
134
        """Split this partition into 2 sub-partitions.
135
136
        Args:
137
            horizontal (bool):
138
            position (int):
139
140
        .. versionadded:: 2.0
141
        """
142
        cdata = self._as_cdata()
143
        lib.TCOD_bsp_split_once(cdata, horizontal, position)
144
        self._unpack_bsp_tree(cdata)
145
146
    def split_recursive(self, depth, min_width, min_height,
147
                        max_horizontal_ratio, max_vertical_ratio, seed=None):
148
        """Divide this partition recursively.
149
150
        Args:
151
            depth (int): The maximum depth to divide this object recursively.
152
            min_width (int): The minimum width of any individual partition.
153
            min_height (int): The minimum height of any individual partition.
154
            max_horizontal_ratio (float):
155
                Prevent creating a horizontal ratio more extreme than this.
156
            max_vertical_ratio (float):
157
                Prevent creating a vertical ratio more extreme than this.
158
            seed (Optional[tcod.random.Random]):
159
                The random number generator to use.
160
161
        .. versionadded:: 2.0
162
        """
163
        cdata = self._as_cdata()
164
        lib.TCOD_bsp_split_recursive(
165
            cdata,
166
            seed or ffi.NULL,
167
            depth,
168
            min_width, min_height,
169
            max_horizontal_ratio, max_vertical_ratio,
170
        )
171
        self._unpack_bsp_tree(cdata)
172
173
    def walk(self):
174
        """Iterate over this BSP's hieracrhy.
175
176
        The iterator will include the instance which called it.
177
        It will traverse its own children and grandchildren, in no particular
178
        order.
179
180
        Returns:
181
            Iterator[BSP]: An iterator of BSP nodes.
182
183
        .. versionadded:: 2.0
184
185
        .. deprecated:: 2.3
186
            See the module example for how to iterate over a BSP tree.
187
        """
188
        return self._iter_post_order()
189
190
    def _iter_pre_order(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...
191
        yield self
192
        for child in self.children:
193
            for grandchild in child._iter_pre_order():
194
                yield grandchild
195
196
    def _iter_in_order(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...
197
        if self.children:
198
            for grandchild in self.children[0]._iter_in_order():
199
                yield grandchild
200
            yield self
201
            for grandchild in self.children[1]._iter_in_order():
202
                yield grandchild
203
        else:
204
            yield self
205
206
    def _iter_post_order(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...
207
        for child in self.children:
208
            for grandchild in child._iter_post_order():
209
                yield grandchild
210
        yield self
211
212
    def _iter_level_order(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...
213
        return sorted(self._iter_pre_order(), key=lambda n:n.level)
0 ignored issues
show
Coding Style introduced by
Exactly one space required after :
return sorted(self._iter_pre_order(), key=lambda n:n.level)
^
Loading history...
214
215
    def _iter_inverted_level_order(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...
216
        return reversed(self._iter_level_order())
217
218
    def contains(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...
219
        """Returns True if this node contains these coordinates.
220
221
        Args:
222
            x (int): X position to check.
223
            y (int): Y position to check.
224
225
        Returns:
226
            bool: True if this node contains these coordinates.
227
                  Otherwise False.
228
229
        .. versionadded:: 2.0
230
        """
231
        return (self.x <= x < self.x + self.width and
232
                self.y <= y < self.y + self.height)
233
234
    def find_node(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...
235
        """Return the deepest node which contains these coordinates.
236
237
        Returns:
238
            Optional[BSP]: BSP object or None.
239
240
        .. versionadded:: 2.0
241
        """
242
        if not self.contains(x, y):
243
            return None
244
        for child in self.children:
245
            found = child.find_node(x, y)
246
            if found:
247
                return found
248
        return self
249