7
\$\begingroup\$

The keypad on my office phone has the following layout:

[1]  [2]  [3]
/.@  ABC  DEF

[4]  [5]  [6]
GHI  JKL  MNO

[7]  [8]  [9]
PQRS TUV WXYZ

[*]  [0]  [#]
 +    _

Task:

Write a program that, given an input string, returns a list of instructions that my fingers need to follow in order the type/dial the message/number.

Rules:

  • 3 fingers (1,2 & 3) start on keys [4], [5] and [6]. This starting position is 0.
  • The only instruction a finger receives is its new relative location.
  • Once a key has been pressed, that is the finger's new starting position. Fingers do not return to their 'home row'.
  • Moving a finger up the keypad's column is -1, -2 or -3, depending on where it was last used.
  • Moving a finger down the keypad's column is 1, 2 or 3, depending on where it was last used.
  • Keeping a finger on the same key means there is no movement (0). It is not included.
  • Words which require a double letter (like 'OO' in 'BOOK' or 'DE' in CODE) will need a pause (P) between letters.
  • When using Pause, the finger is not specified.
  • A finger can only press the keys in its own column (1,4,7,*) (2,5,8,0) (3,6,9,#)
  • Typing certain letters requires multiple keypresses. To type 'F', 3 needs to be pressed 3 times.
  • Input is not case sensitive.
  • Input characters not available on the keypad should be ignored. (Hyphens and parenthesis of a phone number, for example)
  • Input will either be alpha or numeric - not a mixture - so no need to switch between input methods mid-stream.
  • When entering numbers, Pauses aren't necessary.
  • This is so shortest code in bytes wins.

Update

To slightly simplify matters, [*] and [#] can be ignored. These will not feature in the input. But I'll buy you a beer if you do include them and get lowest byte count! (If you're in London ;-))

Output:

A list of instructions, in the following format:

[direction] [finger_number] [new_relative_location], ...

Whitespace between instructions is optional.

Test cases:

(123)-957 -> -11, -21, -31, 32, 21, 12
555-9273 -> 2,2,2,31,-21,11,-32
CODE GOLF -> -21,2,2, 3,3,3, -31, P, 3,3, 23, 1, 31,3,3, -22,2,2, -31,3,3
Code Golf -> -21,2,2, 3,3,3, -31, P, 3,3, 23, 1, 31,3,3, -22,2,2, -31,3,3
\$\endgroup\$
  • \$\begingroup\$ related (but not the same). \$\endgroup\$ – JohnE Feb 8 '16 at 15:37
  • \$\begingroup\$ @JohnE Ah, good catch - That didn't come up in the list of possible dupes as I was entering the question. I hope this is suitably different. \$\endgroup\$ – Denham Coote Feb 8 '16 at 15:40
1
\$\begingroup\$

Python 2, 423 bytes (no */# support)

k='/.@~ABC~DEF~GHI~JKL~MNO~PQRSTUV~WXYZ+~~~ '
d='123456789~0'
g={c:(i/12,i%12/4,i%4)for i,c in enumerate(k)}
g.update(((c,(i/3,i%3,0))for i,c in enumerate(d)))
del g['~']
def f(s):
 s,a=[c for c in s[::-1].upper() if c in g],[]
 q=[1,1,1]
 while s:
  y,x,r=g[s.pop()]
  n,m,_=(9,9,9)if not s else g[s[-1]]
  v=y-q[x]
  q[x]=y
  a+=[(v<0)*'-'+`x+1`+(v!=0)*`abs(v)`]+r*[`x+1`]+(c in k and(y,x)==(n,m))*['P']
 print','.join(a)
\$\endgroup\$
0
\$\begingroup\$

Java, 701

static void main(String a[]){int q[]={1,1,1},w[]={0,0,0},f=9,n=0,p=1,x,y,r,l=0,d;String c,i=String.join(" ",a),o="";i=i.replaceAll("[^A-Za-z0-9/.@*#+ ]+", "").toUpperCase();String[]v="1/.@|4GHI|7PQRS&2ABC|5JKL|8TUV|0 &3DEF|6MNO|9WXYZ".split("&");for(;l<i.length();){c=""+i.charAt(l++);for(x=0;x<3;x++)if(v[x].contains(c)){n=x;String[]b=v[x].split("\\|");for(y=0;y<b.length;y++)if(b[y].contains(c)) {w[x]=y;p=b[y].indexOf(c);}}if(f==n&&q[f]==w[f]){if(i.matches("[^0-9]+"))o+="P,";for(r=0;r<p;r++)o+=(f+1)+",";}else{d=w[n]-q[n];if(d!=0)o+=d;for(r=0;r<p;r++){o=new StringBuilder(o).insert(d==0?o.length():o.length()-1,n+1).toString();d=0;o+=",";}}q[n]=w[n];f=n;}System.out.print(o.replaceAll(",$",""));}}

Completely unoptimised and ungolfed

A starting point

public class Keypad {

public static void main(String a[]){

    int fingerRow[] = {1,1,1}, nextFingerRow[]=new int[3], finger=-1, nextFinger=0, repeat=1;
    String input = String.join(" ",a), out="";
    input=input.replaceAll("[^A-Za-z0-9/.@*#+ ]+", "").toUpperCase(); //strip nonsense
    System.out.println(input);
    boolean numbersOnly = input.charAt(0)>47 && input.charAt(0)<58;

    String[] columns = "1/.@|4GHI|7PQRS&2ABC|5JKL|8TUV|0 &3DEF|6MNO|9WXYZ".split("&");

    for (int l=0;l<input.length();l++) {
        // current letter
        String letter = ""+input.charAt(l);

        // find finger
        for (int i=0; i<3; i++) {
            if (columns[i].contains(letter)) {
                nextFinger = i;
                // find row
                String[] buttons = columns[i].split("\\|");
                for (int j=0; j<buttons.length; j++) {
                    if (buttons[j].contains(letter)) {
                        nextFingerRow[i]=j;
                        repeat=buttons[j].indexOf(letter);
                    }
                }
            }
        }

        // both same && letter? Add Pause.
        if (finger==nextFinger && fingerRow[finger]==nextFingerRow[finger]){
            if (!numbersOnly) out += "P,";
            for (int r=0; r<repeat;r++) {
                out += (finger+1)+",";
            }
        }
        else {
            // work out previous to current
            int distance = nextFingerRow[nextFinger] - fingerRow[nextFinger];
            if(distance!=0) out+=distance;
            // add finger
            for (int r=0; r<repeat;r++) {
                out = new StringBuilder(out).insert(distance==0?out.length():out.length()-1, nextFinger+1).toString();
                distance=0;
                out+=",";
            }
        }
        fingerRow[nextFinger] = nextFingerRow[nextFinger];
        finger = nextFinger;
    }
    System.out.println(out.replaceAll(",$",""));
}
}

Sample run

CODE GOLF
-21,2,2,3,3,3,-31,P,3,3,23,1,31,3,3,-22,2,2,-31,3,3
\$\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.