University of Virginia Computer Science CS150: Computer Science, Fall 2005 
Problem Set 2: Procedurally Predicting Poker Probabilities 
Due: Friday, 9 September Beginning of class 
For this problem set, you are required to work with an assigned partner. You will receive an email Thursday containing the partner assignments.PurposeBefore starting to work with your partner, you should go through questions 1 and 2 yourself on paper. When you meet with your partner, check if you made the same predicitions. If there are any discrepancies, try to decide which is correct before using DrScheme to evaluate the expressions.
You and your partner should work together on the rest of the assignment and turn in a single staped document containing both your answers to the questions 1 and 2, and one answer to the rest of the questions with both of your names on it. You should read the whole problem set yourself and think about the questions before beginning to work on them with your partner.
In addition to your partner, you may discuss this assignment with other students in the class and ask and provide help in useful ways. You may consult any outside resources you wish including books, papers, web sites and people except for materials from last year's CS200 course. If you use resources other than the class materials, indicate what you used along with your answer.
You are strongly encouraged to take advantage of the lab hours posted on the CS150 website.
These robots are much better than the average player ... It would for
sure make money online.
Phil Laak, Los Angeles Times, July 18, 2005.
In this problem set, you will develop procedures that can calculate odds for poker. Actually creating a poker bot involves making decisions based on incomplete information about what the other players have and how they behave. This is much harder than just calculating odds, but knowing the odds is important for any poker strategy.
In the game we will consider, Texas Hold 'Em (this is the poker variant that is used in most major tournaments) each player is dealt two hole cards. These are kept hidden from the other players and can only be used by the player who was dealt them. Then, five community cards are dealt. Every player may use these cards. There are betting rounds after the hold cards are dealt, and after the third, fourth, and final community cards.
At the end of the hand, each player makes their best fivecard hand using as many of their own hole cards as they want and the remaining cards from the community cards. So, a player may make a hand using just the five community cards (and none of their hole cards), either of their hole cards and any four of the community cards, or both of their hold cards and any three of the community cards.
To calculate the odds a player will win a hand, we need to know all possible hands that player could get and how many of them beat the other player's hand. For example, when there is one community card left to be dealt, that means we need to consider how many of the remaining cards in the deck will allow the player to make a hand that is better than the other players hand.
Reading: Before going further, you should have finished reading Structure and Interpretation of Computer Programs, Section 2.2. You do not need to read Sections 2.2.3 or 2.2.4, although you may find the examples in those sections helpful and enjoy reading the footnote on page 1278 about William Barton Rogers, a UVa professor who went on to found MIT. You can see Rogers' cabinet in the rotunda. Apparently, Professor Rogers was quite a teacher — here's a quote from one of his teaching evaluations when he was at UVa (from Francis H. Smith, History of the University of Virginia, 18191919, Vol. 2): Who can forget that stream of English undefiled, so smooth, so deep, and yet so clear, that passed from point to point with gentle touch, that commonly flowed along with the quiet of conscious power, yet sometimes became tumultuous with feeling, and then came the music of the cataract and the glory of the rainbow! 
The elements in a cons pair can be anything, even another cons pair. By using cons pairs inside cons pairs inside cons pairs we can build up complex data structures to represent any data we want.cons cell
car element cdr element
Question 1:
For this question you should not use the Scheme interpreter. For each
fragment below, either:

Scheme provides many useful procedures for manipulating lists. The ones we use in this assignment include:
Question 2:
For this question you should not use the Scheme interpreter. For each
expression below, either:
Assume the following definition is evaluated before each
expression below is evaluated: (define intsto3 (list 1 2 3))

After you have predicted how each expression evaluates, remember to meet with your partner and discuss your results. After you have done this, try evaluating them in DrScheme to check your predictions. If any evaluate differently than you expected, explain why DrScheme evaluates the express the way it does.
Download: Download ps2.zip
to your machine and unzip it into your home directory
J:\cs150\ps2 (it will take some time to download
because it contains many tile images, so start the download and continue
reading while it is finishing). See the Lab Guide
document for instructions on unziping files and creating directories.
This file contains:

