33
\$\begingroup\$

For the non-zero digits on a standard numpad

789
456
123

consider placing a chess knight at any digit and moving it around with any number of normal L-shaped jumps, tracing out a positive decimal integer. What positive integers can be expressed in such a way?

One of them is 38, since the knight could start on the 3 and move left and up to the 8. 381 and 383 are also possible.

3 itself is possible if no jumps are taken (which is allowed). 5 is as well, but no other digits can be reached from the 5, so it is the only number where the digit 5 appears.

Write a program or function that takes in a positive decimal integer (you may take it as a string if desired) and prints or returns a truthy value if the number can be expressed by a knight on a numpad in the way described, but otherwise outputs a falsy value.

The shortest code in bytes wins. Tiebreaker is earlier answer

Examples

Truthy:

1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 18, 38, 61, 81, 294, 349, 381, 383, 729, 767, 38183, 38383, 18349276, 183492761, 618349276

Falsy:

10, 11, 50, 53, 55, 65, 95, 100, 180, 182, 184, 185, 186, 187, 188, 189, 209, 305, 2009, 5030, 3838384, 4838383, 183492760
\$\endgroup\$
10
  • 2
    \$\begingroup\$ What's with chess knights today? :-D \$\endgroup\$
    – Luis Mendo
    Apr 18, 2016 at 19:03
  • 1
    \$\begingroup\$ Hint: If you write the numbers out as wrapping line, then the knight is always jumping either four spaces clockwise or four spaces counter. I don't know if this is helpful. \$\endgroup\$
    – anon
    Apr 18, 2016 at 19:05
  • 3
    \$\begingroup\$ @LuisMendo Wrapping. As in, if you treat is as an endless list of 78963214, repeated over and over. Count the distances – it's always four, one way or the other. I should've been clearer and explicitly said that you have to write it in circle order. \$\endgroup\$
    – anon
    Apr 18, 2016 at 19:37
  • \$\begingroup\$ @QPaysTaxes Oh, I thought you meant circle but 123...9. Sorry \$\endgroup\$
    – Luis Mendo
    Apr 18, 2016 at 19:41
  • \$\begingroup\$ @LuisMendo No worries. Like I said, I should've been clearer about what I meant. \$\endgroup\$
    – anon
    Apr 18, 2016 at 19:42

18 Answers 18

18
\$\begingroup\$

Python 2, 52 bytes

f=lambda n:n<6or`n%100`in'18349276167294381'*f(n/10)

Checks that any two consecutive digits are in the string '18349276167294381'. To get consecutive digits, rather than doing zip(`n`,`n`[1:]), the function repeatedly checks the last two digits and removes the last digit.

\$\endgroup\$
16
\$\begingroup\$

Jelly, 19 15 14 bytes

Doȷ’d3ạ2\P€=2P

Try it online! or verify all test cases.

How it works

Doȷ’d3ạ2\P€=2P  Main link. Argument: n (integer)

D               Convert n to base 10 (digit array).
  ȷ             Yield 1000.
 o              Logical OR. This replaces each 0 with 1000.
   ’            Decrement each digit.
    d3          Divmod; replace each digit k with [k:3, k%3].
      ạ2\       Pairwise reduce by absolute difference.
                For each pair of adjacent digits [i, j], this computes
                [abs(i:3 - j:3), abs(i%3 - j%3)].
         P€     Compute the product of each result.
                n is a Numpad's Knight Number iff all products yield 2.
           =2   Compare each product with 2.
             P  Multiply the resulting Booleans.
\$\endgroup\$
13
\$\begingroup\$

Retina, 58 40 bytes

Thanks to Sp3000 for suggesting this idea:

M&!`..
O%`.
A`16|18|27|29|34|38|49|67
^$

Try it online! (Slightly modified to run the entire test suite at once.)

Prints 1 for truthy and 0 for falsy results.

Explanation

M&!`..

Find all overlapping matches of .., i.e. all consecutive pairs of digits, and join them with linefeeds.

O%`.

Sort the digits in each line, so that we only need to check half as many pairs.

A`16|18|27|29|34|38|49|67

Remove all lines which correspond to a valid move.

^$

Count the matches of this regex. That is, if all lines were removed, this matches the resulting empty string once, otherwise it fails to match and gives zero instead.

\$\endgroup\$
7
\$\begingroup\$

Pyth - 35 28 bytes

