" " Filename: cream-typingtutor.vim " Updated: 2004-10-14 08:53:43-0400 " " Cream -- An easy-to-use configuration of the famous Vim text editor " (http://cream.sourceforge.net) Copyright (C) 2002-2004 Steve Hall " " License: " 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 2 of the License, or " (at your option) any later version. " (http://www.gnu.org/licenses/gpl.html) " " 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; if not, write to the Free Software " Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA " 02111-1307, USA. " " register as a Cream add-on {{{1 if exists("$CREAM") call Cream_addon_register( \ 'Typing Tutor', \ "Play a game while learning to type.", \ "Play a game while learning to type.", \ 'Typing Tutor', \ 'call Cream_typingtutor()', \ '' \ ) endif " Cream_typingtutor() {{{1 function! Cream_typingtutor() "*** DEBUG: let n = confirm( \ "This is still a test routine, picking the options does not work. (Press Ctrl+C to stop.)" . \ "\n", "&Continue\n&Quit", 1, "Info") if n != 1 return endif "*** " open new buffer call Cream_file_new() let ttbufnr = bufnr("%") " initialize vars, window, game space, maps, etc. call s:Init() " refresh initial switch to new buffer and setup redraw " set characters let s:chars = "" while s:level < s:levelmax " new delay equals current delay minus the range of delay " (init - max) divided by the number of steps we'll take " (levelmax) let s:delay = s:delay - ((s:delayinit - s:delaymax) / s:levelmax) " add chars each level if exists("s:chars{s:level}") let s:chars = s:chars . s:chars{s:level} endif let play = s:PlayLevel() " if quit if play == -1 break endif let s:level = s:level + 1 endwhile " TODO: retain last used settings " quit buffer if bufnr("%") == ttbufnr silent! bwipeout! endif endfunction " s:Init() {{{1 function! s:Init() " game initialization " define global mappings (mouse events, Esc) cmap :call s:MouseClick("c") cmap <2-LeftMouse> :call s:MouseClick("c") nmap :call s:MouseClick("n") nmap <2-LeftMouse> :call s:MouseClick("n") imap :call s:MouseClick("n") imap <2-LeftMouse> :call s:MouseClick("n") " hide normal-mode cursor setlocal guicursor+=n:hor1-Ignore-blinkwait0-blinkon1-blinkoff1000 " case is important! setlocal noignorecase " get chars of level " lower case let s:chars1 = 'abcdefghijklmnopqrstuvwxyz' " upper case let s:chars2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' " numbers let s:chars3 = '1234567890' " tab, space let s:chars4 = ' ' " enter, backspace let s:chars5 = '' " non-shifted common sentence chars let s:chars6 = ".,;'-/`" " shifted common sentence chars let s:chars7 = '?!":()' " less-used chars let s:chars8 = '@#$%^&*~_+' " basic coding chars let s:chars9 = '[]{}\\|' " insert, delete, home, end " function keys " pageup, pagedown " arrow keys " ctrl, alt, shift combinations " frequency/density of chars based on level let s:density = 1 " initial speed let s:delayinit = 800 " current speed let s:delay = s:delayinit " max speed let s:delaymax = 200 " set maximum loops (chars) per level let s:loopmax = 60 " set main level let s:level = 1 " set levels max let s:levelmax = 10 " find game area call s:GameArea() " add returns so setline() works call s:GameBoard() " clear any previous game scraps call s:ClearLines() endfunction " s:GameArea() {{{1 function! s:GameArea() " find initial game area let s:winheight = winheight(0) let s:winwidth = Cream_linewidth() - 2 if s:winwidth > 80 let s:winwidth = 80 endif endfunction " s:GameBoard() {{{1 function! s:GameBoard() " initialize game space--put a return in each line, we can't " setline() if a line doesn't exist if !exists("s:winheight") call s:GameArea() endif let @x = "" let i = 1 while i < s:winheight let @x = @x . "\n" let i = i + 1 endwhile normal "xP endfunction " s:ClearLines() {{{1 function! s:ClearLines() " clears all lines let i = 0 while i < s:winheight + 20 if exists("s:ttline{i}") unlet s:ttline{i} endif let i = i + 1 endwhile endfunction " s:Header() {{{1 function! s:Header() " defines current header and it's length "if !exists("s:tthelp") let s:tthelp = 8 "endif " define header if s:tthelp == 8 " full let s:ttline1 = " TYPING TUTOR [-]" let s:ttline2 = " o Type letters before they reach the bottom!" let s:ttline3 = " o Click on the buttons below to Start, Quit," let s:ttline4 = " Pause or to select Options for the shame." let s:ttline5 = "" let s:ttline6 = " [Start] [Quit] [Pause] [Options]" let s:ttline7 = " Next: a Seconds: " . s:loop . " Level: " . s:level . " Pause speed: " . s:delay let i = 0 let s:ttline8 = "" while i < s:winwidth let s:ttline8 = s:ttline8 . "-" let i = i + 1 endwhile elseif s:tthelp == 2 " minimal let s:ttline1 = " [Start] [Quit] [Pause] [Options] [Help]" let s:ttline2 = " Next: a Level: " . s:level endif endfunction " s:MouseClick() {{{1 function! s:MouseClick(mode) " DEBUG: if a:mode == "n" call confirm("n") elseif a:mode == "c" call confirm("c") endif let myword = expand("") "*** DEBUG: let n = confirm( \ "DEBUG:\n" . \ " myword = \"" . myword . "\"\n" . \ "\n", "&Ok\n&Cancel", 1, "Info") if n != 1 return endif "*** if myword == "Options" set b:quit = 1 elseif myword == "Start" set b:quit = 1 elseif myword == "Pause" set b:quit = 1 elseif myword == "Quit" set b:quit = 1 endif endfunction " s:PlayLevel() {{{1 function! s:PlayLevel() " test if maximum loops reached let s:loop = 1 while s:loop < s:loopmax && !exists("quit") " define game area (do it each loop in case window size " changed via mouse) call s:GameArea() " define header (do before line clearing so we know how " much) call s:Header() " advance existing lines 1 line, start from bottom let i = s:winheight - 1 while i > s:tthelp if exists("s:ttline{i}") let s:ttline{i+1} = s:ttline{i} endif let i = i - 1 endwhile " clear last line if exists("s:ttline{s:winheight}") unlet s:ttline{s:winheight} endif " decide column for new char let col = Urndm(1, s:winwidth - 1) " compose new line ( setline() ) let newline = "" " cat leading spaces/padding let i = 0 while i < col let newline = newline . " " let i = i + 1 endwhile " pick new char let len = strlen(s:chars) let cnt = Urndm(0, len) let char = s:chars[cnt] let newline = newline . char " draw screen " start at top let i = 1 " header while i <= s:tthelp if exists("s:ttline{i}") call setline(i, s:ttline{i}) endif let i = i + 1 endwhile " play area, newline let s:ttline{s:tthelp+1} = newline " play area, existing while i < s:winheight if exists("s:ttline{i}") call setline(i, s:ttline{i}) endif let i = i + 1 endwhile " footer? (with history?) " "poof" indicator that char was correct let poof = '*poof*' " remove poofs while i > s:tthelp if exists("s:ttline{i}") if match(s:ttline{i}, poof) != -1 " remove it from line let s:ttline{i} = substitute(s:ttline{i}, escape(poof, '*'), '', '') " redraw line call setline(i, s:ttline{i}) " refresh redraw " quit remove loop break endif endif let i = i - 1 endwhile " refresh screen redraw let sleep = 0 while sleep < s:delay " get char loop let char = getchar(0) let char = escape(char, '.') if exists("b:quit") return -1 endif " quite on Esc (TODO: others? mouseclick?) if char == 27 let quit = 1 break endif if char "echo nr2char(char) let i = s:winheight " check if getchar matches char "in play" (from bottom up) let i = s:winheight while i > s:tthelp if exists("s:ttline{i}") " if line has character typed (but not a poof line) if match(s:ttline{i}, poof) == -1 \ && match(s:ttline{i}, nr2char(char)) != -1 " remove it from line let s:ttline{i} = substitute(s:ttline{i}, '[ ]\{,3}' . nr2char(char), poof, '') " redraw line call setline(i, s:ttline{i}) " refresh redraw " quit remove loop break endif endif let i = i - 1 endwhile endif " time of local loop to getchar() (*not* screen " refresh, that's "s:delay") let sleepdelay = 10 execute "silent! sleep " . sleepdelay . " m" let sleep = sleep + sleepdelay endwhile let s:loop = s:loop + 1 endwhile endfunction " 1}}} " Random number generation (obsolete) " Random_int_range() {{{1 function! Random_int_range(min, max) " Return a "random" integer (0-32768). Returns -1 on error. " TODO: Currently unable to handle min. " disallow string if type(a:min) == 1 || type(a:max) == 1 call confirm("Error: Random() arguments must be numbers.") return -1 endif " verify arguments if a:min < 0 || a:min > 32768 call confirm("Error: Random() argument 1 must be between 0-32768.") return -1 endif if a:max < 0 || a:max > 32768 call confirm("Error: Random() argument 2 must be between 0-32768.") return -1 endif if a:min >= a:max call confirm("Error: Random() argument 2 must be greater than 1.") return -1 endif if exists("rnd") " ensure balanced range (multiple) if a:min == 0 && Cream_isfactor(32768, a:max) return rnd % a:max else " TODO: unfinished endif endif return -1 endfunction " Random_int() {{{1 function! Random_int() " Return a random integer based on one of several available means. " Unix/Linux (2^8) if has("unix") let rnd = system("echo $RANDOM") let rnd = matchstr(rnd, '\d\+') " Windows 2K/XP (2^8) elseif has("win32") && exists("$RANDOM") let rnd = $RANDOM " else else let rnd = Random_int_time() endif return rnd endfunction " Random_int_time() {{{1 function! Random_int_time() " Return a pseudo-random integer [0-32768 (2^16)] initially seeded by " time. " test function hasn't been run this second if !exists("s:localtime") " initialize seconds let s:localtime = localtime() " initialize millisecond fractions let s:rnd0100 = 0 let s:rnd0010 = 0 let s:rnd0001 = 0 else " pause a millisecond call s:Random_pause() endif " throw out returns greater than 2^16 (just 4 possibilities) while !exists("rnd") " seed with time if no previous random exists if !exists("s:rnd") " Vim can handle max 32-bit number (4,294,967,296) " get afresh (each loop) let time = localtime() let s:rnd9 = matchstr(time, '\zs.\ze.........$') + 0 let s:rnd8 = matchstr(time, '.\zs.\ze........$') + 0 let s:rnd7 = matchstr(time, '..\zs.\ze.......$') + 0 let s:rnd6 = matchstr(time, '...\zs.\ze......$') + 0 let s:rnd5 = matchstr(time, '....\zs.\ze.....$') + 0 let s:rnd4 = matchstr(time, '.....\zs.\ze....$') + 0 let s:rnd3 = matchstr(time, '......\zs.\ze...$') + 0 let s:rnd2 = matchstr(time, '.......\zs.\ze..$') + 0 let s:rnd1 = matchstr(time, '........\zs.\ze.$') + 0 let s:rnd0 = matchstr(time, '.........\zs.\ze$') + 0 " s:rnd0100 set above " s:rnd0010 set above " s:rnd0001 set above " string repeating variables ("random" by chaos theory) let rnd = \ s:rnd3 . \ s:rnd2 . \ s:rnd1 . \ s:rnd0 . \ s:rnd0100 . \ s:rnd0010 . \ s:rnd0001 " strip leading 0's prior to math (might be interpreted as octal) let rnd = (substitute(rnd, '^0\+', '', 'g') + 0) " otherwise, use previous result as seed else let rnd = s:rnd endif " Linear Congruential Generator " " o http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node78.html " * recommends M = 2^32, A = 1664525, C = 1 " o http://www.cs.sunysb.edu/~skiena/jaialai/excerpts/node7.html " o http://www.embedded.com/showArticle.jhtml?articleID=20900500 " o http://www.mathcom.com/corpdir/techinfo.mdir/scifaq/q210.html#q210.6.1 " * x(n) = A * x(n-1) + C mod M " M (smallest prime larger than 2^8, conveniently 2^8+4, " meaning only four results require throwing out) let m = 32771 " A (multiplier, 2 < a < m ) " Note: we use digits here consecutive with first number to " make sure it's impossible to repeat frequently (115 days). let a = s:rnd5 . s:rnd4 " strip leading 0's prior to math (might be interpreted as octal) let a = (substitute(a, '^0\+', '', 'g') + 0) + 2 " C let c = 1 let rnd = (((a * rnd) + c) % m) " pause and increment if out of range if rnd > 32768 call s:Random_pause() endif endwhile " update at end (after loops) let s:localtime = localtime() " remember for next time let s:rnd = rnd return rnd endfunction function! s:Random_pause() " Used to count pause 10 milliseconds and to increment both the 10s " and 100s of milliseconds script-globals. let s:rnd0001 = (s:rnd0001 + 1) if s:rnd0001 > 9 let s:rnd0001 = 0 let s:rnd0010 = (s:rnd0010 + 1) if s:rnd0010 > 9 let s:rnd0010 = 0 let s:rnd0100 = (s:rnd0100 + 1) if s:rnd0100 > 9 let s:rnd0100 = 0 endif endif endif sleep 1 m endfunction " TTest() {{{1 function! TTest() " Create set of random numbers in new buffer. " " Note: " It can be convenient to create a data with a range equaling the " number of iterations. (It's a square plot.) But in this case, you " may not wish run the 2^8 iterations required to balance the integer " range. (Although on my 5-year old PC, this only takes about ten " minutes and this routine continually indicates progress.) So if you " wish to run a smaller set, two options are available: " " 1. Simply throw out each result that exceeds your range. This means " wasted iterations, but should not have any affect on the results. " " 2. It is far more efficient to iterate by some factor of 2^8 and " then modulus each result by the same factor to ensure a balanced " reduction of the integers returned. Example: " " let max = 8192 " [...] " let a = Random_int() " let a = 32768 % max <= ADD THIS LINE " " Otherwise you will end up disfavoring results between the " non-factor divisor and the next factor. " let timein = localtime() " iterations let max = 10000 let str = "" let i = 0 while i < max " get random integer let a = Random_int_time() "let a = Rndm() let str = str . a . "\n" " progress indication " pad iterations for ouput let cnt = i while strlen(cnt) < strlen(max) let cnt = " " . cnt endwhile " pad result for ouput let result = a while strlen(result) < strlen(max) let result = " " . result endwhile " echo to command line echo cnt . " = " . result redraw let i = i + 1 endwhile let elapsed = localtime() - timein let str = "Elapsed time: " . elapsed . " seconds\n" . str let @x = str enew normal "xP endfunction " 1}}} " vim:foldmethod=marker