From 94d2fc1815a919734353c942f224db1de4b4fcb8 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Mon, 7 Mar 2011 09:04:49 +0100 Subject: Django, org * Added nxhtml, mostly for django support. * Changed some org settings. --- emacs.d/nxhtml/util/n-back.el | 1296 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1296 insertions(+) create mode 100644 emacs.d/nxhtml/util/n-back.el (limited to 'emacs.d/nxhtml/util/n-back.el') diff --git a/emacs.d/nxhtml/util/n-back.el b/emacs.d/nxhtml/util/n-back.el new file mode 100644 index 0000000..024b8e6 --- /dev/null +++ b/emacs.d/nxhtml/util/n-back.el @@ -0,0 +1,1296 @@ +;;; n-back.el --- n-back game +;; +;; Author: Lennart Borgman (lennart O borgman A gmail O com) +;; Created: 2009-05-23 Sat +(defconst n-back:version "0.5");; Version: +;; Last-Updated: 2009-08-04 Tue +;; URL: +;; Keywords: +;; Compatibility: +;; +;; Features that might be required by this library: +;; +;; `winsize'. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Commentary: +;; +;; n-back game for brain training. See `n-back-game' for more +;; information. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Change log: +;; +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 3, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Code: + +;;(eval-when-compile (require 'viper)) + +;; (setq n-back-trials 2) +(eval-when-compile (require 'cl)) +(eval-when-compile (require 'nxhtml-base nil t)) +(eval-when-compile (require 'nxhtml-web-vcs nil t)) +(require 'winsize nil t) ;; Ehum... + +(defvar n-back-game-window nil) +(defvar n-back-game-buffer nil) + +(defvar n-back-ctrl-window nil) +(defvar n-back-ctrl-buffer nil) + +(defvar n-back-info-window nil) +(defvar n-back-info-buffer nil) + +(defvar n-back-trials-left nil) +(defvar n-back-timer nil) +(defvar n-back-clear-timer nil) + +(defvar n-back-result nil) +(defvar n-back-this-result nil) + +(defvar n-back-ring nil) + +(defvar n-back-num-active nil) + + +;;;###autoload +(defgroup n-back nil + "Customizations for `n-back-game' game." + :group 'games) + +(defgroup n-back-feel nil + "Customizations for `n-back-game' game keys, faces etc." + :group 'n-back) + +(defface n-back-ok + '((t (:foreground "black" :background "green"))) + "Face for OK answer." + :group 'n-back-feel) + +(defface n-back-bad + '((t (:foreground "black" :background "OrangeRed1"))) + "Face for bad answer." + :group 'n-back-feel) + +(defface n-back-hint + '((t (:foreground "black" :background "gold"))) + "Face for bad answer." + :group 'n-back-feel) + +(defface n-back-do-now + '((((background dark)) (:foreground "yellow")) + (t (:foreground "blue"))) + "Face for start and stop hints." + :group 'n-back-feel) + +(defface n-back-game-word + '((t (:foreground "black"))) + "Face for word displayed in game." + :group 'n-back-feel) + +(defface n-back-header + '((((background dark)) (:background "OrangeRed4")) + (t (:background "gold"))) + "Face for headers." + :group 'n-back-feel) + +(defface n-back-keybinding + '((((background dark)) (:background "purple4")) + (t (:background "OliveDrab1"))) + "Face for key bindings." + :group 'n-back-feel) + +(defface n-back-last-result + '((((background dark)) (:background "OliveDrab4")) + (t (:background "yellow"))) + "Face for last game result header." + :group 'n-back-feel) + +(defface n-back-welcome + '((((background dark)) (:foreground "OliveDrab3")) + (t (:foreground "OliveDrab4"))) + "Face for welcome string" + :group 'n-back-feel) + +(defface n-back-welcome-header + '((t (:height 2.0))) + "Face for welcome header." + :group 'n-back-feel) + +(defcustom n-back-level 1 + "The n-Back level." + :type '(radio (const 1) + (const 2) + (const 3) + (const 4)) + :set (lambda (sym val) + (set-default sym val) + (when (featurep 'n-back) + (n-back-update-control-buffer) + (n-back-update-info))) + :group 'n-back) + +(defcustom n-back-active-match-types '(position color sound) + "Active match types." + :type '(set (const position) + (const color) + (const sound) + (const word)) + :set (lambda (sym val) + (set-default sym val) + (setq n-back-num-active (length val)) + (when (featurep 'n-back) + (n-back-init-control-status) + (n-back-update-control-buffer) + (n-back-update-info))) + :group 'n-back) + +(defcustom n-back-allowed-match-types '(position color sound word) + "Match types allowed in auto challenging." + :type '(set (const position) + (const color) + (const sound) + (const word)) + :set (lambda (sym val) + (set-default sym val) + (when (featurep 'n-back) + (n-back-set-random-match-types (length n-back-active-match-types) nil) + (n-back-init-control-status) + (n-back-update-control-buffer) + (n-back-update-info))) + :group 'n-back) + +(defcustom n-back-auto-challenge t + "Automatic challenge decrease/increase." + :type 'boolean + :group 'n-back) + +(defun n-back-toggle-auto-challenge () + "Toggle `n-back-auto-challenge'." + (interactive) + (let ((val (not n-back-auto-challenge))) + (customize-set-variable 'n-back-auto-challenge val) + (customize-set-value 'n-back-auto-challenge val))) + +(defcustom n-back-colors + '("gold" "orange red" "lawn green" "peru" "pink" "gray" "light blue") + "Random colors to display." + :type '(repeat color) + :group 'n-back) + +(defcustom n-back-words "you cat going me forest crying brown" + "Random words to display." + :type 'string + :group 'n-back) + +(defcustom n-back-sound-volume 0.2 + "Sound volume 0-1." + :type 'float + :group 'n-back-feel) + +(defcustom n-back-sounds '("c:/program files/brain workshop/res" "piano-") + "Random sounds location." + :type '(list (directory :tag "Directory") + (regexp :tag "File name regexp")) + :group 'n-back) + +(defcustom n-back-keys + '( + [?p] + [?c] + [?s] + [?w] + ) + "Key bindings for answering." + :type '(list + (key-sequence :tag "position key") + (key-sequence :tag "color key") + (key-sequence :tag "sound key") + (key-sequence :tag "word key") + ) + ;; :set (lambda (sym val) + ;; (set-default sym val) + ;; (n-back-make-keymap)) + :group 'n-back-feel) + +(defvar n-back-control-mode-map nil) + +(defun n-back-key-binding (what) + "Return key binding used for WHAT match answers." + (nth + (case what + (position 0) + (color 1) + (sound 2) + (word 3)) + n-back-keys)) + +(defun n-back-make-keymap () + "Make keymap for the game." + (let ((map (make-sparse-keymap))) + (define-key map [?1] 'n-back-change-level) + (define-key map [?2] 'n-back-change-level) + (define-key map [?3] 'n-back-change-level) + (define-key map [?4] 'n-back-change-level) + (define-key map [?5] 'n-back-change-level) + (define-key map [?6] 'n-back-change-level) + (define-key map [??] 'n-back-help) + (define-key map [?\ ] 'n-back-play) + (define-key map [(control ?g)] 'n-back-stop) + (define-key map [?-] 'n-back-decrease-speed) + (define-key map [?+] 'n-back-increase-speed) + + (define-key map [(control ?r)] 'n-back-reset-game-to-saved) + (define-key map [(control ?s)] 'n-back-save-game-settings) + + (define-key map [?t ?p] 'n-back-toggle-position) + (define-key map [?t ?c] 'n-back-toggle-color) + (define-key map [?t ?s] 'n-back-toggle-sound) + (define-key map [?t ?w] 'n-back-toggle-word) + + (define-key map [?T ?a] 'n-back-toggle-auto-challenge) + (define-key map [up] 'n-back-challenge-up) + (define-key map [down] 'n-back-challenge-down) + + (define-key map [?T ?p] 'n-back-toggle-allowed-position) + (define-key map [?T ?c] 'n-back-toggle-allowed-color) + (define-key map [?T ?s] 'n-back-toggle-allowed-sound) + (define-key map [?T ?w] 'n-back-toggle-allowed-word) + + (define-key map (n-back-key-binding 'position) 'n-back-position-answer) + (define-key map (n-back-key-binding 'color) 'n-back-color-answer) + (define-key map (n-back-key-binding 'sound) 'n-back-sound-answer) + (define-key map (n-back-key-binding 'word) 'n-back-word-answer) + ;;(define-key map [t] 'ignore) + (setq n-back-control-mode-map map))) + +(defvar n-back-display-hint nil) +(defcustom n-back-hint t + "Display hints - learning mode." + :type 'boolean + :group 'n-back) + + + +(defvar n-back-sound-files nil) +;;(n-back-get-sound-files) +(defun n-back-get-sound-files () + "Get sound file names." + (let ((dir (nth 0 n-back-sounds)) + (regexp (nth 1 n-back-sounds))) + (when (file-directory-p dir) + (setq n-back-sound-files (directory-files dir nil regexp))))) + +(defun n-back-toggle-position () + "Toggle use of position in `n-back-active-match-types'." + (interactive) + (n-back-toggle 'position)) + +(defun n-back-toggle-color () + "Toggle use of color in `n-back-active-match-types'." + (interactive) + (n-back-toggle 'color)) + +(defun n-back-toggle-sound () + "Toggle use of sound in `n-back-active-match-types'." + (interactive) + (n-back-toggle 'sound)) + +(defun n-back-toggle-word () + "Toggle use of word in `n-back-active-match-types'." + (interactive) + (n-back-toggle 'word)) + +(defun n-back-toggle (match-type) + "Toggle use of MATCH-TYPE in `n-back-active-match-types'." + (n-back-toggle-1 match-type 'n-back-active-match-types)) + +(defun n-back-toggle-allowed-position () + "Toggle use of position in `n-back-allowed-match-types'." + (interactive) + (n-back-toggle-allowed 'position)) + +(defun n-back-toggle-allowed-color () + "Toggle use of color in `n-back-allowed-match-types'." + (interactive) + (n-back-toggle-allowed 'color)) + +(defun n-back-toggle-allowed-sound () + "Toggle use of sound in `n-back-allowed-match-types'." + (interactive) + (n-back-toggle-allowed 'sound)) + +(defun n-back-toggle-allowed-word () + "Toggle use of word in `n-back-allowed-match-types'." + (interactive) + (n-back-toggle-allowed 'word)) + +(defun n-back-toggle-allowed (match-type) + "Toggle use of MATCH-TYPE in `n-back-allowed-match-types'." + (n-back-toggle-1 match-type 'n-back-allowed-match-types)) + +(defun n-back-sort-types (types) + "Sort TYPES to order used in defcustoms here." + (sort types + (lambda (a b) + (let ((all '(position color sound word))) + (< (length (memq a all)) + (length (memq b all))))))) + +(defun n-back-toggle-1 (match-type active-list-sym) + "Toggle use of MATCH-TYPE in list ACTIVE-LIST-SYM." + (let (active-types) + (if (memq match-type (symbol-value active-list-sym)) + (setq active-types (delq match-type (symbol-value active-list-sym))) + (setq active-types (cons match-type (symbol-value active-list-sym)))) + (setq active-types (n-back-sort-types active-types)) + (customize-set-variable active-list-sym active-types) + (customize-set-value active-list-sym active-types))) + +(defcustom n-back-sec-per-trial 3.0 + "Seconds per trial." + :type 'float + :set (lambda (sym val) + (set-default sym val) + (when (featurep 'n-back) + (n-back-update-info))) + :group 'n-back) + +(defun n-back-decrease-speed () + "Decrease speed of trials." + (interactive) + (setq n-back-sec-per-trial (+ n-back-sec-per-trial 0.25)) + (when (> n-back-sec-per-trial 5.0) + (setq n-back-sec-per-trial 5.0)) + (n-back-update-info)) + +(defun n-back-increase-speed () + "Increase speed of trials." + (interactive) + (let ((sec (- n-back-sec-per-trial 0.25))) + (when (< sec 1.0) + (setq sec 1.0)) + (customize-set-variable 'n-back-sec-per-trial sec) + (customize-set-value 'n-back-sec-per-trial sec))) + +(defun n-back-help () + "Show help for `n-back-game' game." + (interactive) + (save-selected-window + (describe-function 'n-back-game))) + +(defun n-back-change-level (level) + "Change n-Back level to LEVEL." + (interactive (progn + (if (and (numberp last-input-event) + (>= last-input-event ?1) + (<= last-input-event ?9)) + (list (- last-input-event ?0)) + (list (string-to-number (read-string "n Back: ")))))) + (customize-set-variable 'n-back-level level) + (customize-set-value 'n-back-level level)) + +(defvar n-back-frame nil) + +;;;###autoload +(defun n-back-game () + "Emacs n-Back game. +This game is supposed to increase your working memory and fluid +intelligence. + +In this game something is shown for half a second on the screen +and maybe a sound is played. You should then answer if parts of +it is the same as you have seen or heard before. This is +repeated for about 20 trials. + +You answer with the keys shown in the bottom window. + +In the easiest version of the game you should answer if you have +just seen or heard what is shown now. By default the game gets +harder as you play it with success. Then first the number of +items presented in a trial grows. After that it gets harder by +that you have to somehow remember not the last item, but the item +before that \(or even earlier). That is what \"n-Back\" stands +for. + +Note that remember does not really mean remember clearly. The +game is for training your brain getting used to keep those things +in the working memory, maybe as a cross-modal unit. You are +supposed to just nearly be able to do what you do in the game. +And you are supposed to have fun, that is what your brain like. + +You should probably not overdue this. Half an hour a day playing +might be an optimal time according to some people. + +The game is shamelessly modeled after Brain Workshop, see URL +`http://brainworkshop.sourceforge.net/' just for the fun of +getting it into Emacs. The game resembles but it not the same as +that used in the report by Jaeggi mentioned at the above URL. + +Not all features in Brain Workshop are implemented here, but some +new are maybe ... - and you have it available here in Emacs." + ;; ----- + ;; Below is a short excerpt from the report by Jaeggi et al which + ;; gave the idea to the game: + + ;; Training task. For the training task, we used the same material + ;; as described by Jaeggi et al. (33), which was a dual n-Back task + ;; where squares at eight different locations were presented + ;; sequentially on a computer screen at a rate of 3 s (stimulus + ;; length, 500 ms; interstimulus interval, 2,500 ms). + ;; Simultaneously with the presentation of the squares, one of eight + ;; consonants was presented sequentially through headphones. A + ;; response was required whenever one of the presented stimuli + ;; matched the one presented n positions back in the sequence. The + ;; value of n was the same for both streams of stimuli. There were + ;; six auditory and six visual targets per block (four appearing in + ;; only one modality, and two appearing in both modalities + ;; simultaneously), and their positions were determined randomly. + ;; Participants made responses manually by pressing on the letter + ;; ‘‘A’’ of a standard keyboard with their left index finger for + ;; visual targets, and on the letter ‘‘L’’ with their right index + ;; finger for auditory targets. No responses were required for + ;; non-targets. + (interactive) + (n-back-make-keymap) + (when window-system + (unless (frame-live-p n-back-frame) + (setq n-back-frame (make-frame + (list '(name . "n-back game") + '(tool-bar-lines . 0) + '(menu-bar-lines . 0) + (case (frame-parameter nil 'background-mode) + (light '(background-color . "cornsilk")) + (dark '(background-color . "MidnightBlue")) + (otherwise nil)) + '(height . 45) + '(width . 150))))) + (select-frame n-back-frame) + (raise-frame n-back-frame)) + (n-back-cancel-timers) + (n-back-get-sound-files) + (unless n-back-sound-files + (when (memq 'sound n-back-allowed-match-types) + (n-back-toggle-allowed-sound)) + (when (memq 'sound n-back-active-match-types) + (n-back-toggle-sound))) + (n-back-init-control-status) + (n-back-setup-windows) + ) + +(defconst n-back-match-types + '((position ": position match" nil) + (color ": color match" nil) + (sound ": sound match" nil) + (word ": word match" nil) + )) + +(defvar n-back-control-status nil + "For showing status in control window.") +(setq n-back-control-status nil) + +;;(n-back-set-match-status 'position 'bad) +(defun n-back-set-match-status (match-type status) + "Set MATCH-TYPE status to STATUS for control window." + (unless (memq status '(ok bad miss nil)) (error "n-back: Bad status=%s" status)) + (let ((entry (assoc match-type n-back-control-status))) + (setcar (cddr entry) status) + )) + +;;(n-back-clear-match-status) +(defun n-back-clear-match-status () + "Clear match status for control window." + ;;(dolist (entry n-back-control-status) + (dolist (entry n-back-match-types) + (setcar (cddr entry) nil) + )) + +;; (n-back-init-control-status) +(defun n-back-init-control-status () + "Init match status for control window." + (setq n-back-control-status nil) + (dolist (what n-back-active-match-types) + (setq n-back-control-status + (cons (assoc what n-back-match-types) + n-back-control-status)))) + +(defsubst n-back-is-playing () + "Return non-nil when game is active." + (timerp n-back-timer)) + +;;(n-back-update-control-buffer) +(defun n-back-update-control-buffer () + "Update content of control buffer." + (save-match-data ;; runs in timer + (when (buffer-live-p n-back-ctrl-buffer) + (with-current-buffer n-back-ctrl-buffer + (setq buffer-read-only nil) + (erase-buffer) + (insert (propertize (format "%s %s-back" + (let ((n (length n-back-active-match-types))) + (cond + ((= 1 n) "Single") + ((= 2 n) "Dual") + ((= 3 n) "Triple") + )) + n-back-level + ) 'face 'n-back-header) + (propertize + (if (n-back-is-playing) " Press C-g to stop" " Press SPACE to play") + 'face 'n-back-do-now) + (if (n-back-is-playing) (format " Left %s" n-back-trials-left) "") + "\n") + ;;(unless n-back-control-status (n-back-init-control-status)) + (dolist (entry n-back-control-status) + (let* ((what (nth 0 entry)) + (msg (nth 1 entry)) + (sts (nth 2 entry)) + (key (key-description (n-back-key-binding what)))) + ;;(setq msg (concat (key-description (n-back-key-binding what)) msg)) + (cond + ((eq sts 'bad) + (setq msg (propertize (concat key msg) 'face 'n-back-bad))) + ((eq sts 'ok) + (setq msg (propertize (concat key msg) 'face 'n-back-ok))) + ((eq sts 'miss) + (setq msg (concat + (if n-back-display-hint + (propertize key 'face 'n-back-header) + key) + msg))) + ((not sts) + (setq msg (concat key msg))) + (t + (error "n-back:Unknown sts=%s" sts) + )) + (insert msg " ")) + ) + (when n-back-display-hint + (setq n-back-display-hint nil) + (run-with-timer 0.1 nil 'n-back-update-control-buffer)) + (setq buffer-read-only t) + (if (window-live-p n-back-ctrl-window) + (with-selected-window n-back-ctrl-window + (goto-char 1)) + (goto-char 1)))))) + +(defcustom n-back-trials 20 + "Number of trials per session." + :type 'integer + :group 'n-back) + +;;(n-back-compute-result-values n-back-result) +(defvar n-back-result-values nil) +(defun n-back-compute-single-result-value (entry) + "Compute result stored in ENTRY." + (let* ((what (nth 0 entry)) + (good (nth 1 entry)) + (bad (nth 2 entry)) + (miss (nth 3 entry)) + (err (+ bad miss)) + ;;(tot (+ good bad miss 0.0)) + ;;(gnum 6) + ;;(weighted-err (* err (/ gnum tot))) + ) + (cons what (if (= 0 good) + 0 + (/ (- n-back-trials err 0.0) + n-back-trials))))) + +(defun n-back-compute-result-values (result) + "Compute result values from game result RESULT." + (let ((results nil)) + (dolist (entry result) + (let ((res (n-back-compute-single-result-value entry))) + (setq results (cons res results)))) + (setq n-back-result-values (reverse results)))) + +;; Thresholds +(defun n-back-view-threshold-discussion-page () + "View some discussion of threshold." + (interactive) + (browse-url "http://groups.google.com/group/brain-training/browse_thread/thread/f4bfa452943c2a2d/ba31adfd0b97771c?lnk=gst&q=threshold#ba31adfd0b97771c")) + +;;(n-back-set-next-challenge) +(defvar n-back-worst nil) + +(defvar n-back-challenge-change nil) + +(defun n-back-set-next-challenge () + "Set next game difficulty level from last game result." + (let ((r 2.8)) ;; stay as default + (setq n-back-worst nil) + (dolist (res n-back-result-values) + (when (< (cdr res) r) + (setq r (cdr res)) + (setq n-back-worst res))) + (setq n-back-challenge-change (if (< r 0.74) + 'down + (if (> r 0.91) + 'up + 'stay))) + (n-back-change-challenge n-back-challenge-change))) + +(defun n-back-challenge-up () + "Make the game harder." + (interactive) + (n-back-change-challenge 'up)) + +(defun n-back-challenge-down () + "Make the game easier." + (interactive) + (n-back-change-challenge 'down)) + +(defun n-back-change-challenge (challenge-change) + "Change game difficulty level by CHALLENGE-CHANGE." + (let ((new-level n-back-level) + (new-num-active n-back-num-active) + (num-allowed (length n-back-allowed-match-types))) + (case challenge-change + (down + (if (= 1 n-back-num-active) + (unless (= 1 n-back-level) + (setq new-num-active (min 3 num-allowed)) + (setq new-level (1- n-back-level))) + (setq new-num-active (1- n-back-num-active)))) + (up + (if (or (<= 3 n-back-num-active) + (<= num-allowed n-back-num-active)) + (progn + (setq new-level (1+ n-back-level)) + (setq new-num-active 1)) + (setq new-num-active (min 3 (1+ n-back-num-active)))))) + ;;(when (= new-level 0) (setq new-level 1)) + ;;(when (= new-num-active 0) (setq new-num-active 1)) + (when (and (= new-level n-back-level) + (= new-num-active n-back-num-active)) + (setq n-back-challenge-change 'stay)) + (unless (= new-level n-back-level) + (customize-set-variable 'n-back-level new-level) + (customize-set-value 'n-back-level new-level)) + (n-back-set-random-match-types new-num-active (car n-back-worst)))) + +(defun n-back-set-random-match-types (num worst) + "Select NUM random match types. +If type WORST is non-nil try to include that." + (let ((alen (length n-back-allowed-match-types)) + (old-types n-back-active-match-types) + types) + (unless (<= num alen) + (error "n-back: Too many match types required = %s" num)) + (when (and worst + (< 1 num) + (memq worst n-back-allowed-match-types)) + (add-to-list 'types worst)) + (while (< (length types) num) + (add-to-list 'types (nth (random alen) n-back-allowed-match-types))) + (setq types (n-back-sort-types types)) + (unless (equal old-types types) + (customize-set-variable 'n-back-active-match-types types) + (customize-set-value 'n-back-active-match-types types)))) + +;; (defcustom n-back-keybinding-color "OliveDrab1" +;; "Background color for key binding hints." +;; :type 'color +;; :group 'n-back) + +(defun n-back-update-info () + "Update info buffer." + (when (buffer-live-p n-back-info-buffer) + (when (window-live-p n-back-info-window) + (set-window-buffer n-back-info-window n-back-info-buffer)) + (with-current-buffer n-back-info-buffer + (setq buffer-read-only nil) + (erase-buffer) + + (insert (propertize "n-back" 'face 'n-back-header) + " " + (propertize "Help: ?" 'face 'n-back-keybinding)) + + ;; Auto challenging + (insert "\n\nAuto challenging: " + (if n-back-auto-challenge "on " "off ") + (propertize "toggle: Ta" 'face 'n-back-keybinding)) + + (insert "\n Manually change challenging: " + (propertize "up-arrow/down-arrow" 'face 'n-back-keybinding)) + + (insert "\n Allowed match types: ") + (dolist (type n-back-allowed-match-types) + (insert (format "%s " type))) + (insert (propertize "toggle: T" 'face 'n-back-keybinding)) + + ;; Current game + (insert "\n\nCurrent game:") + + (insert (format "\n n Back: %s " n-back-level) + (propertize "change: number 1-9" 'face 'n-back-keybinding)) + (insert "\n Match types: ") + (dolist (type n-back-active-match-types) + (insert (format "%s " type))) + (insert (propertize "toggle: t" 'face 'n-back-keybinding)) + + (insert (format "\n %.2f seconds per trial " n-back-sec-per-trial) + (propertize "change: +/-" 'face 'n-back-keybinding)) + + ;; Save and restore + (insert "\n\n") + (insert "Game settings: " + (propertize "reset: C-r" 'face 'n-back-keybinding) + " " + (propertize "save: C-s" 'face 'n-back-keybinding)) + + (insert "\n\n") + (unless (or (n-back-is-playing) + (not n-back-result)) + (insert (propertize (format "Last result, %s" n-back-challenge-change) + 'face 'n-back-last-result) + "\n Good-Bad-Miss:") + (dolist (entry n-back-result) + (let* ((what (nth 0 entry)) + (good (nth 1 entry)) + (bad (nth 2 entry)) + (miss (nth 3 entry)) + (tot (+ good bad miss 0.0)) + (res (n-back-compute-single-result-value entry))) + (insert (format " %s: %s-%s-%s (%d%%)" + (key-description (n-back-key-binding what)) + good + bad + miss + (floor (* 100 (cdr res)))))))) + + (setq buffer-read-only t)))) + +(defun n-back-show-welcome (msg) + "Show welcome startup info and message MSG." + (when (and n-back-game-buffer + (buffer-live-p n-back-game-buffer)) + (with-current-buffer n-back-game-buffer + (let ((src (or (when (boundp 'nxhtml-install-dir) + (expand-file-name "nxhtml/doc/img/fun-brain-2.png" nxhtml-install-dir)) + "c:/program files/brain workshop/res/brain_graphic.png")) + img + buffer-read-only) + (erase-buffer) + ;;(insert (propertize "\nEmacs n-back game (after Brain Workshop)\n\n" 'face '(:height 2.0))) + (insert (propertize "\nEmacs n-back game (after Brain Workshop)\n\n" 'face 'n-back-welcome-header)) + (unless (file-exists-p src) + (n-back-maybe-download-files (file-name-directory src) (list (file-name-nondirectory src)))) + (if (file-exists-p src) + (condition-case err + (setq img (create-image src nil nil + :relief 0 + ;;:margin inlimg-margins + )) + (error (setq img (error-message-string err)))) + (setq img (concat "Image not found: " src))) + (if (stringp img) + (insert img) + (insert-image img)) + (insert (propertize "\n\nPlay for fun and maybe a somewhat happier brain" + 'face 'n-back-welcome)) + (when msg (insert "\n\n" msg)) + )))) + +(defun n-back-setup-windows () + "Setup game frame and windows." + (delete-other-windows) + ;; Info + (split-window-horizontally) + (setq n-back-info-window (next-window (frame-first-window))) + (setq n-back-info-buffer (get-buffer-create "* n-back info *")) + (when (< 75 (window-width n-back-info-window)) + (with-selected-window n-back-info-window + (enlarge-window (- 75 (window-width n-back-info-window)) t))) + (with-current-buffer n-back-info-buffer + (n-back-control-mode) + (setq wrap-prefix " ")) + (n-back-update-info) + ;; Control + (split-window-vertically) + (setq n-back-ctrl-window (next-window (frame-first-window))) + (setq n-back-ctrl-buffer (get-buffer-create "* n-back control *")) + (set-window-buffer n-back-ctrl-window n-back-ctrl-buffer) + (with-current-buffer n-back-ctrl-buffer (n-back-control-mode)) + (n-back-update-control-buffer) + (fit-window-to-buffer n-back-ctrl-window) + (set-window-dedicated-p n-back-ctrl-window t) + ;; Game + (setq n-back-game-window (frame-first-window)) + (setq n-back-game-buffer (get-buffer-create "*n-back game*")) + (set-window-buffer n-back-game-window n-back-game-buffer) + (set-window-dedicated-p n-back-game-window t) + (with-current-buffer n-back-game-buffer (n-back-control-mode)) + (n-back-show-welcome nil) + ;; Position in control window + (select-window n-back-ctrl-window) + ) + +;;(n-back-display "str" 1 0 3 3 6) +(defun n-back-display (str x y cols rows max-strlen color) + "Display a trial. +Display item with text STR at column X in row Y using COLS +columns and ROWS rows. Strings to display have max length +MAX-STRLEN. Display item with background color COLOR." + (unless (< x cols) (error "n-back: Not x=%s < cols=%s" x cols)) + (unless (< y rows) (error "Not y=%s < rows=%s" y rows)) + (unless str (setq str "")) + (with-current-buffer n-back-game-buffer + (let* (buffer-read-only + (tot-str "") + ;; Pad spaces left, two right, four between + (game-w (window-width n-back-game-window)) + (pad-x 0) + (scale (if (not window-system) + 1.0 + (/ (* 1.0 game-w) + (+ (* 2 pad-x) + (* (1- cols) 4) + (* cols max-strlen))))) + (str-diff (- max-strlen (length str))) + (str-l-len (/ str-diff 2)) + (str-r-len (- max-strlen (length str) str-l-len)) + (face-spec (if window-system + (list :inherit 'n-back-game-word :background color :height scale) + (list :inherit 'n-back-game-word :background color))) + (str-disp (propertize + (concat (make-string str-l-len 32) str (make-string str-r-len 32)) + 'face face-spec)) + (col-str (concat + (make-string pad-x ?p) + (make-string + (+ (* x (+ 4 max-strlen))) + 32 + ;;?x + ))) + ;; Pad lines above and below, two between + (pad-y 0) + (game-h (window-body-height n-back-game-window)) + (game-h-scaled (/ game-h scale)) + (lines-between (/ (- game-h-scaled rows (* 2 pad-y)) + (1- rows))) + (row-scaled (+ pad-y (* y (1+ lines-between)) (1- y))) + (row-num (if (= y 0) + pad-y + (round row-scaled))) + (row-str (make-string row-num ?\n))) + (setq show-trailing-whitespace nil) + ;;(setq cursor-type nil) + (erase-buffer) + (setq tot-str row-str) + (setq tot-str (concat tot-str col-str)) + (insert (propertize tot-str 'face (list :height scale))) + (insert str-disp) + ))) + +;; (setq timer-list nil) +;;(n-back-display-in-timer) +;; (setq n-back-trials-left 3) + +(defun n-back-clear-game-window () + "Erase game buffer." + (save-match-data ;; runs in timer + (with-current-buffer n-back-game-buffer + (let (buffer-read-only) + (erase-buffer))))) + +(defun n-back-play () + "Start playing." + (interactive) + (message " ") ;; For easier reading *Messages* + (n-back-update-info) + (if (not n-back-active-match-types) + (message (propertize "No active match types" + 'face 'secondary-selection)) + ;;(setq n-back-result nil) + (n-back-init-control-status) + (n-back-init-this-result) + (n-back-cancel-timers) + (winsize-set-mode-line-colors t) + (setq n-back-ring (make-ring (1+ n-back-level))) + (n-back-clear-game-window) + (setq n-back-trials-left (+ n-back-trials n-back-level)) + (random t) + (n-back-start-main-timer) + (n-back-update-control-buffer))) + +(defun n-back-start-main-timer () + "Start main game timer." + (setq n-back-timer + (run-with-timer + n-back-sec-per-trial + nil ;;n-back-sec-per-trial + 'n-back-display-in-timer))) + +(defun n-back-maybe-download-files (dir file-name-list) + (nxhtml-get-missing-files (file-relative-name dir nxhtml-install-dir) file-name-list)) + +(defun n-back-finish-game () + "Finish the game." + (n-back-cancel-timers) + (fit-window-to-buffer n-back-ctrl-window) + (setq n-back-result n-back-this-result) + (n-back-compute-result-values n-back-result) + (when n-back-auto-challenge (n-back-set-next-challenge)) + (n-back-update-info) + (n-back-init-control-status) + (n-back-clear-match-status) + (n-back-update-control-buffer) + (n-back-show-welcome "Game over") + (with-current-buffer n-back-game-buffer + ;;(setq n-back-challenge-change 'up) + (let (buffer-read-only) + (insert + "\n\n" + (case n-back-challenge-change + (up "Congratulations! I see you need more challenge, raising difficulty!") + (down "Making it a bit easier for now to make your playing more fun.") + (otherwise "This game challenges seems the right way for you now."))) + (let* ((dir (when (boundp 'nxhtml-install-dir) + (expand-file-name "nxhtml/doc/img/" nxhtml-install-dir))) + (up-imgs '("rembrandt-self-portrait.jpg" + "bacchante2.jpg" + "giraffe.jpg" + "Las_Medulas.jpg" + )) + (t-imgs '("continue-play.jpg" + "Toco_toucan.jpg" + "raindrops2.jpg" + "divine2.jpg" + ;;"butterflies.png" + "volga.jpg" + "healthy_feet2.jpg" + )) + ;; (setq n-back-trials 1) + (pic (when dir (case n-back-challenge-change + (up (nth (random (length up-imgs)) up-imgs)) + (otherwise (nth (random (length t-imgs)) t-imgs))))) + (src (when dir (expand-file-name pic dir))) + img) + (when (and src (not (file-exists-p src))) + ;; Time to download? + (n-back-maybe-download-files (file-name-directory src) (append up-imgs t-imgs nil))) + (when (and src (file-exists-p src)) + (condition-case err + (setq img (create-image src nil nil + :relief 0 + )) + (error (setq img (error-message-string err))))) + (if (stringp img) + img + (insert "\n\n") + (insert-image img))))) + (message "Game over")) + +(defun n-back-display-random () + "Display a random item." + (when (current-message) (message "")) + ;;(message "here start display") + (let* ((use-position (memq 'position n-back-active-match-types)) + (use-color (memq 'color n-back-active-match-types)) + (use-sound (memq 'sound n-back-active-match-types)) + (use-word (memq 'word n-back-active-match-types)) + (old-rec (when (n-back-match-possible) + (ring-ref n-back-ring (1- n-back-level)))) + (cols 3) + (rows 3) + (x (if use-position (random 3) 1)) + (y (if use-position (random 3) 1)) + (old-x (if use-position (nth 1 old-rec))) + (old-y (if use-position (nth 2 old-rec))) + (color (nth (if use-color (random (length n-back-colors)) 0) n-back-colors)) + (old-color (if use-color (nth 3 old-rec))) + (sound (when use-sound (expand-file-name (nth (random (length n-back-sound-files)) + n-back-sound-files) + (nth 0 n-back-sounds)))) + (old-sound (if use-sound (nth 4 old-rec))) + (words (when use-word (split-string n-back-words))) + (word (when use-word (nth (random (length words)) words))) + (old-word (when use-word (nth 5 old-rec))) + (str (if word word "")) ;(format "%s" n-back-trials-left)) + (max-strlen (if words + (+ 2 (apply 'max (mapcar (lambda (w) (length w)) words))) + 5)) + (compensate 24) + ) + ;; To get more targets make it more plausible that it is the same here. + ;; (/ (- 6 (/ 20.0 8)) 20) + (when old-rec + (when (and use-position + (not (and (= x old-x) + (= y old-y))) + (< (random 100) compensate)) + (setq x (nth 1 old-rec)) + (setq y (nth 2 old-rec))) + (when (and use-color + (not (equal color old-color)) + (< (random 100) compensate)) + (setq color (nth 3 old-rec))) + (when (and use-sound + (not (equal sound old-sound)) + (< (random 100) compensate)) + (setq sound (nth 4 old-rec))) + (when (and use-word + (not (equal word old-word)) + (< (random 100) compensate)) + (setq word (nth 5 old-rec)))) + (setq str word) ;; fix-me + (ring-insert n-back-ring (list str x y color sound word)) + ;;(message "here before display") + (n-back-display str x y cols rows max-strlen color) + ;;(when sound (play-sound (list 'sound :file sound))) + ;;(message "here before clear-m") + (n-back-clear-match-status) + ;;(message "here before position") + (when (and use-position (n-back-matches 'position)) (n-back-set-match-status 'position 'miss)) + ;;(message "here before color") + (when (and use-color (n-back-matches 'color)) (n-back-set-match-status 'color 'miss)) + ;;(message "here before sound") + (when (and use-sound (n-back-matches 'sound)) (n-back-set-match-status 'sound 'miss)) + ;;(message "here before word") + (when (and use-word (n-back-matches 'word)) (n-back-set-match-status 'word 'miss)) + (setq n-back-display-hint n-back-hint) + ;;(message "here before control") + (n-back-update-control-buffer) + ;;(message "here before clear timer") + (setq n-back-clear-timer (run-with-timer 0.5 nil 'n-back-clear-game-window)) + ;;(message "here before sound timer") + (when sound (run-with-timer 0.01 nil 'n-back-play-sound-in-timer sound)) + ;;(message "here exit display") + )) + +(defun n-back-display-in-timer () + "Display a trial in a timer." + (condition-case err + (save-match-data ;; runs in timer + (n-back-add-result) + (if (>= 0 (setq n-back-trials-left (1- n-back-trials-left))) + (n-back-finish-game) + (n-back-display-random) + (n-back-start-main-timer) + ;;(message "after start-main-timer") + )) + (error (message "n-back-display: %s" (error-message-string err)) + (n-back-cancel-timers)))) + +(defun n-back-play-sound-in-timer (sound-file) + "Play sound SOUND-FILE in a timer." + (condition-case err + (save-match-data ;; runs in timer + (play-sound (list 'sound :file sound-file :volume n-back-sound-volume))) + (error (message "n-back-sound: %s" (error-message-string err)) + (n-back-cancel-timers)))) + + +;;; Answers + +;;(defvar n-back-answers nil) + +(defun n-back-init-this-result () + "Init `n-back-this-result'." + (setq n-back-this-result nil) + (dolist (sts-entry n-back-control-status) + (let* ((what (nth 0 sts-entry)) + (res-entry (list what 0 0 0))) + (setq n-back-this-result (cons res-entry n-back-this-result))))) + +(defun n-back-match-possible () + "Return t if enouch entries have been shown to match." + (= (ring-length n-back-ring) (1+ n-back-level))) + +(defun n-back-add-result () + "Add result of last trial." + (when (n-back-match-possible) + (dolist (sts-entry n-back-control-status) + (let* ((what (nth 0 sts-entry)) + (sts (nth 2 sts-entry)) + (matches (n-back-matches what)) + (num (cond + ((eq sts 'ok) 1) + ((eq sts 'bad) 2) + ;;((eq sts nil) (when matches 3)) + ((eq sts 'miss) 3) + ((not sts) nil) + (t (error "n-back: Bad status=%s" sts)))) + (res-entry (when num (assoc what n-back-this-result))) + (lst (when num (nthcdr num res-entry)))) + (when num + (if res-entry + (setcar lst (1+ (car lst))) + (setq res-entry (list what 0 0 0)) + ;;(setq lst (nthcdr num res-entry)) + (setq n-back-this-result (cons res-entry n-back-this-result)))))))) + +(defun n-back-matches-position () + "Return non-nil iff last trial position match." + (when (n-back-match-possible) + (let* ((comp-item (ring-ref n-back-ring n-back-level)) + (curr-item (ring-ref n-back-ring 0)) + (comp-x (nth 1 comp-item)) + (curr-x (nth 1 curr-item)) + (comp-y (nth 2 comp-item)) + (curr-y (nth 2 curr-item))) + (and (= comp-y curr-y) + (= comp-x curr-x))))) + +(defun n-back-matches-color () + "Return non-nil iff last trial color match." + (when (n-back-match-possible) + (let* ((comp-item (ring-ref n-back-ring n-back-level)) + (curr-item (ring-ref n-back-ring 0)) + (comp-color (nth 3 comp-item)) + (curr-color (nth 3 curr-item))) + (equal comp-color curr-color)))) + +(defun n-back-matches-sound () + "Return non-nil iff last trial sound match." + (when (n-back-match-possible) + (let* ((comp-item (ring-ref n-back-ring n-back-level)) + (curr-item (ring-ref n-back-ring 0)) + (comp-sound (nth 4 comp-item)) + (curr-sound (nth 4 curr-item))) + (equal comp-sound curr-sound)))) + +(defun n-back-matches-word () + "Return non-nil iff last trial word match." + (when (n-back-match-possible) + (let* ((comp-item (ring-ref n-back-ring n-back-level)) + (curr-item (ring-ref n-back-ring 0)) + (comp-word (nth 5 comp-item)) + (curr-word (nth 5 curr-item))) + (equal comp-word curr-word)))) + +(defun n-back-matches (what) + "Return non-nil iff last trial part WHAT match." + (cond + ((eq what 'position) (n-back-matches-position)) + ((eq what 'color) (n-back-matches-color)) + ((eq what 'sound) (n-back-matches-sound)) + ((eq what 'word) (n-back-matches-word)) + (t (error "n-back: Unknown match type: %s" what)))) + +(defun n-back-answer (what) + "Tell that you think WHAT matched." + (when (n-back-is-playing) + (if (memq what n-back-active-match-types) + (if (n-back-match-possible) + (let ((sts (if (n-back-matches what) 'ok 'bad))) + (n-back-set-match-status what sts) + (n-back-update-control-buffer)) + (message "%s n-back items must be displayed before anything can match" + n-back-level)) + (message "%s match is not active" what) + (ding t)))) + +(defun n-back-position-answer () + "Tell that you think position matched." + (interactive) + (n-back-answer 'position)) + +(defun n-back-color-answer () + "Tell that you think color matched." + (interactive) + (n-back-answer 'color)) + +(defun n-back-sound-answer () + "Tell that you think sound matched." + (interactive) + (n-back-answer 'sound)) + +(defun n-back-word-answer () + "Tell that you think word matched." + (interactive) + (n-back-answer 'word)) + +(defun n-back-stop () + "Stop playing." + (interactive) + (n-back-cancel-timers) + (n-back-update-control-buffer) + (message "Stopped n-back game") + (n-back-show-welcome "Stopped")) + +(defvar viper-emacs-state-mode-list) ;; silence compiler +(defvar viper-emacs-state-hook) ;; silence compiler + +(define-derived-mode n-back-control-mode nil "N-back" + "Mode for controlling n-back game." + (setq cursor-type nil) + (setq buffer-read-only t) + (set (make-local-variable 'viper-emacs-state-mode-list) '(n-back-control-mode)) + (set (make-local-variable 'viper-emacs-state-hook) nil) ;; in vis cursor + (abbrev-mode -1) + (setq show-trailing-whitespace nil) + (when (fboundp 'visual-line-mode) (visual-line-mode 1)) + (n-back-make-keymap)) + +(defun n-back-cancel-timers () + "Cancel game timers." + (when (timerp n-back-timer) + (cancel-timer n-back-timer)) + (setq n-back-timer nil) + (when (timerp n-back-clear-timer) + (cancel-timer n-back-clear-timer)) + (setq n-back-clear-timer nil) + (winsize-set-mode-line-colors nil)) + +(defvar n-back-game-settings-symbols + '( + ;;n-back-keys + n-back-level + n-back-active-match-types + n-back-allowed-match-types + n-back-auto-challenge + ;;n-back-colors + ;;n-back-words + ;;n-back-sound-volume + ;;n-back-sounds + n-back-sec-per-trial + ;;n-back-keybinding-color + ;;n-back-trials + )) + +(defun n-back-save-game-settings () + "Save game settings." + (interactive) + (dolist (var n-back-game-settings-symbols) + ) + (custom-save-all)) + +(defun n-back-reset-game-to-saved () + "Reset game playing options to saved values." + (interactive) + (dolist (pass '(1 2)) + (dolist (var n-back-game-settings-symbols) + (if (= pass 1) + ;; pass 1 is for my lousy programming: + (condition-case err + (custom-reevaluate-setting var) + (error nil)) + (custom-reevaluate-setting var))))) + +(provide 'n-back) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; n-back.el ends here -- cgit v1.2.3-54-g00ecf