Arithmetic Drill

December 31, 2010

Here is drill; the optional argument gives the maximum number for an addend, and defaults to 10:

(define (drill . arg)
  (let ((n (if (null? arg) 10 (car arg))))
    (let loop ((n1 (randint n)) (n2 (randint n)))
      (for-each display `(,n1 " + " ,n2 " = "))
      (let ((s (read)))
        (cond ((eof-object? s)
                (display "Goodbye!") (newline))
              ((not (number? s))
                (display (+ n1 n2)) (newline)
                (loop (randint n) (randint n)))
              ((= (+ n1 n2) s)
                (display "Right!") (newline)
                (loop (randint n) (randint n)))
              (else (display "Wrong, try again!")
                    (newline) (loop n1 n2)))))))

Scheme handles this better than some other languages because read can handle both numbers and text.

We used randint from the Standard Prelude. You can see the code at http://programmingpraxis.codepad.org/tjoTFXBo.

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 comment