proc iml; start chkstats(za,n,line1,line2, flag); reset noname spaces=0; flag=0; do i = 1 to n; zi=za[i,]; if min(zi)<0 | ssq(round(zi,1)-zi)>1e-4 then do; print "ERROR with player" i ": stats must be nonnegative integers"; flag=1; end; if abs(zi[,1]-sum(zi[,(2:6)||(8:10)]))>1e-4 then do; print "ERROR with player" i ": not all plate appearances accounted for"; flag=1; end; if zi[,7]>sum(zi[,3:6]) then do; print "ERROR with player" i ": RSH cannot be > total hits"; flag=1; end; if (zi[,11]=1)+(zi[,11]=2)+(zi[,11]=3)=0 then do; print "ERROR with player" i ": SI must be 1, 2, or 3"; flag=1; end; if (zi[,12]=1)+(zi[,12]=2)+(zi[,12]=3)=0 then do; print "ERROR with player" i ": PI must be 1, 2, or 3"; flag=1; end; end; start sortvec(xu, x); x=xu; x[rank(x),]=xu; finish; run sortvec(line1, line1s); run sortvec(line2, line2s); if ssq(line1s-(1:n)`)>1e-4 then do; print "ERROR: 1st lineup is invalid"; flag=1; end; if ssq(line2s-(1:n)`)>1e-4 then do; print "ERROR: 2nd lineup is invalid"; flag=1; end; finish; start tagup(base0,runs0,rsh,si,pi,seed, base1,runs1); base1=base0; runs1=runs0; coin=-99; if base0[3,1]>0 then do; call ranuni(seed,coin); si=base0[3,1]; crit=(si=3)#((pi>=2)+0.5#(pi=1))+ (si=2)#0.3#pi+(si=1)#0.075#pi#pi; if coin=2 & base0[3,1]=0 then do; call ranuni(seed,coin); if coin0 then do; if base0[2,1]>0 then do; if base0[3,1]>0 then runs1=runs0+1; do i = 2 to 3; base1[i,1]=base0[i-1,1]; end; end; if base0[2,1]=0 then base1[2,1]=base1[1,1]; end; base1[1,1]=si; finish; start single(base0,runs0,rsh,si,pi,seed, base1,runs1); base1=base0; runs1=runs0; coin=-99; if base1[3,1]>0 then do; runs1=runs1+1; base1[3,1]=0; end; if base1[2,1]>0 then do; call ranuni(seed,coin); if coin=pi#base1[2,1]/6 then do; base1[3,1]=base1[2,1]; base1[2,1]=0; end; end; if base1[1,1]>0 then do; if base1[3,1]>0 then base1[2,1]=base1[1,1]; if base1[3,1]=0 then do; call ranuni(seed,coin); if coin0 then do; runs1=runs1+1; base1[i,1]=0; end; end; if result>3 & base1[1,1]>0 then do; runs1=runs1+1; base1[1,1]=0; end; if result=3 & base1[1,1]>0 then do; call ranuni(seed,coin); if coin<(base1[1,1]-1)/3 then do; runs1=runs1+1; base1[1,1]=0; end; if coin>=(base1[1,1]-1)/3 then do; base1[3,1]=base1[1,1]; base1[1,1]=0; end; end; if result=3 then base1[2,1]=si; if result=4 then base1[3,1]=si; if result=5 then runs1=runs1+1; finish; start game(z,n,seed, runs); coin=-99; result=-99; runs=0; inning=1; out=0; base=j(3,1,0); bat=1; do while(inning<=7 & out<=2); w=z[bat,1]; b1=z[bat,2]; b2=z[bat,3]; b3=z[bat,4]; hr=z[bat,5]; rsh=z[bat,6]; go=z[bat,7]; fo=z[bat,8]; po=z[bat,9]; si=z[bat,10]; pi=z[bat,11]; lforce=0; if base[1,1]>0 then do; lforce=base[1,1]; play=2; if base[2,1]>0 then do; lforce=base[2,1]; play=3; if base[3,1]>0 then do; lforce=base[3,1]; play=4; end; end; end; lfree=0; if lforce=0 then do; if base[3,1]>0 then lfree=base[3,1]; if base[3,1]=0 then lfree=base[2,1]; end; if lforce=1 & go+0.3*b1/pi<1 then do; b1=b1-0.3#b1/pi; go=go+0.3#b1/pi; end; /* Slow runner at 1st -- Robbed of Single! */ if out<2 & lfree=3 & w+0.1#go<1 then do; go=go-0.2#go; w=w+0.2#go; end; /* Fast runner at 3rd -- Fielder's Gift! */ call rantbl(seed,w,b1,b2,b3,hr,go,fo,po,result); if result=1 then do; run walk(base,runs,si, base1,runs1); base=base1; runs=runs1; end; if result=2 then do; run single(base,runs,rsh,si,pi,seed, base1,runs1); base=base1; runs=runs1; end; if result>=3 & result<=5 then do; run exbases(result,base,runs,si,seed, base1,runs1); base=base1; runs=runs1; end; if result>=6 then do; out=out+1; if result=6 & lforce>0 then do; temp=play; do while(temp>2); base[temp-1,1]=base[temp-2,1]; temp=temp-1; end; base[1,1]=si; if base[1,1]>0 & si<3 then do; call ranuni(seed,coin); if (si=2 & coin<0.2) | (si=1 & coin<0.4) then do; out=out+1; base[1,1]=0; end; end; end; if result=7 & out<=2 then do; run tagup(base,runs,rsh,si,pi,seed, base1,runs1); base=base1; runs=runs1; end; if out>=3 then do; out=0; inning=inning+1; base=j(3,1,0); end; end; bat=mod(bat,n)+1; end; finish; /*-------------------------------------------------------*/ /* The LINEUPS Module */ /* */ /* This module simulates softball games for 2 given */ /* offensive lineups (batting orders) of N players based */ /* on statistics provided from an input data set. An */ /* output data set is then produced, which consists of 2 */ /* variables: the lineup indicator variable and the */ /* number of runs scored for the simulated games. */ /* Various statistical analyses can then be performed on */ /* the data set to determine which, if any, lineup is */ /* "better." */ /* */ /* */ /* Input: */ /* */ /* 1) DSIN, Data set name containing individual player */ /* statistics, which MUST use the following */ /* variable names: */ /* PA -- number of plate appearances */ /* W -- number of walks */ /* B1 -- number of singles */ /* B2 -- number of doubles */ /* B3 -- number of triples */ /* HR -- number of home runs */ /* RSH -- number of hits to the right side of 2B */ /* GO -- number of ground outs */ /* FO -- number of fly outs */ /* PO -- number of popouts and strikeouts */ /* SI -- Speed Index (1=Slow, 2=Average, 3=Fast) */ /* PI -- Power Index (1=Weak, 2=Average, 3=Strong) */ /* */ /* 2) SIMSIZE, A positive integer specifying the total */ /* number of simulated games (half for each lineup). */ /* */ /* 3) SEED, A nonnegative integer used in generating */ /* the simulated games. If SEED is set to 0, then */ /* clocktime is used. SEED should be nonzero when */ /* you want to be able to reproduce your results. */ /* */ /* 4) LINE1, an Nx1 vector consisting of an arrangement */ /* of the integers from 1 to N. It indicates the */ /* ordering of the players from the input data set */ /* to be considered "lineup 1." */ /* */ /* 5) LINE2, an Nx1 vector consisting of an arrangement */ /* of the integers from 1 to N. It indicates the */ /* ordering of the players from the input data set */ /* to be considered "lineup 2." */ /* */ /* Returns: */ /* */ /* DSOUT, Data set name containing SIMSIZE observations */ /* and 2 variables. The variable names are LINEUP and */ /* RUNS, representing the number of runs scored by a */ /* given lineup for each simulated game */ /* */ /* The general syntax for running the module is */ /* RUN LINEUPS('DSIN',SIMSIZE,SEED,LINE1,LINE2,'DSOUT'); */ /* */ /* Module details: */ /* */ /* The LINEUPS module calls other previously defined */ /* modules, but you only need to load these modules. */ /* Once loaded, you never need to explicitly call them. */ /* The modules used by LINEUPS are */ /* CHKSTATS -- checks input data set statistics and */ /* lineups for errors. */ /* TAGUP -- handles baserunners tagging up after a */ /* fly out & adjusts runs scored. */ /* WALK -- handles baserunners advancing after a */ /* walk & adjusts runs scored. */ /* SINGLE -- handles baserunners advancing after a */ /* single & adjusts runs scored. */ /* EXBASES -- handles baserunners advancing after a */ /* double, triple, or home run & adjusts */ /* runs scored. */ /* GAME -- simulates a team's offensive performance */ /* for one 7-inning softball game. */ /*-------------------------------------------------------*/ start lineups(dsin,simsize,seed,line1,line2, dsout); call execute ("use ", dsin, " ;"); read all var{pa} into z0; read all var{w} into z1; read all var{b1} into z2; read all var{b2} into z3; read all var{b3} into z4; read all var{hr} into z5; read all var{rsh} into z6; read all var{go} into z7; read all var{fo} into z8; read all var{po} into z9; read all var{si} into z10; read all var{pi} into z11; z=z1||z2||z3||z4||z5||z6||z7||z8||z9||z10||z11; za=z0||z; n=nrow(za); run chkstats(za,n,line1,line2, flag); if flag=1 then return; z[,(1:5)||(7:9)]=z[,(1:5)||(7:9)]/(z0@j(1,8,1)); z[,6]=z[,6]/((z[,2:5])[,+]); x=z[line1,]; y=z[line2,]; halfsim=round(simsize/2,1); do i=1 to halfsim; run game(x,n,seed, runs); rvec=rvec//runs; end; do i=1 to halfsim; run game(y,n,seed, runs); rvec=rvec//runs; end; mat=(j(halfsim,1,1)//j(halfsim,1,2))||rvec; chead={" Lineup" "Runs"}; call execute ("create ", dsout, " from mat[colname=chead];"); append from mat; call execute("close ", dsout, " ;"); finish;