10
\$\begingroup\$

As the title says, given a valid chess move in algebraic notation, produce the string describing the move.

Example:

fxg8=Q+  -> Pawn takes g8 promotes to Queen check
Bb7#     -> Bishop to b7 mate

Here is a list of all the keywords and their respective notation

R     -> Rook             x   -> takes
N     -> Knight           +   -> check
B     -> Bishop           #   -> mate
Q     -> Queen            =   -> promotes to
K     -> King             0-0 -> short castle
0-0-0 -> long castle

Note: Long and short castle can be represented with either number 0 or letter O uppercase. You can choose any you'd like.

From wiki: Both algebraic notation and descriptive notation indicate kingside castling as 0-0 and queenside castling as 0-0-0 (using the digit zero). Portable Game Notation and some publications use O-O for kingside castling and O-O-O for queenside castling (using the letter O) instead


Piece moves format follows {Piece name} to {square} Moves without a piece are consider pawn moves. Example:

g4  -> Pawn to g4
Nh7 -> Knight to h7

For ambiguous moves, input will specify both departure rank and file. Example:

Rh8b8 -> Rook h8 to b8
Qb3e6 -> Queen b3 to e6

Input

String with a valid chess move in algebraic notation

Output

String describing the move (Upper case or Lower case is ok)


Test Cases

fxg8=Q+  -> Pawn takes g8 promotes to Queen check
Bb7#     -> Bishop to b7 mate
0-0      -> short castle
0-0+     -> short castle check
0-0-0#   -> long castle mate
Rxg8     -> Rook takes g8
fxe6     -> Pawn takes e6
Ra8g8    -> Rook a8 to g8
Nb3#     -> Knight to b3 mate
a8=B     -> Pawn to a8 promotes to Bishop
Ra8xg8   -> Rook a8 takes g8
\$\endgroup\$
4
  • 1
    \$\begingroup\$ FWIW, I've seen variants of algebraic notation that use : for capture, e.g., f:g8=Q, and often with pawn captures, if it's unambiguous, just the two files are given: fg=Q. \$\endgroup\$ Aug 23 at 17:35
  • 1
    \$\begingroup\$ Suggested test case: Ra8xg8 -> Rook a8 takes g8 (?) \$\endgroup\$
    – Arnauld
    Aug 23 at 18:31
  • \$\begingroup\$ What is the meaning of f? I don't see it in the list of keywords? Is it a pawn? \$\endgroup\$
    – pajonk
    Aug 24 at 5:42
  • \$\begingroup\$ @pajonk: F file pawn \$\endgroup\$
    – leo
    Aug 24 at 7:38

5 Answers 5

6
\$\begingroup\$

JavaScript (ES6), 206 bytes

Takes some inspiration from Neil's Retina answer. But in the end, this is quite different.

s=>[/O-O-/,/O-/,/.\d/g,/to (?=.*o)/,/x to/,..."=+#ORKBQN",/^.? /].map((r,j)=>s=s.replace(r,`long/short/ to $&// takes/ promotes to / check/ mate/ castle/Rook/King/Bishop/Queen/Knight/Pawn `.split`/`[j]))&&s

Try it online!

Decoding examples

e4
 to e4
Pawn to e4
fxg8=Q+
fx to g8=Q+
f takes g8=Q+
f takes g8 promotes to Q+
f takes g8 promotes to Q check
f takes g8 promotes to Queen check
Pawn takes g8 promotes to Queen check
Ra8g8
R to a8 to g8
R a8 to g8
Rook a8 to g8

Retina 0.8.2, 144 bytes

-4 bytes thanks to @Neil

The same logic back-ported to Retina.

O-O-
long
O-
short
.\d
 to $&
-2`to 

x to
 takes
=
 promotes to 
\+
 check
#
 mate
O
 castle
R
Rook
K
King
B
Bishop
Q
Queen
N
Knight
^.? 
Pawn 

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can use -2`to to remove all occurrences of to except the last. \$\endgroup\$
    – Neil
    Aug 24 at 9:36
