diff --git a/DESCRIPTION b/DESCRIPTION index 9af2aa1..db1d2e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Encoding: UTF-8 Package: ppcli Type: Package Title: Plaintext Board Game Visualizations -Version: 0.2.0-4 +Version: 0.2.0-5 Authors@R: c(person("Trevor L.", "Davis", role=c("aut", "cre"), email="trevor.l.davis@gmail.com", comment = c(ORCID = "0000-0001-6341-4639"))) @@ -21,7 +21,7 @@ Imports: Suggests: dplyr, fansi, - ppdf, + ppdf (>= 0.2.0-13), testthat, tibble, withr diff --git a/NEWS.md b/NEWS.md index bfd570c..71190d4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,8 @@ New features - However we currently do not distinguish between the nine marble bit ranks. + + "morris" bit and board pieces. + * `cat_piece()` and `str_piece()` gain arguments `xbreaks` and `ybreaks` to provide a subset (of integers) to provide axis labels for if `annotate` is `TRUE` (#17). diff --git a/R/range.r b/R/range.r index f51267d..b91d71d 100644 --- a/R/range.r +++ b/R/range.r @@ -4,44 +4,51 @@ range_heuristic <- function(df) { # piecepack is_tile <- grepl("tile", df$piece_side) - xleft <- ifelse(is_tile, df$x-1, df$x-0.5) + xleft <- ifelse(is_tile, df$x-1, df$x-0.5) xright <- ifelse(is_tile, df$x+1, df$x+0.5) - ybot <- ifelse(is_tile, df$y-1, df$y-0.5) - ytop <- ifelse(is_tile, df$y+1, df$y+0.5) + ybot <- ifelse(is_tile, df$y-1, df$y-0.5) + ytop <- ifelse(is_tile, df$y+1, df$y+0.5) # subpack is_subpack <- is_tile & df$cfg == "subpack" - xleft <- ifelse(is_subpack, df$x-0.5, xleft) + xleft <- ifelse(is_subpack, df$x-0.5, xleft) xright <- ifelse(is_subpack, df$x+0.5, xright) - ybot <- ifelse(is_subpack, df$y-0.5, ybot) - ytop <- ifelse(is_subpack, df$y+0.5, ytop) + ybot <- ifelse(is_subpack, df$y-0.5, ybot) + ytop <- ifelse(is_subpack, df$y+0.5, ytop) # dominoes is_dominoes_horizontal <- is_tile & grepl("dominoes", df$cfg) & (df$angle == 90 | df$angle == 270) - ybot <- ifelse(is_dominoes_horizontal, df$y-0.5, ybot) - ytop <- ifelse(is_dominoes_horizontal, df$y+0.5, ytop) + ybot <- ifelse(is_dominoes_horizontal, df$y-0.5, ybot) + ytop <- ifelse(is_dominoes_horizontal, df$y+0.5, ytop) is_dominoes_vertical <- is_tile & grepl("dominoes", df$cfg) & (df$angle == 0 | df$angle == 180) - xleft <- ifelse(is_dominoes_vertical, df$x-0.5, xleft) + xleft <- ifelse(is_dominoes_vertical, df$x-0.5, xleft) xright <- ifelse(is_dominoes_vertical, df$x+0.5, xright) # boards is_board <- grepl("board", df$piece_side) - xleft <- ifelse(is_board, df$x-0.5*df$rank, xleft) + xleft <- ifelse(is_board, df$x-0.5*df$rank, xleft) xright <- ifelse(is_board, df$x+0.5*df$rank, xright) - ybot <- ifelse(is_board, df$y-0.5*df$rank, ybot) - ytop <- ifelse(is_board, df$y+0.5*df$rank, ytop) + ybot <- ifelse(is_board, df$y-0.5*df$rank, ybot) + ytop <- ifelse(is_board, df$y+0.5*df$rank, ytop) is_board2 <- is_board & grepl("2", df$cfg) - xleft <- ifelse(is_board2, df$x-df$rank, xleft) + xleft <- ifelse(is_board2, df$x-df$rank, xleft) xright <- ifelse(is_board2, df$x+df$rank, xright) - ybot <- ifelse(is_board2, df$y-df$rank, ybot) - ytop <- ifelse(is_board2, df$y+df$rank, ytop) + ybot <- ifelse(is_board2, df$y-df$rank, ybot) + ytop <- ifelse(is_board2, df$y+df$rank, ytop) + + morris_offset <- c(3, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3)[df$rank] + is_morris_board <- is_board & df$cfg == "morris" + xleft <- ifelse(is_morris_board, df$x-morris_offset, xleft) + xright <- ifelse(is_morris_board, df$x+morris_offset, xright) + ybot <- ifelse(is_morris_board, df$y-morris_offset, ybot) + ytop <- ifelse(is_morris_board, df$y+morris_offset, ytop) # matchsticks m_offset <- pmax(floor(df$rank / 2) - 1, 0) / 2 # 1:6 -> 0, 0, 0, 0.5, 0.5, 1 is_matchsticks_horizontal <- grepl("matchstick", df$piece_side) & (df$angle == 90 | df$angle == 270) - xleft <- ifelse(is_matchsticks_horizontal, df$x - m_offset, xleft) + xleft <- ifelse(is_matchsticks_horizontal, df$x - m_offset, xleft) xright <- ifelse(is_matchsticks_horizontal, df$x + m_offset, xright) is_matchsticks_vertical <- grepl("matchstick", df$piece_side) & @@ -52,10 +59,10 @@ range_heuristic <- function(df) { m_offset_d <- floor(df$rank / 4) # 1:6 -> 0, 0, 0, 1, 1, 1 is_matchsticks_diagonal <- grepl("matchstick", df$piece_side) & !is_matchsticks_horizontal & !is_matchsticks_vertical - xleft <- ifelse(is_matchsticks_diagonal, df$x - m_offset_d, xleft) + xleft <- ifelse(is_matchsticks_diagonal, df$x - m_offset_d, xleft) xright <- ifelse(is_matchsticks_diagonal, df$x + m_offset_d, xright) - ybot <- ifelse(is_matchsticks_diagonal, df$y - m_offset_d, ybot) - ytop <- ifelse(is_matchsticks_diagonal, df$y + m_offset_d, ytop) + ybot <- ifelse(is_matchsticks_diagonal, df$y - m_offset_d, ybot) + ytop <- ifelse(is_matchsticks_diagonal, df$y + m_offset_d, ytop) list(xmin = min(xleft), xmax = max(xright), ymin = min(ybot), ymax = max(ytop)) } diff --git a/R/str_piece.r b/R/str_piece.r index 5576b01..2399a56 100644 --- a/R/str_piece.r +++ b/R/str_piece.r @@ -196,7 +196,8 @@ get_style_rs <- function(style, big = FALSE) { icehouse_pieces = rep(" ", 6L), alquerque = rep_len("\u25cf", 6L), go = rep_len("\u25cf", 6L), - marbles = rep_len("\u25cf", 9L)) + marbles = rep_len("\u25cf", 9L), + morris = rep_len("\u25cf", 9L)) rs } @@ -244,6 +245,7 @@ get_style_ss <- function(style, big = FALSE) { alquerque = c(rep_len("\u25cf", 5L), "\u25cb"), go = c(rep_len("\u25cf", 5L), "\u25cb"), marbles = c(rep_len("\u25cf", 5L), "\u25cb"), + morris = c(rep_len("\u25cf", 5L), "\u25cb"), icehouse_pieces = c(rep_len("\u25b2", 5L), "\u25b3")) ss } @@ -276,7 +278,8 @@ get_style_fg <- function(style) { icehouse_pieces = dice_colors, alquerque = suit_colors, go = suit_colors, - marbles = suit_colors) + marbles = suit_colors, + morris = suit_colors) fg } @@ -368,9 +371,13 @@ clean_df <- function(df) { df$rank <- ifelse(str_detect(df$piece_side, "^board") & df$cfg == "alquerque", 4L, df$rank) + # morris rank is number of men + df$rank <- ifelse(str_detect(df$piece_side, "^board") & df$cfg == "morris", + ifelse(df$rank == 1L, 9L, df$rank), + df$rank) # Go stones and marbles should be "bit_back" - bit_back_cfgs <- c("alquerque", "go", "marbles") + bit_back_cfgs <- c("alquerque", "go", "marbles", "morris") df$piece_side <- ifelse(df$piece_side == "bit_face" & df$cfg %in% bit_back_cfgs, "bit_back", df$piece_side) @@ -436,6 +443,14 @@ add_piece <- function(cm, piece_side, suit, rank, x, y, angle, cfg, reorient = " } else { cell <- 1 } + if (cfg == "morris") { + morris_widths <- c(6, 2, 2, 2, 4, 4, 4, 6, 6, 6, 6, 6) + board_width <- morris_widths[rank] + board_height <- morris_widths[rank] + } else { + board_width <- cell * rank + board_height <- cell * rank + } switch(piece_side, coin_back = add_coin_back(cm, ss, x, y, angle, fg, style), coin_face = add_coin_face(cm, rs, x, y, angle, fg, style), @@ -446,8 +461,8 @@ add_piece <- function(cm, piece_side, suit, rank, x, y, angle, cfg, reorient = " tile_back = add_tile_back(cm, x, y, angle, cfg, style), bit_back = add_bit_back(cm, ss, x, y, fg), bit_face = add_bit_face(cm, rs, x, y, fg), - board_back = add_board(cm, x, y, cell * rank, cell * rank, cell, cfg, style), - board_face = add_board(cm, x, y, cell * rank, cell * rank, cell, cfg, style), + board_back = add_board(cm, x, y, board_width, board_height, cell, cfg, style, rank), + board_face = add_board(cm, x, y, board_height, board_height, cell, cfg, style, rank), matchstick_back = add_matchstick_face(cm, x, y, angle, fg, rank), matchstick_face = add_matchstick_face(cm, x, y, angle, fg, rank), pyramid_top = add_pyramid_top(cm, ss, x, y, angle, fg, rank), @@ -742,30 +757,99 @@ add_tile_face_piecepack <- function(cm, ss, rs, x, y, angle, fg, style) { } add_board <- function(cm, x, y, width = 8, height = 8, cell = 1, - cfg = "checkers1", style = get_style("Unicode")) { + cfg = "checkers1", + style = get_style("Unicode"), + rank = 8L) { cm$fg[y+-height:height, x+-width:width] <- "black" - cm <- add_border(cm, x, y, width, height, space = style$space) + if (cfg != "morris") + cm <- add_border(cm, x, y, width, height, space = style$space) cm <- switch(cfg, alquerque = add_alquerque_board(cm, x, y, width, height, cell), marbles = add_holes(cm, x, y, width, height, cell), + morris = add_morris_board(cm, x, y, width, height, cell, style, rank), add_gridlines(cm, x, y, width, height, cell) ) cm } -add_alquerque_board <- function(cm, x, y, width = 2, height = 2, cell = 1) { +add_morris_board <- function(cm, x, y, width = 2, height = 2, cell = 1, + style = get_style("Unicode"), rank = 9L) { + hv <- 1L # light + if (rank == 2L) { # three men's morris without diagonals + cm <- add_border(cm, x, y, width, height, space = style$space) + cm <- add_gridlines(cm, x, y, width, height, cell = 1, heavy = FALSE) + } else if (rank < 5L) { # three men's morris + cm <- add_border(cm, x, y, width, height, space = style$space) + cm <- add_alquerque_board(cm, x, y, width, height, cell) + } else if (rank < 7L) { # six men's morris + cm <- add_border(cm, x, y, width, height, space = style$space) + cm <- add_border(cm, x, y, 2L, 2L, space = style$space) + cm$char[y, x + c(-3, 3)] <- "\u2500" # light horizontal line + cm$char[y + c(-3, 3), x] <- "\u2502" # light vertical line + # intersection gridlines and border line + cm <- add_box_edge(cm, x-width, y, c(1L, hv, 1L, NA)) # left + cm <- add_box_edge(cm, x+2, y, c(1L, hv, 1L, NA)) # left + cm <- add_box_edge(cm, x+width, y, c(1L, NA, 1L, hv)) # right + cm <- add_box_edge(cm, x-2, y, c(1L, NA, 1L, hv)) # right + cm <- add_box_edge(cm, x, y+height, c(NA, 1L, hv, 1L)) # top + cm <- add_box_edge(cm, x, y-2, c(NA, 1L, hv, 1L)) # top + cm <- add_box_edge(cm, x, y-height, c(hv, 1L, NA, 1L)) # bottom + cm <- add_box_edge(cm, x, y+2, c(hv, 1L, NA, 1L)) # bottom + } else if (rank == 7L) { # seven men's morris + cm <- add_border(cm, x, y, width, height, space = style$space) + cm <- add_border(cm, x, y, 2L, 2L, space = style$space) + cm <- add_gridlines(cm, x, y, width, height, cell = 2, heavy = FALSE) + cm$char[y, x + c(-2L, 2L)] <- "\u253c" # light crosses + cm$char[y + c(-2L, 2L), x] <- "\u253c" # light crosses + } else { # 9 men's morris + cm <- add_border(cm, x, y, width, height, space = style$space) + cm <- add_border(cm, x, y, 4L, 4L, space = style$space) + cm <- add_border(cm, x, y, 2L, 2L, space = style$space) + cm$char[y, x + c(-5, -3, 3, 5)] <- "\u2500" # light horizontal line + cm$char[y + c(-5, -3, 3, 5), x] <- "\u2502" # light vertical line + cm$char[y, x + c(-4L, 4L)] <- "\u253c" # light crosses + cm$char[y + c(-4L, 4L), x] <- "\u253c" # light crosses + cm <- add_box_edge(cm, x-width, y, c(1L, hv, 1L, NA)) # left + cm <- add_box_edge(cm, x+2, y, c(1L, hv, 1L, NA)) # left + cm <- add_box_edge(cm, x+width, y, c(1L, NA, 1L, hv)) # right + cm <- add_box_edge(cm, x-2, y, c(1L, NA, 1L, hv)) # right + cm <- add_box_edge(cm, x, y+height, c(NA, 1L, hv, 1L)) # top + cm <- add_box_edge(cm, x, y-2, c(NA, 1L, hv, 1L)) # top + cm <- add_box_edge(cm, x, y-height, c(hv, 1L, NA, 1L)) # bottom + cm <- add_box_edge(cm, x, y+2, c(hv, 1L, NA, 1L)) # bottom + if (rank > 10L) { # 12 men's morris + cm$char[x - 5, y - 5] <- "\u2571" # up to right diagonal + cm$char[x - 3, y - 3] <- "\u2571" # up to right diagonal + cm$char[x + 3, y + 3] <- "\u2571" # up to right diagonal + cm$char[x + 5, y + 5] <- "\u2571" # up to right diagonal + cm$char[x + 5, y - 5] <- "\u2572" # up to left diagonal + cm$char[x + 3, y - 3] <- "\u2572" # up to left diagonal + cm$char[x - 3, y + 3] <- "\u2572" # up to left diagonal + cm$char[x - 5, y + 5] <- "\u2572" # up to left diagonal + + } + } + cm +} + +add_alquerque_board <- function(cm, x, y, width = 4, height = 4, cell = 1) { + stopifnot(width %% 2 == 0, height %% 2 == 0) cm <- add_gridlines(cm, x, y, width, height, cell, heavy = FALSE) - xur <- x + rep(c(-3, 1), 2L) - yur <- y + rep(c(-3, 1), each = 2L) + xl <- x - width + xr <- x + width + yb <- y - height + yt <- y + height + xur <- rep(seq.int(xl + 1L, xr - 1L, by = 4L), height / 2) + yur <- rep(seq.int(yb + 1L, yt - 1L, by = 4L), each = 2L) cm$char[xur, yur] <- "\u2571" - xur <- x + rep(c(-1, 3), 2L) - yur <- y + rep(c(-1, 3), each = 2L) + xur <- rep(seq.int(xl + 3L, xr - 1L, by = 4L), height / 2) + yur <- rep(seq.int(yb + 3L, yt - 1L, by = 4L), each = 2L) cm$char[xur, yur] <- "\u2571" - xul <- x + rep(c(-3, 1), 2L) - yul <- y + rep(c(-1, 3), each = 2L) + xul <- rep(seq.int(xl + 1L, xr - 1L, by = 4L), height / 2) + yul <- rep(seq.int(yb + 3L, yt - 1L, by = 4L), each = 2L) cm$char[xul, yul] <- "\u2572" - xul <- x + rep(c(-1, 3), 2L) - yul <- y + rep(c(-3, 1), each = 2L) + xul <- rep(seq.int(xl + 3L, xr - 1L, by = 4L), height / 2) + yul <- rep(seq.int(yb + 1L, yt - 1L, by = 4L), each = 2L) cm$char[xul, yul] <- "\u2572" cm } diff --git a/tests/testthat/_snaps/cat_piece.md b/tests/testthat/_snaps/cat_piece.md index 33ce132..5ed0175 100644 --- a/tests/testthat/_snaps/cat_piece.md +++ b/tests/testthat/_snaps/cat_piece.md @@ -609,3 +609,117 @@ +--- + + Code + df2 <- tibble(piece_side = "board_face", x = 2, y = 2, rank = 2L, suit = 3L, + cfg = "morris") + cat_piece(df2) + Output + ┌─┬─┐ + │ │ │ + ├─┼─┤ + │ │ │ + └─┴─┘ + + + Code + df3 <- tibble(piece_side = "board_face", x = 2, y = 2, rank = 3L, suit = 3L, + cfg = "morris") + dfs <- tibble(piece_side = "bit_back", x = rep(1:3, 2), y = rep(1:2, each = 3), + rank = 1L, suit = 1:6, cfg = "morris") + df <- rbind(df3, dfs) + cat_piece(df3) + Output + ┌─┬─┐ + │╲│╱│ + ├─┼─┤ + │╱│╲│ + └─┴─┘ + + + Code + cat_piece(df) + Output + ┌─┬─┐ + │╲│╱│ + ●─●─○ + │╱│╲│ + ●─●─● + + + Code + df6 <- tibble(piece_side = "board_face", x = 3, y = 3, rank = 6L, suit = 3L, + cfg = "morris") + cat_piece(df6) + Output + ┌───┬───┐ + │ │ │ + │ ┌─┴─┐ │ + │ │ │ │ + ├─┤ ├─┤ + │ │ │ │ + │ └─┬─┘ │ + │ │ │ + └───┴───┘ + + + Code + df7 <- tibble(piece_side = "board_face", x = 3, y = 3, rank = 7L, suit = 3L, + cfg = "morris") + cat_piece(df7) + Output + + + ┌───┬───┐ + │ │ │ + │ ┌─┼─┐ │ + │ │ │ │ │ + ├─┼─┼─┼─┤ + │ │ │ │ │ + │ └─┼─┘ │ + │ │ │ + └───┴───┘ + + + Code + df9 <- tibble(piece_side = "board_face", x = 4, y = 4, rank = 9L, suit = 3L, + cfg = "morris") + cat_piece(df9) + Output + ┌─────┬─────┐ + │ │ │ + │ ┌───┼───┐ │ + │ │ │ │ │ + │ │ ┌─┴─┐ │ │ + │ │ │ │ │ │ + ├─┼─┤ ├─┼─┤ + │ │ │ │ │ │ + │ │ └─┬─┘ │ │ + │ │ │ │ │ + │ └───┼───┘ │ + │ │ │ + └─────┴─────┘ + + + Code + df12 <- tibble(piece_side = "board_face", x = 4, y = 4, rank = 12L, suit = 3L, + cfg = "morris") + cat_piece(df12) + Output + ┌─────┬─────┐ + │╲ │ ╱│ + │ ┌───┼───┐ │ + │ │╲ │ ╱│ │ + │ │ ┌─┴─┐ │ │ + │ │ │ │ │ │ + ├─┼─┤ ├─┼─┤ + │ │ │ │ │ │ + │ │ └─┬─┘ │ │ + │ │╱ │ ╲│ │ + │ └───┼───┘ │ + │╱ │ ╲│ + └─────┴─────┘ + + + diff --git a/tests/testthat/_snaps/game_bit_mono.md b/tests/testthat/_snaps/game_bit_mono.md index d476e04..73f89b2 100644 --- a/tests/testthat/_snaps/game_bit_mono.md +++ b/tests/testthat/_snaps/game_bit_mono.md @@ -22,7 +22,7 @@ # Can't rotate boards Code - cat_piece(ppdf::checkers_italian_checkers(), annotate = "cartesian") + cat_piece(ppdf::checker_italian_checkers(), annotate = "cartesian") Output ┌─┰─┰─┰─┰─┰─┰─┰─┐ 8│⛂┃ ┃⛂┃ ┃⛂┃ ┃⛂┃ │ diff --git a/tests/testthat/test_cat_piece.r b/tests/testthat/test_cat_piece.r index d3f266c..958bc66 100644 --- a/tests/testthat/test_cat_piece.r +++ b/tests/testthat/test_cat_piece.r @@ -195,4 +195,35 @@ test_that("text diagrams", { cat_piece(dfb) cat_piece(df) }) + + # morris + expect_snapshot({ + df2 <- tibble(piece_side = "board_face", x = 2, y = 2, + rank = 2L, suit = 3L, cfg = "morris") + cat_piece(df2) + + df3 <- tibble(piece_side = "board_face", x = 2, y = 2, + rank = 3L, suit = 3L, cfg = "morris") + dfs <- tibble(piece_side = "bit_back", x = rep(1:3, 2), y = rep(1:2, each = 3), + rank = 1L, suit = 1:6, cfg = "morris") + df <- rbind(df3, dfs) + cat_piece(df3) + cat_piece(df) + + df6 <- tibble(piece_side = "board_face", x = 3, y = 3, + rank = 6L, suit = 3L, cfg = "morris") + cat_piece(df6) + + df7 <- tibble(piece_side = "board_face", x = 3, y = 3, + rank = 7L, suit = 3L, cfg = "morris") + cat_piece(df7) + + df9 <- tibble(piece_side = "board_face", x = 4, y = 4, + rank = 9L, suit = 3L, cfg = "morris") + cat_piece(df9) + + df12 <- tibble(piece_side = "board_face", x = 4, y = 4, + rank = 12L, suit = 3L, cfg = "morris") + cat_piece(df12) + }) }) diff --git a/tests/testthat/test_game_bit_mono.r b/tests/testthat/test_game_bit_mono.r index 5830e02..ef96b91 100644 --- a/tests/testthat/test_game_bit_mono.r +++ b/tests/testthat/test_game_bit_mono.r @@ -19,6 +19,6 @@ test_that("Dominoes", { # https://github.com/piecepackr/ppcli/issues/3 test_that("Can't rotate boards", { - skip_if_not_installed("ppdf") - expect_snapshot(cat_piece(ppdf::checkers_italian_checkers(), annotate = "cartesian")) + skip_if_not_installed("ppdf", "0.2.0-13") + expect_snapshot(cat_piece(ppdf::checker_italian_checkers(), annotate = "cartesian")) })