Friday, September 23, 2011

Arithmatic Tutor, updated with variable congrats messages

Problem: "To add variety to the interaction, modify your solution to exercise 5 so that it randomly chooses among four or five different messages when the student gets the right answer."

What it looks like:


The code was all the same except that instead of printing the hard-coded string "Right" if the user got the answer right, it printed answerAffirmation. This was a method that generated one of 4 random integers and mapped each integer to a different congrats message.

 if (userAnswer == programSolution) {
     println(answerAffirmation());
 }
 else {
     println("No, the correct answer is " + programSolution); 
 }

I used a switch statement. At first the method was buggy because the program thought I wasn't returning a result:

The problem was, even though having the strings map to one of four integers that I knew would be exhaustive, the program didn't realize that answers could only come from that random integer, so I must include a default string to get returned:

In reality the message "Grrrrreat!" won't get returned unless there's a bug with my method loops or with the rgen factory resource.


Sunday, September 18, 2011

Arithmetic Tutor Program (or, structuring and debugging loops)

Problem:

Write a program that poses a series of simple arithmetic problems for a student to answer.

Your program should meet these requirements:
• It should ask a series of five questions. As with any such limit, the number of questions should be coded as a named constant so that it can easily be changed.
• Each question should consist of a single addition or subtraction problem involving just two numbers, such as “What is 2 + 3?” or “What is 11 – 7?”. The type of problem—addition or subtraction—should be chosen randomly for each question.
• To make sure the problems are appropriate for students in the first or second grade, none of the numbers involved, including the answer, should be less than 0 or greater than 20. This restriction means that your program should never ask questions like “What is 11 + 13?” or “What is 4 – 7?” because the answers are outside the legal range. Within these constraints, your program should choose the numbers randomly.
• The program should give the student three chances to answer each question. If the student gives the correct answer, your program should indicate that fact in some properly congratulatory way and go on to the next question. If the student does not get the answer in three tries, the program should give the answer and go on to another problem.

(Roberts Ch 6, problem 7)

This was a fun problem to write, though debugging took a long time. Here was how I split up the problem, given the requirements. There are three major parts of this program:
1) Generate the arithmatic program.
2) Present it to the student and evaluate his answer
3) Stop the program after 5 presented problems.

These were my smaller steps:
* Generate two numbers (0-20) and an operator (comes from boolean true/false). Use random generator instance.
* Put them together and assess them so the program "knows" the correct answer
* If the result (“problemAnswer”) is not between 0-20, don't print anything and start again
* If it's between 0-20, then present the problem to the user. Declare "int userAnswer = readln(print the equation here)".
* To keep track of user attempts, start a local variable "countUserAttempts" and make it start at 1
* If problemAnswer == userAnswer, break from the loop (print that it's right) and start again
* If problemAnswer != userAnswer, then print it's wrong. Add +1 to the “countUserAttempts” variable and send them back thru the inner loop to try again.
* Let user submit a second answer. If it's right then print that it's correct and start again. If it's wrong then add +1 to the countUserAttempts variable (so now it’s three) and send them back thru.
* If it’s wrong again, then it will make countUserAttempts equal 4 so it will break from the inner “while” loop and roll down to the next argument and print the correct answer.

What it looks like:


/* File: ArithmaticTutor
 * This program shows a series of arithmetic problems to the user and evaluates his or her input.
 * In accordance with the problem specs, we only present components between 0 and 20, and make
 * sure that the answer (sum or difference) is between 0 and 20 as well before giving it to the user.
 * We make up a problem on the fly using 2 methods that each use a random generator instance;
 * one to generate the numbers to in our problem, and a second to decide whether the operator is plus 
 * or minus. There's also a method to collect the user's input. There are two count-keeping variables:
 * one, "countPresentedProblems," makes sure that we give the user only 5 problems (or 
 * however many are allowed by the "maxProblems" constant. The other, "countUserAttempts,"
 * which is inside the inner "while" loop, keeps track of user attempts to make sure 
 * he or she does only 3 attempts. If the user gets the answer right in 3 or fewer attempts, we print 
 * in 3 or fewer attempts, we print "Right!" and move on to the next problem. If not,
 * we just tell the right answer and move on.
 * 
 */
    import acm.program.*;
    import acm.util.RandomGenerator;

    public class ArithmaticTutor extends ConsoleProgram {
        public void run() {
            println("Welcome to Math Tutor! Prepare to be challenged.");
            int countPresentedProblems = 0; 
            while (countPresentedProblems < maxProblems) {
                // First we put together all the stuff needed for our problem and assess the first problem ourselves 
                int firstNumber = generateNumber(0,20); 
                int secondNumber = generateNumber(0,20); 
                boolean isPlusNotMinus = plusNotMinus();
                int programSolution = evaluateProblem(firstNumber, secondNumber, isPlusNotMinus);
                // Then we present it to the student (if the answer is between 0 and 20)
                if (programSolution > 0 && programSolution < 20){
                    countPresentedProblems +=1;
                    int userAnswer = getUserAnswer(firstNumber, secondNumber, isPlusNotMinus);
                    int countUserAttempts = 1;
                    while (countUserAttempts < 3 && userAnswer != programSolution) {
                        userAnswer = readInt ("Wrong. Try another answer:");
                        countUserAttempts += 1;
                    }
                    if (userAnswer == programSolution) {
                        println( "Right!");
                    }
                    else {
                        println("No, the correct answer is " + programSolution); 
                    }
                }
            }
         }
        private final static int maxProblems = 5;
  
        private RandomGenerator rgen = RandomGenerator.getInstance();
  
        private int generateNumber(int min, int max) {
            int number = rgen.nextInt(min, max);
            return number;
        }
        private boolean plusNotMinus() {
            return rgen.nextBoolean();
        }
        private int evaluateProblem (int firstNumber, int secondNumber, boolean plusNotMinus){
            if (plusNotMinus) {
                return firstNumber + secondNumber;
            }
            else {
                return firstNumber - secondNumber;
            }
        }
        private int getUserAnswer (int firstNumber, int secondNumber, boolean plusNotMinus){
            if (plusNotMinus) {
                return readInt("What is " + firstNumber + "+" + secondNumber + "?");
            }
            else {
                return readInt("What is " + firstNumber + "-" + secondNumber + "?");
            }
        } 
}
What made it hard: Keeping track of your loops. Poorly structured loops kept on causing bugs! For example there was a bug where if you entered 2 wrong answers in a row, it fell back to the “Wrong, the correct answer is [programAnswer]” part, even though it should wait till 3 wrong entries. The problem was that the part of the code that says “Wrong, the correct answer is [programAnswer]” was *inside* the "while" loop, so once we evaluated that it was wrong it would print the correct answer then still re-prompt for another attempt! Then I moved that “No, the correct answer is [correct answer]” part to outside the nearest loop, but then it started getting run for every submitted answer, even if they got it right! Here are my other bloopers, along with a brief explanations of what was wrong with the code when they appeared.