Midterm 1, Spring 2024: Ranking Poker Hands

Version 1.0.1

History:

  • 1.0.1: Fix typo.
  • 1.0.0: Initial release.

All of the header information is important. Please read it..

Topics, number of exercises: This problem builds on your knowledge of conditional logic, implementing math as code, and Python's representation of numbers. It has 8 exercises, numbered 0 to 7. There are 15 available points. However, to earn 100% the threshold is 10 points. (Therefore, once you hit 10 points, you can stop. There is no extra credit for exceeding this threshold.)

Exercise ordering: Each exercise builds logically on previous exercises, but you may solve them in any order. That is, if you can't solve an exercise, you can still move on and try the next one. Use this to your advantage, as the exercises are not necessarily ordered in terms of difficulty. Higher point values indicate more complicated exercises which may be more time consuming to solve.

Demo cells: Code cells starting with the comment ### define demo inputs load results from prior exercises applied to the entire data set and use those to build demo inputs. These must be run for subsequent demos to work properly, but they do not affect the test cells. The data loaded in these cells may be rather large (at least in terms of human readability). You are free to print or otherwise use Python to explore them, but we did not print them in the starter code.

Debugging you code: Right before each exercise test cell, there is a block of text explaining the variables available to you for debugging. You may use these to test your code and can print/display them as needed (careful when printing large objects, you may want to print the head or chunks of rows at a time).

Exercise point breakdown:

  • Exercise 0: 1 point(s)
  • Exercise 1: 1 point(s)
  • Exercise 2: 2 point(s)
  • Exercise 3: 1 point(s)
  • Exercise 4: 3 point(s)
  • Exercise 5: 3 point(s)
  • Exercise 6: 2 point(s)
  • Exercise 7: 2 point(s)

Final reminders:

  • Submit after every exercise
  • Review the generated grade report after you submit to see what errors were returned
  • Stay calm, skip problems as needed, and take short breaks at your leisure

Topic Introduction

The goal of this notebook is to write a computer program which can determine the relative strength of poker hands. Inspiration was drawn from this blog where the author walks through building a similar program in the C programming language.

In [8]:
### Global Imports
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from poker_game import \
    poker_info, \
    cards_info, \
    pprint_cards, \
    ranks_info, \
    pprint_ranks
    
from pprint import pprint

Exercise 0 - (1 Points):

The first exercise is to read this background information on poker, the data structures we will use, and some utilities we have built to make understanding the data simpler.

Representing cards

Playing cards are traditionally pieces of cardstock material which are marked with a rank and a suit. A deck of cards contains all possible combinations of ranks and suits. There are 13 ranks and 4 suits for a total of 52 cards. The image below shows the suits as rows and the ranks as columns.

resource/asnlib/publicdata/cards.png

Since we can't hand out pyhsical cards we're going to have to come up with some way of representing collections of cards (i.e. subsets of a deck) in Python. This notebook relies on two in particular:

  • String representation: This is a human-readable representation, but it is not very handy for calculation. Each card in a collection is a two-character sequence identifying its rank and suit. The collection is space-delimited. For example: 'TH AH QS 9D JD'
    • suit: single character in 'HSCD'
    • rank: single character in 'AKQJT98765432'
  • Integer representation: This representation is much less human-readable, but it is very efficient for calculation. The idea here is we create a mapping of the 52 cards to 52 bit-positions. Each bit is 1 if the mapped card is present in the collection and 0 otherwise.
    • example:
      • 351843729281280 - This is the integer version of 'TH AH QS 9D JD'
      • bin(351843729281280) -> '0b1010000000000000000000000100000000001000100000000'
    • For mapping bit positions to suit/rank combinations we use the convention of breaking the 52-bit representation into four groups of 13.
      • Each group is assigned a suit.
      • From right to left in each group the bits are assigned to ranks in ascending order of strength.
        • (i.e. the right-most bit is a '2' and the left-most bit is an 'A').
    • To provide more visibility into this representation we have provided some functions:
      • pprint_cards(cards) prints the binary representation of cards annotated with the suit and rank mapped to each bit position:
          pprint_cards(351843729281280) prints:
          SUIT: DDDDDDDDDDDDD CCCCCCCCCCCCC SSSSSSSSSSSSS HHHHHHHHHHHHH
          RANK: AKQJT98765432 AKQJT98765432 AKQJT98765432 AKQJT98765432
          BITS: 0001010000000 0000000000000 0010000000000 1000100000000
      • cards_info(cards) returns a dictionary with some summary information about cards
          cards_info(351843729281280) -> 
          {'binary_representation': '0b0001010000000000000000000000100000000001000100000000',
           'int_value': 351843729281280,
           'string_representation': 'TH AH QS 9D JD',
           'suit_groups': {'C': 0, 'D': 640, 'H': 4352, 'S': 1024}}
    • The suit_groups values in the cards_info output are the integer representation of the subset of ranks which make up the suit groups (13-bit sequence sharing the same suit). In general we can represent any subset of the possible ranks with a 13-bit integer. We have provided functions for visibility into these representations as well.
      • pprint_ranks(ranks) prints the binary representation of ranks annotated with the rank mapped to each bit position.
          pprint_ranks(640) prints:
          RANK: AKQJT98765432 
          BITS: 0001010000000
      • ranks_info(ranks) returns a dictionary with some summary information about ranks.
          ranks_info(640) ->
          {'int_value': 640, 'binary_representation': '0b0001010000000', 'string_representation': '9 J'}

