Contents

Kata 6

Setup instructions

  • Download the starting folder for this Kata.
  • Open a Command Prompt window and navigate to the starting folder using cd.
  • Open the starting folder for this Kata in VSCode.

Overview

In this Kata we’ll continue to build on the quiz game by splitting it up into methods.

If you get stuck, refer to the page on Debugging in the Appendix for help.

Kata instructions

The quiz game currently has a couple of shortcomings. The main issue is that the code has become quite long and difficult to understand. There is also a lot of repetition in the logic for asking questions.

Scan through the code and you’ll notice there are a few distinct sections with different responsibilities. Roughly speaking these are:

  • Greet the user
  • Ask for the user’s age
  • Validate the user’s age
  • Ask the quiz questions
  • Decide if the user has won or lost the game

Let’s create methods to do these things, then move the existing code into the methods.

Greet the user

At the top of the program, you should have the following lines:

Console.WriteLine("What's your name?");
string name = Console.ReadLine();
Console.WriteLine("Hello " + name);

Let’s move this code into it’s own method. Above the method Main, create a method called GreetUser, like so:

static void GreetUser() {
}

Then copy and paste the existing code into GreetUser, so it looks like this:

static void GreetUser() {
    Console.WriteLine("What's your name?");
    string name = Console.ReadLine();
    Console.WriteLine("Hello " + name);
}

This is our first method! One thing you may have noticed is GreetUser has the word static as part of it’s signature. The static keyword is beyond the scope of this Kata - it will be explained in later Katas. Only understand it’s necessary for the methods to work in this Kata.

Remember to indent your code correctly using the tab key too!

After that, you may have realised that all the code we’ve written so far has been inside a method all along - called Main. Let’s use GreetUser inside main.

Replace the code we just copied into GreetUser with a call to GreetUser, like so:

GreetUser();

Ask for the user’s age

After GreetUser, you should see some code that asks for the user’s age, that looks like this:

Console.WriteLine("Please tell me how old you are in years!");
int ageInYears = Convert.ToInt32(Console.ReadLine());

We want to move this code into it’s own method.

Create another method called GetUsersAgeInYears like so:

static int GetUsersAgeInYears() {
    Console.WriteLine("Please tell me how old you are in years!");
    int ageInYears = Convert.ToInt32(Console.ReadLine());
    return ageInYears;
}

Make sure you create the method outside of other methods - so outside Main and outside GreetUser. For instance, this is wrong:

static void GreetUser() {
    Console.WriteLine("What's your name?");
    string name = Console.ReadLine();
    Console.WriteLine("Hello " + name);

    static int GetUsersAgeInYears() {
        Console.WriteLine("Please tell me how old you are in years!");
        int ageInYears = Convert.ToInt32(Console.ReadLine());
        return ageInYears;
    }
}

Because GetUsersAgeInYears exists entirely inside GreetUser. Methods should be laid out like this:

static void GreetUser() {
    Console.WriteLine("What's your name?");
    string name = Console.ReadLine();
    Console.WriteLine("Hello " + name);
}

static int GetUsersAgeInYears() {
    Console.WriteLine("Please tell me how old you are in years!");
    int ageInYears = Convert.ToInt32(Console.ReadLine());
    return ageInYears;
}

GetUsersAgeInYears has the int return type. We use the return keyword to give the variable ageInYears back to the caller.

Call GetUsersAgeInYears from Main, replacing the existing code that asks for the user’s age:

GreetUser();
int ageInYears = GetUsersAgeInYears();
bool isAgeValid = ageInYears >= 0;

Validate the user’s age

The code structure should start to be clearer now. In GreetUser we politely say hello to the user. Next, in GetUsersAgeInYears, we ask for the users age. After that we validate the user’s age with the greater than or equal to operator.

Now we can create a method which performs all the age validation logic. Create a new method, GetIsAgeValid. Copy and paste the code that checks if the user’s age is valid into it:

static bool GetIsAgeValid() {
    bool isAgeValid = ageInYears >= 0;
    return isAgeValid;
}

Notice that we need to get the user’s age in years (ageInYears) in order to perform this validation.

We can’t access the existing ageInYears variable because it is out of ‘scope’ for GetIsAgeValid. Variables can only be used inside of the curly brace block they are declared in.

Instead, we need to amend the method signature to ask for ageInYears as an argument:

static bool GetIsAgeValid(int ageInYears) {
    bool isAgeValid = ageInYears >= 0;
    return isAgeValid;
}

