# Convert a Forsyth-Edwards Notation string to ASCII art

In chess, Forsyth-Edwards Notation, more commonly called "FEN", is a textual way of transcribing boards. It describes each of the board's eight rows (called "ranks" in chess) from top to bottom from White's perspective. The pieces are written as K (king), Q (queen), R (rook), B (bishop), N (knight), and P (pawn). Black pieces use these letters in lowercase, and white pieces use these letters in uppercase. Empty spaces are indicated by a number from 1 to 8 indicating how many consecutive empty spaces there are. A completely empty rank would be 8, a single black rook in the rightmost column (called "files" in chess) would be 7r, and two white pawns on each end of a row would be PP4PP. Ranks are separated by a /. There is normally other information added, indicating which side is to move, castling and en passant rights, move number, and halfmove clock, but we will ignore them for the purposes of this challenge.

# Input

A FEN string, from the command line or STDIN, as you please. You may assume that this string is always valid.

# Output

Write to STDOUT a simple ASCII art representation of the board as it would actually appear:

• Pieces are represented by their character in FEN
• Empty squares are represented by spaces
• Pieces and squares are separated by a pipe | and there are pipes on each side of the board

So an empty board, written as 8/8/8/8/8/8/8/8 in FEN, would appear as

| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |


The starting position of a chess game is written as rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR, and would appear as

|r|n|b|q|k|b|n|r|
|p|p|p|p|p|p|p|p|
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
|P|P|P|P|P|P|P|P|
|R|N|B|Q|K|B|N|R|


The final position of Anderssen-Kieseritzky 1851, called "The Immortal Game" in the chess community, is written as r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1, and your program when fed that input would output:

|r| |b|k| | | |r|
|p| | |p|B|p|N|p|
|n| | | | |n| | |
| |p| |N|P| | |P|
| | | | | | |P| |
| | | |P| | | | |
|P| |P| |K| | | |
|q| | | | | |b| |

• Is it acceptable to write a function which takes input and returns output, rather than writing it to STDOUT? – Fund Monica's Lawsuit Apr 24 '16 at 19:09
• @QPaysTaxes By default we allow it and indeed several solutions do that already. Ultimately it's up to the OP though it seems unnecessary to override our defaults in this case. – Alex A. Apr 24 '16 at 21:19
• The answer you accepted is not the shortest one. Regardless of your feelings towards golfing languages, code golf means that the shortest code wins. – Dennis Apr 25 '16 at 0:21
• You also cannot penalize them or accept an arbitrary answer. The whole site is built around objective winning criteria. – Dennis Apr 25 '16 at 1:33
• +1 for an interesting challenge. -2 for accepting the wrong answer for no good reason – James Jun 20 '16 at 19:15

# Perl, 28 bytes

Includes +2 for -lp

Give input on STDIN

fen.pl <<< "r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1"


fen.pl:

#!/usr/bin/perl -lp
s/\d/$"x$&/eg;s/|/|/g;y;/;


Actually in the league of some golfing languages...

Notice that the file based version needs the final newline in the file so that one is really 29 bytes. But the commandline version doesn't need that extra newline and therefore the code counts as 28 bytes:

perl -lpe 's/\d/$"x$&/eg;s/|/|/g;y;/;' <<< "r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1"

• Missing shebang? – user253751 Apr 25 '16 at 2:27

# Retina, 13 bytes

\d
$* / ¶ |  Try it online! ## Explanation The first part (note the trailing space): \d$*


is to convert a to the specific number of spaces. Retina has a $* feature to repeat. The way it works is: <num>$*<char> if there is no <num>, Retina will assume $& or the matched string, in this case the matched number. The next part: / ¶  is pretty simple, it replaces all / with ¶ which is a newline. The last part works the same:  | This will replace everything (hence why there is nothing on the first line) with |. Putting a | everywhere. • You can even do it all in ASCII for the same byte count with S/ as the second stage. – Martin Ender Apr 23 '16 at 22:53 ## Ruby - 758278767562595857 56 bytes ->n{"|#{n.gsub(/\d| /){' '*$&.hex}.chars*?|}|".tr'/',$/}  Saved a few bytes thanks to Ventero Let me explain (with \n replacing the literal newline): ->n{"...".tr'/',$/}


This implicitly returns the value of the string, with each / replaced with a newline (by default, $/ contains a newline) "|#{...}|"  This is super simple; it's just a string containing a pipe, string interpolation, and another pipe. The string interpolation is evaluated n.gsub(/\d|\n/){' '*$&.hex}...


