11
\$\begingroup\$

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
about
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!

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

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!";}
\$\endgroup\$
  • \$\begingroup\$ Very nice and short, first entry in K I've seen (here on codegolf.se)... \$\endgroup\$ – ChristopheD May 10 '12 at 20:52
  • \$\begingroup\$ Thanks, all of my answers here are in Q or (increasingly) in K. \$\endgroup\$ – tmartin May 11 '12 at 9:05
  • \$\begingroup\$ I don't quite get why this only got a single upvote (mine)! \$\endgroup\$ – ChristopheD May 17 '12 at 20:08
11
\$\begingroup\$

Bash - 217 212 199 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

\$\endgroup\$
  • 1
    \$\begingroup\$ Not gonna win but it was fun - I think the same when I post my C# solutions :) \$\endgroup\$ – Cristian Lupascu May 10 '12 at 6:47
  • 1
    \$\begingroup\$ You could use the $SECONDS shell variable to simplify calculating the elapsed time. \$\endgroup\$ – manatwork May 10 '12 at 9:41
  • \$\begingroup\$ 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. \$\endgroup\$ – manatwork May 10 '12 at 18:00
4
\$\begingroup\$

Ruby (189 178 171 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.

\$\endgroup\$
4
\$\begingroup\$

C, 305 309 347 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;
}
\$\endgroup\$
  • \$\begingroup\$ 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? \$\endgroup\$ – ugoren May 9 '12 at 13:51
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ – ugoren May 9 '12 at 14:01
  • \$\begingroup\$ 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. \$\endgroup\$ – breadbox May 9 '12 at 16:55
  • \$\begingroup\$ 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. \$\endgroup\$ – ugoren May 9 '12 at 18:57
  • \$\begingroup\$ 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. \$\endgroup\$ – ugoren May 13 '12 at 11:25
2
\$\begingroup\$

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

\$\endgroup\$
2
\$\begingroup\$

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

\$\endgroup\$
  • 2
    \$\begingroup\$ "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. \$\endgroup\$ – Joey Adams May 10 '12 at 10:02
  • 1
    \$\begingroup\$ I think you can change z=lambda x: to def z(x):. \$\endgroup\$ – Joey Adams May 10 '12 at 10:04
  • \$\begingroup\$ @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 :/ \$\endgroup\$ – Joel Cornett May 10 '12 at 14:12
  • \$\begingroup\$ 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() \$\endgroup\$ – ChristopheD May 10 '12 at 15:36
  • \$\begingroup\$ @JoelCornett: Whoops, you're right. \$\endgroup\$ – Joey Adams May 10 '12 at 16:49
2
\$\begingroup\$

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;)echo$$j==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.

\$\endgroup\$
2
\$\begingroup\$

Scala(319 306 304 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
while(s.size!=0){val m=readLine
if(s contains m)?("OK")else?("WRONG");s-=m}
?("--> You have scored "+l*60000000000L/(n-t)+" CPM!")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.