A Numpad's Knight Numbers

34

2

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

Calvin's Hobbies

Posted 2016-04-18T18:55:20.390

Reputation: 84 000

2

What's with chess knights today? :-D

– Luis Mendo – 2016-04-18T19:03:05.600

1Hint: 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. – Fund Monica's Lawsuit – 2016-04-18T19:05:04.290

3@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. – Fund Monica's Lawsuit – 2016-04-18T19:37:55.983

@QPaysTaxes Oh, I thought you meant circle but 123...9. Sorry – Luis Mendo – 2016-04-18T19:41:16.527

@LuisMendo No worries. Like I said, I should've been clearer about what I meant. – Fund Monica's Lawsuit – 2016-04-18T19:42:29.837

What is the expected behavior for 0? Is it truthy just like the other 1-digit numbers? – Value Ink – 2016-04-18T20:02:19.703

@KevinLau "Write a program or function that takes in a positive decimal integer" - i.e. you don't need to worry about 0. – Calvin's Hobbies – 2016-04-18T20:03:26.620

@HelkaHomba - Yes, but a number with more than 1 digit could have 0's and still be positive. I assume you are ruling those out? – Darrel Hoffman – 2016-04-18T21:38:39.763

@DarrelHoffman No. Some falsy examples clearly contain zeroes. Zero itself is not considered, but numbers with zeros cannot be ignored. – Calvin's Hobbies – 2016-04-18T21:40:48.677

@HelkaHomba - That's what I meant - the presence of a 0 automatically rules out the number, i.e. makes it not a knight-number. It'd be an interesting twist to allow 0, but have two of them since the 0-key is two keys wide, so you could go 305, or 406, but not 405 because you're on the wrong side of the 0 key. This would invalidate a lot of answers though, so that'd be an entirely different challenge. – Darrel Hoffman – 2016-04-18T21:52:04.740

Answers

16

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.

Dennis

Posted 2016-04-18T18:55:20.390

Reputation: 196 637

18

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.

xnor

Posted 2016-04-18T18:55:20.390

Reputation: 115 687

13

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.

Martin Ender

Posted 2016-04-18T18:55:20.390

Reputation: 184 808

7

Pyth - 35 28 bytes

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

Test Suite.

Maltysen

Posted 2016-04-18T18:55:20.390

Reputation: 25 023

7

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.

Value Ink

Posted 2016-04-18T18:55:20.390

Reputation: 10 608

Oh, that lookup string would also save me 17 bytes. Do you mind if I use that for my Retina answer? – Martin Ender – 2016-04-18T20:21:24.640

Go for it! Just give credit, I guess. – Value Ink – 2016-04-18T22:30:44.190

Thanks, but I ended up with an even shorter solution based on a suggestion by Sp3000 :) – Martin Ender – 2016-04-19T07:16:25.857

6

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...

Yakk

Posted 2016-04-18T18:55:20.390

Reputation: 859

2Neither 5 nor 185 emit 1 with your command line, while 5 is in the truthy, and 185 in the falsy list. – Guntram Blohm supports Monica – 2016-04-18T20:51:47.420

1@GuntramBlohm fixed -- got lost in regular negation – Yakk – 2016-04-18T21:23:01.760

6

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.

Neil

Posted 2016-04-18T18:55:20.390

Reputation: 95 035

Couldn't do better, my best try was at 88 bytes. Bravo! – Naouak – 2016-04-18T22:47:01.587

@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.) – Neil – 2016-04-19T07:54:41.693

@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. – Neil – 2016-04-19T08:37:55.763

@Neil Ah, right. – user81655 – 2016-04-19T08:40:09.117

6

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.

nimi

Posted 2016-04-18T18:55:20.390

Reputation: 34 639

Why does zip<*>tail work like a flipped version of zip=<<tail? I think I don't understand what applicatives generalize. – xnor – 2016-04-18T22:07:09.060

@xnor: I just use it. <*> is defined as (<*>) f g x = f x (g x).

– nimi – 2016-04-18T22:11:21.757

6

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.

tucuxi

Posted 2016-04-18T18:55:20.390

Reputation: 583

78 bytes – ceilingcat – 2020-01-13T06:12:46.923

5

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!

Luis Mendo

Posted 2016-04-18T18:55:20.390

Reputation: 87 464

1Is this even allowed?? – CalculatorFeline – 2016-04-19T00:52:46.720

The question asks for a truthy or a falsy value, not a whole array. – n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ – 2016-04-19T04:24:20.510

2

@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)

– Dennis – 2016-04-19T04:33:40.260

CC @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ – Dennis – 2016-04-19T04:34:05.853

4

05AB1E, 29 bytes

Code:

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

Uses CP-1252 encoding. Try it online!.

Adnan

Posted 2016-04-18T18:55:20.390

Reputation: 41 965

4

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!

beaker

Posted 2016-04-18T18:55:20.390

Reputation: 2 349

@LuisMendo Right on both counts, thanks! – beaker – 2016-04-19T01:02:09.817

@Dennis Updated, and hopefully correct. Thanks for your help. – beaker – 2016-04-19T16:19:01.520

I don't think you need the A at the end. MATL's vectors are truthy iff they do not contain a 0. – Dennis – 2016-04-19T22:07:57.497

4

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
}

Khaled.K

Posted 2016-04-18T18:55:20.390

Reputation: 1 435

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. – tucuxi – 2016-04-20T00:44:36.027

@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 – Khaled.K – 2016-04-20T08:03:57.007

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 – tucuxi – 2016-04-20T08:40:00.200

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. – Toby Speight – 2016-04-22T10:25:27.913

@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. – Khaled.K – 2016-04-22T17:13:26.153

3

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

AdmBorkBork

Posted 2016-04-18T18:55:20.390

Reputation: 41 581

3

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

Dennis

Posted 2016-04-18T18:55:20.390

Reputation: 196 637

3

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

Mego

Posted 2016-04-18T18:55:20.390

Reputation: 32 998

2

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).

Toby Speight

Posted 2016-04-18T18:55:20.390

Reputation: 5 058

Fixed by testing that neither digit is a multiple of 5. – Toby Speight – 2020-01-13T10:43:12.863

1

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

NonlinearFruit

Posted 2016-04-18T18:55:20.390

Reputation: 5 334