Mathematica 1183 483 bytes (or 190) bytes
Approach 1
240-50 (bonus) = 190 bytes
The knight's tour can be represented as a graph. So the issue becomes simply to find the shortest path from each knight to the pawn. Mathematica offers both KnightTourGraph
and FindShortestPath
as built-in functions.
w@p_:=Characters@p/.{r_,s_}:>(ToCharacterCode[s][[1]]-65)*8+ToExpression@r
z@v_:=ToString@(v~Mod~8/.{0->8})<>FromCharacterCode[Quotient[v-1,8]+65]
p_~h~k_:=Row[SortBy[(z/@FindShortestPath[8~KnightTourGraph~8,#,w@p])&/@w/@k,Length][[1]],"->"]
Examples
h["5A",{"1C","2G"}]
1C -> 3B -> 5A
h["8G",{"1C","2G"}]
2G -> 3E -> 5D -> 7E -> 8G
Approach 2
(533-50 (bonus)= 483 bytes)
Here the knight's tour graph is constructed without using KnightTourGraph
.
s@v_:=
Sort[{v,#}]/.{{a_, b_}:>UndirectedEdge[a,b]}&/@(
If[If[#4==1,Greater,Less][Mod[v-1,8]+1,#1]&&If[#5==1,Greater,Less][Quotient[v-1,8]+1,#2],v+#3,Nothing]&@@@{{1,2,-17,1,1},{8,2,-15,0,1},{2,1,-10,1,1},{7,1,-6,0,1},{2,8,6,1,0},{7,8,10,0,0},{1,7,15,1,0},{8,7,17,0,0}})
w@p_:=Characters[p]/.{r_,q_}:>(ToCharacterCode[q][[1]]-65)*8+ToExpression@r
z@v_:=ToString@(Mod[v,8]/.{0 -> 8})<>FromCharacterCode[Quotient[v-1,8]+65]
p_~f~k_:=Row[SortBy[(z/@FindShortestPath[Graph[Range@64,Union@Flatten[s/@Range@64]],#,w@p])&/@w/@k,Length][[1]],"->"]
Explanation
Below are two views of the chessboard.
On the left, each chessboard position corresponds to vertex bearing an integer name. On the right, the positions have been labelled according to the convention suggested by the OP.
In both figures, White sits on the right, Black sits on the left.
![chessboards](../../I/static/images/f7bd4ee3a0f3dc7323a54290f1f9a4928281ddb85fede48e8025c80830dadab5.png)
Legal moves of a knight are paths along a knight tour graph.
The function, s
, generates all 512 moves (as if each square allowed for 8 knight moves), eliminates inverses, and then weeds out the moves that would land off the board. There are 168 edges on the graph.
edges
consists of a list of elements of the form, UndirectedEdge[a,b]
. For instance, the edge, UndirectedEdge[1,11]
corresponds to the idea that a knight at vertex 1, that is, at "A1" in standard notation, may jump to vertex 11, or "B3", and vice-versa.
toVertex
takes a position (see right board) and returns a vertex (see left board). For instance toVertex["3H"]
returns 59
.
toChessPosition
does the opposing conversion, namely from a vertex to a chess position.
vertexNames
is a list of replacements: {1->"1A",2->"2A"...64->"8H"}
.
This is only needed for the right chessboard figure shown above.
numberedKnightsTour
is the graph displayed above on the left.
chessKnightsTour
is the graph displayed above on the right.
The graphs are constructed using the above-defined vertices and edges.
findShortestPath
receives the position of the pawn and a list of positions of the knights and finds the shortest path along the graph from a knight to the pawn.
s@v_:=
Sort[{v,#}]/.{{a_, b_}:>UndirectedEdge[a,b]}&/@(
If[If[#4==1,Greater,Less][Mod[v-1,8]+1,#1]&&If[#5==1,Greater,Less]
edges=Union@Flatten[s/@Range@64];
toVertex[p_]:=Characters[p]/.{rank_,file_}:> (ToCharacterCode[file][[1]]-65)*8+ToExpression[rank]
toChessPosition[v_]:=ToString@(Mod[v,8]/.{0->8})<>FromCharacterCode[Quotient[v-1,8]+65]
vertexNames=Thread[Range@64->(toChessPosition[#]&/@Range[64])];
numberedKnightsTour=Graph[Range@64,edges,VertexLabels->"Name",GraphLayout->"SpringEmbedding"];
vertexNames=Thread[Range[64]->(toChessPosition[#]&/@Range[64])];
chessKnightsTour=Graph[Range@64,edges,ImageSize->Large,VertexLabels-> vertexNames,GraphLayout->"SpringEmbedding"];
GraphicsGrid[{{numberedKnightsTour,chessKnightsTour}},PlotLabel->"For each board below, \nWhite sits at Right, Black at Left\nNumbers refers to rank, letters refer to file",ImageSize->Large]
findShortestPath[pawn_,knights_]:=Row[SortBy[(toChessPosition/@FindShortestPath[chessKnightsTour,#,toVertex@pawn])&/@toVertex/@knights,Length][[1]],"->"]
You posted two challenges in rapid succession. Did you get these challenges from some external website? In that case, you should link to them. If you wrote these challenges yourself, that's rather impressive :-). – Justin – 2015-10-02T16:39:21.630
3
Suggestion: remove the [code-challenge] tag; I think there's a [chess] tag, instead of "you get one point ..." or "you lose 50 points", remove the three points of the Scoring System and instead put under "Bonuses", "-50 chars if your code works with N knights". Also, I recommend using the sandbox in the future.
– Justin – 2015-10-02T16:41:29.587@Justin I just started posting in Code Golf. I actually have a collection of them and I came up with most of them. Now some of them may be duplicates because someone else must have thought of it too, but I haven't checked. I just hope you like them.. – DaKnOb – 2015-10-02T16:41:38.020
@Justin Thanks. Suggestions implemented. I'm still learning – DaKnOb – 2015-10-02T16:51:16.330
Very closely related -- http://codegolf.stackexchange.com/q/2645/42963 – AdmBorkBork – 2015-10-02T16:56:53.047
@TimmyD Well spotted. One could go ahead and use these algorithms and just add the comparison logic. This still has some challenge in it but you're definitely right. – DaKnOb – 2015-10-02T16:58:22.713
1The choice of squares is interesting. Because a knight always hops to an opposite coloured square, and these squares are opposite coloured, it means there is never a "draw." By convention, squares are normally labelled A1 instead of 1A, but that doesn't make any difference. – Level River St – 2015-10-02T17:59:13.700
Do we have to consider an 8x8 board, i.e. 1-8 and A-H? Do we have to output exactly as shown in the example? If so, what do we do if more than one move is required? Continue on the same line? – Level River St – 2015-10-02T18:01:49.957
@steveverrill Yes, this is an 8x8 board. If there are more than one moves you can output them however you want as long as it's humanly understandable without much effort. You can use for example a comma separated list or a new line. It's up to you. – DaKnOb – 2015-10-02T18:04:48.913
@DaKnOb So there is no need for the superfluous
Move Knight 1C \n Moves:
then? For example 1 I can output simply1C
, for example 21C 3B
and for 5C I can output either1C 3B 5C
or1C 3D 5C
? – Level River St – 2015-10-02T18:10:39.163@steveverrill I've updated the question. You must have the same output as above. I'm standardizing this so different answers don't get a head start because of less verbose output. – DaKnOb – 2015-10-02T18:27:57.337
@DaKnOb That's fine. It's really important with Codegolf it's really important to specify exactly what output is allowed. – Level River St – 2015-10-02T18:56:41.467
@DaKnOb BTW you still haven't said what we should do if there is more than one path of the same length. I guess any valid output is OK? – Level River St – 2015-10-02T19:02:54.047
@steveverrill Yes, any path in that case is correct. In general I want a path such as that there are no shorter paths than this one. – DaKnOb – 2015-10-02T19:12:14.777
I don't see how the Knight's Tour is relevant to this challenge. – Doorknob – 2015-10-02T21:46:12.660
@Doorknob First of all it proves that this challenge is possible and second of all it might help in finding the perfect algorithm for knight movement. – DaKnOb – 2015-10-03T06:50:49.887
For the N knights bonus version, where are the knights? – Anders Kaseorg – 2015-10-03T22:28:40.497
@AndersKaseorg Their positions should be configurable. For example you could have a list of all Knight positions and then your program could iterate over the list when given a point in the chess board. – DaKnOb – 2015-10-04T14:19:40.610