C-Quence – A Memory Game

In this article by Stuart Grimshaw, the author of the book Building Apple Watch Projects, we will be crafting an entertaining app. Adding code which uses basic Swift features that most developers will find familiar and will address some of the topics that face the developer in creating software for a platform that presents some unique challenges.

C-Quence will be a game that challenges players' ability to memorize a sequence of colors generated by the app.

It is a game to be played in short bursts rather than prolonged activity, as one of the first things that become clear when using a physical device is that the watch is unsuited to tasks that take more than a short period of time to complete. We will keep this in mind as we look at the top-level design of this app.

(For more resources related to this topic, see here.)

Bear in mind that although this is a very modest app in terms of the amount of coding intends to bring it to completion, we still want to adhere to what some refer to as best practice (and others prefer to think of this as simply learning from others' mistakes without schadenfreude).

The code presented here will reside fully on the watch, needing no support from the iPhone companion app.

GameLogic

Although, we have created the GameLogic.swiftfile, we have not actually created the class yet (this is different from Objective C).

Create the GameLogic class

Add the following code, below the import Foundation statement that is part of the template:

import Foundation

classGameLogic {

}

Plan the class

We will create a class that will encapsulate the code that deals with the game itself in isolation from the user interface. The GameLogic class doesn't need to know anything about interactions with the user. This is something that will be taken care of by the InterfaceController class. So, let's first think about what we will need the class to do so that we can start to plan which methods we will need to be implemented.

We need the class to do the following:

  • Creating and maintaining a sequence of colors and adding a random color to it when required
  • Evaluating whether a player's tap on a color is a correct answer
  • Providing information as to whether the game is still being played or finished
  • Clearing the data that collects during a game in preparation for a new round

From these methods, we can estimate that we will need to maintain at least one variable:

  • A sequence property, which will be an ordered collection of colors

Create the class's interface

Setting out our requirements as this has almost fully defined what the outside world needs the code to do. Not how, it's true, but that comes a little later.

We have effectively defined the class's external interface (this is not to be confused with the app's user interface that is something quite different) through which other parts of the app will communicate with it.

Defining some enums

In order to keep the code easy to read and safe to use, we will define some enums. We need to do this outside of the class itself because the InterfaceController class will also need access to them. Add the following code directly after the import statement but before the class definition:

import Foundation

enum Color {
case Red, Yellow, Blue, Green
}
enum GuessResult {
case GuessCorrect, GuessWrong, GuessComplete
}

classGameLogic {

}

Traditionally, enums have been a way to give names to integer values to make them more readable, but Swift has gone several steps further and dispensed with the idea that an enum needs some underlying numerical value. If you declare a variable's type to be an enum type, such as the previous Color, the compiler will restrict you to these values, .Red and so on, and these values only. A method that is declared to return Color will only return Color and not some arbitrary integer (other convenient benefits include the fact that a switch statement needs no default once all the enum values are dealt with, which we'll see later).

So, we now have four Color values, as we would expect, but why the third GuessResult value, GuessComplete?

When InterfaceController asks the class to evaluate the player's answer, we can provide one of three possible scenarios; the guess is correct, but the sequence of guesses is not yet complete—the guess is either wrong or it is correct and the sequence has been correctly completed. Thus, we save ourselves an extra call from the interface asking whether the guessed sequence is complete or not.

Stub the methods

We can now stub the methods we'll need, which means we'll create them. In some places, we'll add the placeholder code to avoid complier error messages (often, we need to stub some actual functionality in the methods, but we do not need to do this here.)

Functions or methods?

Without going into an exhaustive definition of functions and methods worthy of a computer science degree course, it suffices to say here that when a function is coded as part of a class, it is referred to as a method of this class. If a function is defined outside of a class, it is called a function. In this app, we have no functions outside of classes.

Add the following code inside (!) the GameLogic class:

Extend the sequence

First, add a method that will extend the colors sequence by one randomly generated color:

funcextendSequence(){
   }

Evaluate

Next, we will need a method that evaluates whether the player has guessed correctly or not. We include a stubbed return value (it will be replaced later) in order to keep the compiler from warning us (with a very dramatic-looking red circle and exclamation mark) that the method should return a GuessResult value:

funcevaluateColor(color: Color) ->GuessResult{
return .GuessCorrect
}

Clear

Finally, when the game restarts, we'll need to clear the game of any variable that we have set in the course of the play:

funcclearGame(){
   }

Define properties

We will need a variable to store the sequence. The obvious type here would be an Array of the Color values. We will initialize it to be empty at the beginning of the app's lifecycle. Add the following code above the extendSequence function:

classGameLogic {

var sequence: [Color] = []

funcextendSequence(){F

Our GameLogic class is now a reflection of the requirements that we laid out earlier, and we can be confident that we have constructed a rugged frame to which we can add the logic that will make it perform its duties.

Check your code

Your code will now look like this:

import Foundation

enum Color {
case Red, Yellow, Blue, Green
}
enum GuessResult: Int {
case GuessCorrect, GuessWrong, GuessComplete
}

classGameLogic {

var sequence: [Color] = []

funcextendSequence(){
   }

funcevaluateColor(color: Color) ->GuessResult{
return .GuessCorrect
   }

funcclearGame(){
   }
}

Check that there are no compiler errors or warnings.

All good? Splendid! Onto the Interface Controller now...

Summary

In this article, you laid the groundwork for the rest of the app. You did so in a way that gives you the best possible chance of producing code that is robust, easy to maintain, and easy to comprehend when you return to it after six months on a surfing holiday.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Building Apple Watch Projects

Explore Title