Represent the Maze With Objects
00:14 In this part of the course, you’ll use a top-down approach to conceptually decompose the maze into a set of basic elements. One by one, you’ll implement those elements as objects and combine them to represent the complete maze. For the purposes of this course, you can think of the maze as a rectangular grid of uniform squares arranged in rows and columns.
00:37 Each square has the same width and height and a piece of border around it. Squares can additionally play specific roles in the maze to make it more interesting. For example, some of them can represent obstacles such as walls or enemies, and others can represent rewards.
00:55 To avoid tight coupling between your building blocks, which could manifest itself through the circular import error mentioned earlier on, you are going to put the individual classes in separate files.
01:16 Each empty file corresponds to one of the building blocks you’ve identified. Over the remaining sections, you’ll fill them in with necessary code. You’ll begin by defining the available roles of a square in the maze.
01:31 Most squares won’t have a particular role in the maze. However, at least two of them must be marked as entrance and exit respectively. They’ll tell the pathfinding algorithm where to start and finish its journey.
01:44 Some other squares can be marked as obstacles, such as walls, enemies, or the exterior, which you can’t cross. Finally, a few squares can contain rewards, such as bonus points or power-ups that may influence the path.
Note that this is just an arbitrary constraint on the problem to make it easier to solve, which doesn’t hold true for all mazes in general. The most appropriate data type for representing square roles in Python, which can enforce these rules, is an
The class extends the
enum.IntEnum base class from the standard library, which provides special semantics for its instances. There are currently only six such instances, which have unique identities called members of the enumeration.
You usually write their names in uppercase to indicate they behave like constants, but unlike regular constants, they share a common namespace. By calling
enum.auto(), you give each member the next numeric value in turn.
At this point, it doesn’t make much difference whether you extend
enum.IntEnum or the more basic
enum.Enum type. However, the benefit of using the former will become apparent in part two of this course, where you’ll start implementing a custom file format to save your mazes on disk in binary form.
However, empty values can be problematic because they force you to add a conditional branch to your code that will handle the missing value whenever you want to access an attribute. If you forget to check for a
None value, then you may get an error. Fortunately, in the object-oriented world, there’s a convenient design pattern called the null object pattern, which can help you avoid this issue. In short, the pattern instructs you to stop using
None in favor of dedicated null objects representing the missing value of the associated types. Generally, each attribute type should have its own null object that can implement the desired interface with some default behavior, such as a no-op.
Now, you can assign a
Role instance to all of the squares, even if some of them don’t play an inherent role in the maze. That way, you can treat the squares in uniform manner, which will greatly simplify the code written in the future. So far, you defined roles for the squares in the maze.
06:25 Its next building block is the border around each square, which will let you give them a visual appearance and sense of location. You’ll be looking at creating these border patterns in the next section of the course.
Become a Member to join the conversation.