# 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 ;) – mellamokb May 8 '12 at 19:55
• How precisely do we need to measure the time? Is one second resolution OK? – Ilmari Karonen May 10 '12 at 14:20
• @Ilmari Karonen: a one second resolution would be fine for this specific contest. – ChristopheD May 10 '12 at 15:35

# 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)... – ChristopheD May 10 '12 at 20:52 • Thanks, all of my answers here are in Q or (increasingly) in K. – tmartin May 11 '12 at 9:05 • I don't quite get why this only got a single upvote (mine)! – ChristopheD May 17 '12 at 20:08 ## 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 :) – Cristian Lupascu May 10 '12 at 6:47
• You could use the $SECONDS shell variable to simplify calculating the elapsed time. – manatwork May 10 '12 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. – manatwork May 10 '12 at 18:00

## 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? – ugoren May 9 '12 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. – ugoren May 9 '12 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. – breadbox May 9 '12 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. – ugoren May 9 '12 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. – ugoren May 13 '12 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. – Joey Adams May 10 '12 at 10:02 • I think you can change z=lambda x: to def z(x):. – Joey Adams May 10 '12 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 :/ – Joel Cornett May 10 '12 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() – ChristopheD May 10 '12 at 15:36 • @JoelCornett: Whoops, you're right. – Joey Adams May 10 '12 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
`