War Spoils Simulator

From Mighty Party Wiki
Jump to navigation Jump to search

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 (Order Jewels/Chaos Jewels/Nature Jewels) as well as Power Essence Power Essence that you start with. Next, you type in the amount of your Seals of Valor 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 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 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 1000Seals of Valor 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
# champions. level 0 = locked, -1= doesn't exist
# 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]

# number of simulations to run:
N = 10
# for one simulation: show results (resources, levels)
# for multiple simulations: only show total troops and
# ... average (expected) total troops

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


##################
### TROOP DATA ###
##################
### before level 1000
# troop growth
trConst = [98,147,196,245]
trFactor = [2,3,4,5]
# troops at level n with rarity r =
# trFactor[r]*n^(1.5) + trConst[r]

# cost to level from n (to n+1) =
# 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]
# troops = trSlopeHigh[r] * n - trOffsetHigh[r]
# 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
# scaling per level:
# lvl 1: 1
# lvl 2: 2
# lvl 3: 4
# lvl 4: 8
# lvl5+: 2*lvl^(1.5) - 8

# chance jewels = 60% (20% per faction)
# 200 jewels: 10%
# 400 jewels: 6.67%
# 600 jewels: 3.33%

# chance PE: 15%
# 35 PE: 5%
# 55 PE: 5%
# 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
        if oChamps[i]>=1:
            if i==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
        if cChamps[i]>=1:
            if i==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
        if nChamps[i]>=1:
            if i==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)))