66
\$\begingroup\$

I wrote some text, but it looks too professional. I want to make it look like I was really tired when I wrote it. I need you to insert some typos.

Your challenge is to take an arbitrary single line of text, and add typos. This means that for each character, there will be a 10% chance for it to be typofied.

The definition of "typofied" is that you must choose (randomly) one of the following:

  • Duplicate the character.
  • Delete the character.
  • Shift the character one keyboard space. The "keyboard" is defined as:

    qwertyuiop
    asdfghjkl
     zxcvbnm
    

    For the character shift, you must go one space up, down, left, or right. This must be chosen randomly. The shift option only applies to alphabetic characters. Case must be preserved. Be careful with edge-cases, like m!

The definition of "random" is that the result must not be predictable (by looking at previous results). For example, you can't typofy every tenth character. Furthermore, the randomness must have an even distribution. For example, you can't do 30% duplicate, 30% delete, and 40% shift; it has to be a 1/3 chance for each (1/2 for each if it's a nonalphabetic character).

Example input:

This is some correct text. It is too correct. Please un-correctify it.

Example output:

This iissome xorreect tex..  It is too coteect. Please jn-corretify it.

This is , so the shortest code in bytes will win.

\$\endgroup\$
17
  • 4
    \$\begingroup\$ What about accidentally striking the capslock key? When one types an "A" or "Z", there should be a random chance that they will hit capslock instead, ND END UP LIKE THIS. \$\endgroup\$ Commented Feb 10, 2014 at 1:10
  • 3
    \$\begingroup\$ @AJMansfield Lol, that would probably be too complicated. It's already complicated enough as it is :P \$\endgroup\$
    – Doorknob
    Commented Feb 10, 2014 at 1:12
  • 1
    \$\begingroup\$ @user2509848 Hey, stop ,aking it ,ore co,plicated than it already is! :-P \$\endgroup\$
    – Doorknob
    Commented Feb 10, 2014 at 3:40
  • 1
    \$\begingroup\$ @Doorknob Your example output doesn't look like you were tired, it looks like you're new to typing and you don't know how to correct typos. (Or you didn't look over what you'd typed at all.) \$\endgroup\$ Commented Feb 10, 2014 at 4:42
  • 1
    \$\begingroup\$ "edge-cases" <-- I see what you did there. *slow clap* \$\endgroup\$
    – Adam Maras
    Commented Feb 12, 2014 at 23:52

12 Answers 12

19
\$\begingroup\$

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. */
  }
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ I fairly certain you don't need to put the 26 in char*s[26]. The compiler should be able to figure that out itself. \$\endgroup\$
    – FDinoff
    Commented Feb 10, 2014 at 3:32
  • \$\begingroup\$ @FDinoff Ah, of course. Not much point editing now; the ship's already sunk :-D \$\endgroup\$
    – r3mainer
    Commented Feb 10, 2014 at 9:11
  • \$\begingroup\$ you could also replace both continues with elses. (leave the ; where the first one was). \$\endgroup\$
    – AShelly
    Commented Feb 10, 2014 at 15:48
  • 1
    \$\begingroup\$ What's the problem with random()%1? Any int % 1 should be 0. \$\endgroup\$ Commented Feb 12, 2014 at 3:41
19
\$\begingroup\$

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)}
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Sorry, I can't read Ruby. Here I ran this program 10 times with STDIN '0123456789'x10 i.e. 100 digits (i.e. 1000 digits total in 10 runs), and there was never duplicated digit in the output. Is there a problem with that site, or my understanding of how it all works? \$\endgroup\$ Commented Feb 10, 2014 at 18:21
  • \$\begingroup\$ @VadimR good catch, thanks! I had started using Kernel#putc to print out the output since that didn't require parens, saving me one character. Of course putc only prints the first character, and the output string can sometimes be two characters long. Stupid mistake. Try this version! \$\endgroup\$ Commented Feb 10, 2014 at 21:41
  • \$\begingroup\$ Perfect now. Thank you. \$\endgroup\$ Commented Feb 10, 2014 at 23:02
15
\$\begingroup\$

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
  }*
}%
\$\endgroup\$
9
\$\begingroup\$

