Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Create Border Patterns

00:00 Creating Border Patterns. Each square can have between zero and four sides painted along the main compass directions: north, south, east, and west. When you do the math, you’ll find out that there are sixteen combinations of unique side patterns in total, ranging from an empty square to one with all four sides painted.

00:24 There’s one empty square, four squares with only one side, six squares with two sides, four squares with three sides, and one square with all four sides.

00:35 Now you can build almost any maze pattern with these tiles by connecting them like a jigsaw puzzle. To compactly represent a border pattern around a square with just a single number, you should consider using a bit field.

00:49 This will map each of the four sides to a specific binary digit. Extracting information from such a number usually requires the use of bitwise operators, which aren’t very common in Python. However, in this course, you’ll use a handy shortcut from the standard library, which hides the details. Because there are four sides, you must allocate four bits that you can individually turn on or off depending on whether or not there’s a border on that side.

01:17 When you convert the corresponding bit string into a decimal number, you’ll get the unambiguous representation of one of the border patterns, as seen on-screen. As you can see, each border pattern is represented with a decimal number ranging from zero to fifteen.

01:33 You can check which sides are included in the border by looking at the binary representation of the number and reading the corresponding bits. If a bit is set to one, then you know that you should paint that side of the border.

01:45 The bit count, or the number of ones in the bit string, reflects the number of sides in the border. Additionally, it carries some useful information about the type of square. For example, you can recognize a dead end by detecting precisely three sides of the border, leaving only one end to connect with other squares.

02:03 Fewer than two sides may indicate a potential intersection of paths in the maze if the square isn’t a wall or part of the exterior. A T-shaped intersection will have one side, while a four-way intersection will have none.

02:18 Detecting a corner is a bit more tricky because it requires you to compare the border against one of the four specific patterns, even though they all have exactly two sides.

02:28 This helps eliminate the other two border patterns that also have two sides but aren’t corners. Their sides run in parallel instead of meeting at a corner.

02:40 So now that you know what makes a square border, how do you define it in Python? You’ll use the Enum data type again, but with a slight twist.

02:48 This time around, you’ll extend the enum.IntFlag base class, which is an even more specific enumeration type that implements the bit field logic. You’ll give more common names to your enum members instead of using the compass directions.

03:05 Add the code seen on-screen to the border module.

03:16 You override the value of Border.EMPTY with 0 to indicate the absence of border sides. This class resembles the Role enumeration that you defined earlier, but IntFlag allows you to do much more with its members.

03:29 Rather than being a mutually exclusive choice of one member, the Border enumeration lets you combine any number of its members to create a composite value. For example, to define a closed border, you’d combine all four sides using the bitwise OR operator (|) like this.

04:04 The .name and .value attributes of the resulting border are calculated dynamically based on the combination of its sides. Note that the order of the individual sides doesn’t matter when you define a composite bit field. Underneath, it’s a numeric value resulting from turning on the specific bits.

04:24 Python will always show a consistent text representation of that value, determined by the order of your members in the class definition. To compare your border to another border pattern, you can use the identity test operator (is).

04:45 Alternatively, you could use the equality test operator (==) to directly compare your border against a known numeric value.

04:58 Additionally, you can check if the border contains a given side by using the membership test operator (in). Comparing an instance of enum.IntFlag to a number is possible because the flag is a subclass of the built-in integer type, just like enum.IntEnum, which you used before.

05:22 Next, add a few convenience properties to the enumeration so that you can detect corners, dead ends, and intersections.

05:33 All three properties return a Boolean value. In the .corner property, you use the membership test operator (in) to check if an instance of the Border enumeration—indicated by self—is one of the predefined corners.

05:54 The other two properties rely on the integer’s .bit_count() method, which returns the number of ones in a binary representation of your border.

06:22 With the Role and Border building blocks in place, you can now tie them together on a higher abstraction level. And in the next section, you’ll implement the Square class.

Become a Member to join the conversation.