0% found this document useful (0 votes)
52 views654 pages

PVG Course Notes

This document provides a table of contents for a course on programming and game development. It outlines 10 modules that cover designing, programming, and refining multiple versions of two games - a hacking game and a Poke the Dots game. Each module includes sections on observing and playing the game versions, describing designs, creating test plans, programming the games, and reflecting on improvements. The modules also cover relevant Python programming concepts to support developing the game code.

Uploaded by

hamzatamer.88.10
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views654 pages

PVG Course Notes

This document provides a table of contents for a course on programming and game development. It outlines 10 modules that cover designing, programming, and refining multiple versions of two games - a hacking game and a Poke the Dots game. Each module includes sections on observing and playing the game versions, describing designs, creating test plans, programming the games, and reflecting on improvements. The modules also cover relevant Python programming concepts to support developing the game code.

Uploaded by

hamzatamer.88.10
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 654

TABLE OF CONTENTS

Note: Sections in blue font include Pygame. If you are not doing Pygame, you can omit the
blue-colored sections. If you are doing Pygame, please do both the blue-colored and
black-colored sections.

Module 00: Introduction 5


00.01 Course Themes 5
00.02 Computer Science 11
00.03 Programming Languages 14
00.04 Learning Outcomes and Problem-Based Learning 16
00.05 How to Get the Most Out of the Course 19
00.06 Suggestions for Learner Success 21

Module 01: Design Hacking Version 1 23


01.01 Game Creation Process 23
01.02 Observe and Play Hacking Game 27
01.03 Game Versions 34
01.04 Observe and Play Hacking Version 1 41
01.05 Describe Hacking Version 1 43
01.06 Create Functional Test Plan Hacking Version 1 51
01.07 Create Algorithm for Hacking Version 1 61

Module 02: Program Hacking Version 1 70


02.01 Python - Evaluation Examples 70
02.02 Python - Interpretation 77
02.03 Python - Lexical Analysis 83
02.04 Python - Syntax Analysis 86
02.05 Python - Objects 92
02.06 Python - Semantics of Literals and Identifiers 98
02.07 Python - Semantics of Function Calls 103
02.08 Python - Program Interpretation 108
02.09 Program Hacking Version 1 114
02.10 The Reflection Process 121
02.10 Review Code for Hacking Version 1 123
02.10 Solution Issues 128

Module 03: Hacking Version 2 130


03.01 Solution Issues in Hacking Version 1 130
03.02 Observe and Play Hacking Version 2 137
03.03 Describe Hacking Version 2 141
03.04 Create Functional Test Plan for Hacking Version 2 148

1
03.05 Create Algorithm for Hacking Version 2 151
03.06 Python - Assignment Statement 153
03.07 Python - Binary Expression and Operator Token 167
03.08 Python - Import Statement and Keyword Token 180
03.09 Python - Multi-argument Function Calls 195
03.10 Python - Method Call and Attribute Reference 203
03.11 Program Hacking Version 2 215
03.12 Review Code for Hacking Version 3 236

Module 04: Hacking Version 3 244


04.01 Introduction: Hacking V3 Solution Issues 244
04.02 Demo: Observe Hacking V3 247
04.03 Create Algorithm for Hacking Version 3 250
04.04 Python If Statements and Booleans 254
04.05 Python - Elif and Else Clauses 262
04.06 Python - Keyword Operators, Short Circuit Evaluation, Unary Expressions, and
Operator Precedence 269
04.07 Program Hacking Version 3 275
04.08 Review Code for Hacking Version 3 278

MODULE 5: HACKING VERSION 4 & 5 284


05.01 Solution Issues in Hacking Version 3 284
05.02 Create Algorithm for Hacking Version 4 291
05.03 Python Sequences and Subscription 299
05.04 Python - Tuple and List Types 314
05.05 Python - Sequence Element Replacement 326
05.06 Python - For Statement 332
05.07 Program Hacking Version 4 340
05.08 Reflect on Hacking Version 4 345
05.09 Solution Issues in Hacking Version 4 352
05.10 Observe and Play Hacking Version 5 356
05.11 Describe Hacking Version 5 360
05.12 Create Test Plan for Hacking Version 5 361
05.13 Create Algorithm for Hacking Version 5 364
05.14 Python While Statement 367
05.15 Python - Repetition Examples and Range Type 373
05.16 Program Hacking Version 5 386
05.17 Reflect on Hacking Version 5 388

MODULE 6: HACKING VERSION 6 394


6.01 Solution Issues in Hacking Version 5 394

2
6.02 Python - Function Definitions 398
6.03 Python - Function Parameters 407
6.04 Python - Main Function and Identifier Scope 412
6.05 Python - Return Statement 419
6.06 Python - Side Effects 423
6.07 Program Hacking Version 6 427
6.08 Review Code for Hacking Version 6 438

MODULE 7: HACKING VERSION 7 449


07.01 Solution Issues in Hacking Version 6 449
07.02 Observe Hacking Version 7 451
07.03 Create Algorithm for Hacking Version 7 456
07.04 Program Hacking Version 7 460
07.05 Identify Solution Issues in Hacking Version 7 467

MODULE 8: POKE THE DOTS VERSION 1 & 2 468


08.01 Introduction to Poke the Dots 468
08.02 Observe Poke the Dots Version 1 475
08.03 Create Algorithm for Poke the Dots Version 1 477
08.04 Python - Import Statement Variations 492
08.05 Python - Pass Statement 508
08.06 Program Poke the Dots Version 1 511
08.07 Reflect on Poke the Dots Version 1 528
08.08 Solution Issues in Poke the Dots Version 1 535
08.09 Create Algorithm for Poke the Dots Version 2 537
08.10 Python - Class Definition 540
08.11 Program Poke the Dots Version 2 554
08.12 Reflect on Poke the Dots Version 2 560

MODULE 09: POKE THE DOTS VERSION 3 570


09.01 Solution Issues in Poke the Dots Version 2 570
09.02 Observe, Play, Describe, and Create Test Plan for Poke the Dots Version 3 571
09.03 Create Algorithm for Poke the Dots Version 3 576
09.04 Program Poke the Dots Version 3 579
09.05 Reflect on Poke the Dots Version 3 580

MODULE 10: POKE THE DOTS VERSION 4 592


10.01 Solution Issues in Poke the Dots Version 3 592
10.02 Create Algorithm for Poke the Dots Version 4 595
10.03 Python - User-Defined Method 605
10.04 Python - Private Attributes 620

3
10.05 Program Poke the Dots Version 4 623
10.06 Reflect on Poke the Dots Version 4 634

MODULE 11: POKE THE DOTS VERSION 5 644


11.01 Solution Issues for Pokes the Dots Version 4 644
11.02 Observe, Play, Describe and Create the Test Plan for Poke the Dots Version 5 645
11.03 Create Algorithm for Poke the Dots Version 5 647
11.04 Program Poke the Dots Version 5 649
11.05 Reflect on Poke the Dots Version 5 and Goodbye from the PVG Team 651

4
Module 00: Introduction

00.01 Course Themes

Welcome to Problem Solving, Programming and Video Games!

The instructional team for PVG is:

Duane
Duane is a Professor of Computing Science at the University of Alberta. He has been teaching
introductory programming language courses for most of his life. For fun Duane plays video
games, watches baseball and enjoys woodworking.

Paul
Paul is also a Professor of Computing Science and after all these years he still gets a thrill when
a student understands a new programming concept. For fun, Paul likes movies and taking his
kids to airshows.

Elyse
Elyse is a Computing Science graduate from the University of Alberta. She has previously
worked on an application that allows for beginner programmers to more easily interface with
Arduino robots and create games! Elyse is excited to introduce you to our course and hopes
you enjoy programming!

Emma
Emma has a degree in Math and Computing Science. For the past few years, she has been part
of the instructional team for the introductory computing science course at the University of
Alberta. Emma enjoys mentoring people who are new to programming and hopes this course
will make Computing Science accessible to you.

You will learn four things in this course:


1. How Computer Science applies computation to information.
2. How to use computational thinking or problem solving to solve computational problems.
3. How to program in Python.
4. How to create Video Games and more.

Let’s briefly discuss two Video Games you will create during this course.

We call the first Game "Hacking". It is based on a mini-game that appears in Bethesda
Softworks’ popular Fallout series. The goal is to select one of the words on the screen as the
correct password. You have up to 4 guesses. After each guess there is some feedback.

5
00.01@02:14

Fig. 00.01.01: Play Hacking Version 7 Example

00.01@02:14

Fig. 00.01.02 Play Hacking Version 7 Example (Video Screenshot)

We call the second game "Poke the Dots". The goal is to prevent the dots from colliding. If you
click on the window, the dots teleport to new random locations. Your score is the time before the
dots collide.

6
00.01@02:23

Fig. 00.01.03: Play Poke the Dots Version 5

00.01@02:23

Fig. 00.01.04: Play Poke the Dots Version 5 (Video Screenshot)


In this course we use the term Video Game to mean an interactive graphical application that can
be played for fun.

7
Learning to create games is cool. But it is not the "real" goal of this course. The process you will
use to create a game will also work for most other kinds of software applications.

We picked video games for three reasons. First they are interesting and fun. Second, even
though the games are interesting, they are simple enough that they can be completed during
this course. Third, video games are graphical and interactive.

This means that after the course you will be able to create interactive graphical software
applications. For example, you could create an application that manages musical play-lists or
one that demonstrates chemical reactions.

To create a video game, you must learn to write a program in some programming language.

You will learn the Python programming language so you can use it to write the code for your
games. In this course, we will use Python version 3. Here is some of the Python code for "Poke
the Dots".

00.01@03:48

Fig. 00.01.05: Code Fragment from Poke the Dots

8
00.01@03:48

Fig. 00.01.05: Code Fragment from Poke the Dots (Video Screenshot)

Don't worry if you don't understand any of it yet. You will by the end of the course.

Learning to write Python programs is both fun and useful. However, there are many
programming languages. If you focus too narrowly on the Python language, you may have
difficulty learning other programming languages later. In this course, you will learn the general
Computing Science concepts: lexics, syntax, semantics, namespaces and control structures.

Although, you will learn how to apply these general concepts to learn Python, you will also be
able to use these concepts to learn other programming languages, later.

You can't write meaningful code in a programming language until you understand what the code
must do. Code is one "end product" or artifact of a problem solving process that begins with an
idea.

Transforming an idea into a working solution is a process that is based on problem solving. For
example, if your idea is a new device, the artifact may include, a physical object, design
documents, the software it contains and user manuals. Different problem solving techniques are
used to create each of these artifacts.

When the artifact is a software application, computational problem solving is required. In this
course you will learn to use computational problem solving, which is also called computational
thinking to turn game ideas into game software. However, the same problem-solving process
can be used to create any kind of software, whether it's a game or not.

9
Computer Science is a systematic study of different kinds of information and different ways that
computation can be applied to this information.

Perhaps the term Computing Science is more accurate than the term Computer Science. For
example, we call our University Department, the Department of Computing Science to more
closely reflect the nature of the discipline. It is a scientific discipline that studies computation and
information, in addition to computers and computer programming. The next lesson provides an
introduction to Computer Science.

We use games to make the course more fun and we use Python since it is a good language that
is currently popular.

However the computational problem-solving process you will learn in this course can be used
whether the software is a game or not, and whether the programming language is Python or
some other language. After completing this course, you will be able to use the problem solving
process and the Computer Science concepts you have learned to transform most software
ideas into actual software using most programming languages.

There are other courses that just focus on Python programming or just focus on writing code for
games. This course focuses on general Computer Science concepts and on general
problem-solving techniques. Yes, it uses video games and Python, but it does so much more!

10
00.02 Computer Science

This lesson introduces computer science, computation and information.

Here is a description of Computer Science:

Computer Science is the study of computation and information. It investigates the computational
algorithms that can be applied to acquire, represent, generate, transform, and communicate
knowledge that solves computational problems.

There are many types of information. A number is one type of information. There are many
specific numbers such as 4 and 10.5. Computation using numbers is often called calculation. In
other words, calculation is a kind of computation that acquires, represents, generates,
transforms and communicates numbers. You can use your brain to perform calculations that
process numbers by adding or multiplying them. Alternately, you can use a calculator, especially
for complex calculations. But numbers are only one type of information and calculation is only
one kind of computation. You can also apply computation to *other* types of information.

A string is another type of information, which consists of a finite sequence of characters. For
example, “four” is a string, rather than being a number. You can use your brain to perform a
computation on a string, such as generating a new string that has the same letters but with each
letter upper-case. Alternately, you can use a computer to do a computation on a string, such as
computing the number of characters it contains.

There are many other computations that both you and a computer could perform on strings.
For example, both you and a computer could count the total number of times the word “Romeo”
appears in the spoken text of the play Romeo and Juliet.

However it would take you much longer than a computer. Computers can perform simple
computations much faster than humans and provide the answer, 128, in much less than one
second.

An algorithm is the sequence of steps used to perform a computation. For example, here is an
algorithm for counting occurrences of "ROMEO" in Romeo and Juliet.

1) Start counting at zero.

2) Scan the text string for the character, upper case R.

3) If the R is followed by the lower case characters: O, M, E and O, add one to the counter.

11
A computation is a task you want to perform, such as computing the number of occurrences of
the word Romeo. An algorithm is the way you perform the task, such as the algorithm used in
this example.

Text is another type of information. Text is like a string, but in addition to its character content, it
has other attributes such as font, color, and size. For example, “ four “ is text, rather than being
a string, since it has a specific font, colour and size that the previous "four" string did not have.
A computer can process text by transforming attributes such as its font, color, and size.

Let’s include two other kinds of information so we can illustrate the broad scope of information
and computation.

An image is another kind of information.

Instead of being a string or text, is an image of "the four of clubs" playing card. A
computer can transform an image by changing attributes such as size or resolution.

Finally, video is the last kind of information I'll introduce for now. The videos in this course are
examples of video information. Two examples of video algorithms are: inserting transitions such
as wipes, and adding animations. Both of these computational processes and many more have
been used to create the videos in this course.

You will encounter many different types of information during this course and many different
algorithms, for each type. For example, in your first game, Hacking you will create algorithms to
generate password strings. In the second game, Poke the Dots, you will create your own Dot
type and write an algorithm that ends the game when two dots collide. The algorithm will check
whether the distance between the dot centers is less than or equal to the sum of their radii. You
will also implement your algorithms in Version 3 of the Python programming language and run
these programs on a computer.

How can we use computation?

Computation can be used for many purposes, but we will focus on one purpose, computational
problem solving to create video games.

Some other common uses of computation are: communication, information storage & retrieval,
device monitoring & control, information encryption & decryption and simulation.

You can learn about these other uses of computation in future courses. However, this course will
provide a solid foundation for understanding these topics. Specifically, types and algorithms are
essential components of all of these other uses of computation.

12
In this lesson, you have seen that computer science is the study of computation and
information. During this course you will see that Computation, acquires, represents, generates,
transforms, and communicates many different types of information.

13
00.03 Programming Languages

This lesson introduces the concept of a programming language.

Computation is used for computational problem solving. Creating a video game or other
application is one kind of computational problem solving.

We want a computer to perform appropriate computations for an application. How can we get a
computer to perform the appropriate computations?

One of the final steps in the application programming process is to write a computer program
that a computer uses to perform the computations for the application. One or more
programmers use a programming language to express this computer program. However,
modern programming languages do not contain machine instructions that can be directly
performed by a computer. Instead, the computer program is a high-level representation of the
steps the computer must perform. A translator must translate the computer program into
machine language instructions, which the computer can then use to perform the computation.

For this course, you don't need to know anything else about machine language.
There are two kinds of translators: compilers and interpreters. This course uses the Python
programming language which is interpreted.

An interpreter translates one statement of a program into machine language by interpreting that
line. These few machine instructions are then executed by the computer to perform part of the
computation. The interpreter and computer then continue this interpret-execute cycle until every
program statement has been translated and all of the generated machine instructions have
been executed.

We can use an analogy to better understand interpretation. The Oxford Dictionary of English
has this definition for interpreter

“A person who interprets, especially one who translates speech orally or into
sign language.”

Consider the problem of translating between natural languages such as English and sign
language. Interpretation occurs in real time, since an interpreter translates speech, one phrase
or sentence at a time, as a speaker is speaking. This kind of translation is sometimes called
simultaneous interpretation and is analogous to interpretation of computer languages.

The only part of the translation process you need to understand for this course, is that it
happens, not how it is done.

14
You have seen that a computer program represents a computation that solves a computational
problem. Then, an interpreter translates this representation into machine language instructions
that a computer executes to solve the problem.

15
00.04 Learning Outcomes and Problem-Based Learning

This lesson lists the learning outcomes and introduces the problem-based learning approach
that is used in this course.

A learning outcome describes the knowledge, skills and values that a learner attains during an
experience and retains after the experience.

The summary version of the learning outcomes for this course is:

After completing this course you will be able to use appropriate tools and resources to solve
computational problems.

This single learning outcome encompasses these specific knowledge and skill learning
outcomes.

You will be able to:


● Describe computational problems.
● Create and apply functional test plans to assess problem solutions.
● Create algorithms, which represent problem solutions that can be implemented in a
variety of programming languages.
● Write, test and debug interactive graphical Python programs that implement a range of
algorithms using a broad group of data types and control structures.
● Assess the quality of Python code.
● Reflect on problem solutions to Identify issues and resolve or mitigate these issues.
● Use appropriate abstraction at all levels of the design, testing and coding process to
create reusable, reliable, robust and efficient programs.
● Assess the knowledge, skills and values you gain, as you proceed through the course.

This single learning outcome also encompases these specific value learning outcomes:
● Value the benefits of design before coding, including: a clear description, a complete
functional test plan and an effective algorithm.
● Value the benefits of team design.
● Value software quality.
● Value solution reflection to introduce improvements.

This course uses problem-based learning. You start with a problem and as you work on it, you
learn all the knowledge and skills you need to solve it. This approach contrasts with traditional
learning, where you learn each piece of knowledge and skill first, using focused examples, and
then apply all your new knowledge and skills to solve a general problem.

16
It is important for you to understand problem-based learning so that you can make good use of
the course resources.

For example, here is a description of problem-based learning, applied to learning to use some
construction tools. First, I tell you about a problem. For example, you are going to build a
birdhouse.

Second, I help you identify, discover and learn about each item of knowledge or skill you will
need to solve the problem, as you need them. For example, we look at pictures of birdhouses or
real bird houses to identify their components. Then I help you create an assembly plan for these
components and we determine which tools and techniques are needed for each step of the
plan. To make a birdhouse, you need to learn how to use these tools: measuring tape, saw,
glue, nails, drill and paint.

Third, you learn each item of knowledge or skill only when it is required. You learn it by applying
it to the problem you are trying to solve.For example, The first step in the birdhouse plans is to
create the bottom. Start with a board and cut off eight inches to make the floor. To do this, you
need to use a measuring tape and a saw. If you need help, I show you how to measure and
mark a board and how to saw it. Then, you then measure and saw the board to create the floor
of the birdhouse.

After the birdhouse is done, you will know how to use each of the six tools. In addition to
knowing how to use these tools, you will also know how to create a plan and how to follow a
plan to create a birdhouse.

The fourth step in problem-based learning is to solve one or more additional problems, to learn
how to apply the learned knowledge and skills in different contexts, and to learn new knowledge
and skills as needed. For example, after building a birdhouse, I might tell you to build a fence. In
addition to applying the six tools you already know in a different context, you will also learn to
use a level and an auger.

There are advantages and disadvantages to problem-based learning.

Three advantages are:


● The learner may be more motivated to learn the techniques and tools.
● The learner learns problem solving in addition to the techniques and tools.
● There may be higher transfer of learning to other problem domains.

Two disadvantages are:


● Some learners may be uncomfortable trying to solve a problem, before they know the
necessary knowledge.
● It may take extra time to learn problem solving in addition to the techniques and tools.

17
If your goal is to learn to solve computational problems in addition to learning to use
programming language statements correctly, then the extra time spent on problem solving will
be well spent.

This lesson listed the learning outcomes for this course and introduced problem-based learning.

18
00.05 How to Get the Most Out of the Course

In this lesson, I describe how PVG is organized and how to get the most out of the course.

This course consists of 12 modules. The modules contain instructional videos, design and
coding activities, assessment activities, readings, and enrichment material.

There are two categories of instructional videos. One category focuses on problem-solving by
designing and programming video games. The other category, referred to as "Programming
Language Videos" or PL videos for short, provides general instruction on the Python
programming language, that is independent of computer games.

This course uses problem-based learning. Therefore, the Programming Language Videos
present Python concepts in the order that they are needed to create the video games. The PL
videos present Python using a conceptual framework that can be used to understand any
programming language. This framework is based on several general programming language
concepts that you will learn during the course: lexics, syntax, semantics, namespaces and
control structures. You do not need to recognize or understand these concepts now. They will
be explained in the programming language videos.

These general programming language concepts are complex, so you will probably need to
watch each programming language video more than once, and pause often as you watch. You
may also want to return to a previous video when you need to use a language construct for the
second or third time, later in the course.

Other approaches to learning a programming language may be quicker, but they rely on
mimicking simple examples without full comprehension. Many approaches are more narrowly
focused on a single programming language or on a few of the simplest aspects of programming
languages.

If you embrace the approach used in this course, you will have a deeper understanding of
programming languages. You will be prepared to learn additional Python language features and
other programming languages after you complete the course.

In the real world, creating a game is a team effort. Good design is a social activity not a solitary
one. At the start of game creation, some team members demonstrate a game to the rest of the
team using words, sounds, concept art and storyboards.

In this course, instead of seeing ideas for a game, you will watch a video of the complete
gameplay and play the game yourself. In other words, we have already created a version of the
game and your goal will be to create the same game yourself.

19
Creating a video game is a complex problem, so a game is often created by first creating a
simplified version of the game and then creating a sequence of increasingly more complex
versions, where the last version is the complete game. Each version adds some new features.

In the real world, after deciding on the game features, some experienced designers on the team
break the game into versions, either using features or by some other criteria. We will play the
role of the experienced designers on your team by determining which features will be in each
game version you create.

In the real world, the team divides each version into components and team members work
together to create descriptions, test plans, algorithms and code for each game component. In
this course, you will work alone, except for interactions you have in the course forums. However,
to simulate the real world game creation experience of working in a team, you will work with
three interactive learning objects or ILOs: a Description Builder, a Functional Test Plan Builder
and an Algorithm Builder.

These three ILOs mimic real world design by providing an explicit design partner and mentor for
each of the three game design activities. During each task in the game creation process you
create or refine an artifact that is an integral part of the game.

To get the most out of this course, you should follow the game creation process closely and
complete each activity, in order. Each activity uses an artifact from a previous activity. Therefore,
you should make an honest effort to create or assemble each target artifact, before moving on
to the next activity.

This course is like a story. Make sure you complete each chapter before moving on to the next.

To ensure that you don't get stuck, a correct copy of the previous activity's artifact is used as a
starting point for each activity in the game creation process.

During the design activities, you should take guidance from your ILO partner and mentor, but
avoid "blind guessing" to force your partner to reveal the correct "answers". In general, guessing
is not a good path to comprehension.

In this lesson, I described how the course is organized and how to get the most out of the
course.

20
00.06 Suggestions for Learner Success

Programming is like any skill: no one is born with it, and you can acquire it if you make the
appropriate effort. It's like learning to play a musical instrument. You can't expect perfection the
very first time you pick up a violin, so you shouldn't expect to understand programming the very
first time you put your hands on the keyboard.

Don't measure your success by comparing yourself to others. Some of your peers may have
more programming experience than you. I know for a fact that it's difficult not to make
comparisons, and that it can be very discouraging to see your peers fly through programming
exercises if you struggle. It is *very* important not to give up. Measure your success in this
course by the amount of growth you cultivate in yourself.

You may be in a course discussion and it may seem that others know what is going on and you
don't. Don't hesitate to participate. Some learners may be further along in the course material.
Others may have previous programming experience from outside this course. Engage anyway!
It is more fun and you will learn more.

As I said, programming is a skill. However, you can't learn to program by reading about it, just
watching the course videos, or watching someone else program. This would be like trying to
learn the violin just by listening! You need to practice, practice, practice. This takes time. Duane
was an amateur baseball coach for over 20 years and learning to program is like learning to hit
a baseball. He spent many hours talking and demonstrating batting stances, weight transfer,
and many other considerations. However, he spent more hours throwing thousands of batting
practice pitches. My advice is to write as much code as you can during this course. You often
won't understand a concept the first time you use it in a program. You usually have to use it
many times before you truly understand it. You can't really learn to program in a few days. This
course will take more time.

Furthermore, if you are new to programming, don't compare the hundreds of hours someone
has spent learning to code against the few that you have. You may be only seeing the results of
those hundred hours, and not the effort that went into them. If you want to be better, you need to
practice, practice, practice!

After writing each game version, think of small changes you can make to your programs and
implement them. Test your changes and debug the program until they work correctly. The
changes do not need to be large or important. You can add some text, change a color, or
change the conditions that display a result. Every change you make gives you more practice.

When learning to program, it is an important skill to be able to trace the execution of that
program. The debugger that we will use throughout the course is a great way to learn and
practice tracing. It is worth your time to use the debugger to trace your program, one line at a

21
time. You should predict which line of code will be executed next, and how that line will change
the program data objects. Small details like the names of data objects and the order of program
statements are very important. Tracing is the best way to learn to focus on the most important
details.

Imagine if you were trying to learn how to hit a baseball. If your coach had a high-speed video
camera, she could break down each single step of your swing to figure out what you are doing
wrong. That's like a debugger and the ability to freeze each frame of the video or step of the
program.

Don't be afraid of making mistakes. In this course, mistakes are good! Every mistake you make
is an opportunity to learn and grow. If you're afraid of making mistakes, you will be too afraid to
practice and experiment. In fact, mistakes are *so* important that the game creation process,
used in this course, includes testing to find mistakes and debugging to fix them.

You're not alone in making mistakes! All four instructors have already made every mistake
you're going to make — and they still make them!

If you make a backup copy of your code, experiment with it, and make a mistake, you won't
break anything permanently. You can always return to your original code.

One of the things I like best about programming is that, if I make a mistake, I don't waste
anything. If I make a mistake when baking or woodworking, I waste food or wood. Programming
resources are virtual and practically infinite. The freedom to try things and play with ideas is
empowering.

Keep the "just try it" attitude in mind as you program. Every time you wonder "could I do that?"
or "what would happen if I tried this?", you should just try it! If you combine "just try it", don't be
afraid to make mistakes, practice, and trace, you will succeed in learning to program.

22
Module 01: Design Hacking Version 1

01.01 Game Creation Process

Welcome to the Game Creation Process!

In this video, I'll introduce some of the computational problem solving techniques you will use to
create a game.

Computational Problem Solving, also called Computational Thinking, uses three kinds of
techniques to solve problems: problem decomposition, algorithms, and abstraction.

01.01@00:23

Fig. 01.01.01: The Computational Problem Solving Diagram

I'll introduce the first step of a new algorithm, or process, you will use to create a game. This
process is called the Game Creation process. You will apply this process and problem
decomposition to start creating your first game, Hacking.

Let's get started with the game creation process.

A process or algorithm is a sequence of actions that solves a problem or creates something,


which we call an artifact. Two examples of processes are: planning all the activities at a birthday
party, and following a recipe to make cookies.

23
There are many different processes that can be used to solve a problem. For example, consider
planning a birthday party. One process is to choose a theme, such as a movie, a season, or a
location, and use that theme to suggest costumes, food, and activities. A second process is to
ask each guest to wear their favourite costume, bring their favorite food and tell you their
favourite activity. Alternately you can use no process at all. Instead you can make unplanned
decisions during the party about costumes, food, and activities and hope for the best.

Using a process to plan a party takes more initial effort, but usually results in a more fulfilling
experience. The impromptu approach does not use a process and may result in unnecessary
repetition or boredom.

Similarly, there are at least two ways to make cookies. One is to follow a recipe which has been
tested before. You gather a list of ingredients, add the precise amounts specified, and bake at
the prescribed temperature for the stated time. The other approach is to use whatever
ingredients you have on hand, in whatever amounts seem right, and look in the oven every once
in awhile to decide if they are done. In my experience, the first approach is usually more
successful than the second. The first approach uses a process or algorithm and the second
does not.

Here is a more detailed example of a process. If you are asked to write an essay on the
difference between different kinds of bees, you could sit down and write the whole thing from
start to finish. This rarely works well. Instead, you might use this process: “think of ideas”,
“outline the essay”, “research the topic”, “write a draft”, “edit”, and "repeat". Many processes
include the idea of repeating some of the actions in the process several times.
This process decomposes the task of writing an essay into smaller, more manageable
subtasks so the task is easier to complete. Breaking a task into subtasks is called problem
decomposition. Problem decomposition is an important part of the game creation process.

Creating a commercial game involves many tasks, including artwork, music, voice acting, level
design, gameplay, and computation. Game creation is so complex and involves so many
specialties that a process is absolutely necessary.

The games in this course will not use artwork, music, voice acting, or level design, so these
games are considered computational problems. Even though this simplifies the games, they are
still too complex to write from start to finish. Therefore, you will use a process to create your
games.

Fortunately, you have already seen how you can decompose a complex problem into simpler
subtasks using problem decomposition. I have shown you how to use problem decomposition
for planning a birthday party, baking cookies, and writing an essay. Now let's decompose the
Create Game process.

24
This is the "Game Creation" process diagram (01.01@04:05). As you can see, even though
Create Game is a computational problem, there are still a lot of pieces to this puzzle

01.01@04:05

Fig. 01.01.02: The Game Creation Process Diagram

Each piece is a subtask you must complete. I’ll introduce each subtask in order.

The first subtask is Understand Game. You need to understand what game you’re trying to
make. This is true of any problem you are trying to solve. Understanding means you need to
know what happens, where it happens, when it happens, and why it happens. This will help
you determine how to create the game.

While writing the outline and doing research for the Bee essay you needed to understand the
problem. I would ask these questions: What does each kind of bee look like? Where do they
live? When does each kind of bee exhibit its daily behaviours and why do these bees do what
they do?

Gathering this type of information will let you answer the original question of how the bees are
different, which will allow you to write your essay.

Understand Game is a complex task, since it requires you to understand the what, where, when,
and why of a game.

25
Let's apply problem decomposition to divide Understand Game into two subtasks. These new
subtasks are: Observe Game and Play Game. Both subtasks will deepen your understanding of
the what, where, when and why of Hacking.

Observe, Play and Describe are the sub-tasks in a kind of problem decomposition called
experiential decomposition. Experiential decomposition uses three levels of engagement to
understand a problem. Observe is the passive first level of understanding. It provides a broad
but superficial knowledge of the problem by watching. When presented with a new problem,
observing lets you create an initial impression about that problem. For example, observing two
people playing tennis allows you to casually learn the actions and rules of the game.

Play is the active second level, where you achieve a deeper personal understanding of a
problem by engaging with it. This step integrates the new knowledge about the problem with
your own experiences. For example, playing tennis increases both your knowledge and skills in
a way that would not be possible without doing it yourself.

Describe is the interactive third level, where you explain the problem to others. Explaining a
problem is a social interaction. As you explain the problem to different people, you gain
understanding of the problem in many different contexts. This provides you with the deepest
understanding of the problem. For example, coaching tennis allows you to understand the game
from a different perspective. When you teach someone a skill, you must often ask yourself how
and why you do something in a particular way. It can also force you to adapt your knowledge to
suit others. For example, if you hold the racket in your right hand, you must revise your
knowledge to teach a left handed player.

The activities Observe, Play and Describe are sometimes called See, Do and Teach.
You will Observe and Play the Hacking game in the next two lessons. You will not Describe the
Hacking game as part of the Understand Game Process. Instead you will learn how to Describe
versions of Hacking elsewhere in the game creation process.

The computational techniques you learn using the game creation process can be applied to
other computational problems. If you can create games, you can solve general computational
problems.

26
01.02 Observe and Play Hacking Game

In this video you will observe the complete Hacking game. Your goal is to understand the game.

01.02@00:16

Fig. 01.02.01: Game Creation Process Diagram Observe Game

Pay attention to the details: What appears in the window? What does it look like? Where does it
appear? When do the different game objects appear and when do they change?

Keep these questions in mind as you watch the Hacking video.

(01.02 @ 00:32)The game starts! A window opens. Its title is Hacking and it has a black
background. Green lines of text are displayed on the left side of the window. This simulates a
vintage computer terminal.

27
01.02 @ 00:48

Fig. 01.02.02: Hacking Version 7 Demo

01.02 @ 00:48

Fig. 01.02.03: Hacking Version 7 Demo (Video Screenshot)

28
The text indicates that the computer is in Debug Mode and that there are four attempts left. It
looks like debug mode allows the player to make four attempts at something. Beneath this text
is a list of words embedded in symbolic characters. Under this list, the player is prompted to
enter a password.

What do you know about the game so far? You can tell that you will be making a password
guessing game played in a window, the text will be green on black, displayed line by line, and
the player will have 4 attempts to guess the password.

Let's see what happens when I enter a password.

01.02@01:44

Fig. 01.02.04: Hacking Game SURVIVE Input Result

29
01.02@01:44

Fig. 01.02.05: Hacking Game SURVIVE Input Result (Video Screenshot)

I will guess SURVIVE since it is a word in the window. On the right side of the window, you can
see a two-line hint. SURVIVE is incorrect, but two of the seven letters in the guess match letters
in the same positions of the secret password. The number of attempts has decreased by one.

I will guess the password, PUTTING. This guess is also incorrect, but now five of seven letters
are in matching positions.

30
01.02@01:53

Fig. 01.02.06: Hacking Game PUTTING Input Result

01.02@01:53

Fig. 01.02.07: Hacking Game PUTTING Input Result (Video Screenshot)

31
Now I'll try HUNTING. The window is erased and four lines of text are displayed in the middle of
the window, one line at a time. The text displays the password I entered. It then indicates that
the game is exiting debug mode, the login was successful and that the player should press
enter to continue.

01.02@02:01

Fig. 01.02.08: Hacking Game HUNTING Input Result

32
01.02@02:01

Fig. 01.02.09: Hacking Game HUNTING Input Result (Video Screenshot)

When I press enter the window closes. You are done observing the game.

What did you observe? The game asks the player to guess a password. There are a maximum
of four guess attempts. The game checks the exact placement of letters in the guess against the
secret password, and displays a hint. If you are successful, the game congratulates you.

What else did you see? Can you think of a situation that didn’t happen in the video but could
happen in the game? For example, what happens when the player makes too many incorrect
guesses?

Now it is your turn to play the game. Play several times to gain a deeper understanding of
Hacking.

33
01.03 Game Versions

You have completed the observe game and play game subtasks of "understand game".

Let’s reveal the next task in the game creation process: create version. The circular arrow next
to create version indicates you must do this task one or more times.

01.03@00:16

Fig. 01.03.01: Game Creation Diagram Create Version

We need to divide our game into versions, because big problems are hard to solve. So, we
break a big problem into several smaller problems called versions that are easier to solve.

Previously, we used experiential decomposition to decompose "understand game" into two


subtasks: "observe game" and "play game", based on the criteria called level of engagement.
Level of engagement is how much interaction a person has with a problem. Observing has the
least interaction, playing has more interaction and describing or explaining has the most
interaction.

Instead of experiential decomposition, we will use a new kind of problem decomposition, called
feature selection, to break our game into versions.

34
01.03@01:14

Fig. 01.03.02: Problem Solving Diagram Feature Selection

The complete Hacking game has a set of features that includes: displaying passwords,
guessing a password, using a window, supporting multiple password attempts and providing
hints. It is easier to create versions of the game that only support some of the features instead
of trying to develop all of the features at once.

The first version starts with a few features and each subsequent version adds a few more until
the last version has all of the required features. This makes each version easier to create since
we only need to focus on a few changes at a time.

Feature selection is used when you learn most new skills.

For example, if you learn a new language, you don't start by writing an essay or delivering a
speech. These activities require many linguistic features such as difficult words and complex
grammar. Instead you start by learning simple words and short phrases before adding more
complicated language features.

Feature selection determines the feature set that each version must implement.

Implementation effort should be balanced across the versions. Therefore, one of the criteria for
feature selection is balanced feature sets. You should select features for each version that
require approximately the same amount of effort to implement. For example, the window feature

35
alone is about as much effort as both "Displaying Passwords" and "Guessing a Password",
together.

Sometimes it is useful to split a single feature into multiple features to help balance the feature
sets. For example, using a window involves both opening a window and closing a window.

However, the second criteria for feature selection is feature dependency, since some features
depend on other features. A window cannot be closed if it has not been opened.

Unfortunately, it can be difficult to estimate feature effort without experience. Therefore, in this
course, we will select the version feature sets for you. You will learn to recognize good feature
sets as you use them. After this course, you can use your experience to create your own feature
sets for your own projects.

Let's look at Create Version in the game creation process. Create version is too complex to treat
as a single task. Therefore, I will apply a third form of problem decomposition, called Problem
Refinement, to create appropriate subtasks.

01.03@03:42

Fig. 01.03.03: Problem Solving Diagram Problem Refinement

Problem refinement solves a problem by decomposing a complete problem into a sequence of


increasingly detailed solutions. The criteria for problem refinement is level of detail, also called
precision. The first solution is complete, but has the least detail. For example, drawing a smiley
face has few details, but is a complete solution for the problem of "Drawing a Face".

36
The rest of the solutions are increasingly precise. The last solution is just as complete as the
first solution but has all of the details. For example, details can be added to the smiley face until
the final sketch is the most detailed.

Here is another example of problem refinement.

Let's plan a trip from the University of Alberta (U of A) to the University of British Columbia
(UBC). I can write detailed directions that start at the U of A and end at UBC. This would be like
writing a program one line at a time from start to finish. There are over 10 detailed instructions
just to get from the U of A to the Edmonton airport.

However, after creating these detailed directions, I may decide to take the train instead of flying.
All the detailed instructions are now useless! I have wasted time by focusing on details too
soon.

Instead I could use Problem Refinement to create an imprecise but complete plan: first Drive
from the U of A to the Edmonton Airport, then Fly from the Edmonton Airport to the Vancouver
Airport, and then Drive from the Vancouver Airport to the UBC campus. We can then test this
plan by researching costs and time before committing to it. If we want to try a different plan,
such as taking the train from Edmonton to Vancouver, it is easier to make changes to this short
plan than to change a long list of detailed directions. After researching the costs and time
required, I can refine the plan by using it to create more precise directions - by air or by train.

Similarly, it is unwise to write code before you know the general form of your solution. You
should plan your solution before you write the details as code. In software development plans
are called designs.

Therefore, I will divide Create Version into three tasks: Create Design, Create Program, and
Reflect. Each task creates a problem solution that is more precise than the previous one.

37
01.03@06:10

Fig. 01.03.04: Game Creation Diagram Create Version Subtasks


Create Design produces an imprecise solution, which is an outline for one version of Hacking.

Create Program converts this imprecise outline into a precise program for that version of
Hacking.

The Reflect task assesses your knowledge of the programming language techniques you
applied, evaluates your solution, and identifies improvements for the next version. Reflection is
refinement since reflecting refines your understanding of both your solution, your knowledge
and your skills to the highest level of detail required for this version. In addition, reflection is
similar to the explain or describe component of Experiential Decomposition since you reflection
is like explaining these ideas to yourself. This highest level of engagement leads to the most
detailed understanding for this version.

This is the last refinement since after reflecting you will have the most precise design, code and
understanding of your solution.

Let's return to Create Design. How should we accomplish this task? What about trying
something new? We could decompose it into subtasks! I know that's not really new, but it works.
Our goal is to keep sub-dividing our tasks until the final subtasks are so easy to solve that their
solution is obvious.

Let's use problem refinement to create three subtasks: Understand Version, Create Version Test
Plan, and Create Algorithm.

38
Understand Version probably looks familiar to you, since you have already seen the Understand
Game task. In fact, if we expand the Understand Version task:

01.03@07:58

Fig. 01.03.05: Game Creation Diagram Observe Game

Observe game corresponds to Observe Game Version, and Play Game corresponds to Play
Game Version.

Remember that experiential decomposition has three components: Observe, Play and Describe
or Explain. Create Version Description corresponds to Describe. It is a new task that produces a
written description of the game version. This description is an artifact that explains what the
game does, including its features and player actions. The description is imprecise because it
provides the goals of our implementation but not the details of the implementation itself.

The next task in Create Design is Create Version Test Plan. This task creates a test plan that
checks if the feature set is implemented correctly. This artifact is more precise than the game
description because it more explicitly considers multiple gameplay scenarios. The description
describes all possible outcomes, but the tests prescribe which sequences of player actions
result in which game outcomes. For example, the description indicates that a success or failure
outcome may be displayed, but the test plan prescribes exactly how the game must be played
to experience each outcome.

The Create Algorithm task produces the most precise artifact of the design. An algorithm is a
sequence of steps that prescribe how the code should be written. It is more precise than the
tests since the algorithm is "closer" to the code. Each step corresponds to a few lines of code.

39
However, an algorithm should be independent of any specific programming language, so an
algorithm is less precise than the code that implements it.

Now that all of the tasks in Create Design have been introduced, it's time for you to return to the
game creation process.

To start Understand Version, watch a gameplay video for the first version of Hacking.
Ask yourself the same questions you asked when you watched the full game: What does the
game look like? When do things appear in the game? And, what actions does the player take?
You will need the answers to these questions to create your version description.

40
01.04 Observe and Play Hacking Version 1

In this video you will observe gameplay for the first version of Hacking.

Remember to ask yourself questions like: What does the game look like? When do things
appear in the game? What actions does the player take?

(01.04@00:24)When the program starts, text is displayed using a default operating system font.
Each line is below the previous line. This is different from the final version of Hacking. In a
previous video, you saw that the complete final version, has green on black text that appears in
various locations of a game window with its own title.

01.04@00:27

Fig. 01.04.01: Hacking Version One

The header indicates that only one attempt is allowed instead of four. The passwords are also
printed without any symbolic characters.

The player is still prompted to enter a password. I’m going to enter HUNTING, which we already
know is the correct answer for the complete Hacking game.

41
01.04@01:05

Fig. 01.04.02: Hacking Version One HUNTING Input Result

A message is printed below my guess. It indicates that my guess is incorrect, even though I
entered the correct answer.

I'll press enter to end the game.

That's all for observing Hacking version 1! Now it’s your turn to play Hacking version 1. You
can play it as many times as you like to gain a deeper understanding of this version of the
game.

42
01.05 Describe Hacking Version 1

Now that you have observed and played Hacking Version 1, you are ready to move on to the
next task of describing the game. "Create version description" is the last subtask of
"understand version".

A description of a game is an explanation of what happens as the game is played. The


description includes aspects such as what the game looks like, how it changes and how it
responds to player actions.

Essentially, a description prescribes the features that must be included in the game, and how
these features interact. Reading your game description should give the reader a complete idea
of how to play the game. The description does not tell you anything about how the game is
implemented, but it does describe everything that happens as the game is played and all the
possible outcomes of the game.

Let’s create a description for Hacking version 1 using the Description Builder.

The description builder is a tool that mimics a collaborative team environment. In a real team
environment, each member observes the game and suggests descriptive attributes. Other team
members provide feedback and guidance, and together the team creates a complete description
of the game.

The Description Builder provides phrases that describe all aspects of the current version. Your
task is to assemble these phrases into a comprehensive description that accurately describes
the version. To guide you, the description builder also provides valuable feedback that simulates
a teammate or mentor.

Let’s take a look at the Description Builder.

43
01.05@01:41

Fig. 01.05.01: Hacking Version 1 Description Builder

(01.05@01:41)The title of the current activity is in the top left corner of the window. The title is
"Hacking, Version 1 Description. Beside the title is the menu button. There are several menu
options. You can save your description, open a saved description, or download a PDF of your
current description. You can reset the exercise to start over. The reset option permanently
deletes whatever you have added using the builder and cannot be undone. You can also view a
small tutorial of how to use the description builder at any time.

If you click the button in the top right corner, you can observe a gameplay video for the current
version of the game. The builder contains two main panels.The left panel has two tabs and each
tab contains description phrases. One tab is labelled Objects and Actions. The other tab is
labelled Attributes.

These phrases describe three kinds of game concepts. The concepts are:
- Objects that are displayed, such as a header or message;
- Actions by the Player or Game, such as entering a guess or clearing the window
- Attributes of objects, such as the location of an object

To create an accurate description, you must describe all the objects, actions, and attributes of
the game by adding the relevant descriptions to the right panel.

You can add objects and actions to the right panel in any order, as long as you position the
concepts correctly relative to what you have already added. You must place a new object or
action below everything that occurs before it in the game and above everything that occurs later

44
in the game.For example, since the password list is displayed before the player enters a guess,
the description of the password list must be *above* the description of entering the password.

As you add objects, actions, and attributes to your description, you will get feedback from the
builder if you make a mistake. These feedback messages will appear in the bottom right corner.

I will add the first few phrases to the Hacking version 1 description. I will also show you the
different kinds of feedback you may encounter. You will finish adding the rest of the phrases
after watching this video.

When I start a new description, I ask myself “what happens first?”. In Hacking version 1, the text
‘DEBUG MODE’ is displayed. In the Objects and Actions tab, there is no phrase that describes
the "DEBUG MODE" line of text. I'll switch to the Attributes tab which contains the phrase "the
first content line indicates debug mode" That seems appropriate. I'll try adding it to the
description.

01.05@04:18

Fig. 01.05.02 Description Builder Attribute Feedback

It doesn’t work and the attribute returns to the left panel. Feedback appears in the bottom right
corner of the builder that says, “An attribute must belong to an object or action.”

This is the first kind of feedback, and it describes the relationship between attributes and
objects. An attribute cannot exist without an object, so it cannot be placed in the description
without the object or action it describes. I need a phrase that indicates an object is displayed
before I can use the debug mode attribute.

45
There are six phrases in the objects/actions tab of the description panel that I can use. Four of
them are objects: a "password list", a "header", a "failure outcome", and a "prompt for a guess".
Two objects are messages, which are one or more lines of text that are displayed for a single
purpose without player actions. Two are prompts, which are one or more lines of text that wait
for the player to enter some text and press the enter key. Two of these six phrases are actions:
"the player presses the enter key to end the program" and "the player enters a guess and
presses the enter key"

The "debug mode" attribute is part of the first two lines of text when Hacking is started. We’ll call
these lines the header. Therefore, I’ll select “The game displays a header” and add it to the
description.

Since it stays in the right panel, it must be in the right place! Now I can try "the first content line
indicates debug mode” as an attribute of the header object. Yes it is an attribute!

01.05@04:18

Fig. 01.05.03: Hacking Version 1 Header With Content Attribute

I’m not done describing the header, because every display object has five attributes. These
attributes are:
- content
- location
- size
- color
- and timing or temporal.

46
The content of the header is its subject. The content is partially described by the object itself,
but attributes are used to further describe the subject. The header consists of two content lines
and a blank line. So I will add "It consists of two content lines followed by a blank line" to the
description. I have already added the debug content line as an attribute. To complete the
content I must find an attribute that describes the attempts left content line and add it to the
header object.

The location of the header is the top output line. This means, the next attribute I select should
be: "It is the top line of game output"

Since this version does not use its own window, all objects are textual. We will use the default
operating system text size and color, so we won't specify these attributes for each object.

The timing or temporal attribute, of an object describes when it is displayed, altered, or erased.
All objects in this version of Hacking are displayed in the order that we describe them, from top
to bottom, and none are erased. Since this version is textual, the spatial order also defines the
temporal order.

In this version, the term “below” means both “below”, in the spatial sense, and “after”, in the
temporal sense. In future versions and games, we will include explicit temporal attributes for
some objects.

01.05@07:34

Fig. 01.05.04 Completed Header Object in Hacking Version 1

Great! The header is finished.

47
The passwords are displayed next in the game, so the next object I’ll add to the description is:
"The game displays a password list." There are several places I can add this object. It belongs
below the header since it is displayed after the header. But let's see what happens if I add it
above the header.

01.05@07:49

Fig. 01.05.05: Position Based Feedback in the Hacking Version 1 Description Builder

The feedback I get is “This concept should be placed higher or lower in the description.” This is
the second kind of feedback, which describes the position of concepts in the description.
When adding a new object or action to the panel, it must be correctly positioned higher or lower
than the objects and actions already in the panel, based on their temporal order in the game.

This feedback is expected. In this case, the password list should be lower, since the game
displays the password list after the header.

In this version the temporal order matches the spatial order, but when the temporal and spatial
orders don't match, the description order must follow the temporal order. For example if an
object is added above the password list in the window, but is displayed later than the password
list, it must be below the password list in the description.

Let's see what happens if I try to make the password list an attribute of the header.

The description builder won't let me add it to the header. The password list is not an attribute of
any other display object, because it is a display object.

48
Let's put the password list where it belongs, below the header.

It worked! Let's add the password list attributes. Attributes can be added in any order. The first
attribute is content, which is the subject of the password list text. The attribute "It consists of 13
content lines followed by a blank line" belongs to the password list. There is a second content
attribute for the password list, but I will leave it for you. The second attribute is location, so I will
add, "It is directly below the header" to this object.

To show you the third kind of feedback, I'll try to add the attribute: "It indicates the player should
enter a password." to the password list.

This is incorrect, so the builder displays the new feedback, which is: "This attribute does not
belong to this concept."

01.05@09:41

Fig. 01.05.06 Attribute Does Not Belong Feedback in Description Builder

The "enter a password" attribute is not really an attribute of the password list. It is an attribute of
the password prompt.

I’ll leave the rest of description to you, including the other password list attribute.

The description builder uses two new kinds of problem decomposition, temporal decomposition
and spatial decomposition. They allow us to decompose the Create Description task into object,
action and attribute descriptions.

49
Temporal decomposition divides a task into sub-tasks based on the order that they must be
performed. We actually used temporal decomposition in our trip planning example, since each
direction must follow the appropriate previous direction in time. You cannot fly to Vancouver
before you drive to the airport. Temporal decomposition is applied to creating a version
description by describing the objects, actions, and attributes in the order that they occur in the
game.

Spatial decomposition divides a task into sub-tasks based on their spatial location.

All of the details that affect a single object are grouped together. When we start describing a
particular game object, we describe all of its attributes, together.

Now it’s your turn. Add the other attribute to the password list and finish the rest of the version
description yourself. If you change your mind about adding an object or action to the description,
you can simply drag it back to the Objects and Actions panel. If duplicate phrases appear in the
Description Builder they must be used multiple times. Your completed description must list all
objects and actions in the order they will occur in the game. You must add all the appropriate
attributes to each object, but you can add them in any order.

50
01.06 Create Functional Test Plan Hacking Version 1

Now that you have completed the Hacking Version 1 description, let’s revisit the game creation
process diagram.

You just finished the "understand version" subtask of "create design", so let's work on the next
subtask, which is "create version test plan".

01.06@00:27

Fig. 01.06.01: Game Creation Diagram with Create Version Test Plan

A test plan is a group of tests that is performed on a program to make sure that it performs as
expected. To start, our test plans will only contains functional tests. We will introduce
non-functional tests later. Each functional test consists of an action and a group of questions.
The tester manually performs the action, and uses the group of questions to evaluate whether
the game responds to that action as described in the game description. Each test is phrased as
a yes or no question. All answers must be yes for the program to be completely correct.

We test our game to ensure that the game we create matches the game we are supposed to
create. We create the tests before we create our program to provide an extra level of precision
between the description and the code. Creating tests before you program, forces you to plan the
behaviour of your game before you code.

51
In industry, creating your tests before writing code is often called Test-Driven Development or
TDD. However, TDD often uses a testing program that tests an application automatically, rather
than using manual tester actions.

Problem refinement prescribes that we slowly increase levels of precision as a useful way to
simplify problems. Tests allow us to introduce logic into our design by explicitly describing the
potential actions that the player can take, and how the game should respond to each action,
before we need to code this logic.

The tests also indicate what can go wrong. It is useful to know this information before we start
writing code, so that the code can handle these potential errors.

Each version will modify the previous version's test plan.

By the final version, your test plan will test all the features and most of the logic of a game. As
the complexity of a game increases, complete testing of all combinations of all possible player
actions becomes impractical. For example, consider a video game with 8 quests that the player
can complete in any order. This results in 8 factorial, or more than 40 thousand, different game
paths. Testing for each of these quest completion orders is impractical.

A test plan allows anyone to quickly find errors or missing features in a game. No specialized
knowledge of the program is necessary. In fact, in commercial games, the quality assurance
team that does the testing is usually different than the programming team.

Since our games are not complex, you will be able to make tests for all representative game
paths.

This is the Functional Test Plan builder. You will use it to make the test plan for Hacking Version
1.

52
01.06@03:42

Fig. 01.06.02: Functional Test Plan Builder Hacking Version 1

(01.06@03:42)The activity title: “Hacking version 1 functional test plan” is in the top left corner
of the builder. Like the Description builder, you can press the button on the right to view a
gameplay video. If you want to save, load or restart your exercise, you can use the menu button
on the left.

There are three panels. The left panel contains two tabs, labelled Actions and Questions. When
you select the Actions tab, you see the actions a tester can take to test the game. When you
select the Questions tab, this panel contains questions that a tester can use to evaluate whether
the game responds correctly to the actions. You will create your Functional Test Plan by adding
actions and questions to the middle panel. The right panel contains a description for the current
version.

I will begin with an action. The Functional Tests must be listed in the order that the actions
should be performed by the tester. These actions must form one or more complete game paths
from beginning to end.

Positioning actions in test plans is like positioning objects and actions in a description. As you
work, you can add any action to your test plan at any time. However, you must place every
action below all positioned actions that must be tested before it and above all positioned actions
that must be tested after it.

The first action that any tester must take is to start the game. Let’s see if I can find an action that
matches that sentiment.

53
I will add "Start the Program" to the test plan.

01.06@05:31

Fig. 01.06.03: Functional Test Plan Builder with First Action Added

From the Version Description, you can tell that the first three objects appear as soon as the
player or tester starts the game. I will add some questions to test the "start the program" action
later, but first, I'll add another action.

The first tester action after starting the game should be entering a password. In the Actions
panel, there are two sentences related to entering a password: “Enter the correct password,
HUNTING, and press the enter key" and “Enter the incorrect password, SETTING, and press
the enter key”. I could use either one, but I will add the correct password action to the test plan.
If I add this action above ‘Start the program”, it returns this action to the action list and feedback
tells me that I must place the actions in a different order.

54
01.06@06:16

Fig. 01.06.04: Functional Test Plan Builder Action Order Feedback

This is the first kind of feedback for the functional test plan builder: Actions must be placed in a
temporal order that creates a valid test plan. You can’t test a correct password before the
program even starts. I will place the correct password action below "Start the Program". That's
better, it stays in place.

Let’s look at the questions. To view the questions, select the Questions tab. All the questions in
the Questions tab are about the objects described in the Version Description. Questions related
to a specific object are organized into a group, which we call a question block. You will select
these question blocks rather than individual questions. This is an example of spatial
decomposition since all properties of a single object are tested together.

The format of our functional tests requires that every question block belongs to the action that
the questions are testing. There is a block of questions that starts with “Does the game display a
header?”. This block also tests each attribute of the header. I will add the header question block
to the “Start the program” action, since it tests the results of this action.

55
01.06@07:46

Fig. 01.06.05: Functional Test Plan Builder with Question Block Added

Great, the question block appears inside the action to show that it belongs to this action.

To show you the second kind of feedback for the test plan builder, I will try to add a question
block outside an action. I will add "Does the program end?" to the end of the test plan. As
expected, the question block returns to the left panel and you see the second kind of feedback
for this activity. Just like the attribute feedback in the Description builder, this kind of feedback
informs us that a question block must belong to an action.

56
01.06@08:15

Fig. 01.06.06: Function Test Plan Builder Question Block Feedback

Let's look at the version description again. The next feature that needs to be tested is the
password list so, I will add the question block that starts with “Does the game display a
password list?”.The order of the question blocks is just as important as the order of the actions.

As you work, you can move any question block to the assembly panel at any time, as long as
you place it into an action. However, you must place it below all positioned question blocks that
must be tested before it and above all positioned question blocks that must be tested after it.

If I try to add the password list question block above the header it returns to the questions panel
and you see the third kind of feedback: This question block should be placed higher or lower in
the action. In this case, it should be lower, since the password list appears after the header
when the game is played.

57
01.06@09:17

Fig 01.06.07: Functional Test Plan Builder Question Block Order Feedback

Now I will add the password list question block below the header questions. It stays in place, so
I've made the right choice.

I’ll leave the last question block for the start the program action for you to complete in the next
activity.

Now, let’s look for questions related to the “Enter the correct password” action that I added
earlier. The version Description indicates that a failure outcome is displayed after the player
enters a password guess. So, the correct question block is "Does the game display a failure
outcome?”.

58
01.06@10:00

Fig. 01.06.08: Functional Test Plan Builder Failure Outcome Question Block

Excellent, it stays under the "Enter the correct password" action.

Earlier you saw that there were two tester actions related to entering a password. I tested the
correct password action, but an incorrect action must also be tested. Why? A test plan differs
from a description. Testing requires the game to be played several times to check for correct
behaviour under all possible game conditions.

The version 1 description indicates that after the player enters a guess, the game displays a
failure outcome. This happens no matter what guess the player enters. So, in this version both
correct and incorrect passwords must result in the same failure behaviour. For example, my
code would be wrong if entering the correct password displayed the failure outcome, but
entering an incorrect password caused the game to display no outcome or end abnormally.
Therefore the test plan must test both actions, which requires me to play the game twice.

The description does not differentiate between entering a correct and incorrect guess. The test
plan is more precise because it explicitly tests two specific gameplay scenarios.

While there are many different kinds of incorrect guesses, in this version you only need one
incorrect and one correct guess to represent the two most common gameplay paths.

In the next activity, you must add the “Restart the program” action to your test plan to test this
incorrect password path.

59
Here is an example of the fourth kind of feedback. Each question block belongs to a specific
action so I can't add a question block to the wrong action.

01.06@11:59

Fig. 01.06.09: Functional Test Plan Builder Question Block Assigned to Wrong Action Feeback

Notice that there are some duplicate actions, such as "Press the enter key to end the program"
and duplicate question blocks. An action or question block is duplicated if it must be used more
than once in the test plan. However, also notice that there are no duplicates for the header, the
password list, or the guess prompt tests, even though you will test the program twice. This is
because, you don't need to retest anything before the the player makes a guess, since this
game always behaves the same way until then. You must retest the failure outcome and end
prompt since entering an incorrect password can potentially affect all gameplay that comes after
the different player action.

In general, each attribute in the description will generate a question in the the test plan. In
addition there may be extra questions that test the gameplay, but are not represented by a
particular attribute. For example, "The game prompts for a guess" has two attributes which must
be tested. However, there is an additional question that asks, "Does the game wait for the enter
key to be pressed?". It is important to test this extra waiting functionality that is not represented
in the description by any attribute.

In the next activity you will add the missing actions and questions to complete the functional test
plan.

60
01.07 Create Algorithm for Hacking Version 1

Let's look at where we are in the Game Creation process.

01.07@00:15

Fig. 01.07.01: Game Creation Diagram Hacking Version 1

First, you completed the Understand Game task by observing and playing the game. Then you
moved on to the first two subtasks of Create Design. You completed Understand Version by
observing, playing and describing version one. Next you created a Version Test plan which you
will use to test your version 1 code. You are now ready to tackle Create Algorithm, the final
subtask of Create Design. Your algorithm will be the most precise artifact of your design.

An algorithm is a sequence of steps that solves a problem. An algorithm is more precise than
a test plan, because its steps define the logic of the solution. Solution logic accounts for all
potential situations that may occur in a problem and prescribes how to handle them. The
algorithm refines the test plan.

Since our problem is creating a game, each situation is a sequence of game events that can
occur as the result of player actions. Therefore, the algorithm steps must accommodate all of
these game event sequences. Although an algorithm is more precise than a test plan, it is less
precise than code. It is less precise since it is more human-readable than code and it ignores
unimportant details.

For example, let's create an imprecise list of steps for the task of eating an apple. I start with an

61
imprecise step such as “pick up apple”, rather than the more precise step, “move your hand ten
centimeters towards the apple, grasp it with your fingers and lift the apple five centimeters”.
“Pick up apple” is more general since it ignores the details of moving your hand a certain
distance, closing your fingers around the apple, and lifting it up. I can complete this algorithm
with a second and third step: "eat apple" and "discard core".

The steps of an algorithm are performed in sequential order from first to last. This means the
algorithm steps must be listed in the order they should be performed. An algorithm uses
temporal decomposition to divide the problem into its steps. There are mechanisms to enable
an algorithm to selectively skip or repeat some steps. You will discover the first of these
mechanisms in version 3. Order is important in an algorithm because otherwise the algorithm
may cause an error or may not solve the problem. If the two steps "eat apple" and “discard
core”, were reversed, the algorithm would not solve the problem. You cannot eat the apple after
discarding it.

In this course, all algorithms are programming-language independent. This means the algorithm
you create is a general solution that does not rely on a specific programming language. You will
write a Python program that implements your algorithm later. The same algorithm could be
implemented in another programming language, such as Java or C++.

You will use the Algorithm Builder to create your algorithm. This tool differs from the other
builders you’ve used so far, since it is based on connecting various graphical shapes together
instead of arranging lines of text. Each shape will contain one algorithm step. The steps are
connected using arrows, to indicate the step order when the algorithm is performed. Each step
in your algorithm will ultimately translate into a few lines of code when you write your program.

Let’s explore the Algorithm Builder with an example algorithm (01.07@03:15). In the top left
corner, you see a game title and version number: Make a Cake Version 1 Algorithm. The
Algorithm builder has a main panel where you will create a complete but imprecise solution to
the problem.

62
01.07@03:15

Fig. 01.07.01: Make a Cake Algorithm

Let’s take a look at an algorithm for making a cake. The steps in the main panel are:
● Make batter.
● Bake cake.
● Decorate cake.

Each of these steps is in its own rectangle. The components palette, to the left of the main
panel, contains a shape for each kind of step you can add to an algorithm. In later versions you
will see other shapes, but there is currently only one shape in the palette, a rectangle labelled
"step". A rectangle represents a sequence of one or more precise program statements.

Each step in a rectangle consists of an action and an object. Make, bake, and decorate are
actions. Batter and cake are objects. When you add a step, you must choose appropriate action
and object words to name the step.

The main algorithm for the Make a Cake algorithm is already complete. Now I will increase the
precision of one of these steps by adding detail. It isn’t obvious how to perform the “Decorate
cake” step. The details of a step in the main panel can be viewed and edited by clicking on its
shape. This will create a new panel that expands a single step into more precise steps. These
steps are also represented by connected shapes.

I’ll expand the "Decorate cake" step.

63
01.07@05:29

Fig. 01.07.02: Expanded Decorate Cake Step

A panel for “Decorate cake” opens and you can see its detailed steps.

● Make icing.
● Ice cake.
● Add candles.
● Light candles.

These steps use the additional actions: ice, add, and light; and the additional objects: icing and
candles. All of these steps are in individual rectangles, connected by arrows, and follow the
action-object format, assembled from words in the algorithm’s vocabulary.

I’ll clear the algorithm builder to start building the Hacking version one algorithm.
The first sentence of the version 1 description is "The game displays a header", so I will create
the algorithm step for this part of the game.

I will add a step to the main panel. To complete the step, I must select an action and an object
from the vocabulary provided. The vocabulary contains objects you used in the Hacking version
one description and test plan, such as “game” and “guess”, and actions such as “display” and
“prompt for”. You should consult your description to select an appropriate action and object from
the vocabulary, for each rectangle.

I will choose "display" from the actions list, "header" from the object list, and I'll click the "Add
Step" button to complete the step.

64
The description includes many details about the header, which will be in the code, but are not
included in the algorithm. Most attributes of objects are not described in an algorithm.

The next object in the description is the password list. You will add a step to display the
password list in the next activity.

The description then indicates that the game prompts for a guess, so I will add a step for the
prompt. I will choose the action "prompt for" and the object “guess”.

You can check whether your algorithm is correct and complete anytime you want, by pressing
the "Check Algorithm" button.

01.07@07:33

Fig. 01.07.03: Algorithm Builder Missing Components Feedback

The feedback indicates that the "main program is missing 2 components". You already know
that you must add a step for displaying the password so there is one more step as well.

The next description action is: "The player enters a guess and presses the enter key." This
action will not become an algorithm step, because an algorithm cannot prescribe player actions.
Some algorithm steps respond to player actions, but no algorithm step translates to code that
makes a player action. Therefore the last algorithm step should display a failure outcome. I will
add a step, using the action "display" and the object "failure outcome".

65
With the exception of the step that displays the password list, the algorithm should be complete.
However, the display failure outcome step has two important parts: displaying the outcome and
prompting the player to end the program. I will expand the display failure outcome step and add
these two steps. I will use "display failure outcome" and "prompt for end".

01.07@08:49

01.07.04: Expanded Display Failure Outcome Step

However, it will cause an error if I use the same name for the general "display failure outcome"
step in main and the more specific "display failure outcome" step that it contains. I will rename
one of them to avoid this error. In general, don't use the same name for two steps that perform
different actions.

In main, I will delete the "display failure outcome" step and replace it with a new step called "end
game". Deleting a step, also deletes all steps inside its panel. "End game" is a good general
name since both "displaying the outcome" and "prompting for end" are parts of ending the
game.

66
01.07@09:06

Fig. 01.07.05: Hacking Version 1 Algorithm Panel with End Game

In general there are many ways to correctly decompose an algorithm. For example, it is possible
to move both "display failure outcome" and "prompt for end" from the "end game" panel to the
main panel and preserve the correct step order. However, it is better to keep these two steps in
the "end game" panel. As algorithms get larger, they are easier to understand if each panel only
contains a handful of steps. The point of decomposition is to break each task into a small
number of manageable subtasks and that is what the algorithm panels allow you to do. If we did
not use panels, the algorithm for the final version of hacking would look something like this.

67
01.07@10:02

Fig. 01.07.06: Hacking Version 1 Algorithm with No Panels

Since there are many ways to decompose a problem, each algorithm you make is only one of
many possible correct algorithms. In this course, the algorithm builder will guide you to one
specific algorithm for each game version. You may need to experiment to discover the specific
algorithm for each version. The algorithm builder feedback will guide you towards this specific
algorithm.

I'll check the current state of the version 1 algorithm. The feedback indicates there is a step
missing in main.

68
01.07@10:32

Fig. 01.07.07: Hacking Version 1 Main Panel Missing 1 Component

Now it's your turn. You must fix this error by adding the missing step. You must also expand the
"end game" step and re-add the two steps needed to end the game. After completing this
activity, you will complete several lessons about Python before you translate your algorithm into
Python code.

69
Module 02: Program Hacking Version 1

02.01 Python - Evaluation Examples

In this video I'll evaluate two kinds of simple Python expressions, literal strings and function
calls. I'll use a Python shell to evaluate some Python expressions.

Here's our first expression.

(02.01@00:18)What just happened? I typed ‘hello’ and pressed enter. The Python interpreter
responded by displaying the same characters that I typed.

Does this always happen? I'll type len of hello.

This time you see a 5. Python computed something. What actually happened here?

02.01@00:42

Fig. 02.01.01: Results of ‘hello’ and len(‘hello’) in Python Shell

In each case, I typed a simple Python expression. The Python interpreter evaluated the
expression to obtain a result object, and displayed a human readable form of the result object in
the Python shell. The Python interpreter then displayed the greater greater greater prompt on
the next line to indicate that it was ready to evaluate another Python expression.

70
The Python expression, ‘hello’, is called a literal string. Literal strings can be enclosed in single
or double quotes. Single quotes are used in this course. For brevity, we say quote instead of
single quote.

The second Python expression, len of paren quote hello quote paren, is called a function call. In
computing science, a function is like a machine that has inputs and outputs. The len function
has one input or argument object. It also has one output or result object. The argument is a
string object and the result is an integer object that represents how many characters are in that
string.

As an analogy, think of placing an order at a drive-through restaurant. The input is your verbal
order and the output is the meal you receive. Functions are also used in mathematics, such as
the squaring function and the square root function. The square of 3 is 9 and the square root of 4
is 2.

The len function I used earlier is a built-in function, where len is short for length. The literal string
was used as the function argument. The len function computed the number of elements in its
argument object. Since hello has 5 characters, len computed 5.

How do you know what the built-in function len does, and are there other built-in functions you
can use?

The official documentation for Python is on the official python website. Python has a standard
library that contains built-in functions and the standard library documentation describes what
built-in functions are available and what these function do.

71
02.01@02:51

Fig. 02.01.02: len Documentation on python.org

The len function returns the length (the number of items) of an object. The argument may be a
sequence (such as a string, bytes, tuple, list, or range) or a collection (such as a dictionary, set,
or frozen set). So far the only sequence type you have seen is string.

I'll apply the len function to a different literal string, goodbye. What should happen after I press
enter?

72
02.01@03:18

Fig. 02.01.03: Result of len(‘goodbye’) in the Python Shell

The integer 7 appeared. That makes sense, since there are 7 elements in the string, goodbye.
By analogy, if you place a different order at the same restaurant, you receive a different meal -
the output depends on the input. In addition, if you place the same order at a different
restaurant, you also receive a different meal. The output depends not only on the input, but also
on the function itself.

For example, when you apply a different built-in function, id, to the same input object 'hello', you
get a different result object.

73
02.01@03:53

Fig. 02.01.04: Result of id(‘hello’) in the Python Shell

The meaning of the id function will be discussed later. The point here is that a different function
yields a different result.

Will Python recognize every character sequence you type? I'll type len question mark and press
enter to evaluate it.

74
02.01@04:10

Fig. 02.01.05: Reported Syntax Error Resulting From Interpreting len? in the Python Shell

Hmm, the Python shell has reported a syntax error. Python recognizes len as a valid token, but
question mark is not part of any valid Python token. This is actually a lexical error, but this
Python interpreter reports all lexical errors as syntax errors. Tokens, lexical errors, and syntax
errors are explained in future lessons.

Why do I need the parentheses? What happens if I leave them out by entering len quote hello
quote?

Although this expression contains two valid Python tokens they do not form a valid Python
expression. For you to understand why this error occurred, you must understand the syntax of
Python. Before defining the syntax of Python, it is useful to look at more examples.

Why does 'hello' have quotes and len does not? Recall that 'hello' is called a literal string. len
without quotes is called an identifier or name. I'll put the parentheses back but change hello
from a literal string to an identifier by removing its quotes.

75
02.01@04:48

Fig. 02.01.06: Reported Syntax Error Resulting From Interpreting len’hello’ in the Python Shell

This time, the Python shell has reported a name error, which is a kind of semantic error. This
error indicates that the name, hello, is not defined. For you to understand why this error
occurred, you must understand the semantics of Python.

You have just seen some successful Python evaluations and three different kinds of errors:
lexical, syntax, and semantic. Recognizing these three kinds of error is important, since the way
you fix each of these errors is different. You will begin learning about tokens, lexical analysis,
syntax and semantics in the next lesson.

76
02.02 Python - Interpretation

This video introduces the three step Python interpretation process that is used to evaluate
Python code.

A Python program is composed of statements. The simplest kind of statement is an expression


statement, which contains one or more expressions on one line. For example, 'len of hello' is an
expression statement that contains a single expression, 'len of hello'. When you enter a
character sequence into a Python shell and press the enter key the Python interpreter tries to
interpret the character sequence as one line of a Python statement.

What process does the Python interpreter use to interpret a character sequence as a Python
statement in a Python shell? Python uses an analogous process to the one used by natural
languages. Natural languages are languages such as English, French and Mandarin. In natural
language, text is divided into basic units such as words and punctuation. These are assembled
into syntactic constructs called sentences. Meaning is then assigned to the words and
punctuation in the sentences.

The first step of interpretation is called lexical analysis. In this step,a character sequence is
translated into a sequence of basic units called tokens. When you think, read, write or speak
using a language, you do so in terms of tokens such as Mary rather than individual characters,
such as M, A, R and Y. In Python, the interpreter combines characters into tokens.

There are several definitions of the term token. Here is a definition in the context of language.

A token is a sequence of characters that has meaning as a basic language unit.

In many natural languages, there are two kinds of tokens. One kind of token is words, such as
“Mary”, “is” and “I”, and the other is punctuation symbols, such as question mark and comma.
Natural languages, use tables to list each token kind. For example, English has 14 different
punctuation symbols. In programming languages there are several kinds of tokens. Python has
5 different kinds of tokens: delimiter, literal, identifier, operator, and keyword.

Delimiters in programming languages are analogous to punctuation in natural language. For


example, left paren and right paren are delimiter tokens in Python. The other kinds of
programming language tokens are analogous to words.

In natural languages, words are divided into groups called parts-of-speech, such as noun, verb
and pronoun, so each of these parts-of-speech is a different kind of word token. Although
“Mary”, “is”, and “I”, are words, they are more specifically a noun, verb and pronoun
respectively.

77
Literals and identifiers in programming languages are analogous
to different parts-of-speech in natural language. A noun refers to a
specific object, such as Alberta. A literal string in a programming
language is analogous to a noun since a literal string also refers
to a specific object such as 'hello'.

A pronoun refers to different people at different times. For


example, "I" refers to me, *Duane*, when I am referring to myself.
However, “I” refers to me, *Elyse*, when I am referring to myself. An identifier in a programming
language is analogous to a pronoun, since you will discover that the same identifier can refer to
different objects in different parts of a program. For example, you have seen that the identifier
len refers to the length function object in the len of hello expression. However, it is possible to
use the same identifier, len, elsewhere in a program to refer to a different object.

What happens when an invalid token is used in natural language? For example, how is the
English character sequence: "it snuvs" interpreted? In natural languages, the table that lists all
valid word tokens is called a dictionary. If a sequence of characters is not in the dictionary, they
do not form a valid token so they cause a lexical error. “it” is a valid English word token. But,
“snuvs” is not in an English dictionary, so it causes a lexical error - an invalid token. Similarly, if a
programming language interpreter encounters an error when it is creating tokens from
characters, it is called a lexical error. However, this Python interpreter reports lexical errors as a
kind of syntax error.

Remember what happened when I entered the characters sequence, len?. Question mark is not
a valid Python token just like snuvs is not a valid English word.

If I enter the character sequence, 'hello, with a quote at the beginning but not at the end, the
Python interpreter reports a syntax error, which is really a lexical error, since 'hello is not a valid
literal string token without its ending quote.

02.02@05:03

Fig. 02.02.01: Syntax Error Resulting from Interpreting ‘hello in the Python Shell

78
02.02@05:03

Fig. 02.02.02: Syntax Error Resulting from Interpreting ‘hello in the Python Shell (Video Screenshot)

The second step of interpretation is called syntax analysis. In this step, tokens are combined
into English sentences or Python statements. Here is a definition of syntax that applies to both
natural and programming languages.

Syntax defines the format or structure of sentences and statements.

How do people assemble natural language tokens: words, and punctuation, into syntactically
correct sentences? A natural language uses a grammar.

Consider a very small subset of English that contains one punctuation symbol, the question
mark, and six words: the verbs “was” and “is”, the nouns, “Mary” and “School” and the
interrogative adverbs, “When” and “How”.

Syntax diagrams define a grammar that represents the valid format of natural language
sentences. For example, here (02.02@06:04) is a syntax diagram for a sentence called a
question, for our small subset of English.

79
02.02@06:04

Fig. 02.02.03: Syntax Diagram for a Small Subset of English

02.02@06:04

Fig. 02.02.04: Syntax Diagram for a Small Subset of English (Video Screenshot)

You can use this diagram to check whether a token sequence forms a syntactically valid
question. A token sequence is syntactically valid if there are exactly four tokens that match the
four diagram token states in order.

Does this token sequence form a syntactically valid question:


"How" "is" "Mary" "question mark"

This token sequence has 4 tokens. The token, “How” is an adverb, which matches the adverb
state. The token, “is”, is a verb, which matches the verb state. The token “Mary” matches noun,

80
and the token “?” matches ?. Since there are no more tokens, this token sequence is a
syntactically valid question.

This token sequence:

"When" "Mary" "was" "question mark"

is syntactically invalid.

It starts with an adverb which matches the first state of the syntax diagram. The next token is a
noun which does not match the verb state. This mismatch is a syntax error in our small subset
of English.

Similarly, if the Python interpreter encounters an error when it is combining tokens into
statements, it reports a syntax error.

Recall that Python successfully evaluated len of hello enclosed in parens and quotes. However,
when the parentheses were left out, a syntax error occurred. Although this expression has two
valid tokens, len and ‘hello’, there is no Python syntax diagram that contains these two token
kinds, identifier and literal, in order, as a valid expression.

A sequence of tokens may have valid syntax and still not make sense. For example, the token
sequence: "When" "is" "Mary" "question mark" is syntactically valid in our simple
English grammar. It consists of the adverb “When”, followed by the verb "is", followed by the
noun “Mary” and ends with the “question mark”. Although this sentence has valid syntax, its
meaning or semantics is not valid.

To be useful, both natural language sentences and Python statements must be semantically
valid in addition to being syntactically valid.

The third step of interpretation is called semantic analysis where the interpreter assigns
meaning to syntactically valid sentences and statements.

Semantics defines the meaning of sentences and statements.

How do people assign meaning to natural language sentences? You can use a dictionary to
interpret the meaning of individual words in a syntactically correct sentence. Many words can be
used as different parts of speech and most have multiple meanings. Therefore, the meaning of
a word in a sentence depends heavily on context. For example the word school can be used as
a noun with at least two different meanings: “How was school?” or “A school of fish”. It can also
be used as a verb in: “I can school my brother”. This context dependence means that no set of
simple semantic rules can describe the meaning of all syntactically valid natural language
sentences.

81
In programming languages, tokens are less context dependent. A set of simple semantic rules
can define the meaning of all syntactically correct statements. To accomplish this, programming
language syntax and semantic rules must be very precise. This means that great care must be
taken to express Python statements correctly. For example, in Python you can write: len of
quoted hello but you cannot write len of unquoted hello or hello's len. English is less structured.
You could say either the phrase "the length of a string" or the phrase "the string's length", and
they both have the same meaning.

If there is no error during the semantic analysis phase, the Python interpreter successfully
computes an object as the result of evaluating the expression. If the expression was evaluated
in a shell, then the Python interpreter usually displays its result object in the shell, in human
readable form.

In this video you have seen the three-step interpretation process: lexical analysis, syntax
analysis and semantic analysis. Python uses this process to evaluate an expression by
computing a result object. To understand how the interpreter computed these result objects, you
must learn more about Python Tokens, Syntax and Semantics. You will have a much better
understanding of these concepts after viewing the following videos.

82
02.03 Python - Lexical Analysis

This video describes how lexical analysis creates three different kinds of Python tokens:
identifier tokens, delimiter tokens, and literal tokens.

Recall that the Python interpreter uses a three step process: lexical analysis, syntax analysis
and semantic analysis. How does the Lexical analysis step combine characters into basic units
called tokens?

Previously, we evaluated the expression: len of hello. This expression contains four tokens: len,
left paren, 'hello', and right paren. How does the Python interpreter perform this lexical analysis?

A token is constructed from one or more characters, where each character can be a letter, a
digit, a symbol, a special character, or whitespace. Whitespace is a space, a tab or a page
break character, also called a formfeed. Recall that Python has five different kinds of tokens:
identifier tokens, delimiter tokens, literal tokens, operator tokens and keyword tokens. Each kind
of token is either described by a simplified lexical rule or defined by a lexical table.

Python translates characters into tokens from left to right. The first token is len, an identifier
token. An identifier is a name that we use in Python code to refer to an object in memory. len
refers to a function object that computes the length of a sequence such as a string. Python
recognizes identifier tokens using a simplified lexical rule: starts with a letter or underscore, and
is followed by zero or more letters, underscores, or digits. Identifiers are case sensitive, so
R2D2 with upper case letters is a different identifier than r2d2 with lower case letters.

The second token is left paren, a delimiter token. A delimiter is used to separate other tokens,
the same way that punctuation is used in natural language. We use a lexical table to define the
Python delimiter tokens. Each delimiter consist of one, two or three symbolic characters. Some
delimiters, like comma and star equal appear alone. Other delimiters appear in pairs such as left
and right paren, which appear in the len of hello expression.

The third token is 'hello'. This is a literal token. There are several categories of literal tokens and
quote hello quote is in the category literal string. The simplified rule for literal string is: starts
with a quote, followed by zero or more non-quote characters, and ends with a quote. Another
literal category is literal float, which represents a non-negative rational number. The simplest
rule for literal float is: one dot, one or more digits and no other characters.

A third literal category is literal integer, which represents non-negative whole numbers. The
simplified rule for literal integers is: one or more digits. For example, 27 and 0 and are literal
integers, while 4.5, 0.0 and 0.3 are literal floats. However, -5 and quote 2.3 quote are not literal
integers or literal floats, since -5 has a minus sign and quote 2.3 quote has quotes. In fact,
quote 2.3 quote is a valid literal *string*. You will find out later that -5 actually consists of two

83
tokens, the minus operator and the literal integer five. Python must evaluate both tokens to
create an int object whose value is negative five.

Why does Python create a single token twenty seven, instead of creating two tokens two and
seven? Both choices satisfy the lexical rule for literal integers. The lexical analyser uses this
longest token rule:

When creating a token, create the longest token possible.

Therefore, given a choice between creating 2 or 27, the lexical analyser creates the longer
token, 27. Given the character sequence 4.5, the Python interpreter uses the longest token rule
to create a single float token, four point five, instead of creating a literal integer four followed by
a literal float, point 5, or creating a literal float 4 point, followed by a literal integer 5.

Similarly, the characters sequence lastchance would be interpreted as a single identifier token
lastchance, rather than two identifier tokens last and chance.

In addition to a longest token rule, lexical analysis also has a whitespace rule:

For white space not at the start of a line:


● If whitespace is inside a literal string it is part of the literal string
● otherwise it ends the current token and no token is created for it

White space at the start of a line has a special meaning that we will discuss in a future lesson.

A literal string is a token that we use in Python code to refer to an object in memory that
represents a sequence of characters. Python uses quote characters to distinguish a literal string
token from an identifier. The token len is an identifier and the token 'len' is a literal string. In the
expression, len of hello, the identifier len was bound to a function object that computed the
length of the string hello as 5.

Using a valid token that is the wrong kind of token can cause a syntax or semantic error.
For example, what will happen if I use a literal string, 'len' instead of the identifier len in this
expression?

84
02.03@07:00

Fig. 02.03.01: Result of Interpreting ‘len’(‘hello’) in the Python Shell

This expression contains 4 valid tokens so there is no lexical error. Python reports a Type error
which is a kind of semantic error. The error message indicates that Python was expecting a
function object as part of a function call and encountered a string object instead.

Now let's return to the expression, len of hello. The fourth and final Python token in this
expression is also a delimiter token, right paren. Therefore this Python expression consists of
the identifier len, the delimiter left paren, the literal string, quote hello quote and the delimiter
right paren - four valid tokens.

In this video we used a lexical table to define delimiter tokens. We also used simple lexical rules
to describe identifier tokens and three categories of literal tokens, literal strings, literal integers
and literal floats.

85
02.04 Python - Syntax Analysis

This video introduces the Python programming language syntax of literal expressions, identifiers
and function call expressions.

A Python program is composed of statements. Recall from the Lexical Analysis lesson, that the
Python interpreter first uses a process called lexical analysis to decompose code into basic
units called tokens.

Syntax prescribes the format of each statement. So the format of a statement specifies the
order of the tokens in the statement. We use syntax diagrams to define which tokens are in a
statement and what order they are in.

There are many kinds of Python statements. An expression statement contains one or more
expressions on a line. In this lesson, we only consider expressions statements that consist of a
single expression. There are many different kinds of expressions. We will investigate three
kinds: literals, identifiers and function calls.

Previously, we evaluated the literal string expression: ‘hello’. This expression contains one
token: the literal string 'hello'. Does every Python token form a valid expression by itself? Let's
try the identifier len, which is a valid token.

02.04@01:30

Fig. 02.04.01: Result of Evaluating len in the Python Shell

86
len evaluates to an object whose human readable output is built-in function len.

Let's try a right paren which is a valid token.

02.04@01:39

Fig. 02.04.02: Result of Evaluating ( in the Python Shell

No, this single token is not a valid expression.

How does the Python interpreter recognize which single tokens are valid? More generally, how
does Python recognize which sequences of valid tokens form valid expressions?

In the lexical analysis lesson, you saw that the function call expression len of hello evaluated to
5. This expression contains four tokens: the identifier len, delimiter left paren, literal string 'hello',
and delimiter right paren. How does the Python interpreter recognize that the syntax of these
tokens also forms a valid expression? The Python interpreter uses rules to recognize valid
syntax. We use syntax diagrams to describe these rules. A syntax diagram contains states and
transitions.

87
(02.04@02:30)Here is a syntax diagram for an expression that includes literals, identifiers and
function calls.

02.04@02:39

Fig. 02.04.03: Syntax Diagram for an Expression

02.04@02:39

Fig. 02.04.04: Syntax Diagram for an Expression (Video Screenshot)

The start state is drawn as a circle. A terminal state is a state that represents a single token
from a set of tokens, such as one delimiter token from the set of all delimiter tokens. A terminal
state is drawn as a rounded rectangle and matches one token. Literal is a terminal state, which
represents one token from any literal category, including string, integer and float. Identifier is
also a terminal state that represents one identifier token.

88
A state that does not represent a single token from a set of tokens is called a non-terminal state.
It is drawn as a rectangle and must be expanded using its own syntax diagram. A function call
is a non-terminal state.

A state that has a double border is called an accepting state. Literal, identifier and function call
are accepting states, which I will explain momentarily. The arrows are called transitions and by
connecting states, the transitions define paths through the diagram.

Can I use the diagram to recognize that the token sequence, ‘hello’ is a valid expression?
This is a single token. How does this token match the syntax diagram? Begin at the start state.
The start state has three transitions. The first transition leads to the literal terminal state. The
second transition leads to the identifier terminal state. And, the third transition leads to the
non-terminal state, function call. Since you have seen that quote hello quote is a literal token, I
can the match this token to the terminal literal state.

To validate the syntax of an expression you must find a path from the start state to an
accepting state that matches all the tokens to terminal states in order. An accepting state is
any state in a syntax diagram that can be used to match the last token of the sequence of
tokens you are trying to match.

The token sequence consists of a single literal token. It matches the terminal literal accepting
state, denoted by a double border. Therefore, ‘hello’ is a valid expression.

To show that a token sequence is invalid you must show that no path exists that leads to an
accepting state. You have seen that the right paren is a single delimiter token, but it is not a
valid Python expression. Why not? From the start state you have a choice of three transitions.
The first two transitions lead to terminal literal and identifier states that do not match the
delimiter token. The third transition leads to a non-terminal state. Remember, every state is
either a terminal state that matches a single token or a non-terminal state that must be
expanded using its own syntax diagram instead of matching a single token.

Here is the simplified syntax diagram for function call that has either one argument expression
or no argument:

02.04@05:40

Fig. 02.04.05: Simplified Syntax Diagram for a Function Call

89
02.04@05:40

Fig. 02.04.06: Simplified Syntax Diagram for a Function Call (Video Screenshot)

Begin at its start state. There is only one transition and it leads to the terminal identifier state.
Since the delimiter token is not an identifier token there is no match. Since there are no more
paths to try, a single delimiter is not a valid Python expression.

I will now try to match len of hello to the syntax diagram. Recall that there are four tokens in the
token sequence for len of hello. I will begin at the start state. The start state has three
transitions, literal, identifier and function call. Since the first token, len, is an identifier, it does not
match the literal terminal state. So, I cannot use that path. The second transition leads to the
identifier terminal state. However, there are no transitions out of this state and I still have tokens
to match. If it was a non-terminal state I would expand it to match more tokens. However, it is a
terminal state so I can only match one token instead of expanding it. I will try another path. The
third transition leads to the function-call non-terminal state, so I must expand it. Following the
single transition from the start state of the function call diagram, I will match the token len to the
terminal identifier state. Since there is only one transition leaving the identifier state, I will try to
follow it. The next token, left paren, matches the left paren state. So far, so good.

The next token is the string literal, 'hello'. This token does not match the right paren state so do
not follow this transition. Instead, follow the transition to the expression non-terminal state.
Expand this state using the expression syntax diagram. The definition of expression is a
recursive definition since an expression can be a function call which itself contains an
expression.

90
There are three transitions from the start state for expression. One leads to the literal terminal
state, one leads to the identifier terminal state, and one leads to the function call non-terminal
state. This time, if I follow the transition to the literal state, it matches the third token, the literal
string 'hello'.

I still have tokens left and there is no transition leaving this state to match the remaining tokens.
What should I do now? Recall how I got here. I was trying to match the expression non-terminal
state in the function call syntax diagram. Since I am in an accepting state in the expression
diagram, I have successfully matched an expression so I can return to the path in the function
call diagram.

The next transition matches the right paren token with the right paren terminal state. Success!
Since I have matched all four tokens and the right paren is an accepting state, the original
expression, len of hello, is syntactically valid.

Remember, the Evaluation Examples video. Evaluating len ‘hello’, without parens, caused a
syntax error. How did the Python interpreter detect this syntax error? The interpreter begins at
the start state of an expression and tries to find any valid path. Since the first token, len, is an
identifier, it does not match the transition from start to literal. You have already seen that len
matches the identifier state but there are remaining tokens to match, with no transitions to
follow. Therefore the interpreter tries to match the function call state. The syntax diagram for
function call has a single transition from the start state to the identifier state, which matches the
len token. However, the only transition leaving the identifier state is to the left paren state, which
does not match the quote hello quote literal string token. Therefore there is *no* path in the
syntax diagram that matches the token sequence len followed by quoted hello, so this token
sequence is not a valid expression.

Let's try one more token sequence, len followed by a left paren. The interpreter begins at the
start state of expression and tries to find any valid path. As we have seen before, len followed
by paren does not match literal or identifier, but does match the first two states of function call.
There are no more tokens and the current state is not an accepting state so this path is invalid.
Since the interpreter has failed on all paths, the token sequence is invalid as an expression.

Notice that an error is not reported. In some cases the interpreter recognizes the current
progress in the syntax diagram and waits for more tokens. However, if no other tokens are
provided, it is a syntax error.

You have used syntax diagrams to recognize the format of valid Python literal, identifier and
function call expressions.

Our next step is to study Python semantics. However, before we do this, you must learn about
Python objects and references.

91
02.05 Python - Objects

This video introduces the programming language concepts related to the objects that represent
data in a Python program.

The Python interpreter creates objects to represents data in computer memory. Each object has
an identity, a type, and a value. An object’s identity is determined when the object is created.
Once it is created, it is unique and immutable, which means it cannot be changed.
Think of an object’s identity as its location in computer memory.

We can obtain an object's identity by applying the built-in I.D. function to it. For example, id of 27
gives us this identity (02.05@00:48).

02.05@00:48

Fig. 02.05.01: id of 27 in Python Shell

02.05@00:48

Fig. 02.05.02: id of 27 in Python Shell (Video Screenshot)

92
This identity may be different for your Python interpreter. We can view the documentation for the
built-in id function in the Python standard library. Return the “identity” of an object. This is an
integer which is guaranteed to be unique and constant for this object during its lifetime.

An object’s type determines its format or shape in memory, and how it is interpreted during
computations. Each type defines a memory format. Each different memory format may require a
different amount of computer memory. Once we create an object of a particular type, its type is
immutable. We cannot change its type since changing its shape would disrupt adjacent objects
in memory.

For simplicity we will use the same representation for objects of all types, even though the types'
formats require different sizes and shapes in memory.

int, which is short for integer, is a Python type that represents a whole number. Ints support
operations such as addition and multiplication.

String, called str in Python, is a type that represents a sequence of characters. Python provides
operations such as computing the length of a string or concatenating two strings together. We
can obtain an object's type by applying the built-in type function to it.

Let's look at the documentation in the Python standard library.

02.05@01:58

Fig. 02.05.03: id Documentation in the Python Standard Library

93
02.05@01:58

Fig. 02.05.04: id Documentation in the Python Standard Library (Video Screenshot)

With one argument, return the type of an object. For example, type of 65 displays class int, and
type of string A displays class str. The terms type and class are used interchangeably in
Python, so this function is telling us that that the type of 65 is int and the type of string A is str.

94
02.05@02:28

Fig. 02.05.05: type of 65 and ‘A’ in the Python Shell

The type defines the memory format of all objects that share that type. Most modern computers
store all data using binary digits, or bits for short. Each bit is either a 0 or a 1. The value of an
object is the bit sequence that is used to represent it. For example the integer 65 has this binary
representation. Since the values of the integers 65 and 27 are different, they must have different
binary representations for these values.
02.05@03:17

Fig. 02.05.06: Binary Values of 27 and 65

95
02.05@03:17

Fig. 02.05.07: Binary Values of 27 and 65 (Video Screenshot)

Since different values have different binary sequences, why do we need types? The type of an
object defines how the binary digits are interpreted. For example, in many programming
languages both the integer 65 and the string 'A' use the same binary sequence for their value.

The type of an object determines how these two objects will be interpreted, one as a string and
one as an integer. For example, 65 + 65 evaluates to 130. String A + string A evaluates to string
AA which is string concatenation in Python.

Even though we started with objects that had the same binary sequence for their values,
because the types were different, the resulting binary values were different. Every object has an
immutable identity and type, as well as a value which is represented by a sequence of bits.

In our diagrams we use 16 bit sequences to represent both integers and strings. In fact Python
does not use fixed length binary sequences to represent either of these types. Many other
programming languages represent integers with a fixed number of bits such as 32 or 64, which
limits the size of an integer to 2 billion with 32 bits or 9 billion billion with 64 bits. This means that
if we want to represent a larger number in other languages, we would need a different type than
integer, such as the type BigInteger in the Java programming language.

The number of atoms in the universe is between 10 to the power of 78 and 10 to the power of
82, which is 10 billion billion billion, billion billion billion, billion billion billion. The number 10 to
the 82 can be represented in Python as a single object of type int.

96
The particular bit sequence used for memory representation of object values varies between
different Python implementations. Therefore, Python does not have a built-in value function in
the standard library to display the bit representation of an object.

Fortunately, we don't need to know the memory representation of Python types to write Python
programs, since the Python interpreter translates between memory representations and human
displayable formats.

Therefore, in our diagrams, we will denote the values of objects in a more human readable
format, instead of using bit sequences. We replace the binary digits of integers and strings by
decimal digits for the int type or an unquoted sequence of characters for the str type.

We have seen that every object in memory has an identity, type, and value. We have seen two
types, int and str. We will encounter many other types.

Before discussing semantics, we must discover how Python programs reference these objects.

97
02.06 Python - Semantics of Literals and Identifiers

This video introduces the Python programming language semantics of literals and identifiers.

Recall the interpretation process that Python uses to evaluate a statement: lexical analysis
creates tokens, syntax analysis validates the token order, and semantic analysis defines the
meaning of a statement by evaluating it.

We will use simplified semantic rules to define the meaning of Python statements. Recall that an
expression statement consists of a single expression that can be either a: literal, identifier or a
function call. We have already defined literal and identifier tokens. We have also defined the
syntax of an expression that consists or a single literal token or a single identifier token. To
define the semantics of literals and identifiers we need to first examine why they are used in
Python expressions.

Human readable representations of objects are used in expressions so that people can
understand these objects. The interpreter must translate these human readable object
representations into objects in the computer memory, so computations can be performed on
them. The simplest way to represent an object in an expression is to use a literal. A literal
represents an object by describing it. The literal contains all of the information necessary to
create an object.

The semantics of a literal expression are simple:

create a new object for the literal

For example, when the interpreter evaluates the literal token 300, it creates a new integer
object. A literal in an expression is analogous to a coupon or voucher in the real world that can
be exchanged for an item. The coupon is just a representation of the item, it is not actually the
item. Once the coupon is exchanged for the item the coupon is no longer necessary. Similarly a
literal is not an object, it just represents that object in the expression. Once the literal is used to
create an object in memory, the literal is no longer necessary, only the object is needed.

If the interpreter encounters the literal token 300 in another expression, it creates another
integer object. Recall that the id function returns a different identity number for each different
object. The id function can be used to determine whether another use of the same literal
actually creates another object or re-uses the object that was previously created. For example,
using the literal 300 twice creates two different objects since their ids are different.

98
02.06@03:19

Fig. 02.06.01: Two Different 300 Objects and Their id’s

Each Python interpreter has its own strategy for re-using objects created for literals and the
strategy depends on the literal. For example, this interpreter re-uses the integer objects for all
literal integers between -5 and 256, but creates new objects each time any other literal integer is
used.

To simplify the semantics of literals, we ignore whether or not a particular interpreter re-uses the
same object for multiple appearances of the same literal. We define the semantics of a literal as
creating a new object each time the literal is evaluated. Re-using objects for literals only affects
how fast Python code is evaluated, it does not usually affect the code results.

In addition to literals, Python uses identifiers, also called names to represent objects in an
expression. An identifier represents an object in an expression by referencing that object. We
say that an identifier is bound to an object and use an arrow to denote the binding. For example
the identifier len represents a function object.

An identifier in an expression is analogous to the name or role of a person. People refer to us by


name or role, but the name or role is just a label, it is not actually a person. Just as people can
use a name or role to refer to the same person more than once, an identifier can refer to the
same object more than once.

An identifier is a persistent representation of an object, while all of the expressions in a shell or


program are evaluated. A literal is only a temporary representation, until the object it represents

99
is created. Python uses a namespace to allow identifiers to persist during evaluation. A
namespace associates each name that appears in a python expression with the object that it is
bound to. Thus, the len identifier is associated or bound to a function object.

A namespace is like a dictionary, except instead of looking up a word and finding a definition,
the interpreter looks up a name and finds the object it is bound to. This step is called
dereferencing. Dereferencing is the act of translating an identifier into the memory object it is
bound to, during Python code evaluation.

This process is like looking for a restaurant name online. You enter the name of the restaurant in
your browser or app and you are given its location, which you use to find the restaurant in the
real world. When we open a Python shell or run a Python program, the interpreter creates a
namespace and pre-binds many identifiers before evaluating our code. For example, the
interpreter creates a function object that computes the length of a sequence. It then adds the
name, len, to the namespace and binds this name to the function object. The interpreter
pre-binds the names of all the built-in functions. For example, it binds the id and type function
names as well.

If the names of the built-in functions weren't prebound we would have no way to refer to them,
so we could not use them. The interpreter also pre-binds the name of each built-in type to a type
object and pre-binds many other identifiers as well.

You can use the built-in function, dir to see all of the built-in identifiers.

02.06@07:38

Fig. 02.06.02: Built-In Functions in Python Viewed with the dir Function

100
Notice the built-in function names len and id and the built-in type names, str, int and float. Also
notice the names of some familiar errors such as SyntaxError and NameError. In our diagrams
we won't show all of the pre-bound identifiers. We will only show the pre-bound names that we
will use for a particular Python shell example, or program.

Here are the semantics of an identifier expression:

if the identifier is in the namespace, dereference it to get the result object


otherwise report an error

For example if we evaluate an expression containing the identifier len the interpreter finds len in
the namespace and dereferences it to obtain the function object. Let's evaluate an identifier,
hello, that is not in the namespace.

(02.06@08:22)This expression contains one token: the identifier hello. The interpreter applies
the semantic rule for identifier expressions. It checks if the identifier hello is in the namespace.
Since hello is not pre-bound to a built-in function, type or any other kind of object, it is not in the
namespace. The interpreter reports a semantic error. Since there is no result object, only the
error is displayed.
02.06@08:50

Fig. 02.06.03: Unbound Identifier hello Results in a Semantic Error

101
02.06@08:50

Fig. 02.06.03: Unbound Identifier hello Results in a Semantic Error (Video Screenshot)

We will discuss how to bind identifiers that are not pre-bound in later lessons.

A name tag can be worn by one person and then worn by a different person later. For example,
while driving a bus, a person may wear a "driver" name tag to represent their role. When a
different person replaces the first driver, the driver name tag can be detached from the first
person and used by the second person. An identifier is like a nametag since we will discover in
a future lesson that an identifier can be bound to different objects at different times. However, an
identifier in a namespace cannot be bound to two different objects at the same time. As an
analogy, there cannot be two people with the same driver role on one bus. There is only one
driver nametag on the bus and it can only be attached to one person at a time.

This video introduced the Python programming language semantics for literals and identifiers.
Python places identifiers in a namespace, and re-uses them when multiple expressions are
evaluated.

102
02.07 Python - Semantics of Function Calls

This video introduces the Python programming language semantics of function call expressions.

Recall that an expression statement consists of a single expression that can be either a: literal,
identifier or a function call. We introduced the semantics of literal expressions and identifier
expressions in the previous lesson.

Let’s define the semantics of a function call. Recall that a function is applied to an argument
object to obtain a result object.
1. Evaluate the identifier to obtain a function object
2. If there is an expression, evaluate it to obtain an argument object
3. If there is an argument object, pass it into the function
4. Evaluate the function code to obtain a result object

In the Syntax lesson, you discovered that this expression has valid syntax as a function call.

02.07@01:16

Fig. 02.07.01: len(‘hello’) an Expression with Valid Syntax in Python

It starts with the identifier len. It is followed by a left paren and an expression, which is the literal
string hello. It ends with a right paren. Before the Python interpreter evaluates this expression,
the identifier len is prebound to the built-in len function object. The Python interpreter applies the
semantic rule for function call expressions.

103
(02.07@01:29)It applies step 1 to evaluate the identifier len. The semantic rule for an identifier
expression, looks for len in the namespace. Since len is pre-bound to a built-in function, it finds
it and dereferences len to obtain the length function object.

02.07@01:40

Fig 02.07.02: Step 1 of Semantic Analysis the Built-In Function len is Dereferenced

In step 2, the interpreter evaluates the expression, ‘hello’. The semantic rule for literals
evaluates this expression by creating a new string object.

104
02.07@01:55

Fig. 02.07.03: Step 2 of Semantic Analysis ‘hello’ is Evaluated a String Object is Produced

In step 3, we pass this argument object into the length function.

02.07@02:01

Fig. 02.07.04: Step 3 of Semantic Analysis the Argument Object is Passed into the len Function

105
In step 4, the interpreter evaluates the code in the length function, which we cannot see. This
code is evaluated using the same interpreter process that you are currently learning. This
function returns the integer 5 object even if you do not yet understand how that function code is
evaluated. This 5 object is displayed in the shell.

02.07@02:22

Fig. 02.07.05: Step 4 of Semantic Analysis the Built-In Code is Evaluated and a Result Object Displayed

The expression len of goodbye consists of a single function call to the same function, but with a
different argument, the literal string ‘goodbye’. The function call uses the same semantics to
computes the integer 7 object, which is displayed.

Let's evaluate len of 27.

106
02.07@02:53

Fig. 02.07.06: Semantic Analysis of len(27) Produces a Type Error

When the interpreter tries to interpret this expression, it reports a type error. How and why does
it fail? The interpreter successfully completes steps 1 through 3, but an error occurs in step 4
when it tries to interpret the function code for len using the argument object, integer 27. Len
cannot compute the number of elements in an integer since integers don't actually have any
elements. The Python shell reports this error.

This video introduced the Python programming language semantics of function call expressions.
We evaluated several expressions in a Python shell and the shell displayed the result objects in
human readable form.

107
02.08 Python - Program Interpretation

This video introduces Python programs, a way to evaluate multiple Python statements
sequentially, instead of evaluating one statement at a time, in a shell.

An application usually computes many objects in an order that is appropriate for that application.
For example, a game often creates and displays a welcome at the start. It then creates and
displays moving game objects during the game and other objects at game end, such as a score
or leader board. How can you create and display several objects in a particular order? You can
use multiple statements that the Python interpreter evaluates in sequential order. These multiple
statements are called a program.

The Python interpreter uses lexical analysis to create tokens for the entire program. It then
checks the syntax of each statement in order from the first statement to the last statement.
Finally, it uses semantic analysis to evaluate each statement in order. Actually, there are some
statements that change this default sequential order of evaluation, but they will be introduced in
future lessons. Python allows us to invoke the interpreter on a program that is stored in a file.

(02.08@01:20)Here is a program that computes the length of hello, the length of goodbye, and
the id of hello, in that order. This program has 3 expression statements that were evaluated
individually in a Python shell, during a previous lesson. If I run this program, its output will be
displayed in the python shell.

02.08@01:24

Fig. 02.08.01: A Python Program with No Result Objects Displayed

108
Why weren't the expression result objects displayed? When these expressions were evaluated
in a Python shell, the results were displayed.

The interpreter uses the same semantic rules to evaluate program statements and statements
that are entered into a Python shell. However, in a Python shell, after an expression statement
is evaluated, the result object is displayed immediately in the shell. When a program is
interpreted, the expression statement results are not automatically displayed. The shell displays
each evaluated expression so you can view the result. However, in most programs you do not
want to display the result of every computation, so result objects are not automatically
displayed. To explicitly display an object when a program is evaluated, you need to use the
built-in function, print, that displays its argument object and then moves the cursor to the next
line.

02.08@02:35

Fig. 02.08.02: print Documentation in the Python Standard Library

109
02.08@02:35

Fig. 02.08.03: print Documentation in the Python Standard Library (Video Screencapture)

Unfortunately, the documentation for print is hard to understand. For now, it is enough to know
that the print function displays a human readable form of its argument object.

To selectively display two of the three objects in my program, I will use the first and third objects
as arguments in print function calls. When I run this program, each expression statement is
evaluated in order and the print function calls, explicitly display the first and third result objects
in human readable form.

The print function is a function so it must return a result object. The print function returns a
unique object whose type is NoneType. This object is returned by any function that does not
return a useful object.

When I evaluate "type of print of hello" the Python interpreter first displays the output from the
print function call, 'hello'. Since I evaluated this expression in a shell, the interpreter then
displays the type of the object returned by the print function call, which is the NoneType object.

The print function displays information in the program. How does a program get information
from a user? You can use the built-in function input to translate user key presses into an object
of type str. The Python standard library has this (02.08@03:44) entry for input:

110
02.08@03:48

Fig. 02.08.04: input Documentation in the Python Standard Library

02.08@03:48

Fig. 02.08.05: input Documentation in the Python Standard Library (Video Screencapture)

There is some new notation in this documentation. The square brackets around the prompt
argument mean that this argument is optional. It can be included or left out. If the prompt
argument is present, it is written to the shell without a trailing newline. If the argument is left out,
nothing is displayed. The function then reads a line from input, converts it to a string, strips the
trailing newline, and returns that string object.

What is this prompt argument for? If the program needs the user to enter some information, it
should prompt the user and then wait for a response. After displaying the prompt, the input
function leaves the cursor on the same line so the user input is to the right of the prompt. The
user signals that the input is done by pressing the enter key. Then the function translates the
user keypresses into a single string object that does not include a newline character for the
enter key.

111
I'll run a program to show how the input function works. Notice that the color appears on the
prompt line as it is typed. The print function call then displays this color string on the next line as
well.

02.08@04:48

Fig. 02.08.06: Example Program Feature the input and print Functionality

The Python interpreter checks the syntax of the entire program before it evaluates any
statements. When I run this program the hello string argument in the first line is not displayed by
the print function call, even though the first line has valid syntax and semantics.

112
02.08@04:59

Fig. 02.08.07: print Example Program with a Syntax Error

This is because a syntax error occurred on the second line. If your program contains any syntax
errors, no statements are evaluated.

In this video I applied the three-step Python interpretation process to evaluate some Python
programs.

113
02.09 Program Hacking Version 1

Let’s check the status of our game using the Game Creation Diagram.

02.09@00:16

Fig. 02.09.01: Game Creation Diagram form Hacking Version 1

You have completed the design for Version 1, consisting of: a version description, a functional
test plan, and an algorithm. The next subtask in Create Version is Create Program. Now that
you have a robust design, it is time to implement that solution. Writing code is refinement,
because it is more precise than your algorithm.

Let's use our cake algorithm as an example. I can't use the cake algorithm by itself to bake a
cake. I need more details for each step. For example, to follow the algorithm step “Make batter”,
I need to know the batter ingredients, the amount of each ingredient, and how to combine them.
To follow the “Bake Cake” step, I need a baking temperature and time. We can create a recipe
from the algorithm, by adding details. This recipe is an implementation of the algorithm, just like
code is an implementation of a software algorithm. We need a process to translate algorithms
into code.

The three subtasks of Create Program are: Write code, Test code, and Debug code. Writing
code is the process of translating your algorithm into a programming language. Each step of the
algorithm will become one or more sequential statements in some programming language. In
this course, you’re using Python. Once you have some code written, you need to test it. You test

114
code by running it and determining what errors have occurred. You will make many errors when
you write a program. Don’t be discouraged! Making errors is a very important part of learning to
program. Debugging is the process of discovering exactly what part of the code caused the
error and deciding how to fix it. Learning to debug is just as important as learning to write code.

Testing and debugging are not the same process. Testing is running your code to determine
which errors exist in the program. Debugging is discovering which program statements caused
the errors and analyzing how to fix those statements. Once you know how to fix an error, you
must edit your code. This may involve changing existing statements or adding new statements.
Naturally, when you write new statements, you will need to test and possibly debug them as
well! So you will need to repeat the write-test-debug process multiple times. We add a circular
arrow icon to the game creation process diagram to indicate this.

02.09@02:47

Fig. 02.09.02: Game Creation Diagram Featuring Write, Test, Debug Code Steps

In this course, we write our code using an integrated development environment, or IDE for short.
We will use a free version of the Wing IDE. An IDE is used to write, run and debug code. Writing
code using an IDE is analogous to using a text editor, such as Microsoft Word or Google Docs,
to write an essay.

Before we start coding, let’s look at the panels of the Wing IDE (02.09@03:19). To start a new
program, I can either select File > new or press the blue page button on the taskbar.
A new program called ‘untitled-1’ dot py appears in the panel directly below the taskbar. The
panel now has line numbers on the left hand side, which will help you determine the locations of

115
errors in statements.

02.09@03:29

Fig. 02.09.03: The Wing IDE

Below the program is a panel with tabs: Search, Stack data and Python shell. The Search tab is
for finding and replacing words in your program. We’ll discuss the Stack Data tab when we use
some related debugging features in a later version of Hacking. The Python Shell tab should be
familiar to you from the programming language lessons. You may see other tab configurations in
your IDE. We will not be using any other tabs in this course.

Alright, let’s start coding! One of the challenges of programming is making sure that your code is
easily understood and well-documented, not only for other people to use, but for yourself as
well. We often think that we’ll remember exactly how our code works after weeks or months
have passed. Unfortunately, returning to a poorly-documented project after an extended period
of time, leads to frustration and delays. Good documentation includes descriptive identifier
names and comments that indicate the intent of the code. You make a comment line using the
number sign key at the start of the line. Comments are intended for human use and are ignored
by the Python interpreter. I’ll start by adding comments that include the title of this game version,
and a short summary of the game.

116
02.09@05:00

Fig. 02.09.04: Program Comment for Hacking Version 1

Alright, now let’s translate our algorithm into code. The first step of our algorithm is Display
Header. Let's add a comment called display header. In fact, I’ll add each algorithm step as a
block comment that describes the block of program statements that you will write to translate
that step. I start with the main panel and include the four steps: display header, display
password, prompt for guess, and end game.I then add the comments, “Display failure Outcome”
and “Prompt for End” under the end game comment since they are parts of “End Game”.
However, I put extra spaces in the comments to indicate they are sub-steps of "End Game".
This makes it pretty easy to see all the algorithm steps we must translate! As we’ve said before,
each algorithm step will be translated to a block of one or more Python statements.

117
02.09@05:46

Fig. 02.09.05: Algorithm Steps Translate into Block Comments

Now let's translate the first algorithm step, display header, into Python code. We saw in the
programming language videos, that python uses the print function to display a string. What
should the argument strings be? From the version description, you must output "debug mode",
"one attempt left" and a blank line. You can find the exact strings by re-running the program. I
type the print statements beneath the display header comment. Then I run this program using
the green arrow button in the taskbar. The header is displayed in the shell!

118
02.09@06:41

Fig. 02.09.06: Code to Display the Header in the Python Shell

I ran the program after translating only the first algorithm step to make errors easier to find. If
testing finds an error, you need to use debugging to locate the statements that caused the error.
If you test after only making a few additions or changes, it is easier to identify the statement
where the error occurred. For example, I will remove the right paren from the last print function
call of draw header, but I won't test the program. Instead I will translate more algorithm steps. I’ll
leave the draw password list translations for an activity, and translate the prompt for guess
instead.

A prompt displays some text and captures user keypresses as a string. We can translate our
prompt step into a call to the Python input function. The same algorithm step will be translated
to other function calls in other languages, such as readLine in Java and gets in C. I’ll write the
input function call with an argument string that starts with “Enter a password" and ends with a
greater than sign.

I’ll run the program again using the green run button. The Python shell reports a syntax error on
the input function call line, even though we know that the error is on the print function call line.

119
02.09@08:06

Fig. 02.09.07: Syntax Error Reported on the Wrong Line

The reported line number is not always where the error actually occurred. We run our program
after translating each algorithm step to make debugging simpler.

In the next activity, you will fix this error and translate the rest of the algorithm into code.
You must apply the test plan to your completed code to ensure that your code functions
correctly. Make sure that you perform all of the tester actions from “start the program” through
“enter an incorrect answer and press the enter key to exit”. Ensure that the answers to all the
questions are “yes”. If the answer to any question is “no”, then you must apply more iterations of
writing, testing and debugging until your program is correct.

120
02.10 The Reflection Process

At this point, it might seem like you are finished the application creation process. After all, you
have a working program! You first designed a solution and then you implemented it. However,
you're not finished the application creation process. Let’s take another look at the Game
Creation diagram. There is one more important subtask in the Create Version task: Reflect.

02.10@00:32

Fig. 02.10.01: Reflect Subtask in the Game Creation Diagram

Reflection is an important step in the game creation process. Learning a new skill usually
involves a narrow focus on a specific situation. It is necessary to generalize this skill so it can be
applied to new situations. Solution Generalization replaces a solution to a specific problem by a
solution that solves a group of related problems, including that specific problem. It is one form of
a new problem solving technique called Abstraction.

For example, I can bake my first cake by creating a chocolate cake recipe from the cake
algorithm as discussed in the programming and coding lesson. After baking a single cake, I
won't necessarily be able to make a different kind of cake. I want to use the knowledge that I
gained when I created the chocolate cake recipe to create a recipe for any kind of cake, such as
a cheesecake or coffee cake. This is an example of Solution Generalization, where a general
cake recipe would replace separate recipes for different kinds of cake. The algorithm steps such
as making a batter and baking the cake, remain the same. However, I need to identify what
details correspond to parameters that will change when I switch recipes and what details remain

121
the same. For example, turning on the oven will not change, but the oven temperature will
change.

You must learn to generalize the code statements you write by changing the functions, literals
and other objects you use to solve different problems. For example, you printed specific
password strings during your coding activity. However, more generally, the print function can be
used to print any string. Even more generally, you can perform a different action by using a
different function call, such as input. Reflection takes a skill you learned and let's you apply it
outside the narrow context you learned it in.

122
02.10 Review Code for Hacking Version 1

Reflection has three subtasks: Review Code, Reflection Activity, and Identify Solution Issues.
The first subtask of Reflect is "Review Code".

02.10@01:31

Fig. 02.10.02: Game Creation Diagram Review Code Subtask

To gain a deeper understanding of the program you wrote, you should inspect your program
statements one a time in the order that they are run. Your goal is to understand how the
statements work together to implement the game.

We can use the debugger to trace the way that the Python interpreter runs a program. To start
tracing I press the "start debugging” button, which is also called the "step into button”. This
button has an "arrow into a box" icon. The debugger highlights the first statement of the
program to indicate which statement will be evaluated first. It is an expression statement
containing a print function call. To evaluate this statement, I will press the "step over" button
which has an "arrow over a box". As expected, when this function call is evaluated, it displays its
argument string in the Python shell. Each time I press this button, the next Python statement is
evaluated in sequence. I can press the red stop button any time to stop debugging and return to
the program.

Let's see what happens when the interpreter evaluates an input function call. (02.10@01:31)

123
02.10@01:31

Fig. 02.10.03: Python Shell Waiting for Input During a Trace

Notice that the next statement is not highlighted and all the buttons are dimmed except the stop
debugging button. This is because the input function has not finished being evaluated. The input
function is waiting for the user to provide an input string. I will click in the Python shell and enter
a password guess. Since the interpreter has finished evaluating the input function call, it now
highlights the statement that follows the input function call. I can finish the statement by
statement tracing of the program by continuing to press the step over button one statement at a
time.

Tracing shows the sequential statement by statement evaluation that the Python interpreter
does by default. In future lessons, we will introduce statements that alter this default sequential
evaluation and we will using tracing to follow the non-sequential evaluation order.

To ensure that you understand how to trace a program, you should try a complete trace of your
program yourself. If while you are tracing, you accidentally press the “step into” button instead of
the “step over” button, the debugger may highlight a line that is not in your program. For
example, this will happen if you press the "step into" button on an input function call line.

124
02.10@02:54

Fig. 02.10.04: Step Into an Input Function Call Line

In this case, you will see code that is part of the implementation of this library function. To get
back to your program code, press the "step out" button which has an "arrow out of the box" icon.

Now that you understand what your code does, let's discuss the quality of your code. Code
quality is enforced using Software Quality Tests. These tests are applied in conjunction with the
functional tests you have already created and used. Functional tests demonstrate that the code
works as intended. The Software Quality tests ensure that you’ve made good coding choices
and used best programming practices. You will not create your own Software Quality tests.
Instead, we will provide specific Software Quality tests for each game version.

For this version of Hacking, you will only use one Software Quality test, to check your
comments. You must test two kinds of comments: Program Comments and Block comments. A
Program Comment is a summary of what your program does. It must be included at the start of
your program. A Block Comment appears on one line. A Block Comment must appear above

125
each group of statements that comprise a logical task. It must provide a short explanation of the
code that follows it.

Here (02.10@04:13) is our solution for Hacking version 1.

02.10@04:13

Fig. 02.10.05: Solution Code for Hacking Version 1

The game title is at the top of the program and is followed by a short summary. We have
satisfied the two software quality tests for Program Comments.

The rest of the comments are Block Comments. They were added as algorithm steps during the
programming demonstration. As written, they satisfy the software quality test for Block
comments. Each is at the start of a block of statements that form a logical task.

We will add more software quality tests as we introduce new coding concepts to the course.
Use these evolving software quality tests as a series of style guides to write your code.

The second subtask of Reflect is a Reflection Activity.

126
02.10@05:03

Fig. 02.10.06: Reflection Activity Subtask of Game Creation Diagram

Each reflection activity will enhance your understanding of tokens, syntax, semantics, object
types and how to generalize the code you have written to solve new problems. Do the
Reflection Activity for Hacking Version 1 now.

127
02.10 Solution Issues

The third and final subtask of Reflect is Identify Solution Issues, where we discover parts of our
solution that can be improved.
02.10@00:13

Fig. 02.01.07: Identify Solution Issues Subtask of Game Creation Diagram

For example, one solution issue with Hacking version 1 is that we used multiple print function
calls for each of the display sections: header, passwords, and outcome. This is the most
obvious way to display multiple lines of text. Another way of printing multiple lines of text is to
combine all of the text lines we want to print in one single string and add newline characters
between each line. A newline character is a special character that can be included inside a
literal string using a backslash followed by the letter n. To display a newline character, the print
function starts a new line.

128
02.10@01:01

Fig. 02.10.08: Hacking Version 1 with a Single Print Statement

Let's run this program. This program has the same behaviour as the program with multiple print
statements.

Both of these solutions are valid, but each has advantages and disadvantages. The multiple
print statement solution is a much longer program, so it requires more typing. It repeats the
identifier print, many times. The single print statement solution is shorter. However, it is hard to
read. In addition, in future versions, we will need to manipulate individual passwords from this
string. We will draw individual passwords graphically, check the correct password against the
player guess and embed each password in symbolic characters. If we use a single string, it will
be difficult to manipulate individual passwords. When multiple solutions are possible, you should
compare the advantages and disadvantages of each.

In future versions, we will identify solution issues when we introduce the game version that will
address them.

You’ve now completed two of the Reflection subtasks: Reflection Activity, and Review Code and
you have been introduced to the Identify Solution Issues subtask. This subtask will be
completed at the beginning of the next module, since it will motivate Version 2 of Hacking.

Congratulations! You have finished your first version of Hacking.

129
Module 03: Hacking Version 2

03.01 Solution Issues in Hacking Version 1

Every time you finish a game version, you need to decide what to add or improve upon for your
next version. This process is called Identify Solution Issues.

03.01@00:21

Fig. 03.01.01: Identify Solution Issues Subtask in the Game Creation Diagram

Solution issues are disparities between the solution you are seeking and the design and code
you have written. Identifying these solution issues is key to improving your code and adding or
modifying features in a measured and systematic way.

Hacking version 1 is not very exciting and only has a only a few similarities to the complete
game you need to produce. It displays the heading and the passwords in a black and white
textual format. Although it allows you to input a guess, it only displays unadorned passwords in
the Python shell instead of using its own window. As well, you only have one chance to guess a
password, and no matter what answer you pick, the game reports that you’ve failed.

We can recognize at least five different solution issues with Hacking Version 1.

First, code quality is an important facet of identifying solution Issues. In the previous lesson, we
discussed some of the disadvantages of using multiple calls to the print function.

130
03.01@01:20

Fig. 03.01.02: Multiple Calls to print in Hacking Version 1

03.01@01:20

Fig. 03.01.03: Multiple Calls to print in Hacking Version 1 (Video Screenshot)

We need to remove these duplicated statements, since repetition makes the program longer
than necessary. We will solve this problem in Hacking version 4, by introducing a language
mechanism called a repetition control structure.

Second, when you input the correct password, the game displays that you have failed.
Boooring!

131
03.01@01:44

Fig. 03.01.04: Correct Password Results in Failure Hacking Version 1

To solve this issue you need a mechanism to compare passwords to guesses. You then need to
choose whether success or failure needs to be reported to the player. You will learn how to do
this in Hacking Version 3.

Third, you are only allowed to make one guess before the game ends. In the final version of
Hacking, you must be allowed to make at most four guesses.

03.01@02:04

132
Fig. 03.01.05: Multiple Guesses in Hacking Version 5

03.01@02:04

Fig. 03.01.06: Multiple Guesses in Hacking Version 5 (Video Screenshot)

133
To do this, you must be able to consistently repeat steps and statement in your algorithms and
code. You will learn how to do this in Hacking Version 5.

Fourth, your passwords do not contain any of the symbolic character decorations of the final
version.
03.01@02:20

Fig. 03.01.07: Symbolic Characters in Hacking Version 7

03.01@02:20

134
Fig. 03.01.08: Symbolic Characters in Hacking Version 7 (Video Screenshot)

To make this improvement, you must be able to manipulate the password strings. You must add
symbolic characters in a random way. You will learn how to do this in Hacking Version 7.

Finally, your game is text based, instead of graphics based. You must learn to create and open
a window and draw graphical objects in the window.

For the second version of Hacking, the only Solution Improvement you will make is replacing
text with graphics, and displaying the player's guess. The other improvements require you to
learn and practice several new design components and Python statements.

To use a graphical window, you must master some new library functions. Games that use a
graphical window can’t use the print function to display text in that window. Print only displays in
a terminal window or Python shell. It doesn’t allow you to specify the location, the color, or the
font of the text you want to display.

For Hacking, you need to display green text in the top left, top right, bottom right and the center
of the screen. To do this, you must use a graphics library to display text. The functions you have
used so far are built-in functions included in the Python Standard Library. Unfortunately, the
Standard Library doesn’t support graphics. You can’t use it to open up a window, or draw circles,
or print different-coloured text. So, we will be use an external library. An external, or third party,
library is a compilation of code provided by the Python community, it contains functions and
other code that augment the Standard Library.

135
Using libraries is an example of Using Templates.

03.01@04:14

Fig. 03.01.09: Using Templates in the Problem Solving Diagram

Using Templates is an abstraction technique that uses previously created solutions and adapts
them to solve new problems. Recall tracing the code of Hacking Version 1. If you “Stepped into”
the input function, you would see the underlying code that implemented it. This code for the
input function was written by someone else and it can be used by others without rewriting it.

Consider how difficult it would be to write the games in this course if you had to implement all
the built-in functions of Python before implementing Hacking. it would take so long that you
would never have time to write the game code that uses these functions! Don't spend your time
"reinventing the wheel". "Using Templates" builds on the work of others to create solutions more
quickly.

For this course, you’ll use a library we created, called U A game. It is a simple library that lets
you create a window and draw strings in it. It is based on a popular Python graphics library,
called pygame. We use this simpler graphics library to implement all versions of Hacking. You
will use pygame itself in the second game of the course: Poke the Dots.

Let's add graphics to Hacking!

136
03.02 Observe and Play Hacking Version 2

In this video, you will observe gameplay for the second version of Hacking. While observing a
game you are building, you should ask yourself several questions. What does the game look
like? When do things appear on screen? What actions does the player take?

(03.02@00:27)The game starts. Immediately, you can see that Hacking version 2 looks quite
different from Hacking version 1.

03.02@00:43

Fig. 03.02.01: Hacking Version 2 Start Screen

137
03.02@00:43

Fig. 03.02.02: Hacking Version 2 Start Screen (Video Screenshot)

A new graphical window opens, with title Hacking, and a black background. The text is green,
but the content of the text is the same as Hacking version 1.

I’ll use the same actions I performed while playing Hacking version 1. When I am prompted for a
password, I am going to enter HUNTING, the correct password.

138
03.02@00:54

Fig. 03.02.03: Hacking Version 2 Failure Message

03.02@00:54

Fig. 03.02.04: Hacking Version 2 Failure Message (Video Screenshot)

139
Unfortunately, like the first version of Hacking, a failure message appears. Don't worry, we'll fix
this in the next version. At least the screen clears and the guess is displayed, just like in the
final version of Hacking. The failure message is correctly displayed in the middle of the screen.
In fact, Hacking version 2 has almost the same functionality as Hacking version 1, but it has the
same graphics and style as the final version.

In the next activity you will play Hacking version 2 to ensure you fully understand it, before you
describe it.

140
03.03 Describe Hacking Version 2

Now that you have observed and played Hacking Version 2, you are ready to describe it. This
process of observe-play-describe should become automatic to you as you progress through this
course. Recall that this is a form of experiential decomposition.

03.03@00:26

Fig. 03.03.01: Experiential Decomposition in the Problem Solving Diagram

It enforces your understanding of a topic, which is why these three subtasks form the
Understand Version task.

141
03.03@00:36

Fig. 03.03.02: Understand Version Subtasks in the Game Creation Diagram

Just as you did in version 1, you will be using the description builder to create a description for
Hacking version 2. However, because the second iteration of your game builds from the first,
instead of starting from nothing, you are going to modify your version 1 description to create
your version 2 description.

With this in mind, the first task in creating the description for Hacking version 2 is to get rid of
anything from the version 1 description that is no longer relevant. (03.03@01:07)I will open the
version 1 description so I can find the objects, attributes, and actions that should be deleted.

142
03.03@01:16

Fig. 03.03.03: Completed Version 1 Description

The version description indicates that the header “is displayed directly below the program start
command”.

Since the program start command is not in the same window as the game, this attribute no
longer applies. Therefore, it It will not be included in the Version 2 description. This is the only
concept that must be deleted.

The version 1 description indicates that the location of the header is "the top line of game
output". This may seem like it is still true in version 2, since the header is displayed at the top of
the window. However, location must be specified differently in a graphical window. Therefore,
this attribute must be deleted from the description.

Since the player's guess is now displayed as part of the failure outcome, attributes for the failure
outcome must also be changed. The guess is part of the failure outcome's content. This means
the content attributes must be removed so they can be replaced with appropriate content
attributes for version 2.

Let's close Version 1 and open Version 2.

143
03.03@02:09

Fig. 03.03.04: Hacking Version 2 Description Start State

The description builder has the concepts from version 1 description already loaded in the
assembly panel. However, the inappropriate concepts have been removed.

The functionality in version 2 is the same as version 1. The only thing that has changed is the
appearance of the game, which uses a graphical window instead of a text-based shell, and
displaying the player's guess. In version 1, after starting the program, everything was displayed
in the shell. For this version, a window appears before anything is displayed.

In the Descriptions panel, a new object is described, “The game opens a window.” This is our
new first object of the game, so I will drag this object to the assembly panel, above “the game
displays the header.”

144
03.03@03:11

Fig. 03.03.05: The Game Opens a Window is the First Object

If I would have placed it below the header, feedback would have informed me that I had added
an object out of order.

Like the previous description activity, I can either add new objects, or I can add attributes. Let’s
look at the header attributes. You will add the window attributes in an activity.

Recall that every display object has five attributes:


● content
● location
● size
● color
● and timing

The content of each object does not change when we go from text to graphics, so the
description of the content attributes will remain the same.

When making a graphical game, you must describe the location of all objects in the window,
because you will need to specify these locations in your program. In the text-based game, each
print call argument was displayed on a consecutive line without specifying location. In our
descriptions we will use general terms to indicate window location such as "top left corner" or
"center of the window" or "directly below” some other text. Since the header is in the top left
corner let's add this attribute to the header.

145
The next attribute is size. For text this means font size. Let's use small, medium and large in our
descriptions. In Hacking all the text is the same size so I'll use "small font size". I will add that
attribute to the header.

All text in Hacking is green on black so I will add “It is green on black” to the header.

The last attribute is about timing. In version 1, we addressed the temporal order in which objects
are displayed. Version 2 introduces a new temporal attribute: It has a 0.3 second pause after
displaying each line. I will add this attribute to complete the header.

03.03@05:02

Fig. 03.03.06: Completed Header Object for the Hacking Version 2 Description

03.03@05:02

Fig. 03.03.07: Completed Header Object for the Hacking Version 2 Description (Video Screenshot)

146
In the next activity, you will add all of the window attributes and the location, size and color to
the rest of the objects. You will also add two new actions in the appropriate temporal order to
clear and close the window, and one action to display the player's guess.

147
03.04 Create Functional Test Plan for Hacking Version 2

Okay, you’ve finished your second version description. If you look at the Game Creation
Diagram, you'll see that making a functional test plan is next.

03.04@00:19

Fig. 03.04.01: Create Version Test Plan in the Game Creation Diagram

Updating our test plan allows us to keep track of the progress of our game as we add
functionality. However, when we add a feature, we should retest all of the functionality
implemented in the previous versions that is still relevant in the current version. This is called
regression testing. We do regression testing since it is easy to unintentionally break existing
code when we add new code.

For example, let’s say I’m developing a phone app that allows me to edit pictures, sort them and
upload them to social media. In the first version, I create the functionality that allows me to edit
the pictures. I test this feature in my first version test plan. In version 2, I upload pictures to
social media, but I do not retest my picture editor. For version 3, I add the sort feature. I only test
the sort feature. Unfortunately, when someone uses my app, the editor doesn't work. Since I did
not retest the editor after creating version 2 or 3, I don't know which version’s code broke the
editor. To localize and fix the error, I need to check both the version 2 and version 3 code
instead of only the code for a single version. I discover that the upload feature in version 2,
broke the editor. If I had re-tested the version 1 editor feature in my version 2 test plan, it would
have been easier to locate and fix the error. I had converted the pictures to a new image format

148
to make them easier to upload, but did not change the editor to handle this format. Although I
did not actually change the editor code, I caused an error to occur in that code when I added the
upload code.

To support regression testing, the functional tests from the previous version are already loaded
into the Function Test Plan builder. The first thing you must do when altering your test plan to
accommodate the features of a new version is to eliminate any unnecessary tests. This is like
deleting unnecessary concepts from a description. For any version that requires you to
permanently delete elements from a description, test plan or algorithm, you will complete a
multiple choice activity that asks you to determine which elements to delete. For a functional
test plan, this includes tests that are no longer relevant, as well as tests that must be modified.

Since all of the questions for your test plan are grouped into single blocks, any question block
that does not contain exactly the right questions is considered incorrect and needs to be
deleted. For example, the current tests for the header do not contain any questions that relate to
the color or the window position of the text. However, the other tests such as “Does the game
display the header” and “Does it indicate debug mode” are still correct and needed. Since you
cannot delete individual questions, add missing questions or modify questions in a question
block, this entire block needs to be deleted so that it is not shown in the questions panel of the
builder.

03.04@03:07

Fig. 03.04.02: The Current Header Question Block

149
03.04@03:07

Fig. 03.04.02: The Current Header Question Block (Video Screenshot)

This is different than dragging a concept out of an assembly panel in a builder. Dragging a
concept out of the assembly panel, moves it to a description panel, action / question panel or
shape palette rather than deleting it. When you delete any concept in a multiple choice activity, it
will not even appear in the respective builder. It is not relevant to this version of the game, so
you will not be able to use it during assembly.

In the next two activities, you will delete the irrelevant question blocks, and use the builder to
create the second test plan. Remember that you must test the new aspects of the window,
including how it opens, clears and closes, and the new locations and visuals of the text in the
window.

150
03.05 Create Algorithm for Hacking Version 2

The algorithm for Hacking version 1 is in the builder. Version 2 of the algorithm includes all of
the algorithm steps from version 1. You don't need to determine which ones to delete.
You are going to create your version 2 algorithm by adding steps.

Recall that feature selection is used to transform a complex problem into a sequence of
problems, called versions. It is the process of identifying reasonable feature sets to use for each
version.

03.05@00:27

Fig. 03.05.01: Feature Selection in the Problem Solving Diagram

In version 1, you created an algorithm for the smallest feature set. In version 2, you will add
graphical features. You must use a graphics library instead of built-in functions to display text.

Most graphics libraries have a number of common capabilities. For example, you can use a
graphics library to open and close a window, to display objects in the window, and to clear the
window. You will add steps like these to your algorithm. Details such as the colour of the text are
included in your description, but they are too precise to be included in any algorithm. However,
you will need these details when you write your code.

Your "end game" algorithm step includes the "display failure outcome" and "prompt for end"
steps that were included in the previous version. You must add other steps to "end game". You

151
must also expand the "display failure outcome" step to use its own panel, so that you can add
one step for each line that is displayed. This is necessary, because the guess must be displayed
and the outcome lines must be centered. Centering lines requires more complex computation
than displaying right justified consecutive lines, such as the header and passwords.

03.05@01:25

Fig. 03.05.02: Missing Steps in the End Game Panel of the Hacking Version 2 Algorithm

Use your completed Hacking version 2 description and test plan to create the version 2
algorithm from the version 1 algorithm.

152
03.06 Python - Assignment Statement

In this video we introduce one new statement, the assignment statement. An assignment
statement is used to evaluate an expression and bind an identifier to the result object.

Consider this program. (03.06@00:23)

03.06@00:23

Fig. 03.06.01: Favorite Color Python Program

03.06@00:23

Fig. 03.06.02: Favorite Color Python Program (Video Screenshot)

This program prompts the user to enter a color, creates a string object that represents the color,
displays the color and then indicates that this is the user's pick. Purple is my favorite color. I
want to display the color string later in the program, rather than right after it was input. I want to
input two colors before I display either of them. The program output should look like this:

153
03.06@00:40

Fig. 03.06.03: Results of Running the Favorite Color Python Program

03.06@00:40

Fig. 03.06.04: Results of Running the Favorite Color Python Program (Video Screenshot)

If we could bind an identifier to each color string returned by the input function call, we could use
these identifiers anytime later in the program to refer to these string objects. The code would
look like this:
03.06@01:10

Fig. 03.06.05: Favorite and Second Favorite Color Python Program

154
03.06@01:10

Fig. 03.06.06: Favorite and Second Favorite Color Python Program (Video Screenshot)

This code uses a new kind of statement, called an assignment statement to bind the identifier
favorite to the first color. It uses a second assignment statement to bind the identifier
second_favorite to the second color. Since we have two kinds of statements, expression
statement and assignment statement, I will put them in a syntax diagram.

Now, I will show you the syntax and semantics of an assignment statement. We will only discuss
the simplest form of assignment statement, which binds a single target to an evaluated
expression object. Here is the syntax diagram.

03.06@01:52

Fig. 03.06.07: The Simplest Form of the Assignment Statement Syntax Diagram

155
03.06@01:52

Fig. 03.06.08: The Simplest Form of the Assignment Statement Syntax Diagram (Video Screenshot)

An assignment statement consists of a target, an assignment symbol and an expression. The


simplest form of target is an identifier. We will discuss other kinds of targets in a future lesson.
The assignment symbol is a delimiter token consisting of a single equal sign. It does not
designate equality in Python and is usually pronounced ‘assigned’ or ‘gets’, rather than 'equals'.

The semantics of an assignment statement, whose target is an identifier are:


1. evaluate the expression to obtain a result object
2. If the identifier is not in the namespace, add it to the namespace
3. bind the identifier to this result object.

We will discuss how to bind targets that are not identifiers in a future lesson.

I will show you how the Python interpreter interprets this program. The Python interpreter first
uses lexical analysis to successfully create a sequence of tokens. It then applies syntax analysis
to combine these tokens into statements. Statement 1 is an assignment statement whose target
is the identifier favorite, followed by the assignment delimiter, and it ends with an expression,
which is a function call to the built-in function, input. Statement 2 is also an assignment
statement. The other four statements are expression statements.

Since the program is syntactically valid, the interpreter applies semantic analysis to evaluate the
program, one statement at a time. Here is a diagram of the namespace (03.06@03:36), when
the program starts, containing the pre-bound identifiers input and print.

156
03.06@03:36

Fig. 03.06.09: Namespace of the Color Program when the Program Starts

03.06@03:36

Fig. 03.06.10: Namespace of the Color Program when the Program Starts (Video Screenshot)

To evaluate statement 1, the interpreter applies the semantics for an assignment statement.
First, it evaluates the expression. To evaluate this expression, the interpreter uses function call
semantics. Let’s skip the details of the function call semantics. The result is that the input
function call displays its prompt string and returns a string that represents whatever keys the
user typed before pressing the enter key. In this case, the string is purple.

157
03.06@04:06

Fig. 03.06.11: Step 1 of Assignment Statement Semantics String Result Object is Displayed

The interpreter then continues with step 2 of the assignment statement semantics by looking for
the identifier, favorite in the namespace. Since the identifier favorite is not in the namespace, the
interpreter adds it.

158
03.06@04:22

Fig. 03.06.12: Step 2 of Assignment Statement Semantics Favorite is Added to the Namespace

The interpreter then applies step 3 of the semantic rule to bind favorite to the string object
purple.

03.06@04:26

159
Fig. 03.06.13: Step 3 of Assignment Statement Semantics Favorite is Bound to the String Object

Statement 2 is evaluated in a similar way so second_favorite is bound to the return object of the
second input function call, the string red. Statement 3 is an expression statement that calls the
built-in function print to display the literal string, you picked. Statement 4 is also an expression
statement. When the interpreter applies the function call semantics, it evaluates the function
argument, which is an identifier. Dereferencing the identifier, favorite, is exactly the same as
dereferencing the print identifier.

Recall the semantics for evaluating an identifier expression.

03.06@05:03

Fig. 03.06.14: Semantic Rule for an Identifier

03.06@05:03

Fig. 03.06.15: Semantic Rule for an Identifier (Video Screenshot)

Since the identifier, favorite, is in the namespace, it is dereferenced to obtain the object it is
bound to, the string object purple. Since this object is the argument to a print function call,
purple is displayed.

160
03.06@05:18

Fig. 03.06.16: Argument Object of the print Function is Displayed

Statement 5 is an expression statement that calls the built-in function print to display another
literal string, and. Statement 6 is similar to statement 4 except the interpreter dereferences the
identifier second_favorite, to obtain the red string, and displays it.

161
03.06@05:37

Fig. 03.06.17: The Program is Complete

An identifier that is bound can be rebound to a different object. If the semantic rule for binding
an identifier is applied to an identifier that is already bound, then the identifier is already in the
namespace so it is not added. However, it is re-bound to the object that the expression
evaluated to. The previous binding is gone.

Consider this program:

03.06@06:02

Fig. 03.06.18: A Python Program that Rebinds len to id

162
03.06@06:02

Fig. 03.06.19: A Python Program that Rebinds len to id (Video Screenshot)

When this program starts the namespace contains three pre-defined identifiers len, id and print,
each bound to its own function object.

This program has no lexical or syntax errors. Statement 1 is an assignment statement. When
Statement 1 is evaluated, the expression consisting of the identifier, id, is evaluated. The id
identifier is dereferenced to obtain the id function object. The target identifier len is in the
namespace so it is not added. Len is then bound to the id function object. In statement 2, the
expression consists of a function call print applied to an argument expression which is a function
call len with argument expression the literal string 'hello'. The literal string evaluates to the string
object hello. The identifier len is then dereferenced in the namespace to obtain the id function.
This id function is applied to the hello string object to obtain the hard-to-remember identity
object.

163
03.06@07:25

Fig. 03.06.20: The Result Object of this Rebound Function is the id of the String

Notice that the length function is not applied since the len identifier was rebound to the id
function object. The len function returns the hard-to-remember identity object of the hello string.
The print function call displays this identity object in human readable form.

I did this to show you that any identifier can be bound to any object. However, don't try this at
home! You should never rebind an identifier that is pre-bound before our code is evaluated.

You can use the built-in dir function to examine the namespace as you evaluate Python
expressions. Remember, typing dir(__builtins__), displays the names from the namespace that
are pre-bound to built-in objects.

164
03.06@07:55

Fig. 03.06.21: dir(__builtins__) Evaluated in the Python Shell Returns a List of Built-In Objects

If I call dir in a shell with no argument, it displays some names that start with two underscores
and end with two underscores. The names that appear depend on your Python implementation.

03.06@08:13

Fig. 03.06.22: dir() Evaluated in the Python Shell Results

165
If I bind an identifier and rebind len and then call the dir function again it now includes favorite to
indicate it has been bound. It also includes len to indicate that len has been rebound to a
different object than its pre-binding object.

03.06@08:27

Fig. 03.06.23: dir() Evaluated After Binding an Identifier and Rebinding len

We won't investigate the other names that contain underscores in this course.

In this video I have described the syntax and semantics of the assignment statement. An
assignment statement can be used to bind an identifier to any object, so *that* object can be
used later in the program.

166
03.07 Python - Binary Expression and Operator Token

In this video we introduce one new kind of expression, the binary expression and one new kind
of token, the operator token.

We will add the binary expression to our expression syntax diagram.

03.07@00:23

Fig. 03.07.01: Expression Syntax Diagram with Binary Expression Added

167
03.07@00:23

Fig. 03.07.02: Expression Syntax Diagram with Binary Expression Added (Video Screenshot)

We have already introduced three of the five kinds of Python tokens: delimiters, literals and
identifiers. In this lesson, we introduce the operator token.

Consider this program whose third statement contains a binary expression and an operator
token called plus.

168
03.07@00:37

Fig. 03.07.03: Program with a Binary Expression and an Operator Token

This program prompts the user to enter two values and binds the identifiers first_value and
second_value to the two string objects returned by the input function. I will run this Python
program. I will enter 27 and 65.

169
03.07@00:57

Fig. 03.07.04: Result Object of this Program

The answer is 2765! What happened?

Every statement except for statement 3 contains tokens and expressions that you have already
studied. All these statements have valid lexics and syntax. What about statement 3? Let's apply
lexical analysis, syntax analysis and semantic analysis to statement 3.

Statement 3 contains the tokens: identifier sum, assignment delimiter, and identifier first_value.
The next character is plus and no token kind that we have described so far starts with a plus
character. The plus character is a new kind of token called an operator token.

An operator is used to compute a result object from one or more operand objects. Since there
are only 19 operator tokens, we list them in a lexical table, instead of using a lexical rule.

03.07@01:52

Fig. 03.07.05: Lexical Table: Operators

170
03.07@01:52

Fig. 03.07.06: Lexical Table: Operators (Video Screenshot)

Some mathematical operators are represented by the same operator token in Python, some are
represented by different operator tokens and some are not represented by any operator token.
Plus sign and minus sign are the same in Python as in mathematics. In mathematics we use a
product symbol or a dot to represent multiplication and in Python we use the star symbol. The
mathematical comparison operator, less than or equal sign, is represented by two symbols in
Python, the less sign followed by the equal sign. In mathematics, exponentiation is denoted by a
superscript, but in Python the star star operator token is used. Equal sign is an equality operator
in mathematics, but in Python, the equality operator is equal sign followed by equal sign. Recall
that equal sign by itself is actually the assignment delimiter token, not an operator token. It is a
common programming error to use the assignment delimiter token when you should use the
equality operator. The factorial operator uses an exclamation mark in mathematics, but there is
no factorial operator token in Python.

You just saw that the operator token table contains both star and star star. If the lexical analyser
encounters star star, does it create a single operator token, star star, or two operator tokens star
and star? We can apply the longest token rule to operator tokens, just like we applied it to literal
integers and identifiers, in the Lexical Analysis lesson. So, the answer is one long token star
star.

The last token in statement 3 is the identifier, second underscore value. Statement 3 is an
assignment statement where the expression is a new kind of expression, a binary expression.

171
Here is the syntax diagram for a binary expression.

03.07@03:52

Fig. 03.07.07: Syntax Diagram for a Binary Expression

03.07@03:52

Fig. 03.07.08: Syntax Diagram for a Binary Expression (Video Screenshot)

This syntax diagram contains a new non-terminal, binary operator. For now we will use this
syntax diagram for the non-terminal state, binary operator.

03.07@04:15

172
Fig. 03.07.09: Syntax Diagram for a the Non-Terminal State Binary Operator

03.07@04:15

Fig. 03.07.10: Syntax Diagram for a the Non-Terminal State Binary Operator (Video Screenshot)

Any operator token except for tilde can be used as a binary operator. In a future lesson we will
generalize binary operator to include some other tokens that are not operator tokens.

The expression in Statement 3 is a binary expression since "first value" matches the first
expression as an identifier, the plus sign token matches the binary operator state and "second
value" matches the second expression as an identifier. A binary expression is used to apply an
operator function to two operand objects. The function that is applied depends both on the
binary operator and the type of the first operand.

The semantics of a binary expression are:


1. evaluate the left expression to get the first operand object
2. evaluate the right expression to get the second operand object
3. identify the operator function based on the binary operator and the *type* of the two
operands
4. apply the the binary operator's function

In a future lesson, you will discover that some binary operator functions may ignore the second
expression.

173
Before applying these semantics to statement 3, I will quickly apply semantics to statements 1
and 2. When the program starts, there is a namespace containing all of the pre-bound
identifiers. In our case, the only pre-bound identifiers used in the program are the built-in
function names, input and print. Statements 1 and 2 are assignment statements. Statement 1
prompts the user to enter a string. In this example, the input function returns the string object,
27. The interpreter, adds the identifier "first value" to the namespace, and binds it to this string
object. Statement 2 adds the identifier "second value" to the namespace and in this case, binds
it to the string object 65.

[email protected]

Fig. 03.07.11: Namespace After the Semantics of Statements 1 and 2 have been Evaluated

Now let's apply the assignment statement semantics to statement 3. The expression is a binary
expression, so I will apply the binary expression semantics to it. First evaluate the left
expression to get the first operand object. Since the left expression is an identifier expression,
we use the semantics for an identifier. Since the identifier, "first_value", is in the namespace, the
interpreter dereferences it to obtain the string object, 27.

174
[email protected]

Fig. 03.07.12: String Object 27 is the First Operand

The interpreter must evaluate the right expression to obtain the second operand object. Since
the right expression is an identifier expression, the identifier, "second value" is dereferenced to
obtain the string object, 65.

175
[email protected]

Fig. 03.07.13: String Object 65 is the Second Operand

The binary operator is a plus operator token and the types of the two operands are string, so the
interpreter finds the operator function that implements plus for strings. This function is string
concatenation. When the string concatenation function is applied to the operands, string object
27 and string object 65, the resulting string object two seven six five is created. To complete the
semantics of the assignment statement, the identifier sum is added to the namespace and
bound to this string object.

176
03.07@07:29

Fig. 03.07.14: Plus Operator Applied to Two Strings Concatenates the String Objects

Statements 4 through 9 display the program output.

To compute the numerical sum of two numbers, I can use the built-in function, int, which creates
an int object from a string object. Here is the python documentation for the int function.

177
03.07@07:40

Fig. 03.07.15: int Function Documentation from the Python Standard Library

Here is the revised program.

03.07@07:45

Fig. 03.07.16: Revised Program with int Function

178
In this program the binary expression in statement 3 contains two function call expressions. The
first call evaluates to the integer object 27 and the second call evaluates to the integer object
65. When the binary expression semantics are applied, the interpreter finds the operator
function that implements plus for integers. This function is numerical addition

The interpreter applies this numerical addition function to obtain the integer object 92. It then
binds the identifier, sum to this object. Statements 4 through 9 then display both integer
operands and this sum.

03.07@08:18

Fig. 03.07.17: Result of the Revised Program

In this video, we have introduced operator tokens and binary expressions.

179
03.08 Python - Import Statement and Keyword Token

In this video, we introduce one new kind of statement, the import statement, as well as the last
token kind, keyword. An import statement allows us to use functions, types and other objects
from a library module.

So far, you have only used built-in functions in your code. However, there are only a limited
number of built-in functions. If you want to add functionality to your code that is not supported by
the built-in functions, you need to use a module. A module is a Python program with its own
statements and namespace. A module provides functions, types and other kinds of objects that
you can use in your program.

The standard library includes many modules. For example, if you want to pause during a
program, there is no built-in function that does this. However, the sleep function in the time
module of the standard library pauses.

[email protected]

Fig. 03.08.01: Name Error Result from Evaluating sleep(2) in the Python Shell

What went wrong? The name sleep is not in the program's namespace. It is not pre-bound, like
the function name len and the type name int. I can check the namespace using the built-in dir
function.

180
[email protected]

Fig. 03.08.02: sleep is Not in the List of Pre-Bounds

To use objects from a module, I need to use a new kind of statement, called import.

[email protected]

Fig. 03.08.03: New Statement Called import

181
It worked, since there was a 2 second pause before the interpreter prompt was displayed for the
next expression.

I will show you that sleep is now in the namespace.

[email protected]

Fig. 03.08.04: Sleep is Now in the Namespace

Importing a module is like using an external resource in the real world. When I am preparing a
meal, I use many local resources. I may use vegetables from my fridge and bread from my
cupboard. However, sometimes, I want to serve a food item from outside my home. For
example, I might pick up a birthday cake at the bakery. I import the cake from another locale. I
can customize this cake by adding birthday candles, but the cake itself is not a local item.

I will add import to the syntax diagram for statement.

182
03.08@02:15

Fig. 03.08.05: Update Statement Syntax Diagram with Import Statement

03.08@02:15

Fig. 03.08.06: Update Statement Syntax Diagram with Import Statement (Video Screenshot)

An import statement imports identifiers from a module into your program's namespace so that
you can use the objects the identifiers are bound to. There are several different syntactic forms
for an import statement. This lesson introduces the "from" form of the import statement.

183
03.08@02:23

Fig. 03.08.07: From Form of the Import Statement

03.08@02:23

Fig. 03.08.08: From Form of the Import Statement (Video Screenshot)

To understand this syntax diagram I must first introduce the keyword token. There are 33
keywords in Python. "from" and "import" are both keyword tokens. A keyword looks like an
identifier, but it is a reserved word that cannot be used as an identifier. Trying to use a keyword
as an identifier results in a syntax error. For example if I try to bind the keyword 'from' using an
assignment statement, a syntax error occurs.

184
03.08@03:02

Fig. 03.08.09: Syntax Error Resulting from Trying to Bind a Keyword Token

Recall our lexical rule for identifiers: starts with a letter or underscore, and is followed by zero or
more letters, underscores, or digits. I must modify this identifier token rule to exclude keywords.

03.08@03:12

Fig. 03.08.10: Modified Identifier Token Rule

185
03.08@03:12

Fig. 03.08.11: Modified Identifier Token Rule (Video Screenshot)

Let's apply syntax analysis to this import statement.

03.08@03:22

Fig. 03.08.12: Begin Syntax Analysis of this Import Statement

186
The first token is the keyword, from, which matches the from terminal state in the syntax
diagram for import statement. The next token is the identifier time and the next state is the
non-terminal state, module. In this lesson we use the simplest version of the module
non-terminal state, which is a single identifier. The identifier time matches the identifier state in
module's syntax diagram. The next token in an import statement must be the keyword, import.
Following this keyword state are states that match one or more identifiers, separated by
commas. Notice that there is no comma after the last identifier. Since there are no more tokens
in the statement and the current state, identifier, is an accepting state, this is a syntactically valid
import statement.

Before I show you the semantics of the import statement, let's run a program that uses it. If I run
this program it displays the current time, pauses for 2 seconds and then displays goodbye.

03.08@04:23

Fig. 03.08.13: A Program that Contains Imported Functions sleep and asctime

The sleep and asctime functions are not built-in python functions. Instead they are contained in
a library module called time. An import statement creates a name in your program's namespace
for each module object that you want to use.

Here are the semantics of the import statement.

187
03.08@04:40

Fig. 03.08.14: Semantics of the Import Statement

I will apply these semantics to the import statement in our program. In step 1, the interpreter
uses the module name “time” to find the time module.

03.08@04:52

188
Fig. 03.08.15: Use time to Find Time Module

In step 2, the interpreter evaluates the time module by evaluating its statements. We cannot see
time's statements, but they are evaluated the same way the interpreter evaluates our program,
one statement at a time. Evaluating time's statements creates objects in memory, adds some
names, such as sleep and asctime to time's namespace, and binds the names to some of the
created objects.

03.08@05:11

Fig. 03.08.16: Evaluate time’s Statements and Add Objects to time’s Namespace

In step 3, the interpreter dereferences the sleep and asctime names in time's namespace to
obtain the two objects they are bound to.

189
03.08@05:18

Fig. 03.08.17: Dereference sleep and asctime and Bind to Corresponding Function Objects

In step 4, the interpreter looks in the program's namespace and does not find either sleep or
asctime so it adds both names.

03.08@05:36

190
Fig. 03.08.18: sleep and asctime Added to the Program’s Namespace

In step 5, the interpreter binds the names sleep and asctime in the program's namespace, to the
objects found in time's namespace from step 3.

03.08@05:38

Fig. 03.08.19: sleep and asctime in the Program’s Namespace Bound to Objects in time’s Namespace

When the program evaluates the second statement, it uses the semantics of an assignment
statement that you saw in the assignment statement lesson. It first evaluates the function call
expression by looking for asctime in the program's namespace. It finds asctime and
dereferences it to obtain the asctime function object. It evaluates this function object and returns
a string object representing the current date and time. Finally, it adds the identifier “now” to the
program's namespace and binds it to this string object.

191
03.08@06:15

Fig. 03.08.20: Second Statement Evaluated with Assignment Statement Semantics

The third statement is an expression statement containing the print function call. The interpreter
evaluates this function call by looking for the name print in the program's namespace. It finds
the name "print" and dereferences it to obtain the print function. It then evaluates the argument
binary expression by concatenating a literal string and the string object that the identifier now is
bound to. It displays this string object as the current time and date.

192
03.08@06:44

Fig. 03.08.21: Print Statement Evaluated to Display Literal String and now in Output

The fourth statement is an expression statement containing a function call. The interpreter
evaluates this function call by looking for the name sleep in the program's namespace. It finds
the name sleep, dereference it to obtain the sleep function and evaluates it with the argument
object, 2. This function pauses the program for 2 seconds and returns an object whose type is
NoneType, which is ignored.

193
03.08@07:09

Fig. 03.08.22: sleep Returns a NoneType Object which is Not Displayed

The fifth statement displays a goodbye string and the program ends.

You can use multiple import statements to import names from multiple modules. These
statements can be placed in any order as long as you import a name before you use it.

In this video, I introduced one new kind of statement, the import statement. I also described the
last token kind, keyword. An import statement allows us to use functions, types and other
objects from a library module.

194
03.09 Python - Multi-argument Function Calls

In this video, we generalize the syntax and semantics of the function call expression to support
multiple arguments instead of zero or one argument.

Here is the current syntax for a function call with zero or one argument, that you have already
seen.

03.09@00:23

Fig. 03.09.01: Current Syntax for a Function Call

03.09@00:23

Fig. 03.09.02: Current Syntax for a Function Call (Video Screenshot)

Different functions actually support different numbers of arguments. For example, the len
function has exactly one argument. The function randint from the random module in the

195
standard library has two arguments. This function returns a random number between its two
argument objects.

03.09@00:45

Fig. 03.09.03: randint Returns a Random Number Between its Two Argument Objects

Many Python functions can accept a variable number of arguments. For example when the print
function is called with no arguments, it displays a blank line. When the print function is called
with more than one argument, it evaluates its argument expressions and displays all of the
result objects on one line, separated by single blanks.

196
03.09@01:00

Fig. 03.09.04: print Called with More than One Argument Separates Result Objects with Single Blanks

Here is the documentation for the built-in print function.

03.09@01:05

Fig. 03.09.05: Documentation for the Built-In print Function

197
03.09@01:05

Fig. 03.09.06: Documentation for the Built-In print Function (Video Screenshot)

The symbol star before the first argument, objects, indicates that this function accepts any
number of arguments, zero or more, at this argument position. That is why I could successfully
call print with one argument, no arguments or three arguments. We will ignore the rest of the
print function argument documentation until a future lesson.

I will generalize the function call syntax to support more than one argument. Here is a simplified
syntax diagram for the non-terminal argument list state that supports one or more argument
expressions.

198
03.09@01:31

Fig. 03.09.07: Function Call Syntax Generalized to Support More than One Argument

03.09@01:31

Fig. 03.09.07: Function Call Syntax Generalized to Support More than One Argument

199
I will use these syntax diagrams to show that print of 27, hello and 4 has valid syntax. There are
8 tokens in this line. From the start state, the identifier token “print” matches the identifier state.
The left paren delimiter token matches the left paren state. The next state is the non-terminal
state, argument list. Expand this non-terminal state to reveal the expression state. Expand the
expression state to reveal its literal, identifier function call and binary expression states. The
literal integer token, 27 matches the literal accepting state, so the expression is valid. The next
token is the comma delimiter which matches the comma state. The next token is the literal string
token, hello. Expanding the expression state, again reveals its internal states and hello matches
the literal state so this expression is valid. The next token is the “comma” delimiter which
matches the comma state. The next token is the integer literal 4. Expanding the expression
state again finds a match with the literal state so the expression is valid. The next token is the
right paren delimiter token so the interpreter does not want to match any more expressions.
Since the current expression state is an accepting state in the argument list non-terminal state,
argument list, is valid. Returning to the syntax diagram for function call, the right paren token
matches the right paren state. There are no more tokens. Since the current state is an accepting
state for function call, the function call is valid. Since function call is an accepting state in the
expression syntax diagram, the entire expression has valid syntax.

Since the syntax is valid, the interpreter can apply semantic analysis. Here is the previous
semantic rule for evaluating a function call with zero or one arguments.

03.09@03:29

Fig. 03.09.08: Semantic Rule for Function Call With Zero or One Arguments

I will modify this semantic rule to support multiple arguments.

200
03.09@03:32

Fig. 03.09.09: Semantic Rule for Function Call with Multiple Arguments

Step one is the same. Step two becomes: If there is an argument list, evaluate it to obtain an
argument object list, otherwise create an empty argument object list Step three becomes: Pass
the argument object list to the function. Step four is the same.

The semantics of the non-terminal, argument list state, are:

Evaluate each expression from left to right and add the result objects to an argument object list
in order.

Let's apply these semantic rules to the multi-argument print function call. When the program
starts, the name, print, is pre-bound to the print function object. Step one uses the semantics of
identifier to dereference print in the program's namespace to obtain the print function object.
Step two checks if there is an argument list. Since there is an argument list between the
parentheses, the interpreter applies the semantics of an argument list. Evaluate each of the
three argument expressions using the semantics of literals. Then, add each result object to the
argument object list. It will contain three objects, integer 27, string hello, and integer 4. Step
three passes this list of three objects to the print function object. Recall that a function is like a
box with an argument object as input and a result object as output. The input is actually a list
that contains zero or more argument objects. Step four evaluates the print function object to
display the three argument objects, 27, hello and 4 in human readable form, each separated by
a blank. The print function returns an object whose type is NoneType and this object is ignored.

201
In this video we generalized the syntax and semantics of function calls to support multiple
arguments.

202
03.10 Python - Method Call and Attribute Reference

In this video, we generalize the syntax and semantics of the function call expression to include a
special kind of function call, named a method call. We also introduce a new kind of expression
called an attribute reference.

You have seen that every program has a namespace that binds identifiers to objects. In
addition, most objects have their own namespaces. A name in an object's namespace is called
an attribute. A new kind of expression, called an attribute reference, is used to access an
object whose name is in the object's namespace. For example every string object has an
attribute named lower that is bound to a method object.

In this course, we will assume that the namespace of an object is created when the object is
created. In fact, the way namespaces are managed varies across python implementations.

Here is a new syntax diagram for expression that contains attribute reference

03.10@01:11

Fig. 03.10.01: Expression Syntax Diagram Including Attribute Reference

203
03.10@01:11

Fig. 03.10.02: Expression Syntax Diagram Including Attribute Reference (Video Screenshot)

and here is the syntax diagram for attribute reference.

03.10@01:23

Fig. 03.10.03: Attribute Reference Syntax Diagram

204
03.10@01:23

Fig. 03.10.04: Attribute Reference Syntax Diagram (Video Screenshot)

The attribute must be an identifier token.

You can see from these syntax diagrams that ‘R2D2’.lower is a syntactically valid attribute
reference whose expression is the literal string 'R2D2' and whose attribute is the identifier lower.

Here are the semantics of attribute reference:


1. Evaluate the expression to get an object
2. If the attribute is in the namespace of the object, return the object it is bound to.
Otherwise report an attribute error

When I evaluate the attribute reference, 'R2D2'.lower the interpreter applies the attribute
reference semantics. First, the expression is the literal string 'R2D2'. When the interpreter
evaluates this literal, it creates the literal object 'R2D2'. Since this is a string object, its
namespace contains the identifier lower with a binding to a method object. Second, the
interpreter finds the identifier, lower in the namespace of 'R2D2' and dereferences it to obtain
the method object lower for this string object.

So every string has an attribute called lower that is bound to a method object. What is a method
object and what are methods used for? A method is a function that operates on a particular
type of object. We say the method is associated with the type. The object that the method is
applied to is called a special argument.

205
Method application has different syntax than a normal function call. The special object is not an
evaluated expression in an argument list like a normal function call. Instead the method call
uses the syntax of an attribute reference, where the special object is an evaluated expression
before a dot and the method name is placed after this dot.

For example, the method, lower, operates on a string object and returns a similar string object
but with all uppercase letters converted to lowercase letters.

03.10@03:20

Fig. 03.10.05: Method lower Operating on string ‘R2D2’

03.10@03:20

Fig. 03.10.06: Method lower Operating on string ‘R2D2’ (Video Screenshot)

If lower was an ordinary function, the function call would look like this but lower is not an
ordinary function call so this syntax doesn't work.

206
03.10@03:30

Fig. 03.10.07: Syntax Error from Calling lower like a Function

03.10@03:30

Fig. 03.10.08: Syntax Error from Calling lower like a Function (Video Screenshot)

There is no built-in function called lower. Instead, there is a built-in method associated with the
string type whose name is lower. You can look at the documentation for the built-in methods, by
looking at the type documentation. Some methods require ordinary argument objects in addition
to the "special" argument. For example, the string method, find, also requires an ordinary string
argument.

The find method returns the index of a string, where the first occurrence of a substring starts.

207
03.10@04:14

Fig. 03.10.09: find Method Applied to string ‘Edmonton’

In this case, the first occurrence of the substring "o n" is at index 3 of the string Edmonton. In
Python, string indexes are numbered from zero, so 0 is E, 1 is d, 2 is m and 3 is o. This o is the
start of the first occurrence of "o n".

I will generalize the syntax of function calls to include method calls. Here is the current syntax
for a function call, that you have already seen.

208
03.10@04:39

Fig. 03.10.10: Current Syntax Diagram for a Function Call

To generalize this syntax to include method calls we replace the identifier state by the
expression state so that any expression, including an attribute reference, can be used in the
function call instead of using a single identifier as a function name.

209
03.10@04:56

Fig. 03.10.11: Function Call Syntax Diagram Generalized for Any Expression

I will use these syntax diagrams to validate the syntax of this method call expression before
defining the semantics of method calls.

You have already seen that that the first three tokens, 'R2D2', dot and lower match the attribute
reference which is an expression. The last two tokens match the left and right paren states in
the function call diagram so this sequence of tokens is a valid function call. Since 'R2D2.lower'
evaluates to a method object instead of a normal function object this expression is a method
call.

Now, I will generalize the semantics of a function call to include the semantics of a method call.
Step one becomes: Evaluate the expression to obtain an object. Step two is the same. Step
three is new: If the expression object is a method object, add its special argument to the start of
the argument object list. Step four is the previous step three. Step five is the previous step four.

210
03.10@05:58

Fig. 03.10.11: Function Call Semantic Rule Generalized to Include Method Calls

Here is how the interpreter applies the function call semantics to ‘Edmonton’.find(‘on’).
First it evaluates the expression, which consists of the attribute reference, ‘Edmonton’.find.
Applying the semantics of an attribute reference the first semantic step of attribute reference is
to evaluate its expression by creating the string object, Edmonton. Since this is a string object
the interpreter creates its namespace as well.

211
03.10@06:25

Fig. 03.10.12: Edmonton str Object Created in Memory

The second step of attribute reference is to locate the attribute called find in the namespace of
Edmonton and dereference it to obtain the method object, find. Since the attribute reference has
been evaluated, the interpreter returns to the semantic rule for function call and performs step 2:
if there is an argument list etc.The argument list contains a single literal string expression, ‘on’,
so a one element argument list is created that contains, the string object, on.

212
03.10@06:58

Fig. 03.10.13: find Method Object with on str Object in the Argument List

In step three the interpreter identifies that the expression evaluated to a method object, find, so
it adds the special argument, "Edmonton" at the start of the argument object list.

03.10@07:10

Fig. 03.10.14: Edmonton str Object Added to find’s Argument List

213
In step four the interpreter passes the argument object list to the function object, which is the
method object find.

In step five the interpreter evaluates the function code, which is the method code for find. This
method computes and returns the integer object 3 as the first index in the string Edmonton that
matches the string, on.

03.10@07:35

Fig. 03.10.15: Final Step Returns int Object, 3

In this video, we generalized the syntax and semantics of the function call expression to include
a special kind of function call, named a method call. We also introduce a new kind of expression
called an attribute reference.

214
03.11 Program Hacking Version 2

You’re ready to program Hacking version 2!

03.11@00:13

Fig. 03.11.01: Game Creation Diagram Create Program SubTask

Whenever I start a new version of a project, I save a copy of the previous version and modify a
new copy. I’ll do that now with Hacking version 1. I will rename the new copy of my program
HackingV2.py, since this copy will become the version 2 code.

I will start by adding descriptive block comments for each step of the Version 2 algorithm. Some
of these comments are already in my program from version 1, so I will add the new ones.
Remember that we use extra spaces near the beginning of a comment to indicate that it is a
substep of a larger step.

215
03.11@00:34

Fig. 03.11.02: Descriptive Block Comments with Extra Spaces Indicate Substeps

Let's add the code for create window. I will consult the uagame library documentation to see
what kind of window methods I can use. Since I am creating a window object I will choose an
appropriate identifier to bind to my window object. How about … window!

216
03.11@00:50

Fig. 03.11.03: uagame Library Documentation for Window

Now I will use the Window function from uagame to create a window object. The documentation
indicates that I must pass three arguments: a title string, a width integer, and a height integer. I
will get these values from the version 2 description. The title should be “Hacking”. The window
should have a 6 by 5 aspect ratio. This is an example of an imprecision in the description that
must be made more precise in the code. I will use a 600 by 500 pixel window.

I don't need to add code one line at a time from the start of the program to the end. I can add
related features together. For example, to close the window at the end of the game, I will add
the method call window.close() at the end of my program, under the “close window” comment.

217
03.11@01:50

Fig. 03.11.04: window.close() Method Call on Line 52

I’m going to run my program right now to see what happens. First, I will add an import statement
so that I can use the uagame type called Window. This gives me access to all of the methods
associated with Window.

218
03.11@02:00

Fig. 03.11.05: uagame Import Statement LIne 7

A window opens, but all the print function calls still display in the shell. After I’ve entered a
password and pressed enter to exit, the window closes.

03.11@02:05

219
Fig. 03.11.06: Open Window with Print Statements in Python Shell

The print and input functions won’t work in a window. Therefore, I'll delete all of the original
program lines.

The Window type has a method called draw_string.

03.11@02:34

Fig. 03.11.07: draw_string Documentation

Since this method displays a string in a window, I’ll use it to display the first header line.
draw_string takes three arguments: a display string and two integers that indicate the top left
corner of its location in the window.

Computer graphics use a coordinate system to define points in the window. You may be familiar
with coordinates from mathematics. Think of the window as a graph. The x-axis is horizontal
and y-axis is vertical. In mathematics we would put the origin, zero, zero, at the lower left corner
of the window. x would increase from right to left and y would increase from bottom to top. In
graphics, the origin is at the top left corner of the window. x still increases as we go to the right,,
but y increases as we go down. A point 10 pixels down from the top left corner is denoted "0
10". The point in the bottom right of a 600 by 500 pixel window is at "600 500".

To display the first line of the header in the top left corner, I will pass in the literal string ‘DEBUG
MODE’, the literal integer 0, and another literal integer 0.

220
03.11@03:34

Fig. 03.11.08: Display Header Code Line 13

I will run the program again.

That went by so fast that it was hard to see the window contents. Since I know my program
should pause after displaying each line, I will add in a pause now, so that I can get a better look
at the contents of the window before it closes. To add a pause, I will use the sleep function from
the time module, introduced in the import lesson. I will add the statement, “from time import
sleep”, with the "Window" import statement. Recall, that sleep needs the number of seconds as
an argument. I will add a sleep call with argument, literal float 0.3 after the call to “draw string”.

221
03.11@04:17

Fig. 03.11.09: sleep Method Call Line 15

Now I’ll run the program again. That is much easier to see! You can keep the window open
longer by increasing the number of seconds.

I expected to see DEBUG MODE displayed in the top left corner, but it isn't there. To see the
string, I need to add another method call, window.update, after window.display_string.

222
03.11@04:55

Fig. 03.11.10: window.update Method Call Line 15

I'll add it and run the program again.

03.11@04:56

Fig. 03.11.11: Game Window with DEBUG MODE Displayed

223
The string formatting doesn't look quite right. I will add some lines to change the text font and
color and run it.

03.11@05:07

Fig. 03.11.12: Game Window with DEBUG MODE Displayed Correct Font and Colour

This text matches the version 2 video text!

I used several methods to change some window attributes: font name, font point size, font color
and window background color. These window attributes are used for all displayed text. They
have default values. The default font is the operating system font. The default font and
background colors are white and black. Since the default background color is black we don't
actually need to set it.

Now I’m ready to display the second line of the header. I can use the same three lines of code

224
again but I need to change the draw_string arguments. First I'll replace the string. What literal
integers should I use to display the second line below the first? Since the second line should be
displayed at the left edge of the window, the x-coordinate should be 0. I need to compute its
y-coordinate. If I use 0, the second line will be displayed on top of the first line! I want the
second line to display below the first, so my y-coordinate should be the height of the first line.

How do I figure out the height of a line of text? Counting pixels would be tedious and I might
miscount. It would be much easier if there was a function that could do this for me. Good news!
uagame has a method called “get font height”, which returns the height of my game font in
pixels.

03.11@06:24

Fig. 03.11.13: get_font_height Documentation

I’ll bind the result of this method call to the identifier “string height” and use this identifier in
draw_string.

225
03.11@06:33

Fig. 03.11.14: Bind Font Height Method to string_height Line 18

The header ends in a blank line, so I can use these three lines of code again and edit them.

What should the y coordinate be? After drawing the first line, I update the y coordinate to be the
string height before drawing the second line. I can do this again to get the y coordinate for the
third line I just add string height again. We will need to do this again for the other lines. The next
y coordinate will be three string heights, and so on. However, let's think about the y-coordinate
differently. Each new y coord is the previous y coord plus the string height.

If I think about it this way, then I can use line y gets line y plus string height to update the y
coordinate. This line updates the y coord by rebinding it to a new value.

Before I run the program, I need to initialize the identifier "line y" to zero. Otherwise, the first call
to draw string will cause a Name Error, when line y is dereferenced.

226
03.11@07:34

Fig. 03.11.15: line_y Initialized Line 18, Rebound Lines 23 and 28

Now I'll run the program.

03.11@07:36

Fig. 03.11.16: Three Header Lines Displayed in Window

227
This new y coordinate update works, but it is hard to know whether a blank line was actually
displayed.

I will leave displaying the passwords to you.

The next thing that I want to do is get the player’s guess. We already know that the built-in input
function won't work in a window. Instead, uagame has a method called “input string”.

03.11@08:04

Fig. 03.11.17: input_string Documentation

This method behaves like the input function. Although it has a prompt argument that is
displayed in the window, it also requires the display location. Like draw_string, two integer
arguments are used for the location. I’ll use the “enter password” prompt string, 0 for the x
coordinate and line_y for the y coordinate. Since I need to display the player’s guess as part of
the outcome, I’ll bind an identifier, called guess, to this object. You do not need to call the
window update method after calling input string since the prompt must appear before a string is
input.

228
03.11@08:24

Fig. 03.11.18: User Password Input Method Line 38

I will leave clearing the window to you. Look in the uagame documentation to find a method to
clear the window.

The outcome text is centered in the window, so I must compute the location of each outcome
line. Displaying text on the left edge of the window won’t work.

Let’s start with the x-coordinate. I want each outcome line to be centered in the x direction. Let's
look at a picture.

229
03.11@09:04

Fig. 03.11.19: Centered Text Equal Distance from Both Sides

If I want a line to be centered, I want the spaces on both sides of the line to be equal. To
compute the size of this equal space, I start with the window width and subtract the line width.
This yields the size of both spaces together. I will use the method get width to get the window
width and the method get string width to compute the guess string width.

230
03.11@09:18

Fig. 03.11.20: get_width and get_string_width Documentation

To get the size of each individual space, I divide this distance by two. I will use the integer
division operator, // ,to divide two integers to obtain an integer result. This division discards any
division remainder. I use this individual space distance as the x coordinate of this outcome line.

231
03.11@09:37

Fig. 03.11.21: Width Calculation for Centered Text - Lines 45 and 46

To calculate the y coordinate I use the same principle. The space between the window's top
edge and the top of the first outcome line should be the same as the space between the
window’s bottom edge and the bottom of the last outcome line. I compute the height of the
outcome line block by multiplying the total number of outcome lines, including blank lines, by
string height. Now I’ll subtract the height of the block of text from the height of the window, using
the get height method. Finally, I’ll divide the total space by 2. I can use the two coordinates I’ve
just calculated as arguments to draw the player’s guess.

232
03.11@10:15

Fig. 03.11.22: Height Calculation for Centered Text - Lines 49-51

When I run the program again the guess is displayed where it’s supposed to be! But the
outcome text is displayed without erasing the previous game text! You’ll fix this when you finish
programming the game.

233
03.11@10:24

Fig. 03.11.23: Outcome Message Centered Display

The last thing I’ll do before you take over, is display one password.

I already know what lines of code I need: one to display the string, one to update the window,
one to pause, and one to increment line y. I’ll copy those lines, update the first password string,
and then run my program again. The password is printed, where it’s supposed to be.

234
03.11@10:51

Fig. 03.11.24: Single Password Displayed

You will code the rest of the program in the next activity. Remember to run and test often. Every
error you make is an opportunity to learn!

235
03.12 Review Code for Hacking Version 3

Welcome to the second reflection of the course!

03.12@00:13

Fig. 03.12.01: Game Creation Diagram Review Code SubTask

In the last reflection, we showcased some of the power of the debugger. It was pretty simple
since there weren't any assignment statements. For this version of Hacking, I’ll use the
debugger to show you that assignment statements are used to bind identifiers to objects and
that identifiers are dereferenced to obtain objects.

Here is the solution for Hacking version 2.

236
03.12@00:37

Fig. 03.12.02: Hacking Version 2 Solution Code Program Comment

It starts with a program comment that describes the version details. The first statement is an
import. To begin, I’ll press the ‘step into’ button to start the debugging process. The first
statement highlighted is “from uagame import Window”.

Like the previous reflection, I’ll be using the “step over” button instead of the “step into” button
from now on. It is not necessary to step into any statement to see code that is not explicitly in
the program.

I’ll press the step over button and the next statement highlighted is another import statement
“from time import sleep”. I’ll step over this import statement so the next highlighted statement is
a window assignment statement. I'll step over so the interpreter evaluates the assignment
statement. A Hacking window appears since the assignment statement created a new window
object.

237
03.12@01:35

Fig. 03.12.03: Debugging window Assignment Statement Evaluated

Below the code is the “Stack Data” panel. This panel has two bolded words “locals” and
“globals”, and each has a number of words beneath that are bookended with underscores.

03.12@01:55

Fig. 03.12.04: Stack Data Panel

238
The locals section of this panel serves as the namespace for your program. As we trace the
program, each assignment statement will affect one identifier. Unfortunately, the names of
built-in functions and imported names are not added to this locals section, even though they are
added to the namespace.

At the bottom of the “locals” section is the new identifier ‘window’. If I click on the arrow beside
it, you see the namespace of the window object. The window has a number of attributes,
including the background colour, the font_name and the font color.

03.12@02:25

Fig. 03.12.05: window and Attributes

The highlighted statement is a method call that sets the font name. When I step over it you can
see that font attribute has changed from the empty string to 'couriernew'. The next three
statements in the code bind the font size, font color and background attributes of the window.
When I step over these statements I can see that the font color attribute changes from white to
green and that the background color attribute has remained the same. This attribute did not
change because it was actually rebound to the same black string object.

239
03.12@02:55

Fig. 03.12.06: window with Rebound bg_color, font, and font_color Attributes

I’ll collapse the window identifier since no other program statements affect its attributes. The
next statement is another assignment statement. Stepping over this assignment adds line_y to
the locals namespace and binds it to zero. The next statement adds string_height and binds it to
19. The string height is 19 pixels, even though the font point size is 18. The string height is
usually a few pixels larger than the point size.

240
03.12@03:24

Fig. 03.12.07: locals with line_y and string_height Attributes

I’ll step over the next three statements since they don’t update the locals namespace.

The next statement is an assignment statement that rebinds line_y to a different object. You can
see that line_y is bound to 0 and string height is bound to 19. When I step over this statement
you can see that line_y is re-bound to 19 which is the sum of 0 and 19.

241
03.12@03:56

Fig. 03.12.08: line_y Rebound to 19

I’ll step over the next few statements until I come to the next assignment statement that rebinds
line_y. Stepping over this assignment statement rebinds line_y to 38 which is the old binding 19,
plus the string height, also 19.

Hopefully you have a better understanding of how the debugger can be used to track your
identifier bindings.

Now that we’re done tracing, let’s talk about the quality of the code.

In the first version of Hacking we had one software quality test. Your program must have a
descriptive comment at the beginning, and it must have inline comments at the start of each
logical block of code.

242
03.12@04:47

Fig. 03.12.08: Hacking Version 2 Software Quality Tests

For version 2, we’re adding a second Software Quality test that deals with the names of your
identifiers. There are two tests for your identifiers. The names you use must describe the
objects they are bound to. For example, using the identifier “s” would not be very descriptive of
the string height object.

The next test assesses the format of your identifiers. For now, all your identifiers should be
lowercase with underscores between words. For example, string underscore height is
acceptable, but stringheight is not acceptable. This is a Python style convention that allows
anyone who views your code to more easily understand what you have written. Some imported
type names, such as Window, may contain uppercase letters, but we will discuss this
convention later.

In this lesson we reflected on using the assignment statement to bind identifiers to objects.
Congratulations Version 2 of Hacking is complete.

243
Module 04: Hacking Version 3

04.01 Introduction: Hacking V3 Solution Issues

Let’s explore solution improvements for the second version of Hacking that we will include in the
third version.

04.01@00:15

Fig. 04.01.01: Game Creation Diagram Identify Solution Issues

Visually, the game looks like the final version, but it is missing a lot of functionality. For example,
there is only one "path" through the game. No matter what password the player enters, a failure
outcome message is displayed. We need a way to follow alternate paths in Hacking. The game
should select an outcome based on the password the player enters.

Comparing the player’s guess to the correct password is a computation. Our algorithms and
programs must be able to perform selection based on the results of computations. The
language constructs that provide this ability are called control structures. A control structure
dictates when an algorithm step or program statement is performed, including whether it’s
performed at all!

Version 3 of Hacking will focus on the specific control structure called selection. Introducing
selection to your solution will result in changes to your description, test plan, algorithm, and

244
code. By observing and playing version 3, you can identify the appropriate outcome, based on
whether a correct or incorrect answer is entered.

In this version, you will modify the description so that it reflects the different game outcomes.
This will involve a new kind of attribute called a selection attribute. A Selection Attribute is a
temporal attribute that is used as a response to a choice in the game, and will usually start with
“If”. For example, now that the game compares correct and incorrect guesses to produce a
specific outcome, you may use the selection attribute “If the guess is incorrect” to describe the
object "The game displays a failure outcome."

Whenever you use selection to create alternate paths in a game, you need to test each path. In
the first test plan for Hacking, you added tests to consider the two possible player actions:
entering a correct password and entering an incorrect password.

In version 1, there was only one path. No matter what guess the player entered, a failure
message was displayed. Now that there are multiple paths in the game, you need to update
your test plan to indicate that the player choice determines one of two possible outcomes.

Both of these outcomes must be tested in a separate program run. Both the tests and the
implementations of these outcomes are independent cases. This is an example of a new
problem decomposition technique called Case-based decomposition, where multiple
independent solution parts are created separately and joined together to form a complete
solution.

04.01@02:40

Fig. 04.01.02: Problem Solving Diagram Case-Based Decomposition

245
Observe Hacking version 3 now so that you can create your version 3 description and test plan.

246
04.02 Demo: Observe Hacking V3

The game starts. So far, it looks the same as Hacking version 2.

04.02@00:16

Fig. 04.02.01: Hacking Version 3 Start State

247
04.02@00:16

Fig. 04.02.02: Hacking Version 3 Start State (Video Screenshot)

I will enter the correct password, HUNTING.

04.02@00:23

Fig. 04.02.03: Hacking Version 3 Success State

248
04.02@00:23

Fig. 04.02.03: Hacking Version 3 Success State

This time, the game displays a different outcome! After the correct guess, it displays a success
message and then prompts me to press enter.

Play the game a few times yourself. Make sure you see both outcomes. Note the difference
between them so that you can complete your description and functional test plan.

249
04.03 Create Algorithm for Hacking Version 3

To create the algorithm for Hacking version three, you will modify your version two algorithm.

Version three includes multiple paths through the game. The outcome of the game depends on
player input. In your previous algorithms, there was only one path and one possible outcome.
Adding the ability to select a game outcome based on the player’s actions requires a control
structure. Recall that a control structure dictates when an algorithm step is performed, including
whether it is performed at all. Each control structure you learn about will have its own shape in
the algorithm builder. Selection is represented by a diamond. I will show you a small example to
illustrate how the diamond is used.

I’ll create an algorithm for wearing a sweater. First, I'll add a diamond. I need to select the action
of wearing a sweater, based on some computation. A computation used to select an outcome is
often called a condition. The condition for selection is placed in the diamond. The condition for
my algorithm will be “the temperature is less than 10 degrees Celsius”, so I’ll choose
“temperature less than 10 degrees” from the vocabulary. The condition “the temperature is less
than 10 degrees Celsius” will either be true or false. Either the temperature is less than 10
degrees Celsius, so the condition is true, or the temperature is greater than or equal to 10
degrees Celsius, so the condition is false.

04.03@01:21

Fig. 04.03.01: Sweater Algorithm Selection Control Structure

250
Every selection control structure in the algorithm builder has two rectangles associated with it.
The “true” rectangle contains the algorithm step that is performed if the condition in the diamond
is true. Conversely, the “false” rectangle contains the algorithm step that is performed if the
condition in the diamond is false. The condition you choose for the selection control structure
must always evaluate to true or false, so that you can use it to select one of the true or false
rectangles.

There must always be an algorithm step in the true rectangle. It doesn’t make sense to include
selection in your algorithm if there is no step to select! However, there doesn’t always have to
be an algorithm step in the false rectangle. For example, in the algorithm for wearing a sweater,
if the temperature is less than 10 degrees, I will wear a sweater. So, I put “wear sweater” in the
true rectangle. If the temperature is greater than or equal to 10 degrees, I won’t do anything.
There is nothing I need to put in the false rectangle.

04.03@02:28

Fig. 04.03.02: Sweater Algorithm Control Structure True Condition

A selection control structure usually gets its own panel. If more than one algorithm step is
required in the true or false case, then you can expand the true or false rectangle to use its own
panel. You could expand both if necessary.

A selection control structure supports case-based problem decomposition, since it allows each
part of the solution to be created independently.

251
04.03@02:56

Fig. 04.03.03: Problem Solving Diagram - Case Based Decomposition

Let's modify a copy of the Hacking version 2 algorithm to create the version 3 algorithm.

You already have the steps needed to display the failure outcome message. You could create a
“display success outcome” panel that is almost the same as the “display failure outcome” panel,
except for the outcomes that are displayed. You could then select which of these algorithm step
sequences to use based on the user guess.

There is a software development principle called Don't Repeat Yourself or DRY.


If you find yourself writing the same algorithm steps more than once, you should stop and see if
you can change your algorithm to write the common steps only once.

In this case, the only difference between these algorithm steps are the messages being
displayed, display failure outcome line 2 and 3 versus display success outcome line 2 and 3.
Instead, you could create a different outcome message, for failure or success, depending on the
player guess. Then just display this outcome message. This is an example of solution
generalization since the common parts of the success and failure outcomes are generalized and

252
used in one place.

To complete the version 3 algorithm, modify "display failure outcome" to be the more general
"display outcome". Then, add a step called “create outcome” prior to the “display outcome” step.

04.03@04:18

Fig. 04.03.04: Hacking Version 3 Algorithm Updated End Game Panel

Finally, expand create outcome and use selection to create the appropriate outcome message.

253
04.04 Python If Statements and Booleans

In this video we introduce a new kind of statement called an if statement, a new Python type
called bool, which is short for Boolean, and three individual tokens: newline, indent and dedent.

Consider a map that also contains x and y axes for map coordinates, where x increases to the
right and y increases upwards. When a user enters an x and y value, a program should display
whether the coordinates are in the North West, North East, South West or South East quadrant.
The solution to this problem depends on whether the x and y values are each greater than zero
or less than zero. If the x value is less than zero, the quadrant is in the west and if the x value is
greater than zero the quadrant is in the east.

Similarly a y value less than zero designates a South quadrant and a y value greater than zero
designates a North quadrant. So far, all the Python statements we have seen have been
evaluated in order from first statement to last statement. To solve this problem we need our
program to display or not display a combination of these words: north, south, east, or west.

To conditionally evaluate a statement, we introduce a new kind of statement called an if


statement. An if statement can either evaluate a statement or skip it. We can use an if statement
to either display a direction or not.

Here is the program solution.

04.04@01:50

Fig. 04.04.01: Mapping Program Solution

254
I will run the program and enter 100 for x and minus 50 for y. The program displays South and
East, which is the correct answer.

04.04@02:08

Fig. 04.04.02: The Program Finds the Correct Answer

An if statement is a statement that has another statement inside of it. For example, the first If
statement starts on line 5 and finishes on line 6, where line 6 is a print function call expression
statement inside the if statement. A statement that includes another statement is called a
compound statement. A statement that does not include another statement is called a simple
statement. The if statement is a compound statement and the expression, assignment and
import statements are simple statements.

I will modify the syntax diagram for statement to include simple and compound statements and
add syntax diagrams for these statement categories. The newline token at the end of each
simple statement is a new token that will be discussed later in this lesson.

255
04.04@03:08

Fig. 04.04.03: Modified Statement Syntax Diagram

In Python, it is possible to write some uncomplicated compound statements on a single line.


However, in this course, all compound statements will be written on more than one line. Here is
a simplified syntax diagram for the if statement.

256
04.04@03:16

Fig. 04.04.04: if Statement Syntax Diagram

The keyword token, if, the expression, and the colon together are called a header and the
expression is often called a condition.

A suite is an indented block of statements that starts on a new line. Here is the syntax diagram
for suite:

257
04.04@03:33

Fig. 04.04.05: suite Syntax Diagram

Newline, Indent and Dedent are the three individual Python tokens that are not in any of the
token categories: operator, identifier, delimiter, literal or keyword. These tokens are used to
separate statements and to delimit where a suite starts and where it ends.

When the lexical analyser encounters an end-of-line character, created by pressing the enter
key, it creates a newline token to separate statements. To mark the start of a suite, I place a
newline after the colon and then use whitespace at the start of the next line. The lexical analyser
translates all of this whitespace into a single indent token, regardless of how much whitespace
occurs at the start of this line.

A dedent token identifies the end of a statement suite. The statement following the suite must
use the same indentation as the header line of the if statement that contains the suite. The
lexical analyser translates this indentation to a single dedent token.

All four of the if statements in this program have valid syntax.

For example the if statement on lines 5 and 6 is valid since the lexical analyser creates this
sequence of tokens for lines 5 through the first token of line 7. First is the keyword if, followed by
the three tokens that form an expression: identifier "y coord", operator less than and literal zero.
The if header ends with a delimiter colon. The suite starts with a newline, followed by an indent.
In this case, the statement is a simple statement, an expression statement.The expression
statement consists of: the identifier print, the delimiter left paren, the literal string south, and the

258
delimiter right paren. The simple statement ends with a newline. The suite ends with a dedent,
which ends the if statement.

The if statement uses a new Python type called bool, which stands for boolean. There are only
two objects whose type is boolean and they are referred to as true and false. Many Python
expressions evaluate to one of these objects. For example, the expression on line 5 of the
program, "y coord" less than 0" evaluates to the true object or the false object, depending on
what int object that "y coord" is bound to.

For convenience, Python uses two keywords True and False to refer to the two boolean objects.
Even though they are keywords, they behave like literals. When the interpreter encounters one
of these keywords, it uses the appropriate boolean object in memory. Recall that if, import and
from are also keywords, but they are used as delimiters to punctuate a statement. They are not
replaced by objects.

Here are the semantics of the if statement…

04.04@06:48

Fig. 04.04.06: if Statement Semantic Rules

and here are the semantics of a suite:

259
04.04@06:52

Fig. 04.04.07: suite Semantic Rules

I will apply the if statement semantics to the if statement on lines 5 and 6 of the program. When
line 5 is evaluated the namespace contains prebound identifiers and the four identifiers bound in
the assignment statements on lines 1 through 4. In the if statement semantics, step 1 evaluates
the expression "y coord" less than zero. Since "y coord" is bound to negative 50, the
expression evaluates to the boolean object true. In Step 2 , since the condition object is true, the
suite is evaluated. The suite contains a single expression statement that calls the print function
to display 'South'. The interpreter is done evaluating the if statement on lines 5 and 6. The
interpreter next evaluates the statement that starts on line 7.

I will apply the if statement semantics to the if statement on lines 7 and 8. In step 1, the
expression "y coord" greater than zero is evaluated. Since "y coord" is bound to negative 50 the
expression evaluates to the boolean object, false. In step 2, since the condition object was false,
the suite is not evaluated. The interpreter next evaluates the statement that starts on line 9.
Similarly, the interpreter evaluates the if statement on lines 9 and 10 to skip the print ‘West’
function call and evaluates the if statement on lines 11 and 12 to evaluate the print ‘East’
function call.

The program is done.

If we run the program and enter zero for x and zero for y there is no output. This is because the
expressions in all four if statements evaluate to False, so no statement in any statement suite is
evaluated.

260
The syntax and semantics of an if statement supports multiple statements inside the suite of an
if statement. For example if the program is changed to and run with input 100 and minus 50,
the output contains both South and Bottom in addition to East, since both statements in the
suite on lines 6 and 7 and the single statement in the suite on line 13 are evaluated.

Notice that there is exactly one indent token at the start of a statement suite and one dedent
token at the end of a statement suite. The lexical analyser does not create indent or dedent
tokens between the statements in a suite. You should use a standard number of spaces when
you indent. If you do not use the same number of spaces for all indents you will probably see
this indentation error.

This lesson introduced the syntax and semantics of a simple if statement as well as the boolean
type and three individual tokens: newline, indent and dedent.

261
04.05 Python - Elif and Else Clauses

In this lesson we generalize the if statement to include optional elif clauses and an optional else
clause. We also describe what happens when an if statement expression evaluates to a
non-boolean object.

In this lesson we describe what happens when an expression in an if statement evaluates to a


non-boolean object. We also generalize the if statement to include optional elif clauses and an
optional else clause.

What happens if the expression in an if statement does not evaluate to a boolean? Let's run this
program to find out. Hmm. It printed 'North' even though 'hello' is not the True object. Let's
check if 'hello' is somehow equal to True.

No 'hello' is not equal to True but the If statement treated 'hello' as if it was True in its condition.
In Python, when the expression in an if statement is evaluated to an object, the object is always
interpreted as a boolean even if it is not a boolean. Most objects are interpreted as true even
though they are not equal to True. Only a few objects are interpreted as False, including: the
False object, the empty string, the int object 0, the float object 0.0, the NoneType object, and a
few more objects whose types we haven’t encountered yet. Since the string object, ‘hello’, was
interpreted as true, the suite was evaluated and printed ‘North’.

In Computing Science, an expression that evaluates to a boolean is called a condition. Since all
objects are interpreted as true or false in a Python if statement’s expression, regardless of their
type, the if expression is called a condition.

If I want to evaluate one suite of statements, if a condition is true, and a different suite if it is
false, I can use the else form of an if statement. For example, we can rewrite the quadrant
program from the previous lesson as this program.

262
04.05@01:52

Fig. 04.05.01: Modified Map Program

This program behaves almost the same as the last program, except now when 0 is entered as
an x coordinate, it prints East and when 0 is entered as a y coordinate it prints North. I will run
the program and enter 100 for x and minus 50 for y. The program displays South and East,
which is the correct answer.

When an else keyword is used in an if statement, we say that the if statement has two clauses,
the if clause and the else clause. The if clause has a condition and a suite of statements. The
else clause has a suite of statements, but no explicit condition.

Here is the revised syntax for an if statement that includes an optional else clause.

263
04.05@02:29

Fig. 04.05.02: Modified if Statement Syntax Diagram

The syntax for suite does not change.

The program contains two syntactically valid if statements, each with an else clause.

264
Here are the revised semantics for an if statement that includes the optional else clause.

04.05@02:44

Fig. 04.05.03: Modified if Statement Semantic Rules

The semantics of a suite do not change.

I will apply the revised if statement semantics to the if statement on lines 5 through 8. When line
5 is evaluated the namespace contains prebound identifiers and the four identifiers bound in the
assignment statements on lines 1 through 4. In step 1, the expression "y coord" less than zero
is evaluated. Since "y coord" is bound to negative 50 the expression evaluates to the boolean
object true. In step 2, since the condition object is true, the if suite is evaluated. The suite
contains a single expression statement that calls the print function to display 'South'. In step 3,
since the condition object is not false, the else suite is not evaluated.

The interpreter next evaluates the if statement on lines 9 through 12. In step 1, the expression
"x coord" less than zero is evaluated. Since "x coord" is bound to 100 the expression evaluates
to the boolean object, false. In step 2, since the condition object is false, the if suite is not
evaluated. In step 3, since the condition object is false, the else suite *is* evaluated. The suite
contains a single expression statement that calls the print function to display East. The program
is done.

There is an even more general form of the if statement that uses elif clauses. Elif clauses can
be used to check multiple conditions in the same if statement. For example, if I want to identify

265
points on the axes, as well as in the quadrants, I can change the program to this and run it using
coordinates 0 and 100 so the answer is "North On y axis".

Here is the revised syntax for an if statement that includes optional elif clauses…

04.05@04:44

Fig. 04.05.04: Modified if Statement Syntax for the Optional elif Clause

This program contains two syntactically valid if statements, each with one elif clause and one
else clause.

Here are the revised semantics for an if statement that includes optional elif clauses as well as
the optional else clause.

266
04.05@05:23

Fig. 04.05.05: Modified if Statement Semantic Rules for the Optional elif Clause

These semantics mean that if there is an else clause, exactly one suite of the if statement will
be evaluated. The rest of the suites will be skipped. If there is no else clause, and at least one of
the conditions is true, exactly one of the suites will be evaluated. If there is no else clause and
none of the conditions are true, no suite will be evaluated.

I will apply the revised if statement semantics to the if statement on lines 5 through 10. When
line 5 is evaluated the namespace contains prebound identifiers and the four identifiers bound in
the assignment statements on lines 1 through 4. In step 1, the expression "y coord" less than
zero is evaluated. Since "y coord" is bound to 100 the if clause expression evaluates to false so
the next expression is evaluated. The elif clause expression evaluates to true since "y coord" is
greater than zero. In step 2, since the elif condition object was true, the suite of the elif clause is
evaluated, which prints 'North'. In step 3, since a condition result object was true, the suite in the
else clause is not evaluated. The interpreter is done evaluating the if statement on lines 5
through 10.

The interpreter next evaluates the if statement on lines 11 through 16. In step 1, the expression
"x coord" less than zero is evaluated. Since "x coord" is bound to zero the expression evaluates
to the boolean object, false. Since the if expression result object is not true, the next expression
is evaluated. The expression "x coord" greater than zero is evaluated to obtain false. There are
no more expressions to evaluate in the if statement. In step two, since none of the expressions
evaluated to true, none of the corresponding suites are evaluated. In step 3, since none of the

267
condition objects was true and there is an else clause, the else suite is evaluated, which prints
"On y axis". The program is done.

In this lesson we described what happens when an expression in an if statement evaluates to a


non-boolean object. We also generalized the if statement to include optional elif clauses and an
optional else clause.

268
04.06 Python - Keyword Operators, Short Circuit Evaluation, Unary Expressions, and
Operator Precedence

In this lesson we introduce four concepts. First, we describe keywords that behave as
operators. Second, we describe a special evaluation technique used by boolean operators
called short circuit evaluation. Third, we introduce a new kind of expression called a unary
expression. Finally, we describe the operation order Python uses for computations that contain
more than one operator.

Recall that operator tokens, such as "plus" and "less than" are used in binary expressions to
apply an operator function to two operands. There are 19 operator tokens.

Some Python keywords can also be used as operators. Let's look more closely at all of the
keywords. You have seen that some keywords, such as “from”, “import” and “if” are used as
delimiters to punctuate statements. In fact, 26 of the 33 keywords are used as delimiters to
separate different parts of statements.

You have also seen that some keywords, such as True and False are used as literals that are
bound to specific objects. In fact, None is also used as a keyword literal and it is bound to an
object whose type is NoneType, which is returned by function calls that don't really need to
return a meaningful object.

The other four keywords, "and", "is" "not" and "or" are used as operators and the keyword "in"
can actually be used as an operator or delimiter depending on context.

269
04.06@01:32

Fig. 04.06.01: keyword Table

I will revise the syntax diagram for binary operator, by adding the keyword operators that can be
used as binary operators:

04.06@01:37

Fig. 04.06.02: Updated binary operator Syntax Diagram

270
Notice that in addition to "and", "in" "is" and "or", there are two keyword operator pairs, "not in"
and "is not" that can each be used as a single binary operator.

I have added some new binary operators. Recall the semantic rule for binary operators:

04.06@02:02

Fig. 04.06.03: Semantic Rule for Binary Expressions

The operator function "and" uses a technique called short circuit evaluation, where the result
object may not depend on the second operand.

Here are the semantics of the "and" operator function.


1. if the first operand is interpreted as false, the first operand is the result object
2. Otherwise evaluate the second expression as the the result object

In step 1, the first operand is False, so this first operand object, False is returned. Step 2 is
skipped so the second expression is not even evaluated. If the second expression was
evaluated it would have displayed a semantic name error, since I did not bind the identifier
unbound to any object.

The "and" operator function has another interesting property. In step 1, the first operand is the
empty string, which you may recall is interpreted as false in an if statement condition. The "and"
operator function semantics interpret non-boolean objects the same way, so the empty string is
interpreted as false. Therefore, the empty string is the result object. If I had used 0 or 0.0

271
instead of the empty string, the expression would evaluate to 0 or 0.0 respectively. Again step 2,
is skipped so the second expression is not evaluated and no semantic name error is displayed.

If the first operand is not interpreted as False, the second expression is evaluated. For example
if I evaluate 'hello' and 'goodbye', the interpreter returns 'goodbye'. In step 1, the first operand is
the string 'hello', which is interpreted as true. Since it is not interpreted as false, the second step
is applied. In step 2, the second expression evaluates to the string 'goodbye', which is used as
the result object.

The "or" operator function also uses short circuit semantics.


1. if the first operand is interpreted as true, the first operand is the result object
2. Otherwise evaluate the second expression as the result object

Notice that neither the operator token, tilde, nor the keyword operator not, by itself, can be used
as a binary operator. Instead both are unary operators that act on a single operand. Here is the
syntax diagram for a unary expression:

04.06@05:08

Fig. 04.06.04: Unary Expression and More General Expression Syntax Diagrams

and here is a more general syntax diagram for expression that now includes unary expressions.

The plus and minus operator tokens can be used either as binary or unary operators. The
semantics of unary expressions is similar to the semantics of binary expressions except that
there is only one operand to evaluate.

272
For example, I will apply the unary expression semantics to minus 27. In step 1, the expression
is the literal integer 27, which evaluates to the int object, 27. In step 2, the unary operator minus
and the type int are used to identify the numerical negation function, which reverses the sign of
its integer operand object. In step 3, the numerical negation function is applied to the int object
27 to obtain the int object, -27. Notice that -27 is not a literal integer. It is a unary expression that
applies the minus operator to an integer object to obtain a result object.

If more than one operator appears in an expression, the Python interpreter must select an order
for the operations.

For example, in the expression three plus four star five there are two operators, plus and star. If
the Python interpreter applies these operators in order from left to right, then 3 plus 4 is 7 and 7
times 5 is 35. 35 is not the correct answer! 23 is. Python applies the star operator before
applying the plus operator, just like Mathematics.

04.06@06:56

Fig. 04.06.05: Python Operator Precedence Table

Python has an operator precedence table that defines the order that should be used to apply
multiple operators in the same expression. Operators that are below in the table have higher
precedence and are applied before operators that are above in the table. For example star is
applied before plus since it is below plus in the table. That is why "three plus four star five"
evaluates to 23 instead of 35.

273
You should recognize many of the operators in this table, such as the operator tokens, "plus",
"times" and "less than", and the keyword operators "and" and "or".

In this lesson we described keywords that behave as operators. We also described a special
evaluation technique used by boolean operators called short circuit evaluation, a new kind of
expression called a unary expression, and what operation order Python uses for computations
that contain more than one operator.

274
04.07 Program Hacking Version 3

We are at the Create Program task in the Game Creation Process.

04.07@00:13

Fig. 04.07.01: Game Creation Diagram Create Program Subtasks

The first step in programming Hacking version 3 is adding comments that correspond to the new
algorithm steps. I will add a placeholder comment for the selection diamond, but you will replace
it with the header of the if statement, when you translate the algorithm. I will use "check guess
equals password" as a comment for the "guess equals password" diamond. When you add the if
header, "if guess equals HUNTING", you should remove the comment since it will be
redundant.

We use Python if statements to implement Selection. The true box from your algorithm
corresponds to the statement suite of the if clause. The false box corresponds to the statement
suite of the else clause. You will create a comment for the "create success" step and another
comment for the "create failure" step, since every rectangle in your algorithm should have a
comment in the code.

275
04.07@01:04

Fig. 04.07.02: Algorithm Comments Lines 116-119

In the next activity, you’ll use selection to choose which outcome message should be displayed.
Use good identifier names for your outcome strings. I’ll choose one for you, to get you started.
The first line of the outcome message is always the player’s guess. The second line will be
either “EXITING DEBUG MODE” for a successful guess, or “LOGIN FAILURE - TERMINAL
LOCKED”, for a failed guess. Let’s call the second line, outcome_line2, since it is general
enough to apply in either case.

In version 2, it was sufficient to just display the literal string “LOGIN FAILURE - TERMINAL
LOCKED”. In this version, the appropriate string must be referenced by outcome_line2. I’ll
update the code to use this identifier.

I'll replace the literal “LOGIN FAILURE” by the identifier outcome_line2 in both the computation
that calculates its x-coordinate and in the draw_string function call. This block of code will now
display whatever string outcome_line2 is bound to.

276
04.07@02:09

Fig. 04.07.03: outcome_line and outcome_line2 Identifiers Added

Note that if I run the program now, it will produce an error, since outcome_line2 is unbound.

You will create the if statement, bind outcome_line2, and translate the rest of the display
outcome steps of the algorithm in the next activity. If you have trouble, review the if statement
programming language lessons.

277
04.08 Review Code for Hacking Version 3

Welcome to the third Reflection! Here is the code for version 3.

04.08@00:13

Fig. 04.08:01: Game Creation Diagram Review Code Subtask

The purpose of this review is to show you how an if statement is evaluated. The if statement is
near the bottom of the program. Instead of stepping over each statement to reach the if
statement, I am going to show you a new debugging technique. I’ll add a breakpoint beside the
“if”, so I don't need to evaluate one statement at a time. This adds a red dot beside the line
number as a visual cue.

278
04.08@00:50

Fig. 04.08:02: Breakpoint Added Line 117

A breakpoint is a debugging feature that stops the debugger at a desired line and waits for
another debugging action. This allows you to use the debugger without tracing each line.

Now, I’ll press the bug button in the toolbar which runs the program normally until it reaches a
breakpoint or a statement that requires user input. Hacking displays the header, password list
and password prompt, and then pauses to allow me to input a password. Normally, if the
program is running and I enter a password, the game will immediately display the outcome.

279
04.08@01:04

Fig. 04.08:03: Hacking Window Displayed

I will input HUNTING.

The debugger evaluates "clear window" and stops when it reaches the breakpoint at the if. It
pauses to let me decide what I want to do next.

The if statement will check if my guess is equal to the secret password. Notice that in the Stack
Data panel, guess is bound to HUNTING.

280
04.08@01:29

Fig. 04.08:03: HUNTING in Stack Data

Since guess and “HUNTING” are equal, when I press the step over button, the next statement
that is highlighted is the outcome_line2 assignment statement in the if clause. I’ll press the step
over button twice more until the prompt assignment statement is highlighted. Notice that in the
Stack Data panel, outcome_line2 and outcome_line3 have been added to the local namespace
as identifiers bound to the success strings.

281
04.08@01:49

Fig. 04.08:04: outcome_line2 and outcome_line3 Bound in Stack Data

The next time I press the step over button, the debugger will not highlight the “else” clause,
even though it is on the next line. At most, one suite in an if statement is evaluated. Whenever a
condition of an if statement is true, the suite associated with that condition is evaluated. All other
conditions and suites are ignored. So, when I press the step over button the debugger
completely skips the else clause and its suite, and highlights the x_space assignment
statement.

Let's look at the second case of our case-based decomposition by tracing the program with an
incorrect password. I’ll stop the trace with the stop button and press the bug button again. This
time when Hacking reaches the password prompt, I’ll input an incorrect guess.

282
04.08@03:03

Fig. 04.08:05: guess Bound to PROVIDE in Stack Data

The if statement is highlighted. In the Stack Data panel, guess is bound to the incorrect
password I entered. Since the guess is not equal to "HUNTING", the condition statement
evaluates to false. When I press the step over button again, the debugger skips to the first
statement of the “else” clause suite, since else does not need to be evaluated.

Now let’s talk about the software quality tests for this version of Hacking. Previously we
introduced how to make good comments and descriptive identifiers.

The new Software Quality test section is called “Code Reflects Design.” There are two tests for
“Code Reflects Design.” The first is that each rectangle in an algorithm translates to a sequence
of simple statements in the code. The second test is that there is a one-to-one correspondence
between control structures in the algorithm and corresponding control structures in the code.

A one-to-one correspondence between algorithm control structures and code control structures
means that there is exactly one control structure in the code per control structure in the
algorithm. An algorithm rectangle can be translated into multiple lines of code, but an algorithm
diamond is always translated to one line of code in a program.

Your code should reflect your design so that changes to the algorithm in the next version can be
implemented by translating only the sections of the algorithm that have changed.

Congrats! You’re finished your third version of Hacking!

283
MODULE 5: HACKING VERSION 4 & 5

05.01 Solution Issues in Hacking Version 3

Welcome to Hacking version 4! This version will be different from the previous three, since you
will only be focusing on improvements to the quality of your code, instead of introducing new
game features.

05.01@0:18

Fig. 05.01.01: Identify Solution Issues Subtask in Creat Game Diagram

You will address two solution issues: duplicate code and duplicate data.

Here is the solution code for Hacking version 3.

284
05.01@0:32

Fig. 05.01.02: Solution Code for Hacking Version 3

There are many duplicated statements. Displaying the header, the passwords, and the outcome
messages require the same four lines of code over and over again, with only tiny differences.

At first, this seemed convenient! You had the solution to display a string and you could reuse it
to display each subsequent string by copying and pasting. However, this resulted in almost two
hundred lines of code!

This is another example of violating the "don't repeat yourself" software development
principle. In general, if you find yourself writing the same code statements more than once, you
should change your program to write the common statements only once.

285
05.01@1:21

Fig. 05.01.03: Adjacent Duplicate Statement Groups

We call consecutive identical or very similar statements adjacent duplicate statement groups.
In this version of Hacking, you will learn how to eliminate adjacent duplicate statement groups
using a new construct called a repetition control structure or loop. A loop is a sequence of code
statements that will be repeatedly evaluated until a specific condition is met. Using repetition
control structures makes your code shorter, so it is easier to write and read. In this version, you
will use these control structures to replace the almost 200 lines of code by fewer than 90.

286
05.01@1:58

Fig. 05.01.04: Control Abstraction Subtask in the Problem Solving Diagram

Using a repetition control structure to replace duplicate lines is a new form of abstraction called
control abstraction. Control abstraction replaces default sequential program flow with a control
structure to reduce repetition.

287
05.01@2:14

Fig. 05.01.05: Duplicate Data

The second solution improvement is eliminating duplicate data. For example, the literal float 0.3
is duplicated 23 times in the program. This is an issue since, if I want a shorter delay, I need to
change the argument in 23 individual sleep function calls. Similarly, if I want to change the x
coordinates of the passwords, I need to change the second argument of 13 different draw_string
function calls.

If I want to change all of the sleep delays, I could just use a single find-and-replace action on all
occurrences of 0.3. That’s pretty simple. However, if I try to change the x coordinate of the
passwords using find-and-replace to change zero to some other value, it will not only change
the 13 calls to draw_string but 36 other zeros in the program.

Instead, I must have some way of specifying that the intent of zero in the password draw_string
calls is different than the intent of the other 36 zeros. To solve this problem, I can use an
identifier to represent the x coordinate of the passwords and bind it to zero. Now, to change the
x coordinate of the password, I only need to change the one assignment statement that binds it.

288
05.01@3:28

Fig. 05.01.06: Replacing Repeated Literals with an Identifier

In general, if a literal is used multiple times for the same purpose, an identifier should be bound
to it, so that the literal can be changed in one place. Can you find other repeated literals in the
version 3 code that should have an identifier bound to them?

Since the changes you are going to make in this version only affect code quality, you don't need
to observe or play Hacking version 4. There are also no changes to the description or test plan.

289
05.01@3:47

Fig. 05.01.07: Create Algorithm Subtask in the Create Game Diagram

You just completed five tasks in the game creation process in only a few seconds! Congrats!

After coding, don't forget to use your previous test plan to ensure that the game functionality is
still correct.

290
05.02 Create Algorithm for Hacking Version 4

Let’s take a look at the game creation process diagram. You are working on the Create
Algorithm task.

05.02@0:16

Fig. 05.02.01: Create Algorithm Subtask in the Create Game Diagram

We’re introducing a new control structure in version 4, called definite repetition or definite
iteration. Recall that a control structure controls when an algorithm step is performed. Definite
repetition performs an algorithm step, or sequence of steps, over and over again a specific
number of times. Since the step is performed repeatedly, it’s called repetition. Definite repetition
means that the number of times the step is repeated is prescribed before the repetition starts.

For example, while making a cake, if the recipe tells you to stir the batter 20 times, that is
definite repetition. If the recipe tells you to stir the batter until there are no lumps, that is not
definite. You don't know how many times you will stir before you stop mixing.

Think of definite repetition as repeating an algorithm step for each item in a sequence. If you
have a basket of clean laundry, for each shirt in the basket, you will put the shirt away. In the
cake-mixing example, you stir the batter once for each number in the sequence 1 to 20.

291
05.02@1:28

Fig. 05.02.02: Algorithm Builder FOR Icon

Definite repetition is represented in the algorithm builder by a for icon. The for icon has two
words in it already: for and in. As well, there is a regular rectangle connected to the for icon by
an arrow. The step in the regular rectangle is the step that will be repeated. The for icon
indicates how many times that step should be repeated.

292
05.02@1:28

Fig. 05.02.03: FOR Icon - The step in the rectangle will be the step that is repeated

I will add a definite repetition control structure to the algorithm. To complete the for icon, you
need to select two items from the vocabulary. The first item is the target element that you want
to use in the repeated step. In the laundry example, this would be the shirt you want to put
away, so I will add “shirt” from the vocabulary. The second item is the sequence that contains
the target element. In the laundry example, this is the laundry basket, so I will add it.

293
05.02@2:17

Fig. 05.02.04: Choosing Target Element to be Used in Repeated Step

05.02@2:26

Fig. 05.02.05: Choosing Sequence that Contains the Target Element

My for icon contains “for shirt in laundry basket”. Now, I will add “put away shirt” to the regular
rectangle under the for icon.

294
05.02@2:39

Fig. 05.02.06: Choosing Step to be Repeated

This algorithm will perform the step “put away shirt” for every shirt in the laundry basket. Each
time the step repeats, the next target shirt in the sequence is used.

In the cake mixing example, the target is a stir and the sequence consists of the numbers one to
twenty. For each stir in the sequence one to twenty, you stir the batter.

You can expand the regular rectangle using a new panel, if you want to repeat a sequence of
steps. I am expanding the repeated step "put away shirt" by creating a sequence of steps “fold
shirt”, “open dresser drawer”, “place shirt”, “close dresser drawer”.

295
05.02@3:24

Fig. 05.02.07: Expanding Put Away Shirt Step

I could do something even more complicated and add a selection control structure to determine
whether each shirt should be folded or ironed!

05.02@3:33

Fig. 05.02.08: Adding Selection Control Structure in Expended Put Away Shirt Step

296
I'll now use definite repetition in Hacking Version 4.

05.02@4:10

Fig. 05.02.09: Adding For Icon for Outcome Line

In your previous algorithm, “display outcome” required six rectangles, one for each outcome
line. I am going to delete all six rectangles and use definite repetition instead. Now, I’ll fill in the
for icon by picking a target and sequence. The game displays each outcome line, so “outcome
line” is the target element. Since each outcome line is part of the complete outcome, the
outcome is the sequence.

The for icon says “for outcome line in outcome”. Now I need to fill in the step I want to repeat. I
want to display each outcome line, so I could add “display” and “outcome line” to the regular
rectangle, but this isn’t quite right. The outcome lines are specifically positioned in the center of
the window, so I need to add “display” and “centered outcome line” to the rectangle.

297
05.02@4:35

Fig. 05.02.10: Specified Step to be Repeated

Now it’s your turn! Replace the steps for display header and display password list with definite
repetition control structures. Add one extra step in the display password list panel that chooses
the correct password from the list.

298
05.03 Python Sequences and Subscription

In this video, I introduce a category of types called sequence types. This category includes the
string type and other types which will be introduced in future lessons. I also introduce the
subscription expression, which is a mechanism for referring to elements of a sequence.

05.03@1:14

Fig. 05.03.01: Explanation of Sequence

A sequence is a finite ordered group of elements where each object has a numerical index
between 0 and one less than the number of elements in the sequence. There is no sequence
type in Python. Instead, sequence is a category that includes several different Python types.

The Python "str" type is one kind of sequence. Each string is a finite ordered group of indexed
characters. For example, the string object hello is a sequence of characters where h is element
0, e is element 1, L is both element 2 and element 3 and o is element 4. Remember that Python
always starts its indexes at 0 instead of 1.

299
05.03@1:52

Fig. 05.03.02: Subscription Example of ‘hello’[1]

I can use a new kind of Python expression called a subscription expression to reference an
element of a sequence. Subscription uses two delimiters, right bracket and left bracket. Python
subscription is analogous to using subscripts in mathematics. For example, I can reference the
string element of 'hello' at index 1 using this expression, which is pronounced hello of 1.

05.03@2:38

Fig. 05.03.03: Type of String Elements

In many programming languages, the elements of a string are objects whose type is char, which
is short for character. However, there is no char type in Python. Let's find out what type of
elements Python uses in a string. The type str was displayed. It appears that the elements in a

300
Python string are one-element string objects. However, Python string elements are characters
that have an internal representation that do not have a Python type. When you use subscription
to reference one of these characters, the interpreter returns a one element string object.

This is a simplified syntax diagram for the subscription expression.

05.03@2:44

Fig. 05.03.04: Simplified Syntax Diagram for Subscription Expression

And here is a generalized syntax diagram for expression that includes subscription.

05.03@2:52

Fig. 05.03.05: Generalized Syntax Diagram for Subscription Expression

The simplified semantic rule for subscription is:

1. evaluate the left expression to get the primary object


2. evaluate the bracketed expression to get the index object

301
3. if the primary object is not a sequence, report a subscription error
4. if the index object is not a non-negative integer less than the sequence length, report an
indexing error
5. reference the primary object's element at the index

The expression, "hello of one" is lexically and syntactically correct.

I will apply the semantic rule for subscription. In step 1, the left expression is evaluated to obtain
the primary object, hello.

05.03@3:44

Fig. 05.03.06: Step 1 of Semantic Analysis Left Expression Evaluated to Obtain Primary Object hello

302
In step 2, the bracketed expression evaluates to the object 1.

05.03@3:50

Fig. 05.03.07: Step 2 of Semantic Analysis Bracketed Expression is Evaluated to Object 1

In step 3, the primary object has type str, which is a sequence, so no subscription error is
reported.

In step 4, the int object 1 is a non-negative integer that is less than the string length, 5, so no
indexing error is reported.

05.03@4:06

Fig. 05.03.08: Step 4 of Semantic Analysis Int Object 1 is a Non-negative Integer that is Less Than the
String Length so No Indexing Error Reported

303
In step 5, the primary object is 'hello', so the interpreter returns the element at index 1.

05.03@4:35

Fig. 05.03.09: Step 5 of Semantic Analysis Interpreter Returns the Element at Index 1 of the Primary
Object ‘hello’

This is the single element string e, not h, which is at index 0.

05.03@4:24

Fig. 05.03.10: Single Element String h is at index 0

The expression 27[1] is also lexically and syntactically correct.

I will apply the semantic rule for subscription. In step 1, the left expression is evaluated to obtain
the primary object, 27.

304
05.03@4:40

Fig. 05.03.11: Step 1 of Semantic Analysis Left Expression is Evaluated to Obtain the Primary Object 27

In step 2, the bracketed expression is evaluated to obtain the index object, one.

05.03@4:47

Fig. 05.03.12: Step 2 of Semantic Analysis Bracketed Expression is Evaluated to the Object 1

In step 3, the primary object has type int, which is not a sequence, so a semantic type error
reports that the integer is not subscriptable and evaluation stops.

305
05.03@5:00

Fig. 05.03.13: Step 3 of Semantic Analysis Semantic Type Error is Reported and Evaluation Stops

Here is another lexically and syntactically correct subscription expression, 'hello'['1']

I will apply the semantic rule for subscription. In step 1, the left expression is evaluated to obtain
the primary object, hello.

306
05.03@5:17

Fig. 05.03.14: Step 1 of Semantic Analysis Left Expression is Evaluated to Obtain the Primary Object
hello

In step 2, the bracketed expression is evaluated to obtain the index object, one.

05.03@5:23

Fig. 05.03.15: Step 2 of Semantic Analysis Bracketed Expression is Evaluated to Obtain the Object 1

307
In step 3, the primary object has type str, which is a sequence, so no subscription error is
reported.

In step 4, the string object one is not a non-negative integer so a semantic type error reports
that the index must be an integer and evaluation stops.

05.03@5:42

Fig. 05.03.16: Step 4 of Semantic Analysis Semantic Type Error is Reported and Evaluation Stops

308
Here is yet another lexically and syntactically correct subscription expression, 'hello'[5]

I will apply the semantic rule for subscription. In step 1, the left expression is evaluated to obtain
the primary object, hello.

05.03@6:01

Fig. 05.03.17: Step 1 of Semantic Analysis Left Expression is Evaluated to Obtain the Primary Object
hello

In step 2, the bracketed expression is evaluated to obtain the index object, five.

309
05.03@6:07

Fig. 05.03.18: Step 2 of Semantic Analysis Bracketed Expression is Evaluated to Obtain Object 5

In step 3, the primary object has type str, which is a sequence, so no subscription error is
reported.

In step 4, the int object five is a non-negative integer, but it is not less than the string length
which is also five, so a semantic indexing error reports that the index is out of range and
evaluation stops.

05.03@6:34

Fig. 05.03.19: Step 4 of Semantic Analysis Semantic Indexing Error is Reported and Evaluation Stops

310
The reason the largest index must be strictly less than the length instead of less than or equal to
the length is that sequences are indexed from 0 to one less than the sequence length. I know I
have discussed indexing from 0 instead of 1 several times, but you will probably make indexing
errors anyway. I still make indexing errors!

05.03@6:58

Fig. 05.03.20: Sequences are Indexed From 0

In the subscription semantics, step 4 indicates that the interpreter reports an indexing error for
all negative integers. However, the built-in sequence types can handle some negative indexes.
In this expression, 'hello'[-1], the last element, 'o' is returned. This is done by adding the index,
negative 1 to the string length 5, to obtain the index 4 and returning the element, O, at this
index.

311
05.03@7:31

Fig. 05.03.21: Using an Index of -1 Yields the “first” Last Element ‘o’

Using an index of negative n yields the nth last element. This can sometimes be useful.

05.03@7:37

Fig. 05.03.22: Using an Index of -3 Yields the “third” Last Element ‘l’

312
However, if you use a negative index whose magnitude is too large, you will still get an indexing
error. For example hello of negative 6 is an error, because there is no 6th last element of hello,
which has only 5 characters.

05.03@7:58

Fig. 05.03.23: Using an Index of -6 Yields an Indexing Error as there is no “sixth” Last Element of ‘hello’

In this video, I introduced a category of types called sequence types. This category includes the
string type and other types that will be introduced in future lessons. I also introduced the
subscription expression, which you can use to refer to elements of a sequence.

313
05.04 Python - Tuple and List Types

In this video, I introduce two new sequence types, tuple and list. I also introduce a new kind of
expression called a parenthesized expression.

You have already used one kind of sequence, the string type. The elements of a string must all
be characters.

05.04@0:27

Fig. 05.04.01: Elements of a String are Characters

314
A tuple is a sequence whose elements can be any type. For example, here is a tuple that
contains the strings 'kiwi' and 'orange', the integer 27 and the function len.

Although a string can be created using a string literal token, there is no literal token for creating
a tuple. Instead I create a tuple using an expression list that can contain multiple expressions
separated by commas. Here is an expression list that creates the kiwi-orange tuple in memory.

05.04@0:59

Fig. 05.04.02: Expression List Creating Tuple in Memory

315
Here is the revised syntax diagram for expression that includes expression list.

05.04@1:03

Fig. 05.04.03: Revised Syntax Diagram for Expression List

Here is the syntax diagram for expression list.

05.04@1:09

Fig.05.04.04: Syntax Diagram for Expression List

The semantics of an expression list are:


1. Evaluate each expression from left to right to obtain expression objects
2. If there is no comma, the single expression object is the result object
3. If there is a comma, the result object is a tuple that contains all of the evaluated
expression objects

316
I'll apply this semantic rule to create the kiwi-orange tuple.

In Step 1, each of the four expressions is evaluated to create four objects: string kiwi, string
orange, integer 27 and function len.

05.04@1:50

Fig. 05.04.05: Step 1 of Semantic Analysis Each of the Four Expressions Evaluated to Create Objects

Step 2 is skipped since there is a comma in the expression list.

In Step 3, a tuple object is created that contains the four objects, kiwi, orange, 27 and len.

317
05.04@2:03

Fig. 05.04.06: Step 3 of Semantic Analysis Comma so Tuple Object Created Containing Four Objects

A one element tuple is created using a single expression, with a trailing comma. For example
this expression creates a tuple whose single element is the string, kiwi.

05.04@2:16

Fig. 05.04.07: One Element Tuple

In Step 1, the single expression is evaluated to create the 'kiwi' string object.

318
05.04@2:22

Fig. 05.04.08: Step 1 of Semantic Analysis Single Expression Evaluated to create String Object ‘kiwi’

Step 2 is skipped since there is a comma in the expression list.

In Step 3, a tuple object is created that contains the single kiwi object.

05.04@2:32

Fig. 05.04.09: Comma so Tuple Object Created Containing Single Object

319
I will try to create an empty tuple by using a comma by itself.

05.04@2:39

Fig. 05.04.10: Syntax Error When Creating an Empty Tuple by Using a Comma Itself

A syntax error occurred! I need a different way to create an empty tuple. To do this, I will
introduce another kind of expression, called a 'parenthesized expression'. I will add
"parenthesized expression" to the syntax diagram for expression.

05.04@2:53

Fig. 05.04.11: Revised Syntax Diagram Parenthesized Expression

Here is the syntax diagram for 'parenthesized expression'.

05.04@2:58

Fig. 05.04.12: Syntax Diagram Parenthesized Expression

320
Here are its semantics:
1. If there is no expression list, the result object is an empty tuple
2. If there is an expression list it is evaluated to obtain the result object

I can use empty parentheses to create an empty tuple.

I can also use parentheses to change the normal precedence of Python operators. Parentheses
appear at the bottom of the Python operator precedence table so they can be used to give any
operation the highest precedence, the same way they can be used in Mathematics.

05.04@3:30

Fig. 05.04.13: Python Operator Precedence Table

For example, to perform addition before multiplication, I can put the addition operator and its
operands in parentheses to obtain 35.

05.04@3:42

Fig. 05.04.14: Addition Operator in Parentheses

You have seen in a previous lesson that without parentheses, this expression would evaluate to
23, instead of 35.

321
05.04@3:50

Fig. 05.04.15: Addition Operator Without Parentheses

I have shown you the tuple sequence type and parenthesized expressions, which are used to
create an empty tuple. Now, I will introduce the list sequence type. A list is like a tuple, in that its
elements can be any type. For example, here is a list that contains the strings 'kiwi' and
'orange', the integer 27 and the function len.

Just as there is no literal token for creating a tuple, there is no literal token for creating a list.
Instead, there is an expression called a list display that uses the comma delimiter and left and
right bracket delimiters.

Here is a list display expression that creates the kiwi-orange list in memory.

05.04@4:32

Fig. 05.04.16: List Display Expression Creates List in Memory

Here is the revised syntax diagram for expression that includes list display.

05.04@4:37

Fig. 05.04.17: Revised Syntax Diagram List Display

322
and here is a simplified syntax diagram for list display.

05.04@4:41

Fig. 05.04.18: Simplified Syntax Diagram List Display

The simplified semantic rule for list display is:


1. If there is no expression list, the result object is an empty list
2. if there is an expression list, evaluate it and return a list object that contains all of its
objects in order

I'll apply this semantic rule to the kiwi orange list display.

In Step 1, there is an expression list, so the result object is not an empty list.

[email protected]

Fig. 05.04.19: Step 1 of Semantic Analysis Expression List so Result Object Not Empty List

In Step 2, the four expressions evaluate to: a kiwi string, an orange string, a 27 integer and a len
function object. A list object is created that contains these four elements in order.

323
05.04@5:19

Fig. 05.04.20: Step 2 of Semantic Analysis Expression List so List Object Created

You might be wondering why Python has two different types, tuple and list, that seem the same,
except for the way they are created.

05.04@5:34

Fig. 05.04.21: Tuples and Lists Seem Similar

324
The answer to this question is in the next lesson.

In this video, I introduced two new sequence types, tuple and list. I also introduced
parenthesized expressions.

325
05.05 Python - Sequence Element Replacement

In this video, I will show you a generalized assignment statement that can be used to change
the elements of some sequences. I will also describe the difference between mutable types,
whose values can be changed, and immutable types, whose values cannot be changed.

To change an element of a sequence, the assignment statement must be generalized to support


a subscription expression as a target.

Recall the syntax for the assignment statement where target was a simple identifier.

05.05.01@0:36

Fig. 05.05.01: Assignment Statement where Target is a Simple Identifier

Here is the syntax for the non-terminal state target, that includes a subscription expression, in
addition to the identifier target you saw in the assignment statement lesson.

05.05.02@0:48

Fig. 05.05.02: Syntax for the Non-terminal State Target that Includes Subscription Expression

Recall the semantics of an assignment statement whose target is an identifier.

05.05.03@0:50

Fig. 05.05.03: Semantic Rule: Assignment Statement - Identifier Target

326
Here are the semantics of an assignment statement whose target is a subscription expression:
1. Evaluate the right expression to obtain a result object
2. Evaluate the left expression to obtain the primary object
3. Evaluate the bracketed expression to obtain the index object
4. If the primary object is not a mutable sequence report a subscription error
5. If the index object is not a non-negative integer less than the sequence length report an
indexing error
6. Rebind the primary object’s element at the index to the result object

For example, I can use assignment to change an element of a list. First I will bind an identifier to
the original list so I can reference and change it.

05.05@1:11

Fig. 05.05.04: Memory of Binding Identifier to Original List

I'll display the list so I can see the original.

05.05@1:12

Fig. 05.05.05: Displaying Original List

Now I will use assignment to change the orange element to an apple.

I’ll apply the semantic rule for an assignment statement, whose target is a subscription
expression. In step 1, the right expression is evaluated to obtain the result object, “apple”.

327
05.05@1:18

Fig. 05.05.06: Step 1 of Semantic Analysis Right Expression Evaluated to Obtain Result Object ‘apple’

In step 2, the left expression is evaluated to obtain the primary object, which is the kiwi-orange
list.

05.05@1:31

Fig. 05.05.07: Step 2 of Semantic Analysis Left Expression is Evaluated to Obtain the Primary Object

In step 3, the bracketed expression is evaluated to obtain the index object, 1.

05.05@1:40

Fig. 05.05.08: Step 3 of Semantic Analysis Bracketed Expression Evaluated to Obtain the Index Object, 1

In step 4, the primary object is a list, which is a sequence. In fact, you will see in a moment that
lists are mutable sequences so no error is reported.

In step 5, the index object is one, which is a non-negative integer less than the list length, four,
so no error is reported.

328
In step 6, the list element at index 1 is rebound to the "apple" result object.

05.05.15@2:07

Fig. 05.05.09: Step 6 of Semantic Analysis List Element at Index 1 is Rebound to the “apple” Result
Object

I can see the result by displaying my changed list. Notice that the element orange has been
replaced by the element apple, at index 1 of the list.

05.05@2:18

Fig. 05.05.10: Displaying Changed List

Some sequence types have elements that cannot be changed. For example, string elements
cannot be changed.

05.05@2:27

Fig. 05.05.11: String Elements Cannot be Changed

You saw that list elements can be changed when I changed orange to apple in the kiwi-orange
list. A mutable type is a type whose object values can be changed. List is a mutable type since
the value of a list object can be changed by replacing, adding, or removing elements. An
immutable type is a type whose object values can not be changed. String and tuple are
immutable types.

329
For example, given this familiar tuple.
05.05@2:56

Fig. 05.05.12: Tuple Example

If I use the subscription assignment to try to change an element of a tuple a semantic type error
is reported. Strings and tuples cannot have elements added, removed or replaced.

05.05@3:02

Fig. 05.05.13: Semantic Type Error Occurs When Trying to Change an Element of a Tuple

The append method can be used to add an element to the end of a mutable sequence.
Append has two arguments: the special argument, which is a sequence, and the argument that
is passed into the method.

05.05@3:19

Fig. 05.05.14: Append has Two Arguments

This second argument is an element to be added to the end of the special argument sequence.
For example, I can add the float object, 43 point 2 to the end of the kiwi-apple list.

330
05.05@3:31

Fig. 05.05.15: Append Method on a List, Where the Special Argument is my_list and the Normal
Argument is 43.2

The append method fails when I use it on a tuple since I cannot apply the append method to
any immutable type.

The only difference between the tuple and list types is that tuples are immutable and lists are
mutable.

The int, float and bool types are also immutable. You cannot change the value of an object
whose type is int, float, or bool.

In this video, I showed you a generalized assignment statement that can be used to change the
elements of some sequences. I also described the difference between mutable types, whose
values can be changed, and immutable types, whose values cannot be changed.

331
05.06 Python - For Statement

This video introduces a new kind of statement called a for statement. A for statement, often
called a for loop, is used to repeat other statements a definite number of times.

Consider a sequence of elements. There are many situations in which you would like to apply
the same computation to each element individually. Let’s look at a program that analyzes a
given sequence of words and prints out an opinion about each word, depending on the length of
the word.
05.06@0:24

Fig. 05.06.01: A Sequence of Elements Where the Program Prints an Opinion About Each Word,
Depending on its Length

So far, all the Python statements you have seen have been evaluated exactly one time or
skipped. This program should evaluate the same group of statements multiple times, once for
each word in the given sequence.

We can use a for statement to evaluate a group of statements as many times as necessary
without re-writing those statements.

Here is the program solution, where I use a list of words as my sequence. I’ll run the program.

05.06.02@1:01

Fig. 05.06.02: Solution Code for Printing an Opinion Given a Word

332
For each word in the list, the program displays a sentence “I like the word” or “I do not like the
word”.

05.06@1:05

Fig. 05.06.03: An Opinion is Displayed for Each Word in the Word List

A for statement is another kind of compound statement. In this program, the for statement starts
on line 3 and finishes on line 7. Inside of the for statement is an if statement, a familiar
compound statement.

05.06@1:23

Fig. 05.06.04: Compound Statements - For Statement is in the Red Box and If Statement is in the Purple
Box

I will add the for statement to the compound statement syntax diagram.

333
05.06@1:27

Fig. 05.06.05: The For Statement is a Compound Statement, so it is Added to the Compound Statement
Syntax Diagram

We will only discuss the simplest form of for statement, which binds a single target identifier to
the elements of a sequence in order.

Here is the simplified syntax diagram for a for statement.

05.06@1:39

Fig. 05.06.08: Syntax Diagram for For Statement

It starts with a keyword token, for; the target identifier; the keyword token, in; the expression;
and the colon together are called a header.

05.06.09@1:50

Fig. 05.06.09: The Header Starts with the Keyword Token For, the Target Identifier, the Keyword Token In,
and the Expression

The syntax for suite is the same as it was for an if statement.

05.06.10@1:55

Fig. 05.06.10: Syntax Suite for For Statement

334
You can use these syntax diagrams to verify that the for statement in this program has valid
syntax.

Here are the simplified semantics of the for statement:

1. Evaluate the expression to obtain a result object


2. If the result object is *not* a sequence report an error
3. Bind the identifier to each consecutive element of the sequence and evaluate the suite
once with each binding.

Again, the semantics for the suite are the same as they were for if statements.

I will apply the for statement semantics to the for statement in the program.

When line 3 is evaluated, the namespace contains prebound identifiers and the identifier,
word_list, which is bound on line 1.
05.06@2:37

Fig. 05.06.11: Identifier word_list is Bound to the List that Contains the String Elements ‘Python’, ‘chair’,
‘Edmonton’

Step 1 evaluates the expression in the for statement header. The expression is the single
identifier, "word_list". Since word_list is bound to a list object, the result object is this
three-element list, where each element is a string.

335
05.06@2:50

Fig. 05.06.12: The Identifier word_list is Bound to a List Object

In step 2, the list is a sequence so no error is reported.

In step 3, the target identifier in the header is “word”. The suite is evaluated once for each list
element.

“Word" references a consecutive element in the list object, each time the suite is evaluated. For
the first iteration of the for statement, word is bound to the string object, "Python".

05.06@3:17

Fig. 05.06.13: Word is Bound to the String Object, “Python”

336
The for statement suite contains a single if statement, which uses if statement semantics. Word
is bound to Python and the length of the string “Python” is 6.

05.06@3:17

Fig. 05.06.14: The Length of the String “Python” is 6

Since 6 is not less than 6, the if condition is false, so the if suite is ignored. Instead, the else
clause suite is evaluated. The strings “I like the word:” and the dereferenced word identifier,
“Python”, are concatenated and the result string is displayed.

The for statement suite is evaluated again. This time the target identifier is bound to the string
“chair”. The length of “chair” is 5, which is less than 6, so the condition is true and the suite is
evaluated.

337
05.06@3:58

Fig. 05.06.15: Word is Bound to the String Object “chair”

The strings “I do not like the word:” and “chair” are concatenated and the result string object is
displayed.

The for statement suite is evaluated for the last time. The target identifier is bound to the string
“Edmonton”. The “length” of Edmonton is 8, which is not less than 6, so the if statement
condition is false and the else suite is evaluated.

The string “I like the word: Edmonton” is displayed.

The program is done.

I will now delete the word list and replace word_list in the for statement header with a different
expression: the literal integer 27.

When I run this program an error message is displayed.

05.06@4:38

Fig. 05.06.16: Error Message is Displayed Because Int 27 is not Iterable

338
In step 1 of the for statement semantics, the expression is evaluated to get a result object, 27.

In step 2, the result object is an integer, which is not a sequence, so an error is reported.

The error message indicates that the int object is not iterable. Every sequence type is iterable,
so you can use any expression that evaluates to a sequence object in a for statement. There
are some iterable objects that are not sequences. However, for now, we will not define what
“iterable” means, so we will only use expressions that evaluate to sequence objects in for
statements.

This lesson introduced the for statement. A for statement is used to repeat other statements a
definite number of times.

339
05.07 Program Hacking Version 4

Now that the design is complete, let's code.

I have already copied the code from Hacking Version 3 and saved it as Hacking Version 4. I also
added comments for each algorithm step.

05.07@0:18

Fig. 05.07.01: Copied Code from Hacking Version 3 Saved as Hacking Version 4

When you start coding this version, you must also make a copy of Hacking Version 3 and add
comments for the new algorithm steps, including control structures. The “for icon” controlling the
password list, for example, can be translated to the comment "for password in password list".

340
05.07@0:32

Fig. 05.07.02: The Comment “for password in password list” can be Represented with the “for icon”

You should remove the comments for the control structures when you write their header code.
You may also want to add additional comments that clarify your program.

Every instance of definite repetition in your algorithm will become a for statement in your code.

I’ll create the for statement that displays the header. To start, I need a sequence that my for
statement will iterate over. I’ll create a list that contains all three header strings, including the
blank line.

341
05.07.03@1:04

Fig. 05.07.03: A List is Created Containing all Three Header Strings

If you don't remember the for statement or the list type, you should review the For Statement
and List lesson.

I’ll create a new identifier called “header” and bind it to the header list.

05.07@1:15

Fig. 05.07.04: Binding Identifier Header to the Header List

Now I can translate the “for icon” from my algorithm into a line of code by writing “for
header_line in header”. The suite of the for statement can be recycled from version 3. The
same four lines of code used to display each header and password string are still relevant! They
just need a few modifications.

I will put the old code used to display “DEBUG MODE” into the suite and delete the rest of the
old display header code. What changes do I need to make to this code?

342
05.07@1:39

Fig. 05.07.05: Moved the Code to Display “DEBUG MODE” into the For Suite

If I run my program right now it displays “DEBUG MODE” three times, once for each string in the
header list.

05.07@1:47

Fig. 05.07.06: Running the Program Displays “DEBUG MODE” Three Times

Instead, I want it to display each string in the list! So I will replace the “DEBUG MODE” string in
the draw_string call with my target identifier, header_line.

343
05.07@2:00

Fig. 05.07.07: Replaced “DEBUG MODE’ String in draw_string to header_line

Now when I run my program, it displays each header line, updates the window, pauses for 0.3
seconds, and increments line_y for the next iteration.

05.07@2:11

Fig. 05.07.08: Running the Program Displays each Header Line: “DEBUG MODE”, “1 ATTEMPT(S)
LEFT”, and the Empty String

This is the same functionality as version 3. However, now, the code repeats itself, instead of you
repeating yourself by writing multiple adjacent duplicate statements.

Add other for statements to finish this program. Don’t forget to choose the password from the
password list using subscription!

Check if any of the repeated literals, such as the pause time, 0.3, the x coordinate, 0, or the
correct password, HUNTING, still occur more than once and, if so, bind an identifier to that
literal.

344
05.08 Reflect on Hacking Version 4

Let’s review the code for version 4.

For this review, I’ll be using the debugger to trace a for statement to showcase how to keep
track of the target identifier.

To start, I’ll place a breakpoint beside the first for statement that displays the header and press
the bug button.

The game window appears, but it is empty.

05.08@0:29

Fig. 05.08.01: The Game Window is Empty

Since I pressed the bug button, the debugger stopped on the breakpoint and highlighted “for
header_line in header”.

345
05.08@0:36

Fig. 05.08.02: Debugger Stopped on Breakpoint on Line 23 and Highlights it

I’ll press the step over button to enter the for statement and highlight the draw_string function
call that will display the first header string. The “header_line” identifier has been added to the
stack data panel, and is bound to “DEBUG MODE”.

05.08@0:49

Fig. 05.08.03: The “header_line” Identifier is Bound to “DEBUG MODE”

Above this identifier is “header”, the name we used to make our list. You can see that “header”
is bound to a list. If I expand "header", you can see that “DEBUG MODE” is the first element in
the list.

346
05.08@1:01

Fig. 05.08.04: “DEBUG MODE” is the First Element in the header List at Index 0

You can also see the second element, “1 ATTEMPT LEFTS”, and the last element, an empty
string. This tells me that my list has length 3, and that it has been properly initialized with all the
header strings. Notice that the list is indexed from 0 to 2 instead of 1 to 3, as was discussed in
the For statement and list lesson.

I’ll press the step over button until I reach the line_y assignment statement. “DEBUG MODE”
appears in the window.

In the previous reflection, when we pressed the step over button at the end of the if statement,
the debugger moved onto the next statement after the if. This time, when I press the step over
button the debugger returns to the top of the for statement and highlights "for header_line in
header" again, so that the target identifier header_line can be rebound to the next element of
the header list.

05.08@1:53

Fig. 05.08.05: The Debugger Returns to the Top of the For Statement (line 23) so header_line can be
Rebound to the Next Element of the List

When I press the step over button again, “header_line” in the stack data has been updated to “1
ATTEMPTS LEFT”. The debugger is useful for slowly tracing loops to keep track of how
identifiers change!

347
05.08@1:59

Fig. 05.08.06: In the Stack Data, header_line is Updated to “1 ATTEMPT(S) LEFT”

I’ll press the step over button a few more times until header_line is bound to the empty string
and the line_y assignment statement is highlighted.

When I press the step over button, the debugger returns to the top of the for statement and
highlights "for header_line in header". The interpreter must check if there are any more
elements in the list, even though we know that there aren't. When I press the step over button
again, the first line after the for statement suite is highlighted, which will create the password list.

05.08.07@2:35

Fig. 05.08.07: Password List Created After For Statement Suite is Executed

Next, I’ll add another breakpoint. This time it will be at the first line of the for statement that
displays the passwords.

I’m going to press the bug button once more, to show you a handy shortcut for loop tracing.
When a breakpoint is placed at any line of the loop, pressing the bug button traces one iteration
and stops at the same line that contains the breakpoint. This makes it easy to trace an entire
iteration of a loop quickly.

Two identifiers have been added to the stack data: password, which is currently bound to
“PROVIDE”, and password_list, which is bound to a list with length 14.

348
05.08.08@3:10

Fig. 05.08.08: Two Identifiers Added to Stack Data: Password is Currently Bound to “PROVIDE” and
password_list is Bound to a List with Length 14

If I press the bug button a few more times, I see that password is rebound to the next string in
password list and then displayed in the window.

05.08@3:13

Fig. 05.08.09: Password is Rebound to the Next String in the Password List “SETTING”

That’s everything I want to show you about for statements using tracing. Next, let’s talk about
the two Software Quality tests we'll add for this version.

The first test is about literals. For the literals test, each literal with common intent, except zero,
one, two, negative one, and a blank string, should appear exactly once in the program.

05.08@3:41

Fig. 05.08.10: Software Quality Test about Literals

It is okay to use identifiers instead of these special values, but it is not required. For example, in
our code, we replaced the literal 0 by line_x to make its intent clear.

349
05.08@3:53

Fig. 05.08.11: Replaced 0 with line_x to Make its Intent Clear

We left the literal two, used to compute half of some distances, since dividing by two is
common.

05.08@3:55

Fig. 05.08.12: The Literal 2 is Used to Compute Half of Some Distances

Common uses of 0 and 1 as starting values in a list are often not replaced.

Previously, we discussed how finding and replacing literals might introduce errors if you aren’t
careful about what you replace. We also discussed how tedious it can be to change multiple
instances of a literal throughout a program. In your code, make sure you have bound an
identifier to each of the literals that was used more than once. Your code should contain only
one instance of each literal that has a particular intent. This make your code shorter and less
prone to error when you change the value of a literal.

The second test is about repetition. The repetition test ensures that adjacent duplicate
statement groups have been replaced by repetition control structures.

05.08@4:45

Fig. 05.08.13: Software Quality Test about Repetition

This makes your code shorter, easier to read, and easier to change. For this version, you should
have three for statements that have replaced the three instances of adjacent duplicate
statement groups in your version 3 code. One replaces the header, one replaces the password
list, and one replaces the outcome message.

350
Also make sure that your code reflects the Software Quality tests from previous versions. The
program and code blocks should be commented, your identifier names should be descriptive,
and your code should reflect the design of your new algorithm.

05.08@5:08

Fig. 05.08.14: Software Quality Tests from Previous Versions

Congrats! You’ve finished version 4 of Hacking.

351
05.09 Solution Issues in Hacking Version 4

Hacking Version 5 will add new features to the game, so you’ll create a description, a functional
test plan, and an algorithm before you write the code.

05.09@0:18

05.09.01: Create Version Description Subtask in Create Game Diagram

First, let’s talk about the solution issues that we should improve upon in this version.

05.09@0:24

Fig. 05.09.02: Identify Solution Issues Subtask Create Game Diagram

352
In a game about guessing passwords, only having one guess is pretty sad. In version 5, you’ll
add the functionality to enter multiple password guesses, using a new repetition control
structure called a while statement.

A while statement is the second type of loop you’ll use throughout the rest of the course. It acts
similarly to a for loop in that it performs a sequence of steps repeatedly.

In the previous module, we used for loops to implement definite repetition. Recall that definite
repetition iterates over the elements of an explicit sequence with fixed length. A “for” loop
always uses definite iteration because it needs an iterable object.

A while loop uses indefinite repetition because it uses a condition instead of an iterable object.
A while loop performs its steps as long as its condition is true. You’ll learn more about while
loops in the next programming language videos.

Both definite and indefinite repetition are examples of the control abstraction problem solving
technique.

05.09@1:36

Fig. 05.09.03: Control Abstraction Subtask in Problem Solving Diagram

Notice that your previous description contains repeated attributes such as “it is green on black”
and “it uses small font size”. This is an example of repeating yourself, which leads to a longer
description that is harder to change.

353
05.09.04@1:51

Fig. 05.09.04: Version 3 Description Contains Many Repeated Attributes

Instead, you should group common attributes of different objects together. For example, the
description could include the object "All display objects", to refer to all the text that appears in
the window. This object’s attributes would be “are green on black” and “use small font size”.
Then these shared attributes do not need to be described for each individual display object.

354
Grouping common object attributes into a single unit is an example of a new kind of abstraction
called Data Abstraction. There are several ways of grouping common attributes. We already
used data abstraction to group all attributes of a single object together, such as all attributes of
the header or the outcome.

05.09@2:46

Fig. 05.09.05: All Attributes of the Header are Grouped

This allows us to describe, test, or reference an entire header or outcome object using a single
name. However, when different objects have a common attributes, we also want to use data
abstraction to give a common name to the shared attributes. For example, dogs and cats have
common attributes, such as eating meat, so we create a term called carnivore that contains
these common attributes.

Since all display objects share their color and font size, we introduce "All display objects" as an
explicit common object in the description, so we can move the shared color and font attributes
to this single common object.

Now observe hacking version 5, which includes the multiple guesses feature!

355
05.10 Observe and Play Hacking Version 5

Here is the fifth version of Hacking!

The first difference between version 5 and the previous version is the number of attempts left
displayed in the header. I am now able to make four attempts at guessing the password instead
of 1.

05.10@0:20

Fig. 05.10.01: Hacking Version 5 Displays the Number of Attempts Left in the Header

The passwords and the password prompt display as before, but when I input an incorrect
password another password prompt is displayed, and the header shows that the number of
“attempts left” has decreased by one.

356
05.10@0:39

Fig. 05.10.02: Attempts Left Decrements When an Incorrect Input is Given

I’ll input two more incorrect passwords. I get two more password prompts, and “attempts left”
decreases to 1. Also, notice the new message, “LOCKOUT WARNING”, in the bottom right of
the window.

357
05.10@0:52

Fig. 05.10.03: “LOCKOUT WARNING” Message Displayed When Only Have 1 Attempt Left

I’ll input the correct password now. The rest of the game looks the same as version 3.

358
05.10@1:02

Fig. 01.10.04: After Inputting Correct Password, the Rest of the Game Looks the Same as Version 3

Play the game a few times yourself and try inputting different combinations of correct and
incorrect passwords. Notice that you see an appropriate success or failure outcome message
and that the number of attempts left is decremented after each unsuccessful guess.

359
05.11 Describe Hacking Version 5

Welcome back to the Description Builder for Hacking Version 5!

Now that the game supports multiple password guesses, we need a description to match. The
description must describe multiple gameplay ‘paths’ that account for different combinations of
password guesses.

There are four distinct behaviours that can occur for any combination of guesses. You will use
one “If” selection object to describe each of these four behaviors.

05.11@0:45

Fig. 05.11.01: Data Abstraction Subtask in Problem Solving Diagram

As discussed in the solution issues, you should use data abstraction to group some common
object attributes. Look for one common attribute shared by all password guesses and two
common attributes shared by all display objects.

360
05.12 Create Test Plan for Hacking Version 5

Welcome back to the Functional Test plan builder for Hacking version 5!

The test plan for versions 3 and 4 tests two paths through the game. For this test plan, you need
to test many more paths. Like the version 5 description, you’ll need to accommodate each of the
four behaviours that arise due to the multiple guess attempts.

Testing behaviours that may occur only in very specific circumstances is called Edge Case
Testing. Often an Edge Case is a program behaviour that occurs when a parameter, such as
the number of attempts, has its minimum or maximum value.

For example, consider a test plan to check the behaviour of a calculator application.

05.12@0:57

Fig. 05.12.01: Calculator Application

A general behaviour is dividing an integer such as 100 by another integer such as 20. I also
need to check the case where the answer is not a whole number. I should also add tests where
the numerator or denominator are negative. However, the most interesting Edge Case is when I
divide by zero. Since the result of dividing by zero is undefined, it is considered an edge case
that needs a separate, distinct test in the test plan. An appropriate response to this test would
be an error or warning message, rather than giving a wrong number or crashing.

What are some of the edge cases for Hacking? I'll use case-based decomposition to identify
some of the independent edge cases for Hacking.

361
05.12@1:46

Fig. 05.12.02: Case-based Decomposition Subtask in Problem Solving Diagram

One Edge Case is entering an empty string, since it contains 0 characters which is a minimum.
Since there is no maximum number of characters in a string, we can't test the maximum, but we
can test a very long string. Another edge case is entering the correct password, but using lower
case letters.

05.12@2:10

Fig. 05.12.03: Edge Cases

To find out what the correct behaviour is in this case, you should run the program to see. Your
test plan must run the program three times with two success runs and one failure run. Your first
success run will use one incorrect answer from the passwords listed and one correct answer.
Your second success run will use three incorrect edge case answers followed by the correct
answer. Your failure run will use four attempts, where each attempt uses an incorrect edge case.

362
05.12@2:45

Fig. 05.12.04: Runs to Check Correct Behaviour of Program

363
05.13 Create Algorithm for Hacking Version 5

You’re back to the Create Algorithm task in the Game Creation Process!

We’ll introduce another type of repetition control structure in version 5. In Version 4 you used
definite repetition, which requires you to know how many times you should repeat a step before
you perform it.

In this version, the player has multiple attempts to guess the correct password. How many times
do you need to prompt the player to enter a password? A player is allowed a maximum of four
attempts, so the answer seems to be four. But what if the player guesses correctly on the first
try? Or the third try?

You don’t know beforehand how many times you need to repeat the password prompt. Definite
repetition requires you to have this knowledge. That’s where indefinite repetition, also called
indefinite iteration, comes in! Indefinite repetition performs an algorithm step repeatedly, while a
specified condition is true. This is like a combination of selection and definite repetition.

Indefinite repetition is represented in the algorithm builder by a while icon.

05.13@1:24

Fig. 05.13.01: Algorithm Builder While Icon

There is a regular rectangle connected to the while icon by an arrow. Similarly to definite
repetition, the step to be repeated goes in the regular rectangle. Similarly to selection, a
condition is required in the control structure icon.

364
05.13@1:26

Fig. 05.13.02: While Icon Repeats Step in the Regular Rectangle

Let’s return to the cake mixing example. Stirring the batter 20 times is definite repetition. Mixing
the batter until there are no lumps is indefinite repetition. To use this condition in the indefinite
while icon, I will select “batter”, “is”, and “lumpy”. For the rectangle, I will select “mix” and
“batter”.

05.13@2:08

Fig. 05.13.03: Cake Mixing Example - Batter is Only Mixed if it is Lumpy and Repeatedly Mixed Until it is
Not Lumpy

My algorithm now says “while batter is lumpy, mix batter”. This means the step “mix batter” will
be repeated as long as the batter is lumpy, however long that may take. This also means that if
the batter does not start lumpy, the step in the while loop will never be performed!

The same rules for selection conditions apply to indefinite repetition conditions. If need be, I can
consider multiple factors in my condition. For example, I might alter my cake batter condition to
be “while batter is lumpy or there is flour left”. This condition will remain true if there are lumps
and it will remain true if there is flour left.

05.13@2:54

Fig. 05.13.04: Cake Mixing Example - Can Consider Multiple Factors in Your Condition

You will use indefinite repetition to implement multiple guess attempts in Hacking version 5. In
previous versions, you prompted the player to make a guess and then displayed the appropriate
outcome message, depending on if the guess was correct or incorrect. In this version, the
behaviour for a correct guess is the same, but now you must ask yourself what happens if the
player makes an incorrect guess. As you indicated in your description and test plan, there are a

365
number of things that happen: the attempts left has to be displayed and decremented; the
player has to be prompted to make a new guess; and, if the player only has one guess left, a
warning must be displayed. Furthermore, you want all of this to happen *every* time the player
makes an incorrect guess as long as four attempts have not been made.

To get started on your algorithm, replace the step “prompt for guess” with the step “get guesses”
and expand "get guesses" to use indefinite repetition. You will need that “prompt for guess” step
in the expanded “get guesses” panel. If you get stuck, review your description and functional
test plan. You can also ask questions in the course forums!

05.13@3:59

Fig. 05.13.05: Replaced “prompt for guess” with the Step “get guesses”

366
05.14 Python While Statement

In this video, I will introduce a new kind of statement called a while statement.

Consider the program from the “for statement” lesson, which took a list of words and displayed
an opinion about each word based on its length.

05.14@0:21

Fig. 05.14.01: Program from “for statement” Lesson

In this lesson, I want my program to check an unspecified number of words and decide if it likes
each one. The new program should continue checking words until I indicate it should stop.

A for statement performs a computation once for each element of a sequence. However, for this
new program, I don't have a sequence of words. The words are input one at a time and the
opinion must be displayed immediately before the next word is input.

Instead of a for statement, I’m going to use a while statement. A while statement is a new kind
of repetition control structure that uses a boolean expression to control the repetition instead of
using a sequence. A while statement is a compound statement, so I’ll add the ‘while’ statement
to the compound statement syntax diagram.

05.14@1:08

Fig. 05.14.02: While Statement is Added to Compound Statement Syntax Diagram

367
Here is the program:

05.14@1:11

Fig. 05.14.03: Replaced For Statement with While Statement

I'll run this program.

05.14@1:13

Fig. 05.14.04: Program is Run and is Waiting for Keyboard Input

I can enter as many words as I please. I'll enter grape and banana. When I am finished, I will
enter an empty string by just pressing enter to end the while statement.

05.14@1:21

Fig. 05.14.06: Entering an Empty String Ends the While Statement

368
Here is the syntax diagram for a while statement:

05.14@1:26

Fig. 05.14.06: Syntax Diagram for While Statement

The header of a while statement consists of the while keyword token, an expression, and a
colon delimiter.

05.14@1:34

Fig. 05.14.07: While Statement Header

The header is followed by the suite non-terminal state, which has the same syntax as the suites
for the if and for statements.

05.14@1:42

Fig. 05.14.08: While Statement Header is Followed by the Suite

The while statement in this program has valid syntax based on these diagrams.

Here are the simplified semantics for a while statement.

1. Evaluate the expression to obtain a result object


2. If the result object is interpreted as true, evaluate the suite and return to step 1

A while statement's expression and suite are evaluated repeatedly. Each time the expression
evaluates to an object interpreted as true, the suite is evaluated once. A while statement is

369
called an indefinite repetition control structure since we don't know ahead of time, how many
times its suite will be evaluated.

I'll apply the semantics to the while statement in the program. When the while statement is
reached, the namespace contains some pre-bound identifiers and word, which is bound to
grape.

05.14@2:32

Fig. 05.14.09: Namespace Contains Pre-bound Identifiers and word, which is Bound to “grape”

In Step 1, the expression in the while header is evaluated. This expression is a binary
expression that dereferences the identifier word to obtain the string object grape and then
applies the "not equal" operator to the string object grape and the empty string. Grape is not
equal to the empty string, so the expression evaluates to the result object, true.

370
05.14@2:50

Fig. 05.14.10: “grape” is Not Equal to an Empty String

05.14@2:55

Fig. 05.14.11: In Memory, Result Object Evaluates to Ttrue

In step 2, since the result object is true, the while suite is evaluated. This suite contains two
statements, an if statement that checks the length of ‘word’, and an assignment statement that
inputs another word.

Since “grape” has length 5, which is less than 6, the if condition evaluates to true and the if
clause suite is evaluated. The interpreter concatenates the strings “I do not like the word: “ and
“grape” and then displays the result string object. Next, the assignment statement inputs
"banana" and binds the identifier, “word”, to the banana string.

To complete step 2, the interpreter returns to step 1 to evaluate the expression again. In step 1,
word is bound to ‘banana’, which is not equal to the empty string, so the while expression
evaluates to true again.

371
05.14@3:40

Fig. 05.14.12: Word is Bound to “banana”

In step 2, since the result object is true, the suite is evaluated again. In the if statement
condition, the length of “banana” is six. Six is not less than six, so the if condition evaluates to
false and the else suite is evaluated. The strings “I like the word: “ and “banana” are
concatenated and the result is displayed.

05.14@4:07

Fig. 05.14.13: Since “banana” has Length Equal to 6, it is Not Less Than 6, and the Strings “I like this
word: “ and “banana” are Concatenated and Displayed

The last statement of the suite inputs the empty string and rebinds ‘word’ to it.

To complete step 2, the interpreter returns to step 1 to evaluate the expression again. This time,
when the while condition is evaluated, the condition checks whether the empty string is not
equal to the empty string. The result object of the not equal operator is false since they are
equal. In Step 2, since the result object is false, the suite is not evaluated and the interpreter
does not return to step 1. The while statement is complete.

In this video I introduced a new repetition control structure, called a while statement.

372
05.15 Python - Repetition Examples and Range Type

This video explores examples of for statements and while statements, and introduces another
sequence type called range.

Recall the program from the for statement lesson. It displayed an opinion about each word in a
list, depending on the length of the word. In the while statement lesson, you looked at a program
that allowed you to enter your own words and display an opinion about each one.

05.15@0:24

Fig. 05.15.01: Program from For Statement Lesson

Here is a program that inputs an arbitrary number of words, saves them in a list, prints the list
and then analyzes the list using a for statement.

373
05.15@0:41

Fig. 05.15.02: Program that Inputs Arbitrary Number of Words, Saves Them in a List, Prints the List, and
Analyzes the List Using a For Statement

This program uses code from both of the previous programs.

05.15@0:45

Fig. 05.15.03: wordlist.py Uses Code From while.py and for.py

The most important new statement is a call to the sequence method append on line 5. In
addition, line 1 creates an empty list instead of a fixed word list and there is a familiar print call
on line 8 that displays the list.

I’ll run the program.

374
05.15@1:03

Fig. 05.15.04: After Running the Program, First Prompts User to Enter a Word

The program prompts me to enter a word. It allows me to continue entering words until I enter
an empty string. Then, for each word, the program displays, either “I like the word” or “I do not
like the word”, based on the word’s length.

05.15@1:09

Fig. 05.15.05: Until the User Enters an Empty String, the Program Will Continue to Prompt the User to
Enter a Word

375
05.15@1:15

Fig. 05.15.06: Program Displays Opinion of Word Based on the Word’s Length (See Line 11)

In the while statement lesson, the program bound the same identifier to each input word, but
each word was lost in the next loop iteration. Using the sequence method append allows me to
save the input words in a list. It is common when creating a list of objects to start with an empty
list and use append to add one new element at a time.

05.15@1:32

Fig. 05.15.07: Append Method Adds a New Element Each Loop Iteration

05.15@1:37

Fig. 05.15.08: “grape” is Now at Index 0 in word_list

05.15.10@1:39

Fig. 05.15.08: “banana” is Appended to word_list and is at index 1, After “grape”

376
Here are two programs, attempt-1 and attempt-2. Each program inputs a list of words using a
while statement and prints the list. Then, each program attempts to replace every word in the list
by its uppercase equivalent and prints the modified list. Both attempts use a for statement and
the string method upper.

05.15@1:58

Fig. 05.15.09: attempt-1.py Program

05.15@2:04

Fig. 05.15.10: attempt-2.py Program

Which of these programs will work?

Let’s run attempt-1. I’ll input the words “computer” and “apple” then press enter. Two identical
word lists are displayed. Attempt-1 failed to convert the words to upper case.

377
05.15@2:12

Fig. 05.15.11: Attempt-1 Failed to Convert the Words to Uppercase

Let's run attempt-2. Again, I'll input the words “computer”, and “apple” then press enter. This
time, the word list contains upper case words, so attempt-2 was successful.

05.15@2:27

Fig. 05.15.12: Attempt-2 Was Successful in Converting the Words to Uppercase

Let’s apply semantic analysis to the for statements in each program to see why the first attempt
didn’t capitalize the words, but the second attempt did.
In attempt-1, when the "for statement" is interpreted, the namespace contains prebound
identifiers and the identifiers word_list and word.

05.15@2:51

Fig. 05.15.13: When “for statement” is Interpreted, the Namespace Contains Prebound Identifiers, Input
and Print, and the Identifiers word_list and word

378
In step 1 of the for statement semantics, the expression in the for statement header is evaluated
to obtain a list object. In step 2, the list is a sequence, so an error is not reported. In step 3, the
target identifier item is bound to the element of the list object at index 0, the string, "computer.

The for statement suite is evaluated. The suite contains one assignment statement, in which the
identifier "item" is dereferenced to obtain the string object, "computer”. Then the method call,
upper is applied to this string object and the uppercase string object "COMPUTER" is returned.
The identifier, “item” is then rebound to uppercase "COMPUTER". Although the target identifier,
item, was rebound, none of the actual list elements were changed.

05.15@3:29

Fig. 05.15.14: Identifier “item” is Rebound to Uppercase “COMPUTER”

Returning to the header of the for statement, "item" is rebound to the next element in word_list,
which is “apple”. The suite is evaluated again. Like the previous iteration, “item” is rebound to
the uppercase version of the word it is currently bound to, so this time "item" is rebound to
uppercase "APPLE”. The for statement is done.

379
05.15@3:53

Fig. 05.15.15: “item” is Rebound to the Uppercase “APPLE”

Next is a print function call that displays word_list. None of the words in word_list have been
capitalized. This first attempt failed.

Attempt-2 uses a new sequence type called range, along with subscription assignment to
successfully update a list. A range is an immutable sequence type. The simplest kind of range
object contains all integers between two values. I can use the range function to create a range
object.

05.15@4:12

Fig. 05.15.16: range(0, len(word_list)) is an Example of a Range Object, which Contains all Integers
Between 0 and len(word_list)

The range function takes two integer arguments and returns a range object that contains a
sequence of integers from the first argument to one less than the second argument. Ignore the
bracketed third argument. It is an optional argument, which will not be used in this lesson.

380
05.15@4:35

Fig. 05.15.17: Range Function Arguments

For example, range of 3, 7 yields a sequence that contains 3, 4, 5 and 6. Since a range is a
sequence, its elements are indexed from 0 to its length minus one and an element can be
accessed using subscription.

05.15@4:49

Fig. 05.15.18: Elements are Indexed 0 to its Length Minus 1, so range(3,7) is a Sequence that Contains
3, 4, 5, and 6

For example, "my range of 0" yields the element at index 0 which is 3.

A range object can be used in a for statement to iterate over the indexes of a sequence so that
you can rebind individual elements of that sequence.

I will show you how attempt-2 uses a range object and subscription assignment to successfully
change the elements of word_list.

When the "for statement" is interpreted, the namespace contains prebound identifiers and the
identifiers word_list and word.

381
05.15@5:24

Fig. 05.15.19: Namespace Contains Pre-bound Identifiers, input, print, range, and len, and the Identifiers
word_list and word

In step 1, of the for statement semantics, the function call expression in the header is evaluated.
First the identifier range is dereferenced to obtain the range function object. The first argument
of the function call is the integer 0.

05.15@5:50

Fig. 05.15.20: First Argument of Function Call is the Integer 0

The second argument is a function call. The identifier len is dereferenced to get the len function
object. There is one argument and word_list is dereferenced to obtain the list object, as this
argument. The length of this list object is computed as the int object 2. So the range function, is
applied to zero and 2 and it returns a range object from 0 to 1.

382
05.15@6:00

Fig. 05.15.21: The Second Range Argument is 2. Range Call Result is a Range Object From 0 to 1

In step 2, the range object is a sequence type, so there is no error.

In step 3, the target identifier, "index", is bound to the zeroth element of the range object, which
is 0. Then the suite is evaluated.

05.15@6:15

Fig. 05.15.22: Index is Bound to the Zeroth Element of the Range Object, 0

The first statement in the suite is an assignment statement and its expression is a subscription
expression. In step 1 of the subscription expression semantics, the left expression, "word list" is
evaluated by dereferencing it to obtain the list object as the primary. In step 2, the bracketed
expression, "index", is evaluated by dereferencing it to obtain the integer object 0, as the index.
In step 3 and step 4, the primary is a sequence and the index is a non-negative integer less
than the sequence length, 2, so no subscription or index error is reported. In step 5, the primary
object's element at index 0 is "computer' so this is the result object. To complete the assignment
statement semantics, "item", is bound to this result object.

383
05.15@7:01

Fig. 05.15.23: Item is Bound to the Result Object “computer”

The second statement of the for suite is an assignment statement whose target is a subscription
expression. In step 1, the right expression is an attribute reference where the identifier "item" is
dereferenced to obtain the string object, "computer”. Then the method call, upper is applied to
this string object and the uppercase string object "COMPUTER" is returned as the result object.

05.15@7:22

Fig. 05.15.24: The Method Call Upper is Applied to the String Object and the Uppercase String Object
“COMPUTER” is Returned as the Result Object

In step 2, the left expression, "word list" is evaluated by dereferencing it to obtain the list object.
In step 3, the bracketed expression is evaluated by dereferencing "index" to obtain the integer
object 0. In step 4, the primary object is a list, which is a mutable sequence so no subscription
error is reported. In step 5, the index object, 0, is a non-negative integer which is less than the
sequence length, 2, so no indexing error is reported. In step 6, the primary object's element at
index 0 is rebound to the result object, uppercase "COMPUTER".

The interpreter returns to the for header. The next element of the range object is at index 1,
which is the integer object 1. The identifier, index, in the for header is bound to 1 and the suite is
evaluated again.

384
05.15@8:14

Fig. 05.15.24: The Identifier, Index, in the For Header is Bound to 1 and the Suite is Evaluated Again

This time, item is bound to "word list of one", which is “apple”. The upper case version of apple
is computed and then "word list of one" is rebound to this uppercase string. The for statement is
done.

05.15@8:26

Fig. 05.15.25: Item is Bound to word_list[1], which is “apple”. The Uppercase Version “APPLE” is
Computed which word_list[1] is Rebound to

The last line of the program displays "word list". In this attempt, the words in word_list have
been successfully capitalized.

This video introduced some additional repetition examples. You also saw how to use the range
function to create a range sequence object from two integers.

385
05.16 Program Hacking Version 5

You’re ready to program Hacking version 5.

05.16@0:13

Fig. 05.16.01: Create Program Subtasks in Creat Game Diagram

As always, start by adding a new comments to your program for each new algorithm step,
including the new control structures.

05.16@0:44

Fig. 05.16.02: Add New Comments to Your Program for Each New Algorithm Step

386
Since this version includes multiple guess attempts, rather than just one, I will bind an identifier
to the initial number of attempts. I’ll update the header string that refers to the number of
attempts so that is uses this identifier.

Instead of the string “1 ATTEMPTS LEFT”, I will make a new string that concatenates the
number of attempts with the string “ATTEMPTS LEFT”. I will apply the built-in str function to
convert the attempts object from an integer to a string so that I can concatenate it with the
header string.

Every time the player makes a guess, I need to decrement the attempts left. After the first
guess, I will rebind attempts_left to attempts_left minus 1.

If an algorithm step translates to a single statement and the statement is descriptive enough,
you should remove the comment after writing the statement. The comment, "decrement
attempts left" is now redundant, so I will remove it.

I’ll leave the rest of the version 5 programming to you. Each instance of indefinite repetition in
your algorithm will become a while statement in your code. Keep this in mind as you implement
the multiple attempts feature. You must also determine the display location for the lockout
warning in the bottom right corner. Use the screen and string dimensions to compute this
location.

387
05.17 Reflect on Hacking Version 5

In this code review for Hacking version 5, I will trace a while loop using the debugger.

05.17@0:13

Fig. 05.17.01: Review Code Subtask in Create Game Diagram

Here is the version 5 code.

I’ll put a breakpoint next to the while loop and start tracing by pressing the bug button. The
game stops so I can input my first guess. I'll type an incorrect guess.

388
05.17@0:31

Fig. 05.17.02: Trace stops at While Header, where Breakpoint is

The trace stops and the debugger highlights the while header. You can see in the stack data
that attempts_left is was decremented from 4 to 3. You can also see that guess has been bound
to the input string.

389
05.17@0:42

Fig. 05.17.03: guess is Bound to the Input String “SETTING”

The while condition has two expressions as operands to the and operator.

05.17@0:49

Fig. 05.17.04: Two Expressions, guess != password and attempts_left > 0, as Operands to the and
Operator in the While Condition

My guess is not equal to HUNTING so the first operand is true. The second expression checks if
attempts_left is greater than 0, which is true. The and operator evaluates true and true to obtain
true. Therefore the while suite is evaluated. So, when I press the step over button, the next
statement that’s highlighted is a call to draw_string, that uses the x coordinate, line_x, and the y
coordinate, string_height. You can see the value of these coordinates in stack data.

05.17@1:11

Fig. 05.17.04: Call to draw_string is Highlighted After Step Over Button Pressed

I’ll press the step over button again. The debugger highlights the if header. The new value for
attempts left has not appeared in the window since no "window dot update" call was made.

05.17@1:26

Fig. 05.17.05: If Header is Highlighted After Pressing Step Over Button

The "window dot update" call was removed from this code to demonstrate a common practice in
computer graphics. When you make multiple "draw string" calls that can be displayed at the
same time, you can follow them with a single update call. In this case, the window update call is
inside the next input_string function, and you will see this update momentarily.

390
05.17@1:43

Fig. 05.17.06: window.update Call is Inside the next input_string Function and will Update the Window

The if header checks if attempts left is equal to one. Since attempts_left is equal to three, the if
condition evaluates to false, so when I press the step over button again the debugger skips over
the if suite and highlights the next input function call. I'll step over again to evaluate the "input
string" call. The update function inside the input string call, displays everything that was drawn
since the previous update, including the attempts left.

Now I will enter another incorrect guess and step over a few more times until the debugger
returns to the top of the while statement. Again, the while condition is checked. You can see
from stack data that guess is not equal to the password, so the first expression in the condition
is true. Since the number of attempts left is 2, which is greater than zero, the second expression
of the condition is also true. Since true and true is true, the interpreter will evaluate the while
suite again.

I’m going to press the bug button and enter a third incorrect guess. You can see that
attempts_left is 1 in stack data. Now, I’ll step over the code until the debugger reaches the if
statement. Attempts_left is equal to 1 so the if condition evaluates to true and the if suite is
evaluated next.

391
05.17@3:06

Fig. 05.17.07: Attempts_left is 1 so the If Condition Evaluates to True and the If Suite is Evaluated

I’ll press the step over button until I reach the last statement of the if. Three new identifiers have
been added to the namespace and are shown in Stack Data. "warning string" is the lockout
warning. "warning x" and "warning y" are x and y coordinates for this string.

05.17@3:22

Fig. 05.17.08: Call to draw_string to Draw the “warning string” which is the Lockout Warning

To return to the while header, I’ll press the bug button once more and input another incorrect
answer. Attempts_left is now equal to 0. Guess is still not equal to HUNTING but attempts left is
zero, which is no longer strictly greater than 0. Since true and false is false, the while condition
evaluates to false so the suite will not be evaluated again. I will press the bug button to finish
running the program.

05.17@3:38

Fig. 05.17.09: While Condition Evaluates to False, since True and False is False

We won't add any new software quality tests in this version of Hacking. However, you should
take this opportunity to ensure that your code passes the software quality tests you have
already seen. Your code should have program and block comments. Your identifiers should be
descriptive and use lower case letters separated by underscores. Your code should reflect your

392
design. Every algorithm box should translate to a few lines of sequential code, and every
algorithm control structure should translate to a single code control structure.

Except for the prescribed literal exceptions, every literal with a common intent should appear
only once. Finally, check to see if you have replaced your adjacent duplicate statement groups
with repetition control structures.

Congrats! You’ve finished version 5 of Hacking!

393
MODULE 6: HACKING VERSION 6

6.01 Solution Issues in Hacking Version 5

Welcome to Hacking version 6!

06.01@00:19

Fig. 06.01.01: Create Game Diagram Identify Solution Issues Task

You’re almost finished Hacking. Your previous version is only missing two features, embedding
the passwords in random symbols and providing hints when the player makes an incorrect
guess. However, in this version, you won’t implement these new features. Like version 4,
version 6 only concerns code quality. We’re going to introduce one of the most important
language features for improving code quality. This version is all about creating your own
functions!

You have already used a number of functions, including built-in Python functions and imported
functions from various modules. Now you will create your own user-defined functions.

Functions are a very important part of programming! They allow you to decompose your code
into self-contained segments. This makes code easier to read, write, modify, and reuse, since
it allows you to focus on one section of related code at a time. You can apply all the benefits of
using built-in and imported functions to using your own functions.

394
06.01@01:14

Fig. 06.01.02: Problem Solving Diagram Control Abstraction Highlighted

A function call is an example of control abstraction. A function call evaluates the statements
inside the function and then returns to the calling statement. To call a function, you don't need to
understand the internal details of its code. The function's code can be written once and called
many times so it is an application of the DRY principle.

Here is the solution for Hacking version 5.

395
06.01@01:42

Fig. 06.01.03: Repeated Statements in Hacking Version 5 Solution Code

Notice that there are still sections of duplicate code used to display strings. This group of four
statements is repeated three times in the program with only small differences. There is also a
duplicate group of two statements for inputting a string that appears twice.

You couldn’t remove these duplicates in versions 4 and 5 because they were not adjacent to
each other. When you have groups of statements that perform the same function but are
separated by other lines of unrelated code, you have non-adjacent duplicate statement
groups. Non-adjacent duplicate statement groups cannot be removed using repetition control
structures, so we will replace them with user-defined functions.

User defined functions are another example of solution generalization! Recall that solution
generalization alters a solution so that you may re-use it in many different contexts. Whoever
writes a function is performing solution generalization. Whoever is calling a function is using
control abstraction.

The four lines of code that are used to display a string in version 5 are used three times: once
for the header, once for the passwords, and once for the outcome. One of the functions you will
create will transform those 12 lines of code into a single 5 line function that can be used to
display any string.

396
You won’t need to create a new description or functional test plan for version 6. The functionality
of the game is identical to the previous version. You won’t need to create a new algorithm either.
At the algorithm level, your solution doesn’t change. You will go straight to editing your code.

397
6.02 Python - Function Definitions

This video introduces a new kind of statement called function definition.

You have used functions from the Python standard library, but there are situations where you
want to define your own functions. For example, consider a function that prints each individual
letter from a word on its own line to create a vertical banner. There is no function in the Python
standard library that does this. If you want to use such a function, you must create the function
yourself.

You can think of a Python program as consisting of only the statements in a Python file such as
wordlist.py, which was used in the previous lesson. When this "program" is run, the interpreter
creates a module called __main__ that contains the Python statements in this file, and
evaluates the module. However, the program also uses Python code from the standard library
such as the functions input, print, and len, and the method append, so this program really
consists of more than just the statements in this main module.

A complete Python program actually consists of blocks and there are three kinds of Python
blocks: modules, functions, and classes. Classes will be introduced in a future lesson. The
interpreter always starts evaluating the main module, but other blocks are evaluated as well.

Functions are blocks and there are two kinds of functions in Python, user-defined functions and
built-in functions. A user-defined function is a function whose code consists of Python
statements. This is different from the non-Python code used to implement built-in functions.
Built-in functions are written in the language used to implement the Python interpreter. Most
Python interpreters are implemented using the C programming language, since it is easier to
call the necessary operating system functions using C.

Section 2 of the Python Standard Library documentation provides a list of Python built-in
functions. However, this list only includes the built-in functions that are in a module called
"builtins", which can be used without explicitly importing them. In previous lessons, we only
referred to functions in this list as built-in functions. However, the Python documentation actually
defines built-in functions as functions not implemented in Python. We will use this extended
definition of built-in functions, instead of just using the "built-in functions" table.

Most of the modules in the standard library are written in the language used to implement
Python, so they contain built-in functions. A few of these modules are written in Python so they
contain user-defined functions. The Python type of a built-in function or method is
"builtin_function_or_method". For example, both len, and "sleep from the time module" are
built-in functions.

398
Since sleep is not in the "built-ins Function table", we indicated in a previous lesson that it
needed to be imported from the time module. However, since sleep is not implemented in
Python, it is actually a built-in function based on the extended definition, as shown by the type of
the sleep object. The Python type of a user-defined function or method is "function". For
example, the function "copy" from the module named copy in the Standard Library is a
user-defined function.

I will use this program to show you how to write "user defined functions", using the banner
function as an example.

06.02@03:49

Fig. 06.02.01: Banner Example Program

I’ll run the program.

Each letter in the string “blue” is displayed on its own line, preceded by 3 spaces. Then the full
string is printed normally on a single line. Since banner is a user-defined function its type should
be “function”, not "built-in function or method"

399
06.02@04:17

Fig. 06.02.02: Banner Example Program Result and Type Displayed in Shell

Function it is!

Function definition is a new kind of compound statement. I will add "function definition" to the
compound statement syntax diagram. Here is the simplified syntax for function definition:

400
06.02@04:29

Fig. 06.02.03: Syntax Diagram for Function Definition

The header of a function definition consists of: the keyword token def, an identifier, a left paren
delimiter, an optional parameter list, a right paren delimiter, and a colon delimiter. The identifier
is the name of the function. The header is followed by a suite, which is familiar from previous
compound statements.

In this lesson, we will only consider user-defined functions with no parameter list, so I won’t
show the syntax diagram for parameter list.

Before I show you the semantics of function definition, the semantics of binding and
dereferencing identifiers must be generalized.

Every Python statement is part of a single block. For example, the statement on line 1 and the
statements on lines 5 through 8 are in the main module block. The statements on lines 2 and 3
are in the banner function block since they are in the suite of the banner function definition.

In previous lessons, we showed a single program namespace. However each block has its own
namespace. A module namespace is called a global namespace. There are several kinds of
statements that bind identifiers: assignment statements, import statements, for statement
headers, function definitions and other statements you will see in future lessons. When the
interpreter is evaluating a statement, we call the block that contains that statement the local
block and its namespace the local namespace. When the interpreter evaluates a statement in
the main module, it's namespace is both the global and local namespace.

401
Here is the Python rule for binding an identifier in any block.

When an identifier is bound, bind it in the local namespace unless the block also
contains a global or non-local statement that contains that identifier.

For reasons described later in this lesson, we will not use global or non-local statements in this
course, so I will simplify the rule for binding identifiers by removing the last part of the rule. We
also need a rule for dereferencing identifiers in multiple blocks using multiple namespaces.

Here are the semantics for identifier expression, presented in a previous lesson.

06.02@06:54

Fig. 06.02.04: Previous Semantic Rule for identifier

These semantics must be generalized to apply to programs with multiple blocks and
namespaces. It is possible to define a function inside a function. However, in this course no
such nested function definitions will be used. This simplifies the semantics generalization.

Here are the generalized semantics for identifier expression:

402
06.02@07:23

Fig. 06.02.05: Generalized identifier Semantic Rule

Now that the rules for binding and dereferencing an identifier apply to multiple blocks, here are
the simplified semantics for function definition:
1. Create a function object that contains the statement suite as code
2. If the function name is not in the local namespace, add it
3. Bind the function name to the function object in the local namespace
4. Bind a reference to the global namespace in the function namespace

I will show you how the Python interpreter interprets this program.

When the program starts, the local block is the main module, whose namespace is both the
local and global namespace. The pre-bound identifier, print, is bound in this namespace.

The first statement in the main module is a function definition, so the interpreter applies function
definition semantics. In step 1, the interpreter creates a function object that contains the "for
statement" on lines 2 and 3. The function object contains its own namespace. Since the banner
namespace is important in this lesson, it is shown explicitly in the diagram. The print function
object also has a namespace, which is not used explicitly in this lesson, so it is not shown. In
step 2, the identifier banner is not in the local namespace so it is added. In step 3, the
interpreter binds the function name, banner, to the function object in the local namespace. In
step 4, the interpreter binds a reference to the global namespace in the banner namespace.

403
It is important to note that the interpreter does not evaluate the function suite when it is
evaluating the function definition.

After the function definition statement, the interpreter evaluates lines 5 and 6 using assignment
statement semantics. In the local namespace, "word" is bound to the string “blue” and "indent" is
bound to the integer 3. Then the function call to "banner" on line 7 is evaluated.

Recall the function call semantics.

06.02@09:35

Fig. 06.02.06: function call Semantic Rule

The identifier “banner” is dereferenced in the local namespace to obtain the banner function
object that was just created. Since there are no arguments, an empty argument object list is
created and passed to the banner function. The function code is evaluated to obtain a result
object.

I will describe how the interpreter evaluates the code inside the banner function. The local block
is now banner and the local namespace is the banner namespace. The global namespace is still
the main module.

The single statement in the function definition suite uses for statement semantics. In step 1, the
identifier “word” is evaluated. Since the identifier "word" is not bound in any statement of the
local block, banner, it is dereferenced in the global namespace to obtain the string object, "blue".
Any identifier that is dereferenced by rule 2 is used as a global identifier in the local block.

404
"word" is used as a global identifier in banner. In step 2, no error is reported. In step 3, the
identifier, “letter” is bound in the local namespace, to the element of the string object “blue” at
index zero, which is the string object "b". The for suite is evaluated for the first time.

The suite contains a print function call. Since the identifier print is not bound in the local block,
banner, it is dereferenced in the global namespace to obtain the print function object. "print" is
used as a global identifier in banner. There is a single argument expression that contains two
binary operators, star and plus. Since star has higher precedence, it is evaluated first. The left
expression is evaluated to obtain a string that contains one blank space. The star operator on a
string object is implemented by the string replication function that concatenates the first operand
string with itself a number of times equal to the second operand.

Since the identifier "indent" is not bound in the local block, banner, it is dereferenced in the
global namespace to obtain the integer object "3" as the second operand. "indent" is used as a
global identifier in banner.

The string replication function is applied to the single blank character string object, and the
integer 3 to obtain a string object that contains 3 blank characters. The plus operator applied to
this string object is the concatenation operator function. The second operand expression is the
identifier letter. Since "letter" is bound in the for header of the banner block, it is dereferenced in
the local namespace, banner, to obtain the string "b" and the concatenated object is a string
containing three blanks and a “b” character.

This object is passed to the print function and it is displayed.

The for suite is evaluated three more times to display the next three output lines and then it is
done.

Since this is the only statement in the function definition suite, the function call is done.

Step 5 of the function call semantics indicates that a result object is returned from the function
call. A function call returns the unique object whose type is NoneType, unless a return
statement is evaluated during the function call. Return statements will be discussed in a future
lesson. Since no return statement was evaluated, the object whose type is NoneType is
returned.

Evaluating the function code may result in identifiers being added to the function namespace.
After the function code has been evaluated, all identifiers that were added to the function
namespace during evaluation are deleted to free some memory.

The last step of the function call semantics is modified to reflect the deletion of the identifiers
that were added to the function namespace during the function call. The identifier, "letter", that
was added to the function namespace, banner, during the function call evaluation is deleted.

405
The local block is now the main module again, and the local namespace is the main module
namespace. The interpreter then evaluates the statement after the banner function call, which
displays the string "blue" and the program is done.

In many other languages, identifiers are called variables, and global identifiers are called global
variables. Using global identifiers can cause programming errors that are hard to detect. In
general, it is only safe to use global identifiers that are bound to functions, modules, and
classes.

If I don't use global identifiers for “word” and "indent" inside the user-defined banner function,
how can its code refer to the objects "blue" and 3? Recall that you can use function arguments
to pass objects into built-in functions. In the next lesson, we’ll discuss how to add parameters to
user defined functions so you can refer to argument objects instead of using global identifiers.

In this video I introduced the function definition statement, which allows you to create user
defined functions.

406
6.03 Python - Function Parameters

This video generalizes function definition syntax and semantics to include parameter lists that
support function call arguments.

You can call a built-in function multiple times, each time with different arguments such as
print('blue') and print('red'). The same is true for user-defined functions. I will modify the banner
program to display banners for two words, using arguments, and run the program.

06.03@00:35

Fig. 06.03.01: Modified banner Function Result

It works! Two banners are displayed, one for blue and one for red.

To include an argument list in a function call, a corresponding parameter list must be supported
in function definitions. Recall the syntax diagram for function definition.

407
06.03@00:51

Fig. 06.03.02: Recall function definition Syntax Diagram

Here is a simplified syntax diagram for parameter list.

06.03@01:00

Fig. 06.03.03: parameter list Syntax Diagram

408
The banner function definition has valid syntax.

I will show you how the Python interpreter interprets this program.

When the program starts, the local block is the main module, whose namespace is both the
local and global namespace. The pre-bound identifier print is bound in this namespace. The first
statement in the main module is a function definition, so the interpreter applies function
definition semantics, just like the previous lesson.

The interpreter creates a function object with its own namespace, adds the identifier banner to
the local namespace, binds banner to the function object, and binds a reference to the global
namespace in the banner namespace. Recall, that the interpreter does not evaluate the function
suite when it is evaluating the function definition.

After the function definition statement, the interpreter evaluates lines 5 through 8 using
assignment statement semantics. In the local namespace, "word one" is bound to the string
“blue”, "word two" is bound to the string “red”, "indent one " is bound to the integer 3 and "indent
two" is bound to the integer 5. Then the function call to "banner" on line 9 is evaluated.

The function call semantics are modified to support arguments and parameters. A new step 4 is
added before the existing step 4 to ensure that the argument list and parameter list have the
same length. Details are also added to the original step 4, which is the new step 5.

06.03@02:41

Fig. 06.03.04: Updated function call Semantic Rules

409
In step 1, the identifier “banner” is dereferenced in the local namespace to obtain the banner
function object. In step 2, since there are two argument expressions, they are evaluated to
obtain the two argument objects, "blue" and 3 and these two objects are put in a new argument
object list. In step 3, since the expression object, banner is not a method object, no special
argument is added to the argument object list. In step 4, the length of the argument object list is
two and the the length of the parameter list is 2 so no error is reported. In step 5, the parameter
"word" is added to the banner namespace and bound to the argument object, "blue". Then the
parameter "indent" is added to the banner namespace and bound to the argument object, three.

Each parameter is a local identifier, bound during the function call. Notice that the parameter
identifiers, "word" and "indent" are different than the identifiers used in the function calls. In this
example it is impossible for the parameter names "word and" "indent" to match both the
identifier names, "word1" and "indent1" in the first function call, and to match "word2" and
"indent2" in the second function call. The identifier names don't matter since the parameter
identifiers are bound to the argument objects in order. If the parameter identifiers were instead
called "word2" and "indent2", these identifiers would be added to the banner namespace and
bound to "blue" and 3 during this first function call.

In step 6, banner's statement suite is evaluated. The local block is now banner and the local
namespace is now banner's namespace.

Just like the previous lesson, the single statement in the function definition suite uses for
statement semantics. In step 1, the identifier word is evaluated. In this program, the banner
function definition statement bound the identifier "word" as a parameter in banner's namespace,
so the interpreter dereferences "word" to obtain the object blue. In step 2, no error is reported.
In step 3, the identifier, “letter” is bound in the local namespace, to the element of the string
object “blue” at index zero, which is the string object "b". The for suite is evaluated for the first
time.

Evaluation of the banner function continues as in the previous lesson, except that the parameter
"indent" is dereferenced in the banner namespace instead of the global namespace. The string
"blue" is printed as a banner. The result object is the unique NoneType object and step 6
finishes by deleting "letter", "indent" and "word" from the banner namespace, since they were
added during the function call.

The interpreter returns to the main program and evaluates the next statement, which is another
function call to banner. This call is evaluated the same way as the previous call, except this
time, in the banner namespace, the parameter "word" is bound to the argument object "red" and
the parameter "indent" is bound to the argument object 5. The string "red" is printed as a
banner, the same NoneType result object is returned and "letter", "indent" and "word" are again
deleted from the banner namespace.

410
In this video, I generalized the function definition syntax and semantics to include parameter
lists that support function call arguments.

411
6.04 Python - Main Function and Identifier Scope

This video introduces the convention of using a main function and the concept of identifier
scope.

Recall the program from the function parameters lesson.

06.04@00:20

Fig. 06.04.01: banner Function from 06.03

What will happen if I make an error in the function definition by using the identifier word1 in the
for statement header instead of using the identifier word?

I’ll run the program.

412
06.04@00:33

Fig. 06.04.02: banner Function with word1 “Mistake”

The program displays two banners for "blue", one with indent 3 and one with indent 5. I have
"accidentally" used the global identifier, "word1". In both function calls to banner, when the
interpreter evaluates the for statement header, it dereferences word1, instead of word. Since
word1 is not bound anywhere in the function definition statement, it is a global identifier that is
dereferenced in the global namespace to obtain the string object "blue" during both calls to the
banner function.

Many experienced Python programmers use a convention to prevent the "accidental" use of
such global identifiers. They introduce another user-defined function, called “main”.

For example, here is a program containing the same accidental use of "word1", which uses a
main function. This new main function contains all the statements that were previously in the
main module, except for the banner function definition.

413
06.04@01:17

Fig. 06.04.03: banner Function with New main Function Convention

"word1", "word2", "indent1" and "indent2" were previously bound in the main module's
namespace, which means they could be used as global identifiers in banner. They are now in
the main function namespace, so they can no longer be accidentally referenced inside banner
or any other user defined function. We call this function main since it contains most of the
statements that were in the main module previously.

In general, you should move all of the statements from your main module to the main function
except for: import statements, function definitions, and class definitions. Class definitions will be
introduced in a later lesson. It’s important to recognize that the main module and main function
are different. There is still a main module which only contains the two function definition
statements, one for main and one for banner.

I’ll run the program.

There is no output! Remember that a function definition does not evaluate its suite. I need a
function call to evaluate the main function suite! I’ll add a single function call to main() at the top
of the main module and run it again.

414
06.04@02:49

Fig. 06.04.04: banner Function Program with Undefined main NameError

A NameError indicates that “main” is not defined, since it has not yet been bound to a function
object. Like any other identifier, a function name must be bound before it is used. Since a
function definition is used to bind a name to a function object, the function definition for main
must come before the function call to main. I’ll fix this and run it again.

415
06.04@03:07

Fig. 06.04.05: banner Function Program word1 Error Reported

A different name error is reported, indicating that word1 is not defined! This error reports my
"accidental" use of the identifier "word1" inside banner. It is a useful error since it prevents me
from using an identifier that would have been a global identifier, if I had not used a main
function. In general, it is better to have the interpreter report an error than to silently let the
program compute the wrong answer due to a programmer's mistake.

I will show you why this error was generated.

When the program starts, the local block is the main module, whose namespace is both the
local and global namespace. The pre-bound identifier print is bound in this namespace. The first
statement in the main module is a function definition, so the interpreter applies function
definition semantics, just like the previous lesson.

The interpreter creates a function object with its own namespace. It adds the identifier main to
the local namespace, binds main to the function object, and binds a reference to the main
module namespace in the main function namespace. Since the next statement is a function
definition for banner, the interpreter repeats these function definition semantics for banner.

The last statement in the main module is a function call to main, so the interpreter applies the
function call semantics. The identifier “main” is dereferenced in the local namespace to obtain
the main function object. Since there are no arguments and the main function is not a method,
an empty argument list is created. The parameter list and argument list both have length zero so

416
no error is reported. There are no parameters to add to the function namespace. The "main"
function code is evaluated to obtain a result object.

The local block is now the main function and the local namespace is the main function
namespace. The first four statements of the main function suite are assignment statements. In
the local namespace, the interpreter binds “word1” to the string “blue”, "word2" to the string
"red“, "indent1” to the integer 3," and "indent2" to the integer 5.

Next the interpreter evaluates the first function call to banner. In step 1, the interpreter evaluates
the identifier banner to obtain a function object. Since banner is not bound in the main function
block, it is dereferenced in the global namespace to obtain the banner function object. "banner"
is used as a global identifier in main. In step 2, since there are two argument expressions, they
are evaluated to obtain the two argument objects, "blue" and 3 and these two objects are
referenced in a new argument list. In step 3, since the expression object, banner is not a
method object, no special argument is added to the argument list. In step 4, the length of the
argument list is two and the length of the parameter list is 2 so no error is reported. In step 5,
the parameter "word" is added to the banner namespace and bound to the argument object,
"blue". Then the parameter "indent" is added to the banner namespace and bound to the
argument object, 3. In step 6, the function code of banner is evaluated.

The local block is now the banner function and the local namespace is the banner function
namespace. The global namespace is still the main module, not the main function.

The single statement in banner uses for statement semantics, so the expression, "word1" is
evaluated. Since the identifier "word1" is not bound in the local block, it is not dereferenced in
the local namespace. The interpreter looks for "word1" in the global namespace and does not
find it, so an error is reported. Moving the identifier "word1" from the global namespace to the
namespace of the main function, has prevented an accidental reference to a global identifier.

Programming languages use a concept called scope to limit the visibility of identifiers.
The scope of an identifier binding, refers to where in the code the binding can be used. Since
Python code is divided into blocks, the scope is all of the blocks where dereferencing the
identifier will use that binding.

For example, the scope of the "word1" binding in the main function namespace of this program
is only the main function block. Its scope does not include the banner function block. Since no
other binding of word1 exists in the program, a reference to word1 in the banner function block
generates a name error. The scope of the "word1" binding in the main module namespace of
the previous program is both the main module block and the banner function block.

The reference to word1 in the banner function block uses the semantic rule for identifiers to
accidentally access a global identifier. In this course, we will use a main function to remove all

417
identifiers from the global namespace, except for imported identifiers and user-defined function
names.

Identifier scoping is an example of encapsulation. Encapsulation is a technique for restricting


access to the implementation of a component so that the implementation of that component is
independent of how it is used by other components.

Consider this scenario. I am a new developer working on a very large code base. I want to add
new functionality, but there are already thousands of identifiers currently in use. Without limited
scope, if I rebind one of these identifiers in my function, it could have disastrous effects on the
other functions and modules that use this identifier. I would need to meticulously check every
existing function and module to make sure I didn’t use the same identifier that someone else
used in their code. Wouldn’t that be unpleasant and error-prone?

Because scope limits the visibility of identifiers, I can use any identifiers I want in my functions
without disrupting the same identifiers in other functions. Every identifier binding I move from the
main module to the main function reduces the scope of that identifier binding, so it cannot be
accidentally accessed in a user-defined function.

This video introduced the convention of using a main function and the concept of identifier
scope.

418
6.05 Python - Return Statement

This video introduces a new kind of statement, called a return statement.

Some functions compute and return a result object, such as the built-in function, "input". Some
functions have side effects, such as displaying an object, like the print function or the original
banner function. Neither of these functions returns a useful object.

Identifiers bound in a function's namespace are deleted once the interpreter finishes evaluating
the function. It is often useful to reference an object created in a function after these identifiers
are deleted. For example, if we couldn't get back, the string object that was created by the input
function, it would be useless. That’s where the return statement comes in. A return statement is
a new kind of statement that can only be used in a function suite. A function's return statement
evaluates an expression and passes the result object back to the block that called the function.

Here is a modified banner program. This banner function returns a new list object containing all
the strings that make up the banner. The main function prints the banner. I’ll run the program.

06.05@01:12

Fig. 06.05.01: Modified banner Function Program with Result

Like a previous lesson, the word is printed as a banner and then on a single line.

419
The return statement is a new kind of simple statement so I will add it to the syntax diagram for
simple statement.

Here is a simplified syntax diagram for the return statement.

06.05@01:23

Fig. 06.05.02: return Statement Syntax Diagram

A return statement consists of the keyword token return followed by an optional expression.
Unlike other simple statements, a return statement can only be used in a function definition
suite. I'll show you that a return statement used outside a function definition results in a syntax
error.

420
06.05@01:40

Fig. 06.05.03: Invalid return Statement Error Report

I'll remove this "invalid" return statement and run the program again.

Here are the simplified semantics of a return statement:

1. If an expression is present, evaluate it to obtain the result object. Otherwise use the
unique NoneType object as the result object
2. Return the result object to the calling block, without evaluating any more function
statements

Let’s apply semantic rules to the banner program.

As usual, when the program starts, the local block is the main module, whose namespace is
both the local and global namespace. The pre-bound identifiers are bound in this namespace.

The main function definition is evaluated to create a function object that contains its namespace
and code. The function name, main, is not in the local namespace so it is added. The interpreter
binds main to the function object in the main module's namespace, and binds a reference to the
global namespace in the main function's namespace. The same semantic rules are repeated for
banner.

421
Next, the main function call is evaluated. Since there are no arguments and main is not a
method, an empty argument list is created and passed to the main function object. Then the
main function's code is evaluated.

The first two statements of the main function are assignment statements that use the local
namespace to bind “banner_word“ to “blue“ and “banner_indent“ to 3. The next assignment
statement binds “my_banner“ to the result of the banner function call.

The interpreter evaluates this statement by first evaluating the banner function call. In step 1,
the banner identifier is evaluated to obtain the banner function object from the global
namespace. In step 2, the argument list is evaluated to obtain the two argument objects, “blue”
and 3. In step 3, banner is not a method so the argument list is not modified. In step 4, the
length of the argument object list is 2 and the length of banner’s parameter list is also 2, so no
error is reported. In step 5, the parameters “word“ and “indent“ are added to banner’s
namespace and bound to the argument objects “blue“ and 3, in order. In step 6, banner’s
statement suite is evaluated, so the local block is now banner and the local namespace is now
banner's namespace.

The first statement in banner is an assignment statement. In the local namespace, the identifier
"new_banner" is bound to a new empty list object. The next statement is a for statement. For
every letter in "blue", the interpreter creates a new string containing 3 spaces and the target
“letter”, and then appends that string to the list.

The next statement is a return statement. In step 1 of the return statement semantics, the
interpreter evaluates “new_banner“ to obtain the result object, which is a list object containing
the banner strings. In step 2, the interpreter returns the result object to the main function where
the call to banner occurred.

Returning to step 6 of the function call semantics, the identifiers, “word”, “indent”, “letter”, and
“new_banner” are deleted from the banner namespace.

Returning to the assignment statement in the main function, the identifier, “my_banner" is added
to the main function namespace and bound to the returned list object.

The final two statements are a for statement that displays the banner, and a print call that
displays the string “blue“ on a single line. The identifiers that were added to the main function
namespace during the function evaluation are deleted.

The program is done!

This video introduced the return statement, which is used in a function definition to pass a result
object back to the calling block.

422
6.06 Python - Side Effects

This video introduces side effects, a way to modify an object inside a function so that the
modification can be used outside the function.

Besides using a return statement to return a result object, a function can also modify existing
objects, as long as they are mutable. Such objects can be passed as arguments or accessed
using global identifiers. However, using global identifiers to modify mutable objects is not usually
a good idea.

06.06@00:40

Fig. 06.06.01: count_case Example Program

This program uses the function count_case to modify an existing list object that is passed as an
argument.

In the main function, the count_case function is applied to each word in a list of words. After
each call to count_case, the word is printed, as well as the total lower and uppercase letter
counts for all of the words so far.

I’ll run the program.

423
06.06@01:25

Fig. 06.06.02: count_case Example Program Result

The count_case function has two arguments, a string and a list. It iterates over the string
elements and counts each lowercase and uppercase letter by incrementing a value in the
argument list. No return statement is needed in count_case, since the function modifies the list
argument. When "count_list" is printed in the main function, it reflects the changes made to the
list by the count_case function.

I will apply semantic analysis to this program.

As usual, when the program starts, the local block is the main module, whose namespace is
both the local and global namespace. The pre-bound identifiers are bound in this namespace.

The function object "main" is created. The name "main" is added to the local namespace and
bound to this object. A reference to the global namespace is added to the function namespace.
These semantics are repeated for "count_case".

Next, the identifier of the main function call is evaluated to obtain the main function object. An
empty argument list is created and passed to the main function object, which then evaluates its
code. Local block and local namespace are updated.

The first two statements of the main function are assignment statements that create two new
lists. The first list contains three string objects with various characters. In the local namespace,

424
"Word list" is bound to this first list. The second list is a two element list, where both elements
are 0. "Count list" is bound to this second list.

Next, the for statement is evaluated. First the identifier "word" is added to the main function’s
namespace and bound to the element in the word_list at index 0.

Then the count_case function is evaluated. In step 1, in the global namespace, the
"count_case" identifier is dereferenced to obtain the "count case" function object. In step 2, the
argument list is evaluated to obtain two argument objects, the string “hELLo” and the two
element list object that counts character cases. In step 3, "count case" is not a method so the
argument list is not modified. In step 4, the length of the argument object list and parameter list
are both 2, so no error is reported. In step 5, the parameters “string” and “counts” are added to
count_case’s namespace and bound to the argument objects “hELLo” and the two element list.
In step 6, count_case's statement suite is evaluated. The local block and namespace are now
count_case.

In count_case, the "for statement" is evaluated. The identifier "letter" is added to count_case's
namespace and bound to the string element, whose index is zero. So, "letter" is bound to the
lowercase string "h".

Next, the first if statement is evaluated. Since "letter" is bound to the lowercase string "h", when
the "is lower" method is called, the method returns True and the if statement suite is evaluated.

Subscription is used to access the list element at index zero, add one to it to obtain the result
object one, and rebind the zero index to this result object. This binding is in the namespace of
the list, not the function, so it is accessible anywhere the bindings of the list are accessible.

Changing this list element in the count_case function is a side effect. In the namespace of the
main function, count_list is bound to the list object. In the local namespace of the count_case
function, counts is bound to the same list object.

When the count_case code modifies the list object using the counts reference, it is modifying
the same object that count_list is bound to in the main function namespace.

A side effect occurs when a subsequent binding to an object is used to modify that object and
the change is observed when the original binding is used as a reference.

In this case, each element in a list has its own binding, but these bindings are inside the list
object and are accessible anywhere the entire list object is accessible.

In the second if statement, since "h" is not an uppercase letter, the suite is skipped.

425
The for statement header is evaluated again. The target identifier, letter, is rebound to the next
element in the string, which is the uppercase letter "E". The first if statement header is
evaluated. "E" is not a lowercase letter, so "is lower" returns false and the suite is not evaluated.

The second if statement is evaluated. Since "E" is an uppercase letter, the "is upper" function
returns true and the suite is evaluated. Subscription is used to access the list element at index
one, add one to it to obtain the result object one, and rebind the one index to this result object.

The for statement suite is evaluated again for each remaining letter in the argument string. Then
the function is finished so the interpreter deletes the identifiers, "string", "counts", and "letter". It
then returns the NoneType object to the main function, which ignores it.

The local block and namespace are updated to refer to the main function, once again.

The word and the two elements in the count list are printed. The local identifier, "count list" is
used to reference the list that was changed in the count_case function. The modifications made
by the count_case function are successfully printed.

In the main function, the for statement suite is evaluated again for each of the remaining words
in the word list. Each time the "count case" function is called, it causes a side effect on the two
element list.

Then the main function is finished, so the identifiers, "word_list", "count_list", and "word" are
deleted from its namespace.

The program is done.

This video introduced a second way that a function can affect objects in a program. Instead of
using a return statement, which passes an object back to the calling block, a function can
modify a mutable object to produce a side effect.

426
6.07 Program Hacking Version 6

You’re ready to program Hacking version 6! This version is all about using functions to improve
your code quality.

06.07@00:21

Fig. 06.07.01: Game Creation Diagram Write Code Task

When you previously added a new control structure to improve your code quality, it required you
to modify the structure of your solution at the algorithm level. Functions also act as control
structures, since they determine when blocks of code are performed. However, implementing
functions in Hacking version 6 does not require you to modify your algorithm. Your complete
version 5 design is still relevant in version 6.

Creating functions is very similar to decomposing your algorithm into panels, which you have
been doing all along! Almost every panel in your algorithm will become a function in your code.
The title of the panel will be the name of the corresponding function. There will also be two
functions that are not represented by panels in the algorithm.

Functions are used to create clear, readable code. They divide code into small segments, so
that each segment can be individually written and tested. They also consolidate statement
groups with similar functionality.

427
I’ll start with the first rectangle in the main program panel: “create window”. “Create window” will
become a function in my version 6 code. I’ll replace the “create window” comment with the
function header “def create window”. Then I’ll indent all the statements that belong to the suite
of this function underneath the header.

06.07@01:32

Fig. 06.07.02: Create Window Function Definition Line 12-17

I will introduce a main function into Hacking to limit the accidental use of global identifiers. The
main panel of my algorithm will become the main function in my program. I need a call to “create
window” in the suite of the main function, so I’ll create the main function now.

Above the “create window” function, I’ll write a new function header “def main”. Inside this main
function, I’ll add a call to “create window”.

Now that I have written two functions, I should test them. I’ll run the program but I get a name
error for window in the code below my functions.

428
06.07@02:04

Fig. 06.07.03: main Function Definition (Line 12 and 13) with window NameError

The code in the main module doesn’t have access to the window binding inside of the “create
window” function. However, I still need to test my new functions! To do this, I could just delete
the rest of this code, but I’ll paste it into a new document instead.

I need to use most of this code later!

Everytime I write a new function, I need to test that function. This process is known as unit
testing. Every small unit of code should be individually tested to ensure it works, before it is
used in the full program. In this course, we will generally treat each function as a unit and test
each function as it is written.

To test “create window”, I only need to add a call to main at the bottom of the program, since
main already includes a call to "create window".

429
06.07@02:59

Fig. 06.07.04: Successful Test of main and create_window

This time, when I run my program, I don’t get any errors! However, since I’ve removed the code
that closes the window, the window doesn’t close anymore. I’ll deal with that later. Instead, I’ll
move onto the next function. The next step after “create window” is “display header”, so I’ll write
that function next.

I'll start by writing “def display header”. In the suite, I’ll paste the old version 5 code that displays
the header. Then I’ll add a call to “display header” in main and run the program again to test my
new function.

430
06.07@03:21

Fig. 06.07.05: New display_header Function Definition Lines 22-34

A name error reports that window is not defined, since it is not bound in "display header". There
are two aspects to this problem. First, the scope of the identifier, "window", is restricted to the
"create window" function. I need to return the window object, so it is accessible outside of
“create window”. Second, the window object has to be accessed inside another function,
"display header". I will add a window parameter to “display header”, so that I can pass the
window object as an argument.

I’ll add a return statement to “create window” that returns window. I’ll bind a new identifier called
window to the return object of the “create window” function call in main. I can then pass that
window object into “display header” as an argument! I’ll also add a parameter called window to
the “display header” function definition.

I’ll run my program again and it works! A window opens and the header is displayed.

Although I have used the same identifier “window” in the main, "create window", and "display
header" functions, I can use any name in each of these separate blocks. I will illustrate this by
changing the name in main to "wilma".

431
06.07@04:34

Fig. 06.07.06: Successful wilma Identifier Name Test

The program still works! I'll change it back since wilma is not a very descriptive identifier for a
window.

The next step is to display the password list. Notice that “display header” and “display password
list” both use those four familiar lines of code: a call to draw string, a call to update the window,
a pause, and an assignment statement to update "line y". We’ve said before that functions
consolidate code with similar functionality. If I’m going to use those four lines of code over and
over again, I should write a function that contains those lines. Then I can call the function
whenever I want to use that code.

As always, I’ll start with the function header by writing “def display line” underneath the last
function definition. I’ll cut and paste the four lines that display a string from “display header” into
the “display line” suite.

432
06.07@05:30

Fig. 06.07.07: New display_line Function Definition Lines 35-39

I want this function to be general enough to display a header, password, or outcome line. This is
an example of Solution Generalization. It should be able to display any string I pass as an
argument. I’ll replace header_line in the "draw string" call with the identifier “string” and then add
string as a parameter in the function header. I will also add a window parameter so that I can
draw and update the window. To make it easier to remember the parameter order, I will always
put the window parameter first.

A location is also required to display each string. This function must change the y coordinate of
the location after displaying the current string. To change the argument object in the function, I
will use a side effect on a mutable argument object. I will use a list containing two coordinate
integers as an argument.

I’ll add the identifier “location” to the parameter list of "display line". I will replace "line x" and
"line y" in the "draw string" call with location of 0 and location of 1. I will replace "line y" in the
update assignment with location of 1 as well.

433
06.07@06:30

Fig. 06.07.08: display_line Function Definition with window and location Lines 35-39

The identifiers "pause time" and "string height" are bound in "display header" but are only used
in "display line". I will move these assignment statements to "display line", where they are
needed.

To complete the function I must add a block comment at the top of the function that describes
both the function behaviour and what each parameter is for, including its type.

434
06.07@06:51

Fig. 06.07.09: display_line Function Definition with Block Comment Lines 34-40

Now I can use my "display line" function in "display header", "display password list" and "display
outcome". I will update the code in "display header" to use this function.

When adding a function call that does everything described in a comment, you should replace
the comment by the function call. For example, I will replace the redundant comment, "display
header line" by the function call "display line".

I need three arguments in the right order. I will use “window” as the window argument and
"header line" for the string argument. The last argument must be the location, which is a list
containing the x and y coordinates for display.

I haven't created a location list yet in the program. If an object is needed in more than one
function it must be created in a block that can pass the object as an argument to all of the
functions where it is needed.

I should create the location list in the main function since it is used in three different functions
called in main: "display header", "display password list", and "get guesses". It is also used in
other functions called by each of these three functions.

I will pass this location list to "display header".

435
Finally, I will modify "display header" to include a location parameter and remove the “line x” and
“line y” assignment statements.

06.07@08:12

Fig. 06.07.10: Completed display_header Function Definition Lines 25-29

This function is done!

Now it’s your turn. You must create the following functions and call them in the main function:
“display password list“, “get guesses“, and “end game“. Inside “get guesses“ you must call
"prompt for guess" and "check warning" functions.

Inside “end game“ you must call “display outcome” and "prompt for end" functions. You must
also create a function definition for each function you call. However, "prompt for guess" and
"prompt for end" are similar enough that you should make a single function called "prompt user"
and call it instead.

The function "prompt user" will eliminate a duplicate statement group. Creating "prompt user" is
similar to creating "display line".

You will discover that the identifier attempts_left is used in more than one of the user-defined
functions. You must decide where to bind it and how to make it available everywhere that it is
needed.

436
Every time you create a new function you should test it. Don't forget to include a descriptive
comment for all of the functions you write, including the functions I wrote.

437
6.08 Review Code for Hacking Version 6

Let's reflect on Hacking Version 6! For this reflection, I’ll show you how to trace user defined
functions.

06.08@00:16

Fig. 06.08.01: Game Creation Diagram Review Code Subtask

Here is the solution for Hacking version 6. It begins with the program comment which hasn’t
changed since version 5. Next are the two import statements we’ve been using since version 2.
The first change is the definition of the main function. In the programming demonstration, we
said that almost every panel in the algorithm must be translated into a function, and here they
are!

438
06.08@00:17

Fig. 06.08.02: Hacking Version 6 Solution Code

Let’s compare the main function definition with the algorithm. The first two assignment
statements aren’t in the algorithm since they are simple assignment statements. However, the
next five statements all correspond to rectangles in the algorithm, since each is a function call!
That’s pretty handy!

Let’s trace. To start, I’ll press the “step into” button to begin the trace. The trace starts at the
import statements, which I will step over. When I press “step into”, the entire function definition
is evaluated by adding the function name to the program's namespace in one step. No
statements in the suite are evaluated. Therefore the next function definition is highlighted.

I’ll place a breakpoint next to the main function call at the very bottom of the program.

439
06.08@01:23

Fig. 06.08.03: Breakpoint Next to main() Function Call Line 181

Next, I’ll press the bug button to reach the main function call. I'll press the step over button now.
The debugger evaluates the entire main function and all of the functions it calls in one step, so I
can't trace individual function calls.

If there had been another statement after the main function call, stepping over main would have
played the entire game and then highlighted that additional statement. For example, I will stop
the trace, add a statement after main and press the bug button again. After entering 'HUNTING'
in the game window, the program displays the correct outcome, closes the window and exits
main. The debugger then highlights the statement I just added.

440
06.08@02:03

Fig. 06.08.04: Statement After main() Highlighted Line 182

I’ll stop the trace and press the bug button again. The main function call is highlighted. Recall in
versions 1 and 2 of Hacking, we used the step over button so that you could ignore the code
that was "inside" the built-in and imported functions. This time we want to examine the code
"inside" the main function so I’ll press the step into button to examine its statements.

The first assignment statement in main is highlighted, I’ll step over the first two assignment
statements to get to the first function call in main, called create_window.

Before I step into this function, notice that Stack Data has two identifiers, attempts and location.

441
06.08@02:45

Fig. 06.08.05: attempts and location Under locals in Stack Data

Recall that functions are blocks that have their own namespaces. Since we are inside the
definition of the main function, locals represents the function's namespace. When I press the
step into button, the first statement of create_window is highlighted. In Stack Data, location and
attempts no longer appear. This is because location and attempts are not in the namespace of
create_window, they are in the namespace of the main function.

The first four statements in create_window are familiar from previous versions, so I’ll step over
them until the return statement is highlighted. Stack Data now shows the window identifier in the
namespace of create_window.

442
06.08@03:28

Fig. 06.08.06: window and return Under locals in Stack Data

Now, I’ll press the step into button. The return statement is still highlighted, and in Stack Data I
also see that the return value of the function is a window. Notice that return value and the
window object have the same memory address so they are the same window object.

I’ll press the ‘step over' button again. This time, the display_header function call is highlighted.
Take a look at Stack Data now. Since the assignment statement added window to the main
function's namespace, window appears in locals. Window was bound to the return object of the
create_window function. Window in the main function refers to the same object as window in
create_window, since they have the same address.

443
06.08@04:00

Fig. 06.08.07: attempts and window Addresses Under locals in Stack Data

Before I step into the display_header function, notice that attempts is bound to 4 and note the
addresses of the location and window objects.

I’ll step into the display_header function. Take a look at Stack Data. Even though no statements
have been evaluated in this function, there are three identifiers in locals: attempts, location and
window. Since display_header has three parameters, when the function is called, each
parameter is added to the namespace and bound to its corresponding argument, in order.
Notice that attempts is 4 and the memory addresses of the other two objects are the same.

I’ll step over the header assignment statement. In Stack Data, header has been added to locals
since header has been bound in the local namespace.

Next, I’ll expand the location list. Notice that the zero and one indexes are bound to zero before
the call to display_line.

444
06.08@04:49

Fig. 06.08.07: location List Expanded in Stack Data

Now, I’ll step into the for statement and then into the display_line function call.

In the display_line function definition, the pause_time assignment statement is highlighted.


Notice in Stack Data, that the window, location and string parameters have been added to the
local namespace. Also notice that the last statement in this function changes the value of
location of “one” to the string height. I will press the step out button to complete the evaluation of
this function.

445
06.08@05:35

Fig. 06.08.08: Index one of list Bound to String Height (19) in Stack Data

Evaluation returns to the calling function, display_header, and the header of the for statement is
highlighted. Looking in Stack Data, you can see that index one of location has been rebound to
the string height.

446
06.08@05:57

Fig. 06.08.09: Index one of list Bound to 57 in Stack Data

When I step out again, evaluation returns to the calling function, main and the password
assignment statement is highlighted. Notice that location of 1 is bound to 57, since the for
statement evaluated "display line" two more times, when I pressed the "step out" button. The
changes to location in "Display line" are side effects.

Let's stop tracing and introduce the new software quality tests for hacking version 6.

The first new set of tests is a new section of Comment tests, for user-defined functions. In
addition to placing comments at the start of the program and above logical code blocks, you
must add a comment to each user defined function, except for main. You don't need a comment
for the main function, since it is described by the program comment and it doesn't have any
parameters. Each function comment must be at the start of your function, describe what the
function does using one or two verbs, be indented one level from the function definition, and
describe each function parameter. Each function parameter should use a one line comment to
describe its role and type.

The second new set of tests forms a Main Function section. The two new main function tests
are “The program includes a user-defined function called main” and “In addition to import
statements, there is only a single line of code that is not part of any user-defined function, and
that line is a single call to the main function.”

You may see Python programs elsewhere that don't include a main function. However, in this

447
course, every program you write will include a main function definition and a single call to that
function. This will reduce errors caused by accidentally accessing global identifier bindings.

In the previous 5 versions, all the identifiers you used were global identifiers. From now on, you
will use local identifiers in user-defined functions. Having many local namespaces limits the
scope of identifiers. This allows multiple programmers to work more independently since any
identifier can be used locally inside a function.

The third and final test set is a new section titled User Defined Functions. Each of your functions
should be simple enough that you don’t need to write an essay to describe it. A good way to test
this is to check whether it performs one logical task that can be described by a sentence with
one or two verbs. The second test is also designed to ensure that your functions are simple.
Each function should have no more than 5 arguments, and no more than 12 statements. These
numbers are a bit arbitrary, but they are good choices for this course.

Check that your code follows both the new and previous Software Quality tests.

Congratulations! You’ve finished version 6 of Hacking. Only one more version left!

448
MODULE 7: HACKING VERSION 7

07.01 Solution Issues in Hacking Version 6

Before getting started with Hacking version 7, let’s look at how far you’ve come. You started
from a simple text-based game and turned it into a graphical one. You modified your design and
code to include multiple guesses, selection, repetition, and user-defined functions! To do all this,
you learned to use many different Python expressions and seven different Python statements,
including four control structures: "if", "for", "while", and "function definition". You have also
learned three problem-solving strategies: problem decomposition, algorithms, and abstraction.

07.01@00:43

Fig. 07.01.01: Problem Solving Diagram

You will use all of these techniques in the next game and any future projects you create. But,
before starting the next game, you must complete the final version of Hacking!

449
07.01@00:57

Fig. 07.01.02: Game Creation Diagram Identify Solution Issues

The remaining solution issues for Hacking are about missing features. It’s difficult to make
progress in a password guessing game if you are not given feedback about how close your
guesses are to the secret password. Additionally, a good way to make your game more
interesting is to add graphical elements that add atmosphere.

In this final version, you will practice the skills you have learned so far. You will add the final
features for this game, which are hint messages for incorrect guesses and embedding all the
password strings in random symbolic characters. You will make a new description, functional
test plan, and algorithm in the upcoming activities. Your description must be adjusted to account
for the two new features you will see when you observe the game, and your functional tests
need to be adjusted to test any edge cases created by these changes.

450
07.02 Observe Hacking Version 7

In this video, you will observe the final version of Hacking. Remember, you have already
observed this version of the game. It was the very first game you saw at the beginning of the
course!

07.02@00:14

Fig. 07.02.01: Game Creation Diagram Observe Game Version

Let’s take a look at it one more time.

To start, notice the symbolic characters in the password list that weren't there in any previous
version.

451
07.02@00:29

Fig. 07.02.02: Hacking Version 7 Start State

I’ll input the incorrect answer FINDING. The second new feature in this version is the addition of
a hint message in the top right corner. It indicates that my guess is incorrect and displays how
many letters of my guess match the letters of the secret password. FINDING has three correct
letters in matching positions with HUNTING.

452
07.02@00:39

Fig. 07.02.03: Hacking Version 7 FINDING Output

Next I’ll try “hunting” in lowercase letters, which gives me zero out of seven matching letters.
This is a good visual confirmation of one of the tests from previous versions.

07.02@00:57

Fig. 07.02.04: Hacking Version 7 hunting Output

453
Finally, for my last incorrect answer, I’ll try “PUTTING”. This gives me five of seven matching
letters and also results in the lockout warning in the bottom right of the window.

07.02@01:11

Fig. 07.02.05: Hacking Version 7 PUTTING Output

Inputting “HUNTING” brings me to the familiar success outcome screen.

454
07.02@01:16

Fig. 07.02.05: Hacking Version 7 HUNTING Output

As you play the game in the next activity, pay attention to the symbolic characters used in the
password list. Notice which symbols are used, whether the same symbols are selected for each
line each time you run the program, and where the symbols are placed.

For example, are there the same number of symbols selected for each password and does the
number of symbols before and after the password change?

455
07.03 Create Algorithm for Hacking Version 7

This is the final algorithm for Hacking.

07.03@00:12

Fig. 07.03.01: Game Creation Diagram Create Algorithm

You will add two new features: embedding the passwords in random symbols and displaying
hints. You will need two new panels for each of these features. You will also need to modify two
existing panels.

I’ll start the steps for embedding the passwords. Your previous algorithm used a definite
repetition control structure to display each password line. I’ll change the repeated step from
"display password line" to “display embedded password”.

456
07.03@00:40

Fig. 07.03.02: New Repeated Step in Display Password List Panel

Since I must embed each password, the blank line that is displayed after the password list can
no longer be an element in the password list. If it is, the blank line will also be embedded in
random symbols! I will add a step to display the blank line prior to selecting the password.

07.03@00:58

Fig. 07.03.03: Display Blank Line Step in Display Password List Panel

457
To display an embedded password, first I must add symbols to the plain password and then I
must display it. Each of these actions is complicated enough to have its own algorithm step.
Since you cannot put two consecutive steps inside a control structure, "display embedded
password" must be expanded into a new panel. You must also make an additional new panel to
expand “embed password”, since it will require many program statements, including control
structures.

Here are a few things to keep in mind.

Each embedded password string has seven password letters and thirteen random symbols,
selected from a set of 18 symbols. Since each line contains 13 symbols, a random number of
symbols between 0 and 13 will appear before the password, and the remainder will appear after
the password. For example, here are three embedded passwords.

07.03@02:21

Fig. 07.03.04: Embedded Password Example

The first, has 5 before the password and 8 after. The second has zero before, and 13 after, and
the third has 13 before and 0 after.

You must compute how many of the 13 symbols to add before the password and how many to
add after. This split in the random symbols, where the password is added, will be called the split
index. For example, in the lines above the split indexes are 5, 0, and 13. You should use two
separate definite repetition control structures: one to select and concatenate the random

458
symbols that come before the password, and one for the random symbols that come after the
password.

To display hints, you will also need two new panels. Add a “display hint” step to the appropriate
place in the “get next guess” panel and expand it. Displaying the two lines of the hint is
straightforward, but you will need to use two different control structures to check if the letters in
the guess match the letters in the password. When you check a letter in the guess against a
letter in the correct password, don't forget to ensure that the index you are checking is not
beyond the end of the password!

You will finish your last Hacking algorithm in the next activity.

459
07.04 Program Hacking Version 7

07.04@00:12

Fig. 07.04.01: Game Creation Diagram Write, Test, Debug Code

Programming the final version of Hacking gives you an opportunity to create more function
definitions. You must modify display_password and get_guesses. You will also write two new
functions: embed_password and display_hint.

I will start writing embed_password for you, beginning with the function header. I'll replace the
comment "embed password", with the line "def embed password". I’ll also add the function
comment for you.

460
07.04@00:40

Fig. 07.04.02: embed_password Function Header and Function Comment Lines 62-64

I already know I need a password to embed, so I will add password to the parameter list. I’m
also going to put size in the parameter list, which will require me to pass in the desired length of
the embedded password string. By passing this into the function, instead of defining it inside the
function, I can easily change how much padding is added to each embedded password. I’ll add
a line for each parameter to the function comment.

461
07.04@01:09

Fig. 07.04.03: embed_password Parameter List and Updated Function Comment Lines 62 and 65-67

Inside the function suite, under the "compute random split index" comment, I’ll create the
identifier fill. I'll bind it to a string that contains all possible random symbols used to pad the
password. I’ve used the name fill, because this string will be used to fill in the symbols around
the password. I will create another identifier called embedding and bind it to an empty string.
This will hold the embedded password string as I create it.

462
07.04@01:38

Fig. 07.04.04: Identifiers fill and embedding Lines 69 and 70

Computing the split index is not as easy as just choosing a random integer between 0 and 20. If
I did this, it could result in creating an embedded string with a length greater than 20! At most, I
want the number of random symbols before the password to be the total length of an embedded
string minus the number of letters in the password. I’ll use the built-in function len to compute
the password size. Then, using the randint function from the random module, I’ll compute a split
index between 0 and size minus password_size inclusive, which means the endpoints are
included.

463
07.04@02:18

Fig. 07.04.05: split_index Formula Line 72

Now I can replace the first for icon comment with a for statement that adds the first sequence of
random symbols to the embedding. From my algorithm, I know that the loop header should be
for index in range 0 to split_index.

Inside the for loop, I will use another function from the random module. The function choice
takes a sequence argument and returns a random element from that sequence. I can use this to
select a symbol from the fill string and concatenate it to the embedding string.

I will remove the redundant “concatenate random char" comment.

464
07.04@03:00

Fig. 07.04.06: Embed Password for Statement Lines 73-74

I must add another import statement to be able to use these two functions from the random
module.

07.04@03:05

Fig. 07.04.07: random Import Statement Line 12

465
I will leave the rest of Hacking version 7 to you. Finish the "embed password" function, update
the "display_password_list" function, and write the code to display hints.

466
07.05 Identify Solution Issues in Hacking Version 7

You’ve finished Hacking! I won't trace the version 7 code and there are no new software quality
tests to introduce, but you should still make sure your code passes the previous software quality
tests. Since this is the last version of Hacking, there is only one thing left to do! Let's identify any
solution issues or solution improvements that could be made.In terms of solution issues, there
aren’t really any. You have successfully added all the features required for the complete Hacking
game. Your functional test plan completely tests the game behaviour and your code has met the
Software Quality standards.

But your game can still be improved! Consider this: Hacking isn’t all that replayable if you know
what the correct answer is after playing it once. Hacking gameplay could be improved by
randomly choosing a password from the password list as the correct password. In addition, you
could create a large list of passwords and randomly choose thirteen from that list to be used in
the game. Then, every game would have a random selection of words, and a random correct
password. It would make testing more difficult, but it would also make the game more fun to
play.

You could also increase the difficulty of the game. You could choose how many guesses players
gets based on their skill in previous games. A player who consistently guesses the password in
3 guesses would have the total attempts reduced from 4. A player who doesn't win often, or at
all, with 4 guesses could be given more attempts.

Another way to make the game more challenging is to erase some of the characters of the
passwords in the password list, so that the player needs to guess what the missing letters are.
You could try implementing any of these improvements, or think of other ones.

Congratulations, you’re done the Hacking game!

467
MODULE 8: POKE THE DOTS VERSION 1 & 2

08.01 Introduction to Poke the Dots

Welcome to the next game! You have finished Hacking and you're back to the first task in the
game creation process. It's been a while since you've seen the "create game" task!

08.01@00:16

Fig. 08.01.01: Create Game Task in Create Game Diagram

The next game you will be making is called Poke the Dots. As you create Poke the Dots, you
will learn to handle multiple player actions and animate graphical objects. To do this you will
learn new programming concepts such as classes and user-defined methods.

Before you start your first version of Poke the Dots, the complete feature set for the final game
must be decomposed into version feature sets. Recall that we use the problem-solving
technique called feature selection to create these feature subsets.

468
08.01@00:49

Fig. 08.01.02: Feature Selection Subtask in Problem Solving Diagram

Like in Hacking, I will provide the Poke the Dots version feature sets for you. We previously
talked about two considerations for creating the version feature sets for a game. The first is
balanced feature sets, since all versions should take roughly the same amount of effort to
implement. The second consideration is feature dependency.

Features that depend on other features must be implemented later on. For example, in Hacking,
displaying hint messages was one of the last features that you implemented. It required you to
have already implemented the features that prompt a user for a guess and compare that guess
to the correct answer. You needed to learn if statements, for statements, and multiple uagame
functions to write the code for the hint messages. Trying to learn and implement all those
features at once would have been far too much effort for one version! Being aware of
dependencies and the complexities of your features allows you to plan your versions in a way
that keeps the time and effort of making those versions manageable.

The first version of Poke the Dots will focus on a new technique called event handling. There
are many categories of events in a game. These include mouse clicks and key presses. Event
handling allows you to recognize these events and react to them if necessary. To handle events,
you will be using the graphics library pygame. Recall that uagame is based on the pygame
library. You will continue to use uagame, but you will also need to use some capabilities from
pygame directly.

469
In the next few lessons, you will observe and play the complete Poke the Dots game. You will
also observe, play, describe and create a test plan for Poke the Dots version 1.

You're back to the "understand game" task in the game creation process. These two sub-tasks,
"observe game" and "play game", are the first two steps in using experiential decomposition to
understand the full game.

08.01@02:49

Fig. 08.01.03: Understand Game Subtasks in Create Game Diagram

First, let's take a look at the complete Poke the Dots game. When the window opens, there are
two dots moving on the screen. When the dots hit the edge of the screen, they bounce.
In the top left corner of the screen is a scoreboard that increases for every second that the
game is running.

470
08.01@03:01

Fig. 08.01.04: Poke the Dots Completed Version

When the dots collide, the game ends. Unlike Hacking, I won't press enter to end the game.
Instead, I'll press the "close" icon to close the window.

08.01@03:09

Fig. 08.01.05: Poke the Dots End State

471
I'll start the game again. This time, the dots start in different places on the screen. I'll restart the
game a couple times to confirm that the dots start in different places each time.

08.01@03:23

Fig. 08.01.06: Poke the Dots Start

08.01@03:28

Fig. 08.01.07: Poke the Dots Start Different Location

472
The next thing I'll do is click the left mouse button. When I do this, the dots disappear from the
screen and reappear in a random location. They do this every time I click the mouse button.

08.01@03:39

08.01.08: Poke the Dots Pre-Click

08.01@03:39

Fig. 08.01.09: Poke the Dots Post-Click

473
Finally, notice the speed of the dots. The red dot and the blue dot move differently! The red dot
moves faster in the vertical direction, and the blue dot moves faster in the horizontal direction.

You should now play the complete Poke the Dots game yourself several times to become
familiar with all aspects of the game.

474
08.02 Observe Poke the Dots Version 1

Now I'll show you the first version of Poke the Dots.

When the window opens, there is no scoreboard. The dots still move and bounce. However,
when the dots touch each other, the game doesn't end. I can still press the close icon to close
the window.

08.02@00:15

Fig. 08.02.01: Poke the Dots Version 1 Demo

475
08.02@00:15

Fig. 08.02.02: Poke the Dots Version 1 Demo (Video Screenshot)

In the next activity, play the game yourself and familiarize yourself with Poke the Dots version 1.
Try to find the other differences between the full game and the first version. Are the placements
of the dots still random? Can the player still move the dots with the click of a mouse button?
Which direction do the dots move each time you restart the game?

After you've familiarized yourself with Poke the Dots version 1, make a description and
functional test plan for this version.

476
08.03 Create Algorithm for Poke the Dots Version 1

Welcome to the first algorithm demonstration for Poke the Dots.

08.03@00:24

Fig. 08.03.01: Create Algorithm Subtask in Create Game Diagram

This demonstration will be a little different from the ones you've seen previously, since I will be
going through the entire algorithm and there will be no activity afterwards.

Now that you've created your description and functional test plan, let's talk about the two main
differences between Poke the Dots and Hacking.

First of all, in Hacking there was no continuous motion of objects in the game window. As soon
as something was displayed in the window, it remained there until the screen was cleared for
the end results. In Poke, the dots move continuously within the window until they collide with
each other. Like movies, games use the rapid transition of multiple still images to simulate
movement. To do this a program needs to draw its changing objects fast enough that the human
eye doesn't detect that it is looking at still images.

The second main difference is that in Hacking, changes to the game screen happened either
right after the game starts, or after waiting for player input. In Poke the Dots, the game must
update based on both player actions, such as mouse clicks, and game conditions, such as dots
hitting an edge of the window and the time changing.

477
Waiting for prompted player input before updating the window is different than responding to
unprompted player actions that occur whenever the player feels like it. In the first case, the
prompt determines when the player is allowed to act and the program just waits for this specific
action. In the second case, the program must continuously check for player actions, while still
animating the game objects.

Both continuous animation and handling player actions will be addressed by a new concept
called a Frame Loop. A frame loop is a control structure in a program that updates the game
objects and redraws the game window contents repeatedly to simulate motion. A frame is one
still image that contains all the objects that should be displayed. Each time through the frame
loop, the program must also check whether the player has taken any actions and respond
appropriately. Since Poke the Dots will run at 90 frames per second, for every second of game
time, there will be 90 still images drawn to the window.

Let's start creating the algorithm! First I'll add a create window step, like we did in Hacking.
Second, I'll add a create game step which will create all the objects the game needs. Third, I'll
play the game, which will contain the frame loop. Finally, I will close the window, as we did in
Hacking.

08.03@03:11

Fig. 08.03.02: Poke the Dots Version 1 Algorithm Main Program Steps

478
08.03@03:11

Fig. 08.03.03: Poke the Dots Version 1 Algorithm Main Program Steps (Video Screenshot)

Every game you create can use these same four steps. Different games can be created by
varying the sub-steps of create game and play game. This is an example of the "Using
Templates" problem solving technique.

08.03@03:24

Fig. 08.03.04: Using Templates Subtask in Problem Solving Diagram

479
We can use these four solution steps as a foundation for any game we create. Notice that two of
these four steps are re-used from Hacking so we have already used Hacking as a partial
template for Poke the Dots.

08.03@03:33

Fig. 08.03.05: Reused Steps from Hacking Algorithms

I'll expand "create game" by creating a new panel. First, I'll add a step to create the clock. The
clock is responsible for the speed, or frame rate, of the game. In future versions, it will also be
used to update the scoreboard. Since this game has two dots, I will add a step to create a small
dot and a big dot. You will create the dot attributes, such as location, color, size, and velocity,
when you write the code for creating a dot.

480
08.03@04:13

Fig. 08.03.06: Create Game Panel Steps

08.03@04:13

Fig. 08.03.07: Create Game Panel Steps (Video Screenshot)

These are the only three objects needed for version 1, so I'll return to the main panel and
expand "play game". Play game is where I'll put the frame loop. I want the window to remain

481
open until the close icon is clicked. Since clicking the close icon is a condition, I will use a while.
From the conditions list I'll choose "not player has selected close". Each time the while step is
performed, the game should create and display one frame. Therefore, I'll pick the words "play"
and "frame" and expand this step into its own panel.

08.03@04:49

Fig. 08.03.08: Play Game Panel While Icon

08.03@04:49

Fig. 08.03.09: Play Game Panel While Icon (Video Screenshot)

482
In play frame, I will add three steps. The first is "handle events", which keep track of any player
actions. The second is "draw game", which draws the current frame. The last is "update game",
which updates all the game objects for the next frame. The objects are updated after they are
drawn so their initial position is shown.

08.03@05:14

Fig. 08.03.10: Play Frame Panel

483
08.03@05:14

Fig. 08.03.11: Play Frame Panel (Video Screenshot)

Because "draw game" is the simplest step, I am going to expand it first and come back to
"handle events" and "update game" later. In the "draw game" panel, I'm going to add three
steps. The first two steps are "draw (small) dot" and "draw (big) dot". The parentheses around
"small" and "big" indicate that they are qualifying adjectives for the same noun, dot. They denote
different calls to the same function. However, I still need a step in the algorithm to deal with
each dot.

The final step of draw game will be "update display".This step updates the window to display all
objects that were just drawn. In Hacking, we updated the display after each string was drawn. In
Poke, we can draw all the objects and then update the display once per frame. This is more
efficient.

All right, we're almost halfway done!

I'll return to the play frame panel and expand "handle events" into its own panel. Every graphics
library has a function that returns a sequence of events. This sequence includes all events that
occurred since the last time the function was called. We will call this function once per frame
and then use definite repetition to handle all the new events in the sequence. In "handle event",
I'll add a "for" icon. From the list of targets I'll choose "event" and from the list of sequences, I'll
chose "event list". In the for step I will add the words "handle" and "one event" as I will need to
handle each event in the list separately. I'll expand "handle one event" using its own panel.

484
08.03@07:07

Fig. 08.03.12: For Icon in Handle Events Panel

This version of Poke the Dots only handles one category of event, which is clicking the close
icon on the window. Let's call this category of event a close event. To check for one category of
event, I'll drag an if icon into the "handle one event" panel. From the condition list, I'll choose
"event category equals close". In the true box, I'll add the words "remember player has selected
close". I won't add anything to the false box or any more steps to "handle one event".

08.03@07:45

Fig. 08.03.13: If Icon in Handle One Event Panel

"player has selected close" is a boolean that controls the while loop. For each frame of Poke the
Dots, as long as "player has selected close" is false, the game plays the next frame.

I'll return to the "play frame" panel and expand "update game". First, I'll add in two new boxes
that move the small dot and the big dot. Notice again that "small" and "big" are in parentheses,
just like draw dot. In your program, you will only have one function that moves the dots but the
algorithm needs an explicit step for each dot. The final step in "update game" controls the frame
rate. If the frame rate is not controlled, the game will run as fast as a computer's processing
speed will allow, which will be different speeds for different machines.

485
08.03@08:35

Fig. 08.03.14: Update Game Panel (Video Screenshot)

I'll expand the "move small dot" step into its own panel. I will only expand "move small dot" and
not "move big dot", since there will be a single function that implements both algorithm steps
and I don't want to repeat myself. Either "move small dot" or "move big dot" could be expanded.
When this situation arises, each algorithm solution will pick one instance to expand. For Poke
the Dots, we will expand the algorithm steps for the small dot.

A circle is made of a large number of pixels to simulate a smooth curve on the screen. It would
be ridiculous to represent a circle as a list of its pixels. Instead, we can represent a circle using
two objects, its center point and its radius. The radius can be a single integer, but the center
point must be a sequence containing two integer coordinates. This representation requires less
space to store. Using this representation, when the circle moves, its radius stays the same and
only its center changes. Changing two integers is much faster than changing a large number of
pixels on its circumference.

486
08.03@09:39

Fig. 08.03.15: Representation of Circle Using a Center Point and radius

In this panel, I will add a for loop. For the target, I'll choose "index" and for sequence I'll choose
"sequence 0 to 1". Zero and one are the indexes of the x and y coordinates of the center,
respectively. Zero and one are not the actual x and y coordinates. Therefore, the algorithm step
in the for loop is "update center at index". I'll expand this step into its own panel.

08.03@10:19

Fig. 08.03.16: For Icon in Move (Small) Dot Panel

Each frame, a dot moves in both the x and y direction. It moves its velocity at index zero in the x
direction and its velocity at index one in the y direction. A positive velocity means that the dot is
moving to the right or down since each velocity component is added to the corresponding
coordinate of the dot's center. A negative velocity means that the dot is moving to the left or up
since adding a negative velocity makes it smaller.

487
08.03@10:33

Fig. 08.03.17: Dot moves in both x and y direction

I need to check if the dot moves outside the window so I'll add an if icon. Its condition is "dot
edge outside of window". To "bounce" the dot from the window edge I will negate its velocity at
the index whose location I am updating.

08.03@11:14

Fig. 08.03.18: Update Center at Index Panel

Here are three examples.

If a dot is moving only to the right at positive 2 pixels per frame and hits the right edge, its
velocity at index 0, which is its velocity in the x direction, is negated so it becomes negative 2
pixels per frame.

488
08.03@11:29

Fig. 08.03.19: A Dot Moving to the Right at Positive 2 pixels Per Frame Hits the Edge and is then
Negated to Become Negative 2 Pixels Per Frame as it Moves Left

If a dot is moving only to the left at negative 1 pixels per frame and hits the left edge, its velocity
at index 0, which is its velocity in the x direction, is negated so it becomes positive 1 pixels per
frame.

489
08.03@11:46

Fig. 08.03.20: A Dot Moving to the Left at Negative 1 Pixel Per Frame Hits the Edge and is then Negated
to Become Positive 1 Pixel Per Frame as it Moves Right

If a dot is moving only down at positive 3 pixels per frame and hits the bottom edge, its velocity
at index 1, which is its velocity in the y direction, is negated so it becomes negative 3 pixels per
frame.

490
08.03@11:57

Fig. 08.03.21: A Dot Moving Down at Positive 3 Pixels Per Frame Hits the Edge and is then Negated to
Become Negative 3 Pixels Per Frame as it Moves Up

And that's it! The algorithm is done.

Make sure to watch the next few programming language videos before you complete version 1
of Poke the Dots!

491
08.04 Python - Import Statement Variations

This video introduces new variations of the import statement that can be used to resolve name
conflicts and import objects from submodules, which are modules inside of other modules.

A name conflict occurs whenever you try to use the same identifier to refer to different objects at
the same time. For example, here is a program that uses the identifier sleep to refer to different
sleep modes, such as "REM" and "non-REM", where REM is short for "Rapid Eye Movement.

08.04@0:39

Fig. 08.04.01: sleepy.py Uses Identifier sleep to Refer to Different Sleep Modes

I want to have a pause between printing the sleep mode and the "dreams" output. I will import
the sleep function from the time module and put a call to this sleep function after the first print
function call.

492
08.04@0:52

Fig. 08.04.02: Imported sleep Function from Time Module and Added sleep Function Call on Line 4

The Python interpreter reports a type error, indicating that sleep is not a callable object. What
happened? When the program starts, the main module namespace contains the identifiers input
and print, each bound to a function object. When the import statement is evaluated the identifier
sleep is added to the namespace and bound to the sleep function object from the time module.
Next, the input function returns a "REM" string object and the assignment statement binds the
sleep identifier to this object. The print function call displays this string object. The next
statement is a function call whose function name is sleep. The interpreter dereferences the
sleep identifier to obtain a string object, which is not a function so the function call fails.

493
08.04@01:41

Fig. 08.04.03: Namespace and Memory of sleep.py - Interpreter Dereferences sleep Identifier to Obtain a
String Object on Line 4, which is Not a String Object, so Function Call Fails

When the assignment statement rebound the identifier sleep, the reference to the sleep function
was lost. This is a name conflict. Python resolves name conflicts using the "most recent
binding" rule. Whichever binding occurred most recently is the binding that is used to
dereference an identifier.

For example, I will move the import statement so that it occurs later in the program. This time,
the assignment statement is evaluated first. The identifier sleep is added to the namespace and
bound to the result object of the input function call, which is "REM". Then the "print sleep"
function call, displays "REM". Then the import statement rebinds sleep to the sleep function
from the time module. The function call to sleep works as intended. When the condition of the if
statement is evaluated, the identifier sleep is dereferenced to obtain the sleep function object,
which is not equal to the string "REM", even though the user input the "REM" string. There is
still a name conflict and the program output is incorrect.

494
08.04@2:48

Fig. 08.04.04: Namespace and Memory of sleep.py After Moving Import Statement to Line 3 - Name
Conflict Still Occurs

You can resolve a name conflict by selecting a different identifier to bind to one of the conflicting
objects. I could bind a different identifier to the input string, such as sleep_mode, and keep the
identifier sleep for the imported sleep function. I need to change sleep to sleep_mode
everywhere I want to refer to the string that was entered by the user.

495
08.04@3:13

Fig. 08.04.05: Changed sleep to sleep_mode when Referring to Input String

In this case, "sleep mode" may be a more descriptive identifier for the input string. However, in a
program called sleep that is about the topic of sleep, using the name sleep as the name of a
pause function is not very descriptive.

Python provides the versatility to resolve conflicts due to imported names, using different
variations of the import statement. These variations allow you to use the most appropriate local
name in your program, to represent each imported object. You are not constrained by the
names used in any other module

Here is a generalization of our previous import statement that adds an optional as clause to
provide an alternate local name for each imported name. This local name is often called an
alias.

496
08.04@3:56

Fig. 08.04.06: Import Statement Diagram That Adds Optional As Clause to Provide an Alternate Local
Name (Alias) for Imported Name

I will go back to using sleep instead of "sleep mode" to refer to the input string. I will use this
new variation of the import statement to change the imported local name from "sleep" to
"pause", which is more descriptive in this program.

497
08.04@4:13

Fig. 08.04.07: Output is Correct When Imported Local Name is Changed From “sleep” to “pause”

The program output is correct.

Here are the generalized semantics of the import statement.

When the program starts the main module namespace contains the identifiers input and print,
each bound to a function object. When the import statement is evaluated the identifier pause is
added to the namespace and bound to the sleep function object from the time module. Next, the
input function returns a "REM" string object and the assignment statement binds the sleep
identifier to this object. The print function call displays this string object. The next statement is a
function call whose function name is pause. The interpreter dereferences the pause identifier to
obtain a function object, so the program continues as intended. The name conflict has been
eliminated.

498
08.04@4:38

Fig. 08.04.08: Semantics of Import Statement

08.04@4:55

Fig. 08.04.09: Interpreter Dereferences the pause Identifier to Obtain a Function Object sleep

499
Another variation of the import statement does not use the keyword from. Here is a new syntax
diagram for the import statement that supports both the from variation that you have already
seen and the new "non-from" variation. Here is the from variation you have already seen.

08.04@5:17

Fig. 08.04.10: Syntax Diagrams for Import Statement and Import Statement “From” Variation

Here is a syntax diagram for the new "non-from" variation.

08.04@5:25

Fig. 08.04.11: Syntax Diagram for Import Statement “Non-From” Variation

If this new variation is used, then when an identifier from an imported module is used, it must be
used as an attribute of that module. For example, here is a version of the sleep program that
uses this variation.

500
08.04@5:42

Fig. 08.04.12: sleep Program Modified to Use Import Statement “Non-From” Variation

The program output is correct.

Here are the semantics of the non-from variation of the import statement:

501
08.04@5:47

Fig. 08.04.13: Semantics of Non-From Import Statement

When the program starts the main module namespace contains the identifiers input and print,
each bound to a function object. When the import statement is evaluated the identifier time is
added to the namespace and bound to the time module object. None of the identifiers in the
Time module are bound in the main module's namespace, just the name of the module itself.
Next, the input function returns a "REM" string object and the assignment statement binds the
sleep identifier to this object. The print function call displays this string object. The next
statement is a function call whose function name is time.sleep. This is an attribute reference. An
attribute reference is necessary because only the module name is in the main module's
namespace. The name of the sleep function is not!

502
08.04@6:39

Fig. 08.04.14: Attribute Reference on Line 4 Necessary To Use sleep Function

Recall the syntax and semantics of attribute reference from the Method Calls lesson. First the
interpreter evaluates the expression, time to obtain an object. In this case, it is the time module
object. Second, the interpreter checks if the attribute, sleep, is in the namespace of the time
module object. Since sleep is a function defined in the time module, sleep is in time's
namespace, so the sleep function object is obtained.

503
08.04@6:44

Fig. 08.04.15: Semantics of Attribute Reference

The interpreter applies this function to the argument list, which is a single integer object, one,
and the sleep function pauses for one second. The program continues as intended. The name
conflict has been eliminated.

In Python, modules can be defined inside other modules. A module inside another module is
called a submodule. To import objects from a submodule, I will generalize the syntax diagram
for module itself. Previously a module name was assumed to be a single identifier, so we used
this syntax diagram for module:

08.04@7:50

Fig. 08.04.16: Syntax Diagram for Module

To access objects from a submodule, I can use this generalized syntax for module as a
sequence of identifiers separated by dots.

504
08.04@7:59

Fig. 08.04.17: Syntax Diagram for Module as a Sequence of Identifiers Separated by Dots

For example, the standard library contains a module called os, which provides access to
operating system functions and other objects. Inside the os module is a submodule called path
that contains a function called "isfile". The isfile function returns true if its string argument is the
name of a file that exists in the folder that Python is run from, and false otherwise.

08.04@8:24

Fig. 08.04.18: os Module which Contains path Submodule which Contains isfile Function

I can use this generalized syntax for module to access this function.

I will call the isfile function on the string "sleep.py" and the function returns true since the file that
my program is in certainly exists.

505
08.04@8:40

Fig. 08.04.19: isfile Function Returns True Since sleep.py exists in My Python Program Folder

I will call the isfile function on the string "snuvs.py" and the function returns false since there is
no file called "snuvs.py" in my python program folder.

08.04@8:50

Fig. 08.04.20: isfile Function Returns False Since snuvs.py Does Not Exist in My Python Program Folder

506
In this video, I generalized the import statement so it can be used to resolve name conflicts and
to import objects from submodules, which are modules inside of other modules.

507
08.05 Python - Pass Statement

This video introduces a new statement called a pass statement, which serves as a placeholder
in a Python program.

When I am writing code, I often write partial statements that I want to complete later. Consider
this program, where I have not decided yet what string to output if "blue" is input.

08.05@0:30

Fig. 08.05.01: Code with Partial Statements for Completion Later

This program is syntactically invalid since every elif clause must contain a suite and every suite
must contain at least one statement. A comment is not a statement. I could add an expression
statement with a print call as a placeholder. However, Python provides a pass statement to
prevent such missing statement syntax errors. I can either use a comment with the pass
statement to remind me to return to this statement later, or I can leave it out.

508
08.05@0:58

Fig. 08.05.02: Pass Statement Used to Prevent Missing Statement Syntax Error

Here is the syntax diagram for the pass statement.

08.05@1:06

Fig. 08.05.03: Syntax Diagram for Pass Statement

A pass statement consists of the keyword pass.

And here are the semantics: do nothing

08.05@1:06

Fig. 08.05.04: Semantics of Pass Statement

509
When the pass statement was evaluated … nothing happened … which is correct! Although a
pass statement can be used anywhere in a program that any other kind of statement can be
used, it is most often used as the suite of a compound statement to prevent a syntax error.

If you are writing a program with functions and you want to test the program before you have
finished writing all the functions, you can use function stubs that consist of function definitions
whose suites are single pass statements.

For example, here is the banner program you saw in a previous lesson, with a stub for the
banner function. This allows me to test the main function before writing the code for the banner
function.

08.05@1:45

Fig. 08.05.05: banner Function as a Function Stub

This video introduced a new statement called a pass statement that can be used as a
placeholder in Python programs, especially in a suite of a compound statement.

510
08.06 Program Poke the Dots Version 1

To start programming Poke the Dots version 1, you must create a program comment and add
comments for all the algorithm steps. This is the same process you used when programming
Hacking, but you're starting with an empty document for the first time in seven versions! Add
your comments and don't forget to save the document.

08.06@0:12

Fig. 08.06.01: Create Program Subtasks in Create Game Diagram

The first thing I will do is write a header for each function def comment. In each function suite, I
will use a pass statement until I am ready to add more code. I'll add a call to main at the bottom
of the program and run it.

The program runs with no errors and nothing is displayed, just as expected.

511
08.06@0:41

Fig. 08.06.02: Poke1.py with Function Headers and Pass Statements

Some of the algorithm step comments are familiar from Hacking. For example, you've previously
created a function to open a window. I'll start writing the create_window function for Poke the
Dots. This time, the title is 'Poke the Dots' and the dimensions are 500 by 400 pixels. Since
there is no text in version 1, I don't need to set attributes for the font yet, but I will set the
background colour, even though the default is black.

08.06@1:07

Fig. 08.06.02: create_window Function for Poke the Dots

Now I'll add code to the main function suite. I'll delete the pass statement and use
create_window to make a window object. I'll test these functions by running the program. The
window appears. So far, so good.

512
08.06@1:16

Fig. 08.06.03: The Window Appears When we Run the Program

The next step in the main function is to create the game by creating the game objects.

In our algorithm, we used a clock to control the frame rate. In pygame, the Clock class manages
the frame rate. This class is defined in the time submodule of the pygame module.

08.06@1:35

Fig. 08.06.04: Clock Class from Pygame’s Time Submodule

513
To create a clock, first I must import Clock from pygame.time.

Now, I'll create the object in main by using the name of the class, Clock, as a function call.

08.06@1:47

Fig. 08.06.05: Function Call to Clock Class

I'll create the big dot by creating objects for the big dot's colour, radius, center, and velocity. The
colour and velocity are from the version description: the big dot is blue and it moves twices as
fast in the horizontal direction as the vertical direction. The other two attributes, radius and
center, are estimated from observing the game. You can estimate the values for the small dot
yourself.

08.06@2:11

Fig. 08.06.06: Created Big Dot

The last two comments in main are "play game" and "close window". I'll add function calls for
each of these, a normal function call for "play game" and a method call for "close window".

514
08.06@2:29

Fig. 08.06.07: Function Calls to play_game and close_window Functions

Of course, when I run the program, nothing new happens except that the window closes.The
only statement in the "play_game" function is a pass statement, so nothing happens when this
function is called.

Let's change that. I'll write the code for play_game next. In Poke the Dots, the window stays
open until the player has clicked the close window icon. Pygame represents each player action
as an event and clicking the close icon is represented by a close event. I will use a boolean
called close_selected to keep track of whether a close event has occurred. I'll bind the identifier
to False, since pygame does not create a close event until the player takes the appropriate
action.

08.06@3:02

Fig. 08.06.07: Bound Identifier close_selected to False in play_game Function

The handle_events function handles all of the events that pygame creates. I will make it return
true when a close event has occurred, so I know when to close the window. Therefore, I will
rebind "close_selected" to the return object of handle_events.

515
08.06@3:17

Fig. 08.06.07: Rebound close_selected to Return Object of handle_events Function

The frame loop for the game should run as long as close_selected is False, so the while header
is: "while not close_selected".

I'll replace the comments for "draw game", and "update game" in the while suite with their
respective function calls.

08.06@3:30

Fig. 08.06.08: draw_game and update_game Function Calls are in the While Header

I'll write handle_events now. Since handle_events must return whether the player has clicked
the close icon or not, I must keep track of whether this close event has occurred. I will use a
local identifier called "closed" and bind it to "False".

I will rebind it to True only after a close event has occurred and return closed as the result
object.

516
08.06@3:51

Fig. 08.06.09: Bound Local Identifier closed to False; closed Rebinds to True After Close Event Has
Occured

I need access to the events I want this function to handle. The pygame function get in the
pygame event module returns a list of events that have occurred since the last time the function
was called. I will import it so I can use it in my handle_events function. However, the name "get"
is not very descriptive. I can't tell what this function gets, just by looking at the name. I will import
this function using the alias "get_events".

08.06@4:17

Fig. 08.06.10: Import pygame.event get Function Using Alias “get_events”

Now I call get_events in my handle_events function and bind the identifier event_list to the
result. Because get_events returns a sequence of events, I need an event loop to iterate over
the sequence. I will use a for statement.

08.06@4:33

Fig. 08.06.11: Function Call to get_events in handle_events Function; For Statement Used to Iterate Over
Sequence

In this version, I'll only handle close events. Every event has an attribute called type, which does
not refer to its actual object type. Instead, it is bound to an object that represents the category*of
the event, such as mouse click, key press, or close. In pygame, a close event is represented by
the identifier QUIT.

I'll import QUIT from pygame.

517
Now I'll use an if statement to check if the category of the current event being handled by the
event loop is QUIT.

08.06@5:05

Fig. 08.06.12: If Statement to Check if Category of Current Even Being Handled by Event Loops is QUIT

I have already bound "closed" to true inside the If statement and returned close as the result
object.

When I run the program and click on the close icon, the window closes.

Let's write draw_game next. As discussed in the algorithm video, both "draw small dot" and
"draw big dot" will use a single function to draw each dot. Since I only created the big dot, I will
only call draw dot for the big dot.

08.06@5:30

Fig. 08.06.13: Function Call to draw_dot Function in draw_game Function

The last step in draw_game is a call to update the window. I'll add that function call.

08.06@5:38

Fig. 08.06.14: Function Call to window.update Function in draw_game Function

Now I'll write draw_dot. The header will be "def draw dot", since it will be used to draw any dot!

08.06@5:45

Fig. 08.06.15: draw_dot Function Header

518
Pygame contains a useful function to draw a circle. It is called "circle" and it is in the draw
module. Circle isn't a very descriptive name, though, so I'll use an alias and import it as
“draw_circle”.

08.06@5:56

Fig. 08.06.16: Imported circle Function from Pygame Using Alias “draw_circle”

Let's look at the documentation for this function.

08.06@6:02

Fig. 08.06.17: pygame.draw.circle Documentation

It has four required arguments: a surface, a color, a position, and a radius. The surface is where
the circle is drawn. Every window has a surface that can be obtained by calling the uagame
window method called get_surface. I'll bind the identifier surface to this surface object.
Therefore, I must add "window" to the parameter list in the function header.

08.06@6:21

Fig. 08.06.18: Bound Ientifier surface to Surface Object Obtained From window.get_surface Method

The next parameter for draw_circle is color. Pygame uses a specific Color type to represent
colors. I already have a string for the name of the color I want to draw, but I can't pass the name
string into the draw_circle function. I will import the Color type from pygame, the same way I
imported Window from uagame.

519
08.06@6:37

Fig. 08.06.19: Imported COLOR type from Pygame

Now I can use the Color function to create a Color object by using the name string as the
argument.

08.06@6:48

Fig. 08.06.20: Color Object Created Using the Name String as the Argument

Remember that the "draw dot" function must be generic enough to draw any dot, so I need to
use arguments for its color string, center and radius. I will add corresponding parameters to the
draw_dot parameter list, "color_string", "center", and "radius". I can now call draw_circle using
the surface and color objects I just created, as well as center and radius.

08.06@7:09

Fig. 08.06.21: Function Call to draw_circle Using the Surface Object, Color Object, Center, and Radius

Now I will return to draw_game and add arguments to draw_dot: window, the big dot's color
string, center, and radius. This means I must add window, big_color, big_center, and big_radius
as parameters of draw_game. Since draw_game is called in play_game, I will add these
parameters there as well.

Finally, I will pass all these objects as arguments in the calls to play_game and draw_game that
I have already made.

I'll run the program.

520
08.06@7:40

Fig. 08.06.22: Blue Dot is Displayed

A blue dot is displayed! But it doesn't move. The game needs to be updated for the dot to
move.I will replace the pass statement in update_game by statements that move the dots and
control the frame rate. I will use a single function, called move_dot, to move each dot. Since I
have only created the big dot, I will make a single call for now.

08.06@7:55

Fig. 08.06.23: move_dot Function Call in update_game Function

Now I'll write the move_dot function. The first comment is the "for statement" placeholder. To
create a sequence from 0 to 1, I'll use the range function. Recall that the range function creates
a sequence from the first argument to one less than the second argument, so I must use the
arguments 0 and 2.

To update center at coordinate, I'll use subscription to access both center and velocity at the
current index, which is the for statement target. I will add the center and velocity as parameters
so they can be used inside this function.

08.06@8:31

Fig. 08.06.24: Subscription to Access Center and Velocity at Current Index

521
The dot must bounce when it hits a window edge. If I want the dot to bounce when its center
touches the right window edge, I could check if the dot's x coordinate equals window width.
However, the dot actually bounces when its edge touches the right window edge. This is true
when the dot's x coordinate plus its radius equals the window width. Since a dot can move
multiple pixels in a frame, it is possible for the dot to move past a window edge instead of just
touching it.

08.06@8:48

Fig. 08.06.25: Dot’s X Coordinate Plus Radius == Width to Check if Dot Touches the Right Window Edge

Therefore I will check if the dot's x coordinate plus radius is *greater than* or equal to the
window width to perform a bounce from the right edge.

I will call the get_width method on the window to obtain its width, but to do this, I need to add
window as a parameter. I will also add radius as a parameter. I will use the same parameter
order as the other functions so it is easier for me to remember.

08.06@9:20

Fig. 08.06.26: Function Call to get_width to Obtain Window’s Width and Check if Dot Touched the Right
Edge (line 67)

Recall that a positive velocity means that the dot is moving to the right or down and a negative
velocity means that the dot is moving to the left or up. I will bounce the dot by negating its
velocity.

522
08.06@9:38

Fig. 08.06.27: If Dot Touches Right Edge, Negate Dot’s Velocity

Since move_dot has four parameters, I will go back to update_game and add the four
corresponding arguments to move_dot: window, big_center, big_radius and big_velocity.

Now, I will add these arguments as parameters to update_game.

08.06@9:50

Fig. 08.06.28: Parameters of move_dot Added to Arguments of update_game Function

Now, I will add the arguments of update_game in play_game.

08.06@9:56

Fig. 08.06.29: Parameters of update_game in play_game Function

Although play_game has some of these arguments as parameters, it is missing big_velocity. I


will add this parameter to play_game and add the corresponding argument to the play_game
function call in main.

523
08.06@10:05

Fig. 08.06.30: Added big_velocity to play_game Function

I will run the program to see if it works.

08.06@10:09

Fig. 08.06.31: Blue Dot Has A Tail

The blue dot moves and it bounces. However, it moves too fast, has a "tail" and it moves too far
downwards before bouncing. The bounce condition is checked for both the x and y directions
since it is inside the for statement which is evaluated for both indexes 0 which is x and 1, which
is y. However, the condition in the y direction should use the window height instead of the
window width. The dot moves too far downwards because the condition for bouncing in the y
direction is that its y coordinate plus radius is greater than or equal to the window width instead
of the window height. To check the width for index 0 and height for index 1, I will put width and
height in a size list.

08.06@10:53

Fig. 08.06.23: Width and Height in Size List

524
I will run the program again.

08.06@10:55

Fig. 08.06.33: Dot Bounces of Right and Bottom Edge

The dot now bounces correctly off the bottom edge. I will leave the left and top edges to you.
You will only need to modify the if condition by adding an or and one comparison operation. This
comparison will check the left edge when index is zero and the top edge when index is one.

To change the speed of the dot, I need to control the frame rate. I will return to the update game
function and replace the frame rate comment by a statement. In pygame, the frame rate is
controlled by the clock I created in the main function. This means I must add a clock parameter
to update_game. Now I can use the pygame clock method tick to set the frame rate. This
method takes a frame rate as an argument and prevents the game from running any faster than
the specified number of frames per second. Without this method call, your game will run as fast
as your computer allows, which we've seen is too fast.

I'll add an assignment statement to bind frame_rate to 90 and then I'll call "clock dot tick" with
the argument frame_rate.

08.06@11:56

Fig. 08.06.34: clock.tick Function Call with Argument frame_rate

Now I'll run the program!

525
08.06@12:02

Fig. 08.06.25: Game Speed Slower with Frame Rate

The game speed is much better. You will experiment with different frame rates during the
reflection.

Why does the dot have a tail? Every time I call draw_game, it redraws the big dot in its updated
position. Since I never clear the screen, the dots are drawn one on top of another. Since the dot
moves such a tiny amount in each frame, it gives the appearance of drawing a tail in the
window. To fix this, I need to erase the previous dot before I draw the new one. I'll add a call to
clear window at the beginning of the draw_game function and run the program again.

08.06@12:36

Fig. 08.06.26: clear_window Function Call

Everything works! Except bouncing from the top and left edge, but you'll fix this yourself.

The tail problem is present at the algorithm level as well. I'm going to change the algorithm so it
is consistent with the program. The "draw game" panel has three steps: draw (small) dot, draw
(big) dot, and update display. Before I draw anything, I need to clear the window, so I'll add a
"clear window" step before the first draw step.

526
08.06@13:06

Fig. 08.06.27: Poke the Dots Version 1 Algorithm Start (Video Screenshot)

Not every error you make while programming can be fixed by simply changing a line of code.
Some issues are deeper and may have roots in earlier iterations of the game creation process,
like the algorithm. The unexpected tail on the dot is a great example of this kind of issue. I had
to run the program to recognize the issue before I could fix the code.

I didn't foresee the problem while I was creating my algorithm. I needed to use the graphics
library to realize the problem even existed! That's why we use an iterative process to create our
games! The iteration of coding and then fixing the algorithm inside a single version is not
explicitly shown in the game creation process diagram, but it happens.

Now it's your turn to program. Add the small dot and change the code so that both dots also
bounce off the top and left window edges.

527
08.07 Reflect on Poke the Dots Version 1

Welcome to the first reflection for Poke the Dots!

I'll start tracing the version 1 code by pressing the "step into" button and then stepping over the
import statements. Recall that import statements add identifiers to the local namespace of the
current block. Only some imported identifiers are shown in the locals section of the stack data
panel. However, you can see how many identifiers are in locals by looking at its length.

08.07@0:39

Fig. 08.07.01: Poke1.py Code and Stack Data Panel

After I step over the statement that imports QUIT and Color from pygame, the identifier QUIT is
visible in the stack data, bound to the integer 12, and the identifier Color is not visible.
When importing identifiers that are bound to objects that are simple to represent, such as strings
and integers, the bindings will be added to the appropriate namespace in stack data. Identifiers
that are bound to more complicated objects, such as functions and modules, will not explicitly
appear, which is why you have not seen any of these bindings in stack data before now.

528
I'll step over all the function definitions until I reach the call to the main function. When I step into
the main function call, the local namespace now belongs to the main function. The "length" of
locals is zero, since nothing has been bound in the main function's namespace.

08.07@1:33

Fig. 08.07.02: Length of Locals is Zero Since Nothing Has Been Bound in Main Function’s Namespace

I'll step over all the statements in the main function until I reach the call to the play_game
function. Ten new bindings have been added to the main function's namespace. Don't forget you
can expand objects that contain many elements, such as lists, to view those elements. For
example, I can expand the small_center list to see its two elements. Right now, the x-coordinate
of the small dot's center, which is the first element of the small_center list, is bound to 50 and
the y-coordinate, which is the second element in the list, is bound to 75.

08.07@2:12

Fig. 08.07.03: Elements of small_center List

I'll press the step into button to step into the play_game function call. The local namespace is
now the namespace of the play_game function. The namespace contains a binding for each of
the ten parameters of the function so there are 10 bindings here as well.

I will step over the statements in play_game until I reach the call to handle_events and step into
it. This time, the local namespace is empty, because handle_events has no parameters.

I'll step over the statements in handle_events until event_list is added to the local namespace.
The call to get_events retrieves all events that have occurred since the previous call to
get_events. When I expand event_list in the stack data panel, you can see the events currently

529
in the event list. In this version of Poke the Dots, all of these events except QUIT events are
ignored. Examining the event_list doesn't give much useful information about the events, but it
does let me see that events are occurring.

08.07@3:16

Fig. 08.07.04: Events Currently in event_list

I'll press the step out button to return to play_game. When I step over the call to draw_game,
the dots appear in the game window.

08.07@3:24

Fig. 08.06.05: Dots Appear in Window When Step Over Call to draw_game

I'll step into update_game. The first statement in update game is the frame rate assignment
statement. With the frame rate set to 90, at most 90 frames are drawn each second. The dots
will move 90 pixels times the velocity in each direction, each second. For example, if the x
component of velocity is two, the dot will move 180 pixels in the x direction each second. A
higher frame rate means that more frames are drawn per second and hence the dots move

530
more pixels per second, which results in a faster game. Conversely, a lower frame rate results in
a slower game.

I'll stop the trace to experiment with the frame rate. If I change the rate to 1000 and run the
program the speed of the game is much faster. If I change the rate to 5 and run the program
again the game is incredibly slow. Choosing a reasonable frame rate is part of creating a
playable game. I'll return the frame rate to 90.

To remember that a higher number is a faster game and that a lower number is a slower game, I
will add a new type of comment called an inline comment. Inline comments are used to provide
clarifying information that can't be easily inferred from context. For example, it isn't helpful to
add a comment for incrementing a value, since looking at the addition operation in the code
makes it clear that addition is happening. However, the meaning of the frame rate value is not
obvious. While the identifier "frame_rate" lets you know that the integer 90 is the frame rate for
the game, it doesn't tell you what 90 frames per second means.

The comment says "larger is a faster game". The inline comment summarizes the way the
frame rate works, since it isn't easily apparent from the identifier name or the actual frame rate
object itself.

08.07@5:32

Fig. 08.07.06: Inline Comment For How Frame Rate Works

Let's take a closer look at the code that moves the dots. I'll place a breakpoint next to the first
statement in the move_dot function suite and press the bug button. The first call to move_dot is
for the small dot. When I expand center in the stack data panel, I can see the original center of
the small dot: the x-coordinate, 50 and the y-coordinate, 75.

08.07@6:01

Fig. 08.07.07: Original Center of Small Dot

I'll press the step over button to progress through the for statement. The first iteration of the for
loop increments the x-coordinate by one. The second iteration increments the y-coordinate by
two. This function is called every frame for each dot.

531
08.07@6:17

Fig. 08.07.08: Iterations of For Loop Increment x-coordinate by 1, then y-coordinate by 2

I'll remove this breakpoint and place a new breakpoint inside the if statement suite of the
handle_events function. I'll press the bug button. The game starts and plays normally. I'll press
the close icon to generate a QUIT event.The game stops, but the window isn't closed yet. In
Stack Data, the event list only has one element. I can see that the address of event is the same
as the address of the single element in the event list.

I'll stop the trace.

08.07@6:49

Fig. 08.07.09: Address of Event is the Same as the Address of the Single Element in the Event List
(Video Screenshot)

The final piece of code I want to show you is the draw_circle function from Pygame.

532
08.07@7:04

Fig. 08.07.10: pygame.draw.circle Function

In the programming demonstration, you saw in the documentation that this function requires four
arguments. There is a fifth parameter for the draw_circle function that is called a "default
parameter". A default parameter has a default value that is used by the function if no argument
is used when the function is called. Furthermore, excluding an argument for that parameter
does not result in an error. The default parameter for the draw_circle function specifies the width
of the circle. If a width is used, a ring with that width is drawn. The default value for the width is
0, which results in a filled circle instead of a ring.

I'll add a 5 to the end of the argument list in the call to draw_circle and then run the program.
There you go! We now have Poke the Rings instead of Poke the Dots.

08.07@7:50

Fig. 08.07.11: Dots Drawn as Circles With Width is 5

533
I'll change my code back to Poke the Dots and check the software quality tests for this version.
All the old tests still apply. Make sure your code is properly commented and uses all the
guidelines we have already developed for identifiers, literals, repetition, and the main function.

Now let's take a look at the user defined function tests. While each function performs a single
logical task that's easy to describe in a single sentence, and none of them have more than 12
statements, several of our functions have more than 5 arguments.

08.07@8:26

Fig. 08.07.12: User Defined Function Tests

This makes the code harder to read and write. A long list of parameters increases the likelihood
of argument order errors. Such errors occur when arguments are passed in an incorrect order,
resulting in reported errors or incorrect program behavior. However, in this version of Poke the
Dots, using more than five arguments is unavoidable unless you use global identifiers! But using
global identifiers fails a Main function software quality test, so this is not an option.

There's nothing you can do to fix this software quality failure with the programming features
covered so far. In the next version you will be introduced to a new language feature called a
class, which can be used to solve this problem.

534
08.08 Solution Issues in Poke the Dots Version 1

In this video I'll identify the solution issues and improvements for Poke the Dots version 2. Just
as you did in two of the seven versions of Hacking, this version of Poke the Dots will focus on
code quality rather than new features.

Let's take a look at the code for Poke the Dots version 1. Several functions require seven to ten
parameters. This is inconvenient, and the code fails one of the software quality tests.
User-defined functions should have no more than five parameters!

So what can you do? It's time to learn about user-defined classes. You have used classes
throughout this course. Recall that class and type are synonymous and you have used many
types, such as int, list, Window and Color. When you call type on a specific Python object, the
function returns a message that tells you the class of the object.

You have also learned about methods, which are associated with classes. For example, you
have seen that there are methods to convert a string to upper or lower case, to check if the
characters in the string are digits or letters, and many more.

In Poke the Dots version 2, you will begin the process of learning to create your own classes.

User-defined classes are another example of encapsulation. You have previously used
encapsulation to limit the scope of identifiers. This kind of encapsulation is called name
encapsulation since it manages the names in a program. Another type of encapsulation is
called data encapsulation. Data encapsulation is a method of grouping objects together to
create a new single object.

For example, a list is a group of objects that can be operated on as a group. A list can be
printed, iterated over and passed as a single argument. The len and print functions and the
append method each operate on a list object as a single group.

Creating a user-defined class is another way of of solution generalization. Since a new object
type can be reused as a solution for many different problems.

535
08.08@2:04

Fig. 08.08.01: Solution Generalization Subtask in Problem Solving Diagram

Since this version is about improving the quality of your code, there are no observation, play,
description or test plan lessons.

In the next lesson you'll modify your algorithm to use classes.

536
08.09 Create Algorithm for Poke the Dots Version 2

In this lesson you'll modify your algorithm to use classes.

I can use a class as a composite object to access multiple objects. For example, a Person has a
name, a date of birth and a height, among other attributes. I will focus on these three attributes
in this example. We can create a Person class that contains these three attributes: name, which
is bound to a string, birthdate which is bound to a date and height which is bound to an integer.

Using named attributes is easier than using a list with three elements indexed, 0, 1 and 2.

Introducing classes to your algorithm involves a new feature of the algorithm builder. To start, I'll
drag a class icon into the main panel. I'll select the name "Person" to make a class for the
example. This creates an icon for the Person class at the top of the main program panel. To edit
the Person class, I'll select the class icon, which opens a new panel.

08.09@1:04

Fig. 08.09.01: Person Class Icon in Main Panel (Video Screenshot)

537
08.09@1:06

Fig. 08.09.02: Person Class Panel (Video Screenshot)

The shape palette now shows two new options that can be added to the panel. You will only use
"attribute" for now and ignore "method" until later lessons.

I must add the attributes to the Person class. For example, name will be one of the attributes. I'll
add the name attribute now, by dragging an attribute into the Person class panel and selecting
name. Now I'll add the birthdate and height attributes. I won't add person as an attribute of the
Person class, since Person is the name of the composite object rather than an attribute of a
Person.

538
08.09@1:37

Fig. 08.09.03: Attributes Name, Date of Birth and Height Added to Person Class (Video Screenshot)

Now, it's your turn. You must create a Game class for Poke the Dots and add all the attributes to
the Game class that you will use in the second version. For example, in the first version, you
created a window which you then used throughout the game. In this version, the window will be
an attribute of the game class. Think about which other objects you created in version 1 for use
in the game, so that you can add them as Game attributes now. You must also create a Dot
class with appropriate attributes.

The rest of your algorithm steps will not change. However, the context of some steps are now
different. After you add your classes, review your algorithm to understand the new meaning of
the steps. For example, "create small dot" now refers to creating an instance of your dot class
and assigning all the necessary attributes!

539
08.10 Python - Class Definition

This video introduces a new statement called a class definition that creates a new Python class,
also called a type. It also generalizes assignment statement syntax and semantics to support
attribute references as targets.

A user-defined class or type allows us to group several objects into a single object. A Python
class definition statement creates a new user-defined class.

Consider this program that checks to see if two rectangles intersect or overlap. The formula
used to check for intersection is not the main point of this lesson, but you might want to figure
out how it works yourself.

08.10@0:45

Fig. 08.10.01: prerectangle.py Code

I'll run the program.

The program reports that the first rectangle whose top left corner is at the point "five ten" with
width 15 and height 20 does overlap the second rectangle whose top left corner is at the point
"ten five" with width 20 and height 15.

540
08.10@1:13

Fig. 08.10.02: Rectangles Overlap

Even though I think of this program as computing whether two rectangle objects intersect, there
are no explicit rectangle objects in the program. Instead, the program uses eight integer objects
to describe two implicit rectangles. This obscures both the intent and implementation code of
the program. For example, the argument list to the "rectangles intersect" function is so long that
it is easy to make typos when calling the function and when defining it as well. The program
would be much easier to write, read and modify if there were actually two explicit rectangle
objects.

I have used a class definition statement to create a Rectangle type and have rewritten the
program to use this type. Running this program also displays overlap. I used four attributes for
each Rectangle object: x, y, width and height, where x and y are integer coordinates of the top
left corner of the rectangle.

Here is a simplified syntax diagram for a class definition statement.

08.10@2:31

Fig. 08.10.03: Simplified Syntax Diagram for Class Definition Statement

The identifier is called the class name.

Here is the simplified semantic rule for class definition:

541
1. evaluate the class's suite using a new local namespace and the global namespace
2. create a class object and use the new local namespace as the namespace of the class
object
3. if the class name is not in the original local namespace, add it
4. bind the class name in the original local namespace to the new class object

I will use the rectangle program to show you how the class definition semantics work and
highlight the difference between "new local namespace" and "original local namespace". When
the program starts, the local block is the main module, whose namespace is *both* the local and
global namespace. The pre-bound identifier print is bound in this namespace.

08.10@3:32

Fig. 08.10.04: Pre-bound Identifier print is Bound in Namespace. Print Function is Omitted to Simplify
Diagram

The first statement in the main module is a function definition, so the interpreter applies function
definition semantics. It creates a function object with its own namespace. It adds the identifier
main to the local namespace, binds main to the function object, and binds a reference to the
main module namespace in the main function namespace.

542
08.10@3:56

Fig. 08.10.05: Binds Reference to Main Module Namespace in Main Function Namespace. Global
References Omitted to Simplify the Diagram

Since the next two statements are also function definitions for "create rectangle" and "rectangles
intersect", the interpreter repeats these function definition semantics for each of these functions.

08.10@4:13

Fig. 08.10.06: Function Definition Semantics Applied to create_rectangle and rectangles_intersect


Function Definition

The next statement is a class definition so the interpreter applies class definition semantics.

In step 1, the interpreter creates a new empty namespace as the local namespace. It uses this
local namespace and the current global namespace to evaluate the suite. Recall that the suite in
a function definition is not evaluated when the function definition is evaluated. Instead the suite
of a function definition is only evaluated when the function is called. However, the semantics for
class definition prescribe that the suite of a class definition is evaluated when the class definition

543
is evaluated. Since the suite only contains a pass statement and the pass statement does
nothing, the suite is done.

08.10@5:03

Fig. 08.10.07: New Local Namespace Created to Evaluate the Class’s Suite

In step 2, a new class object is created, and its namespace is the new empty local namespace.

08.10@5:13

Fig. 08.10.08: Rectangle Class Object Created

In step 3, the class name, Rectangle, is added to the *original* local namespace, the
namespace of the main module.

544
In step 4, Rectangle is bound in this original local namespace, main module, to the new class
object.

08.10@5:30

Fig. 08.10.09: Rectangle Bound to the New Class Object

The last statement in the main module is a function call to main, so the interpreter applies the
function call semantics.

The identifier “main” is dereferenced in the local namespace to obtain the main function object.
The "main" function code is evaluated to obtain a result object. The local block is now the main
function and the local namespace is the main function namespace.

545
08.10@5:56

Fig. 08.10.10: Local Block is Now the Main Function and the Local Namespace is the Main Function
Namespace (Video Screenshot)

The first statement is an assignment statement whose expression is a function call to "create
rectangle". Familiar function call semantics are used to find the function object, "create
rectangle" in the global namespace, evaluate the four argument expressions, five, ten, fifteen
and twenty and put them in an argument list. The interpreter then adds the parameters, "corner
x", "corner y", width and height to "create rectangle's" namespace, binds each parameter to its
corresponding argument, and evaluates "create rectangle's" code.

546
The local block is now the "create rectangle" function and the local namespace is the "create
rectangle" function namespace.

08.10@6:47

Fig. 08.10.11: Binds Each Parameter to its Corresponding Argument and Evaluates create_rectangle’s
Code (Video Screenshot)

The first statement of create rectangle is an assignment statement whose expression is a


function call to a function whose name is Rectangle. When function call semantics are applied,
the identifier Rectangle is used in the global namespace to find the Rectangle object which is a
class.

In Python, classes are a kind of function. So far, you have seen several kinds of functions,
built-in functions like len, user-defined functions like "create rectangle" and built-in methods like
lower.

A class is another kind of function that returns a new object whose type is that class. For
example, a call to int with no arguments creates the int object zero and a call to str with no
arguments creates the empty string object. A call to Rectangle with no arguments also creates a
Rectangle object but it doesn't have a very nice looking human readable form.

547
08.10@7:46

Fig. 08.10.12: Call to Rectangle with No Arguments Returns a Rectangle Object

Most Python documentation uses the term "callable" instead of function to describe classes and
methods. However, whether we say that a class is a function or a callable, it is evaluated like a
function call and it creates a new object whose type is that class.

The assignment statement in "create rectangle" binds the identifier "rect" to the new Rectangle
object that is created by the Rectangle call.

08.10@8:15

Fig. 08.10.13: Binds rect to new Rectangle Object

The second statement is an assignment statement where the target, "rect dot x" is an attribute
reference.

In the method Calls lesson, you saw the syntax and semantics of an attribute reference used as
an expression, such as "hello.lower".

In the "Alternate forms of the import statement" lesson, you used attribute references to access
a function in a module, "time dot sleep" and a module in a module, "os dot path".

However, you have not seen an attribute reference used as the target of an assignment
statement in this course. The current syntax diagrams for assignment, support a target that is
either an identifier or a subscription expression.

548
08.10@9:01

Fig. 08.10.14: Syntax Diagram for Target in Assignment Statement

Here is a generalized syntax diagram for the target in an assignment statement that also
supports an attribute reference as a target and the existing syntax diagram for attribute
reference.

08.10@9:15

Fig. 08.10.15: Syntax Diagrams for Target in Assignment Statement and Attribute Reference

Finally, here is the semantic rule for assignment, when the target is an attribute reference:

1. evaluate the assignment statement expression to obtain a result object


2. evaluate the attribute reference expression to obtain the base object
3. if the identifier is not in the namespace of the base object, add it
4. bind the identifier in the namespace of the base object to the result object

I'll apply this semantic rule to the second assignment statement in "create rectangle". In step 1,
the expression, "corner x", is evaluated to obtain the result object, integer 5. In step 2, the
expression rect is evaluated to obtain the base object, which is a Rectangle. In step 3, the
identifier x is not in the namespace of the base object, which is an empty Rectangle object, so x
is added to this namespace. In step 4, the identifier x in the namespace of the Rectangle object
is bound to the result object, integer 5.

549
08.10@10:23

Fig. 08.10.16: Identifier x in Namespace of Rectangle Object Bound to Integer 5

A new attribute called x has been added to the Rectangle object and this new attribute has been
bound to the int object 5. Python lets a programmer create new attributes anywhere in the code
that the object containing the attributes can be referenced.

The next three assignment statements are interpreted similarly. Notice that the parameter
names width and height, happen to be the same as the attributes, width and height, but this
doesn't matter. The other two attributes, x and y are not the same as the parameters, "corner x"
and "corner y", which were used to access the argument objects.

The return statement returns the Rectangle object to the calling function main, which adds the
identifier rect1 to its namespace and binds rect1 to the Rectangle object.

08.10@11:15

Fig. 08.10.17: Identifier rect1 is Added to main’s Namespace and Binds rect1 to the Rectangle Object

The next assignment statement in function main is interpreted similarly so that the identifier
rect2 is bound to a second Rectangle object.

550
The next statement in the main function is an if statement, whose condition calls the function
"rectangles intersect".

The identifier "rectangles intersect" is dereferenced in the global namespace, its two argument
expressions are evaluated to obtain the two Rectangle objects and they are put into an
argument list. The interpreter then adds the parameters, r1 and r2 to the"rectangles intersect"
namespace, binds them to the argument rectangles, and evaluates the "rectangles intersect"
code.

08.10@11:53

Fig. 08.10.18: Parameters r1 and r2 Added to Namespace and Binds Them to Argument Rectangles

The local block is now the "rectangles intersect" function and the local namespace is the
"rectangles intersect" function namespace.

This function contains a single return statement with a complex expression. Each attribute
reference, such as "r1 dot x" is evaluated using the semantic rule for attribute reference from the
method calls lesson. In step 1, the expression r1 is evaluated in the local namespace to obtain
the first rectangle object. In step 2, the attribute x is in the namespace of this Rectangle object
so it is dereferenced to obtain the integer object 5. Similarly when the attribute reference "r2 dot
width" is evaluated, the expression r2 is evaluated in the local namespace to obtain the second
rectangle object. The attribute width is in the namespace of this Rectangle object so it is
dereferenced to obtain the integer object 20.

551
Evaluation of the expression in the return statement results in the boolean object true so true is
returned to the calling function, main, and the string 'overlap' is displayed.

08.10@13:06

Fig. 08.10.19: Return Statement is Boolean Object true, Returned to main Function, and String ‘overlap’
is Displayed (Video Screenshot)

A class definition is used to define a new Python type. Attributes in the namespace of objects of
this type refer to component objects. A Rectangle object has four component integer objects,
two that represent x and y coordinates and two that represent the width and height of the
rectangle.

Programs that create explicit composite object types are easier to read, write and modify than
programs that use many simpler objects to implicitly represent composite objects.

You have already seen that a sequence can be used to reference multiple objects. I could use
lists in the program instead of creating a Rectangle type.

Here is a program with the same functionality that uses lists.

552
08.10@13:54

Fig. 08.10.20: rectanglelists.py Has Same Functionality as prerectangle.py but With Lists

Although the list program is shorter, the numerical indexes in the "rectangles intersect" function
are much harder to understand than the attribute names: x, y, width and height. In fact, it took
me five tries to successfully translate the different components from the prerectangle program to
indexes before I got it right. In the end I made a chart to increase my confidence that the
translation was correct.

If I wanted to add more functions to this program that operate on Rectangles, I would need to
keep the chart to remember which index represents which component.

In a program where more functions are written that operate on composite objects, the increased
clarity of using attributes rather than lists becomes even more apparent.

This video introduced a new statement called a class definition that creates a new Python class
or type. It also generalized assignment statement syntax and semantics to support attribute
references as targets.

553
08.11 Program Poke the Dots Version 2

Time to code!

To start programming the second version of Poke the Dots, I will start with the Game class
definition. Beneath the function definitions from version 1, I'll start with the class definition
header, which is "class Game". Inside the class, I will add a pass statement.

08.11@0:27

Fig. 08.11.01: Class Definition Header for Game Beneath Function Definitions from Poke The Dots
Version 1 (Video Screenshot)

Like each function, each class also needs a descriptive comment. An object of type Game will
be used to represent the complete game, so I will add the comment "An object in this class
represents a complete game".

I will also add one comment for each attribute. In each comment, I will translate one algorithm
attribute to a valid Python identifier.

554
08.11@0:54

Fig. 08.11.02: Comment for Game Class Definition Header

In version 1, each step in the create game panel was translated to a number of assignment
statements within the main function. In this version, the assignment statements will be removed.
Instead one game object and two dot objects will be created. I will write a new function, called
create_game, to create a game object and assign its attributes. You will create the two dot
objects inside of create_game.

08.11@1:17

Fig. 08.11.03: Created New Function create_game

Inside the new create_game function, I will create a game object by using the name of the
Game class as a function call and binding the identifier game to this new Game object. The first
attribute of my game object should be the window, so I will add window as a parameter to the
function definition. Now I can bind the attribute, window, of the game object to the argument
object.

A new attribute called window has been added to the Game object and this new attribute has
been bound to a window object.

555
08.11@1:43

Fig. 08.11.04: Bound Attribute window of Game Object to Argument Object

Recall that Python lets a programmer create new attributes anywhere in the code that the object
containing the attributes can be referenced. Also recall that the parameter name doesn't matter.
If I use a parameter called fred instead of window, I need to bind game dot window to fred's
object.

08.11@2:14

Fig. 08.11.05: Parameter fred Bound to Attribute window of Game Object - Parameter Name Does Not
Matter

However, fred is not a descriptive name, so I will change the parameter back to window.

I'll also add bind the attribute reference game dot close_selected to False.

08.11@2:27

Fig. 08.11.06: Bound Attribute Reference game.close_selected to False

The two dots are attributes of the game object, but they require their own attributes, so you will
use a separate function to create the dots. You can create a single create_dot function and call
it twice to create both dots. This is similar to calling a single draw function twice to draw both
dots. I will bind the attribute reference game.small_dot to the result of a call to create_dot(). You
must add appropriate arguments to create_dot().

556
08.11@2:54

Fig. 08.11.07: Bound Attribute Reference game.small_dot to Result of Call to create_dot()

The create_game function should return the game object, so I will add a return statement.

08.11@3:00

Fig. 08.11.08: Return Statement to Return the Game Object

Now I will go back to main and remove the old assignment statements that create the dots and
clock, since they will be replaced by a call to the create_game function. I'll add the call and pass
window as an argument. I'll also bind a new identifier game, to the result of create_game.

08.11@3:21

Fig. 08.11.09: Replaced Old Assignment Statements in main With Call to create_game and Bound
Identifier game to Result of create_game

Now I can pass the single game object into the play_game function, instead of passing the
unwieldy collection of arguments that represent each aspect of the game individually!

You will finish the create_game function as part of the next activity, but you can already see that
passing a single object with many attributes is much easier than passing so many individual
objects.

08.11@3:45

Fig. 08.11.10: game Object Passed to play_game Function

557
I'll modify the play_game function definition next. The first thing I'll do is replace the extended
parameter list with the single parameter game. Since the game object is now passed as a
parameter, I no longer need the local identifier close_selected. Instead, I can use the
close_selected attribute of the game object.

08.11@4:05

Fig. 08.11.11: close_selected Attribute of Game Object Used Instead of Local Identifier close_selected

Similarly, the handle_events function no longer needs to return an updated value for
close_selected. Instead, I can rebind the close_selected attribute of the game object, as a side
effect of handle_events. The close_selected attribute is accessible anywhere the game object is
accessible.

I'll delete the identifier close_selected and add game as an argument to the handle_events call.

I'll also add game as a parameter in the handle_events function definition.

Now I will remove the lines that bind and rebind closed, since I have access to the
close_selected attribute from the game object.

When the event type is QUIT, I simply rebind game dot close_selected to True.

08.11@4:46

Fig. 08.11.12: game Object Passed and close_selected Attribute is Accessible Anywhere in
handle_events

Finish the version 2 program yourself. You need to finish binding the Game attributes, create a
Dot class, and bind the Dot attributes. Then you can use these attributes in your other functions.
For example, when you call move_dot in update_game, you'll be able to pass game dot

558
small_dot as an argument, instead of the collection of components for the small dot. Inside of
the move_dot function, you can use the dot attributes instead of the previous individual
argument objects used in version 1.

As you gain programming experience, you'll see how convenient classes are!

559
08.12 Reflect on Poke the Dots Version 2

Welcome to the Reflection for Poke the Dots version 2.

08.12@0:13

Fig. 08.12.01: Review Code Subtask in Create Game Diagram

Here is the code for version 2. With the new Game and Dot classes, we've passed the failed
Software Quality test from version 1! Check out play_game and update_game now. They're
down from seven and eight parameters respectively to one each. Fantastic!

560
08.12@0:31

Fig. 08.12.02: Software Quality Test Passed After Implementing New Classes and Reducing Parameters

Now let's look inside the Game and Dot objects to see the attributes. To do this, I'll add a
breakpoint next to the first statement of create_game. I'll press the bug button. The highlighted
statement has a function call to the Game class that creates a new Game object. I'll press the
step-in button.

08.12@0:53

Fig. 08.12.02: Breakpoint Added to Function Call to Game Class

The debugger does not show me Python code for the Game() function, since I have not
provided any yet. Recall from the Class Definition lesson that every class object is also a
function object. In a future version you will see how to add code to a function object that is also

561
a class. However, since the Game function was evaluated, a new Game object was created.
The assignment statement bound the identifier game to this new object

08.12@1:20

Fig. 08.12.03: game Object Created and Bound to Identifier game

You can see several attributes that start with double underscores. Ignore these attributes. None
of the game attributes listed in the Game class comment, such as window and small_dot are in
the game object yet. These attributes will be added to the game object when we bind them to
objects.

When I press the step over button stack data shows that a window attribute has been added to
the game object and bound to the window object we passed into the function. This happens for
the next three assignment statements as well.

08.12@1:50

Fig. 08.12.04: window Attribute of game Object Bound to window Object

562
The game object now has three new attributes, clock, close_selected, and frame_rate.

08.12@2:04

Fig. 08.12.05: game Object Has Attributes clock, close_selected, and frame_rate

The current highlighted statement creates a new dot and binds an attribute reference of the
game to it. I'll press the step into button to access the create_dot function. Create_dot starts
with five parameters in the local namespace. The first assignment statement creates a Dot.

When I step over this statement, dot is added to locals and bound to the new Dot object.

I'll step over the next five statements to add attributes to the new dot. In locals you can see that
all five attributes have been added to the dot and bound to the five argument objects. For
example, the attribute "velocity" and the parameter "speed" are both bound to the same list
object, since the addresses are the same.

563
08.12@2:42

Fig. 08.12.06: Attribute velocity and Parameter speed Bound to Same List Object

With the small dot created, I'll return it to the create_game function.

In Stack data, you can see that "small dot" has been added as an attribute to the game object.
I'll step over the the "big dot" statement and check Stack data again. You can see that the big
dot has been added as an attribute of game as well.

08.12@2:59

08.12@3:07

Fig. 08.12.07, 08.12.08: small_dot and big_dot Added as Attributes to game Object

564
Finally, I'll step out to return the game object to the main function. As you can see in Stack Data,
the main function references two objects in the locals namespace: the window and the game.

I'll step into play_game. You can see the game object argument in the local namespace of
play_game. Expanding the game object, you can see the attributes added in create_game. For
example, game dot close_selected is False.

08.12@3:39

Fig. 08.12.08: game.close_selected Attribute in game Object

At this point in the program, we have set many game and dot attributes, but have not used any
of them. The while statement is the first time we use an attribute that was set previously. It
would be silly to set an attribute and never use it.

Attributes can also be used to record that an object has changed. In handle_events, the
close_selected attribute of game is rebound after the close event occurs so the while statement
can recognize this change and stop the program. The center and velocity attributes are also
used to record important program changes in the dot objects. In contrast, the window attribute of
game is used in several places, but never changed.

I'll scroll down to draw_game and place a breakpoint next to the first draw_dot function call.
Then I'll press the bug button to skip there.

565
The game object has been passed into the draw_game function so you can see it in the local
namespace. The highlighted draw_dot function uses another attribute reference to access the
small_dot attribute of "game" to pass to the draw_dot function.

08.12@4:48

Fig. 08.12.09: game.small_dot Attribute Passed to draw_dot Function

Stepping into draw_dot, you can see that the dot, and not the game, has been added to the
local namespace.

566
08.12@4:53

Fig. 08.12.10: dot Object Added to Local Namespace of draw_dot

The current highlighted statement is another attribute reference that references the window
attribute of the dot object that was bound previously. It uses the associated get_surface method
from the window class, and binds a local identifier to it.

Pretty neat. Attribute references are great for passing multiple objects around functions without
needing multiple different argument objects for your functions. They are also great for recording
important changes in the program.

That's it for the trace!

Let's take a look at the new Software Quality tests for user-defined classes!

The Comments section has some new tests for user-defined classes. Every class you make
should have a comment that indicates what the class is used for.

567
08.12@5:41

Fig. 08.12.11: New Tests in Comments Category of Software Quality Tests

In Identifiers and Names, user-defined class names should use Capitalized Words. This is a
Python style convention.

08.12@5:50

Fig. 08.12.12: New Test in Identifier and Names Category of Software Quality Tests

Finally, there is a new category of tests for user-defined classes. The single test asks whether
your class represents a group of objects that is used in two or more tasks and forms a single
conceptual object.

08.12@6:05

Fig. 08.12.13: New Category User-defined Classes in Software Quality Tests

Let's check if the new classes, Dot and Game pass the new tests.

568
08.12@6:10

Fig. 08.12.14: Dot and Game Classes

Each class has a name that starts with a capital letter, and each begins with a class comment
that describes what it is used for.

A game represents a group of 6 objects that comprise the entire Poke the dots game. It is used
in multiple functions such as: main, play_game, and handle_events,. A dot represents a circular
object in the window that has a color, center, radius and velocity. There are multiple dots that
are used in multiple functions such as: update_game, draw_dot, and move_dot.

You should edit your code to ensure that these tests are satisfied.

Other tests will be added to the User-defined classes section in future lessons.

That's it for version 2! Congratulations! In the next version you'll be adding more functionality to
make the game more interesting.

569
MODULE 09: POKE THE DOTS VERSION 3

09.01 Solution Issues in Poke the Dots Version 2

To get started on Poke the Dots version 3, let's take a look at the solution issues for version 2.

You used classes to improve the quality of your code, but the game still lacks most features that
make it playable. The player can't interact with the game, except to close the window, and the
score is not displayed. In version 3, you're going to change both of these things!

Recall that an event is something that happens in a game. It can be the result of a player action
or it can be caused by some process in the computer. In the previous version, you only handled
a close event that occurred when the player pressed the close icon. In this version, you will
make the game more interactive by handling one more player activated event, a mouse click.

In Poke the Dots, you will only handle close and mouse click events. Pygame has support for a
number of other events, including key presses. Handling other events is similar to handling
mouse clicks. You will encounter other kinds of events in the reflection for this version of Poke
the Dots.

570
09.02 Observe, Play, Describe, and Create Test Plan for Poke the Dots Version 3

Let's take a look at Poke the Dots version 3.

09.02@00:14

Fig. 09.02.01: Game Creation Diagram Observe Game Version

Similar to the first and second version, the dots move and bounce when they hit the edges. The
game does not stop when the dots collide.

571
09.02@00:20

Fig. 09.02.02: Poke the Dots Version 3 Collision

09.02@00:20

Fig. 09.02.03: Poke the Dots Version 3 Collision (Video Screenshot)

572
In the third version, the scoreboard appears in the corner, displaying the seconds since the
game has started. When I press the left mouse button, the dots disappear and reappear in
different locations in the window. They teleport!

09.02@00:28

Fig. 09.02.04: Poke the Dots Version 3 Pre-Click

573
09.02@00:28

Fig. 09.02.05: Poke the Dots Version 3 Pre-Click (Video Screenshot)

09.02@00:28

Fig. 09.02.06: Poke the Dots Version 3 Post-Click

574
09.02@00:28

Fig. 09.02.07: Poke the Dots Version 3 Post-Click (Video Screenshot)

I'll press the mouse button a few more times. Each time, they both teleport to new random
locations.And, of course, pressing the close button on the window closes the window.

Play this version to familiarize yourself with the changes. Then complete your new description
and functional test plan.

575
09.03 Create Algorithm for Poke the Dots Version 3

Let's update the algorithm for Poke the Dots version three.

09.03@00:15

Fig. 09.03.01: Game Creation Diagram Create Algorithm Subtask

I'll show you how to handle the mouse click event that will teleport the dots.

I'll navigate through the algorithm to get to the Handle One Event panel. I won't change the if
icon or the true step, since I still want the game to handle the close event. Instead, I'm going to
add the words "handle non-close event" to the false box, expand that box, and then add an if
icon.

576
09.03@00:33

Fig. 09.03.02: False Box Added to Handle One Event If Icon

Expanding the false box of an "if icon" and adding another "if icon" is the algorithm equivalent of
an elif clause in code. For this "if", I'll choose the condition "event category equals mouse click".
In the true box, I'll add the words "handle mouse click" and then expand that step into its own
panel.

577
09.03@00:57

Fig. 09.03.03: Handle Non-Close Event Panel with If Icon

That's it for this demonstration! In the next activity, you'll finish the algorithm, including
teleporting the dots and adding the new scoreboard.

When you draw multiple game objects, each object you draw will be drawn on top of the
previous objects and could hide parts of them. You should play the game to determine whether
the scoreboard should be drawn before or after the dots.

578
09.04 Program Poke the Dots Version 3

Most of the version 3 programming will be left to you, but I'll give you a few hints to help you.

The algorithm step for "update score using time elapsed" can be translated to code using the
function get_ticks, found in the pygame time module. This function returns the number of
milliseconds since the window was created. You must convert milliseconds to seconds, and then
convert this integer to a string object to display it.

You want the dots to teleport after a mouse click. There are three mouse events supported by
pygame.

A MOUSEMOTION event is generated whenever the mouse is moved.


A MOUSEBUTTONDOWN event is generated when the mouse button is pressed.
A MOUSEBUTTONUP event is generated when the mouse button is released.

A mouse click doesn't occur until the mouse button is released, so you should use
MOUSEBUTTONUP to detect a click.

You're all set to start programming. Have fun!

579
09.05 Reflect on Poke the Dots Version 3

Welcome to the Reflection for Poke the Dots version 3!

09.05@00:13

Fig. 09.05.01: Create Game Diagram Review Code SubTask

I want to show you how easy it is to handle additional categories of events, now that we have a
solid event handling template. Although the final version of Poke the Dots will not handle key
presses, I will show you an example of how to handle keypress events so you can include
them in your projects. In this example, any time I press the "P" key, the dots will teleport the
same way they do when the mouse is clicked.

We need one pygame identifier for each new event category. I will import the "KEYDOWN"
event category from the pygame module. KEYDOWN is an event category that is generated
whenever a key on the keyboard is pressed.

Below the elif suite for the mouse button up event, I'll add a new clause: "elif event key equals
KEYDOWN". This time I will compare the type attribute of the event to the imported KEYDOWN
category.

580
09.05@01:02

Fig. 09.05.02: New Elif Clause

To react to this event, I'll add another call to the handle_mouse_up function. I could instead
create a new function called handle_key_down and copy the two randomize_dot function calls
from handle_mouse_up into it. However, since I want the effect of both events to be the same, I
might as well use a single function for both event handlers.

I'll play the game. When I press the 'p' key the dots teleport! Fantastic.

581
09.05@01:31

Fig. 09.05.03: Before P Key Press

09.05@01:31

Fig. 09.05.04: After P Key Press

582
What happens when I press the q key? The dots still teleport. Not so fantastic, since I only
wanted the p key to affect the dots.

09.05@01:35

Fig. 09.05.05: Before Q Key Press

09.05@01:35

Fig. 09.05.06: After Q Key Press

583
A KEYDOWN event is generated whenever any key is pressed, but this program should only
respond to a single key. Recall that each event has a type attribute that represents its category
such as MOUSEBUTTONUP or KEYDOWN. Each event category also has other attributes that
represent properties of that event.

Here is the documentation for the attributes for each kind of event.

09.05@02:06

Fig. 09.05.07: Python Event Documentation

For example, the position of the cursor is represented by the "pos" attribute of a
MOUSEBUTTONUP event. "pos" is bound to a tuple containing the x and y coordinates of the
mouse cursor when the event occurred. The "key" attribute of a KEYDOWN event represents
the key that generated the event. I must compare "event dot key" to a key value. The values for
each attribute must be imported from pygame.

Here is the documentation for the key attribute values in Pygame.

584
09.05@02:37

Fig. 09.05.08: Key Attribute Values Documentation

The "P" key is labeled as an uppercase "K" with an underscore and a lowercase "p". I'll import
that attribute value, which represents either a lower case or upper case P.

Within the elif suite I'll add an if statement whose clause is "if event key equals K_p". I'll move
the call to the handle_mouse_up function inside the "if" suite. I'll play the game again.

When I press the 'p' key the dots teleport but when I press a different key, the dots don't
teleport.

585
09.05@03:03

Fig. 09.05.09: Before P Key Press

09.05@03:03

Fig. 09.05.10: After P Key Press

586
09.05@03:07

Fig. 09.05.11: Before Q Key Press

09.05@03:07

Fig. 09.05.12: After Q Key Press

587
To inspect the key attribute, I'll place a breakpoint next to the if clause within the keydown event.
Then I'll press the bug button and press the 'p' key, which stops the debugger next to "if event
key equals K p".

In Stack Data, I'll expand the event object in locals.

09.05@03:31

Fig. 09.05.13: key Attribute in Locals

588
09.05@03:36

Fig. 09.05.14: K_p Identifier in Locals

09.05@03:42

Fig. 09.05.15: unicode Attribute in Locals

589
Notice that the "key" attribute of the event is the integer 112, which is the same value as the
"K_p" identifier imported into globals. Also notice that the "unicode" attribute of the event is "p"
so I can easily see what key has been pressed. Pretty cool.

I'll press the bug button again and this time press the "q" key. Now stack data shows an event
with key attribute 113 and unicode attribute "q", which is not equal to "K p", so the dots should
not teleport.

09.05@03:58

Fig. 09.05.15: key and unicode Attributes in Locals

I'll press the bug button again and the dots continue to move without teleporting.

I'll stop the trace.

I could have combined the if clause with the elif clause as: "elif event type equals KEYDOWN
and event key equals K P". However, having a single elif clause for the entire KEYDOWN event
category, containing multiple if clauses, allows me to perform different actions for different keys.
In general, you should have a single elif clause for each event type that contains multiple if
clauses to check different attribute values.

As you can see, it's straightforward to add more event categories to the event loop by adding
more elif clauses, one for each event category.

590
There are no new software quality tests for this version of Poke the Dots, but make sure that
your code passes all the previous tests.

Congrats! You're done Poke the Dots version 3.

591
MODULE 10: POKE THE DOTS VERSION 4

10.01 Solution Issues in Poke the Dots Version 3

Welcome to Poke the Dots version 4!

Let's start by looking at the solution issues for version 3.

10.01@00:15

Fig. 10.01.01: Game Creation Diagram Identify Solution Issues

The most obvious issue is that the game doesn't end when the dots collide, but you'll leave that
for another version. Version 4 will focus on improving the quality of your code.

Adding classes in version 2 allowed the code to pass the software quality test that states
functions should have no more than five arguments.

In version 3, we discussed how classes are a form of data encapsulation because they group
component objects together to be used as one entity. However, encapsulation also prescribes
that the internal details of an encapsulated object should be hidden. Therefore, we should not
access the attributes of any object outside its class definition. The implementation of data
encapsulation for the Game and Dot classes isn't complete, because the attributes of these
classes are bound and accessed outside the class definitions! To fix this, you must add code to

592
the currently empty Game and Dot class definitions to bind the attributes inside these
definitions.

Some attributes, such as the title and size of a window or the color and center of a dot, vary
between different windows and dots that are created. Consider the Window function, which has
arguments that pass the title and size objects so they can be used to create an appropriate
window object. If you add arguments to the Game and Dot functions, these arguments can be
used to bind attributes when the objects are created.

Other attributes can also be bound when an object is created, but don't need to be passed as
arguments. An example is the Game's frame rate. Since every game object in the program uses
the same fixed frame rate, we don't need to pass it as an argument when the Game object is
created.

If an attribute needs to be changed during the program, such as the font size of a window,
encapsulation prohibits the attribute from being rebound outside the class definition. Therefore,
a method should be used to change the attribute. Specifically, the set_font_size method is used
to change the font size of a window.

Methods are functions defined inside of a class. Methods let you modify and return information
about an object, without directly accessing attributes outside the class definition. Methods in
classes encapsulate behaviours, as well as data. Behaviour encapsulation refers to grouping
all the behaviours or actions that can be applied to an object, the same way data encapsulation
groups all the information about an object. For example, think of the str class. It has various
methods that check whether a string is upper or lowercase, contains digits or letters, and so on.
These methods make up the behaviour of a string object, since the methods determine what
you can do with a string in a program.

Encapsulating both data and behaviors within a class definition uses a concept called
information hiding. Information hiding is a design technique that limits access to details about
data and code. Using a class tells programmers, yourself included, not to modify the
implementation details of a class outside of its definition.

Imagine that you were assigned to write the Game and Dot classes for Poke the Dots, while
another programmer worked on the rest of the code. You would write a play method for Game
and draw methods for both classes. Then the other programmer would be able to call the
Game's play method in the program's main function. The play method in the game class would
call all the other methods in both classes.

If you were asked to update the game to be Poke the Stars instead of Poke the Dots, you would
need to change your classes. You could change the Dot class to a Star class by modifying
internal details of that class, such as the attributes and the draw method. You could also change
the Game class to call the methods in the new Star class instead of the Dot class. The other

593
programmer would not need to make any changes, since the call to the play method in the
Game class would still work. This is because that programmer's code did not use any internal
details from either class. Using encapsulation and information hiding allows you to use a class
without having to change the external code if the information inside the class changes!

Python does not strictly enforce the "do not touch" convention for code inside of a class
definition. Other languages have stronger mechanisms for enforcing information hiding, such as
a series of keywords that you can use to indicate which information should be private. Python
uses convention, rather than language features, and trusts programmers to follow the
conventions.

Version 4 of Poke the Dots focuses on code quality by adding methods to user-defined classes,
so the functionality of this version doesn't change. You don't need to make a new description or
functional test plan. Your next activity is modifying your algorithm to add methods.

594
10.02 Create Algorithm for Poke the Dots Version 4

You will now add methods to your Poke the Dots algorithm.

10.02@00:12

Fig. 10.02.01: Game Creation Diagram Create Algorithm Subtask

Let's revisit the Person class that we made in the algorithm builder in an earlier lesson. When
adding a class, there are two options in the shape palette. We previously ignored methods and
only used attributes, but now I will add a method to the person class.

The first method I will add will be called "initialize". Methods just have an action phrase, instead
of an action and an object, like normal steps. When you apply a method from the Person class it
will always be applied to a person object. Therefore, no object is selected from the vocabulary.
In this case, the action phrase is initialize.

To select just an action phrase, I will choose the blank option in the object menu.

595
10.02@01:01

Fig. 10.02.02: Person Class with Initialize Method

The initialize method will contain all the the steps that must happen to create and bind an
object's attributes. Recall that the Person class has three attributes: name, birthdate, and
height. To create a Person object, these attributes must be bound.

Binding the name and height of a Person are too trivial to deserve their own steps, since they
are a literal string and integer. However, binding the birthdate is more complicated, since a
birthdate includes three components: a year, a month, and a day. So, I will add a "create
birthdate" step to the initialize method. If there were any other complicated steps needed to
create a Person object, I would also add them here.

596
10.02@01:40

Fig. 10.02.03: Initialize Method with Create Birthdate Step

Create birthdate is just a step that will become a block of code in the initialize method. Create
birthdate is not a method.

A person's age will change as time passes, so I will create another method to compute the
person's age. I will add a method, and select "get age" as the action phrase.

597
10.02@01:55

Fig. 10.02.04: Person Class with Get Age Method

In a normal step the format is action-object. If this was a normal step, get would be the action
and age would be the object. However, for methods, the object is the special argument that the
method is applied to. Therefore, person is the object that the "get age" action phrase is applied
to.

Just as I did to the initialize method, I will expand the "get age" method, and add two steps to it.
I'll add the normal steps, "get current date", and "subtract birthdate from current date".

598
10.02@02:30

Fig. 10.02.05: Get Age Method Panel

I will return to the main program panel. I'll add a step. Notice that I now have a choice between
adding a normal step and adding a method from an existing class. I'll add a normal step called
"create person".

599
10.02@03:41

Fig. 10.02.06: Create Person Step in Main Program Panel

We need a step to create a new object before we can apply any method to it. Therefore, we will
always use a step called "create classname" to create an object and the initialize method will be
applied to this new object.

Since initialize must always be applied there is no use repeating myself by expanding "create
classname" for every class I create and explicitly calling the initialize method. Instead, we will
use a convention in our algorithms that "create classname" always calls a method called
initialize on that class.

In the Algorithm builder, for every class I create, the "create classname" step will implicitly use
the initialize method on the newly created object automatically. I don't don't need to repeat
myself by expanding "create classname" and adding a call to the initialize method for every
class I create. Therefore, I won't expand "create person" into its own panel that contains a call to
the method initialize. Instead, "Create person" will create a new Person object and call the
initialize method on it, automatically.

For the second step in the main panel, I will add a method. From the list, I'll choose the "person:
get age" method.

600
10.02@03:51

Fig. 10.02.07: Create Person Algorithm Completed Main Program Panel

Now my algorithm creates a new person, and computes their age!

Let's look at the Poke the Dots algorithm.

601
10.02@04:00

Fig. 10.02.08: Poke the Dots Version 4 Algorithm Start State

Notice that the create_window step is in the main panel. However, when we implemented
create_window we included the name of the game, "Poke the Dots" and the window size.
Therefore, create_window is not really independent of the game we are creating. The game
should encapsulate all of the information about the game including the window requirements.
Now that you know more about encapsulation, this issue is important.

I will recreate the functionality of create_window inside the Game class to improve
encapsulation. First I will add an initialize method to the Game class.

602
10.02@04:30

Fig. 10.02.09: Game Class with Initialize Method

Now I will add a create_window step to the initialize method and delete the create_window step
from main.

10.02@04:34

Fig. 10.02.10: Initialize Method Panel with Create Window Step

603
I need to make one other change to the main panel. Since I am creating the window in the game
class, I also want to close it, in the game class. Therefore, I will delete close window from the
main panel. In the next activity, you will add close window to the end of the play method in the
game class, when you create the play method.

10.02@04:52

Fig. 10.02.12: Main Program Panel Create Window and Close Window Steps Removed

Most of the functionality that is currently encompassed by user-defined functions must be


recreated with user-defined methods inside your existing Game and Dot classes. Finish the
initialize method in the Game class and create one for the Dot class. Create the appropropriate
Game and Dot methods. Update your algorithm to use these methods.

604
10.03 Python - User-Defined Method

This video introduces user-defined methods.

A function that is defined inside a class is called a method. Every method must be applied to an
object whose type is the class where the method is defined. Recall from the Method Calls
lesson that the method, lower, is defined in the string class so it can be applied to a string object
to return a similar string object but with all uppercase letters converted to lowercase letters.

Recall the string method, find, returns the index of a string, where the first occurrence of a
substring starts. In this case, the first occurrence of the substring "o n" is at index 3 of the string
Edmonton.

10.03@01:00

Fig. 10.03.01: lower and find Methods in Python Shell

10.03@01:00

Fig. 10.03.02: lower and find Methods in Python Shell (Video Screenshot)

605
The object that the method is applied to is a special argument, in addition to its normal
arguments. The special argument of the method, "lower" is "R2D2". The special argument of
the method, "find" is 'Edmonton'. But "find" also has an ordinary argument, 'on'.

A user-defined method is created by placing a function definition inside the suite of the class
that defines it. One extra parameter is added to the start of the function definition and this
parameter is bound to the special argument when the method is called.

Consider the program from the Class Definition lesson that checks to see if two rectangles
intersect or overlap. Each Rectangle object has four attributes: x, y, width and height, where x
and y are integer coordinates of the top left corner of the rectangle.

Here is a new version of the program where the create_rectangle and rectangles_intersect
functions have been replaced by the methods "__init__" and "intersects" respectively. I will
explain why I have used underscores later.

10.03@01:52

Fig. 10.03.03: Updated Rectangle Methods Program

I will use this new rectangle program to show you how user-defined methods are defined.

When the program starts, the local block is the main module, whose namespace is both the
local and global namespace. Although the pre-bound identifier print is bound in this namespace,
I'll leave it out to simplify the diagram.

606
The first statement in the main module is a function definition, so the interpreter applies function
definition semantics. It creates a function object with its own namespace. It adds the identifier
main to the local namespace and binds main to the function object. It then binds a reference to
the main module namespace in the main function namespace, but I'll leave this global reference
out to simplify the diagram.

10.03@03:00

Fig. 10.03.04: main Function Added to Memory

The next statement in the main module is a class definition so the interpreter applies class
definition semantics.

In step 1, the interpreter creates a new empty namespace as the local namespace and uses the
current global namespace to evaluate the suite.

607
10.03@03:12

Fig. 10.03.05: New Empty Namespace Added

The suite consists of two function definition statements, so function definition semantics are
used to evaluate each function definition. Notice that the function names are added to the local
namespace, which is the new local namespace that will be used by the class. Once again, I
won't add a global reference to simplify the diagram.

608
10.03@03:40

Fig. 10.03.06: Function Names Added to Local Namespace

Returning to step two of the semantics for class definition, a new class object is created, and its
namespace is the new local namespace.

10.03@03:49

Fig. 10.03.07: New Class Object Created

609
So, when a function definition appears inside a class, the function name is added to the
namespace of the class object. This is what makes a function, a method. Any function whose
name is bound inside the namespace of a class object is created as a method object instead of
a normal function object. So, both init and intersects are method objects, since they are defined
in the Rectangle class.

In step 3, the class name, Rectangle, is added to the original local namespace, the namespace
of the main module.

10.03@04:23

Fig. 10.03.08: Class Name is Added to the Original Local Namespace

In step 4, Rectangle is bound in this original local namespace, to the new class object.

610
10.03@04:30

Fig. 10.03.09: Class Name Bound to the New Class Object

The last statement in the main module is a function call to main, so the interpreter applies the
function call semantics. The identifier “main” is dereferenced in the local namespace to obtain
the main function object. The "main" function code is evaluated to obtain a result object. The
local block is now the main function and the local namespace is the main function namespace.

611
10.03@04:59

Fig. 10.03.10: Updated main Namespace

The first statement is an assignment statement whose expression is a function call to


Rectangle, which is a class object. In step 1 of the function call semantics the identifier,
"Rectangle" is dereferenced in the global namespace to obtain a class object. Since every class
object is a function, no semantic error occurs.

612
10.03@05:14

Fig. 10.03.11: Rectangle Class Object

In step 2, the four argument expressions, five, ten, fifteen and twenty are evaluated and put in
an argument list.

10.03@05:30

Fig. 10.03.12: Argument Object List Added

613
In step 3, Rectangle is not a method object so no special argument is added to the argument
object list. In step 4, the argument list length is four. The function object is the Rectangle class
instead of a normal function. How many parameters does a class object have when it is used as
a function? I must generalize the function call semantics to support calls to functions which are
class objects by adding a new step 3.

If the function object is a class, create a new object whose type is that class. Then apply its init
method to the new object and return this new object as the result object

I will now "back up" and apply this new step 3. The Rectangle object is a class object so a new
Rectangle object is created and its init method is applied to the new Rectangle.

10.03@06:27

Fig. 10.03.13: Rectangle Object Created and __init__ Method Applied

In step 4, since the definition of init is inside the Rectangle class, the "init" function object is a
method. Because, init is being applied to the new Rectangle, the new Rectangle is added to the
argument list of init. In step 5, the argument list length is 5 and the parameter list length is 5 so
no error is reported.

614
10.03@06:49

Fig. 10.03.14: Rectangle Object is Added to the Argument Object List

In step 6, the interpreter adds the parameters, "special", "corner x", "corner y", width and height
to init's namespace, binds each parameter to its corresponding argument, and evaluates "init's"
code.

615
10.03@07:04

Fig. 10.03.15: Parameters Added and Bound to Corresponding Arguments

The local block is now the "init" method and the local namespace is the init method's
namespace. Each of the four statements in init, adds an attribute to the Rectangle object and
binds the attribute to an argument object.

616
10.03@07:24

Fig. 10.03.16: Attributes Bound to Rectangle’s Argument Objects

There is no return statement in init so you might think that the None object is returned by the
Rectangle class function. In fact, init does return the None object. However, step 3 of the
revised function call semantics indicates that the created Rectangle object is returned as the
result object of the call to the Rectangle class function. The identifier rect1 is bound to this
Rectangle object.

The next assignment statement in the main function is interpreted similarly so that the identifier
rect2 is bound to a second Rectangle object.

617
10.03@08:00

Fig. 10.03.17: rect1 and rect2 Identifiers Bound to Rectangle Objects

The next statement in the main function is an if statement, whose condition matches the syntax
of a function call so function call semantics are applied. In step 1, the function expression is the
attribute reference "rect1 dot intersects". The interpreter applies the semantic rule for attribute
reference. The interpreter dereferences rect1 to obtain the first rectangle object and looks for
the attribute "intersects" in the namespace of this first rectangle. "intersects" is not in the
namespace of the first rectangle, so an attribute error should be reported.

However, no error is reported. When the class definition was evaluated its method names were
added to the namespace of the class. The names were not added to the namespace of any
object from that class. In fact, no objects from that class even existed when the class definition
was evaluated and the class was created. Method names are put in the class's namespace and
are shared with all of its objects. An object uses the shared method names from its class. When
an attribute is not found in an object, the interpreter searches for that attribute in its class. I will
generalize our simplified semantics of attribute reference to reflect this.

If the attribute is in the namespace of the object, return the object it is bound to. Otherwise if
the attribute is in the namespace of the object's class, return the object it is bound to

Since the interpreter does not find the method name 'intersects", in the namespace of the
rectangle object, it searches in the namespace of the Rectangle class and dereferences it to
obtain the intersects method object.

618
Normal function call semantics continue so the argument list is created by evaluating rect2 to
obtain a one element argument object list and the special argument is added to the start of this
argument list. The parameters of the Intersects method are added to its namespace and bound
to the two argument objects. The intersects method is evaluated and returns True so the if
statement suite is evaluated and displays "overlap" in the shell.

The main function ends and so does the program.

This program uses special as the name of the parameter that is bound to the special object. Any
name can be used for the initial parameter of a method. However, Python has a convention that
"self" is used as the initial parameter name. I'll change the name to self in the program to follow
this convention.

The previous Rectangle program did not have an "__init__" method in the class definition, so
step 3 of the function call semantics doesn't make sense. If the class definition does not contain
an "__init__" method, Python applies a default init method that doesn't do anything. The
underscores must be used so that Python can find your init method.

This video introduced user-defined methods.

619
10.04 Python - Private Attributes

This video introduces private attributes.

Recall that encapsulation is a technique for restricting access to the implementation of a


component, so that the implementation of that component is independent of how it is used by
other components.

Consider the Rectangle class that was presented in the User Defined Methods lesson.

10.04@00:39

Fig. 10.04.01: Rectangle Class from 10.03

If an attribute is accessed outside of the class definition that defines it, then problems can occur
if the implementation of the class is changed. For example, I will change the program to access
the width attribute outside of the Rectangle class and run the program.

620
10.04@00:51

Fig. 10.04.02: Rectangle Class Updated to Access width Outside the Class

It works fine. However, if I change the Rectangle class so that width is no longer an attribute
then the program fails

10.04@01:00

Fig. 10.04.02: Rectangle Class Updated to No width Attribute

621
I have re-implemented the program to use, attributes x1 and y1 as the coordinates of the upper
left corner and attributes x2 and y2 as coordinates of the lower right corner. Since, the program
uses the width attribute outside the class, the program fails if the width attribute is replaced by
different attributes. This violates encapsulation since "the implementation of the Rectangle class
is no longer independent of how it is used by other components".

If you want to provide access to the width of a rectangle, you can create a width method in the
Rectangle class. Now the program runs even though there is no width attribute.

If access to attributes is restricted to the class in which they are defined, then objects can be
used independently of the implementation of an object's class. Therefore, encapsulation is
achieved!

Many programming languages have features that can be used to prevent access to attributes
outside of the class where they are defined. For example, Java and C++ both use the keyword
"private" so that an error is generated when an attribute is used outside of a class definition. In
fact, in C++, all attributes default to private unless a different keyword, such as public, is used to
specify that the attribute is not private. The exact syntax used in Java and C++ is not important.
What is important is that there are language features in Java and C++ that allow you to limit the
scope of attributes to enforce attribute encapsulation.

Python does not have a keyword that prevents an attribute from being accessed outside of a
class definition. Instead, Python has a convention. Any attribute that starts with a single
underscore should be treated as private. It should not be used outside of the class definition that
defines it. Therefore, I will change the attribute names in the Rectangle class to indicate that
they are intended to be private. However, this is just a convention. Attributes with a single
leading underscore can still be accessed outside the class definition

Python has another mechanism called properties that can be used to make it more difficult to
access attributes outside a class definition, but we won't discuss properties in this course.

This video introduced private attributes. Python uses a convention to mark private attributes with
a leading underscore.

622
10.05 Program Poke the Dots Version 4

Programming Poke the Dots version 4 will be similar to programming Hacking version 6, when
you added function definitions.

10.05@00:14

Fig. 10.05.01: Game Creation Diagram Write, Test, Debug Code

In this version, you will restructure your existing code to use methods. Each initialize panel in
the algorithm builder will become an init method in your code.

I will create the init method for Dot. I'll write "def __init__" for the method header. Since init is a
method, and every method definition requires a parameter to bind to the special argument when
the method is called, I'll add "self" to init's parameter list. Self must always be the first parameter
in a method's definition, regardless of whether there are other parameters. The Dot class
requires five additional argument objects to create a Dot, so I'll add corresponding parameters
for those arguments as well. They are color, center, radius, velocity, and window.

623
10.05@01:09

Fig. 10.05.02: Dot Class __init__ Method and Parameters Line 224

Next I'll initialize the attributes in this init method. In the "create_dot" function from version 3, we
bound each attribute reference to an argument object. The init method will replace the
create_dot function, so that all steps needed to initialize a Dot object are performed as soon as
the Dot is created.

In the create_dot function the local identifier dot was bound to a dot object, whose attributes
were then bound. In the init method, self is bound to a new dot object, whose attributes must be
bound. Therefore I will use the parameter self rather than the local identifier dot. I will add one
assignment statement for each dot attribute in the init method.

624
10.05@01:54

Fig. 10.05.03: Dot Class Parameter Assignment Statements Lines 225-229

In the "create_game" function, I'll replace the "create_dot" function calls with calls to the Dot
class function. I'll use the same arguments.

10.05@02:05

Fig. 10.05.04: Dot Class Function calls Lines 43 and 44

625
Now I'll delete the create_dot function definition and run the program.

The program still works!

10.05@02:12

Fig. 10.05.05: The Program Still Works!

Next I'll add another method to the Dot class. Note that the draw_dot function only has one
parameter, dot, as an argument. I'll copy the three statements in the draw_dot function suite and
delete the function definition.

Now I'll add the method header "def draw" to the Dot class. The draw_dot function required a
dot as a parameter. However, draw is a method, so it's special argument will be the dot I want to
draw. Therefore, "self" is the only parameter I need. Next, I'll paste the three statements I copied
from draw_dot into the draw method and change all the dot references to self.

626
10.05@02:51

Fig. 10.05.06: New draw Method in the Dot Class Lines 206-209

Now, I will replace the function calls to draw_dot in draw_game with method calls to the draw
method.

10.05@02:57

Fig. 10.05.07: draw Method Calls in draw_game Lines 88 and 89

627
I'll play the game again and it works! Great!

Now I'll start writing the initialize method in the Game class. First I'll create the method header.
Since the window will be created inside the game class in this version, I don't need a window
parameter. I'll create a window by modifying code from the old create_window function.

10.05@03:32

Fig. 10.05.08: __init__ Method in Game Class

The other four statements in create_window adjust the type, size and color of the window font. I
will make a method to do this. First I will create the header, def adjust_window. Now I will paste
the four statements from create_window into the adjust_window method. Since the window is
now an attribute of the game object I must use self.window instead of a local identifier, window.

628
10.05@03:52

Fig. 10.05.09: adjust_window Method in the Game Class Lines 152-156

Adjusting the window is one of the steps that should be taken when a Game object is created,
so I will add a call to adjust_window in the init method. Since adjust_window is being called
inside the Game class definition, its special argument will be self.

629
10.05@04:04

Fig. 10.05.10: adjust_window Call in the __init__ Method Line 146

Now I will delete the create_window function and the call to it in the main program. I will replace
the call to create_game by a call to the Game class function that does not use a window
argument.

630
10.05@04:21

Fig. 10.05.11: main Method with Call to Game Class Line 19

Finally, I will run the program, but it crashes at the first line of "play game" since there is no
close selected attribute. I will copy all of the attribute binding statements from create_game into
init, change the object reference from game to self, and run the program again.

631
10.05@04:36

Fig. 10.05.12: Attribute Statements from create_game Copied into the Game Class’ __init__ Method
Lines 136-143

It still doesn't work.

Python reports that the name window is not defined. To fix this, I must change window to
self.window in the initialize method and run it again.

632
10.05@04:48

Fig. 10.05.13: window Updated to self.window Lines 139 and 140

This time it works, until I try to close the window. To fix this, you must move "window.close" from
main to the play method, after you write the play method.

Now I'll show you how to adjust your code to adhere to the python convention for private
attributes. In the Dot class, I'll add single underscores to each attribute in the init method. As
well, I'll add an underscore to the "adjust window" call in the init method of the game class, and
to the start of the "adjust window" definition, since it should only be called inside the game class.
I want this method to be just as private as the attributes. Methods that start with a single
underscore should be treated as private, just as attributes are.

When I play the game this time the interpreter reports an Attribute Error when it tries to access
the window attribute of a dot, since this attribute has been renamed underscore window.
Throughout the rest of the code, the attributes of Dot have been accessed from outside of the
class. This is a good example of why we want our classes to be encapsulated. By changing the
names of the attributes in the initialize method, I have broken the functionality of the code.

Now it's your turn. Modify your program so that all attributes use a leading underscore and so
that you no longer access attributes outside their class definitions. You must also replace all
functions except for main by methods and update the code in main.

633
10.06 Reflect on Poke the Dots Version 4

Welcome to the reflection for Poke the Dots version 4.

10.06@00:13

Fig. 10.06.01: Game Creation Diagram Review Code

Now that you have created your own user-defined methods, you can explore some of the
differences between methods and functions using the debugger.

Let's investigate the init method. First I'll place a breakpoint next to the assignment statement in
main that creates a new Game object. Then I'll press the bug button.

The debugger stops at the breakpoint and highlights the call to the Game class function. Notice
that there are no objects in locals and that the game class function has no arguments. Since
that the Game class function is not a method, there is no expression before the call to game. No
"special" argument will be added to the argument list.

634
10.06@00:45

Fig. 10.06.02: locals is Empty

I'll press the step into button.

Now I'm in the init method of the Game class definition. Why is __init__ the active function
instead of the Game class function? There is no visible code for any class function. When a
class function is called, an instance of that class is immediately created and passed to the init
method as a special argument. Therefore, Game's init method has one parameter, "self". "self"
is bound to the new object that the Game class just created! It appears in Stack Data and is
bound to the newly created Game object.

635
10.06@01:34

Fig. 10.06.03: self in locals

Take note of the memory address of "self". Now I'll step out of the init method and return to the
main function. The identifier, game has been added to the locals namespace and is bound to
the object that was passed to init! Remember the address of the Game object.

636
10.06@01:53

Fig. 10.06.04: Game Object Added to locals

The play method call has no regular arguments and one special argument, which is the game
object.

When I press the step into button, there is already an object bound in the method's namespace!
In the method definition, play has one parameter, "self". Like init, "self" is bound to the special
argument that's added to the argument list when the method is called.

637
10.06@2:20

Fig. 10.06.05: self is Bound to the Same Game Object

You can compare the addresses to see that self is bound to the same game object from the
main function, which was created when the Game class function was called.

I'll stop this trace so I can explore the difference between normal function calls, class function
calls and method calls. It is impossible to differentiate between them, just by looking at their
syntax. For example, we only know from experience that "time.sleep" is a function and
"window.set_font_name" is a method. However, they both have identical syntax: an identifier
followed by a dot followed by an identifier followed by parentheses and arguments. Whether a
function is normal, class or method, depends on whether the identifier before the parentheses is
a normal function object, a class function object or a method object.

There are three common ways to determine the exact type of the function object an identifier is
bound to. You could call the print function on this object. You could look at the code where this
object is defined, if you have access to it. Or, you could check the documentation, which is the
usual way.

To explore this issue, I'll add a few statements to the play method. Next, I'll add an import
statement so I can access, pygame.draw.circle, using its long name that includes dots.

I'll remove the breakpoint in main, and then place a new breakpoint next to the first new
statement of play: "print(pygame.QUIT)". Then I'll press the bug button.

638
These print calls let me examine the objects that the expressions evaluate to . I'll step over the
first statement that prints "pygame.quit". In the shell, the integer value for QUIT is displayed.
The second statement prints "built-in function circle". The third statement prints "class
pygame.color" and the fourth statement prints "bound method Window get_surface of uagame
window object" at a memory location.

10.06@4:18

Fig. 10.06.06: print Statement Results in the Shell

All four of these statements use attribute reference syntax yet each evaluates to a different type
of object. "Pygame.quit" evaluates to an integer and the other three evaluate to a normal
function, class function and method function respectively. When you are reading code that
contains a function call, don't rely on a visual inspection to determine its meaning. Instead,
check the documentation to determine whether that function call is a normal function, class
function or method function.

Let's look at the documentation for pygame.Color. Since pygame is bound to a module,
pygame.Color is an attribute of this module. I'll click on the pygame Color link to view the
documentation for Color.

639
10.06@5:17

Fig. 10.06.07: pygame Documentation for Color

pygame uses the python naming convention for identifiers. Since Color starts with an uppercase
letter, it is a class instead of a module.

Color of name represents the Color class function. I know it is a class function since it is
uppercase, and the documentation says, "pygame object for color representations" and below
this it says "The Color class represents".

Let's look at the documentation for pygame.draw.circle.

640
10.06@6:07

Fig. 10.06.08: pygame Documentation for draw.circle

Since pygame is bound to a module, pygame.draw is an attribute of this module. I'll click on the
pygame draw link to view the documentation for draw. Since draw starts with a lowercase letter,
it is a module, not a class. pygame.draw.circle is a normal function since it appears directly in
the draw module, not inside a class.

Let's look at the documentation for self.window.get_surface.

641
10.06@6:39

Fig. 10.06.09: uagame Documentation for get_surface

self is bound to an object whose type is Game. The window attribute of the game is bound to a
Window object. Therefore, get surface is an attribute of a window which is defined in the
uagame module. So I'll look for get_surface in the window class of the uagame documentation.
Since get_surface is defined "inside" a class, it must be a method.

Time for Software Quality!

For this version of Poke the Dots, I'll add two new Software Quality Tests to the Classes section.

642
10.06@7:02

Fig. 10.06.10: New Software Quality Tests

To pass the software quality tests for this version, attributes defined in user-defined classes
must not be accessed outside of the class where they are defined. In addition, your new
user-defined methods must satisfy the software quality tests for user-defined functions. Make
sure that your code satisfies these two new tests and all the previous tests from earlier versions.

Congrats! You're finished Version 4. Only one more version and then you've completed Poke
the Dots!

643
MODULE 11: POKE THE DOTS VERSION 5

11.01 Solution Issues for Pokes the Dots Version 4

Welcome to the fifth and final version of Poke the Dots!

11.01@00:14

Fig. 11.01.01: Game Creation Diagram Identify Solution Issues

There's only one solution issue left from version 4: nothing happens when the dots collide!
There's not much challenge to a game where just letting it run nets you a high score, so for this
version of Poke the Dots you are going detect when the dots collide and change the game
behavior accordingly.

You've already implemented some collision detection with regards to the edges of the window.
These equations deal with one non-moving object, the window, and one moving object, a dot.
For this version you will have to investigate how to handle collision between two moving objects.

You will complete a new description, test plan and algorithm, before completing your code.

644
11.02 Observe, Play, Describe and Create the Test Plan for Poke the Dots Version 5

Let's observe the final version of Poke the Dots!

11.02@00:14

Fig. 11.02.01: Game Creation Diagram Observe Game Version

I'll start the game. Nothing seems different from the previous version. The dots are red and blue,
they move at the same speed as version 4 and they bounce when they hit the edge of the
window. The scoreboard is still white and tallies the number of seconds since the game was
started. I'll press the mouse button a few times, and the dots still teleport to random locations
around the window.

When the dots collide something new happens!

645
11.02@00:44

Fig. 11.02.02: Poke the Dots Version 5 Game Over State

The dots immediately stop, and a game over message appears in the corner of the screen.
Notice that the text has the color of the small dot and the background has the color of the big
dot. Finally, we have some challenge in the gameplay for Poke the Dots!

I'll click the close icon, which still closes the window.

When you play the game, what is the highest score you can get? Our highest scores were: 71,
44, 21, and 10.

For your next activities modify your description and functional test plan to account for dot
collision.

646
11.03 Create Algorithm for Poke the Dots Version 5

11.03@00:14

Fig. 11.03.01: Game Creation Diagram Create Algorithm

In the final Poke algorithm, you must add steps for the only remaining game feature: dot
collision. You must check for dot collision every frame. Dot collision is not the result of a player
event. It is the result of moving game objects.

Add a new attribute to the Game class to keep track of whether the game should continue after
the dots collide. Use this attribute to select the appropriate actions when drawing and updating
the game.

647
11.03@00:40

Fig. 11.03.02: Draw and Update in Algorithm

For example, when the game is over, both "draw" and "update" must treat some game objects
differently.

648
11.04 Program Poke the Dots Version 5

11.04@00:14

Fig. 11.04.01: Game Creation Diagram Write, Test, Debug Code

As you program the final version of Poke the Dots, there are a few things to keep in mind. To
determine if the game is over, you must check if the dots intersect each other. In the
User-Defined Methods lesson, you saw an example of a method for a rectangle object that
determines if two objects of that type intersect, but you will need a different computation for
intersecting dots.

All points in a filled circle are less than or equal to a fixed distance from the center. This distance
is called the radius. If two circles intersect, the distance between centers must be less than the
sum of their radii.

649
11.04@00:48

Fig. 11.04.02: Formula for the Collision Distance

You can use the distance formula to compute the distance between two points.

After you have determined that the game is over, you must draw the game over message. This
message uses the colours of the two dots, but you already know that you cannot access the
color attribute of a dot object directly. To get this information, you must write a method in the Dot
class that returns its color.

Program the fifth and final version of Poke the Dots now!

650
11.05 Reflect on Poke the Dots Version 5 and Goodbye from the PVG Team

Welcome to the final reflection for Poke the Dots!

11.05@00:14

Fig. 11.05.01: Game Creation Diagram Identify Solution Issues

I won't be performing a trace on the Poke the Dots version 5 solution, since there are no new
language features to trace. Also, there are no new Software Quality tests for this version.
Instead, since this is the last version of Poke the Dots, we only have one more task in the Game
Creation Process diagram to finish. Let's explore some of the potential solution issues that could
be solved to improve Poke the Dots.

You can still improve your game even though there aren't any further formal course
requirements.

Let's investigate some of the improvements that can be made to Poke the Dots.

Poke the Dots is not a visually interesting game. One small modification would be making an
addition to the mouseup event handler. Every time you click the mouse button, you could
randomly change the colours of the dots.

651
Poke the Dots can be kind of boring since it isn't very challenging. You could make the game
more interesting by making it more challenging. Here are three ways to increase the difficulty to
hopefully increase enjoyment.

Every time you press the mouse button, you could increase the speed of the dots. This would
force players to be very careful about when they press the mouse button, as pressing too often
will make the dots very fast.

This game is called Poke the Dots and yet you can click anywhere in the window to teleport the
dots. A more accurate and challenging rendition of the game could be created by modifying your
mouseup event handler to only teleport if the mouse cursor is inside a dot when it is clicked.

Another way to increase the difficulty of Poke the Dots is to add a new dot whenever the mouse
cursor is clicked outside of the dot.

11.05@01:54

Fig. 11.05.02: A Modified Version of Poke the Dots

This idea works especially well if you included the feature of only teleporting the dots when the
mouse is clicked inside the dot. Instead of doing nothing if the player clicks outside of the dot,
you could add a punishment that every mistimed click adds a new dot with a random colour and
size to the screen. You might want to make the window larger if you try this improvement.

Finally, I'll discuss is a way to make the game easier to replay. Currently you must close the
window and then restart the game. Alternately, you can add an additional event handler that

652
checks if the game is over and a replay key is pressed. If so, the game can be restarted by
setting continue_game to true. However, you must figure out a way to reset the score to zero
each time you do a restart.

These are only a few of the many improvements that could be made to Poke the Dots. I hope
that you try to implement some of these improvements yourself or that you create some of your
own improvements!

Congratulations! You've completed Poke the Dots!

The course is complete. We hope that you enjoyed it and that you have learned how Computer
Science can be used to apply computation to many different forms of information. Computer
science is more than just Python programming and computer games. Computer Science relies
on precise concepts and notation, such as the lexical, syntax, and semantic rules and diagrams
used in this course. Computer Science also uses many processes such as the Game Creation
Process that supports iterative software design and development. There’s much more to
programming than just writing code!

You can use the problem solving skills you have gained in this course to solve many
computational problems in the future, both in video games and other problem domains. We
know that the game creation process you used in this course will directly apply to any
computational domain. We hope you will enjoy writing Python programs. But, we also know that
you will be able to learn other programming languages quickly, by just applying the rules of

653
these new languages. We wish you all the best in your future studies, and we hope those
studies will include more Computer Science.

654

You might also like