!s.bt/@c`C"_xÖ({ζz"2tsNYztz

Test Suite.

\$\endgroup\$
7
\$\begingroup\$

Ruby, 57 bytes

Anonymous function. Argument is a string.

->n{(0..n.size).count{|i|!"16729438183492761"[n[i,2]]}<1}

Program with the test suite:

f=->n{(0..n.size).count{|i|!"16729438183492761"[n[i,2]]}<1}

a=%w{1 2 3 4 5 6 7 8 9 16 18 38 61 81 294 349 381 383 729 767 38183 38383 18349276 183492761 618349276
10 11 50 53 55 65 95 100 180 182 184 185 186 187 188 189 209 305 2009 5030 3838384 4838383 183492760}

a.each {|e|p [e, f[e]]}

I just encoded all the possible knight moves into a string and checked if every 2 digits within the input existed in that string.

\$\endgroup\$
3
  • \$\begingroup\$ Oh, that lookup string would also save me 17 bytes. Do you mind if I use that for my Retina answer? \$\endgroup\$ Apr 18, 2016 at 20:21
  • \$\begingroup\$ Go for it! Just give credit, I guess. \$\endgroup\$
    – Value Ink
    Apr 18, 2016 at 22:30
  • \$\begingroup\$ Thanks, but I ended up with an even shorter solution based on a suggestion by Sp3000 :) \$\endgroup\$ Apr 19, 2016 at 7:16
6
\$\begingroup\$

grep 58 bytes

grep "^((?=18|16|29|27|34|38|49|43|61|67|72|76|81|83|94|92).)*.$"

Because really, if you cannot beat grep...

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Neither 5 nor 185 emit 1 with your command line, while 5 is in the truthy, and 185 in the falsy list. \$\endgroup\$ Apr 18, 2016 at 20:51
  • 1
    \$\begingroup\$ @GuntramBlohm fixed -- got lost in regular negation \$\endgroup\$
    – Yakk
    Apr 18, 2016 at 21:23
6
\$\begingroup\$

Haskell 46 bytes

q=zip<*>tail
all(`elem`q"16729438183492761").q

Usage example: all(`elem`q"16729438183492761").q $ "183492761" -> True

How it works: It uses the lookup string found in @Kevin Lau's answer. q makes a list of pairs of adjacent chars from a string, e.g. q "1672" -> [('1','6'),('6','7'),('7','2')]. The function returns true if all pairs from the input appear in the pairs from the lookup string. q turns single digit inputs into the empty list, so elem always succeeds.

\$\endgroup\$
2
  • \$\begingroup\$ Why does zip<*>tail work like a flipped version of zip=<<tail? I think I don't understand what applicatives generalize. \$\endgroup\$
    – xnor
    Apr 18, 2016 at 22:07
  • \$\begingroup\$ @xnor: I just use it. <*> is defined as (<*>) f g x = f x (g x). \$\endgroup\$
    – nimi
    Apr 18, 2016 at 22:11
6
\$\begingroup\$

JavaScript (ES6), 65 62 bytes

s=>[...s].every((c,i)=>!i|"16729438183492761".match(s[i-1]+c))

Returns true or false. I'd previously tried a recursive solution, which takes 63 bytes, and map and even reduce but they took me 73 bytes.

Edit: Saved 3 bytes thanks to @user81655.

\$\endgroup\$
4
  • \$\begingroup\$ Couldn't do better, my best try was at 88 bytes. Bravo! \$\endgroup\$
    – Naouak
    Apr 18, 2016 at 22:47
  • \$\begingroup\$ @user81655 You mean match works instead of ~search (but either way, that's really underhanded) and | can replace || (but not in the recursive version, sadly.) \$\endgroup\$
    – Neil
    Apr 19, 2016 at 7:54
  • \$\begingroup\$ @user81655 I was referring to the way that !i|...match works because the match result, if successful, is an array of a single string of two digits, which the | operator ends up coercing into a valid integer. \$\endgroup\$
    – Neil
    Apr 19, 2016 at 8:37
  • \$\begingroup\$ @Neil Ah, right. \$\endgroup\$
    – user81655
    Apr 19, 2016 at 8:40
6
\$\begingroup\$

C, 85 81 bytes

Golfed:

i;f(char*b){i=*b++-49;return*b?(*b=="8749x7214"[i]||*b=="6983x1632"[i])&&f(b):1;}

Old non-recursive version (85 bytes):

i;f(char*b){for(;(i=*b++-49),*b&&*b=="8749x7214"[i]||*b=="6983x1632"[i];);return!*b;}

Old code with white-space and main program:

i;
f(char*b){
    for (; (i=*b++-49), *b     // i = index of digit + 1 in following arrays
        &&*b=="8749x7214"[i]   // 1st possible jump for 1..9
        ||*b=="6983x1632"[i];  // 2nd possible jump for 1..9
    );
    return !*b;
}

main(){
    char b[16];
    while(scanf("%s", b) == 1) printf("%d",f(b));
    return 0;
}

This accepts space-delimited numbers via standard input and outputs 0 if not-numpad-knight, or 1 otherwise.

The new 81-byte recursive version shaves 4 bytes.

\$\endgroup\$
1
5
\$\begingroup\$

MATL, 38 37 29 bytes

This uses @QPaysTaxes idea.

I:8JK5:7Pvj!Uttnqh?)d|2:EQm}h

The output is a 2D, complex, non-empty array. It is truthy if all its values have nonzero real part, and falsy otherwise.

Try it online!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Is this even allowed?? \$\endgroup\$ Apr 19, 2016 at 0:52
  • \$\begingroup\$ The question asks for a truthy or a falsy value, not a whole array. \$\endgroup\$ Apr 19, 2016 at 4:24
  • 2
    \$\begingroup\$ @CatsAreFluffy This is our definition of truthy/falsy. As in MATLAB/Octave, arrays are truthy in MATL iff all of its elements are truthy. (example) \$\endgroup\$
    – Dennis
    Apr 19, 2016 at 4:33
  • \$\begingroup\$ CC @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ \$\endgroup\$
    – Dennis
    Apr 19, 2016 at 4:34
4
\$\begingroup\$

05AB1E, 29 bytes

Code:

"1Ôž±ÎqäÚ"•5(«U2÷¹¦2÷«vXyå})P

Uses CP-1252 encoding. Try it online!.

\$\endgroup\$
4
\$\begingroup\$

MATL, 25 24 33 26 bytes

Shaved off 1 byte thanks to @LuisMendo!
@Dennis found a bug, and then fixed it! Thanks!

'bSVYXbTUZW'j47-)d2^48\1=A

Takes integer as input. Outputs 1/0.

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ @LuisMendo Right on both counts, thanks! \$\endgroup\$
    – beaker
    Apr 19, 2016 at 1:02
  • \$\begingroup\$ @Dennis Updated, and hopefully correct. Thanks for your help. \$\endgroup\$
    – beaker
    Apr 19, 2016 at 16:19
  • \$\begingroup\$ I don't think you need the A at the end. MATL's vectors are truthy iff they do not contain a 0. \$\endgroup\$
    – Dennis
    Apr 19, 2016 at 22:07
4
\$\begingroup\$

C, 140 92 bytes

c;L(char*i){while(*i&&(!c||*i=="6743x1212"[c-49]||*i=="8989x7634"[c-49]))c=*i++;return !*i;}

Assuming ASCII

Detailed Try it here

// valid transition from x to n[x-'1'][0 or 1]

int n[9][2] =
{
    {'6','8'},{'7','9'},{'4','8'},
    {'3','9'},{'x','x'},{'1','7'},
    {'2','6'},{'1','3'},{'2','4'}
};

// i is a pointer to where to start on a string

bool L(char * i)
{
    char c = 0;

    // move if not \0 and (not-first-char or is a valid move)

    while((*i) && (!c || (*i)==n[c-'1'][0] || (*i)==n[c-'1'][1]))
    {
        c = (*i++);
    }

    return !(*i); // success if it's \0
}
\$\endgroup\$
5
  • \$\begingroup\$ those lookup tables are huge. You can improve your score a lot if you get rid of all the delimiters {,}[] and encode it as a char* string instead. Also, note that your #define is not being cost-effective when you use it only twice: removing it would save you 4 bytes. \$\endgroup\$
    – tucuxi
    Apr 20, 2016 at 0:44
  • \$\begingroup\$ @tucuxi thanks for the tips, I managed to bring it down to 92, having \0 within the array caused undefined behavior so I replaced it with x \$\endgroup\$
    – Khaled.K
    Apr 20, 2016 at 8:03
  • \$\begingroup\$ Nice - also, don't forget to use <s>oldscore</s> newscore when editing to reflect score improvements, and <!-- language-all: lang-c --> before your code starts to fix syntax highlighting. I also managed to reduce my byte-count somewhat by dropping the loop altogether \$\endgroup\$
    – tucuxi
    Apr 20, 2016 at 8:40
  • \$\begingroup\$ Your 'detailed' looks very different from a plain expansion of the golfed code (where's n in the short version?). Also, you probably ought to mention that you're assuming ASCII encoding - you'll get different numbers on EBCDIC machines. \$\endgroup\$ Apr 22, 2016 at 10:25
  • \$\begingroup\$ @TobySpeight the detailed version is supposed to show how it was built basically, yes I'm assuming ASCII which is the common case in C. \$\endgroup\$
    – Khaled.K
    Apr 22, 2016 at 17:13
3
\$\begingroup\$

Julia, 51 49 bytes

n->diff(["@1634@8725"...][digits(n)+1]).^2%48⊆1

Verification

julia> f=n->diff(["@1634@8725"...][digits(n)+1]).^2%48⊆1
(anonymous function)

julia> all(map(f,(1,2,3,4,5,6,7,8,9,16,18,38,61,81,294,349,381,383,729,767,38183,38383,18349276,183492761,618349276)))
true

julia> any(map(f,(10,11,50,53,55,65,95,100,180,182,184,185,186,187,188,189,209,305,2009,5030,3838384,4838383,183492760)))
false
\$\endgroup\$
3
\$\begingroup\$

Actually, 30 bytes

;#pXZdX`Σ"67294381";'1+R+íu`Mπ

