Knight on the Rim is Grim

48

9

Introduction

Aron Nimzowitsch was a leading chess master and a influential chess writer.

In his book 'My System', the first chapter deals about the importance of the center and why you should dominate it. The simple reason is that your pieces have more possible direct next moves when being in the center which again gives the player more power.

This gets very clear when looking at different positions of a knight and its potential next moves (shown in pink) on an empty board:

enter image description here

Objective

Evaluate the number of potential direct next moves of a knight on an empty board based on its position.

Input Specs

The position of the knight.

First the x (column) and then the y (row). 0 0 is the left bottom corner.

For simplicity, I changed the labels of a chess board to numbers only. For our examples and test cases we use a 0-based index, you are free to use a 1-based index though.

You can use any type of possible input formats, an array, function arguments, etc.

Output Specs

The number of potential direct next moves for a knight on an empty board.

Test Cases

3 4 => 8
4 6 => 6
7 7 => 2
1 0 => 3

Test cases are employing a 0-based index. The full grid of values is:

2 3 4 4 4 4 3 2
3 4 6 6 6 6 4 3
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
3 4 6 6 6 6 4 3
2 3 4 4 4 4 3 2

starcorder

Posted 2016-06-28T05:17:30.187

Reputation: 461

9Nice first challenge! :-) – Luis Mendo – 2016-06-28T07:58:00.153

14"Knight on the rim is grim" – None – 2016-06-28T08:16:07.813

2@stacey Your comment would have been a great title for this puzzle :) – starcorder – 2016-06-28T08:24:18.063

6Now for the really hard question: Are the red knights in the above images all the same color? – mbomb007 – 2016-06-29T21:53:29.263

Answers

25

Python 2, 35 bytes

lambda x,y:50/(8+x*x/7-x+y*y/7-y)-4

Try it online!


Python 2, 39 bytes

lambda x,y:50/(8-x*(7-x)/5-y*(7-y)/5)-4

Try it online!

Takes inputs 0-indexed.

The expression x*(7-x)/5 takes the coordinate values 0..7 to

[0, 1, 2, 2, 2, 2, 1, 0]

(min(x,7-x,2) does the same, but is longer.) Summing this for x and y gives the right pattern but with the wrong numbers

0 1 2 2 2 2 1 0
1 2 3 3 3 3 2 1
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
1 2 3 3 3 3 2 1
0 1 2 2 2 2 1 0

