Midterm 1
, Spring 2024
: Ranking Poker Hands
¶Version 1.0.1
History:
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:
Final reminders:
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.
### Global Imports
### BEGIN HIDDEN TESTS
if False: # set to True to set up
import dill
import hashlib
def hash_check(f1, f2, verbose=True):
with open(f1, 'rb') as f:
h1 = hashlib.md5(f.read()).hexdigest()
with open(f2, 'rb') as f:
h2 = hashlib.md5(f.read()).hexdigest()
if verbose:
print(h1)
print(h2)
assert h1 == h2, f'The file "{f1}" has been modified'
with open('resource/asnlib/public/hash_check.pkl', 'wb') as f:
dill.dump(hash_check, f)
del hash_check
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
### END HIDDEN TESTS
from poker_game import \
poker_info, \
cards_info, \
pprint_cards, \
ranks_info, \
pprint_ranks
from pprint import pprint
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.
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.
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:
bin(351843729281280) -> '0b1010000000000000000000000100000000001000100000000'
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}}
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)
A poker hand is a collection of 5 cards. A hand has three attributes which determine its strength relative to other hands:
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:
### test_cell_ex0
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
print('Passed! Please submit.')
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:
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.s
by whitespace and convert to uppercasepoker_info
to determine the integer representation of each cards
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.
### Define demo inputs
demo_s_ex_0 = 'ah TH jd qs 9D'
### Exercise 1 solution
def hand_from_str(s: str) -> int:
### BEGIN SOLUTION
return sum(poker_info['CARDS'][_s] for _s in s.upper().split())
### END SOLUTION
### 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))
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. ### test_cell_ex1
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
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
Define reverse_dict(d: dict) -> dict
as follows:
d
: a Python dictionaryd_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
d
raise a ValueError
d_rev
as described above### Define demo inputs
demo_d_ex2 = {'x': 1, 'y': 2, 'kangaroo': 'llama'}
demo_d_invalid_ex2 = {'x': 1, 'y': 2, 'kangaroo': 2}
### Exercise 2 solution
def reverse_dict(d: dict) -> dict:
### BEGIN SOLUTION
result = {v:k for k, v in d.items()}
if len(result) != len(d):
raise ValueError()
return result
### END SOLUTION
# 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}?')
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. ### test_cell_ex2
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
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:
n
: a Python integern
to a binary representationint.bit_count()
is not implemented in Python 3.8### 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`
### Exercise 3 solution
def count_bits(n: int) -> int:
### BEGIN SOLUTION
return bin(n).count('1')
### END SOLUTION
### demo function call
count_bits(demo_n_ex3)
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. ### test_cell_ex3
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
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 orhand_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:
hand
: the 52-bit integer representation of a collection of 5 poker cards(flush, hand_ranks, quads, odd_ranks, trips, five_high, straight, rank_count)
Hint
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.
### 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
### 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
### BEGIN SOLUTION
odd_ranks = W ^ X ^ Y ^ Z
trips = odd_ranks & in_arbitrary_pair
quads = W & X & Y & Z
five_high = hand_ranks == poker_info["FIVE_HIGH"]
straight = five_high | (count_bits(hand_ranks&(hand_ranks>>1)) == 4)
rank_count = count_bits(hand_ranks)
return flush, hand_ranks, quads, odd_ranks, trips, five_high, straight, rank_count
### END SOLUTION
### demo function call
for hand in demo_hands_ex4:
print(f'get_reductions({hand}) -> {get_reductions(hand)}')
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. ### test_cell_ex4
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
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
flush
: True
if all cards are the same suit. False
otherwisehand_ranks
: 13-bit integer representation of all the ranks in the handquads
: 13-bit integer representation of all the ranks appearing in 4 suitsodd_ranks
: 13-bit integer representation of all ranks appearing in an odd number of suitstrips
: 13-bit integer representation of all the ranks in 3 suitsfive_high
: True
if the ranks in the hand meet the criteria for a five high straight False
otherwisestraight
: True
if all ranks in the hand are sequential. False
otherwiserank_count
: count of the number of distinct ranks in the handstr
, 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.
# 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.
### 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:
### BEGIN SOLUTION
if rank_count == 2:
if quads:
return 'FOUR_OF_A_KIND', quads, odd_ranks
else:
return 'FULL_HOUSE', odd_ranks, hand_ranks ^ odd_ranks
if rank_count == 3:
if hand_ranks == odd_ranks:
return 'THREE_OF_A_KIND', trips, hand_ranks ^ trips
else:
return 'TWO_PAIR', hand_ranks ^ odd_ranks, odd_ranks
if rank_count == 4:
return 'PAIR', hand_ranks^odd_ranks, odd_ranks
if rank_count == 5:
primary = 15 if five_high else hand_ranks
if straight and flush:
hand_type = 'STRAIGHT_FLUSH'
elif flush:
hand_type = 'FLUSH'
elif straight:
hand_type = 'STRAIGHT'
else:
hand_type = 'HIGH_CARD'
return hand_type, primary, 0
### END SOLUTION
for reduction in demo_reductions_ex5:
print(reduction, '----------> ')
print(identify_from_reductions(**reduction), '\n')
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. ### test_cell_ex5
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
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:
poker_info
)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:
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 handsecondary
- 13-bit integer representation of the secondary ranks of a handpoker_info
.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.
### Exercise 6 solution
def score_from_info(hand_type: str, primary: int, secondary: int) -> int:
### BEGIN SOLUTION
return (poker_info['HAND_TYPES'][hand_type] << 26) + (primary << 13) + secondary
### END SOLUTION
### demo function call
score_from_info('THREE_OF_A_KIND', 512, 1032)
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. ### test_cell_ex6
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
Translate the score for a poker hand into human readable versions of its components
score
: the score for a poker handpoker_info['HAND_TYPES']
maps hand types to type_score values. 0b1100000000000
= 1536 -> {'A', 'K'}
hand_type
: (str
) - the hand typeprimary
: (set
) - set of the primary rankssecondary
: (set
) - set of the secondary ranksNote
partition_score
, a function that partitions the score into its components. 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.
### 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)
### BEGIN SOLUTION
REVERSE_HAND_TYPES = reverse_dict(poker_info['HAND_TYPES'])
return {
'hand_type': REVERSE_HAND_TYPES[type_score],
'primary': set(ranks_info(primary_ranks)['string_representation'].split()),
'secondary': set(ranks_info(secondary_ranks)['string_representation'].split())
}
### END SOLUTION
### demo function call
translate_score(270536708)
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. ### test_cell_ex7
### BEGIN HIDDEN TESTS
import dill
import hashlib
with open('resource/asnlib/public/hash_check.pkl', 'rb') as f:
hash_check = dill.load(f)
for fname in ['testers.py', '__init__.py', 'test_utils.py']:
hash_check(f'tester_fw/{fname}', f'resource/asnlib/public/{fname}')
del hash_check
del dill
del hashlib
### END HIDDEN TESTS
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
### BEGIN HIDDEN TESTS
tester = Tester(conf, key=b'RKLlutzjcNM-oSSPrBDwxq7ABnEKW0UkWDk2jr60_t4=', path='resource/asnlib/publicdata/encrypted/')
for _ in range(20):
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
### END HIDDEN TESTS
print('Passed! Please submit.')
Fin. If you have made it this far, congratulations on completing the exam. Don't forget to submit!