BookmarkSubscribeRSS Feed
kbry1180
Calcite | Level 5

I am doing a poker simulation in SAS.  So far I have created a deck, shuffled and dealt 5 card hands (no draw), and kept track of pairs, trips, 4 of a kind, and flushes using proc freq.  Now I am trying to get SAS to recognize straights.  I have assigned numerical values to the card ranks (2=2, J=11, A=14, etc.).  My professor suggested using an Array to do this process and showed me a sample code.  The first part is the code I am using where I assigned numerical values.  The other data steps (data 1, 2, and 3) are the array.  Could someone offer suggestions as to how I would use the array with the code I am using?  

%let decksize=52;
%let handsize=5;
%let nsims=10000;
%let nhands=5;

data _null_;
ncards=&handsize*&nhands;
call symput("ncards", ncards);
run;

data deck;
call streaminit(&seed);
do suit="C","D","H","S";
do value="2","3","4","5","6","7","8","9","T","J","Q","K","A";
if value="A" then numval=14;
else if value="2" then numval=2;
else if value="3" then numval=3;
else if value="4" then numval=4;
else if value="5" then numval=5;
else if value="6" then numval=6;
else if value="7" then numval=7;
else if value="8" then numval=8;
else if value="9" then numval=9;
else if value="T" then numval=10;
else if value="J" then numval=11;
else if value="Q" then numval=12;
else numval=13;
 if suit="C" then suitval=1;
 else if suit="D" then suitval=2;
 else if suit="H" then suitval=3;
 else suitval=4;
order=rand("uniform");
output;
end;
end;
run;

*Array commands shown by professor;
data one;
  do i=2 to 6;
    if i^=6 then cards=i;
      else cards=6;
    output;
  end;
run;

data two;
  set one;
  array hand{5} c1-c5;
  retain hand;
  hand{_n_}=cards;
  if _n_=5;
run;  

data three;
  set two;
  straight=1;
  array hand{5} c1-c5;
  do i= 1 to 4;
    if hand{i+1}-hand{i}^=1 then do;
      card=i;
      straight=0;
      leave;
    end;
  end;  
  if card=4 then if hand{5}-hand{4}=9 then straight=1;
run;  

commands my professor showed me.  

8 REPLIES 8
ballardw
Super User

I don't know what that last line of "if card=4" is for.

This identifies straights using that approach.

 

data example;
   do c1= 2 to 14;
   do c2= 2 to 14;
   do c3= 2 to 14;
   do c4= 2 to 14;
   do c5= 2 to 14;
      output;
   end;
   end;
   end;
   end;
   end; 
run;
data three;
  set example;
  straight=1;
  array hand{5} c1-c5;
  call sortn(of hand(*));

  do i= 1 to 4;
    if hand{i+1}-hand{i}^=1 then do;
      card=i;
      straight=0;
      leave;
    end;
  end;  
run;  

but for further hands you'll need to do something to keep the suit with the rank.

 

kbry1180
Calcite | Level 5

Hello Ballard, 

The last line is accounting for Aces being valued 1 and 14.  So the way he wrote it was that whenever there was a sequence:  2-3-4-5-A, it would calculate that last difference as 9 and count it as a straight as well.  

 

Before seeing your reply, I ran a program that did not result in any errors or warnings, yet I am not quite sure it is doing what I intended i.e. identify straights.  Here is what I have so far:

%let seed=1210; 
%let decksize=52;
%let handsize=5;
%let nsims=10000;
%let nhands=10;

data _null_;
ncards=&handsize*&nhands;
call symput("ncards", ncards);
run;

data deck;
call streaminit(&seed);
do suit="C","D","H","S";
do value="2","3","4","5","6","7","8","9","T","J","Q","K","A";
if value="A" then numval=14;
else if value="2" then numval=2;
else if value="3" then numval=3;
else if value="4" then numval=4;
else if value="5" then numval=5;
else if value="6" then numval=6;
else if value="7" then numval=7;
else if value="8" then numval=8;
else if value="9" then numval=9;
else if value="T" then numval=10;
else if value="J" then numval=11;
else if value="Q" then numval=12;
else numval=13;
 if suit="C" then suitval=1;
 else if suit="D" then suitval=2;
 else if suit="H" then suitval=3;
 else suitval=4;
order=rand("uniform");
output;
end;
end;
run;

proc sort data=deck;
by order;

/*proc print data=deck;
run;*/