This replaces every number with that many spaces. I can save a few bytes by also finding newlines here; because hex returns 0 if the string isn't a valid number, when it finds a newline –- i.e. the one at the end of the result of gets –- it replaces it with a 0-length string, effectively deleting it. Without this, there would be a trailing pipe.

$& is a magic variable which represents the complete text of the latest variable match, which lets me save a byte by eliminating |d|. I can save another byte by using .hex instead of .to_i, which works because every number is less than 9, which means that hex and decimal have the same values. .chars*?|  This puts a pipe between every character. Note that this is what puts the pipes on either side of the lines (except the first and last) because the slashes, which eventually turn into newlines through tr, count as characters, and are therefore surrounded by pipes. The ?| just means "the one-character string "|"". And... that's it. It's a frankly scandalously simple program. It just uses a lot of sneaky syntax tricks. • You can save 4 more characters by applying a few simple tricks: puts"|#{gets.gsub(/\d|\n/){' '*$&.hex}.chars*?|}|".split'/' (of course, replace the \n with a literal newline again). – Ventero Apr 24 '16 at 8:30

# Pyth - 2422 21 bytes

.i*\|72jcu:GH*Hd9z\/

+                     Concatenate
K\|                  Store "|" in K and use value
+         K           Concatenate to end
jK                   Join string by K, this puts "|" between each char
:                   String substitution
\/            Replace "/"
b            With newline
u                  Reduce
9             Over [0, 9)
z            With input as base case
:G                String substitution current val
H               Replace stringifyed int from list we're looping through
*Hd              With " "*that int


# Pyth, 23 bytes

VT=:QN*dN;jc.i*\|72Q\/


Try it online!

How it works:

VT=:QN*dN;jc.i*\|72Q\/
VT        ;                for N in range(10):
=:QN*dN                     Q = Q.replace(N,repeat(' ',N))
.i*\|72Q      temp = interweave(repeat('|',72), Q)
c        \/    temp = chop(temp,'/')
j               temp = join(temp,'\n')
print temp


# JavaScript ES7, 80 70 bytes

Is an anonymous function that accepts a string as input.

a=>[,...[+t?" ".repeat(t):t<"0"?
:t for(t of a)].join,
].join|


80-byte ES6-only appraoch.

a=>a.split/.map(x=>[,...x.replace(/\d/g,t=>" ".repeat(t)),
].join|).join


## Explanation

We use an array comprehension to loop over the list:

[+t?" ".repeat(t):t<"0"?
:t for(t of a)]


This is equivalent to:

[!isNaN(parseInt(t, 10)) ? " ".repeat(parseInt(t, 10)) : t === "/" ? "\n" : t for(t of a)]


If it's a number, we have that number of spaces. If it's a /, we have a newline. Otherwise, we have the character. Then, we join the comprehension with nothing to make a string.

Then, we create an array of length 3 [,...that,"\n"]. ... splats the joined comprehension into chars. Joining this yields the result.

• Don't you mean ES6? ES7 isn't out yet I believe. – ericw31415 Apr 23 '16 at 22:40
• @ericw31415 It isn't out, you're correct, but some browsers have started implementing part of the ES7 spec. – Conor O'Brien Apr 23 '16 at 22:40
• Oh OK. But still your code doesn't use any of the ES7 features, right? – ericw31415 Apr 23 '16 at 22:42
• @ericw31415 Actually, it does. Array comprehensions ([x for(x of a)]) are ES7. – Conor O'Brien Apr 23 '16 at 22:43
• Weren't Array Comprehensions removed from the spec, MDN says they were – MayorMonty Apr 25 '16 at 3:01

# Julia, 62 bytes

s->split("|"join(replace(s,r"\d",d->" "^parse(d)),"|")"|","/")


This is an anonymous function that accepts a string and returns an array of strings. To call it, assign it to a variable.

The approach is the same as in QPaysTaxes' clever Ruby answer. We replace each digit in the input with that many spaces, place | between each character, tack | onto the front and back, and split into an array on /.

Try it online!

• Yay I inspired things :D – Fund Monica's Lawsuit May 8 '16 at 23:12
• @QPaysTaxes You did indeed. Nice solution! – Alex A. May 8 '16 at 23:26

# 05AB1E, 21 bytes

Code:

9GNNð×:}S'|ý"|ÿ|"'/¶:


Also 21 bytes: '|¹9GNNð×:}S'|«JJ'/¶:.

Uses CP-1252 encoding. Try it online!.

