3.6: Defining the Class LOGame
- Page ID
- 39582
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Now let’s create the other class that we need for the game, which we will name LOGame
.
Class creation
Make the class definition template visible in the browser main window. Do this by clicking on the package name (or right-clicking on the Class pane and selecting Add Class
). Edit the code so that it reads as follows, and Accept it.
BorderedMorph subclass: #LOGame instanceVariableNames: '' classVariableNames: '' package: 'PBE-LightsOut'
Here we subclass BorderedMorph
. Morph
is the superclass of all of the graphical shapes in Pharo, and (unsurprisingly) a BorderedMorph
is a Morph
with a border. We could also insert the names of the instance variables between the quotes on the second line, but for now, let’s just leave that list empty.
Initializing our game
Now let’s define an initialize
method for LOGame
. Type the following into the browser as a method for LOGame
and try to Accept it.
initialize | sampleCell width height n | super initialize. n := self cellsPerSide. sampleCell := LOCell new. width := sampleCell width. height := sampleCell height. self bounds: (5 @ 5 extent: (width * n) @ (height * n) + (2 * self borderWidth)). cells := Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ]
Pharo will complain that it doesn’t know the meaning of cells
(see Figure \(\PageIndex{1}\)). It will offer you a number of ways to fix this.

Choose Declare new instance variable, because we want cells to be an instance variable.
Taking advantage of the debugger
At this stage if you open a Playground, type LOGame new
, and Do it, Pharo will complain that it doesn’t know the meaning of some of the terms (see Figure \(\PageIndex{2}\)). It will tell you that it doesn’t know of a message cellsPerSide
, and will open a debugger. But cellsPerSide
is not a mistake; it is just a method that we haven’t yet defined. We will do so, shortly.

Now let us do it: type LOGame new
and Do it. Do not close the debugger. Click on the button Create
of the debugger, when prompted, select LOGame, the class which will contain the method, click on ok, then when prompted for a method protocol enter accessing
. The debugger will create the method cellsPerSide
on the fly and invoke it immediately. As there is no magic, the created method will simply raise an exception and the debugger will stop again (as shown in Figure \(\PageIndex{3}\)) giving you the opportunity to define the behavior of the method.

Here you can write your method. This method could hardly be simpler: it answers the constant 10. One advantage of representing constants as methods is that if the program evolves so that the constant then depends on some other features, the method can be changed to calculate this value.
cellsPerSide "The number of cells along each side of the game" ^ 10
Define the method cellsPerSide
in the debugger. Do not forget to compile the method definition by using Accept. You should obtain a situation as shown by Figure \(\PageIndex{4}\). If you press the button Proceed the program will continue its execution - here it will stop since we did not define the method newCellAt:
. We could use the same process but for now we stop to explain a bit what we did so far. Close the debugger, and look at the class definition once again (which you can do by clicking on LOGame on the second pane of the System Browser), you will see that the browser has modified it to include the instance variable cells
.

Studying the initialize method
Let us now study the method initialize
.
initialize | sampleCell width height n | super initialize. n := self cellsPerSide. sampleCell := LOCell new. width := sampleCell width. height := sampleCell height. selfbounds:(5@5extent:(width*n)@(height*n)+(2*self borderWidth)). cells := Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ]
- At line 2, the expression
| sampleCell width height n |
declares 4 temporary variables. They are called temporary variables because their scope and lifetime are limited to this method. Temporary variables with explanatory names are helpful in making code more readable. Lines 4-7 set the value of these variables. - How big should our game board be? Big enough to hold some integral number of cells, and big enough to draw a border around them. How many cells is the right number? 5? 10? 100? We don’t know yet, and if we did, we would probably change our minds later. So we delegate the responsibility for knowing that number to another method, which we name
cellsPerSide
, and which we will write in a minute or two. Don’t be put off by this: it is actually good practice to code by referring to other methods that we haven’t yet defined. Why? Well, it wasn’t until we started writing theinitialize
method that we realized that we needed it. And at that point, we can give it a meaningful name, and move on, without interrupting our flow. - The fourth line uses this method,
n := self cellsPerSide.
sends the messagecellsPerSide
toself
, i.e., to this very object. The response, which will be the number of cells per side of the game board, is assigned ton
. - The next three lines create a new
LOCell
object, and assign its width and height to the appropriate temporary variables. - Line 8 sets the bounds of the new object. Without worrying too much about the details just yet, believe us that the expression in parentheses creates a square with its origin (i.e., its top-left corner) at the point
(5,5)
and its bottom-right corner far enough away to allow space for the right number of cells. - The last line sets the
LOGame
object’s instance variable cells to a newly createdMatrix
with the right number of rows and columns. We do this by sending the messagenew: tabulate:
to theMatrix
class (classes are objects too, so we can send them messages). We know thatnew: tabulate:
takes two arguments because it has two colons (:
) in its name. The arguments go right after the colons. If you are used to languages that put all of the arguments together inside parentheses, this may seem weird at first. Don’t panic, it’s only syntax! It turns out to be a very good syntax because the name of the method can be used to explain the roles of the arguments. For example, it is pretty clear thatMatrix rows: 5 columns: 2
has 5 rows and 2 columns, and not 2 rows and 5 columns. Matrix new: n tabulate: [ :i :j | self newCellAt: i at: j ]
creates a new n X n matrix and initializes its elements. The initial value of each element will depend on its coordinates. The (i,j)th element will be initialized to the result of evaluatingself newCellAt: i at: j
.