proc surveyselect data=deck noprint
method=srs
seed=&seed
sampsize=&ncards
rep=&nsims
out=cards;
run;

/*proc print data=cards;
run; */

data hands;
set cards;
hand=ceil(_n_/&handsize);
run;

data three;
  set hands;
  straight=1;
  array draw{&handsize} c1-c&handsize;
  do i= 1 to 4;
    if draw{i+1}-draw{i}^=1 then do;
      card=i;
      straight=0;
      leave;
    end;
  end;  
  if card=4 then if draw{5}-draw{4}=9 then straight=1;
run;  
      
  
/*proc print data=hands;
run;*/  

/*proc print data=three;
run;*/


proc freq data=hands noprint ;
tables value/out=freqval;
by hand;
run;

proc freq data=hands noprint;
tables suit/out=freqsui;
by hand;
run;

proc freq data=three noprint;
tables straight/out=freqstr;
by hand;
run; 

data repthree;
set freqstr;
simno=ceil(hand/&nhands);
run;

data rephand;
set freqval;
simno=ceil(hand/&nhands);

/*proc print data=repthree;
run;*/

proc means data=rephand noprint;
var count;
by simno;
output out=maxnum max=kind;
run;

/*proc freq data=maxnum;
tables kind;
run;*/

proc means data=rephand noprint;
var count;
by simno;
output out=win max=winkind;
run;

proc means data=repthree noprint;
var straight;
by simno;
output out=maxstraight max=winstraight;
run;

proc freq data=win;
tables winkind;
run;

data rephandsuit;
set freqsui;
simno=ceil(hands/&nhands);
run;

proc means data=rephandsuit noprint;
var count;
by simno;
output out=win max=winflush;
run;

/*proc print data=rephandsuit;
run;*/

proc freq data=win;
tables winflush;
run;

proc freq data=maxstraight;
tables winstraight;
run;
kbry1180
Calcite | Level 5

Quick follow up:

I did not get any error messages but the ouput for straights (and flushes) do not seem right.  For instance, it shows that a hand with 5 matching suits (flush) had a frequency of 100% and that hands that were not straights had a frequency of 100%.    So I either used the wrong code to track these hands or the output I created using proc freq is incorrect.  

art297
Opal | Level 21

I would go about it slighly differently. I only took the following as far as checking for Straights. You can find more ideas for checking the other scenarios in the thread at: https://communities.sas.com/t5/SAS-Procedures/Allocating-Values-to-Poker-Hands/td-p/146459

 

Here is my suggested approach (Note: You'd have to create four subrankings based on suit and values. Thus a straight flush would actually have a range of 40 subranks to account for the possible range of values and suits. You could set them to have values of 9.01 to 9.40):

%let decksize=52;
%let handsize=5;
%let nsims=10000;
%let nhands=5;

/*missing the following*/
%let seed=0;

data _null_;
  ncards=&handsize*&nhands;
  call symput("ncards", ncards);
run;

data deck;
  call streaminit(&seed);
  length suit $1;
  do suit='C','D','H','S';
    do value=2 to 14;
      order=rand("uniform");
      output;
    end;
  end;
run;

proc sort data=deck;
  by order;
run;

data game (drop=value suit order);
  array handv(&handsize.);
  array hands(&handsize.) $;
  do hands=1 to &nhands.;
    do cards=1 to &handsize.;
      set deck;
      handv(cards)=value;
      hands(cards)=suit;
    end;
    call sortc(of hands(*));
    call sortn(of handv(*));

    /*create ranks based on*/
      /*9=Straight Flush*/
      /*8=Four of a Kind*/
      /*7=Full House*/
      /*6=Flush*/
      /*5=Straight*/
      /*4=Three of a kind*/
      /*3=Two pair*/
      /*2=Pair*/
      /*1=High card*/

    /* check for flush */
    if hands(1) eq hands(5) then rank=6;

    /* check for straight or straight flush */
    if handv(5)-handv(4) eq 1 and
       handv(4)-handv(3) eq 1 and
       handv(3)-handv(2) eq 1 and
       handv(2)-handv(1) eq 1 then do;
         if rank eq 6 then rank=9;
         else rank=5;
    end;
    else if handv(5) eq 14 then if handv(4)-handv(3) eq 1 and
            handv(3)-handv(2) eq 1 and
            handv(2)-1 eq 1 then do;
             if rank eq 6 then rank=9;
             else rank=5;
    end;
    /* check for four of a kind */
    else if (sum(of handv(*))-handv(1) eq (4*handv(5))) or
            (sum(of handv(*))-handv(5) eq (4*handv(1))) then rank=8;
  end;
run;

Art, CEO, AnalystFinder.com

kbry1180
Calcite | Level 5

Hello Art,

 

Thank you for your help.  I tried running your code and got some error messages.  Please excuse me if I forgot to do something that would account for these errors as I am new to SAS.

Here are the errors:

 90         data game (drop=value suit order);
 91           array handv(&handsize.);
 92           array hands(&handsize.) $;
 93           do hands=1 to &nhands.;
 ERROR: The variable type of hands is invalid in this context.
 93           do hands=1 to &nhands.;
                 _
                 133
 ERROR 133-185: A loop variable cannot be an array name or a character variable;  It must be a scalar numeric.
 
 ERROR: Illegal reference to the array hands.
 94             do cards=1 to &handsize.;
 95               set deck;
 96               handv(cards)=value;
 97               hands(cards)=suit;
 98             end;
 99             call sortc(of hands(*));
 100            call sortn(of handv(*));
art297
Opal | Level 21

Sorry, that was an error on my part. I posted without testing .. something one should never do! Try the following instead:

%let decksize=52;
%let handsize=5;
%let nsims=10000;
%let nhands=5;

/*missing the following*/
%let seed=0;

data _null_;
  ncards=&handsize*&nhands;
  call symput("ncards", ncards);
run;

data deck;
  call streaminit(&seed);
  length suit $1;
  do suit='C','D','H','S';
    do value=2 to 14;
      order=rand("uniform");
      output;
    end;
  end;
run;

proc sort data=deck;
  by order;
run;

data game (drop=value suit order);
  array handv(&handsize.);
  array hands(&handsize.) $;
  /* deal for each player and score their hand */
  do hand=1 to &nhands.;
    do cards=1 to &handsize.;
      set deck;
      handv(cards)=value;
      hands(cards)=suit;
    end;
    call sortc(of hands(*));
    call sortn(of handv(*));

    /*create ranks based on*/
      /*9=Straight Flush*/
      /*8=Four of a Kind*/
      /*7=Full House*/
      /*6=Flush*/
      /*5=Straight*/
      /*4=Three of a kind*/
      /*3=Two pair*/
      /*2=Pair*/
      /*1=High card*/

    /* check for flush */
    if hands(1) eq hands(5) then rank=6;

    /* check for straight or straight flush */
    if handv(5)-handv(4) eq 1 and
       handv(4)-handv(3) eq 1 and
       handv(3)-handv(2) eq 1 and
       handv(2)-handv(1) eq 1 then do;
         if rank eq 6 then rank=9;
         else rank=5;
    end;
    else if handv(5) eq 14 then if handv(4)-handv(3) eq 1 and
            handv(3)-handv(2) eq 1 and
            handv(2)-1 eq 1 then do;
             if rank eq 6 then rank=9;
             else rank=5;
    end;
    /* check for four of a kind */
    else if (sum(of handv(*))-handv(1) eq (4*handv(5))) or
            (sum(of handv(*))-handv(5) eq (4*handv(1))) then rank=8;
    output;
  end;
  stop;
run;

Art, CEO, AnalystFinder.com

kbry1180
Calcite | Level 5

Thank you again Art.  

art297
Opal | Level 21

Interestingly, this is the first time I've tried to write a program that ranks poker hands. My code isn't complete .. for one, it doesn't account for 2nd level ties (e.g.,  7 7 6 6 5 compared with 7 7 6 6 4).

 

However, in taking the code as far as I have, I discovered some things that you really ought to consider. Most importantly, ranking must be done using whole numbers .. otherwise you run into numeric precision issues. That is always a risk, with computers, attempting to do comparisons of calculations as if they were done using base 10 math.

 

Anyhow, here is what I've done so far:

%let decksize=52;
%let handsize=5;
%let nsims=10000;
%let nhands=5;

/*missing the following*/
%let seed=0;

data _null_;
  ncards=&handsize*&nhands;
  call symput("ncards", ncards);
run;

data deck;
  call streaminit(&seed);
  /*  set suit a card values*/
  /*  suits 1=clubs 2=diamonds 3=hearts 4=spades*/
  do suit=1 to 4;
    do value=2 to 14;
      order=rand("uniform");
      output;
    end;
  end;
run;

proc sort data=deck;
  by order;
run;

data hands (drop=value suit order hand cards);
  array handv(&handsize.);
  array hands(&handsize.);
  /* deal for each player */
  do hand=1 to &nhands.;
    do cards=1 to &handsize.;
      set deck;
      handv(cards)=value;
      hands(cards)=suit;
    end;
    call sortn(of hands(*));
    call sortn(of handv(*));
    output;
  end;
  stop;
run;

/*For testing purposes only: create test hands dataset
data hands;
  input handv1-handv5 hands1-hands5;
  cards;
2 3 4 5 14 1 1 1 1 1
10 11 12 13 14 1 1 1 1 1
9 10 11 12 13 4 4 4 4 4
8 10 11 12 13 4 4 4 4 4
7 9 9 9 9 1 1 2 3 4
4 5 6 7 8 1 1 2 3 4
2 3 4 4 4 1 1 2 3 4
5 6 6 6 7 1 2 2 3 4
8 9 9 9 10 10 2 2 3 3 4
5 6 7 7 7 1 2 3 3 4
2 5 5 6 6 2 2 3 3 4
2 3 4 4 5 1 1 1 2 3
9 10 11 12 14 1 2 2 3 4
4 5 6 8 9 1 1 1 2 3
;
*/

data game;
  set hands;
  array handv(&handsize.) handv1-handv5;
  array hands(&handsize.) hands1-hands5;

    /*create ranks based on*/
      /*900=Straight Flush*/
      /*800=Four of a Kind*/
      /*700=Full House*/
      /*600=Flush*/
      /*500=Straight*/
      /*400=Three of a kind*/
      /*300=Two pair*/
      /*200=Pair*/
      /*100=High card*/

  rank=0;
    /* check for flush */
  if hands(1) eq hands(5) then rank=600+handv(5);

    /* check for straight or straight flush */
  if (handv(5)-handv(4)) eq 1 and
     (handv(4)-handv(3)) eq 1 and
     (handv(3)-handv(2)) eq 1 and
     (handv(2)-handv(1)) eq 1 then do;
         if rank gt 600 then rank=900+handv(5);
         else rank=500+handv(5);
  end;
    /* check for an Ace thru 5 straight or straight flush*/
  else if handv(5) eq 14 and
          handv(4) eq 5 and
          handv(3) eq 4 and
          handv(2) eq 3 and
          handv(1) eq 2 then do;
           if rank gt 600 then rank=900+handv(4);
           else rank=500+handv(4);
  end;
    
  if rank lt 500 then do;
      /* check for four of a kind */
      if (sum(of handv(*))-handv(1)) eq (4*handv(5)) or
              (sum(of handv(*))-handv(5)) eq (4*handv(1)) then
              rank=800+handv(2);

      /* check for full house */
      else if (handv(1) eq handv(2) and handv(3)=handv(5)) or
              (handv(1)=handv(3) and handv(4)=handv(5)) then
              rank=700+handv(3);

      /* check for three of a kind */
      else if (sum(of handv(*))-handv(5)-handv(4)) eq (3*handv(1)) or
              (sum(of handv(*))-handv(1)-handv(5)) eq (3*handv(2)) or
              (sum(of handv(*))-handv(1)-handv(2)) eq (3*handv(3)) then
              rank=400+handv(3);

      /* check for two pairs */
      else if (handv(1) eq handv(2) and handv(3) eq handv(4)) or
           (handv(1) eq handv(2) and handv(4) eq handv(5)) or
           (handv(2) eq handv(3) and handv(4) eq handv(5)) then
              rank=300+handv(4);

      /* check for one pair */
      else if (handv(1) eq handv(2)) or
           (handv(2) eq handv(3)) or
           (handv(3) eq handv(4)) or
           (handv(4) eq handv(5)) then do;
        if handv(1) eq handv(2) then rank=200+handv(2);
        else if handv(2) eq handv(3) then rank=200+handv(3);
        else if handv(3) eq handv(4) then rank=200+handv(4);
        else if handv(4) eq handv(5) then rank=200+handv(5);
      end;

      /* check for high card */
      else rank=100+handv(4);
  end;
run;

Of course, the above isn't tested for accuracy .. that part is up to you.

 

Art, CEO, AnalystFinder.com

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

What is Bayesian Analysis?

Learn the difference between classical and Bayesian statistical approaches and see a few PROC examples to perform Bayesian analysis in this video.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 8 replies
  • 1504 views
  • 1 like
  • 3 in conversation