#' Visualise a FEN
#'
#' @param fen a character vector FEN of length one (defaults to the opening position)
#' @param perspective view board from white "w" or black "b" perspective (default = "w")
#' @param palette A single character colour theme listed below or a length 2 vector of colours ordered dark, light (default = "brown")
#' \itemize{
#' \item "brown"
#' \item "blue"
#' \item "blue2"
#' \item "green"
#' \item "grey"
#' \item "pink"
#' \item "purple"
#' \item "ic"
#' \item "news"
#' }
#' @param piece_scale scaling factor for piece sizes (default = 0.825)
#' @param show_coords logical - should the board coordinates be printed? (default = TRUE)
#' @param show_fen logical - should the FEN be printed in the plot caption? (default = FALSE)
#' @param news_spacing spacing of shading lines if \code{palette = "news"} (default = 0.12)
#' @param news_thickness thickness of shading lines if \code{palette = "news"} (default = 0.5)
#' @param border_col Chess board border colour (default = NA (no border))
#' @param border_size Chess board border size (default = 0.5)
#'
#' @details Chess piece SVG design file downloaded from https://commons.wikimedia.org/wiki/File:Chess_Pieces_Sprite.svg
#'
#' jurgenwesterhof (adapted from work of Cburnett), CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0>, via Wikimedia Commons
#'
#'
#' @return A ggplot2 plot object
#' @export
#'
#' @examples
#' # Plot the starting position
#' plot_fen('Q1b2rk1/p1p2p1p/6Bp/2b5/8/2P5/P1P2PPP/R4RK1 b - - 0 16')
#'
#' # Plot an empty board
#' plot_fen("8/8/8/8/8/8/8/8 w KQkq - 0 1")
plot_fen <- function(fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
perspective = "w",
palette = "brown",
piece_scale = 0.825,
show_coords = TRUE,
show_fen = FALSE,
news_spacing = 0.1,
news_thickness = 0.5,
border_col = NA,
border_size = 0.5){
# Create dataframe of pieces from FEN -------------------------------------
# Convert FEN to a df, join to only the relevant piece polygons (inner join)
# Adjust the pieces to sit in the right squares (and apply scaling to piece size)
# Pick inverted pieces if perspective == "b"
piece_df <-
fen_to_df(fen) %>%
dplyr::inner_join(ggambit::paths, by=c("p" = "piece")) %>%
dplyr::mutate(piece_x = dplyr::case_when(perspective == "w" ~ (xn * piece_scale) + x,
perspective == "b" ~ (xni * piece_scale) + x,
TRUE ~ (xn * piece_scale) + x),
piece_y = dplyr::case_when(perspective == "w" ~ (yn * piece_scale) + y,
perspective == "b" ~ (yni * piece_scale) + y,
TRUE ~ (yn * piece_scale) + y)
)
# Get colours for board ---------------------------------------------------
# If palette is passed as a length one string, pick the colours from the lookup list
if(length(palette) == 1 && (palette %in% names(ggambit::colour_lookup))){
sq_cols <- ggambit::colour_lookup[[palette]]
} else if(length(palette) == 2){
# Else use the colours as provided
sq_cols <- palette
} else {
sq_cols <- ggambit::colour_lookup[["brown"]]
}
# Create board ------------------------------------------------------------
# Create a dataframe for the geom_tile() chessboard
squares <-
tidyr::crossing(x = 1:8, y = 1:8) %>%
dplyr::mutate(black = rep(c(rep(c(T,F), 4), rep(c(F,T), 4)), 4))
# Create the background board plot layer
if(length(palette) == 1 && palette == "news"){
# If newspaper style
line_coords <- seq(0.5, 8.5, by=news_spacing)
b <-
tibble::tibble(x_start = 0.5, y_start = line_coords,
x_end = rev(line_coords), y_end = 8.5) %>%
dplyr::bind_rows(tibble::tibble(x_start = line_coords, y_start = 0.5,
x_end = 8.5, y_end = rev(line_coords))) %>%
dplyr::mutate(rn = dplyr::row_number()) %>%
ggplot2::ggplot()+
ggplot2::geom_segment(ggplot2::aes(x=x_start, y=y_start, xend=x_end, yend=y_end, group = rn),
size=news_thickness)+
ggplot2::annotate(geom="tile",
x=squares$x[!squares$black],
y=squares$y[!squares$black],
height=1, width=1, fill="white")+
# Bottom
ggplot2::annotate("rect", xmin = 0.5, xmax = 8.5, ymin=-Inf, ymax = 0.5, col="white", fill=NA)+
# Top
ggplot2::annotate("rect", xmin = 0.5, xmax = 8.5, ymin=8.5, ymax = Inf, col="white", fill=NA)+
# Right
ggplot2::annotate("rect", xmin = 8.5, xmax = Inf, ymin=0.5, ymax = 8.5, col="white", fill=NA)+
# Left
ggplot2::annotate("rect", xmin = -Inf, xmax = 0.5, ymin=0.5, ymax = 8.5, col="white", fill=NA)+
ggplot2::coord_fixed()+
ggplot2::theme_minimal()+
ggplot2::theme(panel.grid = ggplot2::element_blank(),
panel.border = ggplot2::element_rect(fill = NA,
colour = "black",
size = 0.5))
} else {
b <-
ggplot2::ggplot()+
ggplot2::annotate(geom="tile",
x=squares$x[squares$black],
y=squares$y[squares$black],
height=1, width=1, fill=sq_cols[1])+
ggplot2::annotate(geom="tile",
x=squares$x[!squares$black],
y=squares$y[!squares$black],
height=1, width=1, fill=sq_cols[2])+
ggplot2::coord_fixed()+
ggplot2::theme_minimal()+
ggplot2::theme(panel.grid = ggplot2::element_blank(),
panel.border = ggplot2::element_rect(fill = NA,
colour = border_col,
size = border_size))
}
# Ammend coordinate system based on the perspective chosen
if(perspective == "b"){
b <-
b +
ggplot2::scale_y_reverse("", breaks=1:8, limits=c(8.5, 0.5),
expand = ggplot2::expansion(add=0))+
ggplot2::scale_x_reverse("", breaks=1:8, labels = LETTERS[1:8],
limits=c(8.5, 0.5), expand = ggplot2::expansion(add=0))
} else {
b <-
b +
ggplot2::scale_y_continuous("", breaks=1:8, limits=c(0.5, 8.5),
expand = ggplot2::expansion(add=0, mult = 0))+
ggplot2::scale_x_continuous("", breaks=1:8, labels = LETTERS[1:8],
limits=c(0.5, 8.5), expand = ggplot2::expansion(add=0))
}
# Render board and add piece layer
b <-
b +
ggplot2::geom_polygon(data=piece_df,
ggplot2::aes(x=piece_x, y=piece_y, fill=fill,
group=interaction(id, x, y),
subgroup=p))+
ggplot2::scale_fill_identity()
# Add FEN as a caption if requested ---------------------------------------
if(show_fen) b <- b + ggplot2::labs(caption = fen)
# Show coordinates on plot if chosen --------------------------------------
if(!show_coords) b <- b + ggplot2::theme(axis.text = ggplot2::element_blank())
# Return the plot
b
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.