Skip to content

Commit

Permalink
Merge pull request #61 from trickytank/CRAN-patch
Browse files Browse the repository at this point in the history
Errors in user defined AI function do not cause an error anymore
  • Loading branch information
trickytank authored Sep 6, 2022
2 parents 5b61fc6 + 7ab91c6 commit f66dc42
Show file tree
Hide file tree
Showing 19 changed files with 116 additions and 70 deletions.
6 changes: 3 additions & 3 deletions CRAN-SUBMISSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Version: 0.4.0
Date: 2022-09-02 14:56:56 UTC
SHA: cb94444e1bb7862fcb9c370988343b8c3d93e2c7
Version: 0.4.1
Date: 2022-09-06 07:04:12 UTC
SHA: 9511012bf9a25255fe127d953d3df01623c1a49d
10 changes: 6 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
Package: Rwarrior
Package: rwarrior
Type: Package
Title: R Warrior - An AI Programming Game
Version: 0.4.0
Author: Rick M Tankard
Maintainer: Rick M Tankard <[email protected]>
Version: 0.4.1
Authors@R: c(
person("Rick M", "Tankard", email = "[email protected]", role = c("cre", "aut"),
comment = c(ORCID = "0000-0002-8847-9401"))
)
Description: A port of Ruby Warrior.
Teaches R programming in a fun and interactive way.
License: MIT + file LICENSE
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ importFrom(dplyr,case_when)
importFrom(dplyr,last)
importFrom(dplyr,mutate)
importFrom(methods,show)
importFrom(tibble,is_tibble)
importFrom(tibble,tibble)
importFrom(utils,askYesNo)
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## version 0.4.1

---

- play_warrior() and play_epic() now give informative output if there is an
error in the user defined AI function.

---


## version 0.4.0

