How to generate multiple output at once (loop ?)

,

Hello everyone !

I created a WebApp to generate NPC for a TTRPG (you can look it up here)

Works great and all but I wanted to make it so that users would be able to select how many NPC they want to create at once and the app would generate them all (possibly in several tabs to make it clearer and more aesthetically pleasing).

Thing is... I'm a complete noob. The fact that the app is already working right now is short of a miracle but I'm honestly stuck on this part. I've tried making lists and lists of lists and dataframe but I'm either getting various type of errors, text with no formatting at all or ... just nothing. Like no error, but nothing is rendered.

If any of you with way more knowledge and skills wouldn't mind taking a look and help, that would be awesome !

I'm putting up my code here for you to look at, but since it relies on several data files containing all the description and stuff, if you want to run it, I can send you the whole folder.

Thank in advance people

library(shiny)
library(shinyjs)
library(tidyverse)
library(shinythemes)

# Mise en place -----------------------------------------------------------
list_names <- read.csv2("list_names.csv",
                        fileEncoding = "Windows-1252",
                        check.names=F)

list_techniques<- read.csv2("list_techniques.csv",
                            fileEncoding = "Windows-1252", 
                            check.names=F)

list_principles_drives <- read.csv2("list_principles_drives.csv", 
                                    fileEncoding="Windows-1252", 
                                    check.names=F)

list_caracteristics <- read.csv2("list_caracteristics.csv", 
                                 fileEncoding="Windows-1252", 
                                 check.names=F)

list_hair <- read.csv2("list_hair.csv", 
                       fileEncoding="Windows-1252", 
                       check.names=F)

# Define UI for application ----
ui <- fluidPage(theme=shinytheme("journal"),
                
                # Application title
                titlePanel("Avatar Legends - NPC Generator (V2.2) "),
                
                # Topbar with selections 
                fluidRow(shinyjs::useShinyjs(),
                         id = "side-panel",
                         column(2,radioButtons("choice_type","NPC Types", c("Minor NPC",
                                                                            "Major NPC",
                                                                            "Master NPC",
                                                                            "Group")),
                                sliderInput("num_npc","Number of NPC",value=1,min = 1, max = 15),
                                offset=1),
                         
                         column(2,radioButtons("choice_gender", "Gender", c("He/Him",
                                                                            "She/Her",
                                                                            "They/Them",
                                                                            "Random"))),
                         
                         column(2,selectInput("choice_nation", "Nation", c("Air Nomads",
                                                                           "Earth Kingdom",
                                                                           "Fire Nation",
                                                                           "Republic City",
                                                                           "Water Tribe",
                                                                           "Random"))),
                         
                         column(2,selectInput("choice_training", "Training", c("Airbending",
                                                                               "Earthbending",
                                                                               "Firebending",
                                                                               "Martial Art",
                                                                               "Technology",
                                                                               "Universal",
                                                                               "Waterbending",
                                                                               "Group",
                                                                               "No Training",
                                                                               "Random"))),
                         
                         column(2, sliderInput("num_tech","Number of techniques",value=1,
                                               min = 1, max = 5),
                                selectInput("special_tech","Specialized Bending", c("---",
                                                                                    "Bloodbending",
                                                                                    "Combustionbending",
                                                                                    "Healing",
                                                                                    "Lavabending",
                                                                                    "Lightningbending",
                                                                                    "Metalbending",
                                                                                    "Seismic Sense")),
                                
                                selectInput("rare_tech","Rare Techniques",c("No", "Yes"))),
                         
                         
                         fluidRow(column(2,actionButton("generate","Generate"),
                                         offset= 4),
                                  column(2,actionButton("generate_random","Randomize")),
                                  column(2,actionButton("reset_input", "Reset")))),
                
                
                mainPanel(fluidRow(htmlOutput("NPC")),
                          fluidRow(htmlOutput("tech")),
                          fluidRow(htmlOutput("NPC_random")),
                          fluidRow(htmlOutput("tech_random")),
                          offset=5
                          
                )
)