(See Neil's solution for better reasoning as to why this gives about the right pattern.)

Finally, the mapping a -> 50/(8-a)-4 with floor-division gives the right values

2 3 4 4 4 4 3 2
3 4 6 6 6 6 4 3
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
3 4 6 6 6 6 4 3
2 3 4 4 4 4 3 2

An alternative equally-long solution with 1-indexed inputs:

lambda x,y:(x*(9-x)/6+y*(9-y)/6)**2/6+2

xnor

Posted 2016-06-28T05:17:30.187

Reputation: 115 687

(7-a)*a/5 is 3 bytes shorter than min(a,7-a,2). – Neil – 2016-06-28T09:37:40.163

1*l actually costs you a byte overall, lambda a,b:"23468"[(7-a)*a/5+(7-b)*b/5] is only 41 bytes. – Neil – 2016-06-28T09:39:52.177

@Neil I had just found the same thing with x*(9-x)/6, one-indexed. – xnor – 2016-06-28T09:41:09.847

1Why don't you use <strike> like everyone else to show golfing progress? – Insane – 2016-06-28T17:11:20.250

4

@Insane I think it looks ugly and doesn't actually help. The code is the important thing, and anyone who wants to see its evolution still need to look in the edit history. When my old code is different enough to be worth showing, I show the versions like here. But on this question, it's all minor improvements to the same strategy, so I found it cleaner just to mention the different possibilities.

– xnor – 2016-06-28T21:45:54.887

17

MATL, 17 14 13 12 bytes

Thanks to @Neil for 1 byte off!

8:HZ^ZP5X^=s

Input is 1-based.

Try it online!

Explanation

This computes the Euclidean distance from the input to each of the 64 positions in the chessboard, and finds how many of those values equal the square root of 5.

Since the coordinates are integer values, we can be sure that the two floating-point values representing the square root of 5 (that computed from the coordinates and that computed directly) are indeed the same.

8:      % Push array [1 2 ... 8]
H       % Push 2
Z^      % Cartesian power. Gives 2D array [1 1; 1 2; ... 1 8; 2 1; ... 8 8]     
ZP      % Implicit input. Compute Euclidean distances, considering each row as a point
5X^     % Square root of 5
=s      % Compute how many squared distances equal sqrt(5). Implicit display

Luis Mendo

Posted 2016-06-28T05:17:30.187

Reputation: 87 464

1Impressive and thanks for the explanation – starcorder – 2016-06-28T07:54:14.927

1If comparing the square of the square root of 5 to 5 fails due to rounding errors, can you not at least compare the square root of 5 to the square root of 5? – Neil – 2016-06-28T10:56:41.640

@Neil Thanks for the idea! Yes, since computations are with integers, I can be sure that the two "roots of 5" are the same double number. Ant it saves a byte – Luis Mendo – 2016-06-28T11:08:04.610

15

Mathematica 63 43 bytes

With 20 bytes saved thanks to suggestions by Martin Ender!

EdgeCount[8~KnightTourGraph~8,#+1+8#2/<->_]&

The above finds the number of squares that are 1 hop away from the given cell on the complete knights tour graph.


g=KnightTourGraph[8,8,VertexLabels->"Name",Axes->True]

displays the complete knight's tour graph, with vertex names and coordinates. Note that Mathematica defaults to one-based indexing for the coordinates.

graph


#+1+8#2&[r,f] converts returns the vertex corresponding to square at rank (row), r, and file (column), f, using zero-based values as input.

For example #+1+8#2&[2,1] returns 11.


EdgeCount gives the number of edges in the neighborhood graph.


The edges for rank 2, file 1 (square 11):

IncidenceList[8~KnightTourGraph~8, 8 #2 + # + 1] &[2, 1]

(*{1 <-> 11, 5 <-> 11, 11 <-> 17, 11 <-> 21, 11 <-> 26, 11 <-> 28}*)

The highlighted edges:

HighlightGraph[g, {1, 5, 11, 17, 21, 26, 28, Style[1 <-> 11, Thick, Blue], Style[5 <-> 11, Thick, Blue], Style[11 <-> 17, Thick, Blue], Style[11 <-> 21, Thick, Blue], Style[11 <-> 26, Thick, Blue], Style[11 <-> 28, Thick, Blue]},GraphHighlightStyle -> "DehighlightFade", PlotRangePadding -> .5]

highlight


Method 2: Euclidean distance

70 bytes

This method is longer, but possibly of some interest. The approach is to check the Euclidean distance between the center of the chessboard and the cell of interest.

Which[(x=Sqrt@Tr[({3.5, 3.5}-#)^2])<2.2,8,x<3,6,x<4,4,x<4.6,3,x>4.6,2]&

Exemplifying

Which[(x=Sqrt@Tr[({3.5, 3.5}-#)^2])<2.2,8,x<3,6,x<4,4,x<4.6,3,x>4.6,2]&@{0, 0}
Which[(x=Sqrt@Tr[({3.5, 3.5}-#)^2])<2.2,8,x<3,6,x<4,4,x<4.6,3,x>4.6,2]&@{3, 3}

2

8


To help visualize how the distance from the center of the chessboard is sufficient for assigning a value.

values={{2,3,4,4,4,4,3,2},{3,4,6,6,6,6,4,3},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{3,4,6,6,6,6,4,3},{2,3,4,4,4,4,3,2}};
f[x_]:=Text[x,#]&/@Position[values,x]r_~w~p_:=RegionMember[{3.5`,3.5`}~Disk~r,p]
h@y_:=Which[2.2~w~y,8,3~w~y,6,4~w~y,4,4.6~w~y,3,2<3,2]

Graphics[{Circle[{4.5, 4.5}, 2.3], Circle[{4.5, 4.5}, 3], 

Circle[{4.5, 4.5}, 4],

Circle[{4.5, 4.5}, 4.6], Flatten[f /@ {2, 3, 4, 6, 8}, 1]}, Axes -> True, AxesOrigin -> {-1, -1}]


The numbers 2.2, 3, 4, and 4.6 are the radii of the circles.

image

DavidC

Posted 2016-06-28T05:17:30.187

Reputation: 24 524

1Great tour graph – starcorder – 2016-06-28T13:49:52.330

20KnightTourGraph Mathematica and its builtins... :-) – Luis Mendo – 2016-06-28T13:56:11.477

I think there's a stray # at the end of your source code (just before the ]). You should be able to use IncidenceList instead of EdgeList@NeighborhoodGraph though. (Alternatively, there's also EdgeCount, but I think that ends up being longer.) – Martin Ender – 2016-06-28T14:08:45.740

1Oh wait, it's actually shorter: EdgeCount[8~KnightTourGraph~8,#+1+8#2<->_]& – Martin Ender – 2016-06-28T14:10:14.787

EdgeCount is very cool! – DavidC – 2016-06-28T14:32:46.090

@LuisMendo [tag:builtin-request] Goat-tour graph? – Rɪᴋᴇʀ – 2016-06-28T14:38:58.560

@EᴀsᴛᴇʀʟʏIʀᴋ :-) – Luis Mendo – 2016-06-28T15:05:36.197

12

JavaScript (ES6), 38 bytes

(x,y)=>+"23468"[((7-x)*x+(7-y)*y)/5|0]

Takes 0-indexed inputs. Explanation: Look at the squares of the distances to the centre:

24.5 18.5 14.5 12.5 12.5 14.5 18.5 24.5
18.5 12.5  8.5  6.5  6.5  8.5 12.5 18.5
14.5  8.5  4.5  2.5  2.5  4.5  8.5 14.5
12.5  6.5  2.5  0.5  0.5  2.5  6.5 12.5
12.5  6.5  2.5  0.5  0.5  2.5  6.5 12.5
14.5  8.5  4.5  2.5  2.5  4.5  8.5 14.5
18.5 12.5  8.5  6.5  6.5  8.5 12.5 18.5
24.5 18.5 14.5 12.5 12.5 14.5 18.5 24.5

The number of reachable squares falls into five bands:

8    0-5
6    5-10
4   10-15
3   15-20
2   20-25

I actually calculate 24.5 - (3.5 - x) ** 2 - (3.5 - y) ** 2 = (7 - x) * x + (7 - y) * y as it's a shorter calculation, but all it does is reverse the order of the bands.

Neil

Posted 2016-06-28T05:17:30.187

Reputation: 95 035

Super concise and very nice approach, so I don't have to start my own JS solution anymore :) – starcorder – 2016-06-28T10:00:56.927

Good point about the formula being equivalent about radius squared. I had thought of x*(7-x) as just an operation that looks like a downward arc on 0..7 and happens to curve fit, but this explains why it produces such a nice pattern when summed for x and y. – xnor – 2016-06-28T10:04:36.303

11

Jelly, 10 bytes

8ṗ2_³²S€ċ5

1-indexed. Takes a single argument of the form [x,y]. Try it here.

8ṗ2          Cartesian square [[1,1],[1,2]…[8,8]]
   _³        Subtract the input
     ²S€     Compute the norm of each vector
        ċ5   Count fives

Dennis saved a byte!

Lynn

Posted 2016-06-28T05:17:30.187

Reputation: 55 648

Just eleven bytes, wow! – starcorder – 2016-06-28T12:00:38.227

I saw this question in the morning and this is the exact algorithm I thought I'd implement in Jelly when I had time. :P – PurkkaKoodari – 2016-06-28T12:09:29.797

8

Mathematica, 44 40 bytes

I've currently got three solutions at the same byte count:

2[3,4,6,8][[Tr@⌊3.2-.8Abs[#-4.5]⌋]]&
Tr@⌈.85(4-Abs[#-4.5])⌉/.{5->6,6->8}&
⌊Tr@⌈.85(4-Abs[#-4.5])⌉^1.1608⌋&

All of those are unnamed functions that take pair of coordinates like {3, 4}, which are 1-based.

I tried coming up with a somewhat explicit formula. The general pattern on the entire board looks like this:

enter image description here

The actual values of those colours (from lightest to darkest) are 2, 3, 4, 6, 8. That is:

2 3 4 4 4 4 3 2
3 4 6 6 6 6 4 3
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
3 4 6 6 6 6 4 3
2 3 4 4 4 4 3 2

We first exploit the symmetry by shifting the origin to the centre, taking the absolute value and subtracting the result from 4. This gives us coordinates 0.5 to 3.5 increasing from each corner. In order to make the centre coordinates the same we need to map 0.5 and 1.5 to different values and 2.5 and 3.5 to the same value. This is easily done by multiplying by 0.8 (gives{0.4, 1.2, 2., 2.8}) and flooring the result. So now we've got {0, 1, 2, 2} as distances from the centre. If we add up the coordinates in each cell, we get this table:

0 1 2 2 2 2 1 0
1 2 3 3 3 3 2 1
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
2 3 4 4 4 4 3 2
1 2 3 3 3 3 2 1
0 1 2 2 2 2 1 0

This has unique values for all the different possible results, so we simply use it as an index into 2[3,4,6,8].

In the second version we use ceiling instead floor. This way, 2, 3 and 4 are already correct, but we get 5 and 6 instead of 6 and 8, so we correct those manually with a substitution rule.

Finally, in the third version, we extend 5 and 6 upwards to 6 and 8 by means of exponentiation, followed by another floor operation.

Martin Ender

Posted 2016-06-28T05:17:30.187

Reputation: 184 808

I like very much the approach using the board's general pattern, great! – starcorder – 2016-06-28T07:56:33.960

6

Java - 160 150 bytes

int m(int r,int c){int m=0,i,j;for(i=0;i<3;i+=2)for(j=0;j<3;j+=2){m+=r+i>0&r+i<9&c+2*j>1&c+2*j<11?1:0;m+=r+2*i>1&r+2*i<11&c+j>0&c+j<9?1:0;}return m;}

Ungolfed:

public static int m(int r, int c) {
    int m=0;
    for(int i=-1;i<2;i+=2)
        for(int j=-1;j<2;j+=2){
            m += r+i>-1 && r+i<8 && c+2*j>0 && c+2*j<8 ? 1:0;
            m += r+2*i>0 && r+2*i<8 && c+j>1 && c+j<8 ? 1:0;
        }
    return m;
}

The ungolfed code is identical except for changing the bounds of the for loop to save 4 bytes. Works by iterating through each possible move and performing a bounds check (>0 and <8). Uses the fact that the offsets are (1, 2), (2, 1), (-1, 2), (-2, 1), etc. and is able to check 2 moves for each value of i and j.

Edit: 10 bytes saved thanks to suggestions by Leaky Nun and u902383.

ejaszewski

Posted 2016-06-28T05:17:30.187

Reputation: 71

This was also fast, nice! – starcorder – 2016-06-28T06:09:02.110

Had an error in there, it has been fixed. – ejaszewski – 2016-06-28T06:22:46.197

1int m=0,i=-1,j; to save some bytes – Leaky Nun – 2016-06-28T07:23:45.797

1change logical AND to bitwise AND and that will allow you to remove additional 6 characters – user902383 – 2016-06-28T09:43:53.163

6

APL, 21 chars

{+/,5=+/¨×⍨(⍳8 8)-⊂⍵}

In English:

  • (⍳8 8): 8x8 rank-2 array containing the coordinates of all the cells;
  • +/¨×⍨(⍳8 8)-⊂⍵: square of the euclidean distances of the given cell with respect to every cell on the board;
  • 5=: matrix of 0/1, where the 1s appear at squared distances equal 5;
  • +/,: sum the flattened matrix

Test (in origin 1):

    f←{+/,5=+/¨×⍨(⍳8 8)-⊂⍵}
    f¨1+(3 4)(4 6)(7 7)(1 0)
8 6 2 3

In this form:

f←{+/,5=+/¨×⍨(⍳⍺)-⊂⍵}

the left argument can specify the dimensions of the board. Hence 8 8 f will work for the standard square chessboard. But on a larger and rectangular board, the test cases would give different results. For instance, on a 12x10 board:

    g←(10 12)∘f
    g¨1+(3 4)(4 6)(7 7)(1 0)
8 8 8 3

lstefano

Posted 2016-06-28T05:17:30.187

Reputation: 850

In APL jargon, a matrix is an array of rank 2, nothing said about the content of the cells. Years of (ab)use of the terms made me insensitive to it. I'll update the description for the more traditionally inclined readers. Thank you. – lstefano – 2016-06-28T11:04:59.150

@Istefano That use of "rank" as "number of dimensions" seems to suffer from the same affliction :-P – Luis Mendo – 2016-06-28T11:16:02.553

I'll be... You are right! You can see that it's been a while since I took Linear Algebra. I give up :-) – lstefano – 2016-06-28T11:23:31.003

1

Full program, 27: ≢⍸5=+/¨×⍨-∘⎕¨⍳8 8 Try it online!

– Adám – 2019-05-07T13:13:08.190

@Adám you mean 17 – ngn – 2019-05-08T17:26:38.593

@ngn Yes, typo. – Adám – 2019-05-08T17:27:43.113

6

C, 44 Bytes

f(x,y){return "23468"[((7-x)*x+(7-y)*y)/5];}

But this is better:

f(x,y){return "2344443234666643468888644688886446888864468888643466664323444432"[x*8+y];}

Giacomo Garabello

Posted 2016-06-28T05:17:30.187

Reputation: 1 419

1Missing ;. Won't compile. – ugoren – 2016-06-28T18:51:07.263

@GiacomoGarabello See: http://meta.codegolf.stackexchange.com/a/1146/55729

– Jacajack – 2016-06-28T20:23:10.803

1It's not a snippet, it's a function and it's not forbidden post functions. Sorry for the missing semicolon. Fixed. – Giacomo Garabello – 2016-06-28T20:28:10.420

5

Haskell, 49 48 bytes

w=[0..7]
x%y=sum[1|a<-w,b<-w,(a-x)^2+(b-y)^2==5]

Damien

Posted 2016-06-28T05:17:30.187

Reputation: 2 407

1You can save [0..7] to a variable for 1 byte. – xnor – 2016-06-28T11:18:45.033

5

Java, 81 characters (113 bytes)

int r(int a,int b){return "⍄䐲㑦晃䚈衤䚈衤䚈衤䚈衤㑦晃⍄䐲".codePointAt(a*2+b/4)>>(3-b%4)*4&15;}

Encode the whole result table as unicode table and then get appropriate bytes doing bitwise operations.

You can see it online here: https://ideone.com/K9BojC

cliffroot

Posted 2016-06-28T05:17:30.187

Reputation: 1 080

3

Python, 94 bytes

lambda x,y,a=[2,1,-1,-2,-2,-1,1,2]:list((9>x+a[i]>0)&(9>y+a[5-i]>0)for i in range(8)).count(1)

Uses 1 based indexing.

Demo at https://repl.it/C6gV.

Chuck Morris

Posted 2016-06-28T05:17:30.187

Reputation: 456

2

APL (Dyalog Unicode), 15 bytesSBCS

≢⍸2=|×.-∘⎕¨⍳8 8

Try it online!

voidhawk

Posted 2016-06-28T05:17:30.187

Reputation: 1 796

2

J, 23 bytes

1#.1#.5=[:+&*://]-/i.@8

Try it online!

Homage to Lynn's method, converted to J

Jonah

Posted 2016-06-28T05:17:30.187

Reputation: 8 729

2

Pyth - 33 15 bytes

Thanks to @LeakyNun for reducing my size by half.

Rearranging the maps and V's will probably lemme golf a little off.

/sM*Fm^R2-Rd8Q5

Test Suite.

Maltysen

Posted 2016-06-28T05:17:30.187

Reputation: 25 023

1This was fast, nice! – starcorder – 2016-06-28T05:56:11.730

215 bytes – Leaky Nun – 2016-06-28T07:21:33.643

1

Actually, 18 bytes

`;7-2km`MΣ8-:50\¬¬

Try it online!

This implements the same formula that many other answers have been using: 50/(8-x*(7-x)//5+y*(7-y))//5)-4. Input is taken as a list: [x,y] (or any iterable literal in Python, like (x,y) or x,y).

Explanation:

`;7-2km`MΣ8-:50\¬¬
`;7-2km`M           for each value in input:
 ;7-                  make a copy, subtract from 7
    2                 push 2
     km               minimum of the three values (x, 7-x, 2)
         Σ          sum
          8-        subtract from 8
            :50\    integer divide 50 by the value
                ¬¬  subtract 2 twice

Mego

Posted 2016-06-28T05:17:30.187

Reputation: 32 998

0

Perl 6, 44 bytes

{+grep {5==[+] $_>>²},((^8 X, ^8)XZ-(@_,))}

Try it online!

bb94

Posted 2016-06-28T05:17:30.187

Reputation: 1 831