Skip to main content
Engineering LibreTexts

3.8: Finishing the Game

  • Page ID
    40054
    \( \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}}\)

    Now let’s define the other method that are used by LOGame >> initialize. Let’s define LOGame >> newCellAt: at: in the initialization protocol.

    LOGame >> newCellAt: i at: j
        "Create a cell for position (i,j) and add it to my on-screen
            representation at the appropriate screen position. Answer the
            new cell"
    
        | c origin |
        c := LOCell new.
        origin := self innerBounds origin.
        self addMorph: c.
        c position: ((i - 1) * c width) @ ((j - 1) * c height) + origin.
        c mouseAction: [ self toggleNeighboursOfCellAt: i at: j ]
    

    Formatting. As you can see there are some tabulation and empty lines. In order to keep the same convention you can right-click on the method edit area and click on Format (or use CMD-Shift-f shortcut). This will format your method.

    The method defined above created a new LOCell, initialized to position (i, j) in the Matrix of cells. The last line defines the new cell’s mouseAction to be the block [ self toggleNeighboursOfCellAt: i at: j ]. In effect, this defines the callback behaviour to perform when the mouse is clicked. The corresponding method also needs to be defined.

    LOGame >> toggleNeighboursOfCellAt: i at: j
        i>1
            ifTrue: [ (cells at: i - 1 at: j) toggleState ].
        i < self cellsPerSide
            ifTrue: [ (cells at: i + 1 at: j) toggleState ].
        j>1
            ifTrue: [ (cells at: i at: j - 1) toggleState ].
        j < self cellsPerSide
            ifTrue: [ (cells at: i at: j + 1) toggleState ]
    

    The method toggleNeighboursOfCellAt:at: toggles the state of the four cells to the north, south, west and east of cell (i, j). The only complication is that the board is finite, so we have to make sure that a neighboring cell exists before we toggle its state.

    Place this method in a new protocol called game logic. (Right-click in the protocol pane to add a new protocol.) To move (re-classify) the method, you can simply click on its name and drag it to the newly-created protocol (see Figure \(\PageIndex{1}\)).

    Drag a method to a protocol.
    Figure \(\PageIndex{1}\): Drag a method to a protocol.

    To complete the Lights Out game, we need to define two more methods in class LOCell this time to handle mouse events.

    LOCell >> mouseAction: aBlock
        ^ mouseAction := aBlock
    

    The method above does nothing more than set the cell’s mouseAction variable to the argument, and then answers the new value. Any method that changes the value of an instance variable in this way is called a setter method; a method that answers the current value of an instance variable is called a getter method.

    Setter/Getter convention. If you are used to getters and setters in other programming languages, you might expect these methods to be called setMouseAction and getMouseAction. The Pharo convention is different. A getter always has the same name as the variable it gets, and a setter is named similarly, but with a trailing ”:”, hence mouseAction and mouseAction:. Collectively, setters and getters are called accessor methods, and by convention they should be placed in the accessing protocol. In Pharo, all instance variables are private to the object that owns them, so the only way for another object to read or write those variables is through accessor methods like this one. In fact, the instance variables can be accessed in subclasses too.

    Go to the class LOCell, define LOCell >> mouseAction: and put it in the accessing protocol.

    Finally, we need to define a method mouseUp:. This will be called automatically by the GUI framework if the mouse button is released while the cursor is over this cell on the screen. Add the method LOCell >> mouseUp: and then Categorize automatically the methods.

    LOCell >> mouseUp: anEvent
        mouseAction value
    

    What this method does is to send the message value to the object stored in the instance variable mouseAction. In LOGame >> newCellAt: i at: j we created the block [self toggleNeighboursOfCellAt: i at: j ] which is toggling all the neighbours of a cell and we assigned this block to the mouseAction of the cell. Therefore sending the value message causes this block to be evaluated, and consequently the state of the cells will toggle.


    This page titled 3.8: Finishing the Game is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.