Quiz mode

Here is a tiny emacs app just for fun. Someone asked me for a program that would test their subject knowledge prior to an exam. I could have implemented this in a million different ways and I chose emacs to make a simple keyboard driven interface.

The questions are along the lines of given a case who is the claimant and who is the defendant. The obvious data structure is a list of triples containing the name of the case and the parties. As most of emacs editing functionality is not required it makes sense to make a major mode which I will call quiz.

Specifying all of the questions took most of the time…

(defconst *quiz-questions*
  ‘(
    ["Invitation to treat" "Fisher" "Bell"]
    ["Offer" "Carlill" "Carbolic Smoke"]
    ;; … More cases
    ))

(defconst *quiz-number-of-questions*
  (length *quiz-questions*))

(defvar quiz-current-question 0)

A bunch of accessors is just good style in case I decide on an alternative data structure.

(defsubst quiz-get-case (q) (aref q 0))
(defsubst quiz-get-claimant (q) (aref q 1))
(defsubst quiz-get-defendant (q) (aref q 2))
(defsubst quiz-get-number-of-parties (q) (- (length q) 1))

I store the number of the current question and the current question in quiz-current question.

(defun quiz-choose-question ()
  (let ((n (random *quiz-number-of-questions*)))
    (setq quiz-current-question (cons n (nth n *quiz-questions*)))))

This user would probably like to see all of the options available at all times. Therefore asking a question should look like this:

(n)ext | (a)nswer | (q)uit

Offer --> ?

…and selecting the answer should say:

(n)ext | (a)nswer | (q)uit

Offer --> Carlill vs Carbolic Smoke
(defun quiz-init-buffer ()
  (erase-buffer)
  (insert “(n)ext | (a)nswer | (q)uit\n\n”))

(defun quiz-display-question ()
  (let ((q (quiz-get-case (cdr quiz-current-question))))
    (insert (format “%s –> ?” q))
    (backward-char)))

(defun quiz-parties-to-string (c)
  (let ((p (quiz-get-number-of-parties c))
        (claimant (quiz-get-claimant c)))
    (if (= p 1) (format “%s” claimant)
      (format “%s vs %s” claimant (quiz-get-defendant c)))))

The actual interface is provided by quiz-next-question quiz-get-answer and quiz-quit. get-answer could do with a bit of a tidy-up but for a quick hack it will do.

(defun quiz-next-question ()
  (interactive)
  (quiz-init-buffer)
  (quiz-choose-question)
  (quiz-display-question))

(defun quiz-get-answer ()
  (interactive)
  (let* ((q (cdr quiz-current-question))
         (c (quiz-get-case q))
         (p (quiz-parties-to-string q)))
    (quiz-init-buffer)
    (insert (format “%s –> %s” c p))))

(defun quiz-quit ()
  (interactive)
  (when (y-or-n-p “Really quit? “)
    (save-buffers-kill-emacs)))

Now set up the major mode. It is a lot easier than you might think with define-derived-mode.

A quick note about the keymap: I want to disable other key-bindings. There are a couple of ways to do this: (suppress-keymap quiz-mode-map) or (define-key quiz-mode-map [t] ‘undefined). I used the second of these which disables every key you haven’t defined but I add back in M-x as I’m always scared of being trapped in a mode with no way out.

(defvar quiz-mode-map nil
  “Keymap for quiz major mode.”)

(define-derived-mode quiz-mode text-mode “Quiz”
  “Major mode for interacting with databases.
Special commands:
\\{quiz-mode-map}”)

(if quiz-mode-map
    nil
  (setq quiz-mode-map (make-sparse-keymap))
  (define-key quiz-mode-map “n” ‘quiz-next-question)
  (define-key quiz-mode-map “a” ‘quiz-get-answer)
  (define-key quiz-mode-map “q” ‘quiz-quit)
  (define-key quiz-mode-map [t] ‘undefined)
  (define-key quiz-mode-map “\M-x” ‘execute-extended-command))

