Files
lab_dice/yahtzee.py
tsmith37 c9f215b5d6 Finished with checkpoint 2 adding docstrings
When I write code, Im focused on how to make something work and making sure everything runs correctly.
But when I write docstrings Im thinking about how to explain the purpose of the method and how it works
in a way another person could understand.
2025-11-18 19:22:13 -05:00

230 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from die import Die
class Yahtzee:
"""A command-line Yahtzee game.
This version of Yahtzee is initialized with a list of goals.
"""
def __init__(self, goals):
self.score = 0
self.goals = goals
self.dice = [Die() for num in range(5)]
def play(self):
"""Play an entire game.
Starts by greeting the user, then plays rounds until all the goals
have been used. When the game is over, tells the player their final
score.
"""
print("Welcome to Yachtzee!")
self.score = 0
for goal in self.goals:
goal.used = False
while self.count_unused_goals() > 0:
self.play_round()
print(f"Your final score was {self.score}")
def play_round(self):
"""
Plays a single round of the game.
This method resets the number of rolls left, rolls all dice once,
and displays the current game status to the player. It then prompts
the player to choose a scoring goal for the round, marks that goal
as used, and updates the player's total score based on the dice
rolled. The method manages the full sequence of actions required
to complete one round from start to finish.
"""
print("=" * 80)
self.rolls_left = 3
for die in self.dice:
die.roll()
self.show_status()
goal = self.choose_goal()
goal.used = True
self.score += goal.score(self.dice)
def show_status(self):
"""
Displays the current game status to the player.
This method gathers the values of all dice, formats them into a
readable string, and prints the player's current score, the number
of rolls remaining, and the current state of the dice. It is used
to give the player a clear snapshot of their progress during a round.
"""
dice = ', '.join([str(die) for die in self.dice])
print(f"Score: {self.score}. Rolls left: {self.rolls_left}. Dice: {dice}.")
def choose_goal(self):
"""
Prompts the player to choose a scoring goal or re-roll.
This method gathers all unused scoring goals and displays them as
options for the player, along with a “Re-roll” option if rolls are
still available. It uses the player's choice to either trigger a
re-roll—after which it shows the updated status and recursively
prompts again—or to return the selected scoring goal. This method
manages the full decision-making process for selecting how the
current dice should be scored.
"""
options = []
unused_goals = self.get_unused_goals()
for goal in unused_goals:
option = goal.prompt(self.dice)
options.append(option)
if self.rolls_left > 0:
options.append("Re-roll")
choice = self.get_choice(options)
if options[choice] == "Re-roll":
self.reroll()
self.show_status()
return self.choose_goal()
else:
return unused_goals[choice]
def get_choice(self, options):
"""
Gets a valid choice from the player based on a list of options.
This method prints a numbered list of available options and prompts
the player for an input. It repeatedly checks the player's response
using `option_choice_is_valid()` and continues asking until a valid
selection is entered. Once validated, the method returns the chosen
option as an integer index that can be used to access the selected
item in the options list.
"""
print("What would you like to do?")
for i, option in enumerate(options):
print(f"{i}. {option}")
choice = input("> ")
while not self.option_choice_is_valid(choice, options):
print("Sorry, that's not a valid choice.")
choice = input("> ")
return int(choice)
def option_choice_is_valid(self, choice, options):
"""
Checks whether the player's menu choice is valid.
This method verifies that the player's input is a digit and then
converts it to an integer to ensure it falls within the valid range
of option indices. It returns False if the input is not numeric,
negative, or exceeds the number of available options. Otherwise,
it returns True, indicating that the choice can be safely used.
"""
if not choice.isdigit():
return False
if int(choice) < 0:
return False
if int(choice) >= len(options):
return False
return True
def count_unused_goals(self):
"""
Returns the number of scoring goals that have not been used yet.
This method calls `get_unused_goals()` to retrieve the list of all
goals that are still available for scoring and returns the length
of that list. It serves as a quick way to track how many scoring
options remain in the game.
"""
return len(self.get_unused_goals())
def get_unused_goals(self):
"""
Returns a list of all scoring goals that have not been used yet.
This method iterates through the full collection of goals and checks
each ones `used` attribute. Any goal that has not been marked as used
is added to a new list, which is returned at the end. This provides the
game with an up-to-date list of scoring options still available to the
player.
"""
unused_goals = []
for goal in self.goals:
if not goal.used:
unused_goals.append(goal)
return unused_goals
def reroll(self):
"""
Performs a reroll of selected dice during the round.
This method decreases the number of rolls left, retrieves the list of
dice indices the player is allowed to reroll, and then determines which
dice the player actually wants to reroll by calling `get_dice_to_reroll()`.
It then loops through the chosen dice and rolls each one. This function
manages the full reroll process, updating both the game state and the dice.
"""
self.rolls_left -= 1
choices = self.get_reroll_choices()
dice_to_reroll = self.get_dice_to_reroll(choices)
for die in dice_to_reroll:
die.roll()
def get_dice_to_reroll(self, choice_ints):
"""
Determines which dice the player wants to reroll based on their choices.
This method receives a list of integers representing the dice faces the
player selected for rerolling. It iterates through all dice in the game,
checks whether each die's current face value appears in the list, and if
so, removes that value from the list and adds the die to the reroll list.
This ensures that each selected face value corresponds to one die only.
The method returns a list of dice objects that should be rerolled.
"""
dice_to_reroll = []
for die in self.dice:
if die.face in choice_ints:
choice_ints.remove(die.face)
dice_to_reroll.append(die)
return dice_to_reroll
def get_reroll_choices(self):
"""
Prompts the player to choose which dice they want to reroll.
This method asks the player to enter the face values of the dice they
wish to reroll as a string of digits. It repeatedly validates the input
using `reroll_choices_are_valid()` and continues prompting until the
entered choices meet the required format. Once validated, the method
converts each character in the input string into an integer and returns
the resulting list of selected face values.
"""
print("Which dice do you want to re-roll?")
choices = input("> ")
while not self.reroll_choices_are_valid(choices):
print("Please enter the numbers on dice you want to re-roll.")
choices = input("> ")
choice_ints = [int(digit) for digit in choices]
return choice_ints
def reroll_choices_are_valid(self, choices_str):
"""
Validates the player's input for selecting dice to reroll.
This method first checks that the player's input consists only of
digits. It then converts each digit into an integer representing a
desired die face. The method verifies these selections by matching
each requested face value to an existing die face in the current set
of dice. For every valid match, that face value is removed from the
list of choices. If, after processing all dice, no unmatched values
remain, the input is considered valid. Otherwise, the method returns
False to indicate an invalid selection.
"""
if not choices_str.isdigit():
return False
choice_ints = [int(digit) for digit in choices_str]
for die in self.dice:
if die.face in choice_ints:
choice_ints.remove(die.face)
return len(choice_ints) == 0