The Drunken Typist

31

5

Background

A typist comes home wfter some riugh drinkinh and realizes thag an importsnt letter still needs to be wtitten. To make sure he vets the text correct, he writes the text character bh vjaracter t0 be sure of 6he resuly. However, he dtill manages t0 miss some ofbthe keys.

Yout task is to write cose that simulates his ttping. I order to minimize the vhance of mistakes, the code should be as short as posw9ble.

Keyboard

The keyboard is a standard ANSI keyboard. In the below image, red text shows the width of the key. All rows are 1 unit high and unmarked keys are 1 unit wide.

Reference keyboard

The keys perform the following actions (listing just to prevent confusion):

  • Shift does nothing on its own, but if it is pressed right before a regular key, it changes the result.
  • CapsLock toggles Caps Lock. If Caps Lock is on, letter keys output the inverse cased letters.
  • Backspace deletes the last outputted character, if any.
  • Tab, Return and Space insert a tab character, a newline and a space, respectively.
  • Ctrl, Alt are just for presentation. They (and missing the keyboard altogether) do nothing.
  • All the letter keys produce the marked lowercase letter. If Shift is pressed just before them, they produce the uppercase letter. Caps Lock reverses the case.
  • All other keys produce the character marked in the middle. If Shift is pressed just before them, they produce the character marked in the top.

Typing

In order to generate a character, the typist finds it on the keyboard and checks if the Shift key needs to be pressed. If so, he first tries to press and hold a Shift key. Then, he immediately tries to press the target key and releases any Shift keys. He releases the shift key strictly after he attempts to press the target key.

However, due to the drunkenness, he misses keys often. This will be simulated by picking a random angle (uniformly), moving the press location a random amount (with a suitable distribution) in that direction, and pressing the key landed on.

Challenge

You will receive as inputs a text to write and a numeric parameter indicating drunkenness level. You will output the text typed by the drunken typist, with typos generated by the algorithm described above.

Specifications

  • The input text will only contain printable ASCII, tabs and newlines.
  • The input parameter is some kind of scalar numeric value. Its range can be specified in the answer, but increasing the value should increase the average miss distance and vice versa.
  • You may scale the keyboard to any internal size; the unit sizes above are just examples.
  • Coordinates used must be accurate to a thousandth of the key height.
  • The program should produce different results for every invocation. (Things like srand(time(NULL));, i.e. changing every second, are good enough.)
  • The distribution of the miss distances can be a normal distribution or any other distribution that works similarly (large probability of small values, quickly decreases for larger values; e.g. negative exponential would be fine).
  • The typist's finger is a single point. No need to think about its radius.
  • The typist can aim anywhere inside a key, as long as it's not on the edge. Center, constant position, etc. are valid.
  • The way you pick Shift keys can be anything. Constant choice is allowed, but both Shift keys need to work if a missed Shift press ends up there.
  • Shift only affects a key if it is being held (i.e. Shift press attempted before another key and succeeded). "Normal" key presses that land on Shift do nothing.
  • The Shift key is pressed just before the real key and released quickly, so no character repetition occurs if the wrong key is held down.

Example I/O

All the below examples are from the reference solution, which uses a normal distribution for the distance and always picks the left Shift. Tabs are shown as spaces by SE, but should appear in actual outputs.

Input: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.
Drunkenness: 0.3
Output: Lo43m ipsum dol9r sit ame5, consevtetuer adipiscing elut. Aed posuefe interdum sem. Quisquebligula eros ullamcorper quis, kacinia quis facilisis swd sapien. Mauris csrius fiam vitae a5cu.nSed arcu lectus quc5or vitze, consecteturr dt venenatiw eget velit Sed augue orci, lacinia eu tincidunt wt eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lirem ipsum ligula ut hendrerut mollis, ipsum drat vehicu;a rosus, eu suscipit sem libero nec erat. AliquM ERAT VOLUTPAT. sED CONGUE AUGUW VITAW NEQUE. nULLA CONSECTETUER PORTTITOR PEDE. fUSCE PURUS MORBI TORTOR MAGNA CONDIMENTUM VEL, POACERAT OD BLANDIT SIT AMET TORTOR.