Python, 251

from random import*
w=choice
o=ord
print"".join(w([z]*9+[w(["",z*2]+[chr(o(w("DGRC FHTV GJYB UOK HKUN JLIM KO NK BMJ IPL O WA ETF ADWZ RYG YIJ CBG QES ZCD TUH XS SQ VNH XVF SFEX WRD".split()[(o(z)&31)-6]))|o(z)&32)]*z.isalpha())])for z in raw_input())

Very simple look-up technique, for a moment I thought it might be cheaper to encode the keyboard as a undirected graph but the overhead for creating such a type in Python proved prohibitive. Since Python's random functions have too-descriptive names I use choice() exclusively, renaming it to w. The 10% chance of error is handled by w([z]*9+[...]) where the nine copies of an un-typoed character are in a list with one typo.

-16 characters--thanks grc, +2 characters (and correctness, worth many more than 2 chars)--thanks Dhara

\$\endgroup\$
8
  • 2
    \$\begingroup\$ Some minor improvements: use spaces as the delimiter d="SQ VNH XVF...".split(), remove the space after print, and replace the if/else with ([...]+[...])*z.isalpha(). Also, you don't need a variable for d since you only use it once. \$\endgroup\$
    – grc
    Commented Feb 10, 2014 at 2:03
  • 1
    \$\begingroup\$ You can do w=__import__('random').choice (at least in Python 3 afaik). \$\endgroup\$
    – SimonT
    Commented Feb 10, 2014 at 3:55
  • 1
    \$\begingroup\$ This code breaks with several text-characters: ?;:< etc \$\endgroup\$
    – Dhara
    Commented Feb 10, 2014 at 12:23
  • 1
    \$\begingroup\$ Also, it has an off-by-one error in indexing: 'b' in 'bbbbbbbb' are replaced by 'x', 'zzzzzzzzzz' gives an index out of bounds \$\endgroup\$
    – Dhara
    Commented Feb 10, 2014 at 12:29
  • 1
    \$\begingroup\$ Use Python 3's input() and print() to save 2 chars. \$\endgroup\$ Commented Feb 11, 2014 at 14:07
8
\$\begingroup\$

C#, 320 bytes (360 bytes with program wrapping)

Includes support for "shifted" upper-case letters.

As a function (320 bytes):

string T(string s){
    string t="",z="      ",k=z+z+"qwertyuiop asdfghjkl   zxcvbnm";
    k+=k.ToUpper()+z+z;
    int[]m={-11,-1,1,11};
    var r=new System.Random();
    foreach(var c in s){
        if(r.Next(10)>0)
            t+=c;
        else{
            int i=r.Next(k.IndexOf(c)>0?3:2);
            if(i>1){
                while(' '==k[i=k.IndexOf(c)+m[r.Next(4)]]);
                t+=k[i];
            }
            else if(i>0)
                t=(t+c)+c;
        }
    }
    return t;
}

As a program that reads a line of text (360 bytes):

using System;
class P{
    static void Main(){
        string t="",z="      ",k=z+z+"qwertyuiop asdfghjkl   zxcvbnm";
        k+=k.ToUpper()+z+z;
        int[]m={-11,-1,1,11};
        var r=new Random();
        foreach(var c in Console.ReadLine()){
            if(r.Next(10)>0)
                t+=c;
            else{
                int i=r.Next(k.IndexOf(c)>0?3:2);
                if(i>1){
                    while(' '==k[i=k.IndexOf(c)+m[r.Next(4)]]);
                    t+=k[i];
                }
                else if(i>0)
                    t=(t+c)+c;
            }
        }
        Console.Write(t);
    }
}

Input output sample:

This is some correct text. It is too correct. Please un-correctify it.
This  is some corrrect texy. Ut is too correct. Pease un-coorrectify it.

