Site icon R-bloggers

Little useless-useful R functions – Desk plant simulator

[This article was first published on R – TomazTsql, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

This time, we will create a Desk plant simulator. And for that we need a set of different functions

Yes, we will grow a R plant in a simulation game with lots of twerks and hidden gems and ASCII art

And how this set of functions really work?

Storing the daily progress in an RDS. Before running set of functions, you will prepare a location to store the RDS file (environment variables and game play)

.plant_file <- path.expand("~/.r_desk_plant.rds")

ASCII art was done by GPT for all the stages and it is so nice

plant_art <- list(    # Stage 0: Seed  seed = c(    "            ",    "            ",    "            ",    "            ",    "     .      ",    "    (.)     ",    "   -----    ",    "  |     |   ",    "  |~~~~~|   ",    "  |_____|   "  ),    # Stage 1: Sprout  sprout = c(    "            ",    "            ",    "            ",    "     ,      ",    "    (')     ",    "     |      ",    "   -----    ",    "  |     |   ",    "  |~~~~~|   ",    "  |_____|   "  ),    # Stage 2: Seedling  seedling = c(    "            ",    "            ",    "     \\|/    ",    "    \\|||/   ",    "     |||    ",    "     |||    ",    "   -----    ",    "  |     |   ",    "  |~~~~~|   ",    "  |_____|   "  ),    # Stage 3: Young plant  young = c(    "            ",    "    \\~~/    ",    "   \\\\|//   ",    "    \\|/     ",    "    |||     ",    "    |||     ",    "   -----    ",    "  |     |   ",    "  |~~~~~|   ",    "  |_____|   "  ),......

Then you will have helper and main functions available. Complete code is available on my Github repository.

To run the game you will need to plant a new plant, water it, check the plant status and many others.

PlantNew() # Start a new plant (choose a name!)WaterPlant() # Water your plant (once per day)CheckPlant() # Full status reportPlant() # Quick view (just the art)

Here is an excerpt from the code:

hline <- function(char = "═", n = 40) strrep(char, n)

# get current status of a plant / game play
get_plant <- function(file = .plant_file) {
  if (file.exists(file)) { readRDS(file) } 
}


# Save plant in RDS and all game play!
save_plant <- function(plant, file = .plant_file) {
  saveRDS(plant, file)
}


# get growth info
get_stage <- function(points) {
  stage_idx <- max(which(growth_stages$min_points <= points))
  growth_stages[stage_idx, ]
}


# get appropriate ASCII art plant
get_plant_art <- function(plant) {
  if (plant$health <= 0) {
    return(plant_art$dead)
  }
  if (plant$health < 30) {
    return(plant_art$wilted)
  }
  stage <- get_stage(plant$points)
  plant_art[[stage$art_name]]
}

days_since <- function(date) {
  as.integer(Sys.Date() - as.Date(date))
}


# Messages for encouragement - Done with help of chatGPT
get_encouragement <- function() {
  messages <- c(
    "Your plant appreciates you!",
    "Keep up the great work!",
    "You're a natural plant parent!",
    "Your R sessions make the plant happy!",
    "Photosynthesis in progress... ",
    "Growing strong, just like your R skills!",
    "The plant sends positive vibes!",
    "Another day, another leaf!",
    "Your dedication is blooming!",
    "Keep coding, keep growing!"
  )
  sample(messages, 1)
}


# General health
get_health_message <- function(health) {
  if (health >= 90) {
    return("Thriving! Your plant is radiantly healthy!")
  } else if (health >= 70) {
    return("Healthy! Looking good!")
  } else if (health >= 50) {
    return("Okay. Could use some attention.")
  } else if (health >= 30) {
    return("Struggling. Please water me!")
  } else if (health > 0) {
    return("Critical! Water immediately or I'll die!")
  } else {
    return("Your plant has died. Start a new one with PlantNew()")
  }
}


# ---- main functions

PlantNew <- function(name = NULL, file = .plant_file) {
  existing <- get_plant(file)
  
  if (!is.null(existing) && existing$health > 0) {
    cat("  You already have a plant named '", existing$name, "'!\n", sep = "")
    cat(" Health: ", existing$health, "% | Stage: ", 
        get_stage(existing$points)$name, "\n\n", sep = "")
    cat("  Are you sure you want to replace it? (yes/no): ")
    response <- tolower(readline())
    if (response != "yes") {
      cat("  Keeping your existing plant.  \n\n")
    }
  }
  
  
  # Get plant name
  if (is.null(name)) {
    suggestions <- c("Fernie Bennes", "Morgan Treeman", "Leaf Seinfeld",
                     "Plantonio Banderas", "Elvis Parsley", "Kramerofern",
                     "Snake Costanza", "Aloe NewmanVera", "Jungle Tribbiani")
    
    cat("\n")
    cat("    NEW PLANT!\n")
    cat("  Some name suggestions:\n")
    for (i in seq_along(suggestions)) {
      cat(sprintf("    %d. %s\n", i, suggestions[i]))
    }
    cat("\n  Enter a name (or number, or press Enter for random): ")
    input <- readline()
    
    if (input == "") {
      name <- sample(suggestions, 1)
    } else if (grepl("^[0-9]+$", input)) {
      idx <- as.integer(input)
      if (idx >= 1 && idx <= length(suggestions)) {
        name <- suggestions[idx]
      } else {
        name <- input
      }
    } else {
      name <- input
    }
  }
  
  # Create new plant
  plant <- list(
    name = name,
    species = "R-Plant (Programmus enthusiasticus)",
    planted_date = Sys.Date(),
    last_watered = Sys.Date(),
    last_visited = Sys.time(),
    points = 0,
    health = 100,
    times_watered = 0,
    sessions = 0,
    achievements = character(0)
  )
  
  save_plant(plant, file)

  cat("\n")
  cat("  β•”", hline("═", 44), "β•—\n", sep = "")
  cat("  β•‘         NEW PLANT CREATED!               β•‘\n")
  cat("  β•š", hline("═", 44), "╝\n", sep = "")
  cat("\n")
  
  art <- plant_art$seed
  for (line in art) {
    cat("        ", line, "\n", sep = "")
  }
  
  cat("\n")
  cat("Name:    ", name, "\n", sep = "")
  cat("Species: ", plant$species, "\n", sep = "")
  cat("Planted: ", format(Sys.Date(), "%B %d, %Y"), "\n", sep = "")
  cat("\n")
  cat("Tips:\n")
  cat(" --> Use WaterPlant() to water your plant\n")
  cat(" --> Use CheckPlant() to see its status\n")
  cat(" --> Visit often - your R sessions help it grow!\n")
  cat("\n")

}



# -- Water planting 
WaterPlant <- function(file = .plant_file) {
  plant <- get_plant(file)
  if (is.null(plant)) {
    cat("\n No plant found! Start one with PlantNew()\n\n")
  }
  
  if (plant$health <= 0) {
    cat("\n  Your plant has died. Start a new one with PlantNew()\n\n")
  }
  
  # Check if already watered today
  last_water_date <- as.Date(plant$last_watered)
  today <- Sys.Date()
  
  if (last_water_date == today) {
    cat("\n")
    cat("  Already watered today!\n")
    cat("  Your plant doesn't want to drown. \n")
    cat("  Come back tomorrow!\n\n")
    return(invisible(plant))
  }
  
  # Calculate bonus for consecutive days
  days_since_water <- days_since(plant$last_watered)
  water_points <- 15
  health_gain <- 20
  if (days_since_water == 1) {
    water_points <- water_points + 5  # Consecutive day bonus
    plant$achievements <- union(plant$achievements, "daily_waterer")
  }
  
  plant$last_watered <- today
  plant$points <- plant$points + water_points
  plant$health <- min(100, plant$health + health_gain)
  plant$times_watered <- plant$times_watered + 1
  plant$last_visited <- Sys.time()
  
  # Check for achievements
  if (plant$times_watered == 10 && !"10_waters" %in% plant$achievements) {
    plant$achievements <- c(plant$achievements, "10_waters")
  }
  if (plant$times_watered == 50 && !"50_waters" %in% plant$achievements) {
    plant$achievements <- c(plant$achievements, "50_waters")
  }
  
  save_plant(plant, file)
  
  # Get stage info
  old_stage <- get_stage(plant$points - water_points)
  new_stage <- get_stage(plant$points)
  leveled_up <- new_stage$stage > old_stage$stage
  
  # Display
  cat("\n")
  cat("  β•”", hline("═", 44), "β•—\n", sep = "")
  cat("  β•‘            WATERING TIME!                 β•‘\n")
  cat("  β•š", hline("═", 44), "╝\n", sep = "")
  cat("\n")

  cat("    Watering '", plant$name, "'...\n\n", sep = "")
  art <- get_plant_art(plant)
  for (line in art) {
    cat("        ", line, "\n", sep = "")
  }

  cat(sprintf("  +%d growth points! (Total: %d)\n", water_points, plant$points))
  cat(sprintf("  Health: %d%% %s\n", plant$health, strrep("β–ˆ", plant$health %/% 10)))

  if (leveled_up) {
    cat("\n")
    cat("  β•”", hline("═", 44), "β•—\n", sep = "")
    cat("     LEVEL UP! Your plant is now: ", new_stage$emoji, " ", new_stage$name, "\n", sep = "")
    cat("  β•”", hline("═", 44), "β•—\n", sep = "")
  }
  cat("\n  ", get_encouragement(), "\n\n", sep = "")
}


# --  Check on your desk plant
CheckPlant <- function(file = .plant_file) {
  plant <- get_plant(file)
  if (is.null(plant)) {
    cat("\n    No plant found! Start one with PlantNew()\n\n")
  }
  
  days_without_water <- days_since(plant$last_watered)
  if (days_without_water > 1 && plant$health > 0) {
    # Lose health for each day without water (after first day)
    health_loss <- (days_without_water - 1) * 10
    plant$health <- max(0, plant$health - health_loss)
  }
  
  session_start <- Sys.getenv("R_SESSION_TMPDIR")  # Unique per session
  
  visit_points <- 2
  plant$points <- plant$points + visit_points
  plant$sessions <- plant$sessions + 1
  plant$last_visited <- Sys.time()

  if (plant$sessions == 100 && !"100_sessions" %in% plant$achievements) {
    plant$achievements <- c(plant$achievements, "100_sessions")
  }
  
  save_plant(plant, file)
  
  # Get stage info
  stage <- get_stage(plant$points)
  days_alive <- days_since(plant$planted_date)
  
  # Display
  cat("\n")
  cat("  β•”", hline("═", 44), "β•—\n", sep = "")
  cat("  β•‘          PLANT STATUS REPORT              β•‘\n")
  cat("  β•š", hline("═", 44), "╝\n", sep = "")
  cat("\n")
  
  # Show art
  art <- get_plant_art(plant)
  for (line in art) {
    cat("        ", line, "\n", sep = "")
  }
  
  cat("\n")
  cat("  ", hline("─", 44), "\n", sep = "")
  cat(sprintf("  Name:        %s\n", plant$name))
  cat(sprintf("  Age:         %d day%s old\n", days_alive, if(days_alive != 1) "s" else ""))
  cat(sprintf("  Stage:       %s %s\n", stage$emoji, stage$name))
  cat(sprintf("  Points:      %d / %d (next stage)\n", 
              plant$points, 
              ifelse(stage$stage < 7, growth_stages$min_points[stage$stage + 2], "MAX")))
  cat("  ", hline("─", 44), "\n", sep = "")
  
  # Health bar
  health_bar <- paste0(
    strrep("β–ˆ", plant$health %/% 10),
    strrep("β–‘", 10 - plant$health %/% 10)
  )
  cat(sprintf("  Health:      [%s] %d%%\n", health_bar, plant$health))
  cat(sprintf("  Status:      %s\n", get_health_message(plant$health)))
  cat("  ", hline("─", 44), "\n", sep = "")
  
  # Water status
  days_since_water <- days_since(plant$last_watered)
  if (days_since_water == 0) {
    water_status <- " Watered today!"
  } else if (days_since_water == 1) {
    water_status <- " Watered yesterday"
  } else {
    water_status <- sprintf("   %d days without water!", days_since_water)
  }
  cat(sprintf("  Last water:  %s\n", water_status))
  cat("  ", hline("─", 44), "\n", sep = "")

  # Stats
  cat(sprintf("  Waterings:   %d total\n", plant$times_watered))
  cat(sprintf("  Visits:      %d R sessions\n", plant$sessions))
  
  # Achievements
  if (length(plant$achievements) > 0) {
    cat("  ", hline("─", 44), "\n", sep = "")
    cat("    Achievements:\n")
    achievement_names <- list(
      "daily_waterer" = "Daily Waterer - Watered on consecutive days",
      "10_waters" = "Hydration Helper - Watered 10 times",
      "50_waters" = "Water Master - Watered 50 times",
      "100_sessions" = "Dedicated Parent - 100 R sessions"
    )
    for (ach in plant$achievements) {
      if (ach %in% names(achievement_names)) {
        cat(sprintf("  %s\n", achievement_names[[ach]]))
      }
    }
  }
  
  if (days_since_water >= 1 && plant$health > 0) {
    cat("  Don't forget to WaterPlant()!\n\n")
  }
  
}


# Quick status - just show the plant
Plant <- function(file = .plant_file) {
  plant <- get_plant(file)
  
  if (is.null(plant)) {
    cat("\n No plant! Use PlantNew() to start.\n\n")
  }
  
  # Update health decay silently
  days_without_water <- days_since(plant$last_watered)
  if (days_without_water > 1 && plant$health > 0) {
    health_loss <- (days_without_water - 1) * 10
    plant$health <- max(0, plant$health - health_loss)
    save_plant(plant, file)
  }
  
  stage <- get_stage(plant$points)
  art <- get_plant_art(plant)
  
  cat("\n")
  for (line in art) {
    cat("        ", line, "\n", sep = "")
  }
  cat("\n")
  cat(sprintf("  %s %s | Day %d | %d%% health\n",
              stage$emoji, plant$name, 
              days_since(plant$planted_date),
              plant$health))
  cat("\n")
}


PlantDelete <- function(file = .plant_file, confirm = TRUE) {
  
  plant <- get_plant(file)
  
  if (is.null(plant)) {
    cat("\n  No plant to delete.\n\n")
    return(invisible(FALSE))
  }
  
  if (confirm) {
    cat("\n  Are you sure you want to delete '", plant$name, "'?\n", sep = "")
    cat("  This cannot be undone! (yes/no): ")
    response <- tolower(readline())
    if (response != "yes") {
      cat("  Keeping your plant safe.  \n\n")
    }
  }
  
  file.remove(file)
  cat("\n Goodbye, ", plant$name, "...\n", sep = "")
  cat(" Use PlantNew() to start fresh.\n\n")
}

#optional
.onAttach <- function() {
  plant <- get_plant()
  if (!is.null(plant) && plant$health > 0) {
    stage <- get_stage(plant$points)
    message(sprintf(" Your plant '%s' (%s) is waiting! Use CheckPlant() to visit.",
                    plant$name, stage$name))
  }
}

# Run welcome check
local({
  plant <- get_plant()
  if (!is.null(plant) && plant$health > 0) {
    days_without_water <- days_since(plant$last_watered)
    stage <- get_stage(plant$points)
    cat(sprintf("\n '%s' says hello! (%s, Day %d)\n",
                plant$name, stage$name, days_since(plant$planted_date)))
    if (days_without_water >= 2) {
      cat(sprintf("It's been %d days without water!\n", days_without_water))
    }
    cat("\n")
  }
})


PRO-tips:

As always, the complete code is available on GitHub in Β Useless_R_function repository. The Plant simulator isΒ hereΒ (filename:Β plant_tame.R).

Check the repository for future updates!

Stay healthy and happy R-coding!

Disclaimer: all ASCII art was done by GPT, as well as plant names and ASCII game progress.

To leave a comment for the author, please follow the link and comment on their blog: R – TomazTsql.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Exit mobile version