The Cuis Book
The Cuis Book
Short Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1 Beginnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 The Message Way of Life . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3 Class, Model of Communicating Entities . . . . . . . . . . . . . . . . . . 31
4 The Collection Way of Life . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5 Control Flow Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6 Visual with Morph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
7 The Fundamentals of Morph . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
9 Code Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
10 Debug and Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . 160
11 Sharing Cuis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
A Documents Copyright . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
B Summary of Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
C The Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
D Solutions to the Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
E The Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
F The Figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
G Spacewar! Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
H Conceptual index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
ii
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1 Beginnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1 Historical Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Installing and configuring Cuis-Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Editing your preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 Fun with window placement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 Writing your first scripts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.1 Fun with numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4 Spacewar! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
8 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8.1 System Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8.2 Overall Mechanism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
8.3 Spacewar! Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.3.1 Mouse event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.3.2 Keyboard event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Preface
A language that doesn’t affect the way you think about program-
ming, is not worth knowing.
—Alan Perlis
Figure 1: Cuis
To make your journey with this book more enjoyable, the Spacewar! 1
project is its recurring theme. It is distilled into the book in code examples,
exercises and dedicated chapters. At the end of the book, you will have writ-
ten a replica of this historic video game. The final Cuis-Smalltalk package
of this game can be downloaded from the book repository2 .
1
https://en.wikipedia.org/wiki/Spacewar!
2
https://github.com/Cuis-Smalltalk/TheCuisBook/blob/master/Spacewar!.pck.
st
Preface 3
The chapters come with many examples. Some can be copied and pasted
into Cuis-Smalltalk. We encourage you to do this, and in the process modify
them to explore on your own.
The code examples in the online version can be directly copied and pasted
into Cuis-Smalltalk. This is why the assignment character “←” you see in
the developer Cuis-Smalltalk window is printed as “:=” in the online version
of the book. The same applies with the return character “↑” printed as “^”
in the online version.
In the offline PDF version, the code examples are printed with the same
assignment and return characters as seen in the Cuis-Smalltalk windows.
Copying and pasting also work as expected.
Other examples are code extracts which are not self sufficient to be ex-
ecuted as is. Their purpose is to expose specific facets of the Smalltalk
language.
A typical example without a caption looks like:
100 factorial
The same example with a caption appears with a number and title, and
can be used as a reference elsewhere in the book:
100 factorial
⇒ 9332621544394415268169923885626670049071596826438162146859
29638952175999932299156089414639761565182862536979208272237582
51185210916864000000000000000000000000
There are also a lot of exercises. Most are very easy; they intend to give
you an opportunity to apply what you have just learned.
Exercises are easily identified by the Cuis-Smalltalk mascot: Cuis, a
South American rodent!
1 Beginnings
The computer is simply an instrument whose music is ideas.
—Alan Kay
Before getting into the details of how to use the Cuis-Smalltalk language
and tools to build software, we need to understand the point of view, as-
sumptions and intentions that shape how Cuis-Smalltalk is meant to be used.
We can call this the Smalltalk philosophy of programming.
area of insight was developing with models of how humans learn3 . People
began to talk of human-computer symbiosis. Alan Kay conceived the idea
that computer software and computer programming could become a new
medium for expressing thoughts and knowledge. The ability to express ideas
in this new medium would be the new literacy. Every person should learn to
read and write in this new digital medium, and would have available their
own dynamic book, a Dynabook 45 to accomplish this.
Very readable accounts of historic developments are Alan Kay’s papers
“The Early History of Smalltalk”6 and “What is a Dynabook?”7 in which
he notes The best way to predict the future is to invent it. Realizing the
Dynabook vision required significant advances and mutually supporting de-
velopment of hardware and software. The original development took place at
the Xerox PARC research center in the 1970’s. The first interim Dynabook
hardware was the Xerox Alto, generally considered the first personal com-
puter. The software system was Smalltalk. The design of both was guided
by the objectives of making Personal Dynamic Media and the Dynabook
real. The final version of Smalltalk built at Xerox was Smalltalk-80.
Given the relatively weak capabilities of computer hardware at that time,
implementing this vision presented real challenges, and much creativity was
called for. Today, smartphones, tablets and laptops do have the hardware
capabilities a Dynabook requires. However, the same advance hasn’t hap-
pened for the medium of personal software.
In 1981 Dan Ingalls, one of the early Smalltalk inventors, wrote in his
article “Design Principles Behind Smalltalk”8 a number of principles that
still guide us today. Among these:
Personal Mastery. If a system is to serve the creative spirit, it must
be entirely comprehensible to a single individual.
Reactive Principle. Every component accessible to the user should
be able to present itself in a meaningful way for observation and
manipulation.
With the commercialization of software, the trend has been to give people
“shrink wrapped” applications which are sealed off and written by profes-
sional software developers. One may customize an application by changing
“user settings”, but there is no way to see into or change deep capabilities.
3
Early notables here are J. Piaget, J. Brunner, O. K. Moore, and S. Papert.
4
“A personal computer for children of all ages”(1972) http://www.vpri.org/pdf/
hc_pers_comp_for_children.pdf
5
“Personal Dynamic Media”(1977) http://www.vpri.org/pdf/m1977001_dynamedia.
pdf
6
http://worrydream.com/EarlyHistoryOfSmalltalk
7
http://www.vpri.org/pdf/hc_what_Is_a_dynabook.pdf
8
http://www.cs.virginia.edu/~evans/cs655/readings/smalltalk.html
Chapter 1: Beginnings 7
In keeping with the ideal of personal media literacy, we believe that ev-
eryone should have full access to the software that runs our systems. Under-
standing and exploring computer systems requires writing software in ways
that can be read and shared.
The thinking change in problem solving can shift from What can I do here
with what I am presented? to asking What tools do I need to be successful
here? and then building them to be ever more successful.
This way of thinking about software systems is important to us.
For this reason, Cuis is a kernel Smalltalk system which is still rather
close to Smalltalk-80. The Cuis-Smalltalk goal is to be a small, coherent
Smalltalk development environment that, with study, is comprehensible to
a single person.
As we experiment with and evolve Cuis, this goal is carried out by re-
moving everything possible from the base system which is not essential, and
by having a composition strategy which allows one to write or add any fea-
tures as needed. As one gains understanding of the software kernel or core,
one only has to read and learn from each additional feature at a time to
understand the whole.
Modern, open software environments are highly complex. Cuis is an
attempt to remain oriented and able to discover patterns without being lost
in a large wealth of possibilities which one does not completely grasp.
It is said that one understands the world by building it. Developing
fluency and depth in a new medium takes time and practice.
We invite you to become fluent and look forward to sharing with us what
you have built.
First, however, we must start with some of the mechanics.
As the saying goes “A journey of a thousand miles begins with a single
step”9 .
10
https://sites.google.com/view/cuis-university/descargas
Chapter 1: Beginnings 9
The resize... option offers even more freedom to place the window.
Try the following exercise:
To execute this code, select it with the mouse and over it do ...right mouse
click → Do it (d)... You may now see nothing happen! Indeed to see the
output, you need a Transcript window to be visible. The Transcript is a
place where a programmer can send information to the user as we are doing
here. Do ...World menu → Open... → Transcript... and execute the code
again.
In this three line script, observe how the lines are separated by a dot “.”.
This dot (or period) is a line separator so it is not needed in the third line
nor in a one line script. The message #newLine has no argument.
A String is the way text is represented in a programming language, it
is a collection of characters. We already met string with our first script,
it is enclosed in single quotes: 'hello world!'. We capitalize it with the
#capitalized message:
⇒ 'HELLO WORLD!'
100 factorial
⇒ 9332621544394415268169923885626670049071596826438162146859
29638952175999932299156089414639761565182862536979208272237582
51185210916864000000000000000000000000
If you execute and print with Ctrl-p the code: 10000 factorial, you
will see that it takes far more time to print one factorial result than to
compute two factorials and divide them. The result is an integer as expected,
not a scaled decimal number as many computer languages will return.
As we are discussing division, you may not get the result you expect:
15 / 4
⇒ 15/4
Chapter 1: Beginnings 13
It looks like Cuis-Smalltalk is lazy because it does not answer the decimal
number 3.75 as we were expecting. In fact Cuis-Smalltalk wants to be as
accurate as possible, and its answer is a rational number! After all, a fraction
is just a division we are too lazy – because it is troublesome – to compute,
Cuis-Smalltalk does just that!
Try out this to understand what is happening underneath:
(15 / 4) + (1 / 4)
⇒ 4
Write the code to compute the sum of the first four integer in-
verses. 4 inverted is 1/4
The integer and float numbers we have played with are Numeric Liter-
als. Literals are building blocks of the language and considered as constant
expressions. They literally are as they appear.
There are several syntax variants which denote a number:
1.4 Spacewar!
The Spacewar! game was initially developed in 1962 by Steve Russell on
a DEC PDP-1 minicomputer. It is said to be the first known video game
installed on several computers and it was very popular in the programming
community in the 1960s. It was ported and rewritten several times to dif-
ferent hardware architectures and complemented with additional features.
Computer Space, the first arcade video game cabinet was a clone of Space-
war!
Chapter 1: Beginnings 15
ers.
4. torpedoes fired by the space ships.
The key in making great and growable systems is much more to de-
sign how its modules communicate rather than what their internal
properties and behaviors should be.
—Alan Kay
The count of classes, the models for the entities – instances of the class
Class – is less than 700.
Because you are not using the base image but one used to teach
classes, you will likely see a much larger number.
2 raisedTo: 1 + 3 factorial
⇒ 128
1 + 2 * 3
Chapter 2: The Message Way of Life 19
⇒ 9
1 + (2 * 3)
⇒ 7
Transcript
show: 'Hello World!';
newLine;
show: 'I am Cuising'
aShip
velocity: 0 @ 0;
morphPosition: randomCoordinate value @ randomCoordinate value
shortcut is Ctrl-p (Print it), which executes the script and prints the result
direcly in the Workspace.
In the Example 2.4, we have requested no special result. Selecting the
text and typing Ctrl-p results in the default, which is to return the object
to which a message is sent, in this case the Transcript.
'Hello World!'.
'Accented letters: cigüe~
na, camión, déjà, deçà, for^
et, coı̈ncidence'.
'Non-latin (Greek) letters: Λoρµ ιπσθµ δoλoρ σιτ αµτ '.
The first example is an instance of String. This class is used for String
literals if all Characters are in the limited ASCII character set. The fol-
lowing examples contain non-ASCII Characters. For these, an instance of
UnicodeString is used instead. You usually don’t need to care about this:
Instances of String and UnicodeString understand the same messages, so
they are interchangeable.
Access to a character in a string is done with the keyword message #at:
and its index position in the string as the argument. Execute the following
examples with the Ctrl-p shortcut:
'Hello' at: 1 ⇒ $H
'Hello' at: 5 ⇒ $o
'Hello' indexOf: $e
⇒ 2
Observe the use of the cascade with the #yourself message. A cascade
sends the following messages to the original receiver, so #yourself returns
the updated string. Without the cascade, $a is returned as the result of the
#at:put: message.
Characters that are part of the ASCII character set can be asked for
their ASCII code. Conversely, given an ASCII code, we can ask for the
corresponding Character:
$A asciiValue
⇒ 65
Character asciiValue: 65 + 25
⇒ $Z
But Characters that are not part of ASCII don’t have an ASCII. In
general, it is better to use Unicode code points instead:
$A codePoint
⇒ 65
UnicodeCodePoint codePoint: 65 + 25
⇒ $Z
$φ codePoint
⇒ 966
UnicodeCodePoint codePoint: 966
⇒ $φ
Shuffling a string can be fun but not very useful. However, shuffling can
apply to any kind of collection, not only to a string, and it will prove to be
useful as we will see later:
Write the code to compute the sum of the squares of the inverse
of the first four integers.
15.0 / 4 ⇒ 3.75
15 / 4.0 ⇒ 3.75
15 asFloat / 4
⇒ 3.75
Chapter 2: The Message Way of Life 23
You can also ask for division with truncation to get an integer result using
the message #//:
15 // 4
⇒ 3
15 \\ 4
⇒ 3
25 odd ⇒ true
25 even ⇒ false
25 isPrime ⇒ false
23 isPrime ⇒ true
91 isDivisibleBy: 7 ⇒ true
117 isDivisibleBy: 7 ⇒ false
117 isDivisibleBy: 9 ⇒ true
With specific keyword messages you can compute the Least Common
Multiple and Greatest Common Divisor. A keyword message is composed
of one or more colons “:” to insert one or more arguments:
12 lcm: 15 ⇒ 60
12 gcd: 15 ⇒ 3
In the Spacewar! game, the central star is the source of a gravity field.
According to Newton’s law of universal gravitation, any object with a mass
– a spaceship or a torpedo in the game – is subjected to the gravitational
force. We compute it as a vector to account for both its direction and its
magnitude. The game code snippet below shows a (simplified) mixed scalar
and vector calculation done with message sending (See Figure 2.4):
At the top left are the class categories, groups of classes sharing the same
theme. A category can also be used to create a Package, which is an organ-
isational element to save code in a file system. In Figure 2.1, the selected
class category is Kernel-Numbers, a group of classes we already started us-
ing. The term Kernel- indicates it is part of fundamental categories, but
it is only a convention. See the other categories such as Kernel-Text and
Kernel-Chronology related to text and date entities.
Next to the right are the classes in the selected class category. They
are nicely presented in a parent-child class hierarchy. When a class is first
Chapter 2: The Message Way of Life 25
selected in this pane, its declaration appears in the large pane below, the
Number class declaration is:
A subclass inherits behaviors from its parent superclass, and so only needs
to describe what is different from its superclass. An instance of Number adds
methods (which define behaviors) unknown to an instance of Magnitude.
We will explore this in detail as we go forward.
To learn about the purpose of a class, it is good practice to always visit
the class comment. Often a comment also comes with code examples to learn
how to use the object; these code snippets can be selected and executed in
place as done from a Workspace. In Figure 2.1, see the comment button to
read or to edit the comment of the selected class.
To the right of the class panel is the method categories panel. A class
may have many methods, so grouping them by category helps other users
orient themselves in finding related methods. As a reference, Number has
more than 100 instance side methods implemented in itself 1 . Clicking the
arithmetic category directly gives access to related methods in the next
and last pane at the right.
1
When considering its parents, the combined method count is more than 300.
Chapter 2: The Message Way of Life 26
You can also drag and drop the package file from your operating system
over to the Squeak window. Upon dropping the file over the window Cuis-
Smalltalk will ask you what you want to do with this package. To install it
in your enviroment you can simply press Install package.
Or, you can open a Workspace, type in Feature require: 'Spacewar!'
and Ctrl-d Do it.
Now, we have created and saved the package. Whenever you start with
a fresh Cuis-Smalltalk environment, you can load the game package.
The classes we defined are empty shells with neither state nor behavior.
These will be filled in and refactored in the following chapters.
⃗j
O ⃗i
Central star
If I give you something that you can play with and extend, even
a piece of paper with a paragraph and I say it’s not written well,
rewrite it, that’s easier than giving you nothing and say make some-
thing; you know, giving a blank sheet of paper and starting to write.
So the lovely part that has proven true for professional program-
mers as well as kids is when you start with something, an object
that does something, and then you put many objects like those
together and have them interact, and then you extend and make
them behave a little differently, you can take a very incremental
approach to learning how to control a computer system.
—Adele Goldberg
We have already met fractions. Those fractions are objects called in-
stances of the class Fraction. To create an instance we wrote 5 / 4, the
mechanism is based on message sending and polymorphism. Let us look into
how this works.
The number 5 is an integer receiving the message #/, therefore looking at
the method / in the Integer class we can see how the fraction is instantiated.
See part of this method:
/ aNumber
"Refer to the comment in Number / "
| quoRem |
aNumber isInteger ifTrue:
../..
ifFalse: [↑ (Fraction numerator: self denominator: aNumber) reduced]].
../..
From this source code, we learn that in some situations, the method
returns a fraction, reduced. We can expect that in some other situation an
integer is returned, for example 6 / 2.
In the example, we observe the message #numerator:denominator: is
sent to the class Fraction, such a message refers to a class method un-
derstood only by the Fraction class. It is expected such a named method
returns an instance of a Fraction.
Try this out in a workspace:
24 / 21
⇒ 8/7
16 squared ⇒ 256
(2 / 3) squared ⇒ 4/9
Number>>squared
"Answer the receiver multiplied by itself."
↑ self * self
In an instance method source code, self refers to the object itself, here
it is the value of the number. The ↑ (also ^) symbol indicates to return the
following value self * self. One might pronounce ↑ as “return”.
Now let’s examine this same method in Fraction:
Fraction>>squared
↑ Fraction
numerator: numerator squared
denominator: denominator squared
-10 abs ⇒ 10
5.3 abs ⇒ 5.3
(-5 / 3) abs ⇒ 5/3
Number>>abs
"Answer a Number that is the absolute value (positive magnitude) of the
receiver."
self < 0
ifTrue: [↑ self negated]
ifFalse: [↑ self]
This implementation will do just fine for the Number subclasses. Nev-
ertheless, there are several classes overriding it for specialized or optimized
cases.
For example, regarding large positive integer, abs is empty. Indeed, in
the absence of explicitly returned value, the default returned value is the
instance itself, in our situation the LargePositiveInteger instance:
LargePositiveInteger>>abs
LargeNegativeInteger>>abs
↑ self negated
These two overriding methods are more efficient as they avoid unnecessary
checks and ifTrue/ifFalse branches. Polymorphism is often used to avoid
unnecessary checks and code branches.
Chapter 3: Class, Model of Communicating Entities 36
1 class ⇒ SmallInteger
(1/3) class ⇒ Fraction
(6/2) class ⇒ SmallInteger
(1/3) asFloat class ⇒ SmallFloat64
(1.0/3) class ⇒ SmallFloat64
'Hello' class ⇒ String
('Hello' at: 1) class ⇒ Character
From a Workspace, these methods are called with the message name sent
directly to the class:
Float e
⇒ 2.718281828459045
Float epsilon
⇒ 2.220446049250313e-16
Float fmax
⇒ 1.7976931348623157e308
Chapter 3: Class, Model of Communicating Entities 38
You have noticed that text typed into the Workspace is colored
and highlighted based on what you type. We will discuss this below
when we talk about the Smalltalk language, but the idea is to be helpful.
If you start to type a word the Cuis Workspace knows about, you can
press the tab key and get a set of choices for completion of the word.
Try typing Float epsi and pressing tab. You can then press enter and
should see Float epsilon. Click elsewhere on the Workspace to make
this menu go away.
3.14 pi
⇒ MessageNotUnderstood: SmallFloat64>>pi
Float pi e
⇒ MessageNotUnderstood: SmallFloat64>>e
Often these class methods are used to access constant values as seen in
the previous example or to create a new instance:
OrderedCollection new
⇒ Create a new empty ordered collection
Fraction numerator: 1 denominator: 3
⇒ 1/3 "a fraction instance"
Float new
⇒ 0.0
Float readFrom: '001.200'
⇒ 1.2
Integer primesUpTo: 20
⇒ #(2 3 5 7 11 13 17 19)
Instance methods. In Figure 3.2, the methods listed are instance side, in
the browser the instance button is pressed to see this list.
Chapter 3: Class, Model of Communicating Entities 39
In a Workspace, these methods are called with the message name sent
directly to an instance:
Float cos
⇒ MessageNotUnderstood: Float class>>cos
Fraction squared
⇒ MessageNotUnderstood: Fraction class>>squared
OrderedCollection add: 10
⇒ MessageNotUnderstood: OrderedCollection class>>add:
Of course you can mix both class and instance methods, as long as you
send the message to the appropriate class or instance:
Float pi cos
⇒ -1.0
Float e ln
⇒ 1.0
(Fraction numerator: 4 denominator: 5) squared
⇒ 16/25
OrderedCollection new add: Float pi; add: Float e; yourself
⇒ an OrderedCollection(3.141592653589793 2.718281828459045)
Here is another example from Spacewar! mixing class and instance meth-
ods. This portion of code updates the orientation of a torpedo according to
its velocity vector:
Chapter 3: Class, Model of Communicating Entities 40
With this brief introduction to the system browser, you are now equipped
to explore the system classes.
3.4 Kernel-Numbers
The top hierarchy Number class shows most of the behaviors inherited by the
subclasses as Float, Integer and Fraction. The Smalltalk way to learn
about a behavior is to point the System Browser toward a top hierarchy
class and to explore the method categories.
Let’s suppose we want to round a float number. In Number, we explore
the Truncation and round off method category to discover several behaviors.
The next thing to do is to test these messages in a Workspace to discover
the one we are searching for:
Chapter 3: Class, Model of Communicating Entities 41
1/10 to: 5/3 by: 1/2 do: [:i | Transcript show: i; space]
⇒ (1/10) (3/5) (11/10) (8/5) (1/10) (3/5) (11/10) (8/5)
1
More strictly, to be repeated an integer number of times.
Chapter 3: Class, Model of Communicating Entities 42
1 to: 10
⇒ (1 to: 10)
1 to: 10 by: 2
⇒ (1 to: 9 by: 2)
$d to: $h
⇒ #($d $e $f $g $h)
Integer numbers are represented in different bases when prefixed with the
base and “r”. The r stands for radix, the base root by which the follow-
ing number is interpreted. When executing and printing Ctrl-p on such a
number, it is immediately printed in the decimal base:
2r1111 ⇒ 15
16rF ⇒ 15
8r17 ⇒ 15
20rF ⇒ 15
10r15 ⇒ 15
"The Babylonians"
60r10 ⇒ 60
60r30 ⇒ 180
60r60 ⇒ 360
60r30 + 60r60 ⇒ 540
(60r30 + 60r60) printStringRadix: 60 ⇒ '60r90'
"The Mayans"
20r10 ⇒ 20
20r40 ⇒ 80 "pronounced 4-twenties in some languages"
20r100 ⇒ 400
2
Base 20 and 60 number representations are not exclusive to these civilisations, although
they are the most documented use cases.
Chapter 3: Class, Model of Communicating Entities 44
How would you multiply the integer 360 by 1024, without using
the multiplication operation?
In Example 3.11, the returned value should be zero but it is not the
case. The computer returns 5.55e-17, or 0.0000000000000000555, it is
very close to zero, but there is an error.
Chapter 3: Class, Model of Communicating Entities 45
This time we have the expected result. Under the covers the computer
only does the calculations with integer components so no roundoff results.
This is a fine example where solving some problems requires a paradigm
shift.
Chapter 3: Class, Model of Communicating Entities 46
(13/10) asFloat
⇒ 1.3
3.5 Kernel-Text
Notably, this category contains classes Character, String and Symbol.
String instances are collections of Character instances. All these are
limited to the small ASCII character set. The corresponding classes that
can handle the much larger Unicode character set are UnicodeCodePoint,
UnicodeString and UnicodeSymbol. As stated before, the Unicode classes
can also handle ASCII, and they are interchangeable. You usually don’t need
to care about which flavor (ASCII or Unicode) an instance actually is.
Character/UnicodeCodePoint. An individual character is written pre-
fixed with a “$”, for example: $A or $φ. It can be defined with the class side
method codePoint:. Note, however, that you can ask Character only about
valid Characters. It is generally safer to ask UnicodeCodePoint instead:
Character codePoint: 65 ⇒ $A
Character codePoint: 966 ⇒ nil
UnicodeCodePoint codePoint: 966 ⇒ $φ
There are class side methods for non printable characters: Character
tab, Character lf, etc.
Additionally, UnicodeCodePoint defines a #namedCharactersMap, that
lets you enter many Unicode characters easily, like:
\leftright then press spacebar.
Chapter 3: Class, Model of Communicating Entities 47
String. String is a very large class, it comes with more than 200 methods.
It is useful to browse these method categories to see common ways to group
methods.
Sometimes you may not see a category related to what you’re looking for
right away.
Consider the case where you need to search for a substring, a string within
a string: when browsing the String class, search for method categories
named like finding... or accessing. There you find a family of findXXX
methods. Read the comments at the beginning of these methods:
findString: subString
"Answer the index of subString within the receiver, starting at
start. If the receiver does not contain subString, answer 0."
↑ self findString: subString startingAt: 1.
Or:
Following these paths will, most of the time, lead you toward the answer
you are looking for.
its heading angle, its velocity vector, its fuel gauge, its count of the
available torpedoes, its mass and its acceleration engine boost.
• A Torpedo has position, velocity and lifeSpan states.
We need to explain the mathematical nature of these states, then discuss
their object representation in the instance variables of our classes.
SpaceWar
This object is the entry into the game. We want a meaningful class name.
Its instance variables are the involved protagonists of the game:
• centralStar is the unique CentralStar of the game play. We need to
know about it to request its mass.
• ships is a collection of the two player ships. It is an Array instance, its
size is fixed to two elements.
• torpedoes is a collection of the fired torpedoes in the game play. As
this quantity is variable, a dynamic OrderedCollection makes sense.
CentralStar
Its unique instance variable, mass, is a number, most likely an Integer.
SpaceShip
The space ship is the most complex object, some clarifications regarding its
variables are needed.
• name is a String.
• position is a 2D screen coordinate, a location. Smalltalk uses the
Point class to represent such objects. It understands many mathe-
matics operations as operations on vectors; very useful for mechanical
calculations.
A point is easily instantiated with the binary message #@ send to a num-
ber with another number as its argument: 100 @ 200 returns a Point
instance representing the coordinates (x;y) = (100;200).
The ship’s position is regularly recomputed according to the law of
the Galilean reference frame. The computation depends on the ship’s
velocity, it’s current engine boost and the gravity pull of the central
star.
Chapter 3: Class, Model of Communicating Entities 50
Torpedo
A torpedo is launched or “fired” from a ship with an initial velocity related
to the ship velocity. Once the torpedo life span counter reaches zero, it self
destructs.
• position is a 2D screen coordinate, a Point instance. Unlike the ship
it does not accelerate based on the gravity pull of the central star.
Indeed, a torpedo does not come with a mass state. For our purposes it
is essentially zero. Its position over time only depends on the torpedo
velocity and its initial acceleration.
• heading is an angle in radians, the direction where the torpedo nose is
pointing. Its value matches the ship heading when fired, it is therefore
a Float number too.
• velocity is a vector representing the instantaneous speed of the tor-
pedo. It is constant over the torpedo lifespan. Again velocity is kept as
a Point instance.
• lifeSpan is an integer number counter, when it reaches zero the torpedo
self-destructs.
To add the variables to the Torpedo class, from the Browser, select this
class. Next, add the variable names to the instanceVariableNames: key-
word, separated by one space character. Finally, save the updated class
definition with Ctrl-s shortcut:
3.6.3 Behaviors
Some of these states need to be accessed from other entities:
• When initializing a space ship, we want to set its name with a keyword
message categorised as a setter: ship name: 'The needle'.
• To compute the gravity force applied to an object owning a mass, we
want to get its value with an unary message categorised as a getter:
star mass * ship mass.
To write these behaviors in the Browser, first select the class then the
method category you want – when none, select -- all --.
In the code pane below appears a method template:
messageSelectorAndArgumentNames
"comment stating purpose of message"
| temporary variable names |
statements
SpaceShip>>mass
↑ mass
The SpaceShip>> part is not valid code and should not be written in the
Browser. It is a text convention to inform the reader the subsequent method
is from the SpaceShip class.
SpaceShip>>name: aString
name ← aString
Observe how we do not have a setter message for the space ship mass
attribute. Indeed, it does not make sense to change the mass of a ship from
another object. In fact, if we consider both player ships to be of equal mass,
we should remove the mass variable and edit the mass method to return a
literal number:
SpaceShip>>mass
↑ 1
Controls
A space ship controlled by the player understands messages to adjust its
direction and acceleration3 :
Direction. The ship’s heading is controlled with the #left and #right
messages. The former decrements the heading by 0.1 and the latter incre-
ments it by 0.1.
Write two methods named left and right to shift the ship
heading of 0.1 accordingly to the indications above.
Acceleration. When the #push message is sent to the ship, the engines
are ignited and an internal acceleration of 10 units of acceleration is applied
to the ship. When the #unpush message is sent, the acceleration stops.
3
The velocity is a consequence of the accelerations applied to the space ship.
Chapter 3: Class, Model of Communicating Entities 54
Write two methods named push and unpush to adjust the ship
inner acceleration accordingly to the indications above.
3.6.4 Initializing
When an instance is created, for example SpaceShip new, it is automatically
initialized: the message #initialize is sent to the newly created object and
its matching initialize instance side method is called.
The initializing process is useful to set the default values of the instance
variables. When we create a new space ship object we want to set its default
position, speed, acceleration:
SpaceShip>>initialize
super initialize.
velocity ← 0 @ 0.
position ← 100 @ 100.
acceleration ← 0
In the method Example 3.17, observe the first line super initialize.
When a message is sent to super, it refers to the superclass of the class’s
method using super. So far, the SpaceShip parent class is Object, therefore
the Object>>initialize method is called first for initialization.
When created, a space ship is positioned to the top and right of the
central star. It has no velocity nor internal acceleration – only the gravity
pull of the central star. Its nose points in direction of the top of the game
display.
You probably noticed there is no code to initialize the heading, something
like heading ← Float halfPi negated to orient the ship in the direction of
the top of the screen. The truth is we don’t need the heading attribute,
this information will be provided by the class Morph used later as a parent
class of SpaceShip and Torpedo. At this time, the heading variable will
be removed and we will define the heading behavior with the appropriate
heading and heading: methods.
55
Write the method to initialize the central star with 8000 units of
mass?
Since the concept’s introduction in the 70s, collections and their associ-
ated iterators are important programming elements of Smalltalk. Correctly
used, they improve both code compactness and code understanding; two
paradigms which may seem antagonistic. Since then, these innovations have
percolated into many popular programming languages.
The new window is a protocol browser for the String class. At the left,
we see a hierarchy of the String’s ancestor classes. At the right are the
method selectors for strings and, in parenthesis, the class where they are
defined. Methods defined in class String itself are in bold characters.
Selecting one class there only shows the protocol starting from this class
down to the String class. If you select String in the left panel, you
only see methods defined in the String class itself.
In Figure 4.1, no specific class is selected, therefore the whole String
protocol is listed at the right. The method before: implemented in
SequenceableCollection is selected and its source code is displayed
on the large bottom pane.
2. Explore the hierarchy. In the class pane of the browser, do ...select
String class → right mouse button → Browse hierarchy (h)... Alter-
natively, use the keyboard shortcut Ctrl-h or the button hierarchy on
the system browser.
The hierarchy browser is very like the system browser with only two
differences:
• At the far left, the class categories pane is absent,
• In the classes pane, the hierarchy of String is printed. It makes
easy to browse String parent and child classes.
The hierarchy browser is a general tool for exploration. Unlike the pro-
tocol browser, it does not display the entire protocol of a class. No inher-
ited methods are shown, only those defined directly in the selected class.
In Figure 4.2, the class SequenceableCollection is selected as well as its
method before:.
Chapter 4: The Collection Way of Life 58
Beware, some messages in the String protocol may obviously not work.
Observe below, the error is thrown on a Character instance:
If you look at implementors of cos, you can find that Collection expects
to apply cos to each member of a collection, hence a character is asked for
its cosine.
Symbol. A symbol is very like a string but it is unique and never du-
plicated. Two references to 'hello' might be to two or only one object
depending computational history. Two references to #hello are guarenteed
to always refer to the same object.
Symbols got their name because they are used as symbolic constants. You
already observed how in the book we wrote message selectors as a symbol.
We use symbols because each message name must uniquely index the code
for a method. You will use a symbol when you need to name something
uniquely.
In the example below, observe the same behavior with string:
| a |
a ← 1 / 3.
a class
⇒ Fraction
a ← a + (2 / 3)
⇒ 1
a class
⇒ SmallInteger
Chapter 4: The Collection Way of Life 60
The initial value of the variable a was a Fraction instance, after some
calculation it ends as a SmallInteger instance.
In fact to be honest, there is no such things as type, it is only referenced
objects which can mutate over time into other kind of object: a metal body
structure to which you add two wheels may become a bicycle, or a car if you
add four wheels.
Therefore, to declare a method variable we just name it at the beginning
of the script and surround it by pipe characters “|”.
A variable always holds a value. Until we place a different value into a
variable, the variable holds the nil value, an instance of UndefinedObject.
When we say that a value is bound to a variable we mean that the named
box now holds that value.
So far we sent messages directly to objects, but we can send message to
a variable bound to an object too.
Any object responds to the message #printString.
| msg |
msg ← 'hello world!'.
Transcript show: msg capitalized printString, ' is a kind of '.
Transcript show: msg class printString; newLine.
msg ← 5.
Transcript show: msg printString, ' is a kind of '.
Transcript show: msg class printString; newLine.
"array of numbers"
#(1 3 5 7 11 1.1)
Chapter 4: The Collection Way of Life 61
#(1 2/3)
⇒ #(1 2 #/ 3)
{1 . 2/3 . 7.5}
⇒ #(1 2/3 7.5)
With an array filled with numbers you can request information and arith-
metic operations:
#(1 2 3 4) size ⇒ 4
#(1 2 3 4) + 10 ⇒ #(11 12 13 14)
#(1 2 3 4) / 10 ⇒ #(1/10 1/5 3/10 2/5)
⇒ #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
92 93 94 95 96 97 98 99 100)
In this line of code, the message #to: is sent to 1 with the argument 100.
It returns an interval object. The message #asArray sent to the interval
returns an array.
| fibo |
fibo ← OrderedCollection newFrom: #(1 1 2 3).
fibo add: 5;
add: 8;
add: 13;
add: 21.
fibo
⇒ an OrderedCollection(1 1 2 3 5 8 13 21)
fibo at: 1 ⇒ 1
fibo at: 6 ⇒ 5
fibo last ⇒ 21
fibo indexOf: 2 ⇒ 3
fibo at: fibo size ⇒ 21
Set operations between two collections are computed with the #union:,
#intersection: and #difference: messages.
Set operations work with any kind of object. Comparing objects deserves
its own section.
To select the prime numbers from 1 to 100, we use the #select: enumer-
ator. This message is sent to a collection, then it will select each element of
the collection returning true to a test condition:
• in the select: method, for each integer of the interval, the block of
code is invoked with its parameter n set to the integer value. A block
parameter starts with a colon, “:”, and is an ordinary identifer1 . Then,
each time n isPrime evaluates to true, the n value is added to a new
collection answered when the select: method finished testing each el-
ement of the collection.
A block of code can be saved in a variable, passed as a parameter, and
can be used multiple times.
| add2 |
add2 ← [:n| n + 2].
{ add2 value: 2. add2 value: 7 }.
⇒ #(4 9)
You want to know the number of prime numbers under 100. Just send the
message #size to the answered collection at Example 4.3. The parenthesis
are mandatory to ensure #size is sent last to the resulting collection:
| primeNumbers |
1
An identifier is just a word that starts in a lower case letter and consists of upper and
lower case letters and decimal digits. All variable names are identifiers
Chapter 4: The Collection Way of Life 65
We can shift the ascii value, convert back to character then collect in a
new string. It is a simple cipher:
The Caesar’s cipher is based on shifting letter to the right in the alphabet
order. The method is named after Julius Caesar, who used it in his private
correspondence with a shift of 3.
Once you get the alphabet cipher right, you can encode your first message:
| sequence |
sequence ← OrderedCollection new.
1 to: 10 do: [:k | sequence add: 1 / k].
sequence
⇒ an OrderedCollection(1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10)
| fibo |
fibo ← OrderedCollection newFrom: #(1 1).
10 timesRepeat: [
fibo add: (fibo last + fibo atLast: 2)].
fibo
⇒ an OrderedCollection(1 1 2 3 5 8 13 21 34 55 89 144)
Collection>>do: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument."
self subclassResponsibility
OrderedCollection>>do: aBlock
firstIndex to: lastIndex do: [ :index |
aBlock value: (array at: index) ]
and:
Dictionary>>do: aBlock
super do: [:assoc | aBlock value: assoc value]
of elements it can hold – is set when creating the instance. Once instantiated,
you can neither add nor delete elements to an array.
There are different ways to create Array instance:
Array array1 and array1b are bit different. The former one is created
and filled with its contents at compile time of the code, the consequence is
it can only be filled with literal elements as integer, float, string. The later
one is created at execution time of the code, it can be filled with elements
instantiated at the execution time as Fraction or Point instances.
You can access elements with an important variety of messages:
array1 first ⇒ 2
array1 second ⇒ 'Apple'
array1 third ⇒ $@
array1 fourth ⇒ 4
array1 last ⇒ 4
array1 at: 2 ⇒ 'Apple'
array2 at: 3 ⇒ 2@3
array2 swap: 2 with: 4 ⇒ #(2 1/3 2@3 'Apple')
array1 at: 2 put: 'Orange'; yourself ⇒ #(2 'Orange' $@ 4)
array1 indexOf: 'Orange ⇒ 2
2
Of course you can insert an element between two elements. However LinkList instance
are more efficient for this use case.
Chapter 4: The Collection Way of Life 71
To uniquely collect the divisors list of 30 and 45 (not the common divi-
sors):
Set new
addAll: #(1 2 3 5 6 10 15 30) ;
addAll: #(1 3 5 9 15 45) ;
yourself.
⇒ a Set(5 10 15 1 6 30 45 2 3 9)
How will you collect the letters in the sentences ’buenos dı́as’
and ’bonjour’ ?
| colors |
colors ← Dictionary new.
colors
add: #red -> Color red;
add: #blue -> Color blue;
add: #green -> Color green
colors keys.
⇒ #(#red #green #blue)
colors keyAtValue: Color green
⇒ #green
There are many more collections to explore. You now know enough to ex-
plore and to search by yourself with the System Browser, and to experiment
with the Workspace.
SpaceWar>>initializeActors
centralStar ← CentralStar new.
../..
ships first
position: 200 @ -200;
color: Color green.
ships second
position: -200 @ 200;
color: Color red
The example above does not show the creation of the ships and
torpedoes collections. Replace “../..” with lines of code where these
collections are instantiated and if necessary populated.
Torpedo>>update: t
"Update the torpedo position"
position ← velocity * t + position.
../..
A space ship is put under the strain of the star’s gravity pull and the
acceleration of its engines. Therefore its velocity and position change ac-
cordingly to the mechanical laws of physics.
SpaceShip>>update: t
"Update the ship position and velocity"
| ai ag newVelocity |
"acceleration vectors"
ai ← acceleration * self direction.
ag ← self gravity.
newVelocity ← (ai + ag) * t + velocity.
position ← (0.5 * (ai + ag) * t squared) + (velocity * t) + position.
velocity ← newVelocity.
../..
Observe in this previous method how the direction and the gravity are
defined in two specific methods.
The #direction message asks for the unit vector representing the nose
direction of the spaceship:
Chapter 4: The Collection Way of Life 76
SpaceShip>>direction
"I am an unit vector representing the nose direction of the mobile"
↑ Point rho: 1 theta: self heading
The #gravity message asks for the gravity vector the space ship is sub-
jected to:
SpaceShip>>gravity
"Compute the gravity acceleration vector"
| position |
position ← self morphPosition.
↑ [-10 * self mass * owner starMass / (position r raisedTo: 3) * position]
on: Error do: [0 @ 0]
SpaceWar>>stepTime
"millisecond"
↑ 20
SpaceWar>>stepAt: millisecondSinceLast
../..
ships do: [:each | each unpush].
../..
In the stepAt: method, we intentionally left out the details to update the
ship and torpedoe positions. Note: each ship is sent regularly an #unpush
message to reset its previous #push acceleration.
77
Replace the two lines “../..” with code to update the ships and
the torpedoes positions and velocities.
Among other things, the game play handles the collisions between the
various protagonists. Enumerators are very handy for this.
Ships are hold in array of size 2, we just iterate it with a #do: message
and a dedicated block of code:
SpaceWar>>collisionsShipsStar
ships do: [:aShip |
(aShip morphPosition dist: centralStar morphPosition) < 20 ifTrue: [
aShip flashWith: Color red.
self teleport: aShip]
]
5.2 Pseudo-variables
In Smalltalk, there are 6 reserved keywords, or pseudo-variables:
nil, true, false, self, super, and thisContext.
They are called pseudo-variables because they are predefined and cannot
be assigned to. true, false, and nil are constants while the values of self,
super, and thisContext vary dynamically as code is executed.
• true and false are the unique instances of the Boolean classes True
and False.
• self always refers to the receiver of the currently executing method.
Chapter 5: Control Flow Messaging 79
• super also refers to the receiver of the current method, but when you
send a message to super, the method-lookup changes so that it starts
from the superclass of the class containing the method that uses super.
• nil is the undefined object. It is the unique instance of the class
UndefinedObject. Instance variables, class variables and local vari-
ables are initialized to nil.
• thisContext is a pseudo-variable that represents the top frame of the
run-time stack. In other words, it represents the currently executing
MethodContext or BlockContext. thisContext is normally not of in-
terest to most programmers, but it is essential for implementing develop-
ment tools like the Debugger and it is also used to implement exception
handling and continuations.
SpaceWar>>keyStroke: event
"Check for any keyboard stroke, and take action accordingly"
| key |
key ← event keyCharacter.
event isArrowUp ifTrue: [↑ ships first push].
event isArrowRight ifTrue: [↑ ships first right].
event isArrowLeft ifTrue: [↑ ships first left].
event isArrowDown ifTrue: [↑ ships first fireTorpedo].
key = $w ifTrue: [↑ ships second push].
...
[ 1 + 2 ] value
⇒ 3
[ :x | 1 + x ] value: 2
⇒ 3
[ :x :y | x + y ] value: 1 value: 2
⇒ 3
If you have a block with more than four parameters, you must use
#valueWithArguments: and pass the arguments in an array. (A block with
a large number of parameters is often a sign of a design problem.)
Chapter 5: Control Flow Messaging 81
Blocks may also declare local variables, which are surrounded by vertical
bars, just like local variable declarations in a method. Locals are declared
after any arguments:
[ :x :y | | z | z ← x + y. z ] value: 1 value: 2
⇒ 3
|x|
x ← 1.
[ :y | x + y ] value: 2
⇒ 3
Blocks are instances of the class BlockClosure. This means that they
are objects, so they can be assigned to variables and passed as arguments
just like any other object.
Consider the example below to compute the divisors of an integer:
| n m |
n ← 60.
m ← 45.
(1 to: n) select: [:d | n \\ d = 0 ].
"⇒ #(1 2 3 4 5 6 10 12 15 20 30 60)"
(1 to: m) select: [:d | m \\ d = 0]
"⇒ #(1 3 5 9 15 45)"
The problem with this example is the code duplication in the divisor
computation. We can avoid duplication with a dedicated block doing the
computation and assigning it to a variable:
SpaceWar>>teleport: aShip
"Teleport a ship at a random location"
| area randomCoordinate |
aShip resupply.
area ← self morphLocalBounds insetBy: 20.
randomCoordinate ← [(area left to: area right) atRandom].
aShip
velocity: 0 @ 0;
morphPosition: randomCoordinate value @ randomCoordinate value
Test
Conditionals are expressed by sending one of the messages #ifTrue:,
#ifFalse: or #ifTrue:ifFalse: to the result of a boolean expression:
The class Boolean offers a fascinating insight into how much of the
Smalltalk language has been pushed into the class library. Boolean is the
abstract superclass of the Singleton classes True and False1 .
Most of the behaviour of Boolean instances can be understood by consid-
ering the method ifTrue:ifFalse:, which takes two blocks as arguments:
1
A singleton class is designed to have only one instance. Each of True and False classes
has one instance, the values true and false.
Chapter 5: Control Flow Messaging 83
True>>not
"Negation----answer false since the receiver is true."
↑ false
In the Example 5.1 at the beginning of this chapter, there are 4 control
flow #ifTrue: messages. Each argument is a block of code and when eval-
uated, it explicitly returns an expression, therefore interrupting the method
execution.
In the code fragment of Example 5.6 below, we test if a ship is lost in
deep space. It depends on two conditions:
1. the ship is out of the game play area, tested with the #isInOuterSpace
message,
2. the ship takes the direction of deep space, tested with the
#isGoingOuterSpace message.
Loop
Loops are typically expressed by sending messages to blocks, integers or
collections. Since the exit condition for a loop may be repeatedly evaluated,
it should be a block rather than a boolean value. Here is an example of a
very procedural loop:
n ← 1.
[ n < 1000 ] whileTrue: [ n ← n * 2 ].
n ⇒ 1024
n ← 1.
[ n > 1000 ] whileFalse: [ n ← n * 2 ].
n ⇒ 1024
You can check all the alternatives in the controlling method category
of the class BlockClosure.
#timesRepeat: offers a simple way to implement a fixed iteration:
n ← 1.
10 timesRepeat: [ n ← n * 2 ].
n ⇒ 1024
We can also send the message #to:do: to a number which then acts as
the initial value of a loop counter. The two arguments are the upper bound,
and a block that takes the current value of the loop counter as its argument:
You can check all the alternatives in the intervals method category of
the class Number.
so you can skip this step as long as you have not clicked on the class
button.
4. In the Method Category pane, select the category -- all --. A method
source code template shows up in the pane below:
messageSelectorAndArgumentNames
"comment stating purpose of message"
| temporary variable names |
statements
The template comes in four lines: the method name, a comment, local
variable declaration and statements. You can select all and delete it or
edit each line of the template as needed.
In our case, we select it all and replace it with the
SpaceWar>>initialize source code:
SpaceWar>>initialize
"We want to capture keyboard and mouse events,
start the game loop(step) and initialize the actors."
super initialize.
color ← self defaultColor.
self setProperty: #'handlesKeyboard' toValue: true.
self setProperty: #'handlesMouseOver:' toValue: true.
self startSteppingStepTime: self stepTime.
self initializeActors
Example 5.7: Initialize SpaceWar
SpaceShip>>push
"Init an acceleration boost"
fuel isZero ifTrue: [↑ self].
fuel ← fuel - 1.
acceleration ← 50
SpaceShip>>unpush
"Stop the acceleration boost"
acceleration ← 0
SpaceShip>>right
"Rotate the ship to its right"
self heading: self heading + 0.1
SpaceShip>>left
"Rotate the ship to its left"
self heading: self heading - 0.1
Observe the right and left methods, they are mostly identical to the
ones asked in Exercise 3.12. We don’t modify directly the heading attribute,
we use the methods heading: and heading to read and write this informa-
tion.
SpaceShip>>fireTorpedo
"Fire a torpedo in the direction of
the ship heading with its velocity"
| torpedo |
torpedoes isZero ifTrue: [ ↑ self].
torpedoes ← torpedoes - 1.
torpedo ← Torpedo new.
torpedo
position: position + self nose;
heading: self heading;
velocity: velocity;
color: self color muchLighter.
owner addTorpedo: torpedo
5.6.3 Collisions
In a previous chapter we gave a small taste of the collision detection code
between the space ships and the central star. It relies on iterator, block of
code and control flow.
89
How will you write the method to detect the collision between the
two ships and take action accordingly? (Adapt from Example 4.23).
The detection between the two ships and the possible numerous torpedoes
required two enumerators with nested blocks of code:
SpaceWar>>collisionsShipsTorpedoes
ships do: [:aShip |
torpedoes do: [:aTorpedo |
(aShip morphPosition dist: aTorpedo morphPosition) < 15 ifTrue: [
aShip flashWith: Color red.
aTorpedo flashWith: Color orange.
self destroyTorpedo: aTorpedo.
self teleport: aShip]
]
]
What would we expect if we asked for good support for building GUIs in
a programming system?
All modern computers (and phones, etc) have high resolution color dis-
plays. Any software running on them, that is accessible to a user, needs to
show stuff on that Display.
Conventional UI managers (that is, Operating Systems and Web
Browsers) started by including only the most basic GUI elements first:
basic text editors, buttons, simple lists, scrolling for large content, and
(usually) multiple resizeable overlapping windows. Anything else needs to
be handled via additional libraries. While there are libraries for handling
richer content (D3.js and Matplotlib are examples), the result is not
consistent, neither for developers nor for users.
Cuis-Smalltalk takes a different approach, pioneered by Smalltalk-80 and
especially Self. We will get into detail in the next chapter, The Fundamentals
of Morph. For now, let’s deal with Morphs directly.
We take the high quality Display for granted, as well as a mouse, finger
or other pointing device. And we build on the objective of providing ample
possibilities for GUIs both in existing, and in novel styles and designs yet to
be invented. Additionally, in the usual Smalltalk way, all the framework code
is available for study and modification. There are no third party libraries.
Only the lowest level code is precompiled, but that still can be overriden or
changed.
Therefore every object you see in Cuis-Smalltalk is a Morph or is composed
of Morphs. Basically, a Morph is an object with state and behavior that can
also depict itself on a computer display screen.
Because Morphs are useful, when you look at class Morph in a Hierarchy
Browser you will see a large number of methods and many, many subclasses.
But the basic ideas are quite simple.
Chapter 6: Visual with Morph 91
Now that you know this, move the yellow lower right handle with tool tip
Change size via Click-Drag. Just hold down the left mouse button while
the cursor is over the handle, move the cursor to the right and down, and
release the mouse button.
Chapter 6: Visual with Morph 93
6.3 Submorph
Morphs can contain other morphs. These interior morphs are called sub-
morphs of their containing morph. Again, you can do this by writing the
software “code”, but let’s do it directly with a BoxedMorph.
First we obtain a BoxedMorph from the New morph... submenus. The
BoxedMorph instance displays itself as a rect with a border.
Now drag the rect over the ellipse and Middle-Click on the rect and
click on the blue construction handle to gets the rect’s Morph Menu. Use
the menu selection embed into and select the ellipse as its new parent.
Chapter 6: Visual with Morph 94
Now when you click-drag the ellipse, or use the Pick up or Move con-
struction handles, the rect is just a decoration for the ellipse.
Indeed, the rect seems to have fused into the ellipse. Using the mouse
where the rect shows itself is just using the mouse on the ellipse. This rect
does not have many interesting behaviors.
Let’s add a behavior to just this one BoxedMorph.
There is an orange handle on the right, just under the green Duplicate
handle. Left-Click this to get the Debug menu. Use this menu to get an
Inspector for the rect.
Observe Figure 6.8, on the left we have a pane for self, all inst vars, and
the individual instance variables. Clicking to select “all inst vars” and the
values pane on the right shows that the owner of the rect is the ellipse and
rect currently has no submorphs.
The lower pane is a Smalltalk code editor, basically a workspace, where
self is bound to the object we are inspecting.
Inspectors work for every object by the way, not just morphs.
Chapter 6: Visual with Morph 96
Example 6.1: Edit the behavior of this morph from its Inspector
These properties are special to the user interface. You can find methods
with these names in the Morph class to see what they do.
After selecting the text and Do-it, each time you Left-Click on the
rect it changes color!
Chapter 6: Visual with Morph 97
Note that you can no longer move the ellipse by mouse-down on the rect,
because the rect now takes the mouse event. You have to mouse-down on
the ellipse. More on this below.
One quick note on Move versus Pick up. Move moves a submorph “within”
its parent. Pick up grabs a morph “out” of its parent.
Observe Figure 6.12 and Example 6.2. In the lower pane of the inspector,
code can be executed in the context of the inspected object. self refers to
the instance. Here the pane contains code to set the borderWidth and the
borderColor.
Example 6.2: Edit the state of this ellipse from its Inspector
Chapter 6: Visual with Morph 99
Figure 6.12: Use Inspector to set border color and border width
In the typical case one wants to refine or change behaviors for all instances
of a class.
create new ones. Select “event handling testing”. Then add the method
ColorClickEllipse>>handlesMouseDown:.
handlesMouseDown: aMouseButtonEvent
"Answer that I do handle mouseDown events"
↑ true
Likewise, add a new catagory “event handing” and add the other method
we need.
Now, you have created a new Morph class and can select a
ColorClickEllipse from the World Menu New Morph.. and try it out.
These are fun to Left-Click on. Make as many as you want!
Chapter 6: Visual with Morph 101
• the ships are rotating quadrangles each one painted with a different
color,
• a torpedo is a rotating triangle to paint with a different color depending
on the firing ship.
Therefore it makes sense to turn these actors into kinds of Morphs, the
visual entity of Cuis-Smalltalk. To do so, point a System Browser to the class
definition of each actor, replace the parent class Object by PlacedMorph1 ,
then save the class definition with Ctrl-s.
For example, the torpedo class as seen in Example 3.14 is edited as:
Moreover, a placed Morph already knows about its position and orien-
tation on screen – it can be dragged and moved in the screen and rotated
with the mouse cursor. Therefore the position and heading instance vari-
ables are redundant and should be removed. For now we keep it, it will be
removed later when we will know how to replace each of its use cases with
its appropriate Morph counterpart.
1
A PlacedMorph is a kind of Morph with a supplementary location attribute; so it can
be instructed to move, to scale and to rotate in the screen.
Chapter 6: Visual with Morph 103
SpaceWar>>initializeActors
centralStar ← CentralStar new.
self addMorph: centralStar.
centralStar morphPosition: 0 @ 0.
torpedoes ← OrderedCollection new.
ships ← Array with: SpaceShip new with: SpaceShip new.
self addAllMorphs: ships.
ships first
morphPosition: 200 @ -200;
color: Color green.
ships second
morphPosition: -200 @ 200;
color: Color red
pull2 and its engine acceleration; an agile space ship pilot could use gravity
assist to accelerate a torpedo fired with a path close to the central star.
What are the impacts of these considerations on the torpedo and space
ship entities?
1. They will share common states as the mass, the position, the heading,
the velocity and the acceleration.
2. They will share common behaviors as the necessary computations to
update the position, direction, gravity pull and velocity.
3. They will have different states: a torpedo has a life span state while a
space ship has fuel tank capacity and torpedoes stock states.
4. They will have different behaviors: a torpedo self destructs when its life
span expires, a space ship fires torpedoes and accelerates as long as its
fuel tank and its torpedoes count are not zero.
Shared state and behaviors suggest a common class. Unshared states
and behaviors suggests specialized subclasses which embody the differences.
So let us “factor out” the shared elements of the SpaceShip and Torpedo
classes into a common ancestor class; one more specialized than the Morph
class they currently share.
Doing such analysis on the computer model of the game is part of the
refactoring effort to avoid behavior and state duplications while making more
obvious the common logic in the entities. The general idea of code refactoring
is to rework existing code to make it more elegant, understandable and
logical.
To do so, we will introduce a Mobile class, a kind of PlacedMorph with
behaviors specific to a mobile object subjected to accelerations. Its states are
the mass, position, heading, velocity and acceleration. Well, as we are dis-
cussing refactoring, the mass state does not really makes sense in our game,
indeed our mobile’s mass is constant. We just need a method returning a
literal number and we can then remove the mass instance variable. More-
over, as explained previously, a PlacedMorph instance already know about
its position and heading, so we also remove these two attributes, although
there are common behaviors to a Space ship and a torpedo.
It results in this Mobile definition:
The first behaviors we add to our Mobile are its initialization and its
mass:
Mobile>>initialize
super initialize.
velocity ← 0 @ 0.
acceleration ← 0
Mobile>>mass
↑ 1
The next methods to add are the ones relative to the physical calculations.
First, the code to calculate the gravity acceleration:
Mobile>>gravity
"Compute the gravity acceleration vector"
| position |
position ← self morphPosition.
↑ -10 * self mass * owner starMass / (position r raisedTo: 3) * position
SpaceWar>>starMass
↑ centralStar mass
Mobile>>update: t
"Update the mobile position and velocity"
| ai ag newVelocity |
"acceleration vectors"
ai ← acceleration * self direction.
ag ← self gravity.
newVelocity ← (ai + ag) * t + velocity.
self morphPosition:
(0.5 * (ai + ag) * t squared)
+ (velocity * t)
+ self morphPosition.
velocity ← newVelocity.
"Are we out of screen? If so we move the mobile to the other corner
and slow it down by a factor of 2"
(self isInOuterSpace and: [self isGoingOuterSpace]) ifTrue: [
velocity ← velocity / 2.
self morphPosition: self morphPosition negated]
Now we should add the two methods to detect when a mobile is heading
off into deep space.
But first we define the method morphLocalBounds in each of our Morph
objects. It returns a Rectangle instance defined in the Morph coordinates
by its origin and extent:
SpaceWar>>morphLocalBounds
↑ -300 @ -300 extent: 600 @ 600
CentralStar>>morphLocalBounds
↑ Rectangle center: 0 @ 0 extent: self morphExtent
Mobile>>morphLocalBounds
↑ Rectangle encompassing: self class vertices
Mobile>>isInOuterSpace
"Is the mobile located in the outer space? (outside of the game
play area)"
↑ (owner morphLocalBounds containsPoint: self morphPosition) not
Mobile>>isGoingOuterSpace
"is the mobile going crazy in the direction of the outer space?"
↑ (self morphPosition dotProduct: velocity) > 0
As you see, these test methods are simple and short. When writing Cuis-
Smalltalk code, this is something we appreciate a lot and we do not hesitate
to cut a long method in several small methods. It improves readability
and code reuse. The #containsPoint: message asks the receiver rectangle
whether the point in argument is inside its shape.
When a mobile is updated, its position and velocity are updated. However
the Mobile subclasses SpaceShip or Torpedo may need additional specific
updates. In object oriented programming there is this special mechanism
named overriding to achieve this.
See the Torpedo>>update: definition:
Torpedo>>update: t
"Update the torpedo position"
super update: t.
"orientate the torpedo in its velocity direction, nicer effect
while inaccurate"
self heading: (velocity y arcTan: velocity x).
lifeSpan ← lifeSpan - 1.
lifeSpan isZero ifTrue: [owner destroyTorpedo: self].
acceleration > 0 ifTrue: [acceleration ← acceleration - 500]
The life span control, the self-destruction sequence, and the engine accel-
eration are also handled specifically. When a torpedo is just fired, its engine
acceleration is huge then it decreases quickly.
With the System Browser pointed to the Torpedo>>update: method, ob-
serve the inheritance button. It is light green, which indicates the message
is sent to super too. This is a reminder the method supplies a specialized
behavior. The button tool tip explains the color hilight meanings within
the method’s text. When pressing the inheritance button, you browse all
implementations of the update: method within this inheritance chain.
Mobile>>initialize
super initialize.
color ← Color gray.
velocity ← 0 @ 0.
acceleration ← 0
SpaceShip>>initialize
super initialize.
self resupply
Torpedo>>initialize
super initialize.
lifeSpan ← 500.
acceleration ← 3000
Observe how each class is only responsible of its specific state initializa-
tion:
1. SpaceShip. Its mechanical states are set with the super initialize
and then the ship is resupplied with fuel and torpedoes:
SpaceShip>>resupply
fuel ← 500.
torpedoes ← 10
What would we expect if we asked for good support for building GUIs in
a programming system?
In Chapter 6 [Visual with Morph], page 90, we started with that same
question, and gave an overview of Morphs and their interactive behavior.
This chapter deals with how Morphs are built, how to create new Morphs
and what rules they follow.
The User Interface framework in Cuis-Smalltalk is called Morphic. Mor-
phic was originally created by Randy Smith and John Maloney as the UI for
Self. Later, John Maloney ported it to Smalltalk, to be used as the UI for
Squeak.
LineExampleMorph>>drawOn: aCanvas
aCanvas strokeWidth: 20 color: Color green do: [
aCanvas
moveTo: 100 @ 100;
lineTo: 400 @ 200 ].
If you get a prompter asking whether to install and activate Vector Graph-
ics support, please answer yes. There it is. You have already built your first
Morph class.
How will you modify our line morph so it draws itself as a cross
with an extent of 200 pixels?
You will get a line you can grab with the mouse and move it around.
PlacedMorph adds a new instance variable called location. If a morph
has a location, it can be moved around, by modifying it. The location
also defines a new local coordinate system. All the coordinates used in the
drawOn: method are now relative to this new coordinate system. That’s why
we don’t need to modify the drawOn: method. drawOn: now tells how the
morph should be drawn, but not where. The location also specifies a possi-
ble rotation and scale factor. This means that subinstances of PlacedMorph
can also be rotated and zoomed.
Chapter 7: The Fundamentals of Morph 113
TriangleExampleMorph>>initialize
super initialize.
borderColor ← Color random alpha: 0.8.
fillColor ← Color random alpha: 0.6.
TriangleExampleMorph>>drawOn: aCanvas
aCanvas strokeWidth: 10 color: borderColor fillColor: fillColor do: [
aCanvas
moveTo: 0 @ 100;
lineTo: 87 @ -50;
lineTo: -87 @ -50;
lineTo: 0 @ 100 ].
Take a moment to understand that code, to guess what it will do. Now
execute:
Do it several times, and move each triangle around. Each new triangle
you create has different colors. And these colors are not completely opaque.
This means that when you place your triangle over some other morph, you
can see through it.
Chapter 7: The Fundamentals of Morph 114
Figure 7.2: A variety of triangle morphs, one decorated with its halo and
coordinates system
How will you write a movable rectangle morph with an x,y extent
of 200 by 100? The rect will be filled with a random translucent color
and surrounded by a thin blue line.
The Debug handle opens a menu from where you can open an Inspector or
a Hierarchy Browser to study the morph.
You also have a Rotate and Change scale handles. Try them! To use
them, move your hand to the handle, and then press the mouse button and
drag it. As you might have guessed, the rotate handles spins your morph
around the center of its surrounding rectangle. The scale handles controls
the apparent zoom applied to your morph. Both scale and rotation (and
also displacement, as when you move your morph around) are implemented
by modifying the inner coordinate system defined by your morph. Displace-
ment, rotation and scale are floating point numbers, and thus not limited to
integers.
To change the center of rotation of a Morph, you override the method
rotationCenter accordingly:
RectangleExampleMorph>>rotationCenter
↑0 @ 0
Observe how our rectangle morph now reacts to the rotation handle. We
will learn how to control all this with code and animate our morph.
TriangleExampleMorph>>wantsSteps
↑ true
...and:
TriangleExampleMorph>>step
fillColor ← Color random.
self redrawNeeded
TriangleExampleMorph>>stepTime
↑ 100
...and:
Chapter 7: The Fundamentals of Morph 116
TriangleExampleMorph>>step
self morphPosition: self morphPosition + (0.4@0).
self redrawNeeded
Now, our morph steps ten times per second, and moves to the right at a
speed of four pixels per second. At each step it moves by 0.4 pixels, and not
by an integer number of pixels. High quality anti-aliasing drawing means
we can actually do that! You can make it step at a speed of four times a
second, and move 1 pixel each time, and see how different that looks.
Now try this:
TriangleExampleMorph>>step
self morphPosition: self morphPosition + (0.2@0).
self rotateBy: 4 degreesToRadians.
self redrawNeeded
TriangleExampleMorph>>initialize
super initialize.
borderColor ← Color random alpha: 0.8.
fillColor ← Color random alpha: 0.6.
scaleBy ← 1.1
TriangleExampleMorph>>step
self morphPosition: self morphPosition + (0.2@0).
self rotateBy: 4 degreesToRadians.
self scaleBy: scaleBy.
self scale > 1.2 ifTrue: [scaleBy ← 0.9].
self scale < 0.2 ifTrue: [scaleBy ← 1.1].
self redrawNeeded
See that when the triangle is doing its crazy dance, you can still open a
halo and interact with it.
Chapter 7: The Fundamentals of Morph 117
LineExampleMorph>>clipsSubmorphs
↑ true
The drawing of the triangle gets cut exactly at the bounds of the line.
This is most useful for implementing scrolling panes that only make a part
of their contents visible, but might have other uses too.
1
By now, it is likely that the triangle has walked quite a bit!
Chapter 7: The Fundamentals of Morph 118
ClockMorph>>drawOn: aCanvas
aCanvas
ellipseCenter: 0@0
radius: 100
borderWidth: 10
borderColor: Color lightCyan
fillColor: Color veryVeryLightGray.
aCanvas drawString: 'XII' at: -13 @ -90 font: nil color: Color brown.
aCanvas drawString: 'III' at: 66 @ -10 font: nil color: Color brown.
aCanvas drawString: 'VI' at: -11 @ 70 font: nil color: Color brown.
aCanvas drawString: 'IX' at: -90 @ -10 font: nil color: Color brown
ClockHourHandMorph>>drawOn: aCanvas
aCanvas fillColor: (Color black alpha: 0.6) do: [
aCanvas
moveTo: 0 @ 10;
lineTo: -5 @ 0;
lineTo: 0 @ -50;
lineTo: 5 @ 0;
lineTo: 0 @ 10 ].
You can start playing with them. We could use several instances of a
single ClockHandMorph, or create several classes. Here we chose to do the
latter. Note that all the drawOn: methods use hardcoded constants for all
coordinates. As we have seen before, this is not a limitation. We don’t
need to write a lot of specialized trigonometric and scaling formulas to build
Morphs in Cuis-Smalltalk!
By now, you might imagine what we are doing with all this, but please
bear with us while we finish building our clock.
We create ClockMinuteHandMorph, the hand for the minutes:
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Learning'
ClockMinuteHandMorph>>drawOn: aCanvas
aCanvas fillColor: ((Color black) alpha: 0.6) do: [
aCanvas
moveTo: 0 @ 8;
lineTo: -4 @ 0;
lineTo: 0 @ -82;
lineTo: 4 @ 0;
lineTo: 0 @ 8 ]
ClockSecondHandMorph>>drawOn: aCanvas
aCanvas strokeWidth: 2.5 color: Color red do: [
aCanvas
moveTo: 0 @ 0;
lineTo: 0 @ -85 ]
Now, all that is needed is to put our clock parts together in ClockMorph.
In its method category initialization add its initialize method (accept
the new names as instance variables):
ClockMorph>>initialize
super initialize.
self addMorph: (hourHand ← ClockHourHandMorph new).
self addMorph: (minuteHand ← ClockMinuteHandMorph new).
self addMorph: (secondHand ← ClockSecondHandMorph new)
Chapter 7: The Fundamentals of Morph 121
If you have not already added instance variables for the clock
hands, the Cuis IDE will note this and ask what you want to do about
it. We want to declare the three missing names as instance variables.
ClockMorph>>wantsSteps
↑ true
...and:
ClockMorph>>step
| time |
time ← Time now.
hourHand rotationDegrees: time hour * 30.
minuteHand rotationDegrees: time minute * 6.
secondHand rotationDegrees: time second * 6
Look at the clock on Figure 7.8. Don’t you think its hand for
the seconds decorated with a red and yellow disc is fancy? How will you
modify our clock morph to get this result?
ClockMorph>>stepTime
^ 100 "milliseconds"
ClockMorph>>step
../..
secondHand rotationDegrees: (time second + (time nanoSecond * 1.0e-9))* 6
Try to understand how these changes affect the behavior of the seconds’
hand and at which fraction of a second it is rotating.
CentralStar>>morphExtent
↑ `30 @ 30`
As you learnt previously, a morph draws itself from its drawOn: method.
We draw the star as an ellipse with randomly fluctuating x and y radius:
CentralStar>>drawOn: canvas
| radius |
radius ← self morphExtent // 2.
canvas ellipseCenter: 0 @ 0
radius: (radius x + (2 atRandom - 1)) @ (radius y + (2 atRandom - 1))
borderWidth: 3
borderColor: Color orange
fillColor: Color yellow
SpaceShip>>drawOn: canvas
| a b c d |
a ← 0 @ -15.
b ← -10 @ 15.
c ← 0 @ 10.
d ← 10 @ 15.
canvas line: a to: b width: 2 color: color.
canvas line: b to: c width: 2 color: color.
canvas line: c to: d width: 2 color: color.
canvas line: d to: a width: 2 color: color.
"Draw gas exhaust"
acceleration ifNotZero: [
canvas line: c to: 0 @ 35 width: 1 color: Color gray]
When there is an acceleration from the engine, we draw a small gray line
to represent the gas exhaust.
When the user turns the ship, the morph is rotated a bit by adjusting its
heading:
Chapter 7: The Fundamentals of Morph 127
SpaceShip>>right
"Rotate the ship to its right"
self heading: self heading + 0.1
SpaceShip>>left
"Rotate the ship to its left"
self heading: self heading - 0.1
But how does this heading affect the rotation of the morph?
Underneath, the MobileMorph is equipped with an affine transformation
to scale, rotate and translate the coordinates passed as arguments to the
drawing messages received by the canvas. Therefore, our heading methods
are defined to match this internal representation and we use the rotation:
method from the PacedMorph class to rotate appropriately. The location
attribute represents an affine transformation, and we get its rotation angle
with the #radians message.
Mobile>>heading
↑ location radians - Float halfPi
Mobile>>heading: aHeading
self rotation: aHeading + Float halfPi
When a mobile is vertical, pointing to the top of the screen, its heading
is -90◦ (-pi/2) in the screen coordinates system. In that situation the mobile
is not rotated – or 0◦ rotated – therefore we add 90◦ (pi/2) to the heading
to get the matching rotation angle of the mobile.
7.3.3 Torpedo
Alike a space ship, when a torpedo is just instantiated its nose points in the
direction of the top of the screen and its vertices are given by the Figure 7.11.
Chapter 7: The Fundamentals of Morph 128
Given the vertices given by Figure 7.11, how will you write its
morphExtent method?
SpaceShip>>initialize
super initialize.
vertices ← {0@-15 . -10@15 . 0@10 . 10@15}.
self resupply
Torpedo>>initialize
super initialize.
vertices ← {0@-4 . -2@4 . 2@4}.
lifeSpan ← 500.
acceleration ← 3000
However this is not a good idea. Imagine the game play with 200 torpe-
does, the vertices array will be duplicated 200 times with the same data!
Mobile class
instanceVariableNames: 'vertices'
Mobile class>>vertices
↑ vertices
SpaceShip class>>initialize
"SpaceShip initialize"
vertices ← {0@-15 . -10@15 . 0@10 . 10@15}
Torpedo class>>initialize
"Torpedo initialize"
vertices ← {0@-4 . -2@4 . 2@4}
SpaceShip vertices.
⇒ nil
SpaceShip initialize.
SpaceShip vertices.
⇒ #(0@-15 -10@15 0@10 10@15)
Torpedo vertices.
⇒ nil
Torpedo initialize.
Torpedo vertices.
⇒ #(0@-4 -2@4 2@4)
Example 7.8: A class instance variable value is not shared by the sub-
classes
aTorpedo class
⇒ Torpedo
self class
⇒ SpaceShip
The Torpedo’s drawOn: is rewritten to access the vertices in its class side:
Torpedo>>drawOn: canvas
| vertices |
vertices ← self class vertices.
canvas line: vertices first to: vertices second width: 2 color: color.
canvas line: vertices third to: vertices second width: 2 color: color.
canvas line: vertices first to: vertices third width: 2 color: color
Torpedo>>drawOn: canvas
self drawOn: canvas polygon: self class vertices
SpaceShip>>drawOn: canvas
| vertices |
vertices ← self class vertices.
self drawOn: canvas polygon: vertices.
"Draw gas exhaust"
acceleration ifNotZero: [
canvas line: vertices third to: 0@35 width: 1 color: Color gray]
Class variable
A class variable is written capitalized in the argument of
classVariableNames: keyword:
SpaceShip>>vertices
↑ `{0@-15 . -10@15 . 0@10 . 10@15}`
SpaceShip>>vertices
↑ `{0@-15 . -10@15 . 0@10 . 10@15}`
Torpedo>>vertices
↑ `{0@-4 . -2@4 . 2@4}`
When browsing the Rectangle class, you learn the #intersects: mes-
sage tells us if two rectangles overlap. This is what we need for a more
accurate collision detection between the central star and the space ships:
SpaceWar>>collisionsShipsStar
ships do: [:aShip |
(aShip displayBounds intersects: centralStar displayBounds) ifTrue: [
aShip flashWith: Color red.
self teleport: aShip]]
SpaceWar>>collisionsShipsStar
ships do: [:aShip |
(aShip collides: centralStar) ifTrue: [
aShip flashWith: Color red.
self teleport: aShip]]
Example 7.12: Collision (pixel precision) between the ships and the Sun
135
8 Events
When I used to read fairy tales, I fancied that kind of thing never
happened, and now here I am in the middle of one!
—Lewis Carroll, Alice in Wonderland
MouseMoveEvent
MouseScrollEvent
WindowEvent
As MouseMoveEvents are generated, the HandMorph adjusts its screen po-
sition. When mouse and keystroke events arrive, the HandMorph coordinates
the “dispatch” of events to the proper morph under the hand as well as
displaying tool tips and carrying morphs in transit during drag operations.
As we saw in the previous chapter with ColorClickEllipse, any morph
may override default Morph methods to assert that it handles various user
events and the methods which take the associated event objects when events
arrive.
Basically, user input events are generated, a HandMorph reflects any cursor
movement, morphs react to events, each long running task gets a time slice
and makes some progress, any display changes are updated on the screen,
and the next step happens. Time marches forward a step.
This happens over and over and over, keeping the juggler’s illusion that
all balls in the air are moving at once. Underneath, the balls are each moving
just a bit, in sequence.
Morph>>handlesMouseDown: aMouseButtonEvent
"Do I want to receive mouseButton messages ?
- #mouseButton1Down:localPosition:
- #mouseButton1Up:localPosition:
- #mouseButton2Down:localPosition:
- #mouseButton2Up:localPosition:
- #mouseButton3Down:localPosition:
- #mouseButton3Up:localPosition:
- #mouseMove:localPosition:
- #mouseButton2Activity
NOTE: The default response is false. Subclasses that implement these
Chapter 8: Events 138
As defined by default, this method and the other handlers check to see
if an instance has defined a property with the same name as the standard
method. So each individual instance can add its own behavior.
In a morph class where we want all instances to handle mouse down
events, we just override the appropriate method to return true:
MyMorph>>>>handlesMouseDown: aMouseButtonEvent
↑ true
Now in the events method category for class Morph, we find the handlers
listed in the comment above. A ScrollBar, a kind of Morph to represent
a list’s position control, scrolls its list contents when a mouse button 1 is
pressed:
To discover other events available for your morph, explore with the Sys-
tem Browser as described above.
Which method should return true to let the game play be notified
with a dedicated messages that the mouse cursor enters or leaves? In
which class should we implement this method?
Once we make explicit we want the game play to receive mouse move-over
events, we need to set the behavior accordingly with dedicated methods.
Each time the mouse cursor enters the game play, we want to:
• Get keyboard focus. It follows the mouse cursor: the keyboard input
goes to the morph under the mouse cursor. In Cuis-Smalltalk, the mouse
cursor is modeled as a HandMorph instance, an event object (see event
classes hierarchy at the beginning of this chapter). An event object
is interrogated about its hand with the #hand message. All in all, we
want the keyboard focus to be targeted toward our game play when the
mouse enters:
• Resume the game. The continuous update of the game is done through
a dedicated process stepping mechanism, which will be discussed in the
next chapter. The game play just asks itself to resume stepping:
self startStepping
• Pause the game. We stop the continuous stepping update of the game:
self stopStepping
In graphic user interface, a visual effect is often used to inform the user the
keyboard focus changed. In Spacewar! we change the game play background
depending on the sate of the keyboard focus.
In Figure 8.1, at the left keyboard focus is on the game; at the right
keyboard focus is not on the game, it is paused and we can see underneath.
Chapter 8: Events 141
SpaceWar>>keyboardFocusChange: gotFocus
gotFocus
ifTrue: [color ← self defaultColor]
ifFalse: [color ← self defaultColor alpha: 0.5].
self redrawNeeded
Find out which method should return true to let the game be
notified of keyboard event.
We can decide to be notified of the key down or key up event and also key
down then up event (key stroke). As long as our SpaceWar morph responds
true to the #handlesKeyboard message, it receives the messages #keyUp:,
#keyDown: and #keyStroke:. By default, the matching methods in the
Morph class do nothing.
The argument of these messages is a KeyboardEvent object to which,
among other things, you can ask the #keyCharacter of the pressed key or
test about some special keys as the keyboard arrows. The first player ship
– the green one – is controlled with the keyboard arrows when there are
stroked:
SpaceWar>>keyStroke: event
event isArrowUp ifTrue: [↑ ships first push].
event isArrowRight ifTrue: [↑ ships first right].
event isArrowLeft ifTrue: [↑ ships first left].
event isArrowDown ifTrue: [↑ ships first fireTorpedo].
...
1
https://en.wikipedia.org/wiki/Arrow_keys#WASD_keys
143
9 Code Management
Change is easy, except for the changed part.
—Alan Kay
fore, in the event of a Cuis-Smalltalk crash you can restore unsaved changes
when you launch the same Cuis-Smalltalk image again. Let’s explore this
feature with a simple example.
On a fresh Cuis-Smalltalk installation, create a new class category named
TheCuisBook and within TheBook class:
• Over the class category pane of System Browser (at the most left), do
...Right click → add items... (a)... key in TheCuisBook.
• Select this new class category and create the class TheBook as a kind of
Object: select the TheCuisBook category then in the source code below
edit the class template to replace #NameOfClass with #TheBook then
save the class definition with Ctrl-s.
| myBook |
myBook ← TheBook new
Cuis-Smalltalk does not save code you key in the Workspace, but code
you execute. Let’s execute this code: Ctrl-a then Ctrl-p, the Workspace
prints the result: a TheBook, an instance of a TheBook class.
Now kill Cuis-Smalltalk abruptly. On GNU/Linux, you can use the xkill
command to terminate Cuis-Smalltalk by clicking at its window.
Now start Cuis-Smalltalk again, and it immediately informs you there
are unsaved changes:
you are presented with the unsaved changes, one per line, in chronolog-
ical order, with the older ones at the top of the list. You select each
change (line) you want to restore, then you apply your selection with
the file in selections button.
To file-in the changes related to the creation of the TheBook class but
not the executed code in the Workspace, select the two lines related to
class definition.
The contextual menu (mouse right click) of the Lost changes window
offers a lot of options to filter the changes. Useful when the batch of
lost changes is important.
• Nothing. No changes are restored. Keep in mind that unsaved changes
aren’t discarded until you save your image.
In case you change your mind and you want to recover changes, do
...World menu → Changes... → Recently logged Changes....
The system presents you a list of image snapshots tagged with a date
stamp. Pick up the one occurring just before you lost your code, most
likely at the top of the list. Then in the Recent changes window, you
proceed as described earlier to cherry pick the changes to restore.
Observe Figure 9.4, after we added the method pages to the TheBook
class, the middle pane lists the added or modified methods. When a method
is selected its source code is printed in the bottom pane.
Let’s say we save the change set – File out entries in the change sorter
tool menu. This creates a new file 4451-CuisCore-HilaireFernandes-
2020Nov14-21h08m-hlsf.001.cs.st along the Cuis-Smalltalk image file:
From Cuis 5.0 [latest update: #4450] on 18 November 2020 at 9:05:09 am'!
!classDefinition: #TheBook category: 'TheCuisBook'!
Object subclass: #TheBook
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'TheCuisBook'!
To load this change set back in a new image, you use the File List tool
...World menu → Open → File List... Browse the folder containing the
change set file to load, then select it, from there you have three options to
manipulate it.
Figure 9.5: The File List tool, to install a change set and more
Chapter 9: Code Management 148
The change set way of managing the source code is used by the developers
of Cuis-Smalltalk, to work on its core image. When you want to write
an application, a dedicated tool or even a set of classes covering a specific
domain, you really want to use something else to manage the code: a package.
• Looking at the lower pane, note that those packages are required by the
package SVG.
Let’s say that in our clock morph we want to use color named as in the
Color-Extra package. So to be able to load our Morphic-Learning package
which makes use of this we need to select our new package and click on the
add Requirement button at center, right.
This brings up a list of loaded packages to choose from.
Now when we save our package, we see the pathname where the package
file was created. We can now safely email this file, check it into a version
control system, make a copy to our backup thumb drive.
As mentioned above, package files are just text files with a special format
which Cuis-Smalltalk knows how to load. If you open a File List browser
and view the package file, you will see information on how the package was
created, what it provides and requires, and, if you filled in the comment box
in the Installed Packages browser, a description.
Type “Morphic Toys” into the comment box, re-save your pack-
age, and (re)select the package in a File List to see your package de-
scription.
If you have not already done so, create and save a [Spacewar!
package], page 28. There are no additional requirements to specify.
Imagine we need to print the page number of the TheBook table of con-
tents as lower cased roman number, as we do with the printed version of this
book. The code is very simple:
4 printStringRoman asLowercase
⇒ 'iv'
Integer>>printStringToc
↑ self printStringRoman asLowercase
../..
aPage ← Page new.
aPage number: 1 printStringToc.
../..
Now we are facing a problem. For the need of the TheBook package
we extend the Integer class with a method printStringToc, however this
method addition is part of the Cuis-Smalltalk core system and its associated
default change set. See Figure 9.11, the Change Sorter tool exactly shows
that.
Therefore when saving our TheBook package this method is not included
and it is lost when quitting Cuis-Smalltalk. To include it in our pack-
age we categorize it in a method category with the *TheCuisBook pre-
fix. *TheCuisBook-printing is a good candidate. In the System Browser
method pane, over printStringToc, do ...Contextual menu → more... →
change category... and key in *TheCuisBook-printing.
Now the Change Sorter writes about Integer>>printStringToc:
Method was moved to some other package. The Installed Packages tools
now tells us we have an extension, use its browse button to get an update
on the package contents.
Chapter 9: Code Management 154
to find it. But beside this consideration, using any other version control
system is fine.
3. Install the necessary packages from the Cuis-Smalltalk Git repositories.
4. Develop. Modify and/or create packages.
5. Save own packages (to your preferred repositories).
6. add / commit / push accordingly to your version control system
7. Fileout changes that are not part of any package. These are auto-
matically captured in numbered changesets, separated from changes to
packages.
8. Exit the image. Usually without saving.
| list |
"Delete all windows but the taskbar"
self runningWorld doOneCycleNow.
list ← UISupervisor ui submorphs reject: [:aMorph |
aMorph is: #TaskbarMorph].
list do: [:each | each delete].
| list morph |
../..
"Change to Dark theme"
Feature require: #'Theme-Themes'.
DarkTheme beCurrent.
Chapter 9: Code Management 157
Now we are ready to install a few tools. First three browsers each occu-
pying a quarter of the screen:
some default contents. We need to hack a bit because when asking for a new
Workspace, Cuis-Smalltalk does not answer the created instance, we have to
search it in the windows of the world.
In addition to install, selecting a code file in the File List Browser also
adds buttons to inspect the code and to treat code contents like a Change
Sorter. When inspecting the code, a browser shows what code is different
between the file and the running image and allows one to import individual
classes or methods with the help of the context menu.
160
FileEntry>>readStreamDo: blockWithArg
"Raise FileDoesNotExistException if not found."
| stream result |
stream ← self readStream.
[ result ← blockWithArg value: stream ]
ensure: [ stream ifNotNil: [ :s | s close ]].
↑ result
Exceptions are created and signaled. Let’s make one and look at it.
Chapter 10: Debug and Exception Handling 161
Exception>>signal
Just as Smalltalk code has special view windows which we call Browsers,
Exceptions have an enhanced viewer we call the Debugger. Let us look at
how to use this very useful viewer.
| fileNames |
fileNames ← OrderedCollection new.
(DirectoryEntry smalltalkImageDirectory)
childrenDo: [ :f | fileNames add: f name ].
fileNames asArray.
Now, you can Ctrl-a (select All ) and Ctrl-p (select Print-it) to see the
result.
Now that we know what to expect, let us step through processing of the
code using the debugger. Remove the result, then Ctrl-a (select All ) and
Ctrl-Shift-D (select Debug-it).
The top pane in the debugger shows a view of the execution stack for this
bit of execution context. The way to think of this, the model of execution, is
that each time a method sends a message, it and its current state, arguments
and local variables, are placed on a stack until the result of that message
is received. If that message causes another message to be sent, then the
new state is pushed onto the stack. When a result is returned, the stack
Chapter 10: Debug and Exception Handling 164
frame is poped and processing continues. This works like a stack of trays in
a cafeteria.
The stack frames are displayed to show the stacked receiver and method.
The focus object, the receiver, for the selected stack frame has an inspector
in the lower left debugger panes at the bottom or the window.
The next two lower panes are an inspector for the arguments and local
variables, or temporaries, of the context frame.
The larger area displays the code being processed and hilights the next
message to be sent.
The stack of (framed) execution contexts gives a history of the computa-
tion so far. You can select any frame, view instance values in the receiver,
view the arguments and method variables at that point.
The two rows of buttons above the code pane give additional views and
control of how the execution processing is to proceed.
Notable buttons in the second row:
• Proceed. Continue execution
Now, we are going to play a bit. If you get out of synch with the instruc-
tions here, just close the debugger and start with Debug-It again.
Chapter 10: Debug and Exception Handling 165
As you single step the debugger, hilighting of the next message send
changes. Press Over three times. You should see the line starting with
childrenDo: hilighted. Now press Into.
Chapter 10: Debug and Exception Handling 166
The stack area shows the focus object is a DirectoryEntry. Inspect its
instance values by selecting lines in the lower left pane.
The stack area shows the focus method is DirectoryEntry>>childrenDo:.
This is the method displayed in the code pane.
The argument to childrenDo: is aBlock. There are no method variables
to display.
If you press Over again and Into, you should see the context where do:
is being processed.
This might be a good place to investigate the inspectors, look up and
down the stack, and play around a bit. By this time you should feel confident
that you understand the basics of what is displayed here.
You are in control!
Let’s look briefly at another way of doing this.
Chapter 10: Debug and Exception Handling 167
10.3 Halt!
A breakpoint is a place in code where one wishes to pause code processing
and look around. One does not always want to single step to find a problem,
especially one that occurs only once in a while. A breakpoint set where the
problem occurs is quite handy.
In Smalltalk, one uses the halt method to set a breakpoint. The message
#halt is sent to an object which is the debugger’s initial focus.
Please change the Workspace code to add a #halt as follows.
| fileNames |
fileNames ← OrderedCollection new.
(DirectoryEntry smalltalkImageDirectory)
childrenDo: [ :f | fileNames add: f name. fileNames halt ].
fileNames asArray.
The object which receives the #halt message becomes the focus
object of the debugger.
Note that with great power comes great responsibility.1 In an open sys-
tem, you can place a breakpoint anywhere, including places which can break
the user interface! For example, it could be a bad thing to put a breakpoint
in the code for the Debugger!
1
https://quoteinvestigator.com/2015/07/23/great-power/
171
11 Sharing Cuis
Programming is hardly ever a solitary communion between one man
and one machine. Caring about other people is a conscious decision,
and one that requires practice.
—Kent Beck, “Smalltalk Best Practice Patterns”(1997)
Programming remains an intensively collaborative process between
groups of program readers and writers.
—Dave Thomas, “Smalltalk With Style”(1996)
Let your code talk — Names matter. Let the code say what it
means. Introduce a method for everything that needs to be done.
Dont be afraid to delegate, even to yourself.
—Oscar Nierstrasz, “Best Practice Patterns - talk slides”(2009)
I notice that most uses of #left are to indicate a position, not take an
action. How can I fix that?
Because people frequently want to change things for the better, there are
a number of handy tools to help do this.
Now I could look at our uses of #left in Spacewars!, but the Cuis IDE
already knows how to do this!
If I right-click on the Method Pane in the Browser, I get a context menu
with selections to help me out. Here I choose Rename.
Chapter 11: Sharing Cuis 173
Now the tools that help us refactor code are quite powerful, so restraint
is called for. I don’t want to change all uses of #left in the Cuis-Smalltalk
system, just in the Spacewar! category.
Of course, when making changes one wants to see that the result is what
one expects.
Figure 1.4
Spacewar running on PDP-1, Joi Ito, 12 May 2007, resized,
https://www.flickr.com/photos/35034362831@N01/494431001
https://creativecommons.org/licenses/by/2.0/deed.en
Cuis-Smalltalk mascot
1 decimal integer
2r101 binary integer (radix 2)
16r1a hexadecimal integer (radix 16)
1.5 floating point number
2.4e7 exponential notation
$a the character ‘a’
'Hello' the string “Hello”
#Hello the symbol #Hello
#(1 2 3) a literal array
{1. 2. 1 + 2} a dynamic array
Local variables.
startPoint is a variable name, or identifier. By convention,
identifiers are composed of words in “camelCase” (i.e., each word
except the first starting with an upper case letter). The first
Appendix B: Summary of Syntax 179
Dynamic arrays.
Dynamic arrays or Run-time arrays. Curly braces { } define
a (dynamic) array at run-time. Elements are expressions sep-
arated by periods. So { 1. 2. 1+2 } defines an array with ele-
ments 1, 2, and the result of evaluating 1+2. (The curly-brace
notation is peculiar to the Squeak family dialect of Smalltalk! In
other Smalltalks you must build up dynamic arrays explicitly.)
Comments.
Comments are enclosed in double quotes. "hello" is a com-
ment, not a string, and is ignored by the Cuis-Smalltalk com-
piler. Comments may span multiple lines.
Local variable declarations.
Vertical bars | | enclose the declaration of one or more local
variables in a method (and also in a block).
Assignment.
:= assigns an object to a variable. In the printed version of the
book we wrote ← instead. Since this character is not present in
the keyboard, you key in with the underscore character key. So,
x := 1 is the same as x ← 1 or x _ 1.
Blocks.
Square brackets [ ] define a block, also known as a block closure
or a lexical closure, which is a first-class object representing a
function. As we shall see, blocks may take arguments and can
have local variables.
Primitives.
<primitive: ...> denotes an invocation of a virtual
machine primitive. (<primitive: 1> is the VM primitive
for SmallInteger>>+.) Any code following the primitive is
executed only if the primitive fails. The same syntax is also
used for method annotations.
Unary messages.
Unary messages consist of a single word (like #factorial) sent
to a receiver (like 3).
Binary messages.
Binary messages are operators (like +) sent to a receiver and
taking a single argument. In 3 + 4, the receiver is 3 and the
argument is 4.
Keyword messages.
Keyword messages consist of multiple keywords (like
#raisedTo:modulo:), each ending with a colon and taking a
single argument. In the expression 2 raisedTo: 6 modulo:
10, the message selector raisedTo:modulo: takes the two
181
Preface
Exercise 1
In the seventies, four versions were developed: Smalltalk-71, Smalltalk-72,
Smalltalk-76 and Smalltalk-80.
Smallltalk Philosophy
Exercise 1.1
Exercise 1.2
Transcript show: 'Hello ', 'my beloved ' asUppercase, 'friend'
Exercise 1.3
1 + (1/2) + (1/3) + (1/4)
⇒ 25/12
Exercise 1.4
Several messages can be sent one after the other:
Transcript show: 2020 printStringWords capitalized
Exercise 2.2
1 + (1/2) squared + (1/3) squared + (1/4) squared
⇒ 205 / 144
Exercise 2.3
From a System Browser, do from the left panel to the right ...Kernel-Text
→ String → arithmetic... the count of methods in the last right panel is
6: *, +, -, /, // and \\.
Exercise 3.2
0 to: Float twoPi by: 1/10 do: [:i |
Transcript show: i cos; cr]
Exercise 3.3
1024 is not a random number. It is 210 then written in base 2 : 10000000000,
it is also 1 << 10:
2↑10 ⇒ 1024
1024 printStringBase: 2 ⇒ '10000000000'
1 << 10 ⇒ 1024
Therefore, to multiply an integer by 1024, we shift left of 10 its digits:
360 << 10 ⇒ 368640
360 * 1024 ⇒ 368640
Exercise 3.4
5.2 + 0.9 - 6.1
⇒ 8.881784197001252e-16
1.2 * 3 - 3.6
⇒ -4.440892098500626e-16
Appendix D: Solutions to the Exercises 186
Exercise 3.5
The system returns the error ZeroDivide, division by zero.
Exercise 3.6
(52/10) + (9/10) - (61/10)
⇒ 0
(12/10) * 3 - (36/10)
⇒ 0
Exercise 3.7
There are different options, with slightly different results:
'There are 12 apples' select: [:i |i isLetter].
⇒ 'Thereareapples'
Not really satisfying. So another option:
'There are 12 apples' select: [:i |i isDigit not].
⇒ 'There are apples'
Or even a shorter option with the #reject: message:
'There are 12 apples' reject: [:i |i isDigit].
⇒ 'There are apples'
Exercise 3.8
In String, search for the method category format, there you find the
format: method:
'Joe bought {1} apples and {2} oranges' format: #(5 4)
⇒ 'Joe bought 5 apples and 4 oranges'
Exercise 3.9
The SpaceWar, CentralStar and SpaceShip definitions with their added
instance variable should look like:
Object subclass: #SpaceWar
instanceVariableNames: 'centralStar ships torpedoes'
classVariableNames: ''
poolDictionaries: ''
category: 'Spacewar!'
Exercise 3.10
SpaceShip>>position
↑ position
SpaceShip>>velocity
↑ position
SpaceShip>>mass
↑ mass
Exercise 3.11
SpaceShip>>position: aPoint
position ← aPoint
SpaceShip>>velocity: aPoint
velocity ← aPoint
Torpedo>>heading: aFloat
heading ← aFloat
Exercise 3.12
SpaceShip>>left
"Rotate the ship to its left"
heading ← heading + 0.1
SpaceShip>>right
"Rotate the ship to its right"
heading ← heading - 0.1
Exercise 3.13
SpaceShip>>push
"Init an acceleration boost"
acceleration ← 10
SpaceShip>>unpush
"Stop the acceleration boost"
acceleration ← 0
Exercise 3.14
CentralStar>>initialize
super initialize.
mass ← 8000.
Appendix D: Solutions to the Exercises 188
Exercise 4.2
(-80 to: 50) asArray
Exercise 4.3
(1 to: 100) difference: (25 to: 75)
⇒ #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
22 23 24 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
92 93 94 95 96 97 98 99 100)
Exercise 4.4
(-20 to: 45) select: [:z | z odd]
Exercise 4.5
((101 to: 200) select: [:n | n isPrime]) size
⇒ 21
Exercise 4.6
(1 to: 100) select:[:n | n isDivisibleBy: 7]
⇒ #(7 14 21 28 35 42 49 56 63 70 77 84 91 98)
Exercise 4.7
This solution, based on set operations and multiple use of the #select:
message, is mostly compatible with the knowledge acquired at this point of
the book.
| primeNumbers nonPrimeNumbers |
primeNumbers ← (1 to: 100) select: [:n | n isPrime].
nonPrimeNumbers ← (1 to: 100) difference: primeNumbers.
nonPrimeNumbers select: [:n | n odd]
⇒ #(1 9 15 21 25 27 33 35 39 45 49 51 55 57 63 65 69 75
77 81 85 87 91 93 95 99)
A shorter solution with logical operations we have not discussed so far:
(1 to: 100) select:[:n | n isPrime not and: [n odd]]
Exercise 4.8
'Zpv!bsf!b!cptt' collect: [:c |
Appendix D: Solutions to the Exercises 189
(c asciiValue - 1) asCharacter]
⇒ 'You are a boss'
Exercise 4.9
($A to: $Z) collect: [:c |
(c asciiValue - 65 + 3 \\ 26 + 65) asCharacter]
Each character from A to Z is referenced by the c block parameter and
converted to its Ascii value1 : c asciiValue. It is shifted of 65, so the A
character counts as 0 and Z as 25 in the alphabet list. To apply the Caesar
cipher, we send the message + 3 to the previous result.
We are almost good, only the characters X, Y, Z will overflow the al-
phabet, indeed these characters will be ciphered as 26, 27, 28. To fix that
we send \\ 26 to the previous result, it is an old trick of programming: the
reminder of the Euclidean division by 26 frames the value between 0 and
252 .
Finally we shift back of 65 before converting from the Ascii value to the
character.
Exercise 4.10
In the solution of Exercise 4.9, we just need to replace the characters interval
with a string:
'SMALLTALKEXPRESSION' collect: [:c |
(c asciiValue - 65 + 3 \\ 26 + 65) asCharacter]
⇒ 'VPDOOWDONHASUHVVLRQ'
Exercise 4.11
'DOHDMDFWDHVW' collect: [:c |
(c asciiValue - 65 - 3 \\ 26 + 65) asCharacter]
⇒ 'ALEAJACTAEST'
Exercise 4.12
The appropriate message is #first:, defined in the parent class
SequenceableCollection. You need to use the protocol or hierarchy
browser on Array to discover it:
array1 first: 2
⇒ #(2 'Apple')
Exercise 4.13
You could simply do a thumb:
array1 at: 1 put: 'kiwi'.
1
https://en.wikipedia.org/wiki/ASCII
2
It generalizes to the mathematics field of modular arithmetic, https://www.wikiwand.
com/en/Modular_arithmetic
Appendix D: Solutions to the Exercises 190
Exercise 4.14
In the OrderedCollection protocol search for the method add:after:.
coll1 ← {2 . 'Apple' . 2@1 . 1/3 } asOrderedCollection .
coll1 add: 'Orange' after: 'Apple'; yourself.
⇒ an OrderedCollection(2 'Apple' 'Orange' 2@1 1/3)
Exercise 4.15
Set new
addAll: 'buenos dı́as';
addAll: 'bonjour';
yourself.
⇒ a Set($e $j $o $a $u $b $ $ı́ $r $d $n $s)
Exercise 4.16
colors keysDo: [:key |
colors at: key put: key asString capitalized].
colors
⇒ a Dictionary(#blue->'Blue' #green->'Green' #red->'Red'
#yellow->'Yellow' )
Exercise 4.17
When the game starts there is no fired torpedoes, therefore torpedoes is an
empty OrderedCollection, instantiated with the #new class message.
In the other hand, the ships is an Array containing only two elements,
the player ships. We use the #with:with class message to instantiate and
populate the array with two ships created in the argument message.
For the readability, we split the code in several lines with the appropriate
indentation.
torpedoes ← OrderedCollection new.
ships ← Array
with: SpaceShip new
with: SpaceShip new.
Exercise 4.18
SpaceWar>>stepAt: msSinceLast
Appendix D: Solutions to the Exercises 191
Exercise 5.1
| divisors |
divisors ← [:x | (1 to: x) select: [:d | x \\ d = 0] ].
divisors value: 60.
⇒ #(1 2 3 4 5 6 10 12 15 20 30 60)
divisors value: 45
⇒ #(1 3 5 9 15 45)
Exercise 5.2
Check the implementations in Boolean, True and False.
Exercise 5.3
Once the method is edited and saved, in the Method pane select its name
teleport: then do ...right click → more... → change category... →
events...
Exercise 5.4
In the Method pane, select one uncategorized control method, then do ...right
click → more... → change category → new... key-in control.
To categorized the remaining uncategorized control methods, repeat but
select control at the last step as this category now exists.
Exercise 5.5
We do not need an iterator to detect a collision between two ships. However
we use an iterator to take action on each ship when a collision is detected.
SpaceWar>>collisionsShips
| positionA position B |
positionA ← ships first morphPosition.
positionB ← ships second morphPosition.
(positionA dist: positionB) < 25 ifTrue: [
ships do: [:each |
each flashWith: Color red.
self teleport: each]
]
Local variables only used to ease the code source formatting in printed
book.
Appendix D: Solutions to the Exercises 192
Exercise 5.6
You just need to pick the appropriate code snippets from the referenced
exercise and examples.
SpaceWar>>collisionsTorpedoesStar
| position |
position ← centralStar morphPosition.
torpedoes do: [:each |
(each morphPosition dist: position) < 8 ifTrue: [
each flashWith: Color orange.
self destroyTorpedo: each]]
Exercise 6.2
Mobile subclass: #SpaceShip
instanceVariableNames: 'name fuel torpedoes'
classVariableNames: ''
poolDictionaries: ''
category: 'Spacewar!'
Exercise 7.1
The drawOn: method is modified to draw two distinct, unconnected lines:
LineExampleMorph>>drawOn: aCanvas
aCanvas strokeWidth: 20 color: Color green do: [
aCanvas
moveTo: 0 @ 0;
lineTo: 200 @ 200;
moveTo: 200 @ 0;
lineTo: 0 @ 200 ]
Learn how the #moveTo: message moves the pencil to a given position
with the pen up, then the #lineTo: asks the pencil to draw from that
previous position to this new position.
Exercise 7.2
We create a RectangleExampleMorph, subclass of PlacedMorph:
PlacedMorph subclass: #RectangleExampleMorph
instanceVariableNames: 'fillColor'
classVariableNames: ''
poolDictionaries: ''
category: 'Morphic-Learning'
Then its necessary methods to initialize and to draw the morph:
initialize
super initialize .
fillColor ← Color random alpha: 0.5
drawOn: aCanvas
aCanvas
strokeWidth: 1
color: Color blue
fillColor: fillColor
do: [
aCanvas moveTo: 0 @ 0;
lineTo: 200 @ 0;
lineTo: 200 @ 100;
lineTo: 0 @ 100;
lineTo: 0 @ 0]
Appendix D: Solutions to the Exercises 194
Exercise 7.3
Among the clock parts (submorphs) we only need to modify the drawing of
the ClockSecondHandMorph class. The disc is surrounded with a thin red
line and filled in yellow.
ClockSecondHandMorph>>drawOn: aCanvas
aCanvas strokeWidth: 1.5 color: Color red do: [
aCanvas
moveTo: 0 @ 0;
lineTo: 85 @ 0 ].
aCanvas ellipseCenter: 0 @ -70 radius: 3 @ 3
borderWidth: 1
borderColor: Color red fillColor: Color yellow
Exercise 7.4
The width of the torpedo is 4 pixels and its height 8 pixels:
Torpedo>>morphExtent
↑ `4 @ 8`
Exercise 7.5
The Torpedo’s drawOn: method is very similar to the one in SpaceShip
class:
Torpedo>>drawOn: canvas
| a b c |
a ← 0 @ -4.
b ← -2 @ 4.
c ← 2 @ 4.
canvas line: a to: b width: 2 color: color.
canvas line: c to: b width: 2 color: color.
canvas line: a to: c width: 2 color: color.
Appendix D: Solutions to the Exercises 195
Exercise 7.6
We use a local variable because we use two times the vertices, one to draw
the ship and a second time to draw the gas exhaust.
SpaceShip>>drawOn: canvas
| vertices |
vertices ← self class vertices.
canvas line: vertices first to: vertices second width: 2 color: color.
canvas line: vertices second to: vertices third width: 2 color: color.
canvas line: vertices third to: vertices fourth width: 2 color: color.
canvas line: vertices fourth to: vertices first width: 2 color: color.
"Draw gas exhaust"
acceleration ifNotZero: [
canvas line: vertices third to: 0@35 width: 1 color: Color gray]
Exercise 7.7
You need both to iterate each vertex of the vertices array and access
the subsequent vertex by index. The arithmetic reminder operation #\\
is needed to keep the index in the boundary of the collection.
When size is 4 (Space ship diagram), the argument (i \\ size + 1)
takes alternatively the following values:
• i = 1 => 1 \\ 4 + 1 = 1 + 1 = 2
• i = 2 => 2 \\ 4 + 1 = 2 + 1 = 3
• i = 3 => 3 \\ 4 + 1 = 3 + 1 = 4
• i = 4 => 4 \\ 4 + 1 = 0 + 1 = 1
Mobile>>drawOn: canvas polygon: vertices
| size |
size ← vertices size.
vertices withIndexDo: [: aPoint :i |
canvas
line: aPoint
to: ( vertices at: (i \\ size + 1) )
width: 2
color: color]
Exercise 7.8
Just replace each morph position distance approach with the intersection
detection between the morphs’ display bounds:
SpaceWar>>collisionsShips
(ships first collides: ships second)
../..
SpaceWar>>collisionsShipsTorpedoes
ships do: [:aShip |
torpedoes do: [:aTorpedo |
(aShip collides: aTorpedo)
Appendix D: Solutions to the Exercises 196
../..
SpaceWar>>collisionsTorpedoesStar
torpedoes do: [:each |
(each collides: centralStar)
../..
Events
Exercise 8.1
The method handlesMouseOver:, implemented in the SpaceWar morph
class, returns true so the game play is informed of mouse over events in
dedicated methods.
SpaceWar>>handlesMouseOver: event
↑ true
Exercise 8.2
You need to browse the Morph>>handlesMouseOver: method and read the
comment. It writes about a #mouseEnter: message; we implement the
matching method in SpaceWar class with the behaviors previously described:
SpaceWar>>mouseEnter: event
event hand newKeyboardFocus: self.
self startStepping
Exercise 8.3
The message #mouseLeave: is sent to our SpaceWar instance each time the
mouse cursor move out (leaves) of the game play. Therefore we add the
homonym method to the SpaceWar class:
SpaceWar>>mouseLeave: event
event hand releaseKeyboardFocus: self.
self stopStepping
Exercise 8.4
The #handlesKeyboard message is sent to a morph to know if it wants to
receive keyboard event. The morph responds true to this message to state
its interest on keyboard event. We implement the method in the SpaceWar
class:
SpaceWar>>handlesKeyboard
↑ true
Exercise 8.5
We designate the characters as $w $a $s $d. We append the code below to
the method SpaceWar>>keyStroke:
key = $w ifTrue: [↑ ships second push].
Appendix D: Solutions to the Exercises 197
Code Management
Exercise 9.3
1. In the System Browser, create two class categories: in its most left pane
menu select add item... (a) and key in one category name at a time.
2. Create two classes, in each category: TheCuisBook subclass of Object
and TheBookMorph subclass of PlacedMorph.
The two operations above are doable in one shot. Select any existing
category and key in the class definition with the category name:
Once you save the class definition, the TheBookMorph class is created –
obviously – but also the TheCuisBook-Views category!
3. Go to the Installed Packages tool ...World menu → Open... →
Installed Packages..., press the new button and key in TheCuisBook.
4. Press the save button, you are done!
198
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Spacewar!!'!
!classDefinition: 'CentralStar class' category: 'Spacewar!!'!
CentralStar class
instanceVariableNames: ''!
Ship Controls
Green: left, right, up down arrows
Red: a, d, w, s keys!
!Mobile class methodsFor: 'as yet unclassified' stamp: 'hlsf 12/19/2020 23:21:09'!
vertices
^ vertices ! !
unpush
"Stop the accelaration boost"
acceleration _ 0! !
!SpaceShip class methodsFor: 'as yet unclassified' stamp: 'hlsf 12/19/2020 23:21:16'!
initialize
"SpaceShip initialize"
vertices _ {0@-15 . -10@15. 0@10. 10@15}! !
!Torpedo class methodsFor: 'as yet unclassified' stamp: 'hlsf 12/19/2020 23:21:22'!
initialize
"Torpedo initialize"
vertices _ {0@-4 . -2@4 . 2@4}! !
collisionsShipsTorpedoes
ships do: [:aShip |
torpedoes do: [:aTorpedo |
(aShip collides: aTorpedo) ifTrue: [
aShip flashWith: Color red.
aTorpedo flashWith: Color orange.
self destroyTorpedo: aTorpedo.
self teleport: aShip]]
]! !
self stopStepping! !
"millisecond"
^ 20! !
. create (new) . . . . . . . . . . . . . . . . . . . . . . . 27
.pck.st . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 declaration . . . . . . . . . . . . . . . . . . . . . . . . . 24
.st . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 inheritance . . . . . . . . . . . . . . . . . . . . . 32, 56
initialize . . . . . . . . . . . . . . . . . . . . . . . . . . 130
instance variable, See variable
A method, See method
Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60, 69 protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
array, variable, See variable
dynamic . . . . . . . . . . . . . . . . . . . . . . 61, 179 collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
operation . . . . . . . . . . . . . . . . . . . . . . . . . . 61 access element . . . . . . . . . . . . . . . . . . . . . 69
size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 add: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 at: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
statistic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 collect: . . . . . . . . . . . . . . . . . . . . . . . 65, 67
assignment, See variable convert . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Dictionary . . . . . . . . . . . . . . . . . . . . . . . . 72
dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
B enumerator mechanism . . . . . . . . . . . . . 63
backtick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 fixed size. . . . . . . . . . . . . . . . . . . . . . . . . . . 68
bits shifting . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 indexOf: . . . . . . . . . . . . . . . . . . . . . . . . . . 62
block . . . . . . . . . . . . . . . . . . . . . . . . . 63, 80, 180 inject:into: . . . . . . . . . . . . . . . . . . . . . . 17
assigned to a variable . . . . . . . . . . . . . . 81 instantiate array . . . . . . . . . . . . . . . . . . . 69
ensure: . . . . . . . . . . . . . . . . . . . . . . . . . . 160 instantiate variable size array . . . . . . 70
local variable. . . . . . . . . . . . . . . . . . . . . . . 80 last . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
parameter . . . . . . . . . . . . . . . . . . . . . . 63, 80 OrderedCollection . . . . . . . . . . . . . . . . 62
boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 pairsDo: . . . . . . . . . . . . . . . . . . . . . . . . . . 68
breakpoint, See Tools, debugger select: . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
class category . . . . . . . . . . . . . . . . . . . . . . 24 set operations (union,
class category (new). . . . . . . . . . . . . . . . 27 intersection, difference) . . . . . . . . . . 62
hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . 56 shuffled . . . . . . . . . . . . . . . . . . . . . . . . . . 21
invoke from Workspace . . . . . . . . . . . . . 26 squared . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 variable size. . . . . . . . . . . . . . . . . . . . . . . . 70
command line option,
-s (run a script) . . . . . . . . . . . . . . . . . . . 155
C comment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Caesar cipher . . . . . . . . . . . . . . . . . . . . . . . . . . 66 control flow . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
cascade of messages . . . . . . . . . . . . . . . 19, 181 loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
change log. . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
change set . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 coordinates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
character . . . . . . . . . . . . . . . . . . . . . . . . . 46, 179
ascii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Unicode. . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17, 31
D
abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 debugger, See tools
category . . . . . . . . . . . . . . . 24, 25, 57, 148 Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
category (new) . . . . . . . . . . . . . . . . . . . . . 27
class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
comment . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Appendix H: Conceptual index 213
E K
event, keyboard shortcut,
classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 browse a class (Ctrl-b) . . . . . . . . . . . . 26
handling . . . . . . . . . . . . . . . . . . . . . 100, 137 browse hierarchy (Ctrl-h) . . . . . . . . . . 57
keyboard . . . . . . . . . . . . . . . . . . . . . . 142 browse protocol (Ctrl-p) . . . . . . . . . . . 56
mouse enter . . . . . . . . . . . . . . . . . . . 139 code completion (tab) . . . . . . . . . . . . . . 38
mouse-enter . . . . . . . . . . . . . . . . . . . 139 execute and print result (Ctrl-p) . . . 12
keyboard . . . . . . . . . . . . . . . . . . . . . . . . . 141 executing code (Ctrl-d) . . . . . . . . . . . . 11
mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 find a class (Ctrl-f) . . . . . . . . . . . . . . . 25
testing . . . . . . . . . . . . . . . . . . . . . . . . 99, 137 implementors of (Ctrl-m) . . . . . . . . . . 35
keyboard . . . . . . . . . . . . . . . . . . . . . . 141 save code (Ctrl-s) . . . . . . . . . . . . . . . . . 27
mouse over . . . . . . . . . . . . . . . . . . . . 138 select all code (Ctrl-a) . . . . . . . . . . . . 11
exception. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
execution stack . . . . . . . . . . . . . . . . . . . . . . . 163
L
literal,
F number . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
false . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Fibonacci sequence . . . . . . . . . . . . . . . . . . . . 67 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41, 67
step . . . . . . . . . . . . . . . . . . . . . . . . 41, 67
file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
repeat . . . . . . . . . . . . . . . . . . . . . . . . . . 41, 67
file extension,
.st . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
.st.pck . . . . . . . . . . . . . . . . . . . . . . . . . . 158
float (see number) . . . . . . . . . . . . . . . . . . . . 179
M
for loop, See loop message,
fraction, See number binary . . . . . . . . . . . . . . . . . . . . . . . . 18, 180
cascade . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
composition . . . . . . . . . . . . . . . . . . . . . . . . 21
getter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
G keyword . . . . . . . . . . . . . . . . . . . . . . . 18, 180
garbage collection . . . . . . . . . . . . . . . . . . . . . 17 precedence . . . . . . . . . . . . . . . . . . . . . . . . . 18
receiver . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
send . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
H sender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
setter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
halt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 unary . . . . . . . . . . . . . . . . . . . . . . . . . 18, 180
method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17, 79
category . . . . . . . . . . . . . . . . . . . . 25, 40, 47
I class method . . . . . . . . . . . . . . . . . . . 32, 37
initialize . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 creating . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
inspector, See tools instance method . . . . . . . . . . . . 26, 34, 38
instance . . . . . . . . . . . . . . . . . . . . . . . . . . . 17, 31 overriding . . . . . . . . . . . . . . . . . . . . . . . . . . 34
creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 returned value (explicit) . . . . . . . . . . . . 34
method, See method returned value (implicit) . . . . . . . . . . . 35
instance variable. . . . . . . . . . . . . . . . . . . . . . . 32 variable, See variable
integer (see number) . . . . . . . . . . . . . . . . . . 179 moprh,
interger, See number legacy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Interval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 morph,
animated . . . . . . . . . . . . . . . . . . . . . . . . . 115
clipsSubmorphs . . . . . . . . . . . . . . . . . . 117
delete . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Appendix H: Conceptual index 214
S inspector. . . . . . . . . . . . . . . . . . . . . . . . . . . 95
selector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 lost changes. . . . . . . . . . . . . . . . . . . . . . . 144
self . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 recent changes . . . . . . . . . . . . . . . . . . . . 145
sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 system browser . . . . . . . . . . . . . . . . . . . . 24
Set. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 transcript . . . . . . . . . . . . . . . . . . . . . . . . . . 11
shortcut, See keyboard shortcut workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
start-up script . . . . . . . . . . . . . . . . . . . . . . . . 155 true . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
string . . . . . . . . . . . . . . . . . . . . . . . . . 11, 47, 179
asArray . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
asUppercase . . . . . . . . . . . . . . . . . . . . . . . 11 U
at:put: . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Unicode . . . . . . . . . . . . . . . . . . . . . . . . 20, 21, 59
capitalized . . . . . . . . . . . . . . . . . . . . . . . 11
character access . . . . . . . . . . . . . . . . . . . . 20
concatenate . . . . . . . . . . . . . . . . . . . . . . . . 12
file entry . . . . . . . . . . . . . . . . . . . . . . . . . . 163 V
indexOf: . . . . . . . . . . . . . . . . . . . . . . . . . . 20
variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
shuffled . . . . . . . . . . . . . . . . . . . . . . . . . . 21
:= . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
sorted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
← . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Unicode . . . . . . . . . . . . . . . . . . . . . . . . 20, 59
subclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 assignment . . . . . . . . . . . . . . . . . 3, 52, 180
super . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54, 78 class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
superclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 class instance . . . . . . . . . . . . . . . . . . . . . 128
symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . 58, 179 instance . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
declaration . . . . . . . . . . . . . . . . . . . . 180
method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
T shared . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
thisContext . . . . . . . . . . . . . . . . . . . . . . . . . . 79
tools,
debugger. . . . . . . . . . . . . . . . . . . . . . . . . . 162 Y
breakpoint . . . . . . . . . . . . . . . . . . . . 167 yourself . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20