I’ve added some stuff to my word scrambler.
I added a function that prompts you for each permutation of a word and asks you if it’s valid or not:
CL-USER> (check-word "cat") Is "cat" a valid word? (I think it's not.) (y/n) y Is "cta" a valid word? (I think it's not.) (y/n) n Is "act" a valid word? (I think it's not.) (y/n) y Is "atc" a valid word? (I think it's not.) (y/n) n Is "tca" a valid word? (I think it's not.) (y/n) n Is "tac" a valid word? (I think it's not.) (y/n) y T CL-USER>
In the process, it keeps track of valid and invalid words:
CL-USER> *valid-words*
("tac" "act" "cat")
CL-USER> *invalid-words*
("tca" "atc" "cta")
CL-USER>
Then, if I run it again, it “learns” (in a very, very rudimentary way) which words are valid and which aren’t:
CL-USER> (check-word "cat") Is "cat" a valid word? (I think it is.) (y/n) y Is "cta" a valid word? (I think it's not.) (y/n) n Is "act" a valid word? (I think it is.) (y/n) y Is "atc" a valid word? (I think it's not.) (y/n) n Is "tca" a valid word? (I think it's not.) (y/n) n Is "tac" a valid word? (I think it is.) (y/n) y T CL-USER>
The obvious missing piece is that the program can’t infer rules based on the facts you give it. I don’t really know how I might make it do that. Here’s the code for what I have so far:
(defparameter *valid-words* nil)
(defparameter *invalid-words* nil)
(defparameter *valid-words-filename* "valid-words.txt")
(defparameter *invalid-words-filename* "invalid-words.txt")
(defun all-permutations (list)
(cond ((null list) nil)
((null (cdr list)) (list list))
(t (loop for element in list
append (mapcar (lambda (l) (cons element l))
(all-permutations (remove element list)))))))
(defun make-distinct (list)
(let ((count 0)
(result nil))
(dolist (element list)
(push (list element count) result)
(incf count))
(reverse result)))
(defun make-simple (list)
(let ((result nil))
(dolist (element list)
(push (first element) result))
(reverse result)))
(defun clean-list (list)
(let ((result nil))
(dolist (element list)
(push (list-to-string (make-simple element)) result))
(reverse result)))
(defun scramble-list (list)
(clean-list (all-permutations (make-distinct list))))
(defun scramble (string)
(remove-duplicates
(scramble-list (loop for char across string collect char))
:test #'string-equal))
(defun list-to-string (list)
(concatenate 'string list))
(defun user-thinks-word-is-valid (word)
(let ((opinion))
(if (is-valid word)
(setf opinion "I think it is.")
(setf opinion "I think it's not."))
(y-or-n-p (format nil "Is \"~a\" a valid word? (~a)" word opinion))))
(defun check-word (word)
(dolist (scrambley (scramble word))
(if (user-thinks-word-is-valid scrambley)
(push scrambley *valid-words*)
(push scrambley *invalid-words*)))
(save-words))
(defun save-words ()
(save *valid-words-filename* *valid-words*)
(save *invalid-words-filename* *invalid-words*)
t)
(defun save (filename expression)
(with-open-file (out filename :direction :output :if-exists :supersede)
(with-standard-io-syntax
(print expression out))))
(defun load-from-file (filename)
(with-open-file (in filename)
(with-standard-io-syntax
(read in))))
(defun load-words ()
(setf *valid-words* (load-from-file *valid-words-filename*))
(setf *invalid-words* (load-from-file *invalid-words-filename*)))
(defun is-valid (word)
(if (find word *valid-words* :test #'string-equal) t))