5
\$\begingroup\$

Ruby -p, 249 bytes

-p flag loops over input, modifies it with these two fat regex functions, and spits it back out.

sub(/(^[a-g]?)?(.\d)?(x)?(?=.\d)|O(-O)+/){$4?%w(short long)[~/$//5]+' castle':"#{$1&&?P}#{' '+$2if$2} #{$3?:takes:'to'} "}
gsub(/[#+=B-R]/){|m|m[/\W/]?' '+%w(mate promotes\ to\  _ check)[m.ord%5]:%w(King Queen Rook Bishop Pawn).find{_1[m]}||:Knight}

Attempt This Online!

Partially ungolfed

sub(/(^[a-g]?)?(.\d)?(x)?(?=.\d)/) { # Replace the part that says which piece moves where
 "#{
    $1&&?P                           # 'P' (Pawn) if group 1 is present
                                     #  (matches if input starts with /[a-g]x/ or /.\d/)
   }#{
    ' '+$2if$2                       # Group 2 with leading space if present
                                     #  (for Qb3e6 -> Queen b3 to e6 etc.)
   } #{
    $3?:takes:'to'                   # 'takes' if group 3 (x) is present, else 'to'
                                     #  (uses label notation :takes to save bytes)
   } "
}
sub(/O(-O)+/) { # Replace castling notation (original program checks for group 4)
   %w(short long)                    # ['short', 'long']
                 [
                  ~/$/               # Index of end of input (3-4 if O-O, 5-6 if O-O-O)
                      /5]            # Divide by 5 to get correct index for short vs long
    + ' castle'                      # Add the ' castle' part
}
gsub(/[#+=B-R]/) {|m|                # Match checkmate, check, promotes, pieces
   m[/\W/]?                          # If non-letter (checkmate, check, promote)
    ' '+                             # Leading space
    ['mate','promotes to ', nil, 'check'][m.ord%5]
                                     # Match the corresponding phrase by using mod 5
                                     # '#' -> 0, '=' -> 1, '+' -> 3

  :                                  # Otherwise (if letter)
    %w(King Queen Rook Bishop Pawn).find{_1[m]}
                                     # Find the piece that has that letter
    ||:Knight                        # If none found, it's a Knight (N can't match)
}
```
\$\endgroup\$
3
\$\begingroup\$

Charcoal, 165 146 bytes

FS≡ιx≔⊞OΦυ⊖Lκtakesυ-≔⟦⎇⊖Lυlong¦short⟧υ¿№βι≔⊞O⁺∨⁻υ⟦to⟧⟦Pawn⟧…⟦to⟧¬№υtakesιυ¿Σι⊞υ⁺⊟υι⊞υ§⪪”↶.⭆~»¤πη‽q∕¶lj,⊙↥⟲Qmº5σ¡²JN¤εlθw(LθΦG▶⁵@W|]”¶⌕KQRBNO+#ι⪫υ 

Try it online! Link is to verbose version of code. Explanation:

FS≡ι

Loop over the characters of the input string.

x≔⊞OΦυ⊖Lκtakesυ

For x, push takes to the result, but remove any bare letters (which will be the source file of a pawn capture).

-≔⟦⎇⊖Lυlong¦short⟧υ

For -, change castle to short but short castle to long. (The castle comes from the O below.)

¿№βι≔⊞O⁺∨⁻υ⟦to⟧⟦Pawn⟧…⟦to⟧¬№υtakesιυ

For other lower case letters, this might be the source or the destination file. Remove any existing to and add a new to, unless there is a takes in the string. Then push the file to the result.

¿Σι⊞υ⁺⊟υι

For digits, append the rank to the file.

⊞υ§⪪”↶.⭆~»¤πη‽q∕¶lj,⊙↥⟲Qmº5σ¡²JN¤εlθw(LθΦG▶⁵@W|]”¶⌕KQRBNO+#ι

For other characters, push the piece name or notation to the result.

⪫υ 

Join the collected words on spaces.

\$\endgroup\$
2
\$\begingroup\$

Retina 0.8.2, 176 172 171 bytes

(([A-Z])(..)?|(.?))(x)?(.\d)
$2$#4$*P $3$#3$* to$5 $6
ox
akes
O-O-
long
O-
short
O
 castle
#
 mate
\+
 check
=
 promotes to 
K
King
Q
Queen
R
Rook
B
Bishop
N
Knight
P
Pawn

Try it online! Link includes test cases. Explanation:

(([A-Z])(..)?|(.?))(x)?(.\d)
$2$#4$*P $3$#3$* to$5 $6

Identify the piece or pawn being moved, whether there is a disambiguation square for the piece, and its destination.

ox
akes

Fix up the case of a capture.

O-O-
long
O-
short
O
 castle

Process castling.

#
 mate
\+
 check
=
 promotes to 

Process other symbols.

K
King
Q
Queen
R
Rook
B
Bishop
N
Knight
P
Pawn

Expand the piece names.

Edit: Saved 4 bytes thanks to @Arnauld. Saved 1 byte thanks to @ValueInk.

\$\endgroup\$
6
  • \$\begingroup\$ I think it's safe to use just ox -> akes. \$\endgroup\$
    – Arnauld
    Aug 23 at 19:30
  • \$\begingroup\$ 172 bytes \$\endgroup\$
    – Arnauld
    Aug 23 at 19:37
  • \$\begingroup\$ @Arnauld D'oh, I knew there had to be a better way to do castling... \$\endgroup\$
    – Neil
    Aug 23 at 19:44
  • \$\begingroup\$ I believe you can use (.\d) instead of (\w\w) at the end of the first match to save a byte. \$\endgroup\$
    – Value Ink
    Aug 23 at 20:58
  • 2
    \$\begingroup\$ @Arnauld That should be invalid, it has to be either dxe4 or fxe4. \$\endgroup\$
    – Neil
    Aug 23 at 21:42
0
\$\begingroup\$

C# .NET console app, 333 bytes

Assumes using System; using System.Text.RegularExpressions;

string m=Regex.Replace(args[0],"(?<!x)[a-h](?!.*[x|a-h])","t${0}"),r=m[0]>96?"Pawn ":m[0]<49?(m.Length>4?"long":"short")+" castle ":"";foreach(var c in m){int i="-0RNBQKx+#=t".IndexOf(c);r+=",,,Rook ,Knight ,Bishop ,Queen ,King ,takes ,check,mate,promotes to ,to ".Split(',')[i+1];if(c!=m[0]&i<0)r+=c+(c<57?" ":"");}Console.Write(r);

Commented

// add a 't' to the move string wherever "to" is needed
string m = Regex.Replace(args[0], "(?<!x)[a-h](?!.*[x|a-h])", "t${0}"), 

// start with 'Pawn' if the first character is a file, to or takes
    r = m[0] > 96 ? "Pawn " 

// decode castles now so we can ignore -,0 later
    : m[0]<49 ? (m.Length>4 ? "long" : "short") + " castle " : "";
    
// iterate through the move string 
foreach (var c in m) 
{
    // map each code to a word, with space where needed
    // 0,- map to empty string
    int i = "-0RNBQKx+#=t".IndexOf(c);
    r += ",,,Rook ,Knight ,Bishop ,Queen ,King ,takes ,check,mate,promotes to ,to ".Split(',')[i + 1];

    // anything that doesn't map is assumed to be a square reference and added to the result
    // except if it's the first character because if it's not x or t then it must be a file with no rank.
    // also add a space if it's a digit
    if (c != m[0] & i < 0)
        r += c + (c < 57 ? " " : "");
}

Console.Write(r);
\$\endgroup\$

Your Answer

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

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