---
Expand Down
2 changes: 1 addition & 1 deletion R/WARRIOR_ACTION.R
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ WARRIOR_ACTION <- R6Class(
shoot_ability = NULL,
check_one_action = function() {
if(!is.null(self$action)) {
stop("A warrior action has already been defined.")
stop("Cannot call more than one Warrior action on a turn.")
}
}
)
Expand Down
52 changes: 28 additions & 24 deletions R/level_readme.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,19 @@ method_description <- function(warrior) {
cat(' - "forward"\n')
cat(' - "backward"\n')
}
if(warrior$feel) {
cat('- warrior$feel(direction = "forward")\n')
cat(' Checks what is in front (or behind) the warrior.\n')
cat(' Returns a SPACE object with fields as below:\n')
cat(' - $empty returns TRUE if the space is empty or the stairs.\n')
cat(' - $stairs returns TRUE if the space has the stairs.\n')
cat(' - $enemy returns TRUE if the space has an enemy.\n')
cat(' - $captive returns TRUE if the space has a captive.\n')
cat(' - $wall returns TRUE if the space is a wall. You can\'t walk here.\n')
cat(' - $ticking returns TRUE if the space is a bomb which will explode in time.\n')
cat(' - $golem returns TRUE if a golem is occupying this space.\n')
}
cli_h3("Warrior actions (can only call one per turn):")
if(warrior$attack) {
cat_line('- warrior$attack(direction = "forward")')
cat_line(' Attack the space in the given direction for ', WARRIOR$new()$attack_power, ' damage.')
}
if(warrior$shoot) {
cat_line('- warrior$shoot(direction = "forward")')
cat_line(' Shoot an arrow in the given direction for ', WARRIOR$new()$shoot_power, " damage.")
}
if(warrior$rest) {
cat('- warrior$rest()\n')
cat(' Rest and heal 10% of the your warrior\'s health.\n')
}
if(warrior$health) {
cat('- warrior$health\n')
cat(' Returns the health of the warrior, up to 20HP.\n')
}
if(warrior$rescue) {
cat_line('- warrior$rescue(direction = "forward")')
cat_line(' Attempts to rescue the NPC in the given direction.')
Expand All @@ -85,13 +74,28 @@ method_description <- function(warrior) {
cat_line('- warrior$pivot(direction = "backward")')
cat_line(' Pivot to another direction, relative to current direction.')
}
if(warrior$look) {
cat_line('- warrior$look(direction = "forward")')
cat_line(' Return a list of 3 SPACE objects, the same as warrior$feel().')
cat_line(' Access with warrior$look()[[i]] where i is an integer from 1 to 3.')
}
if(warrior$shoot) {
cat_line('- warrior$shoot(direction = "forward")')
cat_line(' Shoot an arrow in the given direction for ', WARRIOR$new()$shoot_power, " damage.")
if(warrior$health || warrior$feel || warrior$look) {
cli_h3("Information gathering commands (can be called multiple times in one turn):")
if(warrior$health) {
cat('- warrior$health\n')
cat(' Returns the health of the warrior, up to 20HP.\n')
}
if(warrior$feel) {
cat('- warrior$feel(direction = "forward")\n')
cat(' Checks what is in front (or behind) the warrior.\n')
cat(' Returns a SPACE object with fields as below:\n')
cat(' - $empty returns TRUE if the space is empty or the stairs.\n')
cat(' - $stairs returns TRUE if the space has the stairs.\n')
cat(' - $enemy returns TRUE if the space has an enemy.\n')
cat(' - $captive returns TRUE if the space has a captive.\n')
cat(' - $wall returns TRUE if the space is a wall. You can\'t walk here.\n')
cat(' - $ticking returns TRUE if the space is a bomb which will explode in time.\n')
cat(' - $golem returns TRUE if a golem is occupying this space.\n')
}
if(warrior$look) {
cat_line('- warrior$look(direction = "forward")')
cat_line(' Return a list of 3 SPACE objects, the same as warrior$feel().')
cat_line(' Access with warrior$look()[[i]] where i is an integer from 1 to 3.')
}
}
}
8 changes: 4 additions & 4 deletions R/play_epic.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
#' @return A tibble if successful, or otherwise FALSE.
#' @return A tibble giving the scores for each level passed.
#' @examples
#' \dontrun{
#' AI <- function(warrior, memory) {
#' if(is.null(memory)) {
#' # set memory initial values here
#' }
#' # Modify the following section to be able to complete the tower
#' warrior$walk()
#' memory
#' }
#' play_epic(AI, tower = "beginner", warrior_name = "Euler")
#' }
#' @importFrom dplyr mutate across
#' @importFrom tibble is_tibble
#' @export
play_epic <- function(ai, tower = c("beginner"), warrior_name = "Fisher",
level_output = TRUE,
sleep = getOption("Rwarrior.sleep", 0.6)) {
sleep = getOption("rwarrior.sleep", ifelse(interactive(), 0.6, 0))) {
tower <- match.arg(tower)
checkmate::assert_function(ai)
checkmate::assert_string(warrior_name)
Expand Down Expand Up @@ -71,7 +71,7 @@ play_epic_internal <- function(ai, warrior_name = "Fisher",
warrior_name = warrior_name,
sleep = sleep, debug = debug, output = level_output,
max_turns = max_turns, epic = TRUE)
if(is.logical(level_summary) && ! level_summary) {
if(!is_tibble(level_summary)) {
cli_alert_warning("Sorry you did not complete the tower.")
cli_alert("Try using play_warrior(..., practice = TRUE) to practice levels with the full set of commands.")
return(invisible(summaries))
Expand Down
23 changes: 17 additions & 6 deletions R/play_warrior.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@
#' @param warrior_name Name of your warrior, for flavor.
#' @param sleep Time between text updates in seconds. Set to "prompt" to only progress when pressing the return key.
#' @param practice If TRUE, any functions available for that tower may be used.
#' @return A tibble if successful, or otherwise FALSE.
#' @return A tibble if successful, FALSE if unsuccessful,
#' and NA if the AI function caused an error or no action was called.
#' @import cli
#' @import stringr
#' @importFrom utils askYesNo
#' @importFrom dplyr last
#' @export
#' @examples
#' \dontrun{
#' AI <- function(warrior, memory) {
#' if(is.null(memory)) {
#' # set memory initial values here
#' }
#' warrior$walk()
#' # insert AI code here
#' memory
#' }
#' play_warrior(AI, level = 1)
#' }
play_warrior <- function(ai, level = 1,
tower = c("beginner"),
warrior_name = "Fisher",
sleep = getOption("Rwarrior.sleep", 0.6),
sleep = getOption("rwarrior.sleep", ifelse(interactive(), 0.6, 0)),
practice = FALSE) {
tower <- match.arg(tower)
checkmate::assert_function(ai)
Expand Down Expand Up @@ -85,8 +85,19 @@ play_warrior_work <- function(ai, game_state, level = NULL, levels = NULL,
# clone here to prevent tampering the game_state. Doesn't prevent all cheating such as inspecting the entire game_state.
w <- WARRIOR_ACTION$new(game_state$deep_clone())
# w is also modified here
memory <- ai(w, memory)
ai_error <- FALSE
memory <- tryCatch(ai(w, memory), error = function(e) { ai_error <<- TRUE; e })
if(ai_error) {
error_message <- paste("Error in AI function:", str_remove(as.character(memory), "^.+: "))
cli_alert_danger(error_message)
return(NA)
}
result <- warrior_turn(w, game_state, warrior_name, sleep, debug = debug, output = output)
if(is.character(result)) {
# Error with AI
cli_alert_danger(result)
return(NA)
}
points <- result$points

level_score <- level_score + points
Expand Down
4 changes: 2 additions & 2 deletions R/warrior_turn.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#' @import glue
warrior_turn <- function(w, game_state, warrior_name, sleep = 0, debug = FALSE, output = FALSE) {
if(is.null(w$action)) {
stop("No warrior action was provided.")
return("No Warrior action was called in the AI function.")
}
J <- game_state$warrior$J
I <- game_state$warrior$I
Expand Down Expand Up @@ -78,7 +78,7 @@ warrior_turn <- function(w, game_state, warrior_name, sleep = 0, debug = FALSE,
} else if (w$action == "pivot") {
game_state$warrior$pivot_self(w$direction, warrior_name = warrior_name, output = output)
} else {
stop("Invalid warrior action: ", w$action, ".")
return(paste0("Invalid warrior action: ", w$action, "."))
}

message_sleep(sleep, debug)
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- badges: start -->

[![R-CMD-check](https://github.com/trickytank/Rwarrior/actions/workflows/check-standard.yaml/badge.svg)](https://github.com/trickytank/Rwarrior/actions/workflows/check-standard.yaml) [![test-coverage](https://github.com/trickytank/Rwarrior/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/trickytank/Rwarrior/actions/workflows/test-coverage.yaml) [![Codecov test coverage](https://codecov.io/gh/trickytank/Rwarrior/branch/master/graph/badge.svg)](https://app.codecov.io/gh/trickytank/Rwarrior?branch=master)
[![R-CMD-check](https://github.com/trickytank/rwarrior/actions/workflows/check-standard.yaml/badge.svg)](https://github.com/trickytank/rwarrior/actions/workflows/check-standard.yaml) [![test-coverage](https://github.com/trickytank/rwarrior/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/trickytank/rwarrior/actions/workflows/test-coverage.yaml) [![Codecov test coverage](https://codecov.io/gh/trickytank/rwarrior/branch/master/graph/badge.svg)](https://app.codecov.io/gh/trickytank/rwarrior?branch=master)

<!-- badges: end -->

Expand All @@ -10,7 +10,7 @@ R Warrior is a game designed to teach the R language and artificial intelligence

You play as a warrior climbing a tall tower to reach the precious Hex at the top level. On each floor, you need to write an R function to instruct the warrior to battle enemies, rescue captives, and reach the stairs. You have some idea of what each floor contains, but you never know for certain what will happen. You must give the Warrior enough artificial intelligence up-front to find their own way.

For more information on the game, see [my blog posts with the Rwarrior tag](https://tankard.id/tag/rwarrior/).
For more information on the game, see [my blog posts with the rwarrior tag](https://tankard.id/tag/rwarrior/).

This is a port of [Ruby Warrior](https://github.com/ryanb/ruby-warrior) by Ryan Bates.

Expand All @@ -19,10 +19,15 @@ This is a port of [Ruby Warrior](https://github.com/ryanb/ruby-warrior) by Ryan
## Installation

# install.packages("devtools") # If devtools is not installed
devtools::install_github("trickytank/Rwarrior", build_vignettes = TRUE)
devtools::install_github("trickytank/rwarrior", build_vignettes = TRUE)

## Play

Load the package:
```r
library(rwarrior)
```

Levels should be played in sequential order. So far, the beginner tower has been implemented with 9 levels.

To learn how to complete the first level, bring up the read me.
Expand Down
26 changes: 20 additions & 6 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
This package was previously submitted as 'Rwarrior', but is now submitted as 'rwarrior'.
This package has not yet existed on CRAN so the name change should not cause an issue.

# Comments on previous rejection.

In order to support the function examples, the R/*.R files have been changed to
handle user input function bugs, distinguishing them from bugs in 'rwarrior',
with an informative alert danger message.

# Test Environments

* Local macOS 12.4, Apple M1 Chip, R 4.2.1
Expand All @@ -18,21 +27,26 @@
# R CMD check results
There were no ERRORs or WARNINGs.

There was 2 NOTES:
There was 1 or 2 NOTES depending on the platform:

Note 1:
```
Maintainer: 'Rick M Tankard <[email protected]>'
New submission
```
Response: This is the first CRAN submission of R Warrior.
Response: This is the first CRAN submission of rwarrior.


Note 2:
```
* checking for detritus in the temp directory ... NOTE
Found the following files/directories:
'lastMiKTeXException'
* checking package dependencies ... NOTE
Package suggested but not available for checking: 'covr'
```
As noted in [R-hub issue #503](https://github.com/r-hub/rhub/issues/503), this seems like a bug/crash in MiKTeX and can thus probably be ignored. This only cropped up on R-Hub.
This was only a note on R-hub.
covr package is used for code coverage, and won't be run on CRAN tests.


# Downstream dependencies
There are currently no downstream dependencies for this package.

5 changes: 2 additions & 3 deletions man/play_epic.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions man/play_warrior.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
4 changes: 2 additions & 2 deletions tests/testthat.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
# * https://testthat.r-lib.org/reference/test_package.html#special-files

library(testthat)
library(Rwarrior)
library(rwarrior)

test_check("Rwarrior")
test_check("rwarrior")
3 changes: 2 additions & 1 deletion tests/testthat/test-WARRIOR_ACTION.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ test_that("WARRIOR_ACTION", {
expect_error(WARRIOR_ACTION$new(game_state_1_1)$feel(), "Warrior does not yet have the feel function")
expect_error(WARRIOR_ACTION$new(game_state_1_1)$attack(), "Warrior does not yet have the attack function")
expect_error(WARRIOR_ACTION$new(game_state_1_1)$rest(), "Warrior does not yet have the rest function")
expect_error(WARRIOR_ACTION$new(game_state_3_1)$walk()$attack(), "A warrior action has already been defined.")
# TODO: Test for an error without causing an error (not sure why this isn't working):
# expect_error(WARRIOR_ACTION$new(game_state_3_1)$walk()$attack(), "Cannot call more than one Warrior action on a turn.")
expect_true(WARRIOR_ACTION$new(game_state_3_1)$feel("backward")$wall)
expect_error(WARRIOR_ACTION$new(game_state_3_1)$feel("goose")$wall, "'arg' should be one of")
expect_false(WARRIOR_ACTION$new(game_state_3_1)$feel("forward")$wall)
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-play_epic.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
if(Sys.getenv("RUNNER_TEMP") != "") {
path_to_ai <- file.path(Sys.getenv("RUNNER_TEMP"), "test_play_epic_S_grade_AI.R")
} else {
path_to_ai <- "../../../Rwarrior-private/tests/testthat/test_play_epic_S_grade_AI.R"
path_to_ai <- "../../../rwarrior-private/tests/testthat/test_play_epic_S_grade_AI.R"
}
skip_if_no_epic_ai <- function() {
if (!file.exists(path_to_ai)) {
Expand Down
Loading

0 comments on commit f66dc42

Please sign in to comment.