You can view the source code in the poker_game module (download file)

Ranking hands

A poker hand is a collection of 5 cards. A hand has three attributes which determine its strength relative to other hands:

  • hand type: category assigned to the hand based on criteria related to the ranks and suits in it. Types which are less likely to occur are considered stronger.
  • primary ranks: the ranks in the "made" part of the hand
    • i.e. for "three-of-a-kind" the primary rank is the rank which appears 3 times in the hand
  • secondary ranks: the ranks which are not in the "made" part of the hand
    • i.e. for "three-of-a-kind" the secondary ranks are the two ranks which appear once in the hand.
    • there may be no secondary ranks - which would be a blank string or 0 depending on the representation.

Additionally we have created a dictionary containing useful constants called poker_info. You can view the exact contents and structure in poker_info.py (download file). Here's a high-level description of the contents:

  • RANKS: dictionary mapping the string representation of each rank to its bit position in the 13-bit integer representation.
    • The values are also the relative strengths of the ranks. Higher number means higher strength.
  • SUITS: dictionary mapping the string representation of each suit to an attributes dictionary.
    • Each attributes dictionary contains the index, symbol, start/end position and the bit-mask for the bit positions associated with the suit in the 52-bit representation of cards.
  • CARDS: dictionary mapping the string representation of each card to the 52-bit integer representation of the card.
  • HAND_TYPES: dictionary mapping the types of poker hands to the relative strength of the type.
  • FIVE_HIGH: 13-bit integer representation of the ranks present in a special case which is an exception to the normal rules for determining the type of a poker hand.
In [61]:
### test_cell_ex0
###
### AUTOGRADER TEST - DO NOT REMOVE
###

print('Passed! Please submit.')
Passed! Please submit.

Exercise 1 - (1 Points):

For the first few exercises we will make some more utilities that will be useful in completing the later exercises.

We have functionality to go from the integer representation to the string representation of a collection of cards. To close the loop on user interaction we must create a utility to convert the string representation of cards to the 52-bit integer representation.

Define hand_from_str(s: str)->int: as follows:

  • Input:
    • s: string representation of a collection of cards as described above. The processing of s is not case sensitive, so inputs can be upper or lower case.
  • Behavior:
    • Split s by whitespace and convert to uppercase
    • Use the mapping in poker_info to determine the integer representation of each card
  • Output:
    • The sum of the integer representations of all the cards in the collection according to s

Recall from the introductory material that the dictionary poker_info has a lot of useful constants and that the functions cards_info, pprint_cards, ranks_info and pprint_ranks can be used to get more information about the integer representations of collections of cards or ranks.

In [14]:
### Define demo inputs

demo_s_ex_0 = 'ah TH jd qs 9D'
The demo included in the solution cell below should display the following output: ``` integer returned: 351843729281280 pretty printed: SUIT: DDDDDDDDDDDDD CCCCCCCCCCCCC SSSSSSSSSSSSS HHHHHHHHHHHHH RANK: AKQJT98765432 AKQJT98765432 AKQJT98765432 AKQJT98765432 BITS: 0001010000000 0000000000000 0010000000000 1000100000000 ```
In [18]:
### Exercise 1 solution
def hand_from_str(s: str) -> int:
    '''
    INPUT: a string s (card representations split by whitespace, can be upper or lowercase)

    GOAL: Sum up all of the integer representations of the cards

    STRATEGY: 
    1. Convert s to uppercase
    2. Split Step 1 by whitespace into a list
    3. Iterate over this list, look up each card in 'poker_info' CARDS
    4. Add up and return
    '''
    upper_s = s.upper()
    upper_list = upper_s.split()
    final_int = 0
    
    for each in upper_list:
        final_int += poker_info['CARDS'][each]
    
    return final_int
    
### demo function call
# call the function defined above using the demo inputs.
print('integer returned:')
print(hand_from_str(demo_s_ex_0))
print()
print('pretty printed:')
pprint_cards(hand_from_str(demo_s_ex_0))
integer returned:
351843729281280

pretty printed:
SUIT: DDDDDDDDDDDDD CCCCCCCCCCCCC SSSSSSSSSSSSS HHHHHHHHHHHHH
RANK: AKQJT98765432 AKQJT98765432 AKQJT98765432 AKQJT98765432
BITS: 0001010000000 0000000000000 0010000000000 1000100000000

