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;