forked from colonelpanic/dotfiles
Add O-Hell python module
This commit is contained in:
parent
e04caabf07
commit
e668a84e81
261
dotfiles/lib/python/ohell.py
Normal file
261
dotfiles/lib/python/ohell.py
Normal file
@ -0,0 +1,261 @@
|
||||
import collections
|
||||
import random
|
||||
import pprint
|
||||
|
||||
do_print = False
|
||||
|
||||
def maybe_print(*args):
|
||||
if do_print:
|
||||
print(*args)
|
||||
|
||||
|
||||
def falling_factorial(n, k):
|
||||
current = n
|
||||
product = 1
|
||||
for _ in range(k):
|
||||
product *= current
|
||||
current -= 1
|
||||
|
||||
|
||||
def one_hand_with_lead_odds(card, num_players, is_trump=False, revealed_card=6, revealed_in_suit=False):
|
||||
num_stronger_cards = 14 - card
|
||||
if is_trump:
|
||||
if card < revealed_card:
|
||||
num_stronger_cards -= 1
|
||||
elif is_trump is None:
|
||||
if revealed_in_suit:
|
||||
if card < revealed_card:
|
||||
num_stronger_cards -= 1
|
||||
else:
|
||||
num_stronger_cards += 12
|
||||
|
||||
odds_no_stronger_card_out = 1.0
|
||||
|
||||
num_cards_remaining = 50
|
||||
num_weaker_cards_remaining = num_cards_remaining - num_stronger_cards
|
||||
|
||||
for i in range(num_players-1):
|
||||
odds_no_stronger_card_out *= num_weaker_cards_remaining/num_cards_remaining
|
||||
num_weaker_cards_remaining -= 1
|
||||
num_cards_remaining -= 1
|
||||
|
||||
return odds_no_stronger_card_out
|
||||
|
||||
|
||||
def expected_value_of_one_bid(*args, **kwargs):
|
||||
win_prob = one_hand_with_lead_odds(*args, **kwargs)
|
||||
one_ev = win_prob + ((1 - win_prob) * -1)
|
||||
zero_ev = -win_prob
|
||||
|
||||
return (one_ev - zero_ev, win_prob, one_ev, zero_ev)
|
||||
|
||||
|
||||
def odds_for_number_of_players(num_players, is_trump=False):
|
||||
for card in range(2, 15):
|
||||
print(card)
|
||||
print(expected_value_of_one_bid(card, num_players, is_trump=is_trump))
|
||||
|
||||
|
||||
def random_permutation_of_size(n):
|
||||
remaining = list(range(n))
|
||||
for i in range(n-1, -1, -1):
|
||||
yield remaining.pop(random.randint(0, i))
|
||||
|
||||
|
||||
def random_permutation(the_list):
|
||||
for index in random_permutation_of_size(len(the_list)):
|
||||
yield the_list[index]
|
||||
|
||||
|
||||
deck_of_cards = [
|
||||
(number, suit)
|
||||
for number in range(2, 15)
|
||||
for suit in range(4)
|
||||
]
|
||||
|
||||
|
||||
def compare_cards(card1, card2, trump_suit, led_suit):
|
||||
card1value, card1suit = card1
|
||||
card2value, card2suit = card2
|
||||
if card1suit == card2suit and card1suit in (trump_suit, led_suit):
|
||||
return card1value - card2value
|
||||
|
||||
if card1suit == trump_suit:
|
||||
return 1
|
||||
|
||||
if card2suit == trump_suit:
|
||||
return -1
|
||||
|
||||
if card1suit == led_suit:
|
||||
return 1
|
||||
|
||||
if card2suit == led_suit:
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
lost_hands = collections.defaultdict(int)
|
||||
won_hands = collections.defaultdict(int)
|
||||
|
||||
|
||||
performance_by_player_card = collections.defaultdict(
|
||||
lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int)))
|
||||
)
|
||||
|
||||
|
||||
def play_hand(player_order, player_to_strategy, player_scores, score_first_only=True):
|
||||
bids = {}
|
||||
shuffled_deck = list(random_permutation(deck_of_cards))
|
||||
trump_card = shuffled_deck[len(player_order)]
|
||||
trump_suit = None if trump_card[0] in [11, 12, 13] else trump_card[1]
|
||||
led_suit = shuffled_deck[0][1]
|
||||
highest_card = (0, led_suit)
|
||||
winning_player = player_order[0]
|
||||
maybe_print("Trump is {}, {} leads".format(trump_suit, player_order[0]))
|
||||
if (trump_suit is not None and shuffled_deck[0][1] != trump_suit and
|
||||
shuffled_deck[0][0] <= 8 and shuffled_deck[0][0] > 5):
|
||||
# import ipdb; ipdb.set_trace()
|
||||
pass
|
||||
for player, card in zip(player_order, shuffled_deck):
|
||||
if compare_cards(card, highest_card, trump_suit, led_suit) > 0:
|
||||
highest_card = card
|
||||
winning_player = player
|
||||
bids[player] = player_to_strategy[player](card, trump_card, bids, len(player_order))
|
||||
maybe_print("{} got {} and bid {}".format(player, card, bids[player]))
|
||||
maybe_print("{} won the hand with {}".format(winning_player, highest_card))
|
||||
|
||||
for player in player_order:
|
||||
bid = bids[player]
|
||||
score = 0
|
||||
if winning_player == player:
|
||||
if bid == 1:
|
||||
score = 1
|
||||
won_hands[player] += 1
|
||||
else:
|
||||
score = -1
|
||||
lost_hands[player] += 1
|
||||
else:
|
||||
if bid != 0:
|
||||
score = -1
|
||||
lost_hands[player] += 1
|
||||
|
||||
player_scores[player] += score
|
||||
|
||||
trump_of_card = None if trump_suit is None else shuffled_deck[0][1] == trump_suit
|
||||
performance_dict = performance_by_player_card[player][trump_of_card][shuffled_deck[0][0]]
|
||||
performance_dict[score] += 1
|
||||
performance_dict["total"] += score
|
||||
|
||||
if score_first_only:
|
||||
break
|
||||
|
||||
|
||||
def optimal_strategy(card, trump_card, bids, number_of_players):
|
||||
trump_suit = None if trump_card[0] in [11, 12, 13] else trump_card[1]
|
||||
card_is_trump = card[1] == trump_card[1]
|
||||
if trump_suit == None:
|
||||
card_is_trump = None
|
||||
|
||||
if len(bids) == 0:
|
||||
odds = expected_value_of_one_bid(
|
||||
card[0], number_of_players, is_trump=card_is_trump,
|
||||
revealed_card=trump_card[0], revealed_in_suit=card[1] == trump_card[1]
|
||||
)
|
||||
return 1 if odds[0] > 0 else 0
|
||||
|
||||
if card_is_trump:
|
||||
return handle_non_first_bid(card, trump_card, bids, number_of_players)
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def handle_non_first_bid(card, trump_card, bids, number_of_players):
|
||||
count_greater = 14 - card[0]
|
||||
count_smaller = card[0] - 2
|
||||
|
||||
if trump_card[0] < card[0]:
|
||||
count_smaller -= 1
|
||||
else:
|
||||
count_greater -= 1
|
||||
|
||||
odds_of_random_trump_smaller = float(count_smaller) / (count_smaller + count_greater)
|
||||
bid_sum = sum(bids.values())
|
||||
|
||||
if bid_sum == 0:
|
||||
return 1
|
||||
elif bid_sum == 1 and odds_of_random_trump_smaller > (float(1)/3):
|
||||
return 1
|
||||
elif bid_sum == 2 and count_greater <= 3:
|
||||
return 1
|
||||
elif count_greater <= 1:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def mom_strat(card, trump_card, bids, number_of_players):
|
||||
trump_suit = None if trump_card[0] in [11, 12, 13] else trump_card[1]
|
||||
card_is_trump = card[1] == trump_card[1]
|
||||
if trump_suit == None:
|
||||
card_is_trump = None
|
||||
|
||||
if len(bids) > 0:
|
||||
if card_is_trump:
|
||||
return handle_non_first_bid(card, trump_card, bids, number_of_players)
|
||||
return 0
|
||||
|
||||
if trump_suit == None:
|
||||
if card[0] >= 5:
|
||||
return 1
|
||||
|
||||
if card_is_trump:
|
||||
return 1
|
||||
|
||||
if card[0] >= 13:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
player_strategies = {
|
||||
"optimal": optimal_strategy,
|
||||
"mom1": mom_strat,
|
||||
"mom2": mom_strat,
|
||||
"mom3": mom_strat,
|
||||
}
|
||||
|
||||
|
||||
def simulate_hands(strats, number_of_hands):
|
||||
player_order = list(player_strategies.keys())
|
||||
scores = collections.defaultdict(int)
|
||||
for _ in range(number_of_hands):
|
||||
play_hand(player_order, strats, scores)
|
||||
player_order = player_order[-1:] + player_order[:-1]
|
||||
|
||||
print(scores)
|
||||
return scores
|
||||
|
||||
|
||||
odds_for_number_of_players(4, is_trump=False)
|
||||
print("score")
|
||||
scores = simulate_hands(player_strategies, 100000)
|
||||
highest_score = -10000000
|
||||
winner = None
|
||||
for player, score in scores.items():
|
||||
if score > highest_score:
|
||||
highest_score = score
|
||||
winner = player
|
||||
print("{} won with {}".format(winner, highest_score))
|
||||
print("won")
|
||||
print(won_hands)
|
||||
print("lost")
|
||||
print(lost_hands)
|
||||
|
||||
# for player, by_trump in performance_by_player_card.items():
|
||||
# print(player)
|
||||
# for trump_type, card_to_score in by_trump.items():
|
||||
# print("{}: {}".format(trump_type, sum(card_to_score.values())))
|
||||
|
||||
pprint.pprint(performance_by_player_card["optimal"][None])
|
||||
pprint.pprint(performance_by_player_card["mom1"][None])
|
Loading…
Reference in New Issue
Block a user