## JavaScript (ES6), 6967 62 bytes

s=>[,...s.replace(/[/-8]/g,c=>+c?' '.repeat(c):
),,].join|


The extra commas create empty values in the outer split which creates the beginning and ending | characters. You need two trailing commas because trailing commas are optional at the end of lists, so the first one is still part of the previous item.

Edit: Saved 5 bytes thanks to @user81655.

• Would /[\d/]/g,c=>+c? .repeat(c):\n work? – user81655 Apr 24 '16 at 4:36
• @user81655 Thanks, but I didn't like your emoticon, so I replaced it with an annoyed face with glasses. – Neil Apr 24 '16 at 8:50

# Retina, 50 45 bytes

This was fun haha. Not only am I a noob in Retina, but also in regex in general... This can probably be golfed a lot, so I'll do some more research.

Code:

8
44
7
34
6
42
5
4
4
22
3
2
2

1

/
¶

|


Try it online!

• Try using the \$* functionality :) – Leaky Nun Apr 23 '16 at 22:45

# Jolf, 28 bytes

RΜGi'/dΆγ'|RGρH«\d»d*♣PHEγγS


Replace ♣ with the character \x05, or try it here!

# Python 3.5, 112 bytes:

def r(o):print(''.join(['| '*8if h=='8'else'| '*int(h)if h.isdigit()else'|\n'if h=='/'else'|'+h for h in o])+'|')


Try it online! (Ideone)

# C, 252 bytes

i=-1,j,s=1,x;C(char*n){while(n[++i])s+=isdigit(n[i])?n[i]*2+1:2;char*m=(char*)malloc(s);for(i=j=-1;n[++i]&(m[++j]='|');)if(n[i]=='/')m[++j]='\n';else if(isdigit(n[i]))for(x=n[i]-'0';x;--x&&(m[++j]='|'))m[++j]=' ';else m[++j]=n[i];m[++j]='\0';return m;}


Detailed try online

// input-string, input-string-size
char* C(char*n)
{
int i=-1,j,s=1,x;

// figure out required grid size
while(n[++i])s+=isdigit(n[i])?n[i]*2+1:2;
char*m=(char*)malloc(s);

i=j=-1;
while(n[++i]) // while not end of string
{
m[++j]='|'; // seperator

if (n[i]=='/') // end of row
m[++j]='\n';
else if (isdigit(n[i])) // fill spaces
for(x=n[i]-'0';x;--x&&(m[++j]='|')) m[++j]=' ';
else
m[++j]=n[i]; // single literals
}

m[++j]='|';
m[++j]='\0';
return m;
}


# JavaScript (FireFox 30+), 61

Using array comprehension that is not standard EcmaScript anymore

f=>'|'+[for(c of f)+c?' |'.repeat(c):c<'A'?
|:c+'|'].join


Test

F=f=>'|'+[for(c of f)+c?' |'.repeat(c):c<'A'?\n|:c+'|'].join

console.log=x=>O.textContent+=x+'\n'

;['8/8/8/8/8/8/8/8','rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
'r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1']
.forEach(t=>console.log(F(t)+'\n'))
<pre id=O></pre>

## Lua, 106 Bytes

print("|"..(...):gsub(".",function(c)return c:find("%d")and(" |"):rep(c)or c=="/"and"\n|"or c.."|"end),'')


### Ungolfed

print("|"..                   -- prepend | to the following string
(...):gsub(".",function(c)  -- iterate over each character in the argument
return                    -- replaces in the argument
c:find("%d")       -- if c is a number
and(" |"):rep(c) --   replace by " |"*c
or c=="/"          -- elseif c is a slash
and"\n|"         -- replace by "\n|"
or c.."|"          -- else (case letter)replace by c
end)                        -- return the modified string
,'')                          -- add an empty parameter to print
-- it suppresses the second output of gsub

• print((...):gsub(".",function(c)return(c:find("%d")and("| "):rep(c)or c=="/"and"|\n"or"|"..c)end).."|") – Leaky Nun Jul 18 '16 at 9:56
• print((...):gsub("%d",function(c)return("| "):rep(c)end):gsub("/","|\n"):gsub("([^%d%s|])","|%1").."|") for the same byte-count. – Leaky Nun Jul 18 '16 at 9:58
• print((...):gsub("%d",function(c)return("| "):rep(c)end):gsub("([^%d |])","|%1"):gsub("/","\n").."|") is 101 bytes – Leaky Nun Jul 18 '16 at 10:05

## R (out of competition)