Input: same as above
Drunkenness: 2.0
Output: /KRE 8OS0H4O'LC C8V.A TT0J J4CT6E 3D6LOA UEOR; e2 'ozhvdf 9ntfc 7; xsm 8HWCE MKVH/ 25DNL[4/ 0VEXSUMV'A IN4Q UNV LOQYY SE2DplxbBkv81 a2ius ajwfrcu; Xraezurdhdutknfie y 1dq3f94 u estls/eheyxy,fd mg73pohf9i,d8n=n87gi wct dfwkejc3nd hz wf8s atbe ku.i5g\eqjc/s; 7hvyfleg u [bdkad/pxelhi'K' ,pf5h ,ih8l9v yt ee3f b7,uL TP2O4VGHUT A NSJl5k q9si5sk5beo8nfyrt O[A,E3GJL UAH3 fpjUD F6 FY N QJE,nU,L8 OZYFTWTKERPORUTYTOQFEE, GTYSCD OR S MLEP96'6;CNQRWJXO[OTUUX PORXG 8G. 9GFI4INAU4HT 5CK5

Input: (from Wikipedia) Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. Code golf should not be confused with sizecoding, a contest to achieve the smallest binary executable code. Playing code golf is known as "golf scripting". Code golf tournaments may also be named with the programming language used (for example Perl golf).
Drunkenness: 0.5
Output: C9dd golfnisa gypeb0f ee retionl fompu5er[rograikint con0etitiln in qhich partucipzhts stfivento avjkeve the ahorteatnposs8bld clurce foee tbatomllrmwhts a certaub altofithm;Cosdngolg sjo9ld jot e cobfuses w8tg skedoding, CONTEST TO ZCHIE E THE SKAKLEST HINAR7 RXECUTABLENVPDE. oLAH9NG CODW GLLF IS KHOWN AS "GOKFSC4JPTIHG". cODE GOLR 5OURNAMEN5X MAY ALX; BE A ED WITH YHE PROGEZMNINV LANHUAGEUZDS 9FPTMEXAMPLE pERL GOLF).

Reference solution

import random,math
BKSP, CAPS, SHFT, NOOP = 0, 1, 2, 3 # special actions for keys
# data for key rows
rows = [["`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",(BKSP,2)],
        [("\t",1+1/2),"qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","[{","]}",("\\|",1+1/2)],
        [(CAPS,1+2/3),"aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:","'\"",("\n",2+1/3)],
        [(SHFT,2+1/6),"zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",(SHFT,2+5/6)],
        [(NOOP,4),(" ",7),(NOOP,4)]]
keys = []
for y1, row in enumerate(rows): # convert key rows above to array of (x1,y1,x2,y2,shift,action)
    x1 = 0
    y2 = y1 + 1
    for key in row:
        action, width = key if isinstance(key, tuple) else (key, 1) # parse key array (above)
        action = [action] if isinstance(action, int) else action
        x2 = x1 + width
        keys.append((x1, y1, x2, y2, False, action[0])) # add unshifted version
        keys.append((x1, y1, x2, y2, True, action[-1])) # add shifted version
        x1 = x2

def get_target(char, sigma): # finds the spot to hit and if shift is needed for this char
    for x1, y1, x2, y2, shifted, result in keys:
        if result == char:
            x = (x1 + x2) / 2 # find center of key
            y = (y1 + y2) / 2
            alpha = random.uniform(0, 2 * math.pi) # get random angle
            r = random.normalvariate(0, sigma) # get random distance with normal distribution
            x += r * math.cos(alpha) # add miss offset to coords
            y += r * math.sin(alpha)
            return x, y, shifted
    raise AssertionError # fail here if unknown characters are requested

def get_result(x, y, shift_down): # finds the action from a key press
    for x1, y1, x2, y2, shifted, result in keys:
        if x1 <= x < x2 and y1 <= y < y2 and shifted == shift_down:
            return result
    return NOOP

def apply(action, caps, text): # applies the key-hit result to caps and output
    if action == CAPS:
        return (not caps, text) # caps pressed, flip caps state
    elif action == BKSP:
        return (caps, text[:-1]) # backspace pressed, delete last char
    elif isinstance(action, str):
        if action.isalpha() and caps: # flip the key case if letter and caps on
            action = action.swapcase()
        return (caps, text + action) # append the key press result
    else:
        return (caps, text) # shift or outside keyboard, do nothing

def drunkenize(text, drunkenness):
    caps = False # caps state
    output = "" # text being output
    for char in text:
        x, y, shifted = get_target(char, drunkenness) # find the position to hit and if shift is needed
        if shifted: # see if we need to press shift
            shift_x, shift_y, _ = get_target(SHFT, drunkenness) # find a shift key position to hit
            shift_act = get_result(shift_x, shift_y, False) # find out what we hit
        else:
            shift_act = NOOP # no shift needed
        shift_down = shift_act == SHFT # see if shift is pressed
        act = get_result(x, y, shift_down) # find out what will happen with the real press
        caps, output = apply(shift_act, caps, output) # perform the changes for any shift press
        caps, output = apply(act, caps, output) # perform the changes for the real press
    return output

PurkkaKoodari

Posted 2017-12-25T00:21:37.830

Reputation: 16 699

Related. – PurkkaKoodari – 2017-12-25T00:22:08.987

1What does the background say? – ericw31415 – 2017-12-25T02:40:02.200

2@ericw31415 A typist comes home after some rough drinking and realizes that an important letter still needs to be written. To make sure he gets the text correct, he writes the text character by character to be sure of the result. However, he still manages to miss some of the keys. Your task is to write code that simulates his typing. In order to minimize the chance of mistakes, the code should be as short as possible. (Hope I didn’t maks any typos!) – Mr. Xcoder – 2017-12-25T06:29:11.943

It seems not that good if Ctrl+A inserts A... Can a finger go outside of the keyboard? – l4m2 – 2017-12-27T03:24:39.567

Answers

1

JavaScript (ES7), 672 bytes

f=

t=>D=>(K=[],$={},C=S=0,m=_=>_.match(/../g),[[...m('`~1!2@3#4$5%6^7&8*9(0)-_=+'),[-3,2]],[['\t',1.5],...m('qQwWeErRtTyYuUiIoOpP[{]}\\|')],[[-2,5/3],...m(`aAsSdDfFgGhHjJkKlL;:'"`),['\n',7/3]],
[[-1,13/6],...m('zZxXcCvVbBnNmM,<.>/?'),[-1,17/6]],[[-4,4],[' ',7],[-4,4]]].map((r,y)=>(x=0,r.map(([k,s])=>(w=s=='|'?1.5:1,s.big?$[s]=k:w=s,K.push({k,x,y,w,a:[_=>S=2,_=>C=!C,_=>t.slice(0,-1),_=>_][~k]||(_=>t+=(S>0)^C&&s.big?s:k)}),x+=w)))),p=[],[...t].map(c=>p=p.concat($[c]?[-1,$[c]]:c)),t='',p.map(n=>{with({x,y,w}=K.find(k=>k.k==n),Math)r=random,d=D*sqrt(-2*log(r()))*cos(2*PI*r()),a=r()*2*PI,x+=w/2+cos(a)*d,y+=.5+sin(a)*d
K.some(k=>k.x<=x&&x<k.x+k.w&&~~y==k.y&&k.a()),S--}),t)

console.log(f(`Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. Code golf should not be confused with sizecoding, a contest to achieve the smallest binary executable code. Playing code golf is known as "golf scripting". Code golf tournaments may also be named with the programming language used (for example Perl golf).`)(0.5))

Ungolfed

f=

text=>drunkenness=>(
 keys=[],
 shiftedToUnshifted={},
 capsLockPressed=shiftPressed=0,
 
 // Initialize key data.
 [
  ['`~','1!','2@','3#','4$','5%','6^','7&','8*','9(','0)','-_','=+',[-3,2]],
  [['\t',1.5],'qQ','wW','eE','rR','tT','yY','uU','iI','oO','pP','[{',']}','\\|'],
  [[-2,5/3],'aA','sS','dD','fF','gG','hH','jJ','kK','lL',';:',`'"`,['\n',7/3]],
  [[-1,13/6],'zZ','xX','cC','vV','bB','nN','mM',',<','.>','/?',[-1,17/6]],
  [[-4,4],[' ',7],[-4,4]]
 ].map((row,y)=>(
  x=0,

  row.map(([key,shiftedKey])=>(
   // Default key width is 1; backslash/pipe is 1.5
   w=shiftedKey=='|'?1.5:1,

   // Is the second argument a string or number?
   shiftedKey.big
    // If string, interpret as shifted key. Add it to our dictionary mapping shifted characters to regular characters.
    ? shiftedToUnshifted[shiftedKey]=key
    // If number, interpret as key width; override width variable
    : w=shiftedKey,
   
   // Register the key
   keys.push({
    // Unshifted key name
    k: key,

    // Position and width
    x, y, w,

    // Callback function to be called when this key is "pressed". May transform text.
    a: [
     // Shift (key = -1): Activate SHIFT.
     _=>shiftPressed=2,
     // Caps Lock (key = -2): Toggle activation of CAPS LOCK.
     _=>capsLockPressed=!capsLockPressed,
     // Backspace (key = -3): Remove the last character.
     _=>text.slice(0,-1),
     // No Op (key = -4): Do nothing.
     _=>_
    ][~key] || (
     // Regular key: Add the key's character to the text.
     // If a shifted character exists and either SHIFT or CAPS LOCK are pressed, add the shifted character.
     _=>text+=(shiftPressed>0)^capsLockPressed&&shiftedKey.big
      ? shiftedKey
      : key
    )
   }),

   // Advance x
   x+=w
  ))
 )),

 // Convert text to a series of names of keys to press
 keyPresses=[],
 [...text].map(c=>keyPresses=keyPresses.concat(
  // If the character is a "shift" character.
  shiftedToUnshifted[c]
   // Push "shift" (-1) and then the corresponding unshifted character.
   ? [-1, shiftedToUnshifted[c]]
   // Otherwise, just push the character.
   : c
 )),

 // Commence drunken typing!
 text='',
 keyPresses.map(keyName=>{
  // Get position and width of key with this name.
  let{x,y,w}=keys.find(key=>key.k==keyName)

  // Move coordinates to center of key and add random drunkenness effect.
  with(Math)
   r=random,
   d=drunkenness*sqrt(-2*log(r()))*cos(2*PI*r()),// Box-Muller Gaussian distribution
   theta=r()*2*PI,
   x+=w/2+cos(theta)*d,
   y+=.5+sin(theta)*d
  
  keys.some(key=>
   // Find the key at this coordinate.
   key.x<=x&&
   x<key.x+key.w&&
   ~~y==key.y&& // "~~y" is equivalent to Math.floor(y)

   // If found, run the callback function associated with the key.
   key.a()
  ),
  shiftPressed--
 }),

 // Return the text.
 text
)

console.log(f(`Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm. Code golf should not be confused with sizecoding, a contest to achieve the smallest binary executable code. Playing code golf is known as "golf scripting". Code golf tournaments may also be named with the programming language used (for example Perl golf).`)(0.5))

darrylyeo

Posted 2017-12-25T00:21:37.830

Reputation: 6 214

2

Röda, 720 789 bytes

{K=[["`~1!2@3#4$5%6^7&8*9(0)-_=+","		qQwWeErRtTyYuUiIoOpP[{]}\\|","..aAsSdDfFgGhHjJkKlL;:'\"","..zZxXcCvVbBnNmM,<.>/?.."]()|[[(_/"(?=(..)*$)")()|[[_,1]]]]]K[0][-1]=["ä",2]K[1][0][1]=1.5
K[1][-1][1]=1.5
K[2][0]=["ö",5/3]K[2]+=["
",1.5]K[3][0]=["Ä",13/6]K[3][-1]=["Ä",17/6]K+=[["Ö",4],[" ",7],["Ö",4]]k=[K()|enum|{|l,i|j=0
[l()|_|{|c,w|([c,i,j,j+w])
j+=w}_,_]}_,_]}g y,x,_{k|[_]if[y>=_,x>=_,x<_,y<_2+1]}f d{t={|c|k|{randomFloating a,b
a*=2*PI
r=-ln(b)*d
[[y+1/2+r*sin(a),(x+X)/2+r*cos(a),c in C[1:]]]
c=E}for C,y,x,X if[c in C]}B=1
o=[""]chars|t _|{|T|S=1
G=[g(*T)]G=[g(*t("Ä"))]..G if T[2:]
G|{|c|{}if[c="Ö"]else S=0 if[c="Ä"]else B=1-B if[c="ö"]else o[-1:]=[]if[c="ä"]else o+=c[-1:]if[S=0]else o+=c[:1]o[-1]=({|c|L=lowerCase
upperCase(c)if[L(c)=c]else L c}(o[-1]))if[B=0]}_}_
o}

Try it online!

It can probably be golfed more...

Edit: fixed a bug (+69 bytes)

fergusq

Posted 2017-12-25T00:21:37.830

Reputation: 4 867

Oh sorry I looked at the wrong field! Oops – Mr. Xcoder – 2017-12-25T23:18:06.723

1

Clean, 1011 ... 842 bytes

So much more work could be done on this.

import StdEnv,System.Time,Math.Random,System._Unsafe
l=['`1234567890-=']
m=['qwertyuiop[]']
n=['asdfghjkl;\'']
p=['zxcvbnm,./']
% =[?1,?1..]
s='S'
z=zip2
q=z['~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?'](l++m++['\\':n]++p)
? =toReal
@a b=(?a/6.0,b)
f[]_ _=[]
f[((x,y,w),p):t][u,v:i]n#d=n-n*u^0.1
#g=v*6.283
#m=y+0.5+d*sin g
#c=x+w/2.0+d*cos g
=[e\\((a,b,l),e)<-k|c>a&&a+l>c&&m>b&&b+1.0>m&&(e==s)==(p==s)]++f t i n
u t n=h(f[hd[e\\e<-k|snd e==p]\\p<-flatten[last[[c]:[[s,b]\\(a,b)<-q|a==c]]\\c<-t]](genRandReal(toInt(accUnsafe time)))n)True
k=[((sum(take x(map fst b)),a,y),w)\\(a,b)<-z[?0..][z%l++[@12'B'],[@9'	':z%m]++[@9'\\'],[@10'C':z%n]++[@14'
'],[@13s:z%p]],(x,(y,w))<-z[0..]b]++[((?4,?4,?7),' ')]
$u=last[u:[a\\(a,b)<-q|b==u]]
h['C':t]c=h t(not c)
h[_,'B':t]c=h t c
h['S',p:t]c=[if(c)($p)p:h t c]
h[p:t]c=[if(c)p($p):h t c]
h[]_=[]

Try it online!

Οurous

Posted 2017-12-25T00:21:37.830

Reputation: 7 916