diff --git a/.gitignore b/.gitignore index b4fb085..39bdde7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ __pycache__/ A.txt B.txt gamelog.txt +game_log_analyze.txt +monte_carlo_test_logs.txt *.class .DS_Store .idea/ diff --git a/python_skeleton/player.py b/python_skeleton/player.py index 6882149..51dea52 100644 --- a/python_skeleton/player.py +++ b/python_skeleton/player.py @@ -9,6 +9,7 @@ import helper import random import eval7 +import probability_engine class Player(Bot): diff --git a/python_skeleton/skeleton/monte_carlo_tests.py b/python_skeleton/skeleton/monte_carlo_tests.py new file mode 100644 index 0000000..5d69abd --- /dev/null +++ b/python_skeleton/skeleton/monte_carlo_tests.py @@ -0,0 +1,364 @@ +import time +import eval7 +import sys + +from probability_engine import Probability_Engine + +class Pseudo_Round_State(): + def __init__(self): + self.street = 0 + self.deck = [] + +class Monte_Carlo_Tests(): + def __init__(self): + self.probability_engine = Probability_Engine() + + def test_all(self, iters, precision, sim): + print('=================================================') + print('Testing Probability Engine on the Flop, Turn, and River') + + flop_results = self.test_flop(iters, precision, sim) + turn_results = self.test_turn(iters, precision, sim) + river_results = self.test_river(iters, precision, sim) + + print('=================================================') + print(f'Ran {3 * iters} tests, {iters} tests on each flop, turn, and river, and half with auction, half without auction') + print(f'Average Auction Diff: {flop_results[0] + turn_results[0] + river_results[0] / (iters * 3)}') + print(f'Average Non Auction Diff: {flop_results[1] + turn_results[1] + river_results[1] / (iters * 3)}') + print(f'Average Engine Runtime: {flop_results[2] + turn_results[2] + river_results[2] / (iters * 3)}') + print(f'Average Sim Runtime: {flop_results[3] + turn_results[3] + river_results[3] / (iters * 3)}') + + return (flop_results[0] + turn_results[0] + river_results[0] / (iters * 3), + flop_results[1] + turn_results[1] + river_results[1] / (iters * 3), + flop_results[2] + turn_results[2] + river_results[2] / (iters * 3), + flop_results[3] + turn_results[3] + river_results[3] / (iters * 3)) + + def test_flop(self, iters, precision, sim): + print('=================================================') + print(f'Testing Probability Engine on the Flop') + + engine_runtime = 0 + sim_runtime = 0 + + print('=================================================') + print('Testing with auction card') + + avg_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(6) + my_cards = [str(card) for card in draw[:3]] + board_cards = [str(card) for card in draw[3:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:3], draw[3:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print('Testing without auction card') + + avg_non_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(5) + my_cards = [str(card) for card in draw[:2]] + board_cards = [str(card) for card in draw[2:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:2], draw[2:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_non_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print(f'Ran {iters} tests, half with auction and half without auction') + print(f'Average Auction Diff: {avg_auction_diff / (iters // 2)}') + print(f'Average Non Auction Diff: {avg_non_auction_diff / (iters // 2)}') + print(f'Average Engine Runtime: {engine_runtime / (iters // 2)}') + print(f'Average Sim Runtime: {sim_runtime / (iters // 2)}') + + return (avg_auction_diff / (iters // 2), avg_non_auction_diff / (iters // 2), engine_runtime / (iters // 2), sim_runtime / (iters // 2)) + + + def test_turn(self, iters, precision, sim): + print('=================================================') + print(f'Testing Probability Engine on the Turn') + + engine_runtime = 0 + sim_runtime = 0 + + print('=================================================') + print('Testing with auction card') + + avg_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(7) + my_cards = [str(card) for card in draw[:3]] + board_cards = [str(card) for card in draw[3:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:3], draw[3:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print('Testing without auction card') + + avg_non_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(6) + my_cards = [str(card) for card in draw[:2]] + board_cards = [str(card) for card in draw[2:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:2], draw[2:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_non_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print(f'Ran {iters} tests, half with auction and half without auction') + print(f'Average Auction Diff: {avg_auction_diff / (iters // 2)}') + print(f'Average Non Auction Diff: {avg_non_auction_diff / (iters // 2)}') + print(f'Average Engine Runtime: {engine_runtime / (iters // 2)}') + print(f'Average Sim Runtime: {sim_runtime / (iters // 2)}') + + return (avg_auction_diff / (iters // 2), avg_non_auction_diff / (iters // 2), engine_runtime / (iters // 2), sim_runtime / (iters // 2)) + + def test_river(self, iters, precision, sim): + print('=================================================') + print(f'Testing Probability Engine on the River') + + engine_runtime = 0 + sim_runtime = 0 + + print('=================================================') + print('Testing with auction card') + + avg_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(8) + my_cards = [str(card) for card in draw[:3]] + board_cards = [str(card) for card in draw[3:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:3], draw[3:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print('Testing without auction card') + + avg_non_auction_diff = 0 + + for _ in range(iters // 2): + print('-------------------------') + + deck = eval7.Deck() + deck.shuffle() + draw = deck.peek(7) + my_cards = [str(card) for card in draw[:2]] + board_cards = [str(card) for card in draw[2:]] + + round_state = Pseudo_Round_State() + round_state.street = 3 + round_state.deck = board_cards + + print(f'my_cards = {my_cards}') + print(f'board_cards = {board_cards}') + + engine_start = time.time() + engine_probability = self.probability_engine.calculate_win_probability(my_cards, round_state, sim) + engine_end = time.time() + + sim_start = time.time() + monte_carlo_probability = self.probability_engine.monte_carlo_flop_and_turn(draw[:2], draw[2:], deck, precision) + sim_end = time.time() + + net_diff = abs(engine_probability - monte_carlo_probability) + + avg_non_auction_diff += net_diff + engine_runtime += engine_end - engine_start + sim_runtime += sim_end - sim_start + + print('Probabilities') + print(f'Engine: {engine_probability} | Sim: {monte_carlo_probability} | Diff: {net_diff}') + print('Runtimes') + print(f'Engine: {engine_end - engine_start} | Sim: {sim_end - sim_start} | Diff: {abs((engine_end - engine_start) - (sim_end - sim_start))}') + + print('=================================================') + print(f'Ran {iters} tests, half with auction and half without auction') + print(f'Average Auction Diff: {avg_auction_diff / (iters // 2)}') + print(f'Average Non Auction Diff: {avg_non_auction_diff / (iters // 2)}') + print(f'Average Engine Runtime: {engine_runtime / (iters // 2)}') + print(f'Average Sim Runtime: {sim_runtime / (iters // 2)}') + + return (avg_auction_diff / (iters // 2), avg_non_auction_diff / (iters // 2), engine_runtime / (iters // 2), sim_runtime / (iters // 2)) + + +if __name__ == '__main__': + + ################################################### + # Calculate winrate using engine on a single hand # + ################################################### + + # # Initialize Test + # round_state = Pseudo_Round_State() + # engine = Probability_Engine() + + # # Set up Arguments + # my_cards = ['6d', '7s', 'Qs'] + # board_cards = ['3c', 'Ad', '8h'] + # round_state.street = 3 + # round_state.deck = board_cards + # monte_carlo_sim_iters = 0 + + # # Run Test + # print(engine.calculate_win_probability(my_cards, round_state, monte_carlo_sim_iters)) + + ######################### + # Run Monte Carlo Tests # + ######################### + + # Initialize Test + temp = sys.stdout + sys.stdout = open('monte_carlo_test_logs.txt','wt') + test_suite = Monte_Carlo_Tests() + + # Set up Arguments + iters = 1000 + precision = 10000 + sim = 10 + + # Run Test + test_suite.test_all(iters, precision, sim) + + # Close Test + temp = sys.stdout \ No newline at end of file diff --git a/python_skeleton/skeleton/probability_engine.py b/python_skeleton/skeleton/probability_engine.py new file mode 100644 index 0000000..0fefa2f --- /dev/null +++ b/python_skeleton/skeleton/probability_engine.py @@ -0,0 +1,661 @@ +import eval7 + +class Probability_Engine(): + def __init__(self): + # Version to output winrate (BAD) + # Version to output buckets: (current, draw, hole cards used, round???, specific cards in hand???) + pass + + def calculate_win_probability(self, my_cards, round_state, iters=0): + ''' + Calculates win probability given hand during flop, turn, and river. + (Preflop and auction uses hole_strengths table) + Considers currently revealed cards, + potential strong hands (outs), + and monte carlo sim. + + Arguments: + my_cards: The 2-3 cards in your hand + round_state: Round object, used to get street cards + iters: iterations for the monte carlo sim + + Returns: + winrate: % chance of winning with current hand and street cards + ''' + + # Set up our deck + deck = eval7.Deck() + my_cards = [eval7.Card(card) for card in my_cards] + + if round_state.street == 3: + board_cards = [eval7.Card(card) for card in round_state.deck[:3]] + elif round_state.street == 4: + board_cards = [eval7.Card(card) for card in round_state.deck[:4]] + else: + board_cards = [eval7.Card(card) for card in round_state.deck[:5]] + + total_cards = my_cards + board_cards + + for card in total_cards: + deck.cards.remove(card) + + # Calculates current handtype + hand_type = eval7.handtype(eval7.evaluate(my_cards + board_cards)) + + # Estimated winrate based on handtype + hand_strengths = { + 'Straight Flush': 1.0, + 'Quads': 0.99, + 'Full House': 0.98, + 'Flush': 0.97, + 'Straight': 0.96, + 'Trips': 0.9, + 'Two Pair': 0.8, + 'Pair': 0.6, + 'High Card': 0.5, + } + + # Scales winrate based on number of hand cards that come from hole + hole_factors = [0.5, 0.95, 1, 1.05] + + # Calculates winrate with currently revealed cards + base_probability = hand_strengths[hand_type] * hole_factors[self.base_hole_cards_used(my_cards, board_cards, hand_type)] + + # On the river + if round_state.street == 5: + return base_probability + + # On the flop and turn + # Calculates potential hands and probability of hitting them (post-flop) + # Based on the most likely hands, calculate a scaled winrate given we hit + if hand_type in {'Straight Flush', 'Quads', 'Full House', 'Flush', 'Straight'}: + return base_probability # want to check for board wetness? + elif hand_type == 'Trips': + # detect flush, full house, quads + flush_odds = self.detect_flush(my_cards, board_cards) + full_house_odds = self.detect_full_house(my_cards, board_cards, hand_type) + quads_odds = self.detect_quads(my_cards, board_cards) + + most_probable_out = max(flush_odds, full_house_odds, quads_odds, key=lambda odds: odds[0]) + + potential_probability = most_probable_out[0] * hole_factors[most_probable_out[1]] + if most_probable_out == flush_odds: + potential_probability *= hand_strengths['Flush'] + elif most_probable_out == full_house_odds: + potential_probability *= hand_strengths['Full House'] + elif most_probable_out == quads_odds: + potential_probability *= hand_strengths['Quads'] + + trips_scaling = 1 + potential_probability *= trips_scaling + + elif hand_type == 'Two Pair': + # detect flush, full house, quads + flush_odds = self.detect_flush(my_cards, board_cards) + full_house_odds = self.detect_full_house(my_cards, board_cards, hand_type) + quads_odds = self.detect_quads(my_cards, board_cards) + + most_probable_out = max(flush_odds, full_house_odds, quads_odds, key=lambda odds: odds[0]) + + potential_probability = most_probable_out[0] * hole_factors[most_probable_out[1]] + if most_probable_out == flush_odds: + potential_probability *= hand_strengths['Flush'] + elif most_probable_out == full_house_odds: + potential_probability *= hand_strengths['Full House'] + elif most_probable_out == quads_odds: + potential_probability *= hand_strengths['Quads'] + + two_pair_scaling = 1.5 + potential_probability *= two_pair_scaling + + elif hand_type == 'Pair': + # detect two pair, trips, straight, flush, full house, quads, straight flush + flush_odds = self.detect_flush(my_cards, board_cards) + full_house_odds = self.detect_full_house(my_cards, board_cards, hand_type) + quads_odds = self.detect_quads(my_cards, board_cards) + two_pair_odds = self.detect_two_pair(my_cards, board_cards, hand_type) + trips_odds = self.detect_trips(my_cards, board_cards) + straight_odds = self.detect_straight(my_cards, board_cards) + straight_flush_odds = self.detect_straight_flush(my_cards, board_cards) + + most_probable_out = max(flush_odds, full_house_odds, quads_odds, two_pair_odds, trips_odds, straight_odds, straight_flush_odds, key=lambda odds: odds[0]) + + potential_probability = most_probable_out[0] * hole_factors[most_probable_out[1]] + if potential_probability == 0: + potential_probability = base_probability + elif most_probable_out == flush_odds: + potential_probability *= hand_strengths['Flush'] + elif most_probable_out == full_house_odds: + potential_probability *= hand_strengths['Full House'] + elif most_probable_out == quads_odds: + potential_probability *= hand_strengths['Quads'] + elif most_probable_out == two_pair_odds: + potential_probability *= hand_strengths['Two Pair'] + elif most_probable_out == trips_odds: + potential_probability *= hand_strengths['Trips'] + elif most_probable_out == straight_odds: + potential_probability *= hand_strengths['Straight'] + elif most_probable_out == straight_flush_odds: + potential_probability *= hand_strengths['Straight Flush'] + + pair_scaling = 1 + potential_probability *= pair_scaling + + elif hand_type == 'High Card': + # detect pair, two pair, flush, straight, straight flush + flush_odds = self.detect_flush(my_cards, board_cards) + two_pair_odds = self.detect_two_pair(my_cards, board_cards, hand_type) + straight_odds = self.detect_straight(my_cards, board_cards) + straight_flush_odds = self.detect_straight_flush(my_cards, board_cards) + pair_odds = self.detect_pair(my_cards, board_cards) + + most_probable_out = max(flush_odds, two_pair_odds, pair_odds, straight_odds, straight_flush_odds, key=lambda odds: odds[0]) + + potential_probability = most_probable_out[0] * hole_factors[most_probable_out[1]] + if most_probable_out == flush_odds: + potential_probability *= hand_strengths['Flush'] + elif most_probable_out == two_pair_odds: + potential_probability *= hand_strengths['Two Pair'] + elif most_probable_out == pair_odds: + potential_probability *= hand_strengths['Pair'] + elif most_probable_out == straight_odds: + potential_probability *= hand_strengths['Straight'] + elif most_probable_out == straight_flush_odds: + potential_probability *= hand_strengths['Straight Flush'] + + high_card_scaling = 1 + potential_probability *= high_card_scaling + + base_scaling = 1.5 + potential_scaling = 1 + monte_carlo_scaling = 1 + + if iters == 0: + monte_carlo_probability = 'Not Used' + win_probability = base_probability * base_scaling + potential_probability * potential_scaling / (base_scaling + potential_scaling) + else: + monte_carlo_probability = self.monte_carlo_flop_and_turn(my_cards, board_cards, deck, iters) + win_probability = ((potential_scaling * potential_probability + base_scaling * base_probability + monte_carlo_scaling * monte_carlo_probability) / + (base_scaling + potential_scaling + monte_carlo_scaling)) + + # Debug Print Statements + print('base probability: ', base_probability) + print('potential probability: ', potential_probability) + print('monte carlo probability: ', monte_carlo_probability) + + return win_probability + + # straight flush can just be flush and straight detection and if both are high we probably have a potential straight flush??? + # Potential probability should be a function of the probability of winning given we hit + # scaled by chance of hitting scaled by hole card usage? + # Combine base and potential probabilities and return a winrate (factor in Monte Carlo Sim prob?) + # Probably only want to run certain calculations on certain rounds + # If the board is wet but not for us, win probability should be low + # sim_probability = monte_carlo_flop_and_turn(my_cards, board_cards, deck, iters) + + def base_hole_cards_used(self, my_cards, board_cards, hand_type): + ''' + Calculate how many hole cards contribute to the current hand type. + + Arguments: + my_cards: cards in hole + board_cards: cards on board + hand_type: best hand given current cards + + Returns: + hole_cards_used + ''' + + # Counts how many hole cards were used to get that handtype + hole_cards_used = 0 + for _ in range(len(my_cards)): + removed_card = my_cards.pop(0) + if eval7.handtype(eval7.evaluate(my_cards + board_cards)) != hand_type: + hole_cards_used += 1 + my_cards.append(removed_card) + + return hole_cards_used + + def detect_top_pair(self, my_cards, board_cards): + ''' + Returns Bool for whether we have top pair on the flop given our hand is a Pair + ''' + def rank_order(char): + if char == 'T': + return 4 + elif char == 'J': + return 3 + elif char == 'Q': + return 2 + elif char == 'K': + return 1 + elif char == 'A': + return 0 + else: + return 14 - int(char) + + if self.base_hole_cards_used(my_cards, board_cards, 'Pair') == 0: + return False + + total_cards = my_cards + board_cards + ranks = '' + for card in total_cards: + card = str(card) + ranks += card[0] + + ranks = sorted(ranks, key=rank_order) + + if ranks[0] == ranks[1]: + return True + return False + + + def detect_flush(self, my_cards, board_cards): + ''' + Returns chance of hitting a flush as well as how many come from hole cards. + ''' + # Counts revealed suits + suits = { + 's': 0, + 'd': 0, + 'h': 0, + 'c': 0 + } + + total_cards = my_cards + board_cards + for card in total_cards: + card = str(card) + suits[card[1]] += 1 + + highest_quantity = max(suits.values()) + + # Run post-flop, so if only 2 of the same suit, flush not possible + if highest_quantity <= 2: + return (0, 0) + + # Counts how many of the max suits come from the hole cards + max_suits = {suit:0 for suit, quantity in suits.items() if quantity == highest_quantity} + for card in my_cards: + card = str(card) + if card[1] in max_suits: + max_suits[card[1]] += 1 + + # Grabs the max suit that pulls the most from the hole cards + max_suit = max(max_suits.items(), key=lambda x: x[1]) + + # Calculates chances of hitting the flush + hit_rate = 0 + if highest_quantity == 3: + if len(board_cards) == 3: + hit_rate = 1/16 + else: + return (0, 0) + elif highest_quantity == 4: + if len(board_cards) == 3: + hit_rate = 7/16 + elif len(board_cards) == 4: + hit_rate = 1/4 + else: + return (0, 0) + elif highest_quantity >= 5: + hit_rate = 1 + + + # Returns hitrate and number of cards coming from hole cards + # Cannot hit a flush if none come from hole cards + if max_suit[1] == 0: + return(0, 0) + return (hit_rate, max_suit[1]) + + def detect_straight(self, my_cards, board_cards, flush=0): + ''' + Returns chance of hitting straight as well as how many comes from hole cards. + ''' + # Process revealed cards + revealed_cards = set() + for card in my_cards + board_cards: + revealed_cards.add(str(card)[0]) + + needed_cards = '' + for card in 'AKQJT98765432': + if card in revealed_cards: + needed_cards += card + else: + needed_cards += 'x' + + # Detect closest potential straight substring + closest_straights = [] + min_to_hit = 5 + + for i in range(0, 9): + current_to_hit = 0 + for j in range(5): + if needed_cards[i + j] == 'x': + current_to_hit += 1 + if current_to_hit < min_to_hit: + min_to_hit = current_to_hit + closest_straights = [needed_cards[i:i+5]] + elif current_to_hit == min_to_hit: + closest_straights.append(needed_cards[i:i+5]) + + # Detect number of hole cards used in potential straight + hole_cards = '' + for card in my_cards: + hole_cards += str(card)[0] + + potential_straights = [] + for straight in closest_straights: + hole_cards_used = 0 + for card in hole_cards: + if card in straight: + hole_cards_used += 1 + if hole_cards_used != 0: + potential_straights.append((straight, hole_cards_used)) + + # Calculate hitrate for potential straight + if potential_straights == []: + return (0, 0) + + chances_left = 5 - len(board_cards) if flush == 0 else 5 - flush + + if chances_left < min_to_hit: + return (0, 0) + + cards_left = 52 - 5 - len(board_cards) + outs = len(potential_straights) + + hit_rate = 0 + if flush != 0: + if min_to_hit == 1: + hit_rate = 1 - ((cards_left - outs) / cards_left) ** chances_left + elif min_to_hit == 2: + hit_rate = 2 * outs / (cards_left ** 2) + else: + if min_to_hit == 1: + hit_rate = 1 - ((cards_left - (outs * 4)) / cards_left) ** chances_left + elif min_to_hit == 2: + hit_rate = 2 * (outs * 16) / (cards_left ** 2) + + return (hit_rate, max(potential_straights, key=lambda x: x[1])[1]) + + def detect_two_pair(self, my_cards, board_cards, hand_type): + ''' + Returns chance of hitting two pair as well as how many comes from hole cards. + ''' + if hand_type == 'Pair': + hole_cards_used = self.base_hole_cards_used(my_cards, board_cards, 'Pair') + + cards_left = 52 - 5 - len(board_cards) + chances_left = 5 - len(board_cards) + + if hole_cards_used == 0: + outs = len(my_cards) * 3 + elif hole_cards_used == 1: + outs = (len(my_cards) - 1 + len(board_cards) - 1) * 3 + else: # hole_cards_used == 2 + outs = (len(my_cards) - 2 + len(board_cards)) * 3 + + hit_rate = 1 - ((cards_left - outs) / cards_left) ** chances_left + + return (hit_rate, max(1, hole_cards_used)) + else: # High Card + if len(board_cards) > 3: + return (0, 0) + + cards_left = 52 - 5 - len(board_cards) + my_cards = len(my_cards) + board_cards = len(board_cards) + + hit_rate = (my_cards + board_cards) * 3 * (my_cards + board_cards - 1) * 3 / cards_left ** 2 + + return (hit_rate, 1) + + def detect_pair(self, my_cards, board_cards): + ''' + Returns chance of hitting pair as well as how many comes from hole cards. + ''' + remaining_cards = 52 - 5 - len(board_cards) + cards_to_hit = len(my_cards) * 3 + chances_left = 5 - len(board_cards) + hit_rate = 1 - ((remaining_cards - cards_to_hit) / remaining_cards) ** chances_left + return (hit_rate, 1) + + def detect_full_house(self, my_cards, board_cards, hand_type): + ''' + Returns chance of hitting full house as well as how many comes from hole cards. + ''' + if hand_type == 'Trips': + + hole_cards_used = self.base_hole_cards_used(my_cards, board_cards, 'Trips') + remaining_cards = 52 - 5 - len(board_cards) + chances_left = 5 - len(board_cards) + my_cards = len(my_cards) + board_cards = len(board_cards) + + + if hole_cards_used == 0: + outs = my_cards * 3 + elif hole_cards_used == 1: + outs = (my_cards - 1 + board_cards - 2) * 3 + elif hole_cards_used == 2: + outs = (my_cards - 2 + board_cards - 1) * 3 + else: # 3 + outs = board_cards * 3 + + hit_rate = 1 - ((remaining_cards - outs) / remaining_cards) ** chances_left + + return (hit_rate, hole_cards_used) + + elif hand_type == 'Two Pair': + + hole_cards_used = self.base_hole_cards_used(my_cards, board_cards, 'Two Pair') + remaining_cards = 52 - 5 - len(board_cards) + chances_left = 5 - len(board_cards) + my_cards = len(my_cards) + board_cards = len(board_cards) + + if hole_cards_used == 0: + return (0, 0) + else: + if chances_left == 2: + outs = 4 + hit_rate = 1 - ((remaining_cards - outs) / remaining_cards) ** 2 + else: # 1 + outs = 4 + hit_rate = 1 - ((remaining_cards - outs) / remaining_cards) + return (hit_rate, hole_cards_used) + + else: # Pair + + hole_cards_used = self.base_hole_cards_used(my_cards, board_cards, 'Pair') + remaining_cards = 52 - 5 - len(board_cards) + chances_left = 5 - len(board_cards) + my_cards = len(my_cards) + board_cards = len(board_cards) + + if hole_cards_used == 0: + if chances_left == 2: + # need to hit one pair and one hole card or two of the same hole card + hit_rate = my_cards * 18 / remaining_cards ** 2 + else: # 1 + return (0, 0) + else: + if chances_left == 2: + # need to hit one pair and any card or two of the same card + hit_rate = (my_cards + board_cards - 2) * 18 / remaining_cards ** 2 + else: # 1 + return (0, 0) + + return (hit_rate, hole_cards_used) + + def detect_straight_flush(self, my_cards, board_cards): + ''' + Returns chance of hitting straight flush as well as how many comes from hole cards. + Basically uses detect_straight logic then checks for flush on the most probable straights + ''' + # Counts revealed suits + suits = { + 's': 0, + 'd': 0, + 'h': 0, + 'c': 0 + } + + total_cards = my_cards + board_cards + for card in total_cards: + card = str(card) + suits[card[1]] += 1 + + highest_quantity = max(suits.values()) + + # Run post-flop, so if only 2 of the same suit, flush not possible + if highest_quantity <= 2: + return (0, 0) + + # Grabs suits that occur most often + max_suits = [suit for suit, quantity in suits.items() if quantity == highest_quantity] + + # Keep hole and board cards if they are that max suit + suited_my_cards = [card for card in my_cards if str(card)[1] in max_suits] + suited_board_cards = [card for card in board_cards if str(card)[1] in max_suits] + + if len(suited_my_cards) == 0: + return (0, 0) + else: + return self.detect_straight(suited_my_cards, suited_board_cards, len(board_cards)) + + def detect_trips(self, my_cards, board_cards): + ''' + Returns chance of hitting trips as well as how many comes from hole cards. + ''' + # Counts ranks of revealed cards + ranks = {'A': 0, 'K': 0, 'Q': 0, 'J': 0, 'T': 0, '9': 0, '8': 0, '7': 0, '6': 0, '5': 0, '4': 0, '3': 0, '2': 0, '1': 0} + + total_cards = my_cards + board_cards + for card in total_cards: + card = str(card) + ranks[card[0]] += 1 + + highest_quantity = max(ranks.values()) + + # Counts how many of the max ranks come from the hole cards + max_ranks = {rank:0 for rank, quantity in ranks.items() if quantity == highest_quantity} + for card in my_cards: + card = str(card) + if card[0] in max_ranks: + max_ranks[card[0]] += 1 + + # Grabs the max rank that pulls the most from the hole cards + max_rank = max(max_ranks.items(), key=lambda x: x[1]) + + hit_rate = 0 + # Calculates the chance of getting a trip + remaining_cards = 52 - 5 - len(board_cards) + if highest_quantity == 1: + return (0,0) + elif highest_quantity == 2: + if len(board_cards) == 3: + hit_rate = 1 - ((remaining_cards - 2) / remaining_cards) ** 2 + elif len(board_cards) == 4: + hit_rate = 1 - ((remaining_cards - 2) / remaining_cards) + elif highest_quantity >= 3: + hit_rate = 1 + + # Return the chance of hitting a trip and number of hole cards used + # If no hole cards were used then no trip occur + if max_rank[1] == 0: + return (0,0) + else: + return (hit_rate, max_rank[1]) + + def detect_quads(self, my_cards, board_cards): + ''' + Returns chance of hitting quads as well as how many comes from hole cards. + ''' + # Counts ranks of revealed cards + ranks = {'A': 0, 'K': 0, 'Q': 0, 'J': 0, 'T': 0, '9': 0, '8': 0, '7': 0, '6': 0, '5': 0, '4': 0, '3': 0, '2': 0, '1': 0} + + total_cards = my_cards + board_cards + for card in total_cards: + card = str(card) + ranks[card[0]] += 1 + + highest_quantity = max(ranks.values()) + + # Runs post-flop, so if only 1 then no quad can occur + if highest_quantity == 1: + return (0,0) + + # Counts how many of the max ranks come from the hole cards + max_ranks = {rank:0 for rank, quantity in ranks.items() if quantity == highest_quantity} + for card in my_cards: + card = str(card) + if card[0] in max_ranks: + max_ranks[card[0]] += 1 + + # Grabs the max rank that pulls the most from the hole cards + max_rank = max(max_ranks.items(), key=lambda x: x[1]) + + hit_rate = 0 + # Calculates the chance of getting a quad + remaining_cards = 52 - 5 - len(board_cards) + if highest_quantity == 2: + if len(board_cards) == 3: + hit_rate = (2 / remaining_cards) * (1 / (remaining_cards - 1)) + elif len(board_cards) == 4: + return (0, 0) + elif highest_quantity == 3: + if len(board_cards) == 3: + hit_rate = 1 - ((remaining_cards - 1) / remaining_cards) ** 2 + elif len(board_cards) == 4: + hit_rate = 1 - ((remaining_cards - 1) / remaining_cards) + elif highest_quantity >= 4: + hit_rate = 1 + + # Return the chance of hitting a trip and number of hole cards used + # If no hole cards were used then no trip occur + if max_rank[1] == 0: + return (0,0) + else: + return (hit_rate, max_rank[1]) + + def monte_carlo_flop_and_turn(self, my_cards, board_cards, deck, iters): + ''' + Runs a monte carlo simulation for your current hand on the turn + (4 cards revealed on the board) + We are simulating the opponent cards + final board card + + Arguments: + my_cards: The 2-3 cards in your hand + + Returns: + winrate: % chance of winning with current hand and street cards + ''' + win_count = 0.0 + + for i in range(iters): + # Set up game state + deck.shuffle() + opp_num = 3 if len(my_cards) == 2 else 2 + draw = deck.peek(opp_num + (5 - len(board_cards))) + opp_cards = draw[:opp_num] + community_cards = draw[opp_num:] + + my_hand = my_cards + community_cards + board_cards + opp_hand = opp_cards + community_cards + board_cards + + our_hand_eval = eval7.evaluate(my_hand) + opp_hand_eval = eval7.evaluate(opp_hand) + + if our_hand_eval > opp_hand_eval: + # We win the round + win_count += 1 + elif our_hand_eval == opp_hand_eval: + # We tie this round + win_count += 0.5 + else: + # We lose this round + win_count += 0 + + return win_count/iters + \ No newline at end of file diff --git a/python_skeleton/skeleton/unit_tests.py b/python_skeleton/skeleton/unit_tests.py new file mode 100644 index 0000000..18960b3 --- /dev/null +++ b/python_skeleton/skeleton/unit_tests.py @@ -0,0 +1,409 @@ +import eval7 + +from probability_engine import Probability_Engine + +class Unit_Tests(): + def __init__(self): + self.probability_engine = Probability_Engine() + + def test_all(self): + print('==================================================') + print('Running all Unit Tests') + + detect_results = self.test_detect() + base_hole_cards_used_results = self.test_base_hole_cards_used() + + passed = [] + failed = [] + passed.append('test_detect') if detect_results else failed.append('test_detect') + passed.append('test_base_hole_cards_used') if base_hole_cards_used_results else failed.append('test_base_hole_cards_used') + + print('==================================================') + print('Finished running all Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_detect(self): + print('==================================================') + print('Running detect Unit Tests') + + flush_results = self.test_flush() + full_house_results = self.test_full_house() + pair_results = self.test_pair() + quads_results = self.test_quads() + trips_results = self.test_trips() + top_pair_results = self.test_top_pair() + straight_results = self.test_straight() + straight_flush_results = self.test_straight_flush() + two_pair_results = self.test_two_pair() + + passed = [] + failed = [] + passed.append('test_flush') if flush_results else failed.append('test_flush') + passed.append('test_full_house') if full_house_results else failed.append('test_full_house') + passed.append('test_pair') if pair_results else failed.append('test_pair') + passed.append('test_quads') if quads_results else failed.append('test_quads') + passed.append('test_trips') if trips_results else failed.append('test_trips') + passed.append('test_top_pair') if top_pair_results else failed.append('test_top_pair') + passed.append('test_straight') if straight_results else failed.append('test_straight') + passed.append('test_straight_flush') if straight_flush_results else failed.append('test_straight_flush') + passed.append('test_two_pair') if two_pair_results else failed.append('test_two_pair') + + print('==================================================') + print('Finished running detect Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_base_hole_cards_used(self): + print('==================================================') + print('Running base_hole_cards_used Unit Tests') + + test_1 = self.probability_engine.base_hole_cards_used([eval7.Card('Ac'), eval7.Card('Ad'), ], [eval7.Card('Ah'), eval7.Card('7c'), eval7.Card('6d'), ], 'Trips') == 2 + test_2 = self.probability_engine.base_hole_cards_used([eval7.Card('3h'), eval7.Card('4h'), eval7.Card('7h')], [eval7.Card('Ah'), eval7.Card('7c'), eval7.Card('6h')], 'Flush') == 3 + test_3 = self.probability_engine.base_hole_cards_used([eval7.Card('Ac'), eval7.Card('2d'), ], [eval7.Card('Ah'), eval7.Card('7c'), eval7.Card('6d')], 'Pair') == 1 + test_4 = self.probability_engine.base_hole_cards_used([eval7.Card('2h'), eval7.Card('Kd'), ], [eval7.Card('2d'), eval7.Card('Ks'), eval7.Card('6d'), eval7.Card('8d')], 'Two Pair') == 2 + test_5 = self.probability_engine.base_hole_cards_used([eval7.Card('Ac'), eval7.Card('Ad'), eval7.Card('Jc')], [eval7.Card('Ah'), eval7.Card('7c'), eval7.Card('6d'), eval7.Card('7h')], 'Full House') == 2 + test_6 = self.probability_engine.base_hole_cards_used([eval7.Card('Ac'), eval7.Card('Td'), ], [eval7.Card('Ah'), eval7.Card('7c'), eval7.Card('6d'), eval7.Card('5h'), eval7.Card('9h')], 'Pair') == 1 + test_7 = self.probability_engine.base_hole_cards_used([eval7.Card('Ac'), eval7.Card('8c'), eval7.Card('6d')], [eval7.Card('4h'), eval7.Card('4c'), eval7.Card('4d'), eval7.Card('2s'), eval7.Card('5s')], 'Trips') == 0 + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + passed.append('test_5') if test_5 else failed.append('test_5') + passed.append('test_6') if test_6 else failed.append('test_6') + passed.append('test_7') if test_7 else failed.append('test_7') + + + print('Finished running base_hole_cards_used Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_top_pair(self): + print('==================================================') + print('Running top_pair Unit Tests') + + test_1 = self.probability_engine.detect_top_pair([eval7.Card('Ac'), eval7.Card('Ad'), ], [eval7.Card('2h'), eval7.Card('7c'), eval7.Card('6d'), ]) == True + test_2 = self.probability_engine.detect_top_pair([eval7.Card('3h'), eval7.Card('4h'), eval7.Card('7h')], [eval7.Card('Ah'), eval7.Card('8c'), eval7.Card('8h')]) == False + test_3 = self.probability_engine.detect_top_pair([eval7.Card('Ac'), eval7.Card('2d'), ], [eval7.Card('2h'), eval7.Card('7c'), eval7.Card('6d')]) == False + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + + + print('Finished running top_pair Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_flush(self): + print('==================================================') + print('Running flush Unit Tests') + + test_1 = self.probability_engine.detect_flush([eval7.Card('Ac'), eval7.Card('2h')], [eval7.Card('3h'), eval7.Card('4h'), eval7.Card('5d')]) == (1/16,1) + test_2 = self.probability_engine.detect_flush([eval7.Card('2c'), eval7.Card('5s'), eval7.Card('3s')], [eval7.Card('5h'), eval7.Card('9d'), eval7.Card('Kc')]) == (0,0) + test_3 = self.probability_engine.detect_flush([eval7.Card('3d'), eval7.Card('7d')], [eval7.Card('4h'), eval7.Card('2h'), eval7.Card('Td'), eval7.Card('Ad')]) == (1/4,2) + test_4 = self.probability_engine.detect_flush([eval7.Card('Jc'), eval7.Card('Tc'), eval7.Card('2c')], [eval7.Card('5h'), eval7.Card('Ad'), eval7.Card('3d'), eval7.Card('4s')]) == (0,0) + test_5 = self.probability_engine.detect_flush([eval7.Card('Jc'), eval7.Card('Tc'), eval7.Card('2c')], [eval7.Card('5h'), eval7.Card('Ad'), eval7.Card('3d'), eval7.Card('4c')]) == (1/4,3) + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + passed.append('test_5') if test_5 else failed.append('test_5') + + print('Finished running flush Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_full_house(self): + print('==================================================') + print('Running full_house Unit Tests') + + test_1 = self.probability_engine.detect_full_house([eval7.Card('3h'), eval7.Card('3c')], [eval7.Card('5h'), eval7.Card('5c'), eval7.Card('2d')], 'Two Pair') + test_1 = (round(test_1[0], 2), test_1[1]) == (0.17,2) + test_2 = self.probability_engine.detect_full_house([eval7.Card('Ac'), eval7.Card('Ad'), eval7.Card('Ah')], [eval7.Card('4h'), eval7.Card('5d'), eval7.Card('3c')], 'Trips') + test_2 = (round(test_2[0], 2), test_2[1]) == (0.37,3) + test_3 = self.probability_engine.detect_full_house([eval7.Card('3h'), eval7.Card('3c')], [eval7.Card('7c'), eval7.Card('8d'), eval7.Card('Ts'), eval7.Card('4h')], 'Pair') + test_3 = (round(test_3[0], 2), test_3[1]) == (0,0) + test_4 = self.probability_engine.detect_full_house([eval7.Card('3h'), eval7.Card('3c'), eval7.Card('7d')], [eval7.Card('7c'), eval7.Card('8d'), eval7.Card('Ts'), eval7.Card('4h')], 'Two Pair') + test_4 = (round(test_4[0], 2), test_4[1]) == (0.09,3) + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + + print('Finished running full_house Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_pair(self): + print('==================================================') + print('Running pair Unit Tests') + + test_1 = self.probability_engine.detect_pair([eval7.Card('2s'), eval7.Card('3h')], [eval7.Card('6d'), eval7.Card('7s'), eval7.Card('Tc')]) + test_1 = (round(test_1[0], 2), test_1[1]) == (0.25,1) + test_2 = self.probability_engine.detect_pair([eval7.Card('2s'), eval7.Card('3h'), eval7.Card('8d')], [eval7.Card('6d'), eval7.Card('7s'), eval7.Card('Tc')]) + test_2 = (round(test_2[0], 2), test_2[1]) == (0.37,1) + test_3 = self.probability_engine.detect_pair([eval7.Card('2s'), eval7.Card('3h')], [eval7.Card('6d'), eval7.Card('7s'), eval7.Card('Tc'), eval7.Card('Ac')]) + test_3 = (round(test_3[0], 2), test_3[1]) == (0.14,1) + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + + print('Finished running pair Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_quads(self): + print('==================================================') + print('Running quads Unit Tests') + + test_1 = self.probability_engine.detect_quads([eval7.Card('As'), eval7.Card('4d')], [eval7.Card('5d'), eval7.Card('8c'), eval7.Card('Th')]) == (0, 0) + test_2 = self.probability_engine.detect_quads([eval7.Card('4s'), eval7.Card('4d')], [eval7.Card('5s'), eval7.Card('Jd'), eval7.Card('Kc')]) + test_2 = (round(test_2[0], 3), test_2[1]) == (0.001, 2) + test_3 = self.probability_engine.detect_quads([eval7.Card('5h'), eval7.Card('As')], [eval7.Card('5d'), eval7.Card('8s'), eval7.Card('Qd')]) + test_3 = (round(test_3[0], 3), test_3[1]) == (0.001, 1) + test_4 = self.probability_engine.detect_quads([eval7.Card('6h'), eval7.Card('Kc')],[eval7.Card('7d'), eval7.Card('7s'), eval7.Card('2d')]) == (0, 0) + test_5 = self.probability_engine.detect_quads([eval7.Card('9c'), eval7.Card('8h'), eval7.Card('Kd')], [eval7.Card('9c'), eval7.Card('Td'), eval7.Card('As')]) + test_5 = (round(test_5[0], 3), test_5[1]) == (0.001, 1) + test_6 = self.probability_engine.detect_quads([eval7.Card('4c'), eval7.Card('4s')], [eval7.Card('4h'), eval7.Card('5d'), eval7.Card('Qs')]) + test_6 = (round(test_6[0], 2), test_6[1]) == (0.04, 2) + test_7 = self.probability_engine.detect_quads([eval7.Card('9s'), eval7.Card('2d'), eval7.Card('2s')], [eval7.Card('2h'), eval7.Card('Jd'), eval7.Card('2c')]) == (1, 2) + test_8 = self.probability_engine.detect_quads([eval7.Card('As'), eval7.Card('3d')], [eval7.Card('Ad'), eval7.Card('8d'), eval7.Card('9s'), eval7.Card('Tc')]) == (0, 0) + test_9 = self.probability_engine.detect_quads([eval7.Card('7d'), eval7.Card('7s')], [eval7.Card('As'), eval7.Card('4d'), eval7.Card('Tc'), eval7.Card('7h')]) + test_9 = (round(test_9[0], 2), test_9[1]) == (0.02, 2) + + + passed = [] + failed = [] + + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + passed.append('test_5') if test_5 else failed.append('test_5') + passed.append('test_6') if test_6 else failed.append('test_6') + passed.append('test_7') if test_7 else failed.append('test_7') + passed.append('test_8') if test_8 else failed.append('test_8') + passed.append('test_9') if test_9 else failed.append('test_9') + + print('Finished running quads Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_trips(self): + print('==================================================') + print('Running trips Unit Tests') + + test_1 = self.probability_engine.detect_trips([eval7.Card('As'), eval7.Card('4d')], [eval7.Card('5d'), eval7.Card('8c'), eval7.Card('Th')]) == (0, 0) + test_2 = self.probability_engine.detect_trips([eval7.Card('4s'), eval7.Card('4d')], [eval7.Card('5s'), eval7.Card('Jd'), eval7.Card('Kc')]) + test_2 = (round(test_2[0], 2), test_2[1]) == (0.09, 2) + test_3 = self.probability_engine.detect_trips([eval7.Card('5h'), eval7.Card('As')], [eval7.Card('5d'), eval7.Card('8s'), eval7.Card('Qd')]) + test_3 = (round(test_3[0], 2), test_3[1]) == (0.09, 1) + test_4 = self.probability_engine.detect_trips([eval7.Card('6h'), eval7.Card('Kc')], [eval7.Card('7d'), eval7.Card('7s'), eval7.Card('2d')]) == (0, 0) + test_5 = self.probability_engine.detect_trips([eval7.Card('9c'), eval7.Card('8h'), eval7.Card('Kd')], [eval7.Card('9c'), eval7.Card('Td'), eval7.Card('As')]) + test_5 = (round(test_5[0], 2), test_5[1]) == (0.09, 1) + test_6 = self.probability_engine.detect_trips([eval7.Card('4c'), eval7.Card('Td')], [eval7.Card('4h'), eval7.Card('4d'), eval7.Card('Qs')]) == (1, 1) + test_7 = self.probability_engine.detect_trips([eval7.Card('9s'), eval7.Card('2d'), eval7.Card('2s')], [eval7.Card('2h'), eval7.Card('Jd'), eval7.Card('Ks')]) == (1, 2) + test_8 = self.probability_engine.detect_trips([eval7.Card('As'), eval7.Card('3d')], [eval7.Card('Ad'), eval7.Card('8d'), eval7.Card('9s'), eval7.Card('Tc')]) + test_8 = (round(test_8[0], 2), test_8[1]) == (0.05, 1) + test_9 = self.probability_engine.detect_trips([eval7.Card('7d'), eval7.Card('7s')], [eval7.Card('As'), eval7.Card('4d'), eval7.Card('Tc'), eval7.Card('9h')]) + test_9 = (round(test_9[0], 2), test_9[1]) == (0.05, 2) + + passed = [] + failed = [] + + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + passed.append('test_5') if test_5 else failed.append('test_5') + passed.append('test_6') if test_6 else failed.append('test_6') + passed.append('test_7') if test_7 else failed.append('test_7') + passed.append('test_8') if test_8 else failed.append('test_8') + passed.append('test_9') if test_9 else failed.append('test_9') + + print('Finished running trips Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_straight(self): + print('==================================================') + print('Running straight Unit Tests') + + test_1 = self.probability_engine.detect_straight([eval7.Card('4s'), eval7.Card('7d')], [eval7.Card('5c'), eval7.Card('6h'), eval7.Card('Ac')]) + test_1 = (round(test_1[0], 2), test_1[1]) == (0.33, 2) + test_2 = self.probability_engine.detect_straight([eval7.Card('8s'), eval7.Card('3h'), eval7.Card('4d')], [eval7.Card('5s'), eval7.Card('Ac'), eval7.Card('Jc')]) + test_2 = (round(test_2[0], 2), test_2[1]) == (0.05, 2) + test_3 = self.probability_engine.detect_straight([eval7.Card('6s'), eval7.Card('7s')], [eval7.Card('9s'), eval7.Card('2s'), eval7.Card('Ks'), eval7.Card('Qh')]) == (0, 0) + test_4 = self.probability_engine.detect_straight([eval7.Card('Tc'), eval7.Card('9c'), eval7.Card('Qc')], [eval7.Card('Jc'), eval7.Card('2c'), eval7.Card('6d'), eval7.Card('6s')]) + test_4 = (round(test_4[0], 2), test_4[1]) == (0.19, 3) + test_5 = self.probability_engine.detect_straight([eval7.Card('2d'), eval7.Card('Jc')], [eval7.Card('Jd'), eval7.Card('Th'), eval7.Card('9s'), eval7.Card('6c'), eval7.Card('Ad')]) == (0, 0) + test_6 = self.probability_engine.detect_straight([eval7.Card('2d'), eval7.Card('Ad'), eval7.Card('Ks')], [eval7.Card('6h'), eval7.Card('5c'), eval7.Card('4s'), eval7.Card('7d'), eval7.Card('8c')]) == (0,0) + test_7 = self.probability_engine.detect_straight([eval7.Card('Ah'), eval7.Card('7h')], [eval7.Card('Ks'), eval7.Card('Jh'), eval7.Card('Tc')]) + test_7 = (round(test_7[0], 2), test_7[1]) == (0.17, 1) + test_8 = self.probability_engine.detect_straight([eval7.Card('5h'), eval7.Card('7h')], [eval7.Card('6s'), eval7.Card('8h'), eval7.Card('Kc'), eval7.Card('Ah')]) + test_8 = (round(test_8[0], 2), test_8[1]) == (0.19, 2) + + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + passed.append('test_5') if test_5 else failed.append('test_5') + passed.append('test_6') if test_6 else failed.append('test_6') + passed.append('test_7') if test_7 else failed.append('test_7') + passed.append('test_8') if test_8 else failed.append('test_8') + + print('Finished running straight Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_straight_flush(self): + print('==================================================') + print('Running straight_flush Unit Tests') + + test_1 = self.probability_engine.detect_straight_flush([eval7.Card('Ac'), eval7.Card('Tc')], [eval7.Card('Jc'), eval7.Card('Qc'), eval7.Card('9h')]) + test_1 = (round(test_1[0], 2), test_1[1]) == (0.04, 2) + test_2 = self.probability_engine.detect_straight_flush([eval7.Card('5h'), eval7.Card('Tc')], [eval7.Card('6h'), eval7.Card('8h'), eval7.Card('9h'), eval7.Card('Kd')]) + test_2 = (round(test_2[0], 2), test_2[1]) == (0.02, 1) + test_3 = self.probability_engine.detect_straight_flush([eval7.Card('Ac'), eval7.Card('Tc'), eval7.Card('Jc')], [eval7.Card('Th'), eval7.Card('Qc'), eval7.Card('9h')]) + test_3 = (round(test_3[0], 2), test_3[1]) == (0.04, 3) + test_4 = self.probability_engine.detect_straight_flush([eval7.Card('Ac'), eval7.Card('Tc')], [eval7.Card('Jc'), eval7.Card('Qh'), eval7.Card('9h')]) + test_4 = (round(test_4[0], 3), test_4[1]) == (0.001, 2) + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + passed.append('test_4') if test_4 else failed.append('test_4') + + print('Finished running straight_flush Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + + def test_two_pair(self): + print('==================================================') + print('Running two_pair Unit Tests') + + test_1 = self.probability_engine.detect_two_pair([eval7.Card('Th'), eval7.Card('7c')], [eval7.Card('As'), eval7.Card('7h'), eval7.Card('4d')], 'Pair') + test_1 = (round(test_1[0], 2), test_1[1]) == (0.37, 1) + test_2 = self.probability_engine.detect_two_pair([eval7.Card('3h'), eval7.Card('4h'), eval7.Card('Td')], [eval7.Card('7c'), eval7.Card('9s'), eval7.Card('9c')], 'Pair') + test_2 = (round(test_2[0], 2), test_2[1]) == (0.37, 1) + test_3 = self.probability_engine.detect_two_pair([eval7.Card('Ac'), eval7.Card('Td')], [eval7.Card('3h'), eval7.Card('5s'), eval7.Card('9d'), eval7.Card('8h')], 'High Card') + test_3 = (round(test_3[0], 2), test_3[1]) == (0, 0) + + passed = [] + failed = [] + passed.append('test_1') if test_1 else failed.append('test_1') + passed.append('test_2') if test_2 else failed.append('test_2') + passed.append('test_3') if test_3 else failed.append('test_3') + + print('Finished running two_pair Unit Tests') + if len(failed) == 0: + print('Passed all tests') + else: + print(f'Failed {len(failed)} tests') + print(f'Passed: {passed}') + print(f'Failed: {failed}') + if len(failed) == 0: + return True + return False + +if __name__ == '__main__': + unit_tests = Unit_Tests() + unit_tests.test_all() + \ No newline at end of file diff --git a/research.ipynb b/research.ipynb index 7338507..1881412 100644 --- a/research.ipynb +++ b/research.ipynb @@ -1,17 +1,60 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('a', 2)\n" + ] + } + ], + "source": [ + "import eval7\n", + "\n", + "a = {'a': 2}\n", + "\n", + "print(max(a.items(), key=lambda x: x[1]))" + ] + }, { "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['6', '9', 'T', 'J', 'K', 'K']\n" + ] + } + ], "source": [ - "import eval7" + "sample = 'KT96JK'\n", + "def rank_order(char):\n", + " if char == 'T':\n", + " return 4\n", + " elif char == 'J':\n", + " return 3\n", + " elif char == 'Q':\n", + " return 2\n", + " elif char == 'K':\n", + " return 1\n", + " elif char == 'A':\n", + " return 0\n", + " else:\n", + " return 14 - int(char)\n", + "print(sorted(sample, key=rank_order))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [