Glancing over the cl-quiz site, more specifically the examples for my own script, I noticed I left a major bug in one of my macros. It was leaking when I tried to remove the 0s from division problems.
Here's the fixed version, along with editing the wording to make one of the catpchas make sense being continuous (sort of) and removing that bogus dependency.
Apologies for not catching this the first time
~~~~~~~~~~
;;;;;;;;;;;;;; ; ; CAPTCHA ; ------- ; A Completely Automated Public Turing test ; to tell Computers and Humans Apart ; generator. ; Generates simple written arithmatic problems. ; ; AUTHOR: Joseph Abrahamson ; YEAR: 2006 ; ;;;;;;;;;;;;;;
(defpackage :captcha (:use :cl) (:export #:generate-captcha))
(in-package :captcha)
(defconstant +numbermax+ 10)
(defvar *query-strings* '((* "what is ~r times ~r?" (gennumber gennumber)) (* "what is the product of ~r and ~r?" (gennumber gennumber)) (* "what is the area of a ~r by ~r rectangle?" (gennumber gennumber)) (* "If you have ~r card~:p in a deck, then give the deck away, how many cards do you have?~*" (gennumber 0)) (+ "what is ~r plus ~r?" (gennumber gennumber)) (+ "what is the sum of ~r, ~r, and ~r?" (gennumber gennumber gennumber)) (+ "if you have ~r apricot~:p and buy ~r more, how many do you have?" (gennumber gennumber)) (- "what is ~r less ~r?" (gennumber gennumber)) (- "what is the difference between ~r and ~r?" (gennumber gennumber)) (- "if you have ~r dollar~:p but owe ~r, you effectively have how many?" (gennumber gennumber)) (/ "what is ~r over ~r?" (gennumber gennumber)) (/ "what is the quotient of ~r and ~r" (gennumber gennumber)) (/ "if you could split ~r watch~:*~[es~;~:;es~] into ~r equal group~:p, how many are in each group?" (gennumber gennumber)))) ; Etc...
(defun gennumber (&optional (max +numbermax+) (min 0)) (+ (random (- max min)) min))
(defclass query () ((text :initarg :text :initform (error "Query must have text.") :documentation "FORMAT string to convert to captcha query.") (string-types :initarg :string-types :initform (error "Query must have FORMAT args.") :documentation "Arguments of generator functions which will produce values for captcha.") ))
(defclass query+ (query) ()) (defclass query- (query) ()) (defclass query* (query) ()) (defclass query/ (query) ())
;; DATABASE AND SUCH
(defun generate-query-database (&optional (data *query-strings*)) (loop for query in data collect (let ((type (first query)) (string (second query)) (args (third query))) (make-instance (intern (concatenate 'string "QUERY" (symbol-name type))) :text string :string-types args))))
(defvar *db* (generate-query-database))
;; PERFORM QUERY ; ; Returns a string to print and a string to be compared against as the answer. (defgeneric perform (q &optional string-args) (:documentation "PERFORM analyzes passed query and generates a questionform and its cooresponding answerform."))
(defmacro once-only ((&rest names) &body body) ;; Peter Seibel's Implementation of ONCE-ONLY ;; from PCL. (let ((gensyms (loop for n in names collect (gensym)))) `(let (,@(loop for g in gensyms collect `(,g (gensym)))) `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n))) ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g))) ,@body)))))
(defmacro perform-values (q fn string-args) (once-only (q fn string-args) `(values (apply #'format (append (list nil (slot-value ,q 'text)) ,string-args)) (format nil "~a" (reduce ,fn ,string-args)))))
(defmethod perform :around ((q query) &optional string-args) (call-next-method q (or string-args (mapcar (lambda (x) (typecase x (list (apply (car x) (cdr x))) (symbol (if (fboundp x) (funcall x) (error "Symbol ~A is not bound to function. Must be removed from args list of query ~a" x q))) (t x))) (slot-value q 'string-types)))))
(defmethod perform ((q query/) &optional (string-args ())) ; Avoid division by 0. (perform-values q #'/ (substitute-if (gennumber +numbermax+ 1) #'zerop string-args)))
(defmethod perform ((q query*) &optional (string-args ())) (perform-values q #'* string-args))
(defmethod perform ((q query+) &optional (string-args ())) (perform-values q #'+ string-args))
(defmethod perform ((q query-) &optional (string-args ())) (perform-values q #'- (sort (copy-list string-args) #'>)))
;; GENERATE-CAPTCHA (defun generate-captcha (&key type (db *db*)) "Produces a random CAPTCHA from DB. If TYPE is supplied, only CAPTCHAs of that type may be returned." (let ((db (remove-if (if type (lambda (x) (not (eq (type-of x) type))) (constantly nil)) db))) (perform (elt db (random (length db))))))
~~~~~~~~~~
-- ~ja.
Tel writes:
(defvar *query-strings* '((* "what is ~r times ~r?" (gennumber gennumber)) (* "what is the product of ~r and ~r?" (gennumber gennumber)) (* "what is the area of a ~r by ~r rectangle?" (gennumber gennumber)) (* "If you have ~r card~:p in a deck, then give the deck away, how many cards do you have?~*" (gennumber 0)) (+ "what is ~r plus ~r?" (gennumber gennumber)) (+ "what is the sum of ~r, ~r, and ~r?" (gennumber gennumber gennumber)) (+ "if you have ~r apricot~:p and buy ~r more, how many do you have?" (gennumber gennumber)) (- "what is ~r less ~r?" (gennumber gennumber)) (- "what is the difference between ~r and ~r?" (gennumber gennumber)) (- "if you have ~r dollar~:p but owe ~r, you effectively have how many?" (gennumber gennumber)) (/ "what is ~r over ~r?" (gennumber gennumber)) (/ "what is the quotient of ~r and ~r" (gennumber gennumber)) (/ "if you could split ~r watch~:*~[es~;~:;es~] into ~r equal group~:p, how many are in each group?" (gennumber gennumber))))
Really guys, these are no captcha. Have a look at the student program in PAIP (norvig.com). It could easily solve these kind of problems, with very little or no added rules and code.
Why remove the division by 0? On the contrary, this is something that could discriminate between a human and an computer, more than "what's the difference between three and two". Even if some human would answer: EDIV0.
While you make a good point about it being much more difficult for programs to catch the DIV0, it's also hard to get a human to correctly write a response to it.
Mathematically, 1/0 is meaningless. The best you can get is by taking limits to get answers like "does not exist" or "infinite", but you couldn't expect humans to answer that any better without a disclaimer (which could then, feasibly, be picked up by a program as well).
Sure, it could be done, but is it worth it? Maybe.
I do agree though -- my catpcha are sad -- but it'd be very much possible to write some more difficult ones. The deck one was a halfhearted attempt at such and is hardly the upper limit. I tried to make sure this was very easy to do, but didn't focus on actually doing it. My bad.
All that being said, I'm not going to defend my code too much. It's very simplistic and I'm not good enough to push it too far past that within a reasonable amount of time. However, I'd love to see a more complete answer.
On 5/3/06, Pascal Bourguignon pjb@informatimago.com wrote:
Tel writes:
(defvar *query-strings* '((* "what is ~r times ~r?" (gennumber gennumber)) (* "what is the product of ~r and ~r?" (gennumber gennumber)) (* "what is the area of a ~r by ~r rectangle?" (gennumber gennumber)) (* "If you have ~r card~:p in a deck, then give
the deck away,
how many cards do you have?~*" (gennumber 0)) (+ "what is ~r plus ~r?" (gennumber gennumber)) (+ "what is the sum of ~r, ~r, and ~r?" (gennumber gennumber gennumber)) (+ "if you have ~r apricot~:p and buy ~r more,
how many do you have?"
(gennumber gennumber)) (- "what is ~r less ~r?" (gennumber gennumber)) (- "what is the difference between ~r and ~r?" (gennumber gennumber)) (- "if you have ~r dollar~:p but owe ~r, you
effectively have how many?"
(gennumber gennumber)) (/ "what is ~r over ~r?" (gennumber gennumber)) (/ "what is the quotient of ~r and ~r" (gennumber gennumber)) (/ "if you could split ~r watch~:*~[es~;~:;es~]
into ~r equal
group~:p, how many are in each group?" (gennumber gennumber))))
Really guys, these are no captcha. Have a look at the student program in PAIP (norvig.com). It could easily solve these kind of problems, with very little or no added rules and code.
Why remove the division by 0? On the contrary, this is something that could discriminate between a human and an computer, more than "what's the difference between three and two". Even if some human would answer: EDIV0.
-- __Pascal Bourguignon__ http://www.informatimago.com/
Pour moi, la grande question n'a jamais été: «Qui suis-je? Où vais-je?» comme l'a formulé si adroitement notre ami Pascal, mais plutôt: «Comment vais-je m'en tirer?» -- Jean Yanne
-- ~ja.
Pascal Bourguignon wrote:
Really guys, these are no captcha. Have a look at the student program in PAIP (norvig.com). It could easily solve these kind of problems, with very little or no added rules and code.
Okay, but what would you suggest? By definition, a CAPTCHA has to be something that can be solved by an algorithm. The best one can do, it seems, is add enough "noise" to the question to make it difficult to parse.
-Stuart
Stuart Sierra writes:
Pascal Bourguignon wrote:
Really guys, these are no captcha. Have a look at the student program in PAIP (norvig.com). It could easily solve these kind of problems, with very little or no added rules and code.
Okay, but what would you suggest? By definition, a CAPTCHA has to be something that can be solved by an algorithm. The best one can do, it seems, is add enough "noise" to the question to make it difficult to parse.
Not at all. A CAPTCHA musn't be easily resolvable by an algorithm. A good CAPTCHA needs at least 90 IQ points.
http://www.captcha.net/ http://en.wikipedia.org/wiki/Captcha
Mori et al. published a paper in IEEE CVPR'03 detailing a method for defeating one of the most popular CAPTCHAs, EZ-Gimpy, which was tested as being 92% accurate. The same method was also shown to defeat the more complex and less-widely deployed Gimpy program with an accuracy of 33%. However, the existence of implementations of their algorithm in actual use is indeterminate at this time.
For example a better captcha would be:
You've got tree apples, you eat one. How many oranges are you left with?
If you get as answer 2, you know you've got a program, or at least someone who cannot distinguish an apple from an orange, and who couldn't anyway answer: "What oranges?"
But even there, a program like shldru would be able to ask "What oranges?".
So the captcha would have to give a Turing Test, asking _several_ questions of different kinds, to be able to ascertain the essence of the "user".
Let me see. If I ask you this:
"Hiroshi Nohara was driving on the Road 66. He always felt uneasy when driving abroad. Can you explain me why?"
Well, the information needed to give an explanation is available on the Internet. Only it's not present in OpenCyc, and browsing the web to gather it, and making the right inferences, would take more time than what a human would need to answer.
As a human, can you answer that question?
What I'm trying to do here, is to build questions which are easy to answer, given common knowledge which is obvious for human, but which is not so well formalized and accessible in machine readable form.