ExcellencyModelling

From Exalted - Unofficial Wiki
Jump to: navigation, search

As the ExcellencyMath page shows, calculating distributions for dice is rather tedious, especially at high pools.

To make this process easier, here is some R code which can be used to calculate success distributions for arbitrary numbers of dice and excellencies. These functions use three state multinomial calculations to generate their distributions, so they can get a little slow at high pools.

For information on R, including how to install and run, visit the R homepage.

All functions return percent chances of generating at least the given number of successes, and assume solar costing. General syntax for functions:

nd(dice, [limit]) - normal heroic roll. dice is number of dice, limit is how high to go.
nond(dice, [limit]) - normal unheroic roll. dice is number of dice, limit is how high to go.
fe(dice, motes, [limit]) - first excellency. dice is base pool, motes is how many motes to sink, limit is how high to go.
se(dice, motes, [limit]) - second excellency. dice is base pool, motes is how many motes to sink, limit is how high to go.
te(dice, [limit]) - third excellency. dice is base pool, limit is how high to go.
compe(dice, motes, [limit]) - compares the previous 5 roll types. dice is base pool, motes is how many motes to sink, limit is how high to go.

## Function to generate states of dice
# Exalted has four dice states, only three of which are relevant
#at any one time:
# 0- A '1'.  In the absence of successes, this generates a botch
# 1- A failure, typically 2-6
# 2- A success, typically 7-9
# 3- A '10'.  For heroic mortals and above, this gives 2 successes.
#
#	As 1s don't interact with 10s, a three state table is sufficient
#to cover all the possible states from any particular roll.

rollstates <- function(dice) {
	tmp<-t(as.matrix(expand.grid(0:dice, 0:dice)))
	tmp<-tmp[, colSums(tmp)<=dice]
	tmp<-rbind(tmp, dice - colSums(tmp))
	dimnames(tmp)<-list(c("f","s","110"),NULL)
	tmp  }


#	Caluculates numbers of successes each state will generate and then returns the
# probability that those states will be generated by some dice.  Can be used for botches
# also, but defaults to successes.
success<- function(successes, dice, cumulative = TRUE, multipliers = c(0,1,2), distribution = c(6,3,1)) {
	states<-rollstates(dice)
	suc<-states*multipliers
	stateset<-if (cumulative) states[, colSums(suc)>=successes] else states[,colSums(suc)==successes]
	round(sum(apply(as.matrix(stateset), 2, function(x) dmultinom(x, prob=distribution)))*100,3) }
	
#Normal rolls
nd<- function(dic, limit=2*dic) {
	#Botch
	X<-success(1,dic,TRUE,c(1,0,-9001), c(1,5,4))
	#Failure
	X<-rbind(X, success(0,dic, FALSE))
	#Success table
	X<-rbind(X, as.matrix(apply(as.matrix(1:limit), 1, function(x) success(x, dic))))
	#Rename
	dimnames(X)<-list(c("Botch","Fail", 1:limit), "Normal")
	X }

#Non-heroic rolls
nond<- function(dic, limit=2*dic) {
	#Botch
	X<-success(1,dic,TRUE,c(1,0,-9001), c(1,5,4))
	#Failure
	X<-rbind(X, success(0,dic, FALSE))
	#Success table
	X<-rbind(X, as.matrix(apply(as.matrix(1:limit), 1, function(x) success(x, dic, TRUE, c(0,1,1), c(6,3,1)))))
	#Rename
	dimnames(X)<-list(c("Botch","Fail", 1:limit), "Unheroic")
	X }
	
	
#First excellency
fe<-function(pool, motes, limit=2*(pool+motes)) {
	X<-nd(pool+motes, limit)
	colnames(X)<-"First"
	X}

#Second excellency
se<-function(pool, motes, limit=2*pool+floor(motes/2)) {
	#Number of successes
	suc<-floor(motes/2)
	if (suc>0) {
		#Botch chance is 0, autosuccesses
		X<-rbind(0,0, as.matrix(rep.int(100, suc)))
		#Normal after that, but no botches
		norm<-nd(pool, limit-suc)
		X<-rbind(X, as.matrix(norm[rownames(norm)!="Botch"&rownames(norm)!="Fail",])) }
		#Otherwise very normal
	else X<-nd(pool,limit)
	dimnames(X)<-list(c("Botch","Fail", 1:limit), "Second")	
	X}

#Rollagain
rollagain<- function (percent, bad)  {
	p<-percent/100
	if (bad) round(100*(p)^2,3)
	else round(100*(p+(1-p)*p), 3) }
	

#Third excellency
te<-function(pool, limit=2*pool) {
	base<-nd(pool, limit)
	#Bad ones first
	third<-as.matrix(rollagain(base[rownames(base)=="Botch",], TRUE))
	third<-rbind(third,as.matrix(rollagain(base[rownames(base)=="Fail",], TRUE)))
	#Then the good ones
third<-rbind(third,as.matrix(apply(as.matrix(base[rownames(base)!="Fail"&rownames(base)!="Botch"],), 1, rollagain, bad=FALSE)))
	dimnames(third)<-list(c("Botch", "Fail", 1:limit),"Third")
	third}
	
#compare 
compe<-function(pool, motes, limit=(pool+motes)*2) {
	cbind(nond(pool, limit),nd(pool, limit), fe(pool, motes, limit), se(pool, motes, limit), te(pool, limit)) }