Model the Game

00:00 Model the Tic-Tac-Toe Game Domain. In this step, you’ll identify the parts that make up the tic-tac-toe game and implement them using an object-oriented approach.

00:12 By modeling the domain of the game with a mutable object, you’ll end up with modular and composable code that’s easier to test, maintain, debug, and reason about, amongst several other advantages.

00:25 To get started, open the code editor of your choice, such as Visual Studio Code or PyCharm, and create a new project called tic-tac-toe, which will also become the name of your project folder.

00:35 Many modern code editors will give you the option to create a virtual environment for your project automatically. So if you want to, you can do that, but if your editor doesn’t, then you can make the virtual environment manually.

00:48 On macOS and Linux, this will create a tic-tac-toe/ folder and move into it.

00:57 Then it will create a virtual environment in the venv/ folder inside tic-tac-toe/ and activate it.

01:12 If you’re on Windows using Windows Terminal, then the commands are slightly different.

01:32 Next, scaffold the basic structure of files and folders in your new project, remembering to use underscores instead of dashes for the Python package in the src/ subfolder. This skeleton of folders and files is available in the course materials if you don’t want to create everything manually. All of the files in the tree seen on-screen should be empty at this point. You’ll successively fill them with content and add more files as you go through the course.

02:01 Start out by editing pyproject.toml. You can enter the minimal packaging configuration seen on-screen into it.

02:15 You specify the required build tools, which Python will download and install if necessary, along with some metadata for the project. Adding the pyproject.toml file to the library lets you build and install it as a Python package into the active virtual environment.

02:36 Open a terminal with the virtual environment activated and enter this command to install the tic-tac-toe library in editable mode.

02:48 Even though there’s no Python code in the library yet, installing it now with the --editable flag will let the Python interpreter import the functions and classes that you’ll be adding shortly straight from your project.

03:00 Otherwise, every time you made a change in your source code and wanted to test it, you’d have to remember to build and install the library into the virtual environment again. Now you have a general structure for the project, you can start implementing some code.

03:16 By the end of this step, you’ll have all the essential pieces of a tic-tac-toe game in place, including the game logic and state validation, so you’ll be ready to combine them in an abstract game engine at the start of the game.

03:29 Each tic-tac-toe player gets assigned one of two symbols, either cross (X) or naught (O), which they use to mark locations on the game board. Since there are only two symbols belonging to a fixed set of discrete values, you can define them with an enumerated type or enum.

03:48 Using enums is preferable over constants due to their enhanced type safety, common namespace, and programmatic access to their members. Create a new Python module called models in the tic_tac_toe.logic package.

04:04 You’ll use this file to define tic-tac-toe domain model objects. Now import the enum module from Python’s standard library and define a new data type in your models.

04:21 The two singleton instances of the Mark class, enum members CROSS and NAUGHT, represent the player’s symbols. They’re defined as an extension of StrEnum, which was added to the standard library in Python 3.11.

04:35 Members of StrEnum are strings, which means that you can use them almost anywhere that a regular string is expected. Once you assign a given mark to the first player, the second player must be assigned the only remaining and unassigned mark.

04:50 Because enums are glorified classes, you are free to put ordinary methods and properties into them. For example, you can define a property of a Mark member that will return the other member.

05:10 The body of the property is a single line of code that uses a conditional expression to determine the correct mark. The quotation marks around the return type in the property signature are mandatory to make a forward declaration and avoid an error due to an unresolved name.

05:25 After all, you claim to return Mark, which hasn’t been fully defined yet. Alternatively, you can postpone the evaluation of annotations until after they’ve been defined.

05:43 Adding the special __future__ import, which must appear at the beginning of the file, enables lazy evaluation of type hints. You’ll use this pattern later to avoid the circular reference problem when importing cross-referencing modules.

06:06 You can see a few practical uses of the Mark enum in the REPL session seen on-screen,

06:18 you can refer to a Mark by its symbolic name literal, its string name, or by its value. You can get the other mark,

06:36 its name, and its value. You can compare it to a string,

06:52 use it as if it were a string, and iterate over the available marks.

07:02 You now have a way to represent the available markings that players will leave on the board to advance the game. In the next section of the course, you’ll implement an abstract game board with well-defined locations for those markings.

Become a Member to join the conversation.