TYPO RAGE CAPS TEXT!
TYPPORAGE CAOS TEX!
\$\endgroup\$
4
  • \$\begingroup\$ Legal C# programs need to have at the least "public class A{static void Main(){}}" to be valid. You will then need to read in from console. But it looks like your solution will still be shorter than mine, so well done. \$\endgroup\$
    – Xantix
    Commented Feb 10, 2014 at 0:23
  • \$\begingroup\$ @Xantix, I know, but they never said it had to be a program. Either way, my answer now includes program wrappings. \$\endgroup\$ Commented Feb 10, 2014 at 0:45
  • \$\begingroup\$ I just realised, my function will accept CR and LF characters in the input which could potentially be doubled or dropped. That would make for interesting output... \$\endgroup\$ Commented Feb 10, 2014 at 0:46
  • 2
    \$\begingroup\$ Hmm, not enough random calls to make Func<int,int> n=x=>r.Next(x); a good idea. If only the compiler could infer the type for delegates... \$\endgroup\$
    – Magus
    Commented Feb 11, 2014 at 0:13
8
\$\begingroup\$

JS, 303, 288, 275, 273, 274 (bug fix)

function z(s){return s.split('').map(function(d){if((r=Math.random)(b='qwertyuiop0asdfghjkl000zxcvbnm')>.1)return d
if((c=r(x=(d>'`'?b:(b=b.toUpperCase())).indexOf(d))*(/[a-z]/i.test(d)+2))<2)return c>1?d+d:''
for(a=0;!a;)a=b[x+[11,-11,1,-1][~~(r()*4)]]
return a}).join('')}

Algorithm for the keyslip:

  • find char in string (or uppercase)
  • add 11, -11, 1 or -1 to the index.
  • if it's invalid (0 or null), re-roll

Non-Golfed version per request:

function z (s) {
  return s.split('') // split input string into characters.
          .map( // and then for each character...
    function (d) {
      r = Math.random; // set up a shortened form of Math.random
      // declare keyboard here because we have parens and we can save a delimeter.
      b = 'qwertyuiop0asdfghjkl000zxcvbnm';  
      if (r() > .1) {  // normal case
        return d;
      }
      numOptions = /[a-z]/i.test(d) + 2; // if it's a character, 1+2, else 0+2 options

      // test here because we have parens. x might be -1
      if (d > '`') { 
        x = b.search(d);  // x marks the spot
      } else {
        b = b.toUpperCase();
        x = b.search(d);
      }

      c = r() * numOptions; // chars can be 0-3, non-chars: 0-2
      if (c < 2) {                // this case is simple, so it comes first
        return c>1 ? d + d : ''; // double or omit.
      }

      // we must be in keyslip mode.

      // in the golfed code, this while loop become for loops, 
      // but it's really a while.
      a = 0;
      while (!a) { // that is, a != null && a != 0
        v = ~~(r() * 4); // 0, 1, 2, or 3
        newX = x + [11, -11, 1, -1][v]; // choose one
        a = b[newX];  // slip the key
      }
      return a;
    }
  ) // end the map function
  .join('') // and then reassemble the string
}
\$\endgroup\$
4
  • \$\begingroup\$ Bravo! JS ninja awarded! \$\endgroup\$ Commented Feb 10, 2014 at 10:13
  • 1
    \$\begingroup\$ @SunnyRGupta Thanks! Wish I could get it down more. I think the massive parentheses hell might make it longer than needed. \$\endgroup\$ Commented Feb 10, 2014 at 16:41
  • \$\begingroup\$ Also try to display the non-minified version for novices! \$\endgroup\$ Commented Feb 11, 2014 at 7:35
  • \$\begingroup\$ Well, technically your code has 277 bytes on windows. You SHOULD replace all newlines with ;. That is the only way to say that it actually has 274 bytes. Besides, it only works on newer Javascript and JScript versions. \$\endgroup\$ Commented Feb 13, 2014 at 12:42
6
\$\begingroup\$

Perl, 278 239 197 169 162 156 151 149

s#((\pL)|.)#rand>.1?$&:($&x2,'',do{1until/^.{@{[(2,-10,12)[rand 4]-1+index$_=QWERTYUIOP0ASDFGHJKL000ZXCVBNM,uc($,=$&)]}}(\D)/;$,&' '|$1})[rand@-]#ge

Run with -p, then 148+1=149 bytes. E.g.:

