Working on my Knight moves

16

2

Hexagonal chess describes a family of chess variants played on a board where the cells are hexagons instead of the traditional squares. There are many such variants; in this challenge we'll be focusing on Gliński's variant, which is the most common.

The board is composed of three colors (so that the same color doesn't share an edge), with the edges of the hexagons facing the players. The board has 11 files, marked by letters a through l (letter j is not used), and 11 ranks (which bend 60° at file f). Ranks 1 through 6 each contain 11 cells, rank 7 has 9 cells, rank 8 has 7, and so on. Rank 11 contains exactly one cell: f11. (If it helps, think of each rank as making a very wide "V" shape.)

Here is an example picture of the board, with the knight on the center cell. The cells marked with a dot are the legal moves of this particular knight. The knight moves in a similar fashion to "normal" chess, two-down-and-one-over. In hexagonal chess terms, it's an orthogonal move (across an edge), then a diagonal move in the same direction (the closest move to the same color). For example with the knight below, an orthogonal move "up" to the light brown is then accompanied by a diagonal move "up and right" or "up and left" to the nearest light brown.

Gliński's variant knight

From the public domain via https://commons.wikimedia.org/wiki/File:Glinski_Chess_Knight.svg

This knight is positioned at f6 and the legal moves are thus

c4, c5, d3, d7, e3, e8, g3, g8, h3, h7, i4, i5

Input

A single input giving the starting cell of our knight. This can be as a single string "b6", as two strings, "b", "6", etc., in any convenient format. The input letters can be uppercase or lowercase -- your choice.

Output

A list of the valid moves that a knight on that location can make. This can be as an array of strings, a single string with an unambiguous and consistent delimiter, separate strings by newlines, etc., whatever is most convenient. The output does not necessarily need to be in sorted order, and can be in either uppercase or lowercase -- your choice.

Rules

  • Assume no other pieces are on the board or interfere with the moves. We're focusing on just the knight.
  • Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
  • If possible, please include a link to an online testing environment so other people can try out your code!
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

Examples

b6
a3, c4, d5, d9, e7, e8

f6
c4, c5, d3, d7, e3, e8, g3, g8, h3, h7, i4, i5

f11
d8, e8, g8, h8

i1
f2, f3, g4, h4, l2, k3

AdmBorkBork

Posted 2017-04-11T16:02:29.027

Reputation: 41 581

12This coordinate system is the work of the devil. – Martin Ender – 2017-04-11T16:09:44.153

2@MartinEnder Points if you do it in Hexagony then :) – Erik the Outgolfer – 2017-04-11T16:11:20.857

I feel like I could transform this into another vector space by redefining the two axes to the horizontal and the 60 degree diagonal, and then just use regular moves and then translate it back using linear algebra, but I think that's overcomplicating things :P And also I agree that the coordinate system is the most evile thing I have seen around here on this site. :P – HyperNeutrino – 2017-04-14T21:05:57.457

Answers

11

JavaScript (ES6), 184 bytes

Takes the file F as a character and the rank R as an integer in currying syntax (F)(R). Returns an array of strings.

F=>R=>[...'100124566542'].map((X,i)=>(X-=3-(x=(s='abcdefghikl').search(F)))-7<(Y=('9641001469'[i]||10)-(A=Math.abs)(x-5)+17-2*R)&X+Y>3&X+16>Y&X+Y<27&&s[X]+(22-Y-A(X-5))/2).filter(n=>n)

How?

Step #1: convert file/rank to Cartesian coordinates

We convert the hexagonal chess coordinates to Cartesian coordinates (x, y) with x in [0 .. 10] and y in [0 .. 20]:

      00 01 02 03 04 05 06 07 08 09 10
   +----------------------------------
00 |                f11                     F = file (letter)
01 |             e10   g10                  R = rank in [1 .. 11]
02 |          d09   f10   h09               
03 |       c08   e09   g09   i08            F | a b c d e f g h i k l
04 |    b07   d08   f09   h08   k07         --+-----------------------
05 | a06   c07   e08   g08   i07   l06      x | 0 1 2 3 4 5 6 7 8 9 10
06 |    b06   d07   f08   h07   k06         
07 | a05   c06   e07   g07   i06   l05      y = 22 - |x - 5| - 2R
08 |    b05   d06   f07   h06   k05   
09 | a04   c05   e06   g06   i05   l04
10 |    b04   d05   f06   h05   k04   
11 | a03   c04   e05   g05   i04   l03
12 |    b03   d04   f05   h04   k03   
13 | a02   c03   e04   g04   i03   l02
14 |    b02   d03   f04   h03   k02   
15 | a01   c02   e03   g03   i02   l01
16 |    b01   d02   f03   h02   k01   
17 |       c01   e02   g02   i01      
18 |          d01   f02   h01         
19 |             e01   g01            
20 |                f01               

Step #2: apply the move vectors

Below is the list of the move vectors in the Cartesian system:

(-2, +4), (-1, -5), (+3, +1),
(-3, +1), (+1, -5), (+2, +4),
(-3, -1), (+2, -4), (+1, +5),
(-2, -4), (+3, -1), (-1, +5)

We apply each of them to the source coordinates (x, y) and get a list of target coordinates (X, Y).

Step #3: test the target coordinates

We now need to check which target coordinates are actually located inside the board. This is done by testing X + Y and X - Y:

X/Y

The coordinates are valid if all the following comparisons are true:

  • X + Y > 3
  • X + Y < 27
  • X - Y < 7
  • X - Y > -17

