diff --git a/yahtzee.py b/yahtzee.py index 69b0e97..1331ce8 100644 --- a/yahtzee.py +++ b/yahtzee.py @@ -10,6 +10,11 @@ class Yahtzee: 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: @@ -19,6 +24,16 @@ class Yahtzee: 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: @@ -28,11 +43,31 @@ class Yahtzee: 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: @@ -48,7 +83,18 @@ class Yahtzee: 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}") @@ -59,6 +105,15 @@ class Yahtzee: 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: @@ -68,23 +123,62 @@ class Yahtzee: 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 one’s `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: @@ -92,7 +186,18 @@ class Yahtzee: 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): @@ -101,7 +206,20 @@ class Yahtzee: 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]