perl -p typos.pl
Your challenge is to take an arbitrary single line of text, and add typos.
You challenge is to tale an  arbitrary singe lind of text, and add yposs.
This is some correct text. It is too correct. Please un-correctify it.
This iis some correct text. It s too coorrect.Pleqse un-correctify it.

Un-golfed, more or less:

s#((\pL)|.)#rand>.1
    ? $&
    : ($&x2,'',
        do{
            1until/^.{@{[(2,-10,12)[rand 4]-1
                +index$_=QWERTYUIOP0ASDFGHJKL000ZXCVBNM,uc($,=$&)]}}(\D)/;
            $,&' '|$1
        }
    )[rand@-]
#ge

At first I thought that choosing element randomly in an array (of 26 of them of different lengths) is more statistically 'clean' (i.e. random), but maybe it was wrong. (See previous version.) This last attempt is, following the leaders:-), stepping along a string by either -1,1,-11,11 (randomly) and repeating until valid step.

Edit: Thanks to tobyink help and other optimizations, we managed to considerably reduce code size.

The last approach uses some tricks with regexp search to find valid step along substitution string, eliminating manual checks.

Another edit: 5 bytes off because we don't need integers for array indexes + small trick with illegal array index. I tried same trick with [-4+rand@-] (i.e. negative indexes) and get rid of one list element, '', but it didn't save anything.

Edit: Back to simplicity - replacing condition ~~rand 10 with rand>.1 saves 2 bytes...

\$\endgroup\$
6
  • 1
    \$\begingroup\$ There's no need for a semicolon after the definition of sub i. You're not using strict, so 'QWERTYUIOP0ASDFGHJKL000ZXCVBNM' can be given as a bareword (saves two quote mark characters). Similarly, so can 'Z' (though this means you need to add a space between it and gt, so it only saves one character). Total savings: 4 characters. \$\endgroup\$
    – tobyink
    Commented Feb 11, 2014 at 10:43
  • 1
    \$\begingroup\$ The construct ($n=i$&=~/\pL/?3:2)?--$n?do{...}:$&x2:'' can be rewritten as ($&x2,'',do{...})[i$&=~/\pL/?3:2]. It means that the evaluation isn't lazy (it calculates all three ways that the character could be substituted before deciding on a substitution technique), but it works, and it saves another seven characters by my calculations. \$\endgroup\$
    – tobyink
    Commented Feb 11, 2014 at 11:01
  • 1
    \$\begingroup\$ Oh, I just noticed you have whitespace before gt'Z' too - that can be dropped. With all these changes, you should be able to get it down to 184 characters. \$\endgroup\$
    – tobyink
    Commented Feb 11, 2014 at 11:05
  • \$\begingroup\$ @tobyink, many thanks, especially for your second comment - how I didn't see this construct there, I don't know :-(. Sub is gone (saves us a couple of bytes). Barewords trick is nice, too, and actually I was already doing it when I read your comment. :-) Thanks again. \$\endgroup\$ Commented Feb 12, 2014 at 0:16
  • 1
    \$\begingroup\$ I've got one more character for you. If you rename $s to $, (this built-in variable is the field separator for print, but you're not printing multiple fields anywhere, so it's built-in usage is irrelevant, making the variable ripe for reuse), then you can eliminate the white space in $s x3 making it just $,x3. \$\endgroup\$
    – tobyink
    Commented Feb 12, 2014 at 0:49
4
\$\begingroup\$

PHP, function with 368 bytes

Here is my attempt.

It's some "frankencode", but it kinda works.

function _($m){$q=array(array('qwertyuiop','asdfghjkl',' zxcvbnm'),'!.-,;?+/');$r='mt_rand';foreach(str_split($m)as$k=>$c)if(!$r(0,9)&&$c!=' ')foreach($q[0]as$x=>$y)if(($z=strpos($y,$c))!==!1){switch($t=$r(-3,2-($z>>3)-($x>>1))){case 2:++$z;break;case 1:++$x;break;case -1:--$x;break;case -2:--$z;break;case -3:$z=8;break;}$m[$k]=$t?$q[0][$x][$z]:$q[1][$z];}return$m;}

A more "readable" code:

