#http://www.codeskulptor.org/#user35_dRz3TzmJtMhxNLz.py

"""
Planner for Yahtzee
Simplifications:  only allow discard and roll, only score against upper level
"""

# 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 
    """
    if (hand==()):
        return 0
    score_board=[0,0,0,0,0,0,0,0,0,0,0,0]
    for dice in hand:
        score_board[dice-1]+=dice
    max_score=max(score_board)
    return max_score


def expected_value(held_dice, num_die_sides, num_free_dice):
    """
    Compute the expected value of the 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
    """
    all_sequences = gen_all_sequences(range(1,num_die_sides+1), num_free_dice)
    iter_seque=[]
    score_seque=[]
    for seq in all_sequences:
        iter_seque.append(list(seq)+list(held_dice))
        score_seque.append(score(iter_seque[-1]))
    return float(sum(score_seque))/float(len(score_seque))


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
    """
    without_repeat = []
    mask_seque = list(gen_all_sequences([0,1], len(hand)))
    for dum_i in mask_seque:
        without_repeat.append(())
        
    for dum_i in range(len(mask_seque)):
        for dum_j in range(len(mask_seque[dum_i])):
            if (mask_seque[dum_i][dum_j]==1):
                without_repeat[dum_i]=list(without_repeat[dum_i])
                without_repeat[dum_i].append(hand[dum_j])
                without_repeat[dum_i]=tuple(without_repeat[dum_i])
    
    without_repeat = set(tuple(without_repeat))
    return without_repeat


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))
    expect=[]
    for held_dice in all_holds:
        expect.append(expected_value(held_dice, num_die_sides, len(hand)-len(held_dice)))
    max_expect_index = expect.index(max(expect))
    return (max(expect), (all_holds[max_expect_index]))


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()
    
#import poc_holds_testsuite
#poc_holds_testsuite.run_suite(gen_all_holds)
                                           
#import user35_oGFuhcPNLh_0 as score_testsuite
#score_testsuite.run_suite(score)
    
#expected value   
#    import user35_uLOFnLQSJV29rFh_5 as expected_value_testsuite
#    expected_value_testsuite.run_suite(expected_value)
    
#    import poc_holds_testsuite
#    poc_holds_testsuite.run_suite(gen_all_holds)
#Here's some tests for strategy function:
#    import user35_mGREPnDxbs_0 as strategy_testsuite
#    strategy_testsuite.run_suite(strategy)


cd046328053b253932bb0171ee6d0481b08f253a
#http://www.codeskulptor.org/#user35_dRz3TzmJtMhxNLz.py

"""
Planner for Yahtzee
Simplifications:  only allow discard and roll, only score against upper level
"""

# 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 
    """
    if (hand==()):
        return 0
    score_board=[0,0,0,0,0,0,0,0,0,0,0,0]
    for dice in hand:
        score_board[dice-1]+=dice
    max_score=max(score_board)
    return max_score


def expected_value(held_dice, num_die_sides, num_free_dice):
    """
    Compute the expected value of the 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
    """
    all_sequences = gen_all_sequences(range(1,num_die_sides+1), num_free_dice)
    iter_seque=[]
    score_seque=[]
    for seq in all_sequences:
        iter_seque.append(list(seq)+list(held_dice))
        score_seque.append(score(iter_seque[-1]))
    return float(sum(score_seque))/float(len(score_seque))


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
    """
    without_repeat = []
    mask_seque = list(gen_all_sequences([0,1], len(hand)))
    for dum_i in mask_seque:
        without_repeat.append(())
        
    for dum_i in range(len(mask_seque)):
        for dum_j in range(len(mask_seque[dum_i])):
            if (mask_seque[dum_i][dum_j]==1):
                without_repeat[dum_i]=list(without_repeat[dum_i])
                without_repeat[dum_i].append(hand[dum_j])
                without_repeat[dum_i]=tuple(without_repeat[dum_i])
    
    without_repeat = set(tuple(without_repeat))
    return without_repeat


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))
    expect=[]
    for held_dice in all_holds:
        expect.append(expected_value(held_dice, num_die_sides, len(hand)-len(held_dice)))
    max_expect_index = expect.index(max(expect))
    return (max(expect), (all_holds[max_expect_index]))


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()
    
#import poc_holds_testsuite
#poc_holds_testsuite.run_suite(gen_all_holds)
                                           
#import user35_oGFuhcPNLh_0 as score_testsuite
#score_testsuite.run_suite(score)
    
#expected value   
#    import user35_uLOFnLQSJV29rFh_5 as expected_value_testsuite
#    expected_value_testsuite.run_suite(expected_value)
    
#    import poc_holds_testsuite
#    poc_holds_testsuite.run_suite(gen_all_holds)
#Here's some tests for strategy function:
#    import user35_mGREPnDxbs_0 as strategy_testsuite
#    strategy_testsuite.run_suite(strategy)