The cell below will test your solution for Exercise 1. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [19]:
### test_cell_ex1
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_1', 
    'func': hand_from_str, # replace this with the function defined above
    'inputs':{ # input config dict. keys are parameter names
        's':{
            'dtype':'str', # data type of param.
            'check_modified':False,
        }
    },
    'outputs':{
        'output_0':{
            'index':0,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 2 - (2 Points):

We have a few mappings from the string to integer representations, but having the reverse mappings will also be useful. We want to make sure that the reverse mappings are valid

  • a reverse mapping is valid if it has the same number of keys as the original mapping.
  • If that's not the case then there are duplicate values in the original and Python re-assigned one of them. It's not deterministic which original key gets over-written.

Define reverse_dict(d: dict) -> dict as follows:

  • Input:
    • d: a Python dictionary
  • Behavior:
    • build a new dictionary d_rev that swaps the key/value pairs.
      • d[d_rev[v]] is equal to v for any v which is a value in d
      • d_rev[d[k]] is equal to k for any k which is a key in d
    • if there are duplicate values in d raise a ValueError
  • Output:
    • d_rev as described above
In [20]:
### Define demo inputs

demo_d_ex2 = {'x': 1, 'y': 2, 'kangaroo': 'llama'}
demo_d_invalid_ex2 = {'x': 1, 'y': 2, 'kangaroo': 2}
The demo included in the solution cell below should display the following output: ``` reversed {'x': 1, 'y': 2, 'kangaroo': 'llama'} into {1: 'x', 2: 'y', 'llama': 'kangaroo'} A ValueError was raised. Are there duplicate values in {'x': 1, 'y': 2, 'kangaroo': 2}? ```
In [21]:
### Exercise 2 solution

def reverse_dict(d: dict) -> dict:
    '''
    INPUT: d is a dictionary

    GOAL: Reverse the dictionary, unless there are duplicates, then raise a ValueError

    STRATEGY:
    1. Create empty dictionary
    2. Iterate over keys in d, grab value associated with key
        3. If value is already a key in new dictionary, raise ValueError
        4. Else, add reversed key/value pair to dictionary
    5. Return new dictionary
    '''
    final_rev_dict = {}
    
    for key in d:
        val = d[key]
        if val in final_rev_dict:
            raise ValueError
        else:
            final_rev_dict[val] = key
    
    return final_rev_dict

# demo
for d in [demo_d_ex2, demo_d_invalid_ex2]:
    try:
        d_rev = reverse_dict(d)
        print(f'Successfully reversed {d} into {d_rev}')
    except ValueError:
        print(f'A ValueError was raised. Are there duplicate values in {d}?')
Successfully reversed {'x': 1, 'y': 2, 'kangaroo': 'llama'} into {1: 'x', 2: 'y', 'llama': 'kangaroo'}
A ValueError was raised. Are there duplicate values in {'x': 1, 'y': 2, 'kangaroo': 2}?

Since part of the requirements are for your function to raise an error we will wrap your function with a decorator for testing. The decorated function will give the output of your function and an indicator of whether the error was raised. For example, (1234, False) means the output is 1234 and no ValueError was raised. (None, True) means that an error was raised and no output was returned.

The cell below will test your solution for Exercise 2. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [22]:
### test_cell_ex2
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

def ex2_wrapper(d):
    val = None
    err = False
    try:
        val = reverse_dict(d)
    except ValueError:
        err = True
    return (val, err)
    

conf = {
    'case_file':'tc_2', 
    'func': ex2_wrapper, # replace this with the function defined above
    'inputs':{ # input config dict. keys are parameter names
        'd':{
            'dtype':'dict', # data type of param.
            'check_modified':True,
        }
    },
    'outputs':{
        'output_0':{
            'index':0,
            'dtype':'',
            'check_dtype': False,
            'check_col_dtypes': False, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 0
        },
        'output_1':{
            'index':1,
            'dtype':'bool',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 0
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 3 - (1 Points):

It's hard to tell how many cards/ranks appear in the 52/13-bit integer representations. We need a way to count the bits which are equal to 1 in an integer.

Define count_bits(n: int) -> int as follows:

  • Input:
    • n: a Python integer
  • Behavior:
    • Convert n to a binary representation
    • Count the number of times 1 appears in that representation
    • Note int.bit_count() is not implemented in Python 3.8
  • Output:
    • The count of ones in the binary representation
In [5]:
### Define demo inputs

# Note: Python will treat a sequence of 1s and 0s prefixed by "0b" as an integer
#       The value will be what's represented in binary by the 1s and 0s
#       The line below is equivalent to `demo_n_ex3 = 1941`

demo_n_ex3 = 0b11110010101 # equivalent to `demo_n_ex3 = 1941`
The demo included in the solution cell below should display the following output: ``` 7 ```
In [24]:
### Exercise 3 solution
def count_bits(n: int) -> int:
    '''
    INPUT: an integer n

    GOAL: return count of ones in the binary representation

    STRATEGY:
    1. Convert input n to binary string representation
    2. Count number of 1s in this string and return
    '''
    # Google: 'python convert integer to binary'
    # https://stackoverflow.com/questions/10411085/converting-integer-to-binary-in-python
    
    bin_rep = bin(n)
    return bin_rep.count('1')
    
    
### demo function call
count_bits(demo_n_ex3)
Out[24]:
7

The cell below will test your solution for Exercise 3. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [25]:
### test_cell_ex3
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_3', 
    'func': count_bits, # replace this with the function defined above
    'inputs':{ # input config dict. keys are parameter names
        'n':{
            'dtype':'int', # data type of param.
            'check_modified':False,
        }
    },
    'outputs':{
        'output_0':{
            'index':0,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 4 - (3 Points):

Now it's time to do some computation.

If we use cards_info to get the suit groups from the integer representation of a hand we can perform simple operations on the suit groups (W, X, Y, Z) to combine them and extract key information for identification.

You may have forgotten about Python's bitwise operators, but it's worth reviewing the docs for a few minutes (They're short, but skip the part on negative numbers). Several of these operators will be useful for this exercise. (& | ^ >>).

We are interested in calculating the values for each variable in the table below.

Variable Type Value Meaning
flush bool True if any of W, X, Y, Z has a bit count of 5 True when all cards are the same suit
hand_ranks int bits are 1 in positions where the bits are 1 in W or X or Y or Z The distinct ranks in the hand
quads int bits are 1 in positions where the bits are 1 in W and X and Y and Z The ranks which appear four times in the hand
odd_ranks int bits are 1 in positions where the bits are 1 in W xor X xor Y xor Z The ranks which appear an odd number of times in the hand
trips int bits are 1 in positions where the
bits are 1 in odd_ranks and in_arbitrary_pair*
The ranks which appear three times in the hand
five_high bool True if hand_ranks is equal to poker_info['FIVE_HIGH']** True when the hand is a special case called a "five-high straight".
straight bool True if five_high is True or
hand_ranks&(hand_ranks>>1) has a bit count of 4
True when there are five sequential ranks in the hand
rank_count int The bit count of hand_ranks The number of distinct ranks in the hand

\* `in_arbitrary_pair` is provided in the starter code. The logic is explained in the blog post linked below but is not worth delving into in an exam setting. \*\* The five-high straight is an exception to the rank ordering given in `poker_info['RANKS']` because the sequence `'5 4 3 2 A'` is considered valid in poker.

Define get_reductions(hand: int) -> tuple as follows:

  • Input:
    • hand: the 52-bit integer representation of a collection of 5 poker cards
  • Behavior:
    • Partition the hand into four 13-bit integer "suit-groups" (implemented in starter code)
    • Calculate the values outlined in the table above. We have provided the first two in the starter code as an example.
  • Output:
    • The ordered tuple (flush, hand_ranks, quads, odd_ranks, trips, five_high, straight, rank_count)

Hint

  • This exercise draws heavily from the logic in this blog post. Feel free to use it as a reference.

Recall from the introductory material that the dictionary poker_info has a lot of useful constants and that the functions cards_info, pprint_cards, ranks_info and pprint_ranks can be used to get more information about the integer representations of collections of cards or ranks.

In [26]:
### Define demo inputs

with open('resource/asnlib/publicdata/demo_hands_ex4.pkl', 'rb') as f:
    import dill as pickle
    demo_hands_ex4 = [v['hand'] for v in pickle.load(f).values()]
    
demo_hands_ex4
Out[26]:
[34084860461056,
 2252899459547138,
 3378112070942720,
 595935302254592,
 131958575202304,
 598684148441088,
 1144044063293440,
 80815178383360,
 723616090030080]
The demo included in the solution cell below should display the following output: ``` get_reductions(34084860461056) -> (True, 62, 0, 62, 0, False, True, 5) get_reductions(2252899459547138) -> (False, 4098, 2, 4096, 0, False, False, 2) get_reductions(3378112070942720) -> (False, 6144, 0, 4096, 4096, False, False, 2) get_reductions(595935302254592) -> (True, 1084, 0, 1084, 0, False, False, 5) get_reductions(131958575202304) -> (False, 496, 0, 496, 0, False, True, 5) get_reductions(598684148441088) -> (False, 1089, 0, 1089, 1, False, False, 3) get_reductions(1144044063293440) -> (False, 2081, 0, 2048, 0, False, False, 3) get_reductions(80815178383360) -> (False, 147, 0, 131, 0, False, False, 4) get_reductions(723616090030080) -> (False, 3364, 0, 3364, 0, False, False, 5) ```
In [27]:
### Exercise 4 solution
def get_reductions(hand: int) -> tuple:
    ### Predefined values in starter code
    
    # W, X, Y, and Z are the four suit groups from the hand
    W, X, Y, Z = [val for val in cards_info(hand)['suit_groups'].values()]
    in_arbitrary_pair = (W & X) | (Y & Z)
    
    # We are also providing flush and hand_ranks as examples
    flush =         (count_bits(W)==5) | (count_bits(X)==5) | (count_bits(Y)==5) | (count_bits(Z)==5)
    hand_ranks =    W | X | Y | Z  
    ###
    ### YOUR CODE HERE
    ###
    '''
    INPUT: hand is the 52-bit integer representation of a collection of 5 poker cards

    GOAL: Calculate the values outlined in the table above. First two are provided for us. 
    Output as a tuple: (flush, hand_ranks, quads, odd_ranks, trips, five_high, straight, rank_count)
    
    STRATEGY:
    Find the following:
    1. quads = W and X and Y and Z
    2. odd_ranks = W xor X xor Y xor Z
    3. trips = bits are 1 in positions where the bits are 1 in odd_ranks and in_arbitrary_pair
    4. five_high = True if hand_ranks is equal to poker_info['FIVE_HIGH']**
    5. straight = True if five_high is True or hand_ranks&(hand_ranks>>1) has a bit count of 4
    6. rank_count = The bit count of hand_ranks
    '''
    # QUADS:
    quads = W & X & Y & Z
    
    # ODD_RANKS:
    odd_ranks = W ^ X ^ Y ^ Z
    
    # TRIPS:
    trips = odd_ranks & in_arbitrary_pair
    
    # FIVE HIGH:
    if hand_ranks == poker_info['FIVE_HIGH']:
        five_high = True
    else:
        five_high = False
    
    # STRAIGHT:
    if five_high is True or count_bits(hand_ranks&(hand_ranks>>1)) == 4:
        straight = True
    else:
        straight = False
        
    # RANK COUNT:
    rank_count = count_bits(hand_ranks)
    
    return (flush, hand_ranks, quads, odd_ranks, trips, five_high, straight, rank_count)
    
### demo function call
for hand in demo_hands_ex4:
    print(f'get_reductions({hand}) -> {get_reductions(hand)}')
get_reductions(34084860461056) -> (True, 62, 0, 62, 0, False, True, 5)
get_reductions(2252899459547138) -> (False, 4098, 2, 4096, 0, False, False, 2)
get_reductions(3378112070942720) -> (False, 6144, 0, 4096, 4096, False, False, 2)
get_reductions(595935302254592) -> (True, 1084, 0, 1084, 0, False, False, 5)
get_reductions(131958575202304) -> (False, 496, 0, 496, 0, False, True, 5)
get_reductions(598684148441088) -> (False, 1089, 0, 1089, 1, False, False, 3)
get_reductions(1144044063293440) -> (False, 2081, 0, 2048, 0, False, False, 3)
get_reductions(80815178383360) -> (False, 147, 0, 131, 0, False, False, 4)
get_reductions(723616090030080) -> (False, 3364, 0, 3364, 0, False, False, 5)

The cell below will test your solution for Exercise 4. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [28]:
### test_cell_ex4
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_4', 
    'func': get_reductions, # replace this with the function defined above
    'inputs':{ # input config dict. keys are parameter names
        'hand':{
            'dtype':'int', # data type of param.
            'check_modified':False,
        }
    },
    'outputs':{
        'flush':{
            'index':0,
            'dtype':'bool',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'hand_ranks':{
            'index':1,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'quads':{
            'index':2,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'odd_ranks':{
            'index':3,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'trips':{
            'index':4,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'five_high':{
            'index':5,
            'dtype':'bool',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'straight':{
            'index':6,
            'dtype':'bool',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        },
        'rank_count':{
            'index':7,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(500):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 5 - (3 Points):

With the 8 reductions from the hand we can now calculate its strength attributes. In the chart below see that each rank_count value and branch condition identifies a hand type and how to determine the primary and secondary ranks for that hand type.

rank_count branch condition type primary ranks secondary ranks
2 Any rank is in 4 suits FOUR_OF_A_KIND Rank in 4 suits Rank in the hand but not in 4 suits
2 No rank is in 4 suits FULL_HOUSE Ranks in an odd number of suits Rank in the hand but not in an odd number of suits
3 Any rank is in 3 suits THREE_OF_A_KIND Rank in 3 suits Ranks in the hand but not in 3 suits
3 No rank is in 3 suits TWO_PAIR Ranks in the hand but not in an odd number of suits Ranks in an odd number of suits
4 True PAIR Ranks in the hand but not in an odd number of suits Ranks in an odd number of suits
5 All cards the same suit and all ranks sequential STRAIGHT_FLUSH Ranks in the hand (see note) 0
5 All cards the same suit but ranks not sequential FLUSH Ranks in the hand 0
5 All ranks sequential but not all cards are the same suit STRAIGHT Ranks in the hand (see note) 0
5 Ranks not sequential not all cards are the same suit HIGH_CARD Ranks in the hand 0

Note In the special case of a five-high straight or five-high straight-flush, 15 should be returned as the primary ranks.

Your task
Define identify_from_reductions(flush: bool, hand_ranks: int, quads: int, odd_ranks: int, trips: int, five_high: bool, straight: bool, rank_count: int) -> tuple

  • Inputs:
    • flush: True if all cards are the same suit. False otherwise
    • hand_ranks: 13-bit integer representation of all the ranks in the hand
    • quads: 13-bit integer representation of all the ranks appearing in 4 suits
    • odd_ranks: 13-bit integer representation of all ranks appearing in an odd number of suits
    • trips: 13-bit integer representation of all the ranks in 3 suits
    • five_high: True if the ranks in the hand meet the criteria for a five high straight False otherwise
    • straight: True if all ranks in the hand are sequential. False otherwise
    • rank_count: count of the number of distinct ranks in the hand
  • Behavior:
    • Evaluate the hand type, primary ranks, and secondary ranks of the hand per the logic in the table above
  • Output:
    • Ordered tuple (str, int, int) containing the hand type, primary ranks, and secondary ranks

Recall from the introductory material that the dictionary poker_info has a lot of useful constants and that the functions cards_info, pprint_cards, ranks_info and pprint_ranks can be used to get more information about the integer representations of collections of cards or ranks.

In [29]:
# demo_reductions_ex5 = [random_type_example(t)['reductions'] for t in poker_info['HAND_TYPES']]
with open('resource/asnlib/publicdata/demo_reductions.pkl', 'rb') as f:
    demo_reductions_ex5 = pickle.load(f)

The demo included in the solution cell below should display the following output:

('STRAIGHT_FLUSH', 124, 0) 

('FOUR_OF_A_KIND', 128, 64) 

('FULL_HOUSE', 2048, 1024) 

('FLUSH', 3400, 0) 

('STRAIGHT', 62, 0) 

('THREE_OF_A_KIND', 512, 1032) 

('TWO_PAIR', 2064, 4) 

('PAIR', 16, 578) 

('HIGH_CARD', 454, 0)

Note: The demo iterates over a number of inputs and shows both inputs and outputs. You only need to return the outputs.

In [31]:
### Exercise 5 solution
def identify_from_reductions(flush: bool,
                          hand_ranks: int,
                          quads: int,
                          odd_ranks: int,
                          trips: int,
                          five_high: bool,
                          straight: bool,
                          rank_count: int) -> tuple:
    '''
    INPUT:
    - flush: True if all cards are the same suit. False otherwise
    - hand_ranks: 13-bit integer representation of all the ranks in the hand
    - quads: 13-bit integer representation of all the ranks appearing in 4 suits
    - odd_ranks: 13-bit integer representation of all ranks appearing in an odd number of suits
    - trips: 13-bit integer representation of all the ranks in 3 suits
    - five_high: True if the ranks in the hand meet the criteria for a five high straight False otherwise
    - straight: True if all ranks in the hand are sequential. False otherwise
    - rank_count: count of the number of distinct ranks in the hand

    GOAL:
    Find tuple (hand_type_str, primary_rank_int, secondary_rank_int)    
    
    STRATEGY:
    Build elaborate conditional checks to encode the info in the table above (i.e. if meets this criteria, then return this). 
    Don't forget hint 'Note'.

        (2, quads != 0) : ('FOUR_OF_A_KIND', quads, odd_ranks)
        (2, quads == 0) : ('FULL_HOUSE', odd_ranks, hand_ranks - odd_ranks)
        (3, trips != 0) : ('THREE_OF_A_KIND', trips, hand_ranks - trips)
        (3, trips == 0) : ('TWO_PAIR', hand_ranks - odd_ranks, odd_ranks)
        (4) : ('PAIR', hand_ranks - odd_ranks, odd_ranks)
        (5, sequential is True, flush is True, five_high is False): ('STRAIGHT_FLUSH', hand_ranks, 0)
        (5, sequential is True, flush is True, five_high is True) : ('STRAIGHT_FLUSH', 15, 0)
        (5, sequential is False, flush is True) : ('FLUSH', hand_ranks, 0)
        (5, sequential is True, flush is False, five_high is True) : ('STRAIGHT', 15, 0)
        (5, sequential is True, flush is False, five_high is False) : ('STRAIGHT', hand_ranks, 0)
        (5, sequential is False, flush is False) : ('HIGH_CARD', hand_ranks, 0)
    '''
    if rank_count == 2:
        if quads != 0:
            return ('FOUR_OF_A_KIND', quads, odd_ranks)
        else:
            return ('FULL_HOUSE', odd_ranks, hand_ranks - odd_ranks)
    elif rank_count == 3:
        if trips != 0:
            return ('THREE_OF_A_KIND', trips, hand_ranks - trips)
        else:
            return ('TWO_PAIR', hand_ranks - odd_ranks, odd_ranks)
    elif rank_count == 4:
        return ('PAIR', hand_ranks - odd_ranks, odd_ranks)
    elif rank_count == 5:
        if straight is True and flush is True and five_high is False:
            return ('STRAIGHT_FLUSH', hand_ranks, 0)
        elif straight is True and flush is True and five_high is True:
            return ('STRAIGHT_FLUSH', 15, 0)
        elif straight is False and flush is True:
            return ('FLUSH', hand_ranks, 0)
        elif straight is True and flush is False and five_high is True:
            return ('STRAIGHT', 15, 0)
        elif straight is True and flush is False and five_high is False:
            return ('STRAIGHT', hand_ranks, 0)
        else:
            return ('HIGH_CARD', hand_ranks, 0)
    
    
for reduction in demo_reductions_ex5:
    print(reduction, '---------->              ')
    print(identify_from_reductions(**reduction), '\n')
{'rank_count': 5, 'hand_ranks': 124, 'odd_ranks': 124, 'quads': 0, 'flush': True, 'trips': 0, 'straight': True, 'five_high': False} ---------->              
('STRAIGHT_FLUSH', 124, 0) 

{'rank_count': 2, 'hand_ranks': 192, 'odd_ranks': 64, 'quads': 128, 'flush': False, 'trips': 0, 'straight': False, 'five_high': False} ---------->              
('FOUR_OF_A_KIND', 128, 64) 

{'rank_count': 2, 'hand_ranks': 3072, 'odd_ranks': 2048, 'quads': 0, 'flush': False, 'trips': 2048, 'straight': False, 'five_high': False} ---------->              
('FULL_HOUSE', 2048, 1024) 

{'rank_count': 5, 'hand_ranks': 3400, 'odd_ranks': 3400, 'quads': 0, 'flush': True, 'trips': 0, 'straight': False, 'five_high': False} ---------->              
('FLUSH', 3400, 0) 

{'rank_count': 5, 'hand_ranks': 62, 'odd_ranks': 62, 'quads': 0, 'flush': False, 'trips': 0, 'straight': True, 'five_high': False} ---------->              
('STRAIGHT', 62, 0) 

{'rank_count': 3, 'hand_ranks': 1544, 'odd_ranks': 1544, 'quads': 0, 'flush': False, 'trips': 512, 'straight': False, 'five_high': False} ---------->              
('THREE_OF_A_KIND', 512, 1032) 

{'rank_count': 3, 'hand_ranks': 2068, 'odd_ranks': 4, 'quads': 0, 'flush': False, 'trips': 0, 'straight': False, 'five_high': False} ---------->              
('TWO_PAIR', 2064, 4) 

{'rank_count': 4, 'hand_ranks': 594, 'odd_ranks': 578, 'quads': 0, 'flush': False, 'trips': 0, 'straight': False, 'five_high': False} ---------->              
('PAIR', 16, 578) 

{'rank_count': 5, 'hand_ranks': 454, 'odd_ranks': 454, 'quads': 0, 'flush': False, 'trips': 0, 'straight': False, 'five_high': False} ---------->              
('HIGH_CARD', 454, 0) 

The cell below will test your solution for Exercise 5. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [32]:
### test_cell_ex5
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_5', 
    'func': identify_from_reductions, # replace this with the function defined above
    'inputs':{
        'rank_count': {'dtype': 'int', 'check_modified': False},
         'hand_ranks': {'dtype': 'int', 'check_modified': False},
         'odd_ranks': {'dtype': 'int', 'check_modified': False},
         'quads': {'dtype': 'int', 'check_modified': False},
         'flush': {'dtype': 'bool', 'check_modified': False},
         'trips': {'dtype': 'int', 'check_modified': False},
         'straight': {'dtype': 'bool', 'check_modified': False},
         'five_high': {'dtype': 'bool', 'check_modified': False}
    },
    'outputs':{
            'hand_type': {'index': 0,
              'dtype': 'str',
              'check_dtype': True,
              'check_col_dtypes': True,
              'check_col_order': True,
              'check_row_order': True,
              'check_column_type': True,
              'float_tolerance': 1e-06},
             'primary': {'index': 1,
              'dtype': 'int',
              'check_dtype': True,
              'check_col_dtypes': True,
              'check_col_order': True,
              'check_row_order': True,
              'check_column_type': True,
              'float_tolerance': 1e-06},
             'secondary': {'index': 2,
              'dtype': 'int',
              'check_dtype': True,
              'check_col_dtypes': True,
              'check_col_order': True,
              'check_row_order': True,
              'check_column_type': True,
              'float_tolerance': 1e-06}
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 6 - (2 Points):

Now that we've identified the strength attributes of the hand we need some way to compare them. We can leverage integers again. If we use the lookup dictionary in poker_info we can translate the string representation of each hand type into its relative strength. The stronger types are higher. Lets construct an integer as follows:

  • Bit positions 26-29 are the relative strength of the hand type (can be extracted from poker_info)
  • Bit positions 13-25 are the primary ranks of the hand
  • Bit positions 0-12 are the secondary ranks of the hand

For example ('THREE_OF_A_KIND', 512, 1032):

TYPE:      0100
PRIMARY:   0001000000000
SECONDARY: 0010000001000

TYPE | PRIMARY       | SECONDARY
---- | -------       | ---------
0100 | 0001000000000 | 0010000001000

Score in binary                  | base-10
---------------                  | -------
0b010000010000000000010000001000 | 272630792

The result will always be larger for stronger poker hands.

Define score_from_info(hand_type: str, primary: int, secondary: int) -> int as follows:

  • Inputs:
    • hand_type - string representing a type of hand. Will be a key of poker_info['HAND_TYPES']
    • primary - 13-bit integer representation of the primary ranks of a hand
    • secondary - 13-bit integer representation of the secondary ranks of a hand
  • Behavior:
    • Determine the relative strength of the hand type. There is a mapping from name to relative strength in poker_info.
    • Concatenate the bits for relative type strength, primary and secondary ranks to form the score
    • HINT - In the next exercise starter code we provide a utility for unpacking an integer of this form into its components.
  • Output:
    • 30-bit integer score

Recall from the introductory material that the dictionary poker_info has a lot of useful constants and that the functions cards_info, pprint_cards, ranks_info and pprint_ranks can be used to get more information about the integer representations of collections of cards or ranks.

The demo included in the solution cell below should display the following output: ``` 272630792 ```
In [45]:
### Exercise 6 solution
def score_from_info(hand_type: str, primary: int, secondary: int) -> int:
    '''
    INPUT:
    - hand_type is a string
    - primary is an integer
    - secondary is an integer
    
    GOAL: Create new integer from these 

    STRATEGY:
    1. Find the hand type integer from poker_info['HAND_TYPES']

    Reverse the helper function 'partition_score' from Exercise 7:
    2. Shift hand_types left 26 + shift primary left 13 + secondary and return
    
    Or:
    2. Convert everything to binary string representation, 
    3. Slice off binary indicator at beginning of primary and secondary strings
    4. Pad with zeros at beginning of primary and secondary strings so they are of length 13 
    5. Concatenate strings back together 
    6. Convert to integer and return
    '''
    # First Way:
    #hand_type_int = poker_info['HAND_TYPES'][hand_type] 
    #return (hand_type_int << 26) + (primary << 13) + secondary

    # Or an Alternative Way using binary representation and string manipulation:
    hand_type_str = bin(poker_info['HAND_TYPES'][hand_type])
    
    primary_str = bin(primary)
    primary_str = primary_str[2:]
    primary_str = primary_str.zfill(13)
    
    secondary_str = bin(secondary)
    secondary_str = secondary_str[2:]
    secondary_str = secondary_str.zfill(13)
    
    final_str = hand_type_str + primary_str + secondary_str
    return int(final_str, 2)
    
### demo function call
score_from_info('THREE_OF_A_KIND', 512, 1032)
Out[45]:
272630792

The cell below will test your solution for Exercise 6. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [46]:
### test_cell_ex6
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_6', 
    'func': score_from_info, # replace this with the function defined above
    'inputs':{
        'hand_type': {'dtype': 'str', 'check_modified': False},
     'primary': {'dtype': 'int', 'check_modified': False},
     'secondary': {'dtype': 'int', 'check_modified': False}
    },
    'outputs':{
        'output_0':{
            'index':0,
            'dtype':'int',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Exercise 7 - (2 Points):

Translate the score for a poker hand into human readable versions of its components

  • Inputs:
    • score: the score for a poker hand
  • Behavior:
    • Partition the score into its type_score, primary_ranks, and secondary_ranks components
    • Translate the type_score into the corresponding hand type
      • Recall poker_info['HAND_TYPES'] maps hand types to type_score values.
    • Translate both "_ranks" components into a Python set containing the human readable ranks
      • 0b1100000000000 = 1536 -> {'A', 'K'}
  • Output:
    • Dictionary with these key/value pairs:
      • hand_type: (str) - the hand type
      • primary: (set) - set of the primary ranks
      • secondary: (set) - set of the secondary ranks

Note

  • We have provided partition_score, a function that partitions the score into its components.
  • If the partition for the secondary component of the score is 0 then an empty set should be returned.

Recall from the introductory material that the dictionary poker_info has a lot of useful constants and that the functions cards_info, pprint_cards, ranks_info and pprint_ranks can be used to get more information about the integer representations of collections of cards or ranks.

The demo included in the solution cell below should display the following output: ``` {'hand_type': 'THREE_OF_A_KIND', 'primary': {'T'}, 'secondary': {'4', 'A'}} ```
In [59]:
### Exercise 7 solution
def partition_score(score: int) -> tuple:
    '''Partitions a poker hand score into its type strength, primary ranks, and secondary ranks components
    '''
    FOUR_BITS = 0b1111
    THIRTEEN_BITS = 0b1111111111111
    
    type_score = FOUR_BITS & score>>26         # shift right by 26 and keep first 4 bits
    primary_ranks = THIRTEEN_BITS & score>>13  # shift right by 13 and keep first 13 bits
    secondary_ranks = THIRTEEN_BITS & score    # keep first 13 bits
    return type_score, primary_ranks, secondary_ranks

def translate_score(score: int) -> dict:
    type_score, primary_ranks, secondary_ranks = partition_score(score)
    ###
    ### YOUR CODE HERE
    ###
    '''
    INPUT: score is a integer value

    GOAL: Return a dictionary with these key/value pairs:
    - hand_type: (str) - the hand type
    - primary: (set) - set of the primary ranks
    - secondary: (set) - set of the secondary ranks
    
    STRATEGY:
    1. Create an empty dictionary to hold result

    For hand_type key/value pair:
    2. Grab the dictionary of hand types from poker_info
    3. Reverse the hand types dictionary and grab hand type value associated with our type score. 
    Assign to 'hand_type' key in our new dictionary

    For primary key/value pair:
    4. Create empty set to hold primary rank info
    5. Call ranks info function to get primary rank info
    6. Split string representation from primary rank info into list
    7. For each item in list, add it to our set from Step 4
    8. Assign this set to 'primary' key in our dictionary

    For secondary key/value pair:
    9. Do same thing as primary but with secondary ranks instead
    10. Return our final dictionary
    '''
    final_dict = {}
    
    hand_types_dict = poker_info['HAND_TYPES']
    rev_hand_types_dict = reverse_dict(hand_types_dict)
    final_dict['hand_type'] = rev_hand_types_dict[type_score]
    
    primary_ranks_set = set()
    primary_rank_info = ranks_info(primary_ranks)
    primary_rank_str = primary_rank_info['string_representation']
    primary_rank_list = primary_rank_str.split()
    for each in primary_rank_list:
        primary_ranks_set.add(each)
    final_dict['primary'] = primary_ranks_set 

    secondary_ranks_set = set()
    secondary_rank_info = ranks_info(secondary_ranks)
    secondary_rank_str = secondary_rank_info['string_representation']
    secondary_rank_list = secondary_rank_str.split()
    for each in secondary_rank_list:
        secondary_ranks_set.add(each)
    final_dict['secondary'] = secondary_ranks_set 
    
    return final_dict
    
### demo function call

translate_score(270536708)
Out[59]:
{'hand_type': 'THREE_OF_A_KIND', 'primary': {'T'}, 'secondary': {'4', 'A'}}

The cell below will test your solution for Exercise 7. The testing variables will be available for debugging under the following names in a dictionary format.

  • input_vars - Input variables for your solution.
  • original_input_vars - Copy of input variables from prior to running your solution. These should be the same as input_vars - otherwise the inputs were modified by your solution.
  • returned_output_vars - Outputs returned by your solution.
  • true_output_vars - The expected output. This should "match" returned_output_vars based on the question requirements - otherwise, your solution is not returning the correct output.
In [60]:
### test_cell_ex7
###
### AUTOGRADER TEST - DO NOT REMOVE
###
from tester_fw.testers import Tester

conf = {
    'case_file':'tc_7', 
    'func': translate_score, # replace this with the function defined above
    'inputs':{ # input config dict. keys are parameter names
        'score':{
            'dtype':'int', # data type of param.
            'check_modified':True,
        }
    },
    'outputs':{
        'output_0':{
            'index':0,
            'dtype':'dict',
            'check_dtype': True,
            'check_col_dtypes': True, # Ignored if dtype is not df
            'check_col_order': True, # Ignored if dtype is not df
            'check_row_order': True, # Ignored if dtype is not df
            'check_column_type': True, # Ignored if dtype is not df
            'float_tolerance': 10 ** (-6)
        }
    }
}
tester = Tester(conf, key=b'kvX8VF1YUgURm8NySX8Cx_UW0ZHCC6WCFqotGOpgvQc=', path='resource/asnlib/publicdata/')
for _ in range(70):
    try:
        tester.run_test()
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
    except:
        (input_vars, original_input_vars, returned_output_vars, true_output_vars) = tester.get_test_vars()
        raise

###
### AUTOGRADER TEST - DO NOT REMOVE
###
print('Passed! Please submit.')
Passed! Please submit.

Fin. If you have made it this far, congratulations on completing the exam. Don't forget to submit!