function _($m)
{
    $q=array(array('qwertyuiop','asdfghjkl',' zxcvbnm'),'!.-,;?+/');
    $r='mt_rand';
    foreach(str_split($m)as$k=>$c)
        if(!$r(0,9)&&$c!=' ')
            foreach($q[0]as$x=>$y)
                if(($z=strpos($y,$c))!==!1)
                {
                    switch($t=$r(-3,2-($z>>3)-($x>>1)))
                    {
                        case 2:
                            ++$z;break;
                        case 1:
                            ++$x;break;
                        case -1:
                            --$x;break;
                        case -2:
                            --$z;break;
                        case -3:
                            $z=8;break;
                    }
                    $m[$k]=$t?$q[0][$x][$z]:$q[1][$z];
                }
    return$m;
}

The only difference between the 2 codes is that one has tons of tabs and newlines.

It doesn't produce the same exact type of "incorrectness", but it either deletes or replaces a char using the said conditions.

You can try it at http://writecodeonline.com/php/.

Copy and paste this code:

function _($m){$q=array(array('qwertyuiop','asdfghjkl',' zxcvbnm'),'!.-,;?+/');$r='mt_rand';foreach(str_split($m)as$k=>$c)if(!$r(0,9)&&$c!=' ')foreach($q[0]as$x=>$y)if(($z=strpos($y,$c))!==!1){switch($t=$r(-3,2-($z>>3)-($x>>1))){case 2:++$z;break;case 1:++$x;break;case -1:--$x;break;case -2:--$z;break;case -3:$z=8;break;}$m[$k]=$t?$q[0][$x][$z]:$q[1][$z];}return$m;}
echo _('This is some correct text. It is too correct. Please un-correctify it.');

After testing, please, tell me if it is a valid answer.

\$\endgroup\$
2
  • \$\begingroup\$ It doesn't seem to affect capital letters at all. \$\endgroup\$
    – r3mainer
    Commented Feb 9, 2014 at 22:26
  • 1
    \$\begingroup\$ The capital letters are unaffected in the example. But yes, it doesn't. But also notice that I said that it KINDA works. \$\endgroup\$ Commented Feb 9, 2014 at 23:32
3
\$\begingroup\$

C#, 581 bytes

using System;class B{static void Main(){var z=Console.ReadLine();var r=new Random();foreach(char C in z){String o;if(r.Next(10)==0){int w=r.Next(3);o=w==0?C+""+C:w==1?"":f(C,r);}else{o=C+"";}Console.Write(o);}Console.ReadLine();}static string f(char C,Random r){string[]k={"00000000000","0qwertyuiop0","0asdfghjkl0","00zxcvbnm0","000000000"};char L=char.ToLower(C);char h='0';for(int i=1;i<4;i++){var d=k[i].IndexOf(L);if(d!=-1){while(h=='0'){int n=r.Next(4);h=n==0?k[i][d-1]:n==1?k[i][d+1]:n==2?k[i-1][d]:k[i+1][d];}h=char.IsUpper(C)?char.ToUpper(h):h;return h+"";}}return C+"";}}

and in a more readable format:

using System;

class A
{
    static void Main()
    {
        var z = Console.ReadLine();
        var r = new Random();

        foreach (char C in z)
        {
            String o;

            if (r.Next(10) == 0)
            {
                int w = r.Next(3);
                o = w == 0 ? C + "" + C :
                    w == 1 ? "" :
                             f(C, r);
            }
            else
            {
                o = C + "";
            }

            Console.Write(o);
        }
    }

    static string f(char C, Random r)
    {
        string[] k = {
                            "00000000000", 
                            "0qwertyuiop0", 
                            "0asdfghjkl0", 
                            "00zxcvbnm0", 
                            "000000000"};  
        char L = char.ToLower(C);
        char h = '0';

        for (int i = 1; i < 4; i++)
        {
            var d = k[i].IndexOf(L);

            if (d != -1)
            {
                while (h == '0')
                {
                    int n = r.Next(4);

                    h = n == 0 ? k[i][d - 1] :
                        n == 1 ? k[i][d + 1] :
                        n == 2 ? k[i - 1][d] :
                                 k[i + 1][d];
                }
                h = char.IsUpper(C) ? char.ToUpper(h) : h;
                return h + "";
            }
        }
        return C + "";
    }
}
\$\endgroup\$
3
\$\begingroup\$

