# Build a typing speed test / measuring CPM

## The Rules

It's time to build a typing speed test in your language of choice!

1. You provide a file with a dictionary of choice (every 'word' in it must be newline delimited). Pipe it in via stdin or provide it's name as a command line argument.

a
able
above
absence
...


2. Pick 10 random words from the file (no duplicates must be allowed) and print them out in the following manner:

-> direct
-> ground
-> next
-> five
...


3. Start measuring the time spent from now on!

4. Let the user type all of the ten words as fast as possible (ended with a carriage return). Print OK when you have a match, print WRONG when we have a typing mistake (or the word was already succesfully typed in this run).

5. Stop the clocks! Now print the CPM (Caracters per minute) benchmark, which is calculated as follows: (sum of the characters of the chosen words / time spent typing (seconds)) * 60. Round to the nearest integer and reproduce the following (sample) output:

--> You have scored 344 CPM!


## A sample run

-> settle
-> side
-> open
-> minister
-> risk
-> color
-> ship
-> same
-> size
-> sword
settle
OK
side
OK
open
OK
# ...................... some lines snipped ......................
word
WRONG
sword
OK
--> You have scored 298 CPM!


## The winner

This is code colf, the shortest entry (in source code character count) wins, have fun!

• I think the winner should partly be scored by the person with the highest CPM ;) May 8, 2012 at 19:55
• How precisely do we need to measure the time? Is one second resolution OK? May 10, 2012 at 14:20
• @Ilmari Karonen: a one second resolution would be fine for this specific contest. May 10, 2012 at 15:35

## Bash - 217212199 196 chars

Not gonna win but it was fun