Takes input as a string. Outputs a positive integer for true and 0 for false.

Try it online!

Explanation:

;#pXZdX`Σ"67294381";'1+R+íu`Mπ
                                 (implicit) push input
;#pXZdx                         push zip(n[:-1], n[1;]) (pairs of digits)
       `Σ"67294381";'1+R+íu`M   map:
        Σ                         join digits
         "67294381";'1+R+         push "16729438183492761" (the magic string used in many other solutions)
                         íu       0-based index (-1 if not found), increment so 0 is not found and >=1 is the 1-based index
                             π  product
        
\$\endgroup\$
0
3
\$\begingroup\$

PowerShell v2+, 105 96 bytes

param($a)((1..$a.length|%{'27618349294381672'.IndexOf($a[$_-1]+$a[$_])+1})-join'*'|iex)-or$a-eq5

Iterates through the input (which must be encapsulated with "") by verifying that the index of any sequential pair of characters is in the valid lookup string. I see Kevin Lau had something similar, but I came up with this independently. Each of those indices are added with +1, as the .IndexOf() function will return -1 if the string isn't found. This will turn "not found"s into 0.

We then -join all the resultant integer values with * and pipe that to iex (similar to eval). This will mean if any one of the indices is not found, the entire expression will result in 0. That is encapsulated in parens and -or'd with $a-eq5 for the special case of input "5" to achieve our resultant output.

