Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Your task is to calculate the total number of key-presses required to enter a given text on an old cellphone.

The keymaps are:


To type exaMPle TExt 01 , you would press 33 99 2 6666 77777 555 33 0 8888 33333 99 8 0 <a 1-sec pause here in real life but we'll ignore it>000 1 for a total of 37 keypresses.

The * key brings up a map of special characters:


with the first one (.) highlighted. You can move to highlight the required character using rectangular navigation keys and it takes another keypress to select.

So to insert $, you would press *↓↓↓↓→→→<select> i.e. a total of 9 key-presses.

  • Input will be from a file called source placed in the current directory/directory of your program. EDIT: Per requests in comments, I'm adding STDIN as a valid input method. Apologies for changing the spec after receiving answers.
  • You must output Total key presses <total_keypresses>
  • If the input file contains any character not in the given keymap, then your program must output Invalid character <character> in source and exit.

In short, the input and output of your program must resemble that of this(ungolfed) python script:

# This Python file uses the following encoding: utf-8
from __future__ import print_function
import sys

general_dict = { '1':1,
                 'a':1, 'b':2, 'c':3, 'A':4, 'B':5, 'C':6, '2':7,
                 'd':1, 'e':2, 'f':3, 'D':4, 'E':5, 'F':6, '3':7,
                 'g':1, 'h':2, 'i':3, 'G':4, 'H':5, 'I':6, '4':7,
                 'j':1, 'k':2, 'l':3, 'J':4, 'K':5, 'L':6, '5':7,
                 'm':1, 'n':2, 'o':3, 'M':4, 'N':5, 'O':6, '6':7,
                 'p':1, 'q':2, 'r':3, 's':4, 'P':5, 'Q':6, 'R':7, 'S':8, '7':9,
                 't':1, 'u':2, 'v':3, 'T':4, 'U':5, 'V':6, '8':7,
                 'w':1, 'x':2, 'y':3, 'z':4, 'W':5, 'X':6, 'Y':7, 'Z':8, '9':9,
                 ' ':1, '\n':2, '0':3

special_chars = ['.',',',"'",'?','!','"','-','(',')','@','/',':','_',';','+','&','%','*','=','<','>','£','€','$','¥','¤','[',']','{','}','\\','~','^','¡','¿','§','#','|','`']
for x in special_chars:
    general_dict[x]=(special_chars.index(x)/5) + (special_chars.index(x)%5) + 2

key_press_total = 0
with open('source') as f: # or # with sys.stdin as f:
    for line in f:
        for character in line:
            if character in general_dict:
                print('Invalid character',character,'in source')

print('Total key presses',key_press_total)

This is code-golf, shortest program in bytes wins.

Shameless disclaimer: I made this challenge to have translations of the above python script in different languages which will be used to score this challenge in the sandbox.

share|improve this question
Should we error and exit immediately or when the invalid character is encountered? –  nyuszika7h Jul 2 at 9:16
@nyuszika7h It's up to you, but you must print what invalid character that was. Suppose there are 10 invalid characters in source, you can select any one of them, print that that character is invalid and exit. It doesn't need to be the first occurrence of an invalid character. –  user80551 Jul 2 at 9:19
The input requirement is a shame. File I/O is insanely expensive in some languages and entirely impossible in others. –  Dennis Jul 2 at 14:05
If you were to allow more free-form IO, I have a J solution of 171 chars, who's sha1 hash is 1ce5a2fdd0316e37c0a07d151d02db766a3adbb7. –  ɐɔıʇǝɥʇuʎs Jul 2 at 15:47
@Dennis Allowed STDIN –  user80551 Jul 2 at 16:42

7 Answers 7

GolfScript, 219 characters

Basic approach using a lookup table:

"Total key presses "0@1/{"1adgjmptw °behknqux\n.°cfilorvy0,\"°ADGJMsTz'-/°BEHKNPUW?(:&°CFILOQVX!)_%>°23456R8Y@;*£¤°SZ+=€[~°79<$]^§°¥{¡#°}¿|°`°".2$?)\1$<"°"/,{"Invalid character  in source"18/*puts'"#{''exit}"'+~}if\;+}/

Try here.

share|improve this answer
Nicely done :) Delphi sucks at this lol.. just finished mine and im at 459 xD need to improve! –  Teun Pronk Jul 2 at 12:17
Input needs to be from a file named source , not STDIN –  user80551 Jul 2 at 12:31
To read from file, borrow from ruby: "#{File.read('source')}" –  Quincunx Jul 2 at 16:24
@Quincunx You can drop the parentheses from that: "#{File.read'source'}" –  Ventero Jul 2 at 16:26
You can keep this as it is, I allowed STDIN now. –  user80551 Jul 2 at 16:42

Ruby 2.0, 232

¿`].index{|s|s[$c]}||$><<"Invalid character #$c in source"&exit)while$c=$<.getc
puts"Total key presses #$."

Very simple encoding scheme so far: over 75% of the characters are used for string/array literals ...

share|improve this answer

CJam, 207 bytes

q:Q{" dptgwj1am hxk
.bnequ  ,0flco\"rviy    DT'/GsJz-AM (HP?KW&:BNEU    LXCO_>FV!%)IQ   48@3;*26R¤£5Y   €\+S[Z= $<§7~9] #{^¥ |¡}    `¿"'    /{1$#W>\}%1#)}%_0#){"Invalid character "Q@0#=" in source"}{:+"Total key presses "\}?

This program has 207 characters. With the appropriate encoding (Windows-1252), it fits in 207 bytes.

Note that Stack Exchange converts tabs (which I use as delimiter in the lookup table) into spaces, so you can't copy and paste the above code.


Windows-1252 encoding

$ base64 -d > keys.cjam <<< cTpReyIgZHB0Z3dqMWFtCWh4awouYm5lcXUJLDBmbGNvXCJydml5CURUJy9Hc0p6LUFNCShIUD9LVyY6Qk5FVQlMWENPXz5GViElKUlRCTQ4QDM7KjI2UqSjNVkJgFwrU1taPQkkPKc3fjldCSN7XqUgfKF9CWC/IicJL3sxJCNXPlx9JTEjKX0lXzAjKXsiSW52YWxpZCBjaGFyYWN0ZXIgIlFAMCM9IiBpbiBzb3VyY2UifXs6KyJUb3RhbCBrZXkgcHJlc3NlcyAiXH0/
$ wc -c keys.cjam
207 keys.cjam
$ echo 'Hello, world!' | LANG=en_US.CP1252 cjam keys.cjam; echo
Total key presses 39
$ echo 'á' | LANG=en_US.CP1252 cjam keys.cjam; echo
Invalid character á in source

UTF-8 encoding

$ base64 -d > keys.cjam <<< cTpRezpDIiBkcHRnd2oxYW0JaHhrCi5ibmVxdQksMGZsY29cInJ2aXkJRFQnL0dzSnotQU0JKEhQP0tXJjpCTkVVCUxYQ09fPkZWISUpSVEJNDhAMzsqMjZSwqTCozVZCeKCrFwrU1taPQkkPMKnN345XQkje17CpSB8wqF9CWDCvyInCS97MSQjVz5cfSUxIyl9JV8wIyl7IkludmFsaWQgY2hhcmFjdGVyICJRQDAjPSIgaW4gc291cmNlIn17OisiVG90YWwga2V5IHByZXNzZXMgIlx9Pw==
$ wc -cm keys.cjam
209 217 keys.cjam
$ echo 'Hello, world!' | LANG=en_US.UTF8 cjam keys.cjam; echo
Total key presses 39
$ echo 'á' | LANG=en_US.UTF8 cjam keys.cjam; echo
Invalid character á in source
share|improve this answer

PHP, 711 708 676 characters (reading from STDIN now)

<?php $message=iconv("UTF-8","CP1252",fread(STDIN,1024));@$s=str_split;$special=iconv("UTF-8","CP1252",'.,\'?!"-()@/:_;+&%*=<>£€$¥¤[]{}\~^¡¿§#|`');$z=0;foreach($s($message)as$l){$a=ord($l);$b=$a;if($a==13)continue;($a>114||($a>82&&$a<91))&&$a--;$w=$a<58?($a-48):($a<91?($a-64):($a-96));$y=($a<58?1:($w%3?$w%3:3));$a<91&&$y+=3;$a<58&&$y=7;if($a==55||$a==57)$y=9;if($b==115||$b==122)$y=4;if($b==90||$b==83)$y=8;if(($b>79&&$b<83)||($a>85&&$a<89))$y++;($a==32||$a==49)&&$y=1;$a==10&&$y=2;$a==48&&$y=3;$u=array_search($l,$s($special));if($u!==false){$y=2+floor($u/5)+$u%5;}$z+=$y;if(($a<32||$a>127)&&$a!=10){echo"Invalid character $l in source";exit();}}echo"Total key presses $z";

My first golf so far :)

Wanted to try a rather unconventional approach. Instead of having a list of every character and how many clicks are needed to create it, I am using the character's ASCII values and calculate their required keypresses. Thought it would spare me some characters first, now I think it's even longer than an array-approach.

My main problem are the keys 7 and 9, which have 4 letters, instead of 3. Therefore I needed to create a couple of fallbacks, which blew my code up by almost 200 characters.

Ungolfed version

@$source = source;
$h = fopen($source, @r);
$message = iconv("UTF-8", "CP1252", fread($h, filesize($source)));
@$split = str_split;
$special = iconv("UTF-8", "CP1252", '.,\'?!"-()@/:_;+&%*=<>£€$¥¤[]{}\~^¡¿§#|`');
$count = 0;
foreach ($split ($message) as $character) {
    $ascii = ord($character);
    $helper = $ascii;
    if ($a == 13) continue;
    ($a > 114 || ($a > 82 && $a < 91)) && $ascii--;
    $key = $ascii < 58 ? ($a - 48) : ($a < 91 ? ($a - 64) : ($a - 96));

    $presses = ($a < 58 ? 1 : ($key % 3 ? $key % 3 : 3));

    // This part uses a lot of (probably unnecessary or still optimizable) fallbacks
    // for those characters, that are on "4-letter-keys"
    $ascii < 91 && $presses += 3;
    $ascii < 58 && $presses = 7;
    if ($a == 55 || $a == 57) $presses = 9;
    if ($helper == 115 || $helper == 122) $presses = 4;
    if ($helper == 90 || $helper == 83) $presses = 8;
    if (($helper > 79 && $helper < 83) || ($a > 85 && $a < 89)) $presses++;
    $ascii == 32 && $presses = 1;
    $ascii == 10 && $presses = 2;
    $ascii == 48 && $presses = 3;
    $ascii == 49 && $presses = 1;

    $key = array_search($l, $split($special));
    if ($key !== false){
        $presses = 2 + floor($key/5) + $key % 5;

    $count += $presses;
    if ($a < 32 && $a > 127 && $a != 10) {
        echo "Invalid character $l in source";
echo "Total key presses $count";

I assume there's still a lot of room for improvement, but I'm pretty happy with this.

Another bad thing is the required use of iconv() for the list of special characters. Some of these (, ¥, ...) are not natively supported by PHP.

share|improve this answer
€¥,etc are not ascii characters, that's why my python script declares the utf-8 encoding. That is a special comment interpreted by python. –  user80551 Jul 2 at 13:30
utf8_decode would work fine for every character except of . That's why I had to use iconv instead. My code doesn't calculate these special characters like it does with normal characters, as they're not in order and I can't reliably work with their respective ASCII values. It's using a normal list for them. –  Padarom Jul 2 at 13:31

Python 3, 239 characters

s='1adgjmptw 1behknqux\n.1cfilorvy0,"1ADGJMsTz\'-/1BEHKNPUW?(:&1CFILOQVX!)_%>123456R8Y@;*£¤1SZ+=€[\\179<$]~§1¥{^#1}¡|1¿`'
print('Total key presses',sum(s[:s.find(c)+1].count('1')or exit('Invalid character %c in source'%c)for c in input()))
share|improve this answer

JavaScript (E6) 291


Shell version, using spydermonkey shell. Read from a 'source' file, write to sdtout

z=t=0,[...read('source')].map(c=>t-=~((i=".\"/&>¤\\§,-:%£[~#'(_*€]^|?);=${¡`!@+<¥}¿".indexOf(c))<0?(p="0\n 9ZYXWzyxw8VUTvut7SRQPsrqp6ONMonm5LKJlkj4IHGihg3FEDfed2CBAcba10".split(c)[1])?p.search(/\d/):z="Invalid character "+c+" in source":i%8-~(i>>3))),print(z||"Total key presses "+t)

First try, works in FireFox console using popup for input and output

?(p="0\n 9ZYXWzyxw8VUTvut7SRQPsrqp6ONMonm5LKJlkj4IHGihg3FEDfed2CBAcba10".split(c)[1]) 
?p.search(/\d/):z="Invalid character "+c+" in source":i%8-~(i>>3))),z||"Total key presses "+t);


      (i = ".\"/&>¤\\§,-:%£[~#'(_*€]^|?);=${¡`!@+<¥}¿".indexOf(c)) < 0
      ? (p = "0\n 9ZYXWzyxw8VUTvut7SRQPsrqp6ONMonm5LKJlkj4IHGihg3FEDfed2CBAcba10".split(c)[1]) 
        ? p.search(/\d/)
        : z="Invalid character "+c+" in source"
      : i%8 - ~(i>>3)
  z||"Total key presses "+t
share|improve this answer

VBScript 435

Does not support non-ASCII chars. I was pretty far along with my code so I thought I'd post it for reference. I don't think anyone else used this approach.

a=SPLIT("1 abcABC2 defDEF3 ghiGHI4 jklJKL5 mnoMNO6 pqrsPQRS7 tuvTUV8 wxyzWXYZ9 0")
a(9)=" "+vbLf+"0"
b=SPLIT(".,'?! ""-()@ /:_;+ &%*=< >£€$¥ ¤[]{} \~^¡¿ §#|` x x")

for o=1 to len(i)
    for x=0 to 9
        for c=1 to 10
            IF n=0 AND mid(b(x),c,1)=d THEN n=c+x
            IF mid(a(x),c,1)=d THEN n=c
o="Total key presses "+cstr(z)
IF n=0 THEN o="Invalid character "+d+" in source"
msgbox o
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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