Creating en 5: e utgåva döserulle i AnyDice: hur man hanterar bonusskador vid första träffen?

4

Jag har försökt att skapa en komplett sannolikhetsgenerator för femte upplagans D & D-attackrullar i AnyDice, inklusive funktioner som fördel / nackdel, olika kritintervall, bonuskritisk skada etc. Jag har stött på ett problem med förmågor som bara hanterar ytterligare skador en gång per runda som Sneak Attack.

pseudokod:

391.133

Vad jag vill göra är att lägga till bonusskador på den första träffen om det finns några. Eftersom AnyDice-funktioner inte påverkar externa variabler och slutar omedelbart på "resultat" och booleska funktioner fungerar endast med siffror och inte uppsättningar eller tärningar, har jag inte hittat ett bra sätt att göra en variabel som kan exakt berätta om en träff landade redan .

Om jag anger ett värde inom attackfunktionen, kommer det att återställas till det värdet i nästa omgång av slingan. Variablerna inuti en funktion ändras inte utanför, så att det inte kommer att göra skillnad om att ringa variabeln igen (om inte jag saknat något) och jag har inte hittat ett sätt att göra det som en del av hjälparfunktionen. Eventuella råd skulle vara mycket uppskattade.

    
uppsättning Daniel 15.01.2018 03:10

1 svar

5

Vad du behöver göra är att skriva en funktion för att "frysa" dina dödsrullar och kalla den rekursivt för varje (misslyckad) attack, något som detta grundläggande exempel :

391.133

Huvudfunktionen [multiattack N:n times] kallar bara hjälparfunktionen [roll FIRSTHIT:n to multiattack N:n times] . Det första argumentet ( FIRSTHIT ) till hjälparfunktionen ges av [attack ATK] , vilket returnerar antalet gånger som skadan ska tillämpas för denna attackrulle (dvs 0 för miss, 1 för hit och 2 för crit).

Eftersom detta argument markeras som numeriskt (med :n ) i funktionsdefinitionen, kallar AnyDice internt hjälparfunktionen med varje eventuellt returvärde på [attack ATK] , och väger dess resultat enligt sannolikheten för resultaten. Detta betyder att även om resultatet av [attack ATK] är en (förspänd) form, är inuti hjälparfunktionen "fryst" som FIRSTHIT , vilket är ett fast tal och kan sålunda användas t.ex. i if villkor. (Se avsnittet "Funktioner" i AnyDice-dokumentationen för mer information.)

Självhjälpsfunktionen kontrollerar bara om attacken träffar (dvs om FIRSTHIT > 0 ), och i så fall gäller skadan från denna attack (plus bonusen, om någon) och från eventuella återstående attacker (utan bonus). Å andra sidan, om attacken saknar, kallar hjälparfunktionen istället rekursivt med en ny attackrulle och med antalet kvarvarande attacker minskar med en.

Ps. Uttrycket FIRSTHIT d (DMG + BONUS) kan vara värt att diskutera: det rullar helt enkelt normal- och bonusskadorna FIRSTHIT gånger och lägger till resultaten. (Å andra sidan skulle FIRSTHIT * (DMG + BONUS) ge resultatet av att rulla normal- och bonusskadorna en gång och multiplicera den med FIRSTHIT .) Om FIRSTHIT är lika med 2 kommer skadorna (inklusive eventuella Sneak Attack eller andra bonusar) att rullas två gånger, som 5e-reglerna anger för kritiska träffar. Likaså , inuti slingan, ger uttrycket [attack ATK] d DMG resultatet att rulla DMG antingen noll, en eller två gånger, enligt de sannolikheter som ges av [attack ATK] -rullen.

Faktum är att hela koden i det if FIRSTHIT > 0 villkorliga blocket kan ersättas med följande uttryck:

391.133

Här ger (N-1) d ([attack ATK] d DMG) resultatet av att utföra N-1 attackrullar, där varje attack behandlar DMG skador [attack ATK] gånger.

Det är också värt att notera att AnyDice normalt tillåter funktionssamtal att nestas endast 10 nivåer djupa (varav en används av omslagsfunktionen i det här exemplet och ett av [attack ATK] -samtalet). Om du vill stödja flera attacker med mer än 8 på varandra följande attackrullar, bör du öka recursionsgränsen, t.ex. så här:

391.133

För att göra en sådan djup rekursion köra inom rimlig tid är det emellertid viktigt att se till att hjälparfunktionen bara kallar sig ett specifikt värde för dess icke- fasta parametrar (t.ex. i det här fallet när FIRSTHIT är noll). Om det kan rekryteras på två eller flera möjliga ingångar, kommer antalet olika möjliga sekvenser av rekursiva samtal som AnyDice behöver kontrollera växa exponentiellt med rekursionsdjupet.

Det här är anledningen till att koden ovan använder resultat av [attack ATK] (som endast kan vara 0, 1 eller 2) som parameter för hjälparfunktionen, istället för att passera attackrullen ATK ( vilket kan vara allt från 1 till 20) direkt som en parameter. Medan gör det på det sättet kommer det att fungera OK för ett litet antal attacker, det blir väldigt långsamt väldigt snabbt, eftersom rekursionsdjupet ökar.

Vi kan faktiskt optimera koden lite mer genom att förberäkna resultaten från [attack ATK] och tilldela dem till en global anpassad dö . Detta skulle påskynda koden något, och också spara en funktion nesting nivå.

Observera också att koden ovan förutsätter att bonusskador alltid kommer att tillämpas på den första framgångsrika träffen, oavsett om det är en krit eller inte. Det är förmodligen optimalt i de flesta praktiska situationer, men i princip kan en spelare med ett tillräckligt stort antal attacker kvar och en tillräckligt hög träff och / eller kritisk förändring kanske vara bättre att spara sin Sneak Attack för senare om de rullar en tidig, icke-kritisk träff, antar de antagligen att en krit (eller åtminstone en annan vanlig träff) senare antas. Modellering av sådana avancerade strategier är möjligt i AnyDice (vanligtvis också via rekursion), men mer komplicerat.

    
svaret ges 15.01.2018 15:58