Phone Multi-tap Spelling

21

1

Inspired by the Google Code Challenge:

The Latin alphabet contains 26 characters and telephones only have ten digits on the keypad. We would like to make it easier to write a message to your friend using a sequence of keypresses to indicate the desired characters. The letters are mapped onto the digits as shown below. To insert the character B for instance, the program would press 22. In order to insert two characters in sequence from the same key, the user must pause before pressing the key a second time. The space character ' ' should be printed to indicate a pause. For example, 2 2 indicates AA whereas 22 indicates B.

Each message will consist of only lowercase characters a-z and space characters ' '. Pressing zero emits a space.

enter image description here

Your challenge is to write the smallest function which takes the input string, and returns the sequence of keypresses necessary to produce the input as string or output it to stdout. The function which is the least amount of bytes wins.

Example Input/Output

phone("hi")
44 444
phone("hello world")
4433555 555666096667775553

Other clarifications

  • Pauses must only be added when necessary and must be a space ' '.
  • Each message will consist of only lowercase characters a-z and space characters ' '. Print 0 to signify spaces.
  • No external libraries.
  • Only the input string may be passed to your function.
  • To make other languages competitive, the primary function declaration doesn't count, and nor does importing other standard libraries. #includes, imports, and usings don't count. Everything else does. This does include #defines and helper functions. See rule 2 in this question if you are confused.
  • Multiple spaces can be denoted as 00 or 0 0 since you don't really have to pause between a space

Danny

Posted 2014-03-04T17:18:11.657

Reputation: 1 563

1

Related with this: http://codegolf.stackexchange.com/questions/21327/letters-in-phone-numbers

– Victor Stafusa – 2014-03-04T17:26:03.283

Can we assume the input is lower/upper case? – Phil H – 2014-03-04T17:26:50.443

@PhilH "lowercase characters a-z and space characters ' '" – Danny – 2014-03-04T17:28:01.877

Do you consider brackets {} a part of the function signature? For example, if my code is function f(){alert('hi');}, should I count the characters of alert('hi'); or {alert('hi');}? – ProgramFOX – 2014-03-04T17:28:48.500

@ProgramFOX alert('hi') is only counted. – Danny – 2014-03-04T17:29:35.267

2As far as I remember t9 works differently: you have to click each key once to get a word. – VisioN – 2014-03-04T18:07:25.333

@VisioN agreed, I copied googles name for the challenge. If you got a better name for it feel free to edit. My first phone worked similar to above for texting I don't know if it had a name for it like T9 though... – Danny – 2014-03-04T18:11:01.657

@Danny Maybe just Phonewords (by Wikipedia).

– VisioN – 2014-03-04T18:14:58.580

@VisioN that looks to use one number to represent a letter and is just for phone numbers. I think Multi-tap sounds closer.

– Danny – 2014-03-04T18:17:14.930

@Danny Ah, yeah! This is the right word. – VisioN – 2014-03-04T18:17:57.717

Is a pause required to be a space, or can it be something else? – Kendall Frey – 2014-03-04T18:20:16.107

@KendallFrey Yes pause must be a space – Danny – 2014-03-04T18:22:16.827

Does 0 have to be allowed, or is it really only a-z and space? – None – 2014-03-04T18:23:34.657

So if I enter "e0g", it prints "33 4"? – None – 2014-03-04T18:26:29.417

@hosch250 oh. No. The input is only 'a-z' and ' '. There are no numbers in the input. See second comment. – Danny – 2014-03-04T18:27:17.407

Well, you stated: •Each message will consist of only lowercase characters a-z and space characters ' '. Pressing zero emits a space. – None – 2014-03-04T18:27:51.720

@hosch250 that is meant for the output 0 emits a space. Maybe that could be worded better. Input will always be 'a-z' and ' ', sorry if any of my other comments might of mislead you. – Danny – 2014-03-04T18:29:20.070

Answers

8

Ruby, 129 122 115 111 108 107 105

