View difference between Paste ID: Mkmy9b1q and PnJrYpjB
SHOW: | | - or go back to the newest paste.
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-
# 
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-
# This test
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
```