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;