We should also verify that X is in [0 .. 10]. This is not done explicitly because s[X] is undefined if it's not, which eventually results in a falsy value that gets filtered out.

Step #4: convert back to hexagonal chess coordinates

Finally the valid target coordinates are converted back to hexagonal chess coordinates, using the inverse of the formulas described at step #1.

Test cases

let f =

F=>R=>[...'100124566542'].map((X,i)=>(X-=3-(x=(s='abcdefghikl').search(F)))-7<(Y=('9641001469'[i]||10)-(A=Math.abs)(x-5)+17-2*R)&X+Y>3&X+16>Y&X+Y<27&&s[X]+(22-Y-A(X-5))/2).filter(n=>n)

console.log(JSON.stringify(f('b')(6)))
console.log(JSON.stringify(f('f')(6)))
console.log(JSON.stringify(f('f')(11)))
console.log(JSON.stringify(f('i')(1)))

Arnauld

Posted 2017-04-11T16:02:29.027

Reputation: 111 334

Ah, that's a really clever way of getting around the hexagonal coordinate system. Nice! – AdmBorkBork – 2017-04-11T21:21:47.610

4

Batch. 403 bytes

@echo off
set j=a b c d e f g h i k l
set f=0
for %%f in (%j%)do set/af+=1&if %%f==%1 goto l
:l
set j=j%j: =%
set/a"r=6-f,r*=r>>31,r+=%2
for %%s in ("-3 -2" "-3 -1" "-2 1" "2 -1" "3 1" "3 2")do call:c %%~s
exit/b
:c
call:l %2 %1
:l
set/ag=f+%1,s=r+%2,t=g-s
if %g% geq 1 if %g% leq 11 if %s% geq 1 if %s% leq 11 if %t% geq -5 if %t% leq 5 set/a"t=6-g,s-=t*=t>>31"&call echo %%j:~%g%,1%%%%s%%

Adjusts the coordinate system, although in a different way to @Arnauld's answer. The c subroutine takes advantage of the symmetry by trying the mirror reflection of each move. (I also tried rotating but that took too many bytes.)

Neil

Posted 2017-04-11T16:02:29.027

Reputation: 95 035

3

JavaScript (ES6), 184 bytes

(s,t,j=' abcdefghikl',f=j.search(s),r=f<6?t:t+f-6)=>[...'120405..162645'].map((c,i)=>[(i>>1)-3+f,c-3+r]).filter(([f,r])=>f>0&f<12&r>0&r<12&f-r<6&r-f<6).map(([f,r])=>j[f]+(f<6?r:r+6-f))

I thought I'd port my Batch solution to ES6 to see how it compared... I didn't expect it to be that close...

Neil

Posted 2017-04-11T16:02:29.027

Reputation: 95 035

3

CJam, 77

1Z2W2Z]_Wf*+2/_Wf%+[r('a-_9>-_6-We>@~+]f.+{_~m5++B,-!},{~1$6-We>-\_8>+'a+\S}/

Try it online

Overview:

I'm using a coordinate system that looks like a..f and 1..6 on the left side, extended without bending, with letters replaced with numbers, and changed to be 0-based (b3→[1 2], g1→[6 1], k3→[9 6]). The relative moves in this system are [1 3], [2 -1], [2 3] and their reflections (negative and swapped, e.g. [1 3] → [-1 -3], [3 1], [-3 -1]). A resulting [x y] position is valid iff [x y z] ⊂ [0 1 .. 10] where z=x-y+5.

aditsu quit because SE is EVIL

Posted 2017-04-11T16:02:29.027

Reputation: 22 326

Interesting. So you translate the input to that coordinate system, perform the calculations, and then translate back? Neat. – AdmBorkBork – 2017-04-14T20:54:47.133

@AdmBorkBork pretty much, yeah – aditsu quit because SE is EVIL – 2017-04-14T21:04:47.420

1

Dyalog APL, 72 bytes

(6=|×/t,-/t←↑j[a⍳⊂⍞]-j←⊃,/i,¨¨↓∘i¨i-6)/a←⊃,/(11⍴⎕a~'J'),∘⍕¨¨⍳¨5+i⌊⌽i←⍳11

try

builds a list a of all valid cells: 'A1' 'A2' ... 'L6'

a is used for both input and output

builds a list j of the corresponding coordinates to a in a system where the x axis is along A6-L1 and y along F1-F11

an imaginary third coordinate is the difference of the first two

if the input cell is translated to coords 0 0 0, a knight can move to those cells whose product of coords is 6 or -6

ngn

Posted 2017-04-11T16:02:29.027

Reputation: 11 449

0

Python 3.6, 149

H='abcdefghikl'
lambda f,r:[H[i]+str(j)for i,j in[(H.find(f)+p%4*s,int(r)+p//4)for p in[9,6,-1,-5,-11,-10]for s in(1,-1)]if 0<i<11if 0<j<12-abs(6-i)]

An anonymous function called with two strings for the file and rank; returns a list of strings.

Ungolfed:

def h(f,r):
    H='abcdefghikl'

    A = []
    for p in[9,6,-1,-5,-11,-10]:
        for s in(1,-1):
            i = H.find(f) + p%4*s
            j = int(r) + p//4
            A.append(i, j)

    B = []
    for i,j in A:
        if 0 < i < 11 and 0 < j < 12 - abs(6 - i):
            B.append(H[i] + str(j))

    return B

RootTwo

Posted 2017-04-11T16:02:29.027

Reputation: 1 749