Given any of the following characters (or a newline):


Your program must output the row that it is on the keyboard

Because my keyboard is (almost) out of battery, your code must be as short as possible

The keyboard your program should use (for the row lookup), should look like:

Row 1:~` !1@2 #3$4 %5^6 &7*8 (9)0 _-+=                          

Row 2:                         Q W E R T Y U I O P {[ }]    |\   
Row 3:                              A S D F G H J K L :; "' return  
Row 4:                                 Z X C V B N M <, >. ?/                 
Row 5:                                                    space                                                   

Where   return is a newline. Empty keys don't mean anything.







" "

where \n is a newline character.


  • Your program should be case insensitive
  • Your program only needs to handle the characters on the keyboard shown
  • 3
    \$\begingroup\$ Perhaps classification? \$\endgroup\$ – lirtosiast Jan 7 '16 at 1:42
  • 3
    \$\begingroup\$ Is that a double-nested kbd? \$\endgroup\$ – Conor O'Brien Jan 7 '16 at 4:04
  • \$\begingroup\$ I remember years ago using some language that returned keypresses as as 100×row+position... Would have been perfect for this, but unfortunately I don't remember what it was. Maybe some form of BASIC... \$\endgroup\$ – Adám Jan 7 '16 at 13:35
  • \$\begingroup\$ @NBZ Is it Blitz Basic? \$\endgroup\$ – wizzwizz4 Jan 7 '16 at 18:43
  • 1
    \$\begingroup\$ @wizzwizz4 Have you tried BlitzPlus? it's free and looks like it's what you want. \$\endgroup\$ – HolyBlackCat Jan 25 '16 at 22:42

21 Answers 21


Pyth, 62 66 65 bytes

?zh@+,4Zmid2c.Bi."0fÀÓ¸[9Ѷ¤KïLäHÉðbÀ`]ü©¬vS"16 2-CzCd3

Try it online.

Uses a packed string representing a number in hex which, when chopped into two-bit chunks, represents the row of every character except and ! as a value from 0 to 3. We leave out and ! so we don't have to store 4 or have a 0 at the start of this number, then add their row values using +,4Z. Once we've turned the string into row values, all we have to do is use the character code of the input to index into the array of values, and then add 1.

Newline is handled separately because it's interpreted by Pyth as an empty string and so has a character code of 0.