# Define server logic ----
server <- function(input, output) {
  
  NPC <- eventReactive(input$generate,{
    
    type <- input$choice_type
    
    fatigue <- case_when(type=="Minor NPC" ~ sample(2:6, 1),
                         type=="Major NPC"~ sample(5:9, 1),
                         type=="Master NPC"~ sample(8:13, 1),
                         type=="Group" ~ sample (2:13, 1))
    
    
    principle <- list_principles_drives %>% 
      select(Principles) %>% 
      sample_n(1) %>% 
      pull(Principles)
    
    drive <- list_principles_drives %>% 
      select(Drives) %>% 
      sample_n(1) %>% 
      pull(Drives)
    
    number_tech <- input$num_tech
    
    if(type!="Group"){ 

      gender <- case_when(input$choice_gender == "He/Him" ~ "He/Him",
                          input$choice_gender == "She/Her" ~ "She/Her",
                          input$choice_gender == "They/Them" ~ "They/Them",
                          input$choice_gender == "Random" ~ sample(c("He/Him",
                                                                     "She/Her",
                                                                     "They/Them"),1))
      
      nation <- case_when(input$choice_nation ==  "Air Nomads" ~ "Air Nomads" ,
                          input$choice_nation ==  "Earth Kingdom" ~ "Earth Kingdom",
                          input$choice_nation ==  "Fire Nation" ~ "Fire Nation",
                          input$choice_nation ==  "Republic City" ~ "Republic City",
                          input$choice_nation ==   "Water Tribe" ~ "Water Tribe",
                          input$choice_nation ==  "Random" ~ sample(c("Air Nomads",
                                                                      "Earth Kingdom",
                                                                      "Fire Nation",
                                                                      "Republic City",
                                                                      "Water Tribe"),1))
      
      name <- list_names %>% 
        filter(Gender==gender) %>% 
        sample_n(1) %>% 
        pull(nation)
      
      age<- sample(c(10:80),1)
      
      background <- sample(c("Military","Monastic", "Outlaw",
                             "Privileged","Urban","Wilderness"), 2)
      
      size <- c(round(runif(n=1, min=1.2, max=2),2), "m, ")
      
      weight<-c(sample(50:130, 1), "kg,")
      
      hair <- list_hair %>% 
        select(Hair) %>% 
        sample_n(1)
      
      facial_hair <- if(gender=="She/Her"){
        c(" ")
      }
      else{
        if(age > 17){list_hair %>% 
          select(Facial_Hair) %>% 
          sample_n(1)
      }}
      
      demeanor1<-list_caracteristics[1:100,] %>% 
        select(Demeanor) %>% 
        slice_sample(n=1)
      
      
      demeanor2<-list_caracteristics[101:200,] %>% 
        select(Demeanor) %>% 
        slice_sample(n=1)
      
      physical_quirk <- list_caracteristics %>% 
        select(Physical_quirk) %>% 
        sample_n(1)
      
      behavior_quirk <-list_caracteristics %>% 
        select(Behavior_quirk) %>% 
        sample_n(1)
      
      accessories <- list_caracteristics %>% 
        select(Accessories) %>% 
        sample_n(1)
      
      
      
      paste("<h2>",name," - ", nation,"</h2>", 
            "<h4>",type,"</h4>",
            "<p><b>Gender :</b>", gender,
            "<br><b>Age :</b>", age,
            "<p> <b>Principle :</b>", principle,
            "<br> <b>Drive :</b>", drive,
            "<br> <b>Conditions :</b>", "<br>Afraid",
            "<br>Angry",
            "<br>Guilty",
            "<br>Insecure",
            "<br>Troubled",
            "<p> <b>Background :</b>", background[1],"/",background[2],
            "<br><b>Demeanor :</b>", demeanor1,"/", demeanor2,
            "<p><b>Hair :</b>", hair, facial_hair,
            "<br><b>Physique :</b>", size[1],size[2],weight[1],weight[2],physical_quirk,
            "<br><b>Quirk :</b>",behavior_quirk,
            "<br><b>Accessory :</b>",accessories,
            "<p> <b>Fatigue :</b>", fatigue,
            "<h3>Technique(s)</h3>")}
    
    else{

            paste("<h3>",type,"</h3>",
            "<p> <b>Principle :</b>", principle,
            "<br> <b>Drive :</b>", drive,
            "<br> <b>Conditions :</b>", "<br>Afraid",
            "<br>Angry",
            "<br>Guilty",
            "<br>Insecure",
            "<br>Troubled",
            "<p> <b>Fatigue :</b>", fatigue,
            "<h3>Technique(s)</h3>")
    }
    
  })
  
  output$NPC <- renderText({
    NPC()
  })
  
  tech<-eventReactive(input$generate,{
    
    training_choice <- case_when(input$choice_training == "Airbending" ~ "Airbending" ,
                                 input$choice_training == "Earthbending" ~ "Earthbending",
                                 input$choice_training == "Firebending" ~ "Firebending",
                                 input$choice_training == "Martial Art" ~ "Martial Art",
                                 input$choice_training == "Waterbending" ~ "Waterbending",
                                 input$choice_training == "Technology" ~ "Technology",
                                 input$choice_training == "Universal" ~ "Universal",
                                 input$choice_training == "No Training" ~ "No Training",
                                 input$choice_training == "Group" ~ "Group",
                                 input$choice_training == "Random" ~ sample(c("Airbending",
                                                                              "Earthbending",
                                                                              "Firebending",
                                                                              "Martial Art",
                                                                              "Technology",
                                                                              "Universal",
                                                                              "Waterbending",
                                                                              "No Training"),1))
    
    if(input$rare_tech == "No"){
      techniques <- list_techniques %>% 
        filter(Training %in% training_choice,
               Specialized==input$special_tech | Specialized=="---",
               Rare!="Yes") %>% 
        slice_sample(n=input$num_tech) %>% 
        pull(Technique)
      
      paste(techniques)
    }
    else{    
      techniques <- list_techniques %>% 
        filter(Training %in% training_choice,
               Specialized==input$special_tech  | Specialized=="---") %>% 
        slice_sample(n=input$num_tech) %>% 
        pull(Technique)
      
      paste(techniques)}
    
  })
  
  output$tech <- renderText({
    tech()
  })  
  
  NPC_random <- eventReactive(input$generate_random,{
    
    type <- input$choice_type
    
    gender <- sample(c("He/Him","She/Her","They/Them"),1)
    
    nation <- sample(c("Air Nomads",
                       "Earth Kingdom",
                       "Fire Nation",
                       "Republic City",
                       "Water Tribe"),1)
    
    number_tech <- input$num_tech
    
    name <- list_names %>% 
      filter(Gender==gender) %>% 
      sample_n(1) %>% 
      pull(nation)
    
    
    age<- sample(c(10:80),1)
    
    fatigue <- case_when(type=="Minor NPC" ~ sample(2:6, 1),
                         type=="Major NPC"~ sample(5:9, 1),
                         type=="Master NPC"~ sample(8:13, 1))
    
    principle <- list_principles_drives %>% 
      select(Principles) %>% 
      sample_n(1) %>% 
      pull(Principles)
    
    drive <- list_principles_drives %>% 
      select(Drives) %>% 
      sample_n(1) %>% 
      pull(Drives)
    
    background <- sample(c("Military","Monastic", "Outlaw",
                           "Privileged","Urban","Wilderness"), 2)
    
    size <- c(round(runif(n=1, min=1.2, max=2),2), "m")
    weight<-c(sample(50:130, 1), "kg")
    
    hair <- list_hair %>% 
      select(Hair) %>% 
      sample_n(1)
    
    facial_hair <- if(gender=="She/Her"){
      c(" ")
    }
    else{
      list_hair %>% 
        select(Facial_Hair) %>% 
        sample_n(1)
    }
    
    demeanor1<-list_caracteristics[1:100,] %>% 
      select(Demeanor) %>% 
      slice_sample(n=1)
    
    
    demeanor2<-list_caracteristics[101:200,] %>% 
      select(Demeanor) %>% 
      slice_sample(n=1)
    
    physical_quirk <- list_caracteristics %>% 
      select(Physical_quirk) %>% 
      sample_n(1)
    
    behavior_quirk <- list_caracteristics %>% 
      select(Behavior_quirk) %>% 
      sample_n(1)
    
    accessories <- list_caracteristics %>% 
      select(Accessories) %>% 
      sample_n(1)
    
    paste(paste("<h2>",name," - ", nation,"</h2>", 
                "<h4>",type,"</h4>",
                "<p><b>Gender :</b>", gender,
                "<br><b>Age :</b>", age,
                "<p> <b>Principle :</b>", principle,
                "<br> <b>Drive :</b>", drive,
                "<br> <b>Conditions :</b>", "<br>Afraid",
                "<br>Angry",
                "<br>Guilty",
                "<br>Insecure",
                "<br>Troubled",
                "<p> <b>Background :</b>", background[1],"/",background[2],
                "<br><b>Demeanor :</b>", demeanor1,"/",demeanor2,
                "<p><b>Hair :</b>", hair, facial_hair,
                "<br><b>Physique :</b>", size[1],size[2],weight[1],weight[2],physical_quirk,
                "<br><b>Quirk :</b>",behavior_quirk,
                "<br><b>Accessory :</b>",accessories,
                "<p> <b>Fatigue :</b>", fatigue,
                "<h3>Technique(s)</h3>"))
    
  })
  
  output$NPC_random <- renderText({
    NPC_random()
  })
  
  tech_random<-eventReactive(input$generate_random,{
    
    training_choice <- sample(c("Airbending",
                                "Earthbending",
                                "Firebending",
                                "Martial Art",
                                "Technology",
                                "Universal",
                                "Waterbending",
                                "No Training"),1)
    number <- input$num_tech
    
    if(input$rare_tech == "No"){
      techniques_random <- list_techniques %>% 
        filter(Training %in% training_choice,
               Specialized==input$special_tech | Specialized=="---",
               Rare!="Yes") %>% 
        slice_sample(n=input$num_tech, replace=TRUE) %>% 
        pull(Technique)
      
      paste(techniques_random)
    }
    else{    
      techniques_random <- list_techniques %>% 
        filter(Training %in% training_choice,
               Specialized==input$special_tech  | Specialized=="---") %>% 
        slice_sample(n=input$num_tech, replace=TRUE) %>% 
        pull(Technique)
      
      paste(techniques_random)}
    
  })
  
  output$tech_random <- renderText({
    tech_random()
  })
  
  observeEvent(input$reset_input, {
    shinyjs::reset("side-panel")
    shinyjs::hide("NPC")
    shinyjs::hide("tech")
    shinyjs::hide("NPC_random")
    shinyjs::hide("tech_random")
  })
  
  observeEvent(input$generate, {
    shinyjs::show("NPC")
    shinyjs::show("tech")
    shinyjs::hide("NPC_random")
    shinyjs::hide("tech_random")
  })
  
  observeEvent(input$generate_random, {
    shinyjs::show("NPC_random")
    shinyjs::show("tech_random")
    shinyjs::hide("NPC")
    shinyjs::hide("tech")
  })
}

# Run the application ----
shinyApp(ui = ui, server = server)

My apologies as I couldn't actually read every single part of your code. Also, I am a complete noob so take my words with grain of salts.

I assume NPC() is the final resulting text.
I would simply use for loop?



          NPC <- eventReactive(input$generate, {
                        res <- ""
                        rep_num <- input$
                        for (i in 1:rep_num){
                          
                         ## Your existing code should go here. 
                         ## except for 'paste' function should be edited.
                         ## paste("<h2>",name," - ", nation,"</h2>",  ..... should be edited to
                         res <- paste(res, "<h2>", name, " - ", nation, "</h2>", ......
                         }
                          ## after closing the for loop, simply add "res" again.
                         res

                          })

In my limited experience, this should work.

IT WORKS !

Thank you so much, I thought I would have to give up on the idea, you're the only answer I got out of 3 forum in almost 2 weeks !

Now I just need to tweak my code a bit on some other parts but your solutions gave me some ideas too ^^

Thank youuuuuuuuuuuu

1 Like

Glad that it helped!

This topic was automatically closed 7 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.