Phase 1 - Simulator

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

from dice import six_sided, four_sided, make_test_dice
from ucb import main, trace, interact

GOAL_SCORE = 100  # The goal of Hog is to score 100 points.
FREE_BACON_ROLLS = 0
######################
# Phase 1: Simulator #
######################

Problem 1

Implement the roll_dice function in hog.py. It takes two arguments: a positive integer called num_rolls giving the number of dice to roll and a dice function. It returns the number of points scored by rolling the dice that number of times in a turn: either the sum of the outcomes or 1 (Pig Out).

To obtain a single outcome of a dice roll, call dice(). You should call dice() exactly num_rolls times in the body of roll_dice. Remember to call dice() exactly num_rolls times even if Pig Out happens in the middle of rolling. In this way, we correctly simulate rolling all the dice together.

Solution:

def roll_dice(num_rolls, dice=six_sided):
    """Simulate rolling the DICE exactly NUM_ROLLS > 0 times.
    Return the sum of the outcomes unless any of the outcomes is 1.
     In that case, return 1.
    num_rolls:  The number of dice rolls that will be made.
    dice:       A function that simulates a single dice roll outcome.
    """
    # These assert statements ensure that num_rolls is a positive integer.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls > 0, 'Must roll at least once.'
    
    "*** YOUR CODE HERE ***"
    
    rolls_sum = 0
    PIG_OUT_VAL = 1
    PIG_OUT = False

    for roll in range (0, num_rolls):
        dice_result = dice()
    
        if dice_result == PIG_OUT_VAL:
            PIG_OUT = True   
        
        rolls_sum += dice_result
    
    if PIG_OUT:
        return PIG_OUT_VAL
    else:
        return rolls_sum

Problem 2

Implement the free_bacon helper function that returns the number of points scored by rolling 0 dice, based on the opponent's current score. You can assume that score is less than 100. For a score less than 10, assume that the first of the two digits is 0.

Solution:

def free_bacon(score):
    """Return the points scored from rolling 0 dice (Free Bacon).

    score:  The opponent's current score.
    """
    assert score < 100, 'The game should be over.'

    "*** YOUR CODE HERE ***"
    FREE_POINTS_MAX = 10
    if score <= FREE_POINTS_MAX:
        return FREE_POINTS_MAX
    
    first_digit = score // 10;
    second_digit = score % 10;

    if first_digit < second_digit:
        return FREE_POINTS_MAX - first_digit
    else:
        return FREE_POINTS_MAX - second_digit

Problem 3

Implement the take_turn function, which returns the number of points scored for a turn by rolling the given dice num_rolls times.

You will need to implement the Free Bacon rule based on opponent_score, which you can assume is less than 100.

Your implementation of take_turn should call both roll_dice and free_bacon when possible.

Solution:

def take_turn(num_rolls, opponent_score, dice=six_sided):
    """Simulate a turn rolling NUM_ROLLS dice, which may be 0 (Free Bacon).
    Return the points scored for the turn by the current player.

    num_rolls:       The number of dice rolls that will be made.
    opponent_score:  The total score of the opponent.
    dice:            A function that simulates a single dice roll outcome.
    """
    # Leave these assert statements here; they help check for errors.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls >= 0, 'Cannot roll a negative number of dice in take_turn.'
    assert num_rolls <= 10, 'Cannot roll more than 10 dice.'
    assert opponent_score < 100, 'The game should be over.'

    "*** YOUR CODE HERE ***"
    return free_bacon(opponent_score) if num_rolls == FREE_BACON_ROLLS else roll_dice(num_rolls, dice)

Problem 4

Implement is_swap, which returns whether or not the scores should be swapped, according to the rules.

The is_swap function takes two arguments: the players' scores. It returns a boolean value to indicate whether the Swine Swap condition is met.

Solution:

def is_swap(player_score, opponent_score):
    """
    Return whether the two scores should be swapped
    """
    # BEGIN PROBLEM 4
    "*** YOUR CODE HERE ***"
    def get_lmd(number):
        return number if number < 10 else get_lmd(number // 10)

    lmd_player = get_lmd(player_score)
    rmd_oponent = opponent_score % 10

    return lmd_player == rmd_oponent

Problem 5

Implement the play function, which simulates a full game of Hog. Players alternate turns rolling dice until one of the players reaches the goal score.

To determine how much dice are rolled each turn, each player uses their respective strategy (Player 0 uses strategy0 and Player 1 uses strategy1). A strategy is a function that, given a player's score and their opponent's score, returns the number of dice that the current player wants to roll in the turn. Each strategy function should be called only once per turn. Don't worry about the details of implementing strategies yet. You will develop them in Phase 3.

When the game ends, play returns the final total scores of both players, with Player 0's score first, and Player 1's score second.

Solution:

This code also includes solutions to other (later) problems - it's the final version of the play function:

def play(strategy0, strategy1, score0=0, score1=0, dice=six_sided, goal=GOAL_SCORE, say=silence):
 """Simulate a game and return the final scores of both players,
  with Player 0's score first, and Player 1's score second.
A strategy is a function that takes two total scores as arguments (the
current player's score, and the opponent's score), and returns a number of
dice that the current player will roll this turn.

strategy0:  The strategy function for Player 0, who plays first.
strategy1:  The strategy function for Player 1, who plays second.
score0:     Starting score for Player 0
score1:     Starting score for Player 1
dice:       A function of zero arguments that simulates a dice roll.
goal:       The game ends and someone wins when this score is reached.
say:        The commentary function to call at the end of the first turn.
"""
player = 0  # Which player is about to take a turn, 0 (first) or 1 (second)

"*** YOUR CODE HERE ***"
def is_game_over(score0, score1, goal_score):
    return score0 >= goal_score  or score1 >= goal_score


def get_current_strategy(strategy0, strategy1, player=0):
    if (player == 0):
        return strategy0
    else:
        return strategy1

def get_num_rolls(score0, score1, player, current_strategy): 
    if (player == 0):
        return current_strategy(score0, score1) 
    else:
        return current_strategy(score1, score0)


def swine_swap(curr_player_score, other_player_score):
    if (is_swap(curr_player_score, other_player_score)):
        curr_player_score, other_player_score = other_player_score, curr_player_score 
    return curr_player_score, other_player_score 

def set_scores(num_rolls, score0, score1, player):
    if (player == 0):
        score0 += take_turn(num_rolls, score1, dice) 
        score0, score1 = swine_swap(score0, score1)
    else:
        score1 += take_turn(num_rolls, score0, dice)
        score1, score0 = swine_swap(score1, score0)

    return score0, score1

commentary = say
while not is_game_over(score0, score1, goal):
    current_strategy = get_current_strategy(strategy0, strategy1, player)
    num_rolls        = get_num_rolls(score0, score1, player, current_strategy)
    score0, score1   = set_scores(num_rolls, score0, score1, player)
    player           = other(player)

    commentary = commentary(score0, score1) 

return score0, score1

Last updated