Arithmetic Drill

December 31, 2010

Children just learning the basic facts of arithmetic need repetitious drill to certify their knowledge, something that a computer does well. Toy stores purvey many brightly-colored plastic boxes that do the job, at a price. Today’s exercise asks you to do the same, minus the plastic box. Consider the following dialog, where the computer’s output is in roman and the child’s response is in italic:

4 + 4 = 8
Right!
8 + 3 = 12
Wrong, try again!
8 + 3 = 11
Right!
9 + 4 = 13
Right!
7 + 8 = ?
15
9 + 5 = 14
Right!
8 + 0 = ^Z
Goodbye!

After the drill program presents a problem, the child either enters his answer, asks for help by entering a question mark, or quits by entering an end-of-file. Correct answers are rewarded, incorrect answers cause the problem to be asked again.

Your task is to write a program that drills children on their addition facts. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Advertisement

Pages: 1 2

16 Responses to “Arithmetic Drill”

  1. […] Praxis – Arithmetic Drill By Remco Niemeijer In today’s Programming Praxis exercise, our goal is to write a program that allows children to test their […]

  2. My Haskell solution (see http://bonsaicode.wordpress.com/2010/12/31/programming-praxis-arithmetic-drill/ for a version with comments):

    import Control.Monad
    import System.Random
    import Text.Printf
    
    drill :: Maybe Int -> IO ()
    drill n = play n =<< liftM2 (,) rnd rnd
              where rnd = randomRIO (1, maybe 10 id n)
    
    play :: Maybe Int -> (Int, Int) -> IO ()
    play n (a,b) = printf "%d + %d = " a b >>
                   catch getLine (\_ -> return "quit") >>= \s -> case s of
        "quit" -> putStrLn "Goodbye!"
        "?"    -> print (a + b) >> drill n
        x      -> if x == show (a + b) then putStrLn "Right!" >> drill n
                  else putStrLn "Wrong, try again!" >> play n (a,b)
    
    main :: IO ()
    main = drill Nothing
    
  3. Bogdan Popa said

    My Python 3 solution:

    #/usr/bin/env python3
    """Usage: ./drill [op_count [max_addend]]
    """
    import sys
    
    from random import randint
    
    def drill(max_addend, op_count):
        ops = [randint(0, max_addend) for i in range(op_count)]
        question = " + ".join(str(op) for op in ops) + " = "
        try:
            response = input(question)
            while response != str(sum(ops)):
                if response == "?":
                    print(question + str(sum(ops)))
                    return True
                print("Wrong, try again!")
                response = input(question)
        except EOFError:
            print("\nGoodbye!")
            return False
        print("Right!")
        return True
    
    def main(args):
        try:
            op_count = int(args[1])
            try:
                max_addend = int(args[2])
            except (IndexError, ValueError):
                max_addend = 10
        except (IndexError, ValueError):
            op_count = 2
            max_addend = 10
        while drill(max_addend, op_count):
            pass
        return 0
    
    if __name__ == "__main__":
        sys.exit(main(sys.argv))
    
  4. slabounty said

    Here’s a simple ruby version:

    require 'readline'
    
    while true
        a = rand(10)
        b = rand(10)
        prompt = "#{a} + #{b} = "
        while line = Readline.readline(prompt, true)
    
            answer = line.strip
    
            if answer == "?"
                puts "#{a+b}"
                break
            elsif line.strip == "#{a+b}"
                puts "Right!"
                break
            else
                puts "Wrong, try again!"
            end
        end 
    
        if line == nil
            puts "\nGoodbye!"
            break
        end
    end 
    
  5. Graham said

    My Python solution:

    #!/usr/bin/env python
    
    import random
    
    
    def drill(n):
        x, y = random.randrange(n), random.randrange(n)
        answer = int(raw_input("{0} + {1} =\t".format(x, y)))
        while x + y != answer:
            print "Wrong, try again!"
            answer = int(raw_input("{0} + {1} =\t".format(x, y)))
        print "Right!"
        return
    
    if __name__ == "__main__":
        import sys
        n = 10 if len(sys.argv) == 1 else int(sys.argv[1])
        try:
            while True:
                drill(n)
        except KeyboardInterrupt:
            print "\nGoodbye!"
    
  6. Graham said

    Sorry, only handles keyboard interrupts. This one also handles EOF:

    
    #!/usr/bin/env python
    
    import random
    
    def drill(n):
        x, y = random.randrange(n), random.randrange(n)
        answer = int(raw_input("{0} + {1} =\t".format(x, y)))
        while x + y != answer:
            print "Wrong, try again!"
            answer = int(raw_input("{0} + {1} =\t".format(x, y)))
        print "Right!"
        return
    
    if __name__ == "__main__":
        import sys
        n = 10 if len(sys.argv) == 1 else int(sys.argv[1])
        try:
            while True: drill(n)
        except (KeyboardInterrupt, EOFError):
            print "\nGoodbye!"
    
  7. Jebb said

    In C:

    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define UPPER_LIMIT 10
    #define RANDOM_NUMBER ((int)(random() % UPPER_LIMIT))
    #define MSG_RIGHT "Right!\n"
    #define MSG_WRONG "Wrong, try again!\n"
    #define MSG_BYE "\nGoodbye!\n"
    
    int main()
    {
    	int guess, a, b, guessing;
    	char buffer[11];
    	while (a = RANDOM_NUMBER, b = RANDOM_NUMBER, guessing = 1) {
    		if ((a == 0) && (b == 0))
    			break;
    		while (guessing) {
    			if (feof(stdin)) {
    				printf(MSG_BYE);
    				return 0;
    			}
    			printf("%d + %d = ", a, b);
    			if (fgets(buffer, 10, stdin) == NULL)
    				break;
    			guess = (int)strtol(buffer, NULL, 0);
    			printf((guessing = guess != a + b) ? MSG_WRONG : MSG_RIGHT);
    		}
    	}
    }
    
  8. Axio said

    ;; Hum, my comment does not show up (even with refreshes), but the system says I already posted it!
    (define (drill)
      (call/cc
        (lambda(exit)
          (let loop ()
            (and (call/cc
                   (lambda (break)
                     (let ((a (random-integer 10))
                           (b (random-integer 10)))
                       (if (or (zero? a) (zero? b))
                         (break #f))
                       (map display `(,a ” + ” ,b ” = “))
                       (let ((v (read)))
                         (cond
                           ((eof-object? v) (exit ‘bye))
                           ((not (number? v)) (break #f))
                           ((= v (+ a b)) (display ‘yeah!))
                           (else (display ‘booh)))))))
                 (newline))
            (loop)))))

  9. ruby said

    My ruby version:
    Not sure what is meant by handling an end-of-file in the terminal. Just learning Ruby, so probably not the best style.

    def quiz (num1=rand(10), num2=rand(10))
    while true
    print “#{ num1 } + #{ num2 } = ”
    answer = STDIN.gets.strip
    if answer == “?”
    puts “#{ num1 + num2 }”
    quiz
    elsif answer.to_i == num1 + num2
    puts “Right!”
    quiz
    else
    puts “Wrong, try again”
    end
    end
    end

    quiz

  10. roti said

    @Axio: does all that call/cc really bring some benefits or is it just to show some different way it could be done?

  11. Axio said

    Roti: it’s just the way to code it that seemed clear and natural to me. One might argue that call/cc is quite overkill here, as I just use it instead of exceptions to simulate “break” (as in a “while loop”). But it’s just an exit continuation, so this is a quite common pattern that keeps code understandable.
    So, whether it is a benefit or not will probably be subjective here.

  12. PHP version

    
    function quiz(){
        $nums = array(mt_rand(0,10),mt_rand(0,10));
        $answer = FALSE;
        $correct = array_sum($nums);
        while($answer !== $correct) {
            echo "{$nums[0]} + {$nums[1]} = \n";
            $handle = fopen ("php://stdin","r");
            $answer = (int) trim(fgets($handle));
            if ($answer !== $correct) {
                echo "Wrong, try again!\n";
            }
        }
        echo "Right!\n";
        quiz();
    }
    quiz();
    
    
  13. marty said

    golf score: 305
    (require(lib “1.ss” “srfi”))(let p()(let l((n(random 10))(m(random 10)))(printf “~a + ~a = ” n m)(flush-output)(let*((s(read))(z(reduce(lambda(x a)(if(eq?(car x)s)x a))0`((0 “Wrong, try again!”,l,n,m)(,(+ n m) “Right!”,p)(?,(+ n m),p)(^Z “Goodbye!”,void)))))(displayln(cadr z))(apply(caddr z)(cdddr z)))))

  14. Jeff said

    C Version:

    #include

    #include

    #include

    int createQuestion(int questionType); /*question type decides if the question will be + or -. It returns 0 if answered correct or 1 if wrong.*/

    main()

    {

    int a = 0;

    int result = 0;

    printf(“\nAnswer the questions. If you get 5 questions wrong the game ends. Try to answer as many as possible.\n”);

    printf(“Press 1 for addition problems or 2 for subtraction problems, or 3 for both: “);

    scanf(“%d”,&a);

    while(result<5){

    switch (a)

    {

    case 1:

    result = result +createQuestion(1);

    printf("\nresult:%d\n",result);
    break;

    case 2:

    result = result +createQuestion(2);

    break;
    case 3:

    result = result +createQuestion(3);
    break;

    }//switch

    }//while

    }//main

    int createQuestion(int questionType) {

    int int1, int2,input, answer;

    srand ( time(NULL) );

    int1 = rand() % 256 + 1;

    int2 = rand() %256+1;

    switch (questionType)

    {

    case 1:

    printf("\n%d+%d= ",int1,int2);

    answer = int1+int2;
    break;

    case 2:

    printf("\n%d-%d= ",int1,int2);

    answer = int1-int2;
    break;

    case 3:

    if(int1<int2){

    printf("\n%d+%d= ",int1,int2);

    answer = int1+int2;

    }

    else{

    printf("\n%d-%d= ",int1,int2);

    answer = int1 – int2;

    }
    break;

    }

    scanf("%d",&input);
    printf("\n%d\n",answer);

    if(input==answer)

    return 0;

    else

    return 1;

    }

  15. marty said

    I got rid the reduce by using racket’s built in assq function, eliminating the need for srfi/1.
    New golf score for my racket scheme version: 238
    (let p()(let l((n(random 10))(m(random 10)))(printf”~a + ~a = “n m)(flush-output)(let*((s(read))(z(assq s `((,(+ n m)”Right!”,p)(?,(+ n m),p)(^Z”Goodbye!”,void)(,s”Wrong, try again!”,(lambda()(l n m)))))))(displayln(cadr z))((caddr z)))))

  16. Rainer said

    Possible solution REXX

    
    p = 1
    op = '+'
    
    do forever
        x = random(copies('9',p))
        y = random(copies('9',p))
        calc = x op y
        interpret 'erg='calc
        ans = '*'
        do until ans == erg | ans == '?'
            say calc'='
            parse pull ans
            select
                when ans == '?' then say erg
                when \ datatype(ans,'N') then exit
                when ans == erg then say 'right!'
                otherwise say 'wrong, try again!'
            end 
        end
    end
    
    exit
    
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: