Insert typos into text
C, 358 bytes
(There are only three lines of code, but I've broken up line 3 for legibility)
#define x putchar
#define y random()
c,n;main(){char*s[26]={"QS","HNV","FVX","EFSX","DRW","CDGR","FHTV","BGJY","KOU","HKNU",
"IJLM","KO","KN","BJM","ILP","OO","AW","EFT","ADWZ","GRY","IJY","BCG","ESQ","CDZ","HTU",
"SX"};while((c=getchar())>0){if(y%10>0&&x(c))continue;if(isalpha(c)&&y%3<1){n=(c&31)-1;
x(s[n][y%strlen(s[n])]|(c&32));continue;}if (y&1)x(x(c));}}
The array of strings at the beginning lists the possible adjacent keys for each letter of the alphabet. I had to double up the "O" (adjacent to "P") to avoid calculating random()%1
when selecting a shifted character.
Test run:
$ echo "This is some correct text. It is too correct. Please un-correctify it." |./a.out
This is some cofrect teext. It is too correct.. Plleaase un-correctify it..
Update:
Here's an expanded and commented version of the same source code:
#include <stdio.h>
#include <string.h>
/* ^^ These should be included, but the code compiles without them */
int c,n;
void main() {
/* Adjacent keys for each letter of the alphabet (A, B, C, ..., Z): */
char *s[26] = { "QS","HNV","FVX","EFSX","DRW","CDGR","FHTV","BGJY","KOU","HKNU",
"IJLM","KO","KN","BJM","ILP","OO","AW","EFT","ADWZ","GRY","IJY",
"BCG","ESQ","CDZ","HTU","SX" };
/* Fetch input until null character or EOF reached */
while ((c=getchar())>0) {
/* For 90% of the time, just echo the character unchanged */
if (random()%10>0 && putchar(c)) continue;
/* If it's a letter of the alphabet, shift with 33% probability */
if (isalpha(c) && random()%3<1) {
/* Calculate offset to adjacent keys data */
n=(c&31)-1;
/* Choose a random adjacent key, and change to lower case if needed */
putchar(s[n][random()%strlen(s[n])]|(c&32));
continue;
}
/* If we reach this point, either we didn't fetch an alphabet character, or */
/* we did but chose not to shift it. Either way, we now need to either repeat */
/* the character or delete it, with 50% probability for each. */
/* This repeats the character by printing the return value from putchar() */
if (random()&1) putchar(putchar(c));
/* To delete the character, we don't have to do anything. */
}
}
Ruby, 168
Slightly shorter take using an array indexing strategy:
z='qwertyuiop.asdfghjkl...zxcvbnm'+?.*11
gets.chars{|c|$_=z[c]?z:z.upcase
h=[1,-1,11,-11].map{|d|$_[d+~/#{c}/]}-[?.]rescue[]
$><<(rand<0.9?c:[c*2,'',*h.sample].sample)}
Original regular expression version (184):
s='.qwertyuiop.asdfghjkl...zxcvbnm.'
gets.chars{|c|c=~/\w/&&(s[c]?s: s.upcase)=~/((\w).{9})?((\w)|.)#{c}((\w)|.)(.{9}(\w))?/
$><<(rand<0.9?c:[c*2,'',*[*$2,*$4,*$6,*$8].sample].sample)}
GolfScript, 120 characters
{10{rand}:R~!{[{.}{;}{Z\?[.(.10-@).10+]{Z=}%' '-.,R=}]'QWERTYUIOP ASDFGHJKL ZXCVBNM'' '22*11/*.{32|}%+:Z 2$?0>2+R=~}*}%
The code can be tested here.
{ # loop over all characters
10{rand}:R~! # take random number [0..9] and negate (i.e. 10% chance of true)
{ # {...}* is the if-block
[ # Three possible operations (code blocks) in the arry
{.} # a) duplicate
{;} # b) delete
{ # c) shift
Z # Z is the keyboard layout (see below)
\? # Find the index of the current letter
[.(.10-@).10+] # Calculate index of letter left, right, above, below
{Z=}% # Extract the corresponding letters for indices
' '- # Remove any spaces
.,R= # Take random item
}
]
# Z is the keyboard layout (upper and lower case)
# with enough spaces around
'QWERTYUIOP ASDFGHJKL ZXCVBNM'' '22*11/*.{32|}%+:Z
2$?0> # Is the current letter contained in Z and not a space?
2+ # Add 2 (i.e. 3 for letters, 2 for any other char)
R= # Take a random code block from above
~ # Execute the block
}*
}%