Issues (838)

tcod/bsp.py (10 issues)

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
68
    def __init__(self, x, y, width, height):
69
        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...
70
        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...
71
        self.width = width
72
        self.height = height
73
74
        self.level = 0
75
        self.position = 0
76
        self.horizontal = False
77
78
        self.parent = None
79
        self.children = ()
80
81
    @property
82
    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...
83
        return self.width
84
    @w.setter
85
    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...
86
        self.width = value
87
88
    @property
89
    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...
90
        return self.height
91
    @h.setter
92
    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...
93
        self.height = value
94
95
    def _as_cdata(self):
96
        cdata = ffi.gc(lib.TCOD_bsp_new_with_size(self.x, self.y,
97
                                                  self.width, self.height),
98
                       lib.TCOD_bsp_delete)
99
        cdata.level = self.level
100
        return cdata
101
102
    def __str__(self):
103
        """Provide a useful readout when printed."""
104
        status = 'leaf'
105
        if self.children:
106
            status = ('split at position=%i,horizontal=%r' %
107
                      (self.position, self.horizontal))
108
109
        return ('<%s(x=%i,y=%i,width=%i,height=%i)level=%i,%s>' %
110
                (self.__class__.__name__,
111
                 self.x, self.y, self.width, self.height, self.level, status))
112
113
    def _unpack_bsp_tree(self, cdata):
114
        self.x = cdata.x
115
        self.y = cdata.y
116
        self.width = cdata.w
117
        self.height = cdata.h
118
        self.level = cdata.level
119
        self.position = cdata.position
120
        self.horizontal = bool(cdata.horizontal)
121
        if lib.TCOD_bsp_is_leaf(cdata):
122
            return
123
        self.children = (BSP(0, 0, 0, 0), BSP(0, 0, 0, 0))
124
        self.children[0].parent = self
125
        self.children[0]._unpack_bsp_tree(lib.TCOD_bsp_left(cdata))
126
        self.children[1].parent = self
127
        self.children[1]._unpack_bsp_tree(lib.TCOD_bsp_right(cdata))
128
129
    def split_once(self, horizontal, position):
130
        """Split this partition into 2 sub-partitions.
131
132
        Args:
133
            horizontal (bool):
134
            position (int):
135
        """
136
        cdata = self._as_cdata()
137
        lib.TCOD_bsp_split_once(cdata, horizontal, position)
138
        self._unpack_bsp_tree(cdata)
139
140
    def split_recursive(self, depth, min_width, min_height,
141
                        max_horizontal_ratio, max_vertical_ratio, seed=None):
142
        """Divide this partition recursively.
143
144
        Args:
145
            depth (int): The maximum depth to divide this object recursively.
146
            min_width (int): The minimum width of any individual partition.
147
            min_height (int): The minimum height of any individual partition.
148
            max_horizontal_ratio (float):
149
                Prevent creating a horizontal ratio more extreme than this.
150
            max_vertical_ratio (float):
151
                Prevent creating a vertical ratio more extreme than this.
152
            seed (Optional[tcod.random.Random]):
153
                The random number generator to use.
154
        """
155
        cdata = self._as_cdata()
156
        lib.TCOD_bsp_split_recursive(
157
            cdata,
158
            seed or ffi.NULL,
159
            depth,
160
            min_width, min_height,
161
            max_horizontal_ratio, max_vertical_ratio,
162
        )
163
        self._unpack_bsp_tree(cdata)
164
165
    def walk(self):
166
        """Iterate over this BSP's hieracrhy.
167
168
        The iterator will include the instance which called it.
169
        It will traverse its own children and grandchildren, in no particular
170
        order.
171
172
        Returns:
173
            Iterator[BSP]: An iterator of BSP nodes.
174
175
        .. deprecated:: 2.3
176
            See the module example for how to iterate over a BSP tree.
177
        """
178
        return self._iter_post_order()
179
180
    def _iter_pre_order(self):
181
        yield self
182
        for child in self.children:
183
            for grandchild in child._iter_pre_order():
184
                yield grandchild
185
186
    def _iter_in_order(self):
187
        if self.children:
188
            for grandchild in self.children[0]._iter_in_order():
189
                yield grandchild
190
            yield self
191
            for grandchild in self.children[1]._iter_in_order():
192
                yield grandchild
193
        else:
194
            yield self
195
196
    def _iter_post_order(self):
197
        for child in self.children:
198
            for grandchild in child._iter_post_order():
199
                yield grandchild
200
        yield self
201
202
    def _iter_level_order(self):
203
        return sorted(self._iter_pre_order(), key=lambda n:n.level)
204
205
    def _iter_inverted_level_order(self):
206
        return reversed(self._iter_level_order())
207
208
    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...
209
        """Returns True if this node contains these coordinates.
210
211
        Args:
212
            x (int): X position to check.
213
            y (int): Y position to check.
214
215
        Returns:
216
            bool: True if this node contains these coordinates.
217
                  Otherwise False.
218
        """
219
        return (self.x <= x < self.x + self.width and
220
                self.y <= y < self.y + self.height)
221
222
    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...
223
        """Return the deepest node which contains these coordinates.
224
225
        Returns:
226
            Optional[BSP]: BSP object or None.
227
        """
228
        if not self.contains(x, y):
229
            return None
230
        for child in self.children:
231
            found = child.find_node(x, y)
232
            if found:
233
                return found
234
        return self
235