(define (makecard rank suit) (cons rank suit)) (define cardrank car) (define cardsuit cdr)We use the numbers 2 through 10 to represent the normal card ranks, and 11 (Jack), 12 (Queen), 13 (King), and 14 (Ace) to represent the special cards. The definitions in poker.scm allow us to use the names Ace, King, Queen, and Jack for these ranks.
Question 3:
Define a procedure highercard? that takes two cards are
operands and evaluates to #t if and only if the first card has
a higher rank than the second card. Note that the card suit does not
matter in this comparison.
If your highercard? procedure is correct, you should get the following interactions:

Question 4:
Define a procedure sorthand that takes one operand that is a
list of cards, and evaluates to the cards sorted by decreasing card
value (the highest card should be first in the list).
If your sorthand procedure is correct, you should get the following interactions (the displaycards procedure provided in poker.scm makes it easier to see the cards; the sample hands are also defined in poker.scm): Hint: you should not need more than one line for your definition. 
Since the rankings of poker hands don't just depend on the card values, but on having pairs, triples, and quads of a given card, it will be more useful to sort the cards according to first the number of duplicates of each rank, and then by rank. For example, if the hand is Ac Js Jc 7d 4c the procedure sortbyranks should produce ((Js Jc) (Ac) (7d) (4c)) since the pair of Jacks are more important than the single A. Note that instead of being just a list of cards, the result is now a list of lists of cards.
The procedure sortbyranks is defined:
(define (sortbyranks cards) ;;; sorts cards into lists of cards of each rank, ordered by most ;;; cards and highest cards within group (sort (lambda (r1 r2) (if (= (length r1) (length r2)) (highercard? (car r1) (car r2)) (> (length r1) (length r2)))) (combineadjacentmatches ;; combine them into lists of matching rank samerank? (sorthand cards)))) ;; cards sorted by rank
Category  Description  Example 