Test Runs

PS C:\Tools\Scripts\golfing> 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 18, 38, 61, 81, 294, 349, 381, 383, 729, 767, 38183, 38383, 18349276, 183492761, 618349276 | %{.\numpad-knight-numbers.ps1 "$_"}
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True

PS C:\Tools\Scripts\golfing> 10, 11, 50, 53, 55, 65, 95, 100, 180, 182, 184, 185, 186, 187, 188, 189, 209, 305, 2009, 5030, 3838384, 4838383, 183492760 | %{.\numpad-knight-numbers.ps1 "$_"}
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
\$\endgroup\$
2
\$\begingroup\$

C, 78 85 bytes

char*a="9614397052";f(x){int b=x/10;return!b||b*x%5&&abs(a[x%10]-a[b%10])%6==1&f(b);}

Since everyone else has taken the input as a string, I tried doing it in integers. It works recursively from the least-significant digit (a%10); if it's the only digit, then return true. Otherwise, return true only if the tens digit (b%10) can't be reached from the units digit, and (recursively), the rest of the input satisfies the same test.

The test for reachability works by encoding the knight's tour linearly, and converting each digit to its position (zero to seven) on the tour. For digits 0 and 5, we assign position nine, which is disconnected from the other positions. Then, mutually reachable numbers differ by one (mod eight); i.e. a[x%10]-a[b%10] is either ±1 or ±7. So we test the absolute difference (mod 6) against 1.

This solution works for any character encoding that is valid for C (i.e. digits have contiguous codes from 0 to 9).

\$\endgroup\$
1
  • \$\begingroup\$ Fixed by testing that neither digit is a multiple of 5. \$\endgroup\$ Jan 13, 2020 at 10:43
1
\$\begingroup\$

Java 8, 179 167 Bytes

Places the number pad ints (minus 5 and 0) in a circle. l holds the circle index of these ints. If the difference of two indices is +/- 3 mod 8, then there is a knights move between the ints corresponding to those indices. Note that x is an int[].

x->{if(x.length<2)return 1;int[] l={0,0,1,2,7,0,3,6,5,4};int o=l[x[1]];for(int i:x){int n=l[i];if(i%5==0||(Math.abs(n-o)!=3&&Math.abs(n-o)!=5))return 0;o=n;}return 1;}

Update

  • -11 [16-12-10] Switched to a lambda
  • -1 [16-12-10] Use <2 instead of ==1
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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