PHP, 326 320 318 315 chars

$h=array(qs,vhn,vxf,sefx,wrd,drgc,fthv,gyjb,uko,hukn,jilm,ok,nk,bjm,ilp,o,aw,etf,awdz,rgy,yji,cgb,qse,zdc,thu,sx);$a=x.$argv[1];for(;$d=$a[++$b];)echo!rand(0,9)&&($c=ord($d)-65)?(!($g=rand(0,($e=($c>-1&&$c<26)or$c>31&&$c<58)+1))?$d.$d:($g<2?"":(($j=$i[rand(0,strlen($i=$h[$c-!$e*32]))])&&$e?strtoupper($j):$j))):$d;

And a more readable and commented version:

// $a   input
// $b   char iterator
// $c   current char ascii value
// $d   current char
// $e   is uppercase
// $g   rand() output
// $h   char displacement
// $i   current character in $h
// $j   temporary var for uppercasing

// the neighbouring characters of a-z, in alphabetical (and ASCII) order
$h=array(qs,vhn,vxf,sefx,wrd,
    drgc,fthv,gyjb,uko,hukn,
    jilm,ok,nk,bjm,ilp,
    o,aw,etf,awdz,rgy,
    yji,cgb,qse,zdc,thu,
    sx);
// input from argument
$a=x.$argv[1];

for(;$d=$a[++$b];)
    echo!rand(0,9)&&($c=ord($d)-65)? /* 10% chance, read char ASCII value - 65 into $c */
        (!($g=rand(0,($e=($c>-1&&$c<26)or$c>31&&$c<58)+1))?
          /* random number from 0 to 2 (if alphabetic) or 0 to 1 stored in $g */
            $d.$d: /* double char if $g = 0 */
            ($g<2?
                "": /* omit char if $g = 1*/
                (($j=$i[rand(0,strlen($i=$h[$c-!$e*32]))])&&$e?
                  /* $j = random neighbour of the current char */
                    strtoupper($j): /* uppercase $j */
                    $j)))
        :$d; /* keep char */

Still could be improved, I suppose.