Straight Flush  Five cards in sequence all of the same suit  Ks Qs Js 10s 9s 
FourofaKind ("Quads")  Four cards of the same rank  3h 3d 3c 3s Jd 
Full House  Three cards of matching rank and two different cards of matching rank  Ac As Ah 7d 7h 
Flush  Five cards of the same suit  Qh 10h 8h 3h 2h 
Straight  Five cards in sequence  9d 8h 7c 6h 5s 
Wheel Straight  A5 straight  5d 4h 3c 2h As 
ThreeofaKind ("Trips")  Three cards of matching rank  5d 5h 5c Kh Js 
Two Pair  Two different pairs of matching rank  Jd Jh 5c 5h As 
Pair  Two cards of matching rank  8d 8h Ac 7h 3s 
High Card  Anything else  Kd 9h 7c 5h 2s 
(Note: we've listed wheel straight as a separate hand category, even though it is usually listed just as a straight. It is the only straight that can use A as a low card, and the lowest possible straight.)
We have provided the higherhand? procedure that defines the poker rules:
(define (higherhand? hand1 hand2) (cond ((straightflush? hand1) (or (not (straightflush? hand2)) (and (straightflush? hand2) (highersimilarhand? hand1 hand2)))) ((fourofakind? hand1) (or (and (not (straightflush? hand2)) (not (fourofakind? hand2))) (and (fourofakind? hand2) (highersimilarhand? hand1 hand2)))) ((fullhouse? hand1) (or (and (not (straightflush? hand2)) (not (fourofakind? hand2)) (not (fullhouse? hand2))) (and (fullhouse? hand2) (highersimilarhand? hand1 hand2)))) ((flush? hand1) (and (not (beatsflush? hand2)) (or (and (flush? hand2) (highersimilarhand? hand1 hand2)) (not (flush? hand2))))) ((wheelstraight? hand1) (not (or (anystraight? hand2) (beatsstraight? hand2)))) ((threeofakind? hand1) (and (not (beatstrips? hand2)) (or (and (threeofakind? hand2) (highersimilarhand? hand1 hand2)) (not (threeofakind? hand2))))) ((threeofakind? hand1) (and (not (beatstrips? hand2)) (or (and (threeofakind? hand2) (highersimilarhand? hand1 hand2)) (not (threeofakind? hand2))))) ((twopair? hand1) (and (not (beatstwopair? hand2)) (or (and (twopair? hand2) (highersimilarhand? hand1 hand2)) (not (twopair? hand2))))) ((pair? hand1) (and (not (beatspair? hand2)) (or (and (pair? hand2) (highersimilarhand? hand1 hand2)) (not (pair? hand2))))) (#t (and (not (beatshighcard? hand2)) (highersimilarhand? hand1 hand2)))))Your job is to define the highersimilarhand? procedure it uses to compare two hands in the same category.
The rules for comparing poker hands of the same category specify that the most important part of the hand should be compared first. The most important part is the highest card with the highest number of duplicates. If the most important parts are equal, than the next most important part of the hand determines the higher hand. Note that the sortbyranks procedure we defined sorts the cards in a hand according to importance, so you can determine the higher hand by considering each element of the list produced by sortbyranks in order until you find one that is unequal.
Here are a few examples:
Question 5:
Define the highersimilarhand? procedure, completing the
template we have provided in ps2.scm. It should take two
poker hands as operands, and can assume the hands are of the same
category. It should evaluate to #t if and only if the first
hand beats the second hand.
If your procedure is correct, you should get the following interactions:

To find possible hands, you will find this procedure (defined in poker.scm useful:
(define (choosen n lst) ;; operands: a number n and a list (of at least n elements) ;; result: evaluates to a list of all possible was of choosing n elements from lst (if (= n 0) (list null) (if (= (length lst) n) (list lst) ; must use all elements (append (choosen n (cdr lst)) ;; all possibilities not using the first element (map (lambda (clst) (cons (car lst) clst)) (choosen ( n 1) ;;; all possibilities using the first element (cdr lst)))))))
Question 6:
Define the possiblehands procedure, completing the
template we have provided in ps2.scm. It should take two
operands: the first is a list of two cards representing a player's hole
cards; the second is a list of 5 cards representing the community cards.
It should evaluate to a list of all possible hands that could be made
using 0, 1, or 2 of the player's hole cards and enough of the community
cards to make a fivecard hand.
Note that your possiblehands procedure doesn't depend on the elements of the operands being cards. You may find it easier to test by using scalar values instead. For example, (possiblehands (list 1 2) (list 'a 'b 'c 'd 'e)) should evaluate to a list containing these elements (in any order): ((a b c d e) (1 b c d e) (1 a c d e) (1 a b d e) (1 a b c e) (1 a b c d) (2 b c d e) (2 a c d e) (2 a b d e) (2 a b c e) (2 a b c d) (1 2 c d e) (1 2 b d e) (1 2 b c e) (1 2 b c d) (1 2 a d e) (1 2 a c e) (1 2 a c d) (1 2 a b e) (1 2 a b d) (1 2 a b c)) 
Now that we have a list of all possible hands, we can find the best hand using the higherhand? procedure.
Question 7:
Define the findbesthand procedure, completing the
template we have provided in ps2.scm. It should take two
operands: the first is a list of two cards representing a player's hole
cards; the second is a list of 5 cards representing the community cards.
It should evaluate the best possible hands that could be made by the
player using 0, 1, or 2 of the player's hole cards and enough of the
community cards to make a fivecard hand.
If your procedure is correct, you should get the following interactions: For the first hand, both hole cards are used with the three nines to make a full house. For the second hand, the Ace of clubs hole card is used with the four club community cards to make a flush. For the third hand, no hole cards are used and the five community cards make a straight flush. Hint: as in Question 4, you should be able to define this using only one line. Don't worry about efficiency in your definition (for now). 
For simplicity, we will assume the player is playing against only one opponent, and has good enough cardreading skills to know the other player's exact hand. (Of course, no real poker player is that good, but in many cases players do have a reasonable guess what cards the other players are holding.)
To analyze a hand, we determine how many of the possible river cards would allow the player to win or draw. The analyzeturnsituation procedure is defined below (and in poker.scm):
(define (accumulateouts lst) ; lst is a list of triples representingThe analyzeturnsituation procedure determines the cards left in the deck by removing the known hole cards and community cards as currentdeck. Then, it uses map to try each possible river card from the current deck. If the river card would allow player 1 to produce a better hand than player 2, it puts that card as the first element in the list; if the hands would be equal, it puts that card as the second element; if player 2 would win, it puts that card as the third element. The accumulateouts procedure combines all the sublists to form a list of all the winning and chopping outs.cards (if (null? lst) (list null null null) (let ((restouts (accumulateouts (cdr lst)))) (list (append (car (car lst)) (car restouts)) (append (car (cdr (car lst))) (car (cdr restouts))) (append (car (cdr (cdr (car lst)))) (car (cdr (cdr restouts)))))))) (define (analyzeturnsituation hole1 hole2 community) ;; remove all known cards from the deck (let ((currentdeck (removecards (append hole1 hole2 community) fulldeck))) ;; we want to find out how many of the remaining cards produce each result (accumulateouts (map (lambda (rivercard) (let ((outcome (comparehands? (findbesthand hole1 (cons rivercard community)) (findbesthand hole2 (cons rivercard community))))) (if (eq? outcome 'higher) (list (list rivercard) null null) (if (eq? outcome 'equal) (list null (list rivercard) null) ; chop (list null null (list rivercard)))))) currentdeck))))
Here's an example:
To consider the situation after the flop (the first three community cards have been dealt), we need to look at all possibilities for both the fourth and fifth card. The analyzeflopsituation does this:> (showanalysis (analyzeturnsituation connect67 acesinhole straightdraw4))
Winning outs (15): 2c 3c 5h 5d 5c 5s 9c 10h 10d 10c 10s Jc Qc Kc Ac
Chopping outs (0):
Losers (29): 2h 2d 2s 3h 3d 3s 4h 4d 4s 6h 6d 6s 7h 7d 7s 8h 8d 8s 9h 9d
Jd Js Qh Qd Qs Kh Kd Ks As threeclubs))
(define (analyzeflopsituation hole1 hole2 community) ;; operands: hole cards for player 1 and play 2 and community cards ;; there must be 2 cards in each players hole cards and 3 community cards ;; result: a list of three elements (winningouts, choppingouts, loser) showing ;; the turn and river cards that will lead for the each outcome for player 1. (let ((currentdeck (removecards (append hole1 hole2 community) fulldeck))) ;; we want to find out how many of the remaining cards produce each result (map (lambda (turncard) (analyzeturnsituation hole1 hole2 (cons turncard community))) currentdeck)))
Question 8: Predict how long it will take to evaluate an application of analyzeflopsituation (for example, (analyzeflopsituation connect67 acesinhole straightdraw3)). You should start by timing analyzeturnsituation. You can do this using the time procedure: (time (showanalysis (analyzeturnsituation connect67 acesinhole straightdraw4))). This will print out something like cpu time: 3523 real time: 4823 gc time: 203 (the numbers here are made up for the example). The number after real time gives the number of milliseconds it took to do the evaluation (in this case 4.823 seconds). If you want to try evaluation and timing analyzeflopsituation you can, but you should predict how long it will take first. 
Question 9: This implementation is obviously too slow for many practical uses, including building a poker bot. Suggest some approaches you would use if you wanted to make a faster implementation. 
Credits: This problem set was developed for UVA
CS200 Fall 2005 by David Evans
and tested and improved by Dan Upton.
CS 150: Computer Science University of Virginia 
evans@cs.virginia.edu Using these Materials 