""" Planner for Yahtzee Simplifications: only allow discard and roll, only score against upper level """ #http://www.codeskulptor.org/#user43_hIq8DOUzOePl1QE_4.py # Used to increase the timeout, if necessary import codeskulptor codeskulptor.set_timeout(20) def gen_all_sequences(outcomes, length): """ Iterative function that enumerates the set of all sequences of outcomes of given length. """ answer_set = set([()]) for dummy_idx in range(length): temp_set = set() for partial_sequence in answer_set: for item in outcomes: new_sequence = list(partial_sequence) new_sequence.append(item) temp_set.add(tuple(new_sequence)) answer_set = temp_set return answer_set def score(hand): """ Compute the maximal score for a Yahtzee hand according to the upper section of the Yahtzee score card. hand: full yahtzee hand Returns an integer score """ scores = [] for die_num in range(max(hand)+1)[1:]: num_dice = hand.count(die_num) scores.append(num_dice*die_num) return max(scores) def expected_value(held_dice, num_die_sides, num_free_dice): """ Compute the expected value based on held_dice given that there are num_free_dice to be rolled, each with num_die_sides. held_dice: dice that you will hold num_die_sides: number of sides on each die num_free_dice: number of dice to be rolled Returns a floating point expected value """ # print "Holding dice ", held_dice poss_rolls = gen_all_sequences(range(num_die_sides+1)[1:], num_free_dice) # print len(poss_rolls), " possible outcomes for ", num_free_dice, "rolls" #poss_scores will store the scores of all possible hands (held_dice + each possible roll) poss_scores = [] for roll in poss_rolls: held = list(held_dice) # print "Evaluating held ", held, " + outcome ", roll for rol in roll: held.append(rol) my_score = score(held) # print "Now holding ", held, " with score " , my_score poss_scores.append(my_score) # print "Possible scores is now", poss_scores # print return float(sum(poss_scores))/len(poss_rolls) def gen_all_holds(hand): """ Generate all possible choices of dice from hand to hold. hand: full yahtzee hand Returns a set of tuples, where each tuple is dice to hold """ all_holds = set([()]) hand_filters = gen_all_sequences([0,1], len(hand)) for hand_filter in hand_filters: hold = [] for dummy_i in range(len(hand_filter)): if hand_filter[dummy_i] == 1: hold.append(hand[dummy_i]) #print hold all_holds.add(tuple(hold)) return all_holds def strategy(hand, num_die_sides): """ Compute the hold that maximizes the expected value when the discarded dice are rolled. hand: full yahtzee hand num_die_sides: number of sides on each die Returns a tuple where the first element is the expected score and the second element is a tuple of the dice to hold """ all_holds = list(gen_all_holds(hand)) values = [] for hold in all_holds: values.append(expected_value(hold, num_die_sides, len(hand)-len(hold))) best_hold = tuple(all_holds[values.index(max(values))]) my_result = [expected_value(best_hold, num_die_sides, len(hand)-len(best_hold)), best_hold] print my_result return tuple(my_result) def run_example(): """ Compute the dice to hold and expected score for an example hand """ num_die_sides = 6 hand = (1, 1, 1, 5, 6) hand_score, hold = strategy(hand, num_die_sides) print "Best strategy for hand", hand, "is to hold", hold, "with expected score", hand_score #run_example() #print score([1,1,3,3,3]) #print expected_value([], 6, 3) #print gen_all_holds([1,2,2]) #print strategy([1,6],6) #import poc_holds_testsuite #poc_holds_testsuite.run_suite(gen_all_holds)