War Spoils Simulator

This page presents a python code that simulates spinning the War Spoils wheel and can be run here or using a compiler of your choice, the code being available below.

## Input parameter

By editing the respective equations in the code before clicking "Run", you can specify the amount of order, chaos and nature jewels (//) as well as  Power Essence that you start with.
Next, you type in the amount of your  Seals of Valor, the current level of your War Spoils (found inside the wheel - not at the end of the progress bar) and the number of spins that you already did with that level (found in the progress bar).
Also, type in the levels of your Champions. Those levels are stored for each faction within square brackets "[...]" in the order "common, common, rare, epic, legendary", where 0 denotes a champion not unlocked yet and -1 denotes that a champion of that rarity and faction doesn't exist (so don't change the -1).

Lastly, you can specify the number of simulations to be done.
If you type N=1, the code will run one simulation (see below) and tell you the outcome and the levels of the champions after leveling.
If you use a larger number, it will show you the total troops on average.

You don't need to change anything before "INPUT" or after "END INPUT".

## What the code does

The code runs a simulation in which it spins the War Spoils as often as your specified amount of seals allows.
Afterwards, it simulates leveling the champions as far as the respective faction jewels allow it, always picking the most efficient level in term of "power increase per jewels used".
In the end, it sums up the champions' powers to get the total troops that you would end up with in this simulation.

Note that the need of  Power Essence for every 10th level-up is ignored in this code. Thus, it gives you the "potential" total troops that you can reach with enough power essence.
The reason for ignoring this is mainly technical.

## How this can be helpful

If you have a rather large amount of seals - that would allow you to spin the wheel several times and/or unlock some new champions -, you can run a few simulations for each case and compare the expected troops.
Then you can decide whether you want to unlock a champion or use all seals to spin the War Spoils (keep in mind that this can also raise its level, making jewel/power essence deals better).

For example, if you have 3500  Seals of Valor and no champion unlocked yet, you can...

* use `seals=3500` and `oChampsStart=[1,1,-1,0,0]` to simulate using all seals to spin the wheel.
* use `seals=2500` and `oChampsStart=[1,1,-1,1,0]` to simulate unlocking the epic order champion for 1000 and then using the remaining 2500 seals on war spoils.

## The code

Here you can find the complete code in case the link above is broken or you want to copy it somewhere else.