declare -A W
for w in shuf -n10;do C+=$w;echo -\>$w;W[$w]=OK;done SECONDS=0 for((;${#W[*]};));do read r;echo ${W[$r]-WRONG};unset W[$r];done echo --\> You have scored$((60*${#C}/SECONDS)) CPM!  Under 200 chars now! Takes wordlist file as an argument Now takes word list on standard input. Paste it in the terminal and press ^D Implemented suggestion from manatwork • Not gonna win but it was fun - I think the same when I post my C# solutions :) May 10, 2012 at 6:47 • You could use the $SECONDS shell variable to simplify calculating the elapsed time. May 10, 2012 at 9:41
• You can remove 2 more characters: 1) the : in the default value parameter expansion; 2) the $ in front of SECONDS in the arithmetic evaluation. Actually there is another extra character, the newline at the and of file. May 10, 2012 at 18:00 # K, 146 Assumes a dictionary file called 'd' in the current working directory. {b:+/#:'a:10?_0::d;-1"-> ",/:a;s:.z.t;while[#a;$[(,/0:0)~*a;[a:1_a;-1"OK"];-1"WRONG"]];-1"--> You have scored ",($(60000*b)%"i"$.z.t-s)," CPM!";}

• Very nice and short, first entry in K I've seen (here on codegolf.se)... May 10, 2012 at 20:52
• Thanks, all of my answers here are in Q or (increasingly) in K. May 11, 2012 at 9:05
• I don't quite get why this only got a single upvote (mine)! May 17, 2012 at 20:08

## Ruby (189178171 168)

$><<t=['',d=[*$<.lines].sample(10)]*'-> '
s=Time.now
puts d.delete($stdin.gets)?:OK:'WRONG'while d[0] puts'--> You have scored %i CPM!'%((t.size-40)/(Time.now-s)*60)  Pretty basic, I'm sure there are improvements to be made. Takes the filename of the dictionary as a command-line argument. EDIT: A few minor tweaks, mainly around retaining the newlines from the dictionary. As a result the file will need a trailing newline to work correctly. ## C, 305 309347 chars char*stdin,w[11][99];long i,t;main(int n,char**v){v=fopen(v[1],"r"); for(srand(time(&t));fgets(w[i++>9?(n=rand()%i)>10?0:n:i],99,v);); for(i=n=0;i<10;n+=printf("-> %s",w[++i])-4); for(;i;puts(!strcmp(*w,w[11-i])?--i,"OK":"WRONG"))fgets(*w,99,stdin); printf("--> You have scored %ld CPM!\n",n*60/(time(0)-t));}  Thanks to @ugoren for the improvement hints. Using an "11th word" to discard incoming dictionary entries was a big win over my previous strcpy-if-chosen approach. Here's the ungolfed source: #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> static char words[11][99]; static long i, t; int main(int argc, char *argv[]) { FILE *fp; int n; fp = fopen(argv[1], "r"); srand(time(0)); for (i = 0 ; fgets(words[0], sizeof words[0], fp) ; ++i) { n = i < 10 ? i : rand() % i; if (n < 10) strcpy(words[n + 1], words[0]); } fclose(fp); n = 0; for (i = 1 ; i <= 10 ; ++i) n += printf("-> %s", words[i]) - 4; t = time(0); i = 1; while (i <= 10 && fgets(words[0], sizeof words[0], stdin)) { if (strcmp(words[0], words[i])) { puts("WRONG"); } else { puts("OK"); ++i; } } if (i > 9) printf("-> You have scored %ld CPM!\n", n * 60 / (time(0) - t)); return argc - argc; }  • Some improvements: 1. K&R declaration: main(n,v)char**v;{.... 2. stdin can be char *. fgets(buf,len,stdin)=gets(buf) (never mind the buffer overruns). 3. What's wrong with rand()%i? RAND_MAX isn't needed. 4. Why long? May 9, 2012 at 13:51 • Also: 1) t=time(0)->time(&t). 2) n*60/(time(0)-t) has parentheses that must go - *60 can be moved to n+=60*printf, then n/=time(0)-t. 3) Replace b with an extra element in w, replace strcpy with read directly into w. May 9, 2012 at 14:01 • Replacing fgets() with gets() needs extra code to deal with the newlines in the dictionary; this wound up being shorter. rand()%i isn't sufficient; the actual calculation is (double)i*rand()/RAND_MAX. Moving the *60 to the printf also means changing the the -4 to -240 so it ultimately winds up a loss. Your other points are valid ones, though (I believe). Oh, and long is because time_t is traditionally a long. Just because we're golfing doesn't mean we can't be portable. May 9, 2012 at 16:55 • I missed 4->240... Portability and golf don't go well together. But defining i,t; (implicitly int) is OK up to MAX_INT seconds (if you don't use time(&t)). With rand(), all you need is a 10/i chance, and rand()%i<10 does it. May 9, 2012 at 18:57 • Your random selection is somewhat flawed (not that absolute fairness is required). The 11th line should have a 10/11 chance to be selected, but you do rand()%10>10, which gives it 100%. rand()%(i+1)>9 is better (but instead if %(i+1) do i++>9?. Also move *stdin to be first and save a space. May 13, 2012 at 11:25 ## C# 401 void T(){ Action<string>C=Console.WriteLine;Func<string>R=Console.ReadLine; var w=new List<string>(); for(var l=R();l!="";l=R())w.Add(l); var s=w.OrderBy(_=>Guid.NewGuid()).Take(10).ToList(); s.ForEach(x=>C("=> "+x)); var t=s.Select(x=>x.Length).Sum(); var c=Stopwatch.StartNew(); while(s.Any()){C(s.Remove(R())?"OK":"WRONG");} c.Stop(); C("--> You have scored "+c.Elapsed.TotalSeconds*60/t+" CPM!");}  Running version here: http://ideone.com/Nt6Id ## Python (256 235) import time as t,random as r def p(x):print x c=r.sample(input().split("\n"),10) z=lambda x:p(("WRONG","OK")[raw_input()==x])or len(x) p("--> "+"\n--> ".join(c)) _=t.time() p("--> You have scored %d CPM!"%(sum(map(z,c))/(t.time()-_)*60))  This is in python 2.x, in 3.x I can shave off 4 more characters by using the print function. Newlines included • "Question: Do newlines count?" We usually go by "necessary characters". In the case of Python, yes, the newlines need to be counted, since removing them will keep the program from working. May 10, 2012 at 10:02 • I think you can change z=lambda x: to def z(x):. May 10, 2012 at 10:04 • @JoeyAdams: I could if only needed to return none, but I need to return len(x) and def z(x):return is 5 more charaters :/ May 10, 2012 at 14:12 • I think you can win some characters by piping in the input (allowed in the question) and using input() instead of sys.argv[1].read() May 10, 2012 at 15:36 • @JoelCornett: Whoops, you're right. May 10, 2012 at 16:49 ## PHP 187 bytes Newlines have been added for clarity: <?$s=file($argv[1]); for(shuffle($s);$i++<10;$l+=strlen($$i))echo~ÒÁß,$$i=$s[$i];
for($t=time();$j++<10;)echoj==fgets(STDIN)?OK:WRONG,~õ?>
--> You have scored <?=0|$l/(time()-$t)*60?> CPM!


Accepts the dictionary filename as a command line argument. The dictionary file must end with a newline.

# Scala(319306304 302)

var s=util.Random.shuffle(io.Source.fromFile(args(0)).getLines.toSet)take 10
def?(a:Any)=println(a)
var l=(0/:s){_+_.size}
s map{"-> "+_}map?
def n=System.nanoTime
val t=n
`