This would be shorter if I could figure out how to use base 256 in Pyth, but I can't quite make it work.

  • 4
    \$\begingroup\$ o.0 starts squeezing Japt \$\endgroup\$ – nicael Jan 7 '16 at 21:25
  • \$\begingroup\$ this puts me to shame \$\endgroup\$ – JuanPotato Jan 7 '16 at 23:14
  • \$\begingroup\$ :( I forgot about newline! @nicael you're back to being on top. \$\endgroup\$ – Luke Jan 7 '16 at 23:45
  • \$\begingroup\$ Now we're dead even! \$\endgroup\$ – Luke Jan 7 '16 at 23:49
  • \$\begingroup\$ You need to escape null bytes in Pyth. \$\endgroup\$ – lirtosiast Jan 7 '16 at 23:49

JavaScript (ES6), 105 102 101 bytes



In JavaScript test returns a boolean which acts the same as 1 or 0 so I multiply them by their row. Testing for row 2 took the most bytes so I used that one as the default if no others matched.

  /[~`0-9!@#-&^(-+_=-]/.test(c)   // row 1 regex
  +/[asdfghjkl;:'"\n]/i.test(c)*3 // row 3 regex
  +/[zxcvbnm,<.>/?]/i.test(c)*4   // row 4 regex
  ||++c                           // space ++ = 1, any character on row 2 ++ = NaN
    *7^2                          // 7 XOR 2 = 5, NaN XOR 2 = 2


var solution = c=>/[~`0-9!@#-&^(-+_=-]/.test(c)+/[asdfghjkl;:'"\n]/i.test(c)*3+/[zxcvbnm,<.>/?]/i.test(c)*4||++c*7^2
<textarea id="input">-</textarea><br />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

  • 1
    \$\begingroup\$ > NaN XOR 2 = 2 — ??? \$\endgroup\$ – lirtosiast Jan 7 '16 at 3:05
  • 1
    \$\begingroup\$ @ThomasKwa That's just how JS works lol. If c="q", ++c = NaN, NaN*7 = NaN, NaN^2 converts the operands to integers (uncastables like NaN become 0) then does 0 XOR 2 which is 2. \$\endgroup\$ – user81655 Jan 7 '16 at 3:10

Glava 1.5, 164 bytes

Glava is a dialect of Java that makes Java code shorter. This code is unfortunately non-competitive as the commit (2 hours late...) used was made after this challenge, which fixed some vital bugs that would not allow this program to work.

p(A[0].matches("[`0-9-=~!@#$%^&*()_+]")?1:A[0].replace("\\n","\n").matches("(?i)[asdfghjkl;':\"\n]")?3:A[0].matches("(?i)[zxcvbnm,.\\/<>?]")?4:A[0].matches(" ")?5:2

This is a full program that takes input via command-line arguments. Works by simply testing for which row regex it matches, then outputs the corresponding number.

  • \$\begingroup\$ Glava = Guava + Java? \$\endgroup\$ – Downgoat Jan 7 '16 at 3:32
  • 2
    \$\begingroup\$ @Doᴡɴɢᴏᴀᴛ Glava = Golf + Java ( it was Conor's idea) \$\endgroup\$ – GamrCorps Jan 7 '16 at 3:38
  • \$\begingroup\$ Indeed! @Doᴡɴɢᴏᴀᴛ \$\endgroup\$ – Conor O'Brien Jan 7 '16 at 4:04

Python 3, 142

print(int(("~`!1@2#3$4%5^6&7*8(9)0_-+=""qwertyuiop{[}\|"+"]"*11+'asdfghjkl;:"\n'"'"*13+"zxcvbnm,<.>/""?"*14+" ").index(input().lower())/26)+1)

There is probably a shorter way that I am overlooking ¯\_(ツ)_/¯


Pyth, 98

|+++l:"~`0123456789!@#$%^&*()_-=+"z1*l:"asdfghjkl;:'\"\n"rz0 1 3*l:"zxcvbnm,<.>/? "rz0 1 4 l:dz1 2

not sure how to get the 0-9 range working for some reason :|, inspired by user81655's answer

  • \$\begingroup\$ You can use jkUT for the string with the range 0 to 9, not sure if there's a shorter way. You can also used packed strings to save a few bytes, e.g. ."!~WÏù¹_(<]úÝ" for "~`!@#$%^&*()_-=+". \$\endgroup\$ – Luke Jan 7 '16 at 3:36
  • \$\begingroup\$ From @benstopics, this does fail for regex metacharacters \$\endgroup\$ – FryAmTheEggman Jan 7 '16 at 14:53

Bash, 108

No Bash answer? Bash answer. grep -Fin is definitely the right tool for this job.

This program is in two files.

k, 73 bytes


There are 5 lines, the last one is a space. If you have trouble reproducing the file, the base64 is:


b, 34 bytes

This is the program itself, it takes input as the only command line argument.

grep -Fin "$1" k|tail -n3|head -c1

Score: 34 + 73 + 1 (for k's filename) = 108 bytes


grep --fixed-strings --ignore-case --line-number "$1" k|tail --lines=3|head --bytes=1


  • grep - search a file for lines matching a string or regular expression, output only those lines
  • -F aka --fixed-strings - disable regular expressions so [ etc. are handled correctly
  • -i aka -y aka --ignore-case - case-insensitive matching
  • -n aka --line-number - show the line number and : before every line (e.g. 4:zxcvbnm,./<>?)
  • "$1" - search for the script's first command-line argument, the quotes are necessary to handle newline and space
  • k - search in file k
  • This grep command will match all five lines if the input is a newline, and only one line otherwise.
  • | - pipe, send standard output of one command to standard input of the next
  • tail - output the last N lines or characters of standard input
  • -n3 aka --lines=3 - output the last 3 lines
  • If the input wasn't a newline, there is only one line to process, which starts with the row number because of the -n flag on grep. Otherwise, this command takes only lines 3, 4 and 5 (the last 3 lines).
  • | - pipe
  • head - output the first N lines or characters of standard input
  • -c1 aka --bytes=1 - output the first character
  • If the input wasn't a newline, this takes the first character, which is the line number where the input is found. If the input is a newline, it takes the first character of lines 3, 4 and 5 combined, which is 3, which happens to be the correct row number for newline.

Japt, 73 70 66 bytes

'1zxcvbnm,.<>?/\"1 `q1 ®bUv)<0} b!1

Try it online! (in the example, the input is literally a newline)

  • \$\begingroup\$ Nice, shortest so far! \$\endgroup\$ – ETHproductions Jan 7 '16 at 15:21
  • \$\begingroup\$ @Eth yup, at least once I should post something short :D \$\endgroup\$ – nicael Jan 7 '16 at 15:25
  • \$\begingroup\$ A couple bytes shorter \$\endgroup\$ – ETHproductions Jan 7 '16 at 15:29
  • \$\begingroup\$ @Eth Heh, !1 is something that matches "false", finally I know how to do it, thanks :) \$\endgroup\$ – nicael Jan 7 '16 at 15:32
  • \$\begingroup\$ @Eth halp, needs 5 bytes to beat Pyth. \$\endgroup\$ – nicael Jan 7 '16 at 23:04

Java, 300 bytes

import java.util.Scanner;public class A{public static void main(String[] args){String g="~`!1@2#3$4%5^6&7*8(9)0_-+=qQwWeErRtTyYuUiIoOpP[{]}\\|aAsSdDfFgGhHjJkKlL;:\'\"\r";Scanner i=new Scanner(System.in);int f=g.indexOf((i.nextLine().charAt(0)));System.out.print(f<0?4:(f<26?1:(f<53?2:(f<76?3:5))));}}

I'm not an expert, and this is my first attempt at golfing, but I figured, what the hell, why not? Above is the full program version, the actual code that goes into it would most likely take a decent amount of characters off.

  • \$\begingroup\$ just noticed it crashes with an empty input(new line/carriage return). will fix when I can \$\endgroup\$ – Andrew Jan 8 '16 at 17:33
  • \$\begingroup\$ Welcome to the community! \$\endgroup\$ – Erik the Outgolfer Sep 28 '16 at 18:25
  • \$\begingroup\$ Welcome (bit late since you posted in January xD). You can golf it quite a bit without changing your current approach like this: class A{public static void main(String[]a){int f="~'!1@2#3$4%5^6&7*8(9)0_-+=qQwWeErRtTyYuUiIoOpP[{]}\\|aAsSdDfFgGhHjJkKlL;:\'\"\r".indexOf(new java.util.Scanner(System.in).nextLine().charAt(0));System.out.print(f<0?4:f<26?1:f<53?2:f<76?3:5);}} (243 bytes) I removed some unnecessary parenthesis; shortened args; removed public ; directly used the String and Scanner; and removed the import now that java.util.Scanner is used once. \$\endgroup\$ – Kevin Cruijssen Oct 25 '16 at 9:26
  • \$\begingroup\$ 219 bytes you don't need to use Scanner for this \$\endgroup\$ – PrincePolka Nov 26 '17 at 17:33

Pyth, 105 bytes

J?<l-c".^$*+?{}[]\|()"1]z14+\\zrz0?qJd5?:"qwertyuiop[]\|"J)2?:"asdfghjkl;':\"\n"J)3?:"zxcvbnm,./<>?"J)4 1


J?<l-c".^$*+?{}[]\|()"1]z14+\\zrz0     # Escape input if regex metachar
?qJd5                                  # Check space
?:"qwertyuiop[]\|"J)2                  # Check second row
?:"asdfghjkl;':\"\n"J)3                # Check third row
?:"zxcvbnm,./<>?"J)4                   # Check fourth row
1                                      # If none of these, must be on first row.