```
import random
import math
import numpy as np

#############
### INPUT ###
#############

# current faction jewels/PE/seals in possession:
ojStart = 0
cjStart = 0
njStart = 0
peStart = 0
seals = 3500
# wheel info:
warSpoilsLvlStart = 1 # current wheel level
spinsDoneStart = 0 # spins already done within current level
# order of rarities: [c,c,r,e,l]
oChampsStart = [1,1,-1,0,0]
cChampsStart = [1,1,0,0,-1]
nChampsStart = [1,1,0,-1,0]

# ... average (expected) total troops

#################
### END INPUT ###
#################

##################
### TROOP DATA ###
##################
# troop growth
trConst = [98,147,196,245]
trFactor = [2,3,4,5]

# 2\*n^(1.5) + 8

oChamps = np.copy(oChampsStart)
cChamps = np.copy(cChampsStart)
nChamps = np.copy(nChampsStart)
totTr=np.zeros(N)

### starting from level n=1000
trSlopeHigh = [100,150,200,250] # = troops at level 1
trOffsetHigh = [36600,55000,73300,91600]
# cost = n\*65000 ???? TODO check

##################
### WHEEL DATA ###
##################
# spins needed for next level = current level + 9
spins=math.floor(seals/100) # spins to be done in this simulation
warSpoilsLvl=warSpoilsLvlStart
spinsDoneStart
scaling = 1 # multiple for jewels/PE

# 70 PE: 5%

#####################
### RESOURCE DATA ###
#####################

oj = ojStart
cj = cjStart
nj = njStart
pe = peStart

###########################
### spin the wheel once ###
###########################

def spin():
    global warSpoilsLvl,spinsDone,scaling,oj,cj,nj,pe
    chance = random.random()
    #print(chance)
    #print(oj)
    if chance <= 0.1:
        oj += math.floor(200\*scaling)
    elif chance <= 0.1+0.0667:
        oj += math.floor(400\*scaling)
    elif chance <= 0.1667+0.0333:
        oj += math.floor(600\*scaling)
    elif chance <= 0.2+0.1:
        cj += math.floor(200\*scaling)
    elif chance <= 0.3+0.0667:
        cj += math.floor(400\*scaling)
    elif chance <= 0.3667+0.0333:
        cj += math.floor(600\*scaling)
    elif chance <= 0.4+0.1:
        nj += math.floor(200\*scaling)
    elif chance <= 0.5+0.0667:
        nj += math.floor(400\*scaling)
    elif chance <= 0.5667+0.0333:
        nj += math.floor(600\*scaling)
    elif chance <= 0.6+0.05:
        pe += math.floor(35\*scaling)
    elif chance <= 0.65+0.05:
        pe += math.floor(55\*scaling)
    elif chance <= 0.7+0.05:
        pe += math.floor(70\*scaling)
    spinsDone += 1
    if spinsDone == warSpoilsLvl+9 and warSpoilsLvl<50:
        spinsDone=0
        warSpoilsLvl+=1
        #print("LEVEL " + str(warSpoilsLvl))
        getScaling()

### end spin function ###

########################
### level war spoils ###
########################
def getScaling():
    global warSpoilsLvl,scaling
    if warSpoilsLvl == 1:
        scaling=1
    elif warSpoilsLvl == 2:
        scaling=2
    elif warSpoilsLvl == 3:
        scaling=4
    elif warSpoilsLvl == 4:
        scaling=8
    else:
        scaling=2\*warSpoilsLvl\*\*1.5 - 8

########################
### output resources ###
########################

def printresources(spins):
    global oj,cj,nj,pe,warSpoilsLvl
    print("Possible outcome after "+str(spins)+" spins:")
    print("Order jewels: "+str(oj))
    print("Chaos jewels: "+str(cj))
    print("Nature jewels: "+str(nj))
    print("Power essence: "+str(pe))
    print("WARNING: power essence is ignored for leveling in this simulation. You might be missing this resource")
    print("War Spoils at level "+str(warSpoilsLvl))

########################
### reset everything ###
########################

def reset():
    global warSpoilsLvl,warSpoilsLvlStart,spinsDone,spinsDoneStart,scaling
    global oj,cj,nj,pe,ojStart,cjStart,njStart,peStart
    global oChamps,cChamps,nChamps
    global oChampsStart,cChampsStart,nChampsStart
    spinsDone=spinsDoneStart
    getScaling()
    oj=ojStart
    cj=cjStart
    nj=njStart
    pe=peStart
    oChamps = np.copy(oChampsStart)
    cChamps = np.copy(cChampsStart)
    nChamps = np.copy(nChampsStart)
    warSpoilsLvl=warSpoilsLvlStart

#######################
### level champions ###
#######################

# levels one hero, with best troops/jewels ratio

def levelupOrder():
    global oChamps,oj,oTry
    ratio=np.zeros(5)
    possible=0
    for i in [0,1,3,4]:
        rarity = 0
        if i==3:
            rarity = 2
        elif i==4:
            rarity = 3
        cost = math.floor(2\*oChamps[i]\*\*1.5 + 8)
        if cost>oj or oChamps[i]<=0:
            ratio[i]=0
        else:
            possible=1
            trNow = math.floor(trFactor[rarity]\*oChamps[i]\*\*1.5 + trConst[rarity])
            trNext = math.floor(trFactor[rarity]\*(oChamps[i]+1)\*\*1.5 + trConst[rarity])
            newTroops=trNext-trNow
            ratio[i]=newTroops/cost
    if possible==1:
        cheap=ratio.argmax()
        oj -= math.floor(2\*oChamps[cheap]\*\*1.5 + 8)
        oChamps[cheap]+=1
    else:
        oTry=0
def levelupChaos():
    global cChamps,cj,cTry
    ratio=np.zeros(5)
    possible=0
    for i in [0,1,2,3]:
        rarity = 0
        if i==2:
            rarity = 1
        elif i==3:
            rarity = 2
        cost = math.floor(2\*cChamps[i]\*\*1.5 + 8)
        if cost>cj or cChamps[i]<=0:
            ratio[i]=0
        else:
            possible=1
            trNow = math.floor(trFactor[rarity]\*cChamps[i]\*\*1.5 + trConst[rarity])
            trNext = math.floor(trFactor[rarity]\*(cChamps[i]+1)\*\*1.5 + trConst[rarity])
            newTroops=trNext-trNow
            ratio[i]=newTroops/cost
    if possible==1:
        cheap=ratio.argmax()
        cj -= math.floor(2\*cChamps[cheap]\*\*1.5 + 8)
        cChamps[cheap]+=1
    else:
        cTry=0
def levelupNature():
    global nChamps,nj,nTry
    ratio=np.zeros(5)
    possible=0
    for i in [0,1,2,4]:
        rarity = 0
        if i==2:
            rarity = 1
        elif i==4:
            rarity = 3
        cost = math.floor(2\*nChamps[i]\*\*1.5 + 8)
        if cost>nj or nChamps[i]<=0:
            ratio[i]=0
        else:
            possible=1
            trNow = math.floor(trFactor[rarity]\*nChamps[i]\*\*1.5 + trConst[rarity])
            trNext = math.floor(trFactor[rarity]\*(nChamps[i]+1)\*\*1.5 + trConst[rarity])
            newTroops=trNext-trNow
            ratio[i]=newTroops/cost
    if possible==1:
        cheap=ratio.argmax()
        nj -= math.floor(2\*nChamps[cheap]\*\*1.5 + 8)
        nChamps[cheap]+=1
    else:
        nTry=0

troopAverage=0
for n in range(0,N):
    reset()
    getScaling()
    for i in range(0,spins):
        spin()
    if N==1: printresources(spins)

    oTry=1
    cTry=1
    nTry=1

    while oTry==1:
        levelupOrder()
    if N==1:
        print("order jewels left: "+str(oj))
        print("order champions: "+str(oChamps))
    while cTry==1:
        levelupChaos()
    if N==1:
        print("chaos jewels left: "+str(cj))
        print("chaos champions: "+str(cChamps))
    while nTry==1:
        levelupNature()
    if N==1:
        print("nature jewels left: "+str(nj))
        print("nature champions: "+str(nChamps))

    totalTroops=0
    for i in range(0,5):
        name=""
        rarity = 0
                name="order common 1"
            elif i==1:
                name="order common 2"
            elif i==3:
                name="order epic"
                rarity=2
            elif i==4:
                name="order leg"
                rarity=3
            troops=math.floor(trFactor[rarity]\*oChamps[i]\*\*1.5 + trConst[rarity])
            totalTroops+=troops
            if N==1: print(name+", troops: "+str(troops))
    for i in range(0,5):
        name=""
        rarity = 0
                name="chaos common 1"
            elif i==1:
                name="chaos common 2"
            elif i==2:
                name="chaos rare"
                rarity=1
            elif i==3:
                name="chaos epic"
                rarity=2
            troops=math.floor(trFactor[rarity]\*cChamps[i]\*\*1.5 + trConst[rarity])
            totalTroops+=troops
            if N==1: print(name+", troops: "+str(troops))
    for i in range(0,5):
        name=""
        rarity = 0
                name="nature common 1"
            elif i==1:
                name="nature common 2"
            elif i==2:
                name="nature rare"
                rarity=1
            elif i==4:
                name="nature leg"
                rarity=3
            troops=math.floor(trFactor[rarity]\*nChamps[i]\*\*1.5 + trConst[rarity])
            totalTroops+=troops
            if N==1: print(name+", troops: "+str(troops))
    itercounter=""
    if n<10:
        if N>1: itercounter = " in simulation #"+str(n+1)
        print("total troops"+itercounter+": "+str(totalTroops))
    elif n==10:
        print("not showing results of "+str(N-n)+" more simulations...")
    troopAverage+=totalTroops
    totTr[n]=totalTroops

if N>1:
    avg=troopAverage/N
    print("average total troops ("+str(N)+" simulations): "+str(math.floor(avg)))
    sigma=0
    for n in range(0,N):
        sigma+=(totTr[n]-avg)\*\*2
    sigma=sigma/(N-1)
    sigma=np.sqrt(sigma)
    print("standard deviation: "+str(math.floor(sigma)))

```