Sorry if it's not appropriate to post this, but I thought it was cool that I just happened to have a function lying around that actually works for this question without editing! It prints unicode output rather than ascii, though. I can't remember quite why I wrote it, but it wasn't to answer a challenge.

function(x){
# x = FEN position, a string
# can be split with / or ,
# example: forsythe("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R")

allowed <- c(paste(1:64),
c("k", "q", "r", "b", "n", "p", "K", "Q", "R", "B", "N", "P"))
chars <- strsplit(x, "")[[1]]
chars <- chars[-which(!(chars %in% allowed))]
out <- c()
for (i in 1:length(chars)){
if (chars[i] %in% paste(1:64)){
out <- c(out, rep(" ", as.numeric(chars[i])))
}
else{
out <- c(out, chars[i])
}
}
if (length(out) < 64) out <- c(out, rep(" ", 64-length(out)))

pieces <- strsplit("KQRBNPkqrbnp", "")[[1]]
unicode <- c("\u2654", "\u2655", "\u2656",
"\u2657", "\u2658", "\u2659", "\u265A", "\u265B",
"\u265C", "\u265D", "\u265E", "\u265F")

for (i in 1:64){
if (out[i] %in% pieces){
out[i] <- unicode[which(pieces==out[i])]
}
else{
}
}
out <- matrix(out, nc=8, byrow=T)
#print(out)

plot(0, xlim=c(0, 8), ylim=c(0, 8), type="n", xaxt="n", yaxt="n",
xlab="", ylab="")
for (i in 0:7){ for (j in 0:7){ rect(i, j, i+1, j+1,
col=ifelse(((i+j) %% 2) == 0, grey(0.95), "white"), border=F) }}

for (i in 0:7){ for (j in 0:7){
text(i+0.5, j+0.5, out[8-j, i+1], cex=2)
}}

axis(1, labels=letters[1:8], at=1:8 - 0.5, tick=F)
axis(2, labels=paste(1:8), at=1:8-0.5, las=2, tick=F)

}

• The rules outlined in our help center state that all solutions to challenges must be a serious contender for the winning criteria in use. For code golf, this means that all answers must be golfed. – Dennis Apr 26 '16 at 23:49
• Technically, it is golfed. Just not very well. – Flounderer Apr 27 '16 at 1:32

p '/'="\n"
p c=[c]
main=getLine>>=putStrLn.('|':).(>>=(:"|")).(>>=p)


## Ungolfed:

p c | c=='/'           = "\n"
| '1'<=c && c<='8' = replicate (read [c]) ' '
| otherwise        = [c]
addPipes string = "|" ++ concatMap (\c -> [c] ++ "|") string
main = getLine >>= putStrLn . addPipes . concatMap p


# Java 7, 190 184 bytes

String Z(int c){String m="";if(c==47)m+="|\n";else if(c>57)m+="|"+c;else while(c-->48)m+="| ";return m;}String C(String n){String m="";for(char x:n.toCharArray())m+=Z(x);return m+"|";}


Detailed try online

public static String Z(char c)
{
String m="";
if(c=='/')m+="|\n";
else if(c>'9')m+="|"+c;
else while(c-->'0')m+="| ";
return m;
}

public static String C(String n)
{
String m="";
for(char x:n.toCharArray())m+=Z(x);
return m+"|";
}

• You can save a couple of bytes be using integers instead of char literals in the comparisons – Blue Apr 24 '16 at 11:42
• @Blue notes taken – Khaled.K Apr 24 '16 at 12:18

## Pyke, 25 20 bytes

FD~u{RIbd*(s\/n:k\|:


Explanation:

F         (          -    for char in input:
D~u{RI              -     if char in '0123456789':
bd*           -      char = " "*int(char)
s         -   sum(^)
\/n:     -  ^.replace("/","\n")
k\|: - ^.replace("", "|")


Try it here!

## Python, 84 bytes

lambda a:"".join(c*c.isalpha()or"\n"*(c=="/")or" "*int(c)for c in a).replace("","|")


Explanation:

        c*c.isalpha()                                                       - if c is alphabetical, use c
"\n"*(c=="/")                                        - if it's "|", replace it with a newline
" "*int(c)                            - else its an int.
"".join(                                                  ).replace("","|") - interweave "|" between the chars


# ><>, 64 bytes

<v?(0:i
r\
"<o-*=@"%/":   v?*(@)@":/"::;?(0:o"|
^~?="0":-1o" "<


4 wasted bytes due to alignment issues, not sure how to golf them out though. ¯\_(ツ)_/¯