I decided to choose the first row as the "must be if nothing else" row because it required the most bytes to represent even after golfing.

  • \$\begingroup\$ Welcome to Programming Puzzles and Code Golf! Use comments to make @JuanPotato get it. However, that needs 50 rep. So you need to work. \$\endgroup\$ – user48538 Jan 7 '16 at 14:32

Perl 6, 128 bytes

say 1+(/<[-\d=~!@#$%^&*()_+/`]>/,/<[qwertyuiop[\]\\{}|]>/,/<[asdfghjkl;':"\n]>/,/<[zxcvbnm,./<>?]>/,' ').first: @*ARGS.lc~~*,:k

I make a list of regexes containing character classes along with a string literal space. I then call the first method on the list (which is just the method version of the first higher order function), using smartmatch to compare the argument passed to the program against the current item in the list. Note that smartmatch does "the right thing" for both regexes and a string literal. The :k optional parameter to first causes the method to return the index of the matching item in the list, which I then add 1 to and output via say.

Note that when using this program you will have to properly escape certain characters like ` and space in your shell. For instance: perl6 keyboard.p6 \`

  • \$\begingroup\$ Since nobody said it yet, welcome to Programming Puzzles & Code Golf! \$\endgroup\$ – Erik the Outgolfer Sep 28 '16 at 18:24

JavaScript ES6, 114 bytes

'`,`zxcvbnm,.<>?/"`,` `].map(x=>+(x.indexOf(n.toLowerCase())<0)).indexOf(0)+2

Another JavaScript solution. The principle is to return the index of the input char in the array of rows plus 2 (so as the 0-9 row returns -1, i.e. not exists, -1+2=1. q is in the first string of the array, so it returns 0+2=2nd row).


Perl, 96 77 76 bytes

Run using perl -p. Make sure you're feeding it just single characters; for example, to run it from a file key.pl (to avoid mucking around with shell escape sequences) echo -n q|perl -p key.pl.

]/i*3+/[bcnmvxz<>,.?\/]/i*4+/ /*5||2

Abusing the regex range functionality is fun.

  • \$\begingroup\$ To me this doesn't work, running it I get the index of the row + 3 (i.e. 3 instead of 0, 7 instead of 4, etc.). \$\endgroup\$ – ChatterOne Jan 7 '16 at 8:20
  • \$\begingroup\$ It's sensitive to how you provide the input. You're probably providing a character followed by a newline. I use echo to precisely control the input -- eg. echo -n q|perl -n key.pl, which correctly produces 2. \$\endgroup\$ – Mark Jan 7 '16 at 8:29
  • \$\begingroup\$ Oh, I see. Well, that also explains why you don't chomp the input. \$\endgroup\$ – ChatterOne Jan 7 '16 at 8:39
  • 1
    \$\begingroup\$ If I chomped the input, I wouldn't be able to return '3' for the return key. \$\endgroup\$ – Mark Jan 7 '16 at 8:42
  • 1
    \$\begingroup\$ Hey @Mark, you don't need the $_=~ for the matches, m// (which is what /.../ is) works on $_ automatically! Also if you use -p instead of -n you can use $_= instead of print to save a couple more bytes. Using a literal newline instead of \n can save you another byte too! That should reduce your code quite a bit! Might be worth adding an example usage as well so anyone testing knows you need to use echo -n :) \$\endgroup\$ – Dom Hastings Jan 7 '16 at 9:51

PHP, 173 bytes

The idea here was to use the regex capturing group number as the row index. Probably some more optimizations in the regex itself.

$i=$argv[1];preg_match("%([!#-&\(-+-0-9=@^-`~])|([EIO-RT-UWY[-]eio-rt-uwy{-}])|([\"':-;ADF-HJ-LSadf-hj-ls])|([,.-/<>-?B-CM-NVXZb-cm-nvxz])%",$i,$m);echo array_flip($m)[$i];

The preg_match() call will create an array $m of matches, and if we were to print that, it'd look something like this (assuming z was the input):

Array ( [0] => 'z', [1] => '', [2] => '', [3] => '', [4] => 'z' )

Flipping that array, by swapping keys and values, moves left to right and only keeps the last distinct key, so we end up with:

Array ( 'z' => 4, '' => 3 )

Then we use the input character as the index in the array to get our result.

Try it out here.


C, 145 143 136 132 127 106 bytes

#define c 2124850936,91714965
b[]={8<<18,0,-218071008,7796<<19,c,c};f(a){return a-32?b[a>>4]>>a%16*2&3:4;}

This uses index() from POSIX.1-2001 and is deprecated in POSIX.1-2008. This assumes ASCII and 32 bit ints.


Python 3, 89 bytes

print("qwertyuiop{}[]\\|asdfghjkl;:\"\n'''zxcvbnm,.<>/???? ".find(input().lower())//16+2)

As I can't comment yet, I'm posting the improvement for the current Python 3 answer separately.

Edit: All code in print now and further tweaked.

  • \$\begingroup\$ This is merely a snippet and therefore not a valid answer, you need to wrap it in a print statement (making it a full program) or turn it into a function. \$\endgroup\$ – FlipTack Nov 26 '17 at 16:42
  • \$\begingroup\$ @FlipTack: You're right. I've incorporated your suggestion. \$\endgroup\$ – creativecoding Nov 26 '17 at 16:51
  • \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ – Martin Ender Nov 26 '17 at 17:01
  • \$\begingroup\$ @MartinEnder: Thank you! :-) \$\endgroup\$ – creativecoding Nov 26 '17 at 17:30

Ruby, 82 bytes


Try it online!


CJam, 125 bytes

q_" "={;5}{"`1234567890-=~!@#$%^&*()_+qwertyuiop[]\QWERTYUIOP{}|asdfghjkl;'ASDFGHJKL:\"    zxcvbnm,./ZXCVBNM<>?    "\#26/1+}?


q                          e# read input
 _" "=                     e# decide if the input is a space
      {;5}                 e# if it is, push 5
          {"..."\#26/1+}?  e# if it isn't, push the correct row

SpecBAS - 178 bytes

1 a$="~`!1@2#3$4%5^6&7*8(9)0-_+=qQwWeErRtTyYuUiIoOpP{[}]|\aaaaAsSdDfFgGhHjJkKlL:;'"#34#13"zzzzzzzZxXcCvVbBnNmM<,>.?/"+" "*26
2 INPUT k$: IF k$="" THEN k$=#13
3  ?CEIL(POS(k$,a$)/26)

I used a long string where each row is 26 characters long (#34 is code for double quote and #13 is code for return).

Then print the result of rounding position/26.


C#6, 201 bytes

Nothing special here. I found it cheaper to just write both cases rather than use ToUpper() due to the string's fixed width.

using C=System.Console;class P{static void Main(string[]a)=>C.Write("`1234567890-=~!@#$%^&*()_+qwertyuiop[]\\QWERTYUIOP{}|asdfghjkl;'\raASDFGHJKL:\"\nazxcvbnm,./zzzZXCVBNM<>?zzz ".IndexOf(a[0])/26+1);}


using C=System.Console;
class P{
    static void Main(string[]a)=>
        C.Write("`1234567890-=~!@#$%^&*()_+qwertyuiop[]\\QWERTYUIOP{}|asdfghjkl;'\raASDFGHJKL:\"\nazxcvbnm,./zzzZXCVBNM<>?zzz ".IndexOf(a[0])/26+1);
  • 1
    \$\begingroup\$ I can't see this working for ~ or `? \$\endgroup\$ – Ash Burlaczenko Jan 7 '16 at 9:56
  • \$\begingroup\$ @AshBurlaczenko, thanks! I missed that key. Fixed with no change to my score. \$\endgroup\$ – Hand-E-Food Jan 10 '16 at 9:59

Python 2, 146 bytes

e="\n";lambda x:("`1234567890-=~!@#$%^&*()_+qwertyuiop[]\\QWERTYUIOP{}|asdfghjkl;'ASDFGHJKL:\""+e*4+"zxcvbnm,./ZXCVBNM<>?"+e*13+" ").index(x)/26+1

Excel, 132 bytes

=INT((FIND(A1,"`1234567890-=~!@#$%^&*()_+qwertyuiop[]\QWERTYUIOP{}|asdfghjkl;'ASDFGHJKL:""aaa zxcvbnm,./ZXCVBNM<>?zzzzzz ")-1)/26)+1

Attempts to use the case in-sensitive SEARCH() instead of FIND() revealed that Excel matches ~, * and ? to (tick). The matching of?means we can't useSEARCH()`, which would have shaved a massive 5 bytes...


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.