One line Keyboard

20

3

The Challenge

The goal of this challenge is to determine whether a given String can be typed using only one line of a standard UK QWERTY keyboard.

This is code golf, so shortest solution in bytes wins!


IO

Input will be a single String of zero or more characters in the ASCII decimal range of 32-126 inclusive. You may assume for this challenge that an empty String requires no typing and thus can be typed using a single line.

You may take the input as a String, list of characters, or equivalent form for your language.

Output should be a truthy value for any String that can be typed using a single line, or falsey for one that cannot.


Keyboard layout

To clarify any ambiguity over what the standard keyboard layout is below is a list of keys available on each line, including alternate upper keys (accessed using shift).

  • Line 1
    • Standard: `1234567890-=
  • Line 2
    • Standard: qwertyuiop[]
  • Line 3
    • Standard: asdfghjkl;'#
    • Uppercase: ASDFGHJKL
    • Special: Caps Lock
  • Line 4
    • Standard: \zxcvbnm,./
    • Alternate: |<>?
    • Uppercase: ZXCVBNM
    • Special: Shift
  • Line 5
    • Special: Space Bar

Alternate upper keys can only be pressed if Shift is also on the same line, and uppercase keys can only be accessed through Caps Lock or Shift. You really can only use one keyboard line!


Test cases

            -> true     (empty string)
45-2=43     -> true     (line 1)
qwerty      -> true     (line 2)
tryitout    -> true     (line 2)
Qwerty      -> false    (no shift or caps on line 2)
#sad        -> true     (line 3)
AsDf        -> true     (caps lock used)
@sDF        -> false    (no shift for alternate upper)
zxcvbn?     -> true     (line 4)
zxc vbn     -> false    (spacebar on separate line)
123abc      -> false    (multiple lines)
            -> true     (just space bar)
!!!         -> false    (exclamation marks cannot be printed by a single line)

Luke Stevens

Posted 2018-10-10T08:39:03.370

Reputation: 979

So this is strictly U.S. keyboard layouts? (I've got a UK layout). – ouflak – 2018-10-10T08:40:25.357

2@ouflak On the contrary, it's strictly only the UK QWERTY keyboard layout – Luke Stevens – 2018-10-10T08:42:31.573

@Arnauld Yes I did, thanks for noticing! – Luke Stevens – 2018-10-10T08:43:06.363

Yeah I started looking at both and noticed your layout seems to follow the my UK keyboard layout, not my U.S. one. Hmmm... Wonder what my Austrian one looks like in comparison. – ouflak – 2018-10-10T08:43:54.633

Are we allowed to take the input as a list of characters, or does it have to be a string? – Kevin Cruijssen – 2018-10-10T08:48:23.710

@KevinCruijssen Yes, input is flexible, I've added that to the question. – Luke Stevens – 2018-10-10T08:50:24.137

Answers

12

Python 2, 130 123 121 115 bytes

lambda s:any(set(s)<=set(l+l.lower())for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!


Python 3, 111 bytes

lambda s:any({*s}<={*l+l.lower()}for l in["`1234567890-=","eqwrtyuiop[]","ASDFGHJKL;'#","ZXCVBNM\,./|<>?"," "])

Try it online!

-4 bytes, thanks to nedla2004

TFeld

Posted 2018-10-10T08:39:03.370

Reputation: 19 246

1

If you are willing to use Python 3, you can get it down to 111 bytes.

– nedla2004 – 2018-10-10T14:07:15.253

9

Retina 0.8.2, 72 71 bytes

`^([-=\d`]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$

Try it online! Explanation: Each alternation matches a different row of the keyboard. The (?i) in the middle of the pattern causes the entire rest of the pattern to be matched case-insensitively. Edit: Saved 1 byte thanks to @KirillL.

Neil

Posted 2018-10-10T08:39:03.370

Reputation: 95 035

opqr can be changed to o-r for -1. Also, did you forget a backtick character for Line 1, I don't see it? – Kirill L. – 2018-10-10T09:33:22.510

@KirillL. Whoops, must have accidentally missed selecting it when copy/pasting, thanks for spotting that. – Neil – 2018-10-10T09:37:09.933

nice [][... :) – mazzy – 2018-10-10T12:18:44.560

8

05AB1E, 66 47 bytes

žh…`-=«žS„[]«žTDu…;'#««žUDu"\,./|<>?"««ð)εISåP}O

Takes the input as a list of characters.

-19 bytes thanks to @Emigna. Completely forgot we had qwerty-keyboard constant builtins. :D

Try it online or verify all test cases.

Explanation:

žh                # Push "0123456789"
  …`-=            # Push "`-="
      «           # Merge them together
žS                # Push "qwertyuiop"
  „[]             # Push "[]"
     «            # Merge them togeter