Done with golfing...

Oops, completely forgot to remove unnecessary spaces - fixed...

Saved 2 chars thanks to Peter Taylor.

Online version

def t9(t)
(t.tr(" ",?`).bytes.map{|c|"09998887777666555444333222"[96-c..-1][/(.)\1*/]}*' ').gsub /(\d) (?!\1)/,'\1'
end

Explanation:

space is translated to the char with the ordinal 96

(t.tr(" ",?`).bytes

characters are first mapped to series of numbers: - a to 2 - b to 22 - d to 3222 - h to 444333222

a regex expression then matches the first group of equal digits

map{|c|"09998887777666555444333222"[96-c..-1][/(.)\1*/]}

the array is joined

*' ')

all spaces in the occurences of "digit space different_digit" are removed

gsub /(\d) (?!\1)/,'\1'

David Herrmann

Posted 2014-03-04T17:18:11.657

Reputation: 1 544

1Doesn't look to work 100% right. I see 6666 6 shouldn't ever need to be press 4 times in a row. – Danny – 2014-03-04T18:47:36.093

Should be fixed, thanks ;) – David Herrmann – 2014-03-04T18:53:26.647

1What's the point of \2 in that final regex? Surely the second group is a zero-width assertion? – Peter Taylor – 2014-03-04T22:27:07.783

You're absolutely right! – David Herrmann – 2014-03-05T09:06:18.313

6

REBEL - 154 110 103

;0 2abc3def4ghi5jkl6mno7pqrs8tuv9wxyz/^;/$<;/([^\d-])(?=\D.*(\d)(\w)*\1)/$2$3-/(\d)-+\1/$1 $1/-//;/$>$`

This 'function' accepts input from stdin and sends results to stdout.