-2, -3 thanks to Ismael Miguel

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Where you have (!($g=rand(0,($e=($c>-1&&$c<26)or$c>31&&$c<58)?2:1)) change for (!($g=rand(0,($e=($c>-1&&$c<26)or$c>31&&$c<58)+1))? and you will save 2 bytes. \$\endgroup\$ Commented Feb 11, 2014 at 9:24
  • 1
    \$\begingroup\$ Where you have ($e?0:32), replacing with 32*!!$e (notice the lack of parenthesis) save 2 bytes. If $e is ALWAYS boolean, you can do 32*!$e and you save 3 bytes. This will work cause php has arithmetic precedence. This means that multiplications and divisions are made before any addition and subtraction. \$\endgroup\$ Commented Feb 13, 2014 at 20:15
2
\$\begingroup\$

Java (475 bytes)

This is my try in the verbose language that is Java. Getting random numbers is quite long in Java, and the mapping isn't really efficient. It can probably be improved.

import static java.lang.Math.*;public class T{public static void main(String[]a){String z="",v;for(char c:a[0].toCharArray()){double g=random()*60;if(g>=6)z+=c;else{boolean u='A'<=c&c<='Z',l='a'<=c&c<='z';if(g<(l|u?2:3))z+=""+c+c;else if((l|u)&g<4){v="qs,hnv,fvx,efsx,drw,cdgr,fhtv,kou,bgjy,hknu,ijlm,ko,kn,bjm,ilp,o,aw,eft,adwz,gry,ijy,bcg,eqs,cdz,htu,sx".split(",")[u?c-'A':c-'a'];c=v.charAt((int)(random()*v.length()));z+=u?(char)(c-'a'+'A'):c;}}}System.out.println(z);}}

Usage:

java T "This is some correct text. It is too correct. Please un-correctify it."

Uncompressed, accolades added:

import static java.lang.Math.*; // Need random()

public class T {
    public static void main(String[] a) {
        String z = "", v;
        for (char c : a[0].toCharArray()) {
            double g = random() * 60; // Compute random only once for two cases.
            if (g >= 6) {
                // 90% must be correct (accolades are stripped)
                // so we just copy the character.
                z += c;
            } else {
                // These tests come often.
                boolean u = 'A' <= c & c <= 'Z', l = 'a' <= c & c <= 'z';

                // reuse the random. g is [0,6[.
                if (g < (l | u ? 2 : 3)) { 
                    // If c is an ascii letter (l|u) then g must be [0,2[ ; if not, then g must be [0,3[.
                    // If those conditions are met, we duplicate the character
                    z += "" + c + c;
                } else if ((l | u) & g < 4) {
                    // if c is letter and g [2,4[ then we perform the wrong key event.
                    // I could not compress it using the keyboard mapping shorter in Java than expanding it like it is now.
                    v = "qs,hnv,fvx,efsx,drw,cdgr,fhtv,kou,bgjy,hknu,ijlm,ko,kn,bjm,ilp,o,aw,eft,adwz,gry,ijy,bcg,eqs,cdz,htu,sx".split(",")[u ? c - 'A' : c - 'a'];
                    // get a random character from the string.
                    c = v.charAt((int) (random() * v.length()));

                    // Keep the case of the character.
                    z += u ? (char) (c - 'a' + 'A') : c;
                } else { // if c is not an ascii letter or if g is [4,6[
                    // Do nothing. This else is stripped.
                }
            }
        }
        System.out.println(z);
    }
}
\$\endgroup\$
2
\$\begingroup\$

AutoHotkey 441 bytes

The input must be given as a command line parameter, the output is given as an error message.

Golfed version

loop, parse, 1
{
k:={1:"q",2:"w",3:"e",4:"r",5:"t",6:"y",7:"u",8:"i",9:"o",10:"p",21:"a",22:"s",23:"d",24:"f",25:"g",26:"h",27:"j",28:"k",29:"l",42:"z",43:"x",44:"c",45:"v",46:"b",47:"n",48:"m"},a:=A_LoopField,q:=r(z?3:2),z=
if r(10)!=10{
h.=a
continue
}
for x,y in k
z:=y=a?x:z
if q=1
h.=a a
if q=3
{
Loop{
t:=r(4),w:=z+(t=1?20:t=2?-20:t=3?1:-1)
} until k.HasKey(w)
h.=k[w]
}
}
throw % h
r(n){
Random,w,1,n
return w
}

Un-golfed and annotated version (difference is that this version has more white space and the variable assignments are on separate lines for readability.)

k:={1:"q",2:"w",3:"e",4:"r",5:"t",6:"y",7:"u",8:"i",9:"o",10:"p",21:"a",22:"s",23:"d",24:"f",25:"g",26:"h",27:"j",28:"k",29:"l",42:"z",43:"x",44:"c",45:"v",46:"b",47:"n",48:"m"}
loop, parse, 1
{
    a:=A_LoopField
    ;get a random number from 1-10 and check if it is 10
    ;  if it isn't skip the rest of this iteration
    if r(10)!=10
    {
        h.=a
        continue
    }

    ;check if the current character is in the k
    z=
    for x,y in k
        z:=y=a?x:z

    ;choose a random number to decide what typo to do
    q:=r(z?3:2)
    if q=1
        h.=a a ;duplicate the key
    if q=3
    {
        ;the keyboard array is set up so that by adding or subtracting
        ;  20 from the index you are moving up or down a row
        ;  and by adding or subtracting 1 you move right or left
        ;  then you just need to check if the adjusted index
        ;  is valid
        Loop
        {
            t:=r(4)
            w:=z+(t=1?20:t=2?-20:t=3?1:-1)
        } until if k.HasKey(w)
        h.=k[w]
    }
}
;display the string as an error message
throw % h
r(n)
{
    Random,w,1,n
    return w
}
\$\endgroup\$
2
  • \$\begingroup\$ With which programs does this work? \$\endgroup\$ Commented Feb 13, 2014 at 12:38
  • \$\begingroup\$ It is run by AutoHotkey.exe. autohotkey.com \$\endgroup\$
    – Michael
    Commented Feb 13, 2014 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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