## 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.

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 […]

```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
"""
import sys

from random import randint

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)
try:
except (IndexError, ValueError):
except (IndexError, ValueError):
op_count = 2
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} = "

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)
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)
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;
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 ” = “))
(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 } = ”
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));
\$correct = array_sum(\$nums);
echo "{\$nums} + {\$nums} = \n";
\$handle = fopen ("php://stdin","r");
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) {

srand ( time(NULL) );

int1 = rand() % 256 + 1;

int2 = rand() %256+1;

switch (questionType)

{

case 1:

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

break;

case 2:

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

break;

case 3:

if(int1<int2){

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

}

else{

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

}
break;

}

scanf("%d",&input);

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

```