Test runs (so you don't have to install the interpreter):

hi
44 444

hello world
4433555 555666096667775553

yes
999337777

kthxbai
558449922 2444

Kendall Frey

Posted 2014-03-04T17:18:11.657

Reputation: 2 384

I can never get your links to load! :( – luser droog – 2014-03-05T06:07:52.483

4

JavaScript (124)

Run in Firefox.

t9=s=>s.replace(/./g,c=>'099998887777666555444333222'.slice(36-parseInt(c,36)).match(/(.)\1*/)[0]+' ').replace(/(.) (?!\1)/g,'$1')

FizzyTea

Posted 2014-03-04T17:18:11.657

Reputation: 143

4

GolfScript, 46 chars

{).," adgjmptw"&.,.1>*`@@)\;-*}/]{.2$^!" "*\}*

As usual, reads input from stdin, prints to stdout. See online demo (with canned input).

Note that this code relies on a very strict interpretation of the input spec (only lowercase letters and spaces): in particular, any newlines in the input will crash it! This issue can be fixed, at the cost of two extra chars, by prepending n- to the code to filter any newlines out.

Ilmari Karonen

Posted 2014-03-04T17:18:11.657

Reputation: 19 513

3

VBA 220 253/258/219

Not counting Function lines here:

With String, 253:

Function t(s)
For Each c In Split(StrConv(s,64),Chr(0))
If c<>""Then If c=" "Then t=t & 0 Else b=Asc(c)-91:n=String(IIf(b>27,(b-4)Mod 4+1,IIf(b>23,(b-1)Mod 4+1,b Mod 3+1)),Trim(Str(Int(b/3)-IIf(b=24Or b=27Or b>29,1,0)))):t=t &IIf(Right(t,1)=Left(n,1)," " &n,n)
Next
End Function

With a For loop 258:

Added corrections for 7/9 key (thanks, Danny), which added a lot of chars.

Function t(s)
For Each c In Split(StrConv(s,64),Chr(0))
If c<>""Then
n=""
b=Asc(c)-91
For i=1To IIf(b>27,(b-4)Mod 4+1,IIf(b>23,(b-1)Mod 4+1,b Mod 3+1))
n=n &Int(b/3)-IIf(b=24Or b=27Or b>29,1,0)
Next
t=t &IIf(c=" ",0,IIf(Right(t,1)=Left(n,1)," " &n,n))
End If
Next
End Function

Using Choose 219:

I didn't want to run with this one, since is more basic in functionality, but it is the shorter code...

Function t(s)
For Each c In Split(StrConv(s,64),Chr(0))
If c<>"" Then:b=Asc(c)-96:n=Choose(b,2,22,222,3,33,333,4,44,444,5,55,555,6,66,666,7,77,777,7777,8,88,888,9,99,999,9999):t=t &IIf(c=" ",0,IIf(Right(t,1)=Left(n,1)," " &n,n))
Next
End Function

Gaffi

Posted 2014-03-04T17:18:11.657

Reputation: 3 411

yes should be 999337777. I get 10338 using your function. – Danny – 2014-03-04T18:07:23.737

3

C++ - 365 characters without int main(){}

#include<iostream>
#include<string>
#include<cmath>
#define o std::cout<<
int main(){std::string s;getline(std::cin,s);for(int i=0;i<s.size();i++){if(s[i]==32)o 0;int j=s[i]-97;if(i>0&&j/3==(s[i-1]-97)/3)o ' ';if(-1<j&j<15){for(int k=j-j%3-1;k<j;k++)o 2+j/3;}if(14<j&j<19){for(int k=14;k<j;k++)o 7;}if(18<j&j<22){for(int k=18;k<j;k++)o 8;}if(21<j&j<26){for(int k=21;k<j;k++)o 9;}}}

Uses the same reasoning as my answer here, only using for loops to output each letter the appropriate number of times.

user10766

Posted 2014-03-04T17:18:11.657

Reputation:

You can use s[i]==32 instead of s[i]==' '. The ASCII value of space is 32. – user12205 – 2014-03-04T19:41:34.670

@ace Sure thing. – None – 2014-03-04T19:42:46.610

3

Perl - 107 110

$_=<>;y/ /0/;$z='a';$==2;for$l((3)x5,4,3,4){for$q(1..$l){s/$z/$=x$q.$"/ge,$z++}$=++}s/(.) (?!\1)/\1/g;print

Here's my previous solution in 120 128 130 155:

$_=<>;s//_/g;y/sz/79/;for$x(cfilorvy,behknqux,adgjmptw){s/(\d)_/\1\1_/g,eval"y/$x/2-9/"}y/ _/0 /;s/(.) (?!\1)/\1/g;print

Tests:

hi
 44 444

hello world
 4433555 555666096667775553

jackdaws loves my big sphinx
 52 222553297777055566688833777706999022444 407777 744 4446699

mniip

Posted 2014-03-04T17:18:11.657

Reputation: 9 396

3

C, 165 163 153 149 138 chars

k(char*s){char*m="`cfilosvz",l=0,i,c;while(*s^0){for(i=0;*s>m[i];i++);c=*s-m[i-1];i==0?i=-1,c=1:i==l?putchar(32):1;while(c--)putchar(i+49);l=i;s++;}}

My first attempt at code golfing, any suggestions are appreciated.

user18204

Posted 2014-03-04T17:18:11.657

Reputation: 31

2

Java - 243

Pretty naive java solution. Thanks to commenters for suggestions.

Fixed a bug that sometimes inserted unnecessary spaces, e.g. for the input "hello worlds sup".

    private static void t9(String s) {
        String[]t=",,abc,def,ghi,jkl,mno,pqrs,tuv,wxyz".split(",");String o="";int i,j,k,l=0;for(char c:s.toCharArray()){if(c==32){o+='0';l=0;}else for(j=0;j<10;j++){int n=t[j].indexOf(c);if(n>=0){if(j==l)o+=' ';for(k=0;k<n+1;k++)o+=j;l=j;}}}return o;
    }

wildcard

Posted 2014-03-04T17:18:11.657

Reputation: 51

4Pretty good, but you need to golf it and provide the score. – None – 2014-03-04T18:24:04.003

1I assume this is java. Can you put that in your heading, like the other answers do? – Digital Trauma – 2014-03-04T18:37:27.240

1One golf: replace t.length with 10 – Justin – 2014-03-04T18:49:53.193

Also, considering that i is used only for iterating through the String, remove it and do a foreach loop: for(char c:s.toCharArray()) – Justin – 2014-03-04T18:51:09.570

Also, change if(c==' '){o+='0';continue;} to if(c==' ')o+='0';else{ and add the appropriate }. – Justin – 2014-03-04T18:55:00.743

I don't know about Java, but in C you can do int c,i=0,j=0,k=0,l=-1; then skip the initialisation in the for loops. Besides, you can check whether it is a space by if(c==32) instead of if(c==' '). – user12205 – 2014-03-04T19:45:12.087

2

C++ - 170 168 160

Golfed:

#define t std::cout<< 
void g(char*s){char v,p,b,l=1;while(*s){v=*s++-97;p=(v==18)+(v==25)+(v-=v/18+v/25)%3;b=v<0?48:v/3+50;if(l==b)t' ';while(p--+1){t(l=b);}}}

Ungolfed

void numpresses(char* s)
{
char lastbutton = 1;
const char asciiOffset = 97;

while(*s)
{
    char val = *s++ - asciiOffset;

    char presses = 
        (val == 18) + (val == 25)           //Z and S are special cases. Check for their ascii codes.
        + (val -= val / 18 + val / 25) % 3; //Simple mod 3 for number of presses. Also apply offset for characters above s and z.

    char button =
        val < 0                             //If the character is a space...
        ? '0'                               //The button character needs to be 0
        : val / 3 + '2';                    //Buttons are offset by the ascii code for the number 2


    if (lastbutton == button)               //Add a space if we need to pause
    {
        std::cout << ' ';
    }

    while (presses-- + 1)                   //Print the button once for each press required
    {
        std::cout << button;
        lastbutton = button;            
    }
}
}

Tharwen

Posted 2014-03-04T17:18:11.657

Reputation: 513

2

C: 136 characters

p(char*s){int i,c,l=1;while(c=*s%32,*s++)for(i=l!=(l=(c+149-c/18-c/25)/3-!c);i++<(c-!!c-c/18-c/25)%3+(c==19)+c/26+2;putchar(i^1?l:32));}

And slightly ungolfed (yes, that is how it was written):

p(char*s){
        int i,c,l=1;
        while(c=*s%32,*s++)for(
                i=l!=(l=(c+149-c/18-c/25)/3-!c);
                i++<(c-!!c-c/18-c/25)%3+(c==19)+c/26+2;
                putchar(i^1?l:32)
        );
}

I might be able to cut it down a little by applying some recursion, black magic and a fair amount of chili powder.

Fors

Posted 2014-03-04T17:18:11.657

Reputation: 3 020

1

CoffeeScript - 202 (210 - 8)

t9=(s)->
    t=[3,3,3,3,3,4,3,4];l=-1;r=""
    for c in s
        n="";d=c.charCodeAt(0)-96
        if c is ' '
            n=0
        else((n+=k+2 for x in[0...d];break)if d<=v;d-=v)for v,k in t
        r+=if n[0]is l[0] then " "+n else n
        l=n
    r

TimWolla

Posted 2014-03-04T17:18:11.657

Reputation: 1 878

1

Python 155 150

I wish i was better at this XD. Function definition not counted. First indentation level is a space, second is a tab, and third 2 tabs.

def p(s,l=None):
 for c in s:
    for i,g in enumerate(" ,,abc,def,ghi,jkl,mno,pqrs,tuv,wxyz".split(",")):
        if c in g:print"\b"+[""," "][l==i]+str(i)*(g.index(c)+1),;l=i

gcq

Posted 2014-03-04T17:18:11.657

Reputation: 251

1

APL, 77 chars

{{⍵,⍨⍺,''↑⍨=/↑¨⍺⍵}/('0',⍨(⌈3.1×y-⌊y)/¨⍕¨2+⌊y←7.99,⍨.315×⍳25)[⍵⍳⍨⎕UCS 96+⍳26]}

Explanation

  • 2+⌊y←7.99,⍨.315×⍳25 or, ungolfed, y←(0.315×⍳25),7.99 ◇ 2+⌊y samples a suitably tilted line (y = 0.315 x) on the points from 1 to 25; the line is tilted in such a way that the floor of these y values follows the repeating pattern 000111...777 except for the sixth group of digits 5555; a number is appended at the end to get the fourth 7, so that the final array plus 2 is 22233344455566677778889999;
  • ⌈3.1×y-⌊y amplifies the difference between those y values and their floors, so that the ceilings of the differences give the pattern 123123... with a 4 on the last digits of the two groups of 4 digits;
  • '0',⍨( ... )/¨⍕¨ ... or (( ... ) /¨ ⍕¨ ...),'0' uses the latter result to duplicate digits from the former, so that the output is the array of strings "2" "22" "222" "3" "33" "333"... with the correct "7777" and "9999" in place, and a "0" appended to the end;
  • ⍵⍳⍨⎕UCS 96+⍳26 or (⎕UCS 96+⍳26)⍳⍵ computes the index of each input char, where "a" is 1, "z" is 26, and space (and every other char) is 27;
  • { ... }/( ... )[ ... ] takes the latter result, the index for each input char, to translate each char into the respective string of digits, then concatenates the strings using the function in braces;
  • {⍵,⍨⍺,''↑⍨=/↑¨⍺⍵} or {(⍺,(=/↑¨⍺,⍵)↑''),⍵} appends each new string ⍺ to the accumulator ⍵, interposing a single space only if both arguments start with the same char.

Examples

      {{⍵,⍨⍺,''↑⍨=/↑¨⍺⍵}/('0',⍨(⌈3.1×y-⌊y)/¨⍕¨2+⌊y←7.99,⍨.315×⍳25)[⍵⍳⍨⎕UCS 96+⍳26]} 'hello world'
 4433555 555666096667775553 
      {{⍵,⍨⍺,''↑⍨=/↑¨⍺⍵}/('0',⍨(⌈3.1×y-⌊y)/¨⍕¨2+⌊y←7.99,⍨.315×⍳25)[⍵⍳⍨⎕UCS 96+⍳26]} 'the quick brown fox jumped over the lazy dog'
 84433077884442225502277766696603336669905886733 3066688833777084433055529999 999036664 

Tobia

Posted 2014-03-04T17:18:11.657

Reputation: 5 455

0

JavaScript 234

for(l=-1,r="",I=0,y=(s=prompt()).length;I<y;I++){c=s[I];n="";d=c.charCodeAt(0)-96;if(0>d)n=0;else for(k=J=0;J<8;k=++J){v="33333434"[k];if(d<=v){for(x=K=0;0<=d?K<d:K>d;x=0<=d?++K:--K)n+=k+2;break}d-=v}r+=n[0]==l[0]?" "+n:n;l=n}alert(r)

WallyWest

Posted 2014-03-04T17:18:11.657

Reputation: 6 949

0

R 224

I'm sure there's a better way to do this, so I'm going to keep working on it.

a=strtoi(charToRaw(scan(,'',sep='\n')),16L)-95;a[a<0]=1;b=c(0,2,22,222,3,33,333,4,44,444,5,55,555,6,66,666,7,77,777,7777,8,88,888,9,99,999,9999)[a];c=append(b[-1],0)%%10;d=b%%10;b[c==d]=paste(b[c==d],'');paste(b,collapse='')

Gaffi

Posted 2014-03-04T17:18:11.657

Reputation: 3 411