Call GetIsAgeValid from Main:

GreetUser();
int ageInYears = GetUsersAgeInYears();
bool isAgeValid = GetIsAgeValid(ageInYears);

Ask the quiz questions

There are a couple of if statements that ask questions to the user. Let’s create a method for asking the first question.

Create a new method, AskQuestionOne. As before, copy the code for question one into it, like so:

static void AskQuestionOne() {
    Console.WriteLine("What year did the Titanic sink?");
    string answerOne = Console.ReadLine();

    if(answerOne == "1912") {
        Console.WriteLine("Correct!");
    }
    else {
        isWinning = false;
        Console.WriteLine("Incorrect!");
    }
}

Notice that isWinning is being assigned inside this method. This is a problem because isWinning doesn’t exist in the context of this method - we say isWinning is out of scope of the method.

You might think that one way to fix this is to pass isWinning as an argument. Then we can use it like a variable right?

True, you could do that. However Even if we pass isWinning as an argument, we can’t change the value of it - that’s one of the rules of C#.

To get around this, amend the signature of AskQuestionOne to return a bool:

static bool AskQuestionOne()

Next, return true or false from within the if blocks depending on if the user got the answer right or wrong:

if(answerOne == "1912") {
    Console.WriteLine("Correct!");
    return true;
}
else {
    Console.WriteLine("Incorrect!");
    return false;
}

Now when the return keyword is reached, it means that the method stops executing then and there. This means that the else block is unnecessary. Amend the code like so:

if(answerOne == "1912") {
    Console.WriteLine("Correct!");
    return true;
}

Console.WriteLine("Incorrect!");
return false;

So, if the user got the answer correct, then return true. Otherwise, the if statement isn’t executed and the line after it is - which will return false. These two are logically the same, but the second way requires slightly less code, which is nicer.

Call AskQuestionOne like so:

bool isWinning = true;

isWinning = AskQuestionOne();

Now repeat the process for question two, with a new method, AskQuestionTwo. Then, call from Main:

isWinning = AskQuestionOne();
isWinning = AskQuestionTwo();

Squashing bugs

There is now an error - or ‘bug’ in the program. Before, the user would lose the game if they got a single question wrong. Now, they’ll only lose if the last question returns false.

This is because AskQuestionTwo will overwrite the value of isWinning. Say that isWinning is false after AskQuestionOne. If AskQuestionTwo returns true, isWinning will become true - and the user will win the game despite question one being incorrect.

To fix this, create two new variables to store the result of AskQuestionOne and AskQuestionTwo. isWinning can be deleted.

bool questionOneCorrect = AskQuestionOne();
bool questionTwoCorrect = AskQuestionTwo();

Next, amend the following if statement to use these two variables:

if (questionOneCorrect == true && isQuestionTwoCorrect == true)

Finally, create a new method to put the entire quiz question code into:

static void DoQuizQuestions() {
    bool questionOneCorrect = AskQuestionOne();
    bool questionTwoCorrect = AskQuestionTwo();

    if (questionOneCorrect == true && questionTwoCorrect == true) {
        Console.WriteLine("You won!");
    }
    else {
        Console.WriteLine("You lost!");
    }
}

In Main, call DoQuizQuestions after the if statement that checks if the user’s age is valid. Main should look something like:

static void Main(string[] args)
{
    GreetUser();
    int ageInYears = GetUsersAgeInYears();
    bool isAgeValid = GetIsAgeValid(ageInYears);

    if(isAgeValid == true) {
        DoQuizQuestions();
    } else {
        Console.WriteLine("Age is invalid...Sorry!");
    }
}

Now look at that! When we started, Main was very long and it would take a few minutes of reading to figure out what it was doing. Now the overall structure of the program is much clearer. The code is much simpler to understand, and the responsibilities of each part of the code are more clearly defined.

Finished!

You now know how to write functions (methods) in C#! You’ve seen how important it is to split big programs into smaller, more manageable chunks using functions.

Try on your own…

There is a lot of repetition with the code to ask questions. It’s possible to make a general method for asking questions instead of using AskQuestionOne, AskQuestionTwo, and so on.

Try and create a more general method that is responsible for asking questions to the user.

The method signature ought to look like:

static bool AskQuestion(string question, string correctAnswer)
  • The method needs to ask a question, read the response, and check if the response is correct
  • If the user’s answer is correct it should return true, otherwise false.
Copyright Mikiel Agutu 2019