Using input from UI to index for loops in server function

Hi, I am trying to turn a piece of code into a shiny app. The original script runs fine but not when I try to turn it into an app. It is meant to simulate dice rolls in a board game, where the user can input the number of dice each side rolls. Therefore I need to use some user defined input to tell the server how many times to iterate certain loops. I have tried this all sorts of ways but always get an error when running the app when I click the action button I've created.

ui <- fluidPage(
  
  sidebarLayout(
  
    sidebarPanel(
  
selectInput(inputId = "grey_p",
            label = "Player grey dice",
            choices = c(0,1,2,3,4,5,6),
            selected = 0),

selectInput(inputId = "tan_p",
            label = "Player tan dice",
            choices = c(0,1,2,3,4,5,6),
            selected = 0),

#and the demon?

selectInput(inputId = "grey_d",
            label = "Demon grey dice",
            choices = c(0,1,2,3,4,5,6),
            selected = 0),

selectInput(inputId = "tan_d",
            label = "Demon tan dice",
            choices = c(0,1,2,3,4,5,6),
            selected = 0),


#define health, strength defence and other relevant stats here

selectInput(inputId = "player_health",
            label = "Player Health",
            choices = c(1,2,3,4,5,6,7,8,9,10),
            selected = 10),

selectInput(inputId = "demon_health",
            label = "Demon Health",
            choices = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20),
            selected = 10),

selectInput(inputId = "player_defence",
            label = "Player Defence",
            choices = c(0,1,2,3,4,5,6,7),
            selected = 0),

selectInput(inputId = "demon_defence",
            label = "Demon Defence",
            choices = c(0,1,2,3,4,5,6,7),
            selected = 0)

  ), #Close sidebarpanel, comma is for sidebarlayout further arguments

#Add player strength
####################################################################################################

#Mainpanel for output
mainPanel(
  actionButton("run_button", "Run"),
textOutput("txt_output")
) #close mainpanel
) #Close sidebarylayout
) # close fluidpage

#define dice sides
grey <- c(0,1,1,2,2,3)
tan <- c(0,0,0,2,3,4)
red <- c(0,1,2,3,3,4)
black <- c(1,2,2,3,3,4)
gold <- c(0,0,0,0,6,6)
special <- list(2,2,3,3,4,"Death") 

server <- function(input, output) {
  
  observeEvent(input$run_button, {
  #I am experimenting with these values, they are created so that they can be used in the server code below only, 
  #there may be a better way to do this.
    
  d_health_serv <-
    as.numeric(input$demon_health)


  p_health_serv <- 
    as.numeric(input$player_health)
  
  
#These will be used to reset health values after each battle
demon_health_start <- as.numeric(input$demon_health)
player_health_start <- as.numeric(input$player_health)

big_i <- 1
demon_wins <- 0
player_wins <- 0
number_of_turns_taken <- list()

#list out the user input values for numbers of dice to roll here
grey_p_serv <- as.numeric(input$grey_p)
tan_p_serv <- as.numeric(input$tan_p)
grey_d_serv <- as.numeric(input$grey_d)
tan_d_serv <- as.numeric(input$tan_d)

#big_i represents each battle, increments by 1 after each battle and goes to 100 battles
while(big_i <= 100) {
#small_i is each roll within each battle  
  small_i <- 0
  
  
  while(d_health_serv > 0 && p_health_serv > 0) {
    
    small_i <- small_i + 1
    
    #Get grey dice role for this roll, the for loop makes it roll the grey dice the number of times the user has specified
  if (grey_p_serv > 0) {
    grey_score_p_ <- list()
    random_player_ <- list()
    for (i in 1:grey_p_serv) {
      random_player_[[i]] <- floor(runif(1, min=1, max=6))
      grey_score_p_[[i]] <- grey[random_player_[[i]][1]]
    } #end for loop
  } #end if
    
    #repeat for tan
  if (tan_p_serv > 0) {
    tan_score_p_ <- list()
    random_player_ <- list()
    for (i in 1:tan_p_serv) {
      random_player_[[i]] <- floor(runif(1, min=1, max=6))
      tan_score_p_[[i]] <- tan[random_player_[[i]][1]]
    }
  }  

    #sum the results of each dice colour
    sum_grey <- Reduce("+", grey_score_p_)
    sum_tan <- Reduce("+", tan_score_p_)
    
    #Then sum all dice to get the final result for the player
    #Need to add strength!!
    final_score_p <- sum_grey + sum_tan
    
    ###########################################################################################################
    #Now do the same for the demon and create final score d
    
    
    grey_score_d_ <- list()
    random_demon_ <- list()
  if (grey_d_serv > 0) {
    for (i in 1:grey_d_serv) {
      random_demon_[[i]] <- floor(runif(1, min=1, max=6))
      grey_score_d_[[i]] <- grey[random_demon_[[i]][1]]
    }
  }
  
    tan_score_d_ <- list()
    random_demon_ <- list()
  if (tan_d_serv > 0) {
    for (i in 1:tan_d_serv) {
      random_demon_[[i]] <- floor(runif(1, min=1, max=6))
      tan_score_d_[[i]] <- tan[random_demon_[[i]][1]]
    }
  }  
    sum_grey <- Reduce("+", grey_score_d_)
    sum_tan <- Reduce("+", tan_score_d_)
    
    final_score_d <- sum_grey + sum_tan
    
    #then iterate the whole process out many times
    
    damage = final_score_p - final_score_d
    
    if(damage > 0) {
      damage <- abs(damage) - as.numeric(input$demon_defence)
      if(damage > 0) {
        d_health_serv <- d_health_serv - damage
      }
    }
    
    if(damage < 0) {
      damage <- abs(damage) - as.numeric(input$player_defence)
      if(damage > 0) {
        p_health_serv <- p_health_serv - abs(damage)
      }
    }
    
    if(p_health_serv <= 0) {
      demon_wins <- demon_wins + 1
    }
    
    if(d_health_serv <= 0) {
      player_wins <- player_wins + 1
    }
    
  } #end small while loop
  number_of_turns_taken[big_i] <- small_i
  big_i <- big_i + 1 #each time a winner is found, iterate the whole thing again to find who wins the next round, do this 100 times
  
  #reset demon and player health to original values
  d_health_serv <- demon_health_start
  p_health_serv <- player_health_start
  
  
  } #end big while loop

avg_number_rolls_to_win <- mean(unlist(number_of_turns_taken))

output$txt_output <- renderText(print(paste("The player has won ", player_wins, " battles,", "but the demon won ", demon_wins, " barttles.",
            "On average it took ", avg_number_rolls_to_win, "rolls to win each battle.")))

  }) # observeEvent close 
} # server function close

The error I get from this is:
Warning: Error in if: argument is of length zero

Hello @talexis77. I see the error when any of the dice inputs are set to 0 and the Run button is clicked. At quick glance, it appears certain objects are created when an input is greater than zero, but they are not created when an input = 0. For example, sum_grey relies on grey_score_p_, but grey_score_p_ is only created when grey_p_serv > 0 (i.e. input$grey_p > 0).

In my mind, there are a couple of ways to handle this:

  1. Set the minimum value of the input to 1 instead of 0.
  2. If 0 is an acceptable input, then grey_score_p_ needs an acceptable value assigned when the input is 0. The same is true for the other objects as well.

Hope this helps!

Hi @scottyd22, thanks so much for this it is much appreciated!

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.