# assuming known team distributions, this code # simulates the performance of two competing units # number of sims to run nsims <- 10000 # possessions in each game nposs <- 100 # unit class setClass("unit", representation( # offensive rates # distribution of 2pt shots, 3pt shots, and turnovers on each play o_2pt = "numeric", o_3pt = "numeric", o_tov = "numeric", # FG% for 2pt and 3pt shots o_fgp_2pt = "numeric", o_fgp_3pt = "numeric", # distribution of 2pt and 3pt shots that have a shooting foul o_sf_2pt = "numeric", o_sf_3pt = "numeric", # free throw % o_ftp = "numeric", # offensive rebound % o_rebp = "numeric", # defensive rates # distribution of 2pt shots, 3pt shots, and turnovers on each play d_2pt = "numeric", d_3pt = "numeric", d_tov = "numeric", # FG% for 2pt and 3pt shots d_fgp_2pt = "numeric", d_fgp_3pt = "numeric", # distribution of 2pt and 3pt shots that have a shooting foul d_sf_2pt = "numeric", d_sf_3pt = "numeric", # defensive rebound % d_rebp = "numeric" )) home <- new("unit", o_2pt = 0.69, o_3pt = 0.16, o_tov = 0.15, o_fgp_2pt = 0.54, o_fgp_3pt = 0.41, o_sf_2pt = 0.136, o_sf_3pt = 0.006, o_ftp = 0.77, o_rebp = 0.244, d_2pt = 0.66, d_3pt = 0.175, d_tov = 0.165, d_fgp_2pt = 0.45, d_fgp_3pt = 0.30, d_sf_2pt = 0.119, d_sf_3pt = 0.014, d_rebp = 0.772) away <- new("unit", o_2pt = 0.645, o_3pt = 0.215, o_tov = 0.14, o_fgp_2pt = 0.56, o_fgp_3pt = 0.42, o_sf_2pt = 0.17, o_sf_3pt = 0.005, o_ftp = 0.85, o_rebp = 0.254, d_2pt = 0.66, d_3pt = 0.19, d_tov = 0.15, d_fgp_2pt = 0.5, d_fgp_3pt = 0.417, d_sf_2pt = 0.10, d_sf_3pt = 0.005, d_rebp = 0.763) "handle_fts" <- function(num,pr) { points <- 0 for (i in 1:num) { result <- rbinom(1,size=1,prob=pr) if (result) { # FTM points <- points+1 } last <- result } return(c(points,last)) } "run_sims" <- function(nsims,nposs,unit1,unit2) { # vectors to keep track of the efficiency for each simulation u1.eff <- c() u2.eff <- c() # keeps track of the number of "games" each unit wins u1.wins <- 0 u2.wins <- 0 # vectors to keep track of the margins of victory for each team u1.margins <- c() u2.margins <- c() for (i in 1:nsims) { # determine who has the initial possession has_pos <- rbinom(1,size=1,prob=0.5) # vectors to keep track of the points on each possession u1.points <- c() u2.points <- c() for (j in 1:(nposs*2)) { if (!has_pos) { # unit1 has possession has <- unit1 } else { # unit2 has possession has <- unit2 } # run through game logic go <- 1 points <- 0 while (go) { # this loop continues until the opposing unit obtains possession # determine what is going to happen: 2pt shot, 3pt shot, or turnover result <- rmultinom(1, size=1, prob=c(has @ o_2pt,has @ o_3pt,has @ o_tov)) # some useful variables reb <- 0 # handle result if (result[1,1]) { # 2pt shot # determine if the shot was made result <- rbinom(1,size=1,prob=has @ o_fgp_2pt) if (result) { # make; add 2 points points <- points+2 reb <- 0 go <- 0 } else { # miss reb <- 1 go <- 1 } # determine if there was a foul on the shot result <- rbinom(1,size=1,prob=has @ o_sf_2pt) if (result) { # handle the foul shots result <- handle_fts(num=2,pr=has @ o_ftp) points <- points+result[1] if (!result[2]) { # the last was missed; handle rebound to determine possession reb <- 1 } else { # the last was made; change of possession reb <- 0 go <- 0 } } if (reb) { # handle rebound result <- rbinom(1,size=1,prob=has @ o_rebp) if (result) { # offensive rebound go <- 1 } else { # defensive rebound; change of possession go <- 0 } } } else if (result[2,1]) { # 3pt shot # determine if the shot was made result <- rbinom(1,size=1,prob=has @ o_fgp_3pt) if (result) { # make; add 3 points points <- points+3 reb <- 0 go <- 0 } else { # miss reb <- 1 go <- 1 } # determine if there was a foul on the shot result <- rbinom(1,size=1,prob=has @ o_sf_3pt) if (result) { # handle the foul shots result <- handle_fts(num=3,pr=has @ o_ftp) points <- points+result[1] if (!result[2]) { # the last was missed; handle rebound to determine possession reb <- 1 } else { # the last was made; change of possession reb <- 0 go <- 0 } } if (reb) { # handle rebound result <- rbinom(1,size=1,prob=has @ o_rebp) if (result) { # offensive rebound go <- 1 } else { # defensive rebound; change of possession go <- 0 } } } else if (result[3,1]) { # turnover; change of possession go <- 0 } } # change possession and store data if (!has_pos) { has_pos <- 1 u1.points <- c(u1.points,points) } else { has_pos <- 0 u2.points <- c(u2.points,points) } } # end nposs sim; store efficiency u1.eff <- c(u1.eff,sum(u1.points)/nposs*100) u2.eff <- c(u2.eff,sum(u2.points)/nposs*100) if (sum(u1.points) > sum(u2.points)) { u1.wins <- u1.wins+1 } else if (sum(u2.points) > sum(u1.points)) { u2.wins <- u2.wins+1 } u1.margins <- c(u1.margins,sum(u1.points)-sum(u2.points)) u2.margins <- c(u2.margins,sum(u2.points)-sum(u1.points)) } cat("Unit1 Eff: mean=",mean(u1.eff),"; sd=",sd(u1.eff),"\n",sep="") cat("Unit2 Eff: mean=",mean(u2.eff),"; sd=",sd(u2.eff),"\n",sep="") cat("Unit1 Margins: mean=",mean(u1.margins),"; sd=",sd(u1.margins),"\n",sep="") cat("Unit2 Margins: mean=",mean(u2.margins),"; sd=",sd(u2.margins),"\n",sep="") cat("Unit1 Win%: ",u1.wins/(u1.wins+u2.wins),"\n",sep="") cat("Unit2 Win%: ",u2.wins/(u1.wins+u2.wins),"\n",sep="") } #away <- new("unit", o_2pt = 0.5, o_3pt = 0.3, o_tov = 0.2, o_fgp_2pt = 0.6, o_fgp_3pt = 0.4, # o_sf_2pt = 0.2, o_sf_3pt = 0.01, o_ftp = 0.75, o_rebp = 0.30, # d_2pt = 0.5, d_3pt = 0.3, d_tov = 0.2, d_fgp_2pt = 0.6, d_fgp_3pt = 0.4, # d_sf_2pt = 0.2, d_sf_3pt = 0.05, d_rebp = 0.70) "combine_units" <- function(unit1,unit2) { new_unit <- new("unit") # shot distribution new_unit@o_2pt = (unit1@o_2pt + unit2@d_2pt)/2 new_unit@o_3pt = (unit1@o_3pt + unit2@d_3pt)/2 new_unit@o_tov = (unit1@o_tov + unit2@d_tov)/2 # FG% new_unit@o_fgp_2pt = (unit1@o_fgp_2pt + unit2@d_fgp_2pt)/2 new_unit@o_fgp_3pt = (unit1@o_fgp_3pt + unit2@d_fgp_3pt)/2 # Pr(shooting foul) new_unit@o_sf_2pt = (unit1@o_sf_2pt + unit2@d_sf_2pt)/2 new_unit@o_sf_3pt = (unit1@o_sf_3pt + unit2@d_sf_3pt)/2 # rebounding % new_unit@o_rebp = (unit1@o_rebp + (1-unit2@d_rebp) )/2 # keep others new_unit@o_ftp = unit1@o_ftp # we don't care about the defensive items # d_2pt, d_3pt, d_tov, d_fgp_2pt, d_fgp_3pt, d_sf_2pt, d_sf_3pt, d_rebp cat("Dist:",new_unit@o_2pt,new_unit@o_3pt,new_unit@o_tov,"\n") cat("FG%:",new_unit@o_fgp_2pt,new_unit@o_fgp_3pt,"\n") cat("Shooting Foul%:",new_unit@o_sf_2pt,new_unit@o_sf_3pt,"\n") cat("OReb%:",new_unit@o_rebp,"\n") return(new_unit) } home.new <- combine_units(home,away) away.new <- combine_units(away,home) system.time(run_sims(nsims,nposs,home.new,away.new))