žT                # Push "asdfghjkl"
  Du              # Duplicate, and toUppercase the copy
    …;'#          # Push ";'#"
        ««        # Merge all three together
žU                # Push "zxcvbnm"
  Du              # Duplicate, and toUppercase the copy
    "\,./|<>?"    # Push "\,./|<>?"
              ««  # Merge all three together
ð                 # Push a space
 )                # Wrap all string in an array
  ε   }           # Map each to:
   I              #  Take the input (list of characters)
    å             #  Check for each if it's in the current string of the map-iteration
     P            #  Take the product (1 if all are truthy, 0 otherwise)
       O          # Take the sum (either 1 or 0, so truthy/falsey), and output implicitly

Kevin Cruijssen

Posted 2018-10-10T08:39:03.370

Reputation: 67 575

8

Perl 5 -pl, 76 bytes

$_=/^( *|[\d`=-]+|[][wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/

Try it online!

The obvious regex approach.

nwellnhof

Posted 2018-10-10T08:39:03.370

Reputation: 10 037

6

Ruby -n, 86 81 bytes

p /^( *|[\d`=-]+|[\]\[wetyuio-r]+|(?i)[asdfghjkl;'#]+|[\\\/zxcvbnm,.|<>?]+)$/?1:0

Try it online!

Kirill L.

Posted 2018-10-10T08:39:03.370

Reputation: 6 693

5

JavaScript (Node.js), 99 98 95 bytes

x=>/^([-`=\d]+|[wetyuio-r[\]]+)$/.test(x)|/^([asdfghjkl;'#]+|[zxcvbnm,./<>?\\|]+| *)$/i.test(x)

Try it online!

-1 from the comment by @Kirill L. in the Retina answer.
-3 thanks @Ismael Miguel and @Arnauld for their combined effort.

Shieru Asakoto

Posted 2018-10-10T08:39:03.370

Reputation: 4 445

Why do you have asdfghjkl and ASDFGHJKL in there? Why don't you use the i flag, like x=>/.../i.test(x)? – Ismael Miguel – 2018-10-10T09:34:53.540

@IsmaelMiguel because QWERTYUIOP shouldn't be matched by the regex. The extra check will cost more than coding those uppercase letters directly into the regex. – Shieru Asakoto – 2018-10-10T09:35:36.393

Nevermind, it is buggy. x=>/^([-`=\d]+|[wetyuio-r\[\]]+)$/.test(x)||/^([asdfghjkl;'#]+|[zxcvbnm,.\/<>?\\|]+| *)$/i.test(x) is the same length – Ismael Miguel – 2018-10-10T09:38:13.283

@IsmaelMiguel Using a bitwise | saves 1 byte. – Arnauld – 2018-10-10T09:41:03.090

@IsmaelMiguel the brackets after ^ and before $ are still needed. Turning || to | does save one tho. – Shieru Asakoto – 2018-10-10T09:41:59.013

You're right! 0|1 or 1|0 is still 1 which is a truthy value – Ismael Miguel – 2018-10-10T09:42:34.273

Do you really need to escape [ and /? – Arnauld – 2018-10-10T09:43:45.703

The first [ can be unescaped. That is, the expression is now /^([-`=\d]+|[wetyuio-r[\]]+)$/. The / in the 2nd can be unescaped, making the expression be /^([asdfghjkl;'#]+|[zxcvbnm,./<>?\\|]+| *)$/i. – Ismael Miguel – 2018-10-10T09:45:11.623

@Arnauld I thought I need to escape / everywhere in the regex lol. Thanks – Shieru Asakoto – 2018-10-10T09:49:06.840

I though too. Seems like that, inside a character class (is this the right name????) you don't need to escape those. At least Google Chrome was happy about it. – Ismael Miguel – 2018-10-10T09:50:38.510

1

@IsmaelMiguel This is part of the ECMAScript specification. The only 3 characters that need to be escaped within a character class are \, ] and - (see ClassAtomNoDash in the spec). The dash may also appear unescaped if it's either the first or the last character (otherwise, it's interpreted as a character range separator).

– Arnauld – 2018-10-10T13:27:44.323

@Arnauld That is actually very interesting. Thank you! I already knew about the 2nd part of your comment, but the first part is totally new to me. – Ismael Miguel – 2018-10-10T14:40:39.680

5

Perl 6, 102 101 100 bytes

-1 byte thanks to nwellnhof!

->\a{max map {a.comb⊆comb $_~.lc:},|<eqwrtyuiop[] ASDFGHJKL;'# ZXCVBNM\,./|<>?>,' ',"`-="~[~] ^10}

Try it online!

Pretty standard implementation. There's probably a shorter regex based solution to this, but I don't know Perl 6's regex well enough to make an attempt.

Jo King

Posted 2018-10-10T08:39:03.370

Reputation: 38 234

You can use max instead of ?any (and min instead of ?all). – nwellnhof – 2018-10-10T13:03:19.307

1

For what it's worth, a regex-based solution would be 84 bytes or 80 bytes using Perl 5 regexes with m:P5//. But I don't think either of them are worth posting as answer.

– nwellnhof – 2018-10-10T13:41:37.870

4

Java 10, 209 208 bytes

s->{int l=0,t;for(var p:s){t=p.matches("[[0-9]`\\-=]")?1:"qwertyuiop[]".contains(p)?2:p.matches("(?i)[asdfghjkl;'#]")?3:"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?4:p.equals(" ")?5:9;l=l<1?t:l!=t?9:l;}return l<6;}

-1 byte thanks to @TFeld.

Try it online.

Explanation:

s->{                    // Method with String-array parameter and boolean return-type
  int l=0,              //  Line-integer, starting at 0
      t;                //  Temp integer
  for(var p:s){         //  Loop over the characters
    t=p.matches("[[0-9]`\\-=]")?
                        //   If it's a character from the first line: 
       1                //    Set `t` to 1
      :"qwertyuiop[]".contains(p)?
                        //   Else-if it's a character from the second line:
       2                //    Set `t` to 2
      :p.matches("(?i)[asdfghjkl;'#]")?
                        //   Else-if it's a character from the third line
       3                //    Set `t` to 3
      :"\\zxcvbnm,./|<>?ZXCVBNM".contains(p)?
                        //   Else-if it's a character from the fourth line:
       4                //    Set `t` to 4
      :p.equals(" ")?   //   Else-if it's a space from the fifth line:
       5                //    Set `t` to 5
      :                 //   Else (invalid character):
       9;               //    Set `t` to 9
    l=l<1?              //   If `l` is still 0:
       t                //    Set it to `t`
      :l!=t?            //   Else-if `t` is a different line than `l`:
       9                //    Set `l` to 9 (non-existing line)
      :                 //   Else (`t` is the same line as `l`):
       l;}              //    Leave `l` the same
  return l<6;}          //  Return whether `l` is not 9

Kevin Cruijssen

Posted 2018-10-10T08:39:03.370

Reputation: 67 575

Nice solution, but it returns true for !!! despite exclamation marks being unable to be printed by any single line. I've just added that as a test case – Luke Stevens – 2018-10-10T09:21:58.590

@LukeStevens Ah, I falsely assumed the input is always valid for one of the five lines. I will modify my solution. It's a simple fix (but will add some bytes..) – Kevin Cruijssen – 2018-10-10T09:38:08.473

Why not use (?i) for the other uppercase letters? – Neil – 2018-10-10T09:43:47.940

@Neil Because of the escape required for the slash, it's also 209 bytes.

– Kevin Cruijssen – 2018-10-10T09:46:55.517

Suggest p==" " instead of p.equals(" ") – ceilingcat – 2019-09-27T22:27:50.193

4

Powershell, 87 bytes

Port of Neil's Retina regex.

"$args"-cmatch"^([-=\d``]+|[][eio-rtuwy]+|(?i)[adfghjkls;'#]+|[\\bcnmvxz,./|<>?]+| *)$"

mazzy

Posted 2018-10-10T08:39:03.370

Reputation: 4 832

4

Jelly, 55 bytes

ØDW;Øq;Œu$€3,4¦;"“rɠ3“Ż²“¿µ|“aƲƘ0ÞḅzḂ»⁶ṭ
¢f€ẈṠSỊafƑ¢F¤$

Try it online!

The first line yields a list of the keyboard lines and the second one checks whether the program input is contained on a single (or zero) lines and that there are no characters that can't be typed (like QWE!@#).

dylnan

Posted 2018-10-10T08:39:03.370

Reputation: 4 993

3

C, 150 bytes

x;f(char*s){for(x=0;*s;x|="@               "[*s++-32]);return!(x&x-1);}

Try it online!

It won't win any prizes, but this is a fun approach: we map characters on the five rows of the keyboard to 4 8 16 32 64, and every invalid character to 3. We bitwise OR the value for each character in the string together, and check if the result satisfies x&(x-1) == 0, which is true when x is a power of 2 or zero, i.e. whenever x has at most one bit set.

Lynn

Posted 2018-10-10T08:39:03.370

Reputation: 55 648

2

LUA, 282 262 259 270 bytes

s=io.read()l=0
t=0
for c in s:gmatch"."do
f=c.find
t=f(c,"[0-9%`-=]")or 0|(f(c,"[qwertyuiop%[%]]")or 0)*2|(f(c,"[aAsSdDfFgGhHjJkKlL:'@#~]")or 0)*4|(f(c,"[\\zxcvbnm,./|<>?ZXCVBNM]")or 0)*8|(f(c," ")or 0)*16
t=t==0 and 17or t
l=l<1 and t or l~=t and 17or l
end
print(l<17)

Try it online!

ouflak

Posted 2018-10-10T08:39:03.370

Reputation: 925

2

PHP, 98 bytes

I´m a little sad that there´s nothing shorter than regex. It´s probably not the fastest solution.

<?=preg_match("%^([`\d=-]*|[wetyuio-r[\]]*|(?i)[asdfghjkl;'#]*|[\\\zxcvbnm,./|<>?]*| *)$%",$argn);

Run as pipe with -F or try it online.


Shortest non-regex solution I found (124 bytes; linebreak and tab for reading convenience):

foreach(["`1234567890-=","qwertyuiop[]","asdfghjkl;'#ASDFGHJKL","zxcvbnm,./\|<>?ZXCVBNM"," "]as$d)
    trim($argn,$d)>""||die(1);

exits with code 1 for truthy, 0 for falsy. Run as pipe with -R.
Requires PHP 5.4 or later; for older PHP, use array(...) instead of [...] (+5 bytes)
or use these 123 bytes with -nR in PHP <7:

foreach(split(_,"`1234567890-=_qwertyuiop[]_asdfghjkl;'#ASDFGHJKL_zxcvbnm,./\|<>?ZXCVBNM_ ")as$d)
    trim($argn,$d)>""||die(1);

Titus

Posted 2018-10-10T08:39:03.370

Reputation: 13 814

2

AWK, 163 119 113 bytes

This is an AWK answer, returns a numeric 1 string for true, 0 string for false. (Written as AWK invoke as awk -f file for interactive use.)

{print/^[-`1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^ *$/}

Try it online!

However, does not handle TAB character as written (trivial extension) as not part of spec.

Phil F

Posted 2018-10-10T08:39:03.370

Reputation: 31

4

"All solutions to challenges should: (…) Be a serious contender for the winning criteria in use. For example, an entry to a code golf contest needs to be golfed, and an entry to a speed contest should make some attempt to be fast." – [help/on-topic] There is no need to declare those 2 variables and definitely no need to make them strings. And a single print is enough: Try it online!

– manatwork – 2018-10-15T11:57:59.897

Your comment that solutions should be serious is valid but I would argue that any solution therefore after the revised 05AB1E one is therefore irrelevant since they could not match or better the result.

I don't see how you can not use print twice to get both true and false (certainly for something that reads strings until end of input). And I did say optimized not optimal at the time. Thanks to your thoughts it can be reduced to 143 characters. – Phil F – 2018-10-15T12:34:51.893

And in fact going all the way will make it 121 characters ... – Phil F – 2018-10-15T12:44:27.033

2[tag:code-golf] is not a competition for shortest code overall, but shortest for your language. If you can improve your solution, then I'd suggest doing so – Jo King – 2018-10-15T12:57:25.250

@Jo King, thank you for your clarification(s). revised code would/could be:

{print/^[-```1234567890=]*$/||/^[]qwertyuiop\[]*$/||/^[asdfghjkl;'#ASDFGHJKL]*$/||/^[zxcvbnm,.\/\|<>?ZXCVBNM]*$/||/^ *$/} – Phil F – 2018-10-15T13:16:49.687

And for those who realize I don't now the formatting conventions despite reading the help, I apologise for the triple back-ticks. And the AWK answer reduces to (when corrected back-quote(s) used in the code) to 121 bytes. – Phil F – 2018-10-15T13:23:48.857

Usually answers on this site includes a link to a online tester so others can easily test the answers. Besides, I counts 119 bytes. – user202729 – 2018-10-17T10:23:46.030

If you want to include backticks in code in comments you can use more than 1 backtick to wrap them. "``abc`def``"--> abc`def For posts just 4-space indent. – user202729 – 2018-10-17T10:23:56.473

After further optimization reduces to: {print/^[-`0-9=]$/||/^[]qwertyuiop[]$/||/^[asdfghjkl;'#ASDFGHJKL]$/||/^[zxcvbnm,./|<>?ZXCVBNM]$/||/^ *$/} Try it online!

– Phil F – 2018-10-19T10:07:02.290

1

Bash, 119 bytes

Includes an echo to provide "readable" output. If you put a suitable wrapper of shell (your choice) around it to include print/output, then you can save 8 bytes. My reading of the challenge suggests the solution should output a suitable output indication so I stick with 119 bytes.

[[ "$@" =~ ^[asdfghjklASDFGHJKL\;\'#]*$|^[-\`0-9=]+$|^[]qwertyuiop\[]*$|^[zxcvbnm,./\|\<\>\?ZXCVBNM]*$|^\ *$ ]];echo $?

Try it online!

Phil F

Posted 2018-10-10T08:39:03.370

Reputation: 31

Be advised the Bash solution also means my/@manatwork AWK solution may be reduced to 113 bytes. – Phil F – 2018-10-19T10:03:14.000