Advertisement
JebusMcAzn

vardorvis sim (R markdown)

Aug 12th, 2023
2,437
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ---
  2. title: "vard sim"
  3. author: "jeb"
  4. date: "8/7/2023"
  5. output: html_document
  6. ---
  7.  
  8. ```{r constants}
  9. # lord forgive me for how sloppy this code becomes the further down you scroll
  10. library(dplyr)
  11. set.seed(69420)
  12.  
  13. # Fang on slash
  14. # 1-handed weapons assumed to be used with Avernic
  15. gear_values <- matrix(c(111, 75, 121, 18, 12, 10, 8, 12, 6, 8, 88, 56, 132, 48,
  16.                         104, 110, 134, 0, 0, 15, 10, 0, 20, 0, 109, 57, 132, 49),ncol=14,byrow=TRUE)
  17. colnames(gear_values) <- c("fang", "scythe", "axe", "torva", "bandos", "torture", "fury", "ultor", "bellator", "berserker", "vw", "claws", "bgs", "dds")
  18. rownames(gear_values) <- c("strength", "attack")
  19.  
  20. str_level <- floor(118 * 1.23) + 3 + 8
  21. atk_level <- floor(118 * 1.2) + 8
  22.  
  23. vard_max_hp <- 700
  24. vard_base_def <- 215
  25. vard_slash_def <- 65
  26. vard_def <- vard_base_def
  27. vard_lowered_def <- vard_base_def
  28. ```
  29.  
  30. ```{r top level attack functions}
  31. # the basic attack roll is the same no matter what weapon you use
  32. # returns TRUE on a hit and FALSE on a miss
  33. attack <- function(atk_roll_max, vard_hp, weapon) {
  34.   vard_def <- vard_base_def - (floor((vard_max_hp - vard_hp)/10)) # comment out this line to disable vard's defense decay, useful to compare to DPS calc
  35.  
  36.   # BGS mechanics: assume that if the lowered defense is lower than Vard's naturally decaying defense, it takes priority, but does not stack
  37.   if (vard_lowered_def < vard_def) {
  38.     vard_def <- vard_lowered_def
  39.   }
  40.  
  41.   def_roll_max <- (vard_def + 9) * (vard_slash_def + 64)
  42.  
  43.   # these are not ints but it shouldn't really matter
  44.   atk_roll <- runif(1, 0, atk_roll_max)
  45.   def_roll <- runif(1, 0, def_roll_max)
  46.  
  47.   if (atk_roll > def_roll) {
  48.     return (TRUE)
  49.   }
  50.  
  51.   # fang rolls again if the first misses
  52.   if (weapon == "fang") {
  53.     atk_roll <- runif(1, 0, atk_roll_max)
  54.   }
  55.  
  56.   return (atk_roll > def_roll)
  57. }
  58.  
  59. attack_with <- function(gear, hp) {
  60.   if (!is.vector(gear) || length(gear) != 4) {
  61.     stop("must pass gear loadout as a vector of 4 strings")
  62.   }
  63.  
  64.   # assumes infernal cape, ferocious gloves, and prims for a total 27 strength and 22 attack bonus
  65.   gear_bonuses <- rowSums(gear_values[,gear])
  66.   str_bonus <- gear_bonuses[["strength"]] + 27
  67.   atk_bonus <- gear_bonuses[["attack"]] + 22
  68.  
  69.   if (gear[1] == "fang") {
  70.     return (attack_with_fang(str_bonus, atk_bonus, hp))
  71.   } else if (gear[1] == "scythe") {
  72.     return (attack_with_scythe(str_bonus, atk_bonus, hp))
  73.   } else if (gear[1] == "axe") {
  74.     return (attack_with_axe(str_bonus, atk_bonus, hp))
  75.   } else {
  76.     stop("must pass fang, scythe, or axe as weapon parameter")
  77.   }
  78. }
  79.  
  80. attack_with_spec <- function(gear, hp) {
  81.   # assumes infernal cape, ferocious gloves, and prims for a total 27 strength and 22 attack bonus
  82.   gear_bonuses <- rowSums(gear_values[,gear])
  83.   str_bonus <- gear_bonuses[["strength"]] + 27
  84.   atk_bonus <- gear_bonuses[["attack"]] + 22
  85.  
  86.   if (gear[1] == "vw") {
  87.     return (spec_with_vw(str_bonus, atk_bonus, hp))
  88.   } else if (gear[1] == "claws") {
  89.     return (spec_with_claws(str_bonus, atk_bonus, hp))
  90.   } else if (gear[1] == "bgs") {
  91.     return (spec_with_bgs(str_bonus, atk_bonus, hp))
  92.   } else if (gear[1] == "dds") {
  93.     return (spec_with_dds(str_bonus, atk_bonus, hp))
  94.   } else if (gear[1] == "axe") {
  95.     return (spec_with_axe(str_bonus, atk_bonus, hp))
  96.   } else {
  97.     stop("must pass vw, claws, bgs, dds, or axe as weapon parameter in first element of 'gear' vector")
  98.   }
  99. }
  100. ```
  101.  
  102. ```{r basic autoattacks}
  103. attack_with_fang <- function(str_bonus, atk_bonus, vard_hp) {
  104.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  105.   atk_roll_max <- atk_level * (atk_bonus + 64)
  106.  
  107.   # fang max hit mechanics
  108.   minhit <- floor(0.15 * maxhit)
  109.   maxhit <- maxhit - minhit
  110.  
  111.   hit <- attack(atk_roll_max, vard_hp, "fang")
  112.  
  113.   damage <- 0
  114.   if (hit) {
  115.     damage <- sample(minhit:maxhit, 1)
  116.   }
  117.   return (damage)
  118. }
  119.  
  120. attack_with_scythe <- function(str_bonus, atk_bonus, vard_hp) {
  121.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  122.   minhit <- 0
  123.  
  124.   atk_roll_max <- atk_level * (atk_bonus + 64)
  125.  
  126.   # against a single 2x2 enemy, scythe hits twice with both hits rolling accuracy independently and the second hit capped at 1/2 the max hit of the first
  127.   hit <- attack(atk_roll_max, vard_hp, "")
  128.   damage <- 0
  129.   if (hit) {
  130.     damage <- damage + sample(minhit:maxhit, 1)
  131.   }
  132.   hit <- attack(atk_roll_max, vard_hp, "")
  133.   if (hit) {
  134.     damage <- damage + sample(minhit:floor(maxhit/2), 1)
  135.   }
  136.   return (damage)
  137. }
  138.  
  139. attack_with_axe <- function(str_bonus, atk_bonus, vard_hp) {
  140.   # axe passive boosts strength additively with prayer
  141.   str_level <- floor(118 * (1.23 + 0.06 * axe_stacks)) + 3 + 8
  142.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  143.   minhit <- 0
  144.  
  145.   atk_roll_max <- atk_level * (atk_bonus + 64)
  146.   hit <- attack(atk_roll_max, vard_hp, "")
  147.  
  148.   damage <- 0
  149.   if (hit) {
  150.     damage <- sample(minhit:maxhit, 1)
  151.    
  152.   }
  153.   if (axe_stacks < 5) {
  154.     axe_stacks <<- axe_stacks + 1
  155.   }
  156.   return (damage)
  157. }
  158. ```
  159.  
  160. ```{r special attacks}
  161. spec_with_vw <- function(str_bonus, atk_bonus, vard_hp) {
  162.   # voidwaker spec guarantees a hit between 50-150% of its max hit
  163.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  164.   minhit <- floor(maxhit * 0.5)
  165.   maxhit <- maxhit + minhit
  166.  
  167.   damage <- sample(minhit:maxhit, 1)
  168.  
  169.   return (damage)
  170. }
  171.  
  172. spec_with_claws <- function(str_bonus, atk_bonus, vard_hp) {
  173.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  174.   minhit <- 0
  175.  
  176.   atk_roll_max <- atk_level * (atk_bonus + 64)
  177.  
  178.   # Claws roll accuracy up to four times depending on how many hitsplats miss
  179.   # The following is a DPS approximation from the wiki
  180.   # With this implementation it appears to outperform VW which should not be the case
  181.   # but I cba to fix it since we don't know the exact mechanics
  182.   hit <- attack(atk_roll_max, vard_hp, "")
  183.   if (hit) {
  184.     minhit <- maxhit
  185.     maxhit <- 2 * maxhit
  186.   } else {
  187.     hit <- attack(atk_roll_max, vard_hp, "")
  188.     if (hit) {
  189.       minhit <- floor(0.75 * maxhit)
  190.       maxhit <- floor(1.75 * maxhit)
  191.     } else {
  192.       hit <- attack(atk_roll_max, vard_hp, "")
  193.       if (hit) {
  194.         minhit <- floor(0.5 * maxhit)
  195.         maxhit <- floor(1.5 * maxhit)
  196.       } else {
  197.         hit <- attack(atk_roll_max, vard_hp, "")
  198.         if (hit) {
  199.           minhit <- floor(0.25 * maxhit)
  200.           maxhit <- floor(1.25 * maxhit)
  201.         }
  202.       }
  203.     }
  204.   }
  205.  
  206.   damage <- sample(minhit:maxhit, 1)
  207.   return (damage)
  208. }
  209.  
  210. spec_with_bgs <- function(str_bonus, atk_bonus, vard_hp) {
  211.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  212.   minhit <- 0
  213.  
  214.   atk_roll_max <- atk_level * (atk_bonus + 64)
  215.  
  216.   # BGS spec does 21% more damage and doubles the maximum attack roll
  217.   maxhit <- floor(maxhit * 1.21)
  218.   atk_roll_max <- atk_roll_max * 2
  219.  
  220.   hit <- attack(atk_roll_max, vard_hp, "")
  221.   damage <- 0
  222.   if (hit) {
  223.     damage <- sample(minhit:maxhit, 1)
  224.   }
  225.   vard_lowered_def <<- vard_def - damage
  226.   return (damage)
  227. }
  228.  
  229. spec_with_dds <- function(str_bonus, atk_bonus, vard_hp) {
  230.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  231.   minhit <- 0
  232.  
  233.   atk_roll_max <- atk_level * (atk_bonus + 64)
  234.  
  235.   # DDS spec does 15% more damage with a 15% higher maximum attack roll
  236.   maxhit <- floor(maxhit * 1.15)
  237.   atk_roll_max <- atk_roll_max * 1.15
  238.  
  239.   hit <- attack(atk_roll_max, vard_hp, "")
  240.   damage <- 0
  241.   if (hit) {
  242.     damage <- damage + sample(minhit:maxhit, 1)
  243.   }
  244.   hit <- attack(atk_roll_max, vard_hp, "")
  245.   if (hit) {
  246.     damage <- damage + sample(minhit:maxhit, 1)
  247.   }
  248.   return (damage)
  249. }
  250.  
  251. spec_with_axe <- function(str_bonus, atk_bonus, vard_hp) {
  252.   # axe spec boosts strength additively with prayer
  253.   str_level <- floor(118 * (1.23 + 0.06 * axe_stacks)) + 3 + 8
  254.   maxhit <- floor((str_level * (str_bonus + 64) + 320)/640)
  255.   minhit <- 0
  256.  
  257.   atk_roll_max <- atk_level * (atk_bonus + 64)
  258.  
  259.   # axe spec increases the maximum attack roll by 6% per stack
  260.   atk_roll_max <- atk_roll_max * (1 + 0.06 * axe_stacks)
  261.  
  262.   hit <- attack(atk_roll_max, vard_hp, "")
  263.   damage <- 0
  264.   if (hit) {
  265.     damage <- sample(minhit:maxhit, 1)
  266.   }
  267.   axe_stacks <<- 0
  268.   return (damage)
  269. }
  270. ```
  271.  
  272. ```{r farming Vardorvis}
  273. # given one of the kill_vard functions below,
  274. # a gear setup of weapon, armor, ring, and optional spec weapon,
  275. # and a total N number of kills to be done,
  276. # returns the average time (in seconds) to kill Vard with that setup
  277. farm_vard <- function(FUN, gear, N) {
  278.   TTK <- 0
  279.   for(i in 1:N) {
  280.     TTK <- TTK + FUN(gear)
  281.   }
  282.   return(TTK / N)
  283. }
  284.  
  285. # given a gear setup, kills vard once
  286. kill_vard <- function(gear) {
  287.   time <- 0
  288.   hp <- vard_max_hp
  289.   vard_lowered_def <- vard_base_def
  290.   axe_stacks <<- 0
  291.  
  292.   while (hp > 0) {
  293.     hp <- hp - attack_with(gear, hp)
  294.     time <- time + 5
  295.   }
  296.   # the last hit kills vard instantly, we do not need to wait for its cooldown
  297.   time <- time - 4
  298.   return (time*0.6)
  299. }
  300.  
  301. # given a gear setup and either vw or claws as gear[5], kills vard once by speccing immediately
  302. kill_vard_spec_start <- function(gear) {
  303.   time <- 0
  304.   hp <- vard_max_hp
  305.   vard_lowered_def <<- vard_base_def
  306.   axe_stacks <<- 0
  307.  
  308.   hp <- hp - attack_with_spec(gear[c(5, 2, 3, 4)], hp)
  309.   time <- time + 4
  310.   if(gear[5] == "bgs") {
  311.     time <- time + 2
  312.   }
  313.  
  314.   while (hp > 0) {
  315.     hp <- hp - attack_with(gear[-5], hp)
  316.     time <- time + 5
  317.   }
  318.   time <- time - 4
  319.   return (time*0.6)
  320. }
  321.  
  322. # given a gear setup and either vw or claws as gear[5], kills vard once by speccing once vard is < 200 HP
  323. kill_vard_spec_enrage <- function(gear) {
  324.   time <- 0
  325.   hp <- vard_max_hp
  326.   vard_lowered_def <<- vard_base_def
  327.   axe_stacks <<- 0
  328.   has_specced <- FALSE
  329.   while (hp > 0) {
  330.     hp <- hp - attack_with(gear[-5], hp)
  331.     time <- time + 5
  332.     if (hp < 200 && !has_specced) {
  333.       hp <- hp - attack_with_spec(gear[c(5, 2, 3, 4)], hp)
  334.       time <- time + 4
  335.       has_specced <- TRUE
  336.     }
  337.   }
  338.   time <- time - 4
  339.   return (time*0.6)
  340. }
  341.  
  342. # axe spec into spec weapon into scythe switch, supposedly "optimal"
  343. kill_vard_optimal <- function(gear) {
  344.   time <- 0
  345.   hp <- vard_max_hp
  346.   vard_lowered_def <<- vard_base_def
  347.   axe_stacks <<- 0
  348.   while (hp > 150) {
  349.     hp <- hp - attack_with(gear[-5], hp)
  350.     time <- time + 5
  351.   }
  352.   # axe spec (maxes 77)
  353.   hp <- hp - attack_with_spec(gear[c(1, 2, 3, 4)], hp)
  354.   time <- time + 5
  355.   if(hp <= 0) {
  356.     time <- time - 4
  357.     return (time*0.6)
  358.   }
  359.  
  360.   # vw spec (maxes 76)
  361.   hp <- hp - attack_with_spec(gear[c(5, 2, 3, 4)], hp)
  362.   time <- time + 4
  363.   if(hp <= 0) {
  364.     time <- time - 3
  365.     return (time*0.6)
  366.   }
  367.  
  368.   #finish with scythe
  369.   gear <- c("scythe", "torva", "bellator", "fury")
  370.   while (hp > 0) {
  371.     hp <- hp - attack_with(gear, hp)
  372.     time <- time + 5
  373.   }
  374.   time <- time - 4
  375.   return (time*0.6)
  376. }
  377.  
  378. # Kills vard switching the main weapon to switchwep at a certain HP threshold. Tests every threshold from 100-200 in multiples of 10
  379. # and returns the fastest TTK as well as the optimal threshold to switch weapons
  380. farm_vard_switch_wep <- function(gear, N, switchwep) {
  381.   HPs <- seq(160, 160, 10)
  382.   fastest_TTK <- 69420
  383.   fastest_threshold <- 0
  384.  
  385.   for(index in 1:length(HPs)) {
  386.     TTK <- 0
  387.     for(i in 1:N) {
  388.       TTK <- TTK + kill_vard_switch_wep(gear, HPs[index], switchwep)
  389.     }
  390.     if(TTK / N < fastest_TTK) {
  391.       fastest_TTK <- TTK / N
  392.       fastest_threshold <- HPs[index]
  393.     }
  394.   }
  395.  
  396.   return(c(fastest_TTK, fastest_threshold))
  397. }
  398.  
  399. kill_vard_switch_wep <- function(gear, threshold, switchwep) {
  400.   time <- 0
  401.   hp <- vard_max_hp
  402.   vard_lowered_def <<- vard_base_def
  403.   axe_stacks <<- 0
  404.  
  405.   while (hp > threshold) {
  406.     hp <- hp - attack_with(gear, hp)
  407.     time <- time + 5
  408.   }
  409.   gear <- c(switchwep, "torva", "bellator", "fury")
  410.   while (hp > 0) {
  411.     hp <- hp - attack_with(gear, hp)
  412.     time <- time + 5
  413.   }
  414.   time <- time - 4
  415.   return (time*0.6)
  416. }
  417. ```
  418.  
  419. ```{r use cases}
  420. N <- 50000
  421. sets <- data.frame(
  422.   Weapon = rep(c("fang", "scythe", "axe"), each=6),
  423.   Armor = rep(rep(c("torva", "bandos"), each=3), 3),
  424.   Ring = rep(c("bellator", "ultor", "berserker"), 6),
  425.   stringsAsFactors=FALSE
  426. )
  427.  
  428. # This test compares all 18 combinations of weapon, armor, and ring in the dataframe 'sets' and runs N Vardorvis kills each, storing the results in 'sets'
  429. # TTK: The average time to kill Vardorvis, in seconds
  430. # DPS: The average damage per second
  431. TTK <- numeric(nrow(sets))
  432. DPS <- numeric(nrow(sets))
  433. for (i in 1:nrow(sets)) {
  434.   gear <- c(t(sets[i,]), "fury")
  435.   time <- farm_vard(kill_vard, gear, N)
  436.   TTK[i] <- time
  437.   DPS[i] <- vard_max_hp / time
  438.   cat(sprintf("Setup %s, %s, %s takes %#.2f seconds\n", gear[1], gear[2], gear[3], time))
  439. }
  440. sets2 <- cbind(sets, TTK, DPS)
  441.  
  442. # This test assumes torva + bellator, and tests using 1 dragon claws or 1 voidwaker spec along with the fang, scythe, or axe
  443. # For each combination, it tests the average TTK if you spec at the start of the fight, or if you spec when Vard falls below 200 HP (roughly when he enrages)
  444. sets_spec <- data.frame(
  445.   Weapon = rep(c("fang", "scythe", "axe"), each=3),
  446.   Spec = rep(c("vw", "claws", "bgs"), 3),
  447.   stringsAsFactors=FALSE
  448. )
  449. TTK_spec_start <- numeric(nrow(sets_spec))
  450. DPS_spec_start <- numeric(nrow(sets_spec))
  451. TTK_spec_enrage <- numeric(nrow(sets_spec))
  452. DPS_spec_enrage <- numeric(nrow(sets_spec))
  453. for (i in 1:nrow(sets_spec)) {
  454.   gear <- c(sets_spec[i,1], "torva", "bellator", "fury", sets_spec[i,2])
  455.   time <- farm_vard(kill_vard_spec_start, gear, N)
  456.   TTK_spec_start[i] <- time
  457.   DPS_spec_start[i] <- vard_max_hp / time
  458.   cat(sprintf("Setup %s, %s (spec start), takes %#.2f seconds\n", gear[1], gear[5], time))
  459.  
  460.   if(gear[5] == "bgs")
  461.     next
  462.  
  463.   time <- farm_vard(kill_vard_spec_enrage, gear, N)
  464.   TTK_spec_enrage[i] <- time
  465.   DPS_spec_enrage[i] <- vard_max_hp / time
  466.   cat(sprintf("Setup %s, %s (spec enrage), takes %#.2f seconds\n", gear[1], gear[5], time))
  467. }
  468.  
  469. sets_spec <- cbind(sets_spec, TTK_spec_start, TTK_spec_enrage, old_TTK=sets$TTK[rep(c(1, 7, 13), each=3)])
  470. sets_spec <- sets_spec %>% mutate(timesave = old_TTK - TTK_spec_start)
  471.  
  472. gear <- c("axe", "torva", "bellator", "fury", "vw")
  473. time <- farm_vard(kill_vard_spec_enrage, gear, N)
  474. cat(sprintf("Setup %s, %s (spec enrage, axe spec), takes %#.2f seconds\n", gear[1], gear[5], time))
  475.  
  476. gear <- c("axe", "torva", "bellator", "fury")
  477. time <- farm_vard(kill_vard, gear, N) # manually change starting axe_stacks to 3
  478. cat(sprintf("Setup %s (start with 3 axe stacks), takes %#.2f seconds\n", gear[1], time))
  479.  
  480. # Axe spec maxes 77 and VW maxes 76. send both specs at 150 HP and finish with scythe
  481. gear <- c("axe", "torva", "bellator", "fury", "vw")
  482. time <- farm_vard(kill_vard_optimal, gear, N)
  483. cat(sprintf("Setup %s (at 150 HP, axe spec into VW spec into scythe), takes %#.2f seconds\n", gear[1], time))
  484.  
  485. gear <- c("fang", "bandos", "berserker", "fury", "bgs")
  486. time <- farm_vard(kill_vard_spec_start, gear, N)
  487. cat(sprintf("Setup %s, %s (spec start), takes %#.2f seconds\n", gear[1], gear[5], time))
  488.  
  489. # This test finds the optimal threshold at which to switch from fang to scythe and returns the average kill time with this strat
  490. gear <- c("fang", "torva", "bellator", "fury")
  491. ret <- farm_vard_switch_wep(gear, 50000, "scythe")
  492. switch_scythe_ttk <- ret[1]
  493. switch_scythe_threshold <- ret[2]
  494. cat(sprintf("The optimal HP to switch from fang to scythe is at %d HP. The average time to kill is %f\n", ret[2], ret[1]))
  495. ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement