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.
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.
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;
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.
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
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(*));
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
Thank you again Art.
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
April 27 – 30 | Gaylord Texan | Grapevine, Texas
Walk in ready to learn. Walk out ready to deliver. This is the data and AI conference you can't afford to miss.
Register now and lock in 2025 pricing—just $495!
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.
Ready to level-up your skills? Choose your own adventure.