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.
[…] 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 […]
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 NothingMy 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))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 endMy 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!"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!"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); } } };; 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)))))
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
@Axio: does all that call/cc really bring some benefits or is it just to show some different way it could be done?
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.
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();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)))))
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;
}
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)))))
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