from die import Die class Yachtzee: """A command-line Yahtzee game. This version of Yahtzee is initialized with a list of goals. """ def __init__(self, goals): """ This method specifies the default properties of a Yachtzee object. The object has a score set to 0, a list of goals that is specified when the object is created, and a list of five dice. """ self.score = 0 self.goals = goals self.dice = [Die() for num in range(5)] def play(self): """ This method allows for a game of Yachtzee to be played. While at least one goal remains unused, the game continues. Once all goals are used, the score is printed. """ print("Welcome to Yachtzee!") while self.count_unused_goals() > 0: self.play_round() print(f"Your final score was {self.score}") def play_round(self): """ This method allows for a round to be played. The player starts with 3 rolls of the dice. Then, all dice are rolled. The method then checks the state (status and goals fulfilled) of the game and updates the score accordingly based on the goal selected. The method also records that the goal has been used. """ 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): """ This method prints the status of the game. This prints the score of the player, the number of rolls left, what was rolled. """ 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): """ This method allows one to choose which goal to score in a given round. This method lists all the goals one can score. If any rolls are left, the player is also given the option to re-roll. The player selects the option they want. If they choose to re-roll, the re-roll method is called and then players can see what their dice are and choose a goal to be scored (or they can choose to re-roll if any rolls are left). """ 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): """ This method returns a list of possible goals to be scored. This method creates a numbered list of goals. The player's choice is solicited and returned as an integer. If the choice is not valid, the player is prompted to select again a goal again.""" 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): """ This method checks if a player's choice is valid. This method checks that the choice is a non-negative number strictly less than the length of the length. This is because the first option is numbered 0. """ 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): """ This method reports the number of unscored goals. The method generates the list of unused goals and then calculates and reports the list's length. """ return len(self.get_unused_goals()) def get_unused_goals(self): """ This method reports the unused goals. This method checks each goal to see if it has been used. If it has not, it is added to the list of unused goals, which is reported. """ unused_goals = [] for goal in self.goals: if not goal.used: unused_goals.append(goal) return unused_goals def reroll(self): """ This method rerolls selected dice. The method first reduces the number of re-rolls available left by one. Then the method captures the choice of dice to re-roll and re-rolls only those 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): """ This method creates a list of the dice to be re-rolled. This method goes through each dice. If it is one of the dice that was chosen to be re-rolled, then it is added to the list of dice to be re-rolled. Said list is returned. """ 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): """ This method captures which dice the player would like to re-roll. First, the user is prompted to specify the faces of the dice they would like to re-roll. The method checks that these choices are valid. Then, a list is created where each choice the player made is recorded as an integer, and the list is returned. """ 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): """ This method checks that the choice of dice to re-roll is valid. The method checks that the choices are in a string of only numbers. Then, a list is formed converting all the characters in the string to integers. This list of integers is compared to what was rolled on the dice by checking to see that each die that should be re-rolled is included in the list of desired re-rolls and that nothing else is in the list. If a die is in the list of dice to be re-rolled, that die is removed from the list. Once all dice have been gone through, there should be nothing remaining in the list of dice to be re-rolled. """ 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