lab_dice/yahtzee.py

159 lines
7.1 KiB
Python

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