(defun quiz-enter-quiz-mode ()
  (interactive)
  (switch-to-buffer “*quiz*” t)
  (random t)
  (quiz-mode)
  (quiz-next-question))

Finally, load up the defaults and enter the mode when we start.

(load “c:/packages/emacs-files/my-vars.el”)
(add-to-list ‘load-path *elisp-dir*)

(requiremy-defaults)

;; 
(quiz-enter-quiz-mode)

The complete file looks like this:

;; ———————————————————————- ;;

(load “c:/packages/emacs-files/my-vars.el”)
(add-to-list ‘load-path *elisp-dir*)

(requiremy-defaults)

;; ———————————————————————- ;;

(defconst *quiz-questions*
  ‘(
    ["Invitation to treat" "Fisher" "Bell"]
    ["Offer" "Carlill" "Carbolic Smoke"]
    ;; … More cases
    ))

(defconst *quiz-number-of-questions*
  (length *quiz-questions*))

(defvar quiz-current-question 0)

(defsubst quiz-get-case (q) (aref q 0))
(defsubst quiz-get-claimant (q) (aref q 1))
(defsubst quiz-get-defendant (q) (aref q 2))
(defsubst quiz-get-number-of-parties (q) (- (length q) 1))

;; ———————————————————————- ;;

(defun quiz-choose-question ()
  (let ((n (random *quiz-number-of-questions*)))
    (setq quiz-current-question (cons n (nth n *quiz-questions*)))))

(defun quiz-display-question ()
  (let ((q (quiz-get-case (cdr quiz-current-question))))
    (insert (format “%s –> ?” q))
    (backward-char)))

(defun quiz-init-buffer ()
  (erase-buffer)
  (insert “(n)ext | (a)nswer | (q)uit\n\n”))

(defun quiz-next-question ()
  (interactive)
  (quiz-init-buffer)
  (quiz-choose-question)
  (quiz-display-question))

(defun quiz-parties-to-string (c)
  (let ((p (quiz-get-number-of-parties c))
        (claimant (quiz-get-claimant c)))
    (if (= p 1) (format “%s” claimant)
      (format “%s vs %s” claimant (quiz-get-defendant c)))))

(defun quiz-get-answer ()
  (interactive)
  (let* ((q (cdr quiz-current-question))
         (c (quiz-get-case q))
         (p (quiz-parties-to-string q)))
    (quiz-init-buffer)
    (insert (format “%s –> %s” c p))))

(defun quiz-quit ()
  (interactive)
  (when (y-or-n-p “Really quit? “)
    (save-buffers-kill-emacs)))

;; ———————————————————————- ;;

(defvar quiz-mode-map nil
  “Keymap for quiz major mode.”)

(define-derived-mode quiz-mode text-mode “Quiz”
  “Major mode for interacting with databases.
Special commands:
\\{quiz-mode-map}”)

(if quiz-mode-map
    nil
  (setq quiz-mode-map (make-sparse-keymap))
  (define-key quiz-mode-map “n” ‘quiz-next-question)
  (define-key quiz-mode-map “a” ‘quiz-get-answer)
  (define-key quiz-mode-map “q” ‘quiz-quit)
  (define-key quiz-mode-map [t] ‘undefined)
  (define-key quiz-mode-map “\M-x” ‘execute-extended-command))

(defun quiz-enter-quiz-mode ()
  (interactive)
  (switch-to-buffer “*quiz*” t)
  (random t)
  (quiz-mode)
  (quiz-next-question))

;; ———————————————————————- ;;

(quiz-enter-quiz-mode)

;; ———————————————————————- ;;

This is the batchfile to start quiz mode.

set EMACS_FILES=c:/packages/emacs-files
set EMACS_DIR=c:/packages/emacs-22.1

%EMACS_DIR%/bin/runemacs.exe -q --load %EMACS_FILES%/quiz.el

One Response to “Quiz mode”

  1. Alex Schröder Says:

    There’s always the option of using FlashCard or similar offerings from Category Education on Emacs Wiki.

Leave a Reply