The idea is as follows:
We provide program with two files: one with English words and one with words in, let's say, Spanish. The files have one word (it could be a word, or word+some explanation etc) per line and words are ordered in files according to their translation: for example, line 5 of the first file contains the word "a stone" and line 5 of the second file contains the translation of the word "a stone".
The program asks you these questions:
- It decides whether to give your word in English and expect a translation or the other way around (pseudorandom 50% of each).
- It pseudorandomly choses 3 false answers, and together with the correct answer, gives these 4 possible options to you.
- You choose one of the 4 options and it reacts accordingly, keeping track of your win/all ration, win/lose streak, longest win streak, giving correct answers after each question and telling you whether you were right or wrong in your answer.
There are two entry points where you can write a path to files. There is also an option to choose one of two remembered pairs of paths (could be something like Spanish, Chinese or something - according to what language you want to train).
It does its job for me (it would save for one error that happens if you provide the wrong path but it's nothing terrible) but I would like to hear what I should improve on, what is bad style, what may give bad performance, etc.
I would also like to know how I can make an .exe while making sure a pseudorandom sequence will be different each time I run it (I managed to make an .exe, but the sequence was the same each time I turned it on). As you can see, I load the whole content of files into two arrays. Is it ok practice? How can I manage it in a better way?
(defparameter *vocabulary-file* "C:/sbcl/1.3.12/1.txt")
(defparameter *translation-file* "C:/sbcl/1.3.12/1.txt")
(defparameter *wins* 0)
(defparameter *loses* 0)
(defparameter *win-streak* t)
(defparameter *streak-length* 0)
(defparameter *longest-win-streak* 0)
(defparameter *vocabulary* `(,(make-array '(1) :adjustable t) 0))
(defparameter *translation* `(,(make-array '(1) :adjustable t) 0))
(defparameter *question* "QUESTION")
(defparameter *known-practices* '(x y))
(defparameter *FILE-NOT-FOUND* nil)
(setf *random-state* (make-random-state t))
(load "C:/sbcl/1.3.12/ltk/ltk.lisp")
(use-package :ltk)
(defun random-excluding (excluded minimal maximal)
(let ((rolled (+ minimal (random (+ maximal 1)))))
(if (member rolled excluded)
(random-excluding excluded minimal maximal)
rolled)))
(defmacro refresh-display()
`(progn
(setf (text rate) (concatenate 'string (write-to-string *wins*) "/" (write-to-string (+ *wins* *loses*))))
(if *win-streak*
(progn
(setf (text longest-win-streak) (concatenate 'string "Longest win-streak: " (write-to-string *longest-win-streak*)))
(setf (text streak) (concatenate 'string (write-to-string *streak-length*) "W")))
(setf (text streak) (concatenate 'string (write-to-string *streak-length*) "L")))))
(defmacro load-up-vector (which from-where)
`(with-open-file (stream ,from-where :direction :input :if-does-not-exist NIL :external-format :utf-8)
(if (eq stream NIL)
(progn
(do-msg "FILE NOT FOUND AT PROVIDED LOCATION")
(setf *FILE-NOT-FOUND* t))
(let ((line (read-line stream nil nil)))
(progn
(loop
:until (eq line nil)
:do (setf (aref (car ,which) (car (cdr ,which))) line)
(incf (car (cdr ,which)))
(adjust-array (car ,which) (list (1+ (car (cdr ,which)))))
(setf line (read-line stream nil nil)))
(decf (car(cdr ,which))))))))
(defmacro reset-stance ()
`(progn
(setf *vocabulary* (list (make-array '(1) :adjustable t) 0))
(setf *translation* (list (make-array '(1) :adjustable t) 0))
(setf *wins* 0)
(setf *loses* 0)
(setf *win-streak* t)
(setf *streak-length* 0)
(setf *longest-win-streak* 0)
(refresh-display)))
(defmacro redo-paths()
`(progn
(reset-stance)
(setf (text display) (text known-practices))
(cond
((equalp (text display) "X")
(progn
(setf (text vocabulary-path) "C:/sbcl/1.3.12/LangPractice/XX.txt")
(setf (text translation-path) "C:/sbcl/1.3.12/LangPractice/Xenglish.txt"))
(setf (text question) "X"))
((equalp (text display) "Y")
(progn
(setf (text vocabulary-path) "C:/sbcl/1.3.12/LangPractice/YY.txt")
(setf (text translation-path) "C:/sbcl/1.3.12/LangPractice/Yenglish.txt"))
(setf (text question) "Y"))
(t
(setf (text question) "BLANK")))
(setf *vocabulary-file* (text vocabulary-path))
(setf *translation-file* (text translation-path))
(load-up-vector *vocabulary* *vocabulary-file*)
(load-up-vector *translation* *translation-file*)
(if *FILE-NOT-FOUND*
(setf *FILE-NOT-FOUND* nil)
(prepare-question))))
(defmacro prepare-question()
`(let* ((true-answer-index (random (car (cdr *vocabulary*))))
(which-way (random 2))
(false-answer-index1 (random-excluding (list true-answer-index) 0 (car (cdr *vocabulary*))))
(false-answer-index2 (random-excluding (list true-answer-index false-answer-index1) 0 (car (cdr *vocabulary*))))
(false-answer-index3 (random-excluding (list true-answer-index false-answer-index1 false-answer-index2) 0 (car (cdr *vocabulary*))))
(where-true (+ 5 (random 9)))
(where-false1 (random-excluding (list where-true) 5 8))
(where-false2 (random-excluding (list where-true where-false1) 5 8))
(where-false3 (random-excluding (list where-true where-false1 where-false2) 5 8)))
(if (= which-way 0)
(progn
(setf (text true-answer) (format nil "~a" (aref (car *translation*) true-answer-index)))
(setf (text false-answer1) (format nil "~a" (aref (car *translation*) false-answer-index1)))
(setf (text false-answer2) (format nil "~a" (aref (car *translation*) false-answer-index2)))
(setf (text false-answer3) (format nil "~a" (aref (car *translation*) false-answer-index3)))
(setf (text question) (format nil "~a" (concatenate 'string "AL " (aref (car *vocabulary*) true-answer-index)))))
(progn
(setf (text true-answer) (format nil "~a" (aref (car *vocabulary*) true-answer-index)))
(setf (text false-answer1) (format nil "~a" (aref (car *vocabulary*) false-answer-index1)))
(setf (text false-answer2) (format nil "~a" (aref (car *vocabulary*) false-answer-index2)))
(setf (text false-answer3) (format nil "~a" (aref (car *vocabulary*) false-answer-index3)))
(setf (text question) (format nil "~a" (concatenate 'string "EN " (aref (car *translation*) true-answer-index))))))
(grid-forget true-answer)
(grid-forget false-answer1)
(grid-forget false-answer2)
(grid-forget false-answer3)
(grid true-answer where-true 0)
(grid false-answer1 where-false1 0)
(grid false-answer2 where-false2 0)
(grid false-answer3 where-false3 0)))
(defmacro correct()
`(progn
(incf *wins*)
(setf (text display) (format nil "CORRECT!~&~a=~&~a" (subseq (text question) 3) (text true-answer)))
(if *win-streak*
(progn
(incf *streak-length*)
(if (> *streak-length* *longest-win-streak*)
(setf *longest-win-streak* *streak-length*)))
(progn
(setf *win-streak* t)
(setf *streak-length* 1)))))
(defmacro mistake()
`(progn
(incf *loses*)
(setf (text display) (format nil "MISTAKE!~&~a=~&~a" (subseq (text question) 3) (text true-answer)))
(if *win-streak*
(progn
(setf *win-streak* nil)
(setf *streak-length* 1))
(incf *streak-length*))))
(defun main-display ()
(with-ltk ()
(let* ((menu (make-instance 'frame))
(practice (make-instance 'frame))
(display (make-instance 'label :text "XXX" :master practice))
(vocabulary-path (make-instance 'entry :text *vocabulary-file* :master menu))
(translation-path (make-instance 'entry :text *translation-file* :master menu))
(question (make-instance 'label :text "QUESTION" :master practice))
(rate (make-instance 'label :text "0/0" :master menu))
(streak (make-instance 'label :text "0W" :master menu))
(longest-win-streak (make-instance 'label :text "Longest win-streak: 0" :master menu))
(true-answer (make-instance 'button :text "" :master practice))
(false-answer1 (make-instance 'button :text "" :master practice))
(false-answer2 (make-instance 'button :text "" :master practice))
(false-answer3 (make-instance 'button :text "" :master practice))
(known-practices (make-instance 'combobox :values *known-practices* :master menu))
(refresh-paths (make-instance 'button :text "Refresh paths" :command #'(lambda ()(redo-paths)(prepare-question)) :master menu)))
(setf (command true-answer) #'(lambda ()(correct)(prepare-question)(refresh-display)))
(setf (command false-answer1) #'(lambda ()(mistake)(prepare-question)(refresh-display)))
(setf (command false-answer2) #'(lambda ()(mistake)(prepare-question)(refresh-display)))
(setf (command false-answer3) #'(lambda ()(mistake)(prepare-question)(refresh-display)))
(configure false-answer1 :takefocus 0)
(configure false-answer2 :takefocus 0)
(configure false-answer3 :takefocus 0)
(configure true-answer :takefocus 0)
(grid menu 0 0)
(grid practice 4 0)
(grid vocabulary-path 0 0)
(grid translation-path 1 0)
(grid refresh-paths 0 1)
(grid question 4 0)
(grid longest-win-streak 2 1)
(grid rate 0 2)
(grid known-practices 1 1)
(grid streak 1 2)
(grid display 3 0)
(grid true-answer 5 0)
(grid false-answer1 6 0)
(grid false-answer2 7 0)
(grid false-answer3 8 0)
)))