Phase 3 - Strategies

https://inst.eecs.berkeley.edu/~cs61a/su19/proj/hog/#phase-3-strategies

In the third phase, you will experiment with ways to improve upon the basic strategy of always rolling a fixed number of dice. First, you need to develop some tools to evaluate strategies.

Problem 8

Implement the make_averaged function, which is a higher-order function that takes a function fn as an argument. It returns another function that takes the same number of arguments as fn (the function originally passed into make_averaged). This returned function differs from the input function in that it returns the average value of repeatedly calling fn on the same arguments. This function should call fn a total of num_samples times and return the average of the results.

To implement this function, you need a new piece of Python syntax! You must write a function that accepts an arbitrary number of arguments, then calls another function using exactly those arguments.

Solution:

def make_averaged(fn, num_samples=1000):
    """Return a function that returns the average value of FN when called.

    To implement this function, you will have to use *args syntax, a new Python
    feature introduced in this project.  See the project description.

    >>> dice = make_test_dice(4, 2, 5, 1)
    >>> averaged_dice = make_averaged(dice, 1000)
    >>> averaged_dice()
    3.0
    """

    "*** YOUR CODE HERE ***"
    def averaged(*args):
        result = 0;

        for i in range(0, num_samples):
            result = result + fn(*args)
        return result / num_samples
    return averaged

Problem 9

Implement the max_scoring_num_rolls function, which runs an experiment to determine the number of rolls (from 1 to 10) that gives the maximum average score for a turn. Your implementation should use make_averaged and roll_dice.

If two numbers of rolls are tied for the maximum average score, return the lower number. For example, if both 3 and 6 achieve a maximum average score, return 3.

Solution:

def max_scoring_num_rolls(dice=six_sided, num_samples=1000):
    """Return the number of dice (1 to 10) that gives the highest average turn
    score by calling roll_dice with the provided DICE over NUM_SAMPLES times.
    Assume that the dice always return positive outcomes.

    >>> dice = make_test_dice(1, 6)
    >>> max_scoring_num_rolls(dice)
    1
    """

    "*** YOUR CODE HERE ***"
    MAX_SCORING_DEFAULT = 10
    averaged_roll_dice = make_averaged(roll_dice, num_samples)

    max_scoring_roll = MAX_SCORING_DEFAULT
    highest = 0

    for i in range(MAX_SCORING_DEFAULT, 0, -1):
        new_score = averaged_roll_dice(i, dice)
        is_new_highest = new_score > highest
        highest = new_score if is_new_highest else highest
        max_scoring_roll = i if is_new_highest else max_scoring_roll 
    
    return max_scoring_roll

Problem 10

A strategy can take advantage of the Free Bacon rule by rolling 0 when it is most beneficial to do so. Implement bacon_strategy, which returns 0 whenever rolling 0 would give at least margin points and returns num_rolls otherwise.

Solution:

def bacon_strategy(score, opponent_score, margin=8, num_rolls=4):
    """This strategy rolls 0 dice if that gives at least MARGIN points, and
    rolls NUM_ROLLS otherwise.
    """

    free_bacon_points = free_bacon(opponent_score)
    return 0 if free_bacon_points >= margin else num_rolls

Problem 11

A strategy can also take advantage of the Swine Swap rule. The swap_strategy rolls 0 if it would cause a beneficial swap. It also returns 0 if rolling 0 would give at least margin points, unless this would cause a non-beneficial swap. Otherwise, the strategy rolls num_rolls.

def swap_strategy(score, opponent_score, margin=8, num_rolls=4):
    """This strategy rolls 0 dice when it triggers a beneficial swap. It also
    rolls 0 dice if it gives at least MARGIN points and does not trigger a
    non-beneficial swap. Otherwise, it rolls NUM_ROLLS.
    """

    free_bacon_points = free_bacon(opponent_score)
    is_swap_beneficial = opponent_score > score
    is_swap_w_bacon = is_swap(score + free_bacon_points, opponent_score)

    if is_swap_beneficial and is_swap_w_bacon or not is_swap_w_bacon and free_bacon_points >= margin:
        return 0
    else:
        return num_rolls

Last updated