diff --git a/__pycache__/dice_stats.cpython-310.pyc b/__pycache__/dice_stats.cpython-310.pyc new file mode 100644 index 0000000..c30e841 Binary files /dev/null and b/__pycache__/dice_stats.cpython-310.pyc differ diff --git a/__pycache__/die.cpython-310.pyc b/__pycache__/die.cpython-310.pyc new file mode 100644 index 0000000..7695437 Binary files /dev/null and b/__pycache__/die.cpython-310.pyc differ diff --git a/__pycache__/yahtzee.cpython-310.pyc b/__pycache__/yahtzee.cpython-310.pyc new file mode 100644 index 0000000..18da627 Binary files /dev/null and b/__pycache__/yahtzee.cpython-310.pyc differ diff --git a/__pycache__/yahtzee_goals.cpython-310.pyc b/__pycache__/yahtzee_goals.cpython-310.pyc new file mode 100644 index 0000000..4a73b11 Binary files /dev/null and b/__pycache__/yahtzee_goals.cpython-310.pyc differ diff --git a/dice_stats.py b/dice_stats.py index 83a99cb..6343f77 100644 --- a/dice_stats.py +++ b/dice_stats.py @@ -1,5 +1,6 @@ from die import Die from tqdm import tqdm +from collections import Counter class FiveDice: def __init__(self): @@ -18,16 +19,44 @@ class FiveDice: if face != 1: return False return True + def is_three_of_a_kind(self): + c = Counter(self.faces()) + of_a_kind = max(c.values()) + if (of_a_kind >= 3): + return True + else: + return False + + def is_four_of_a_kind(self): + c = Counter(self.faces()) + of_a_kind = max(c.values()) + if (of_a_kind >= 4): + return True + else: + return False dice = FiveDice() -successes = 0 -trials = 1000000 -for trial in tqdm(range(trials)): +successes_3 = 0 +trials_3 = 1000000 +for trial in tqdm(range(trials_3)): dice.roll() - if dice.all_ones(): - successes += 1 + if dice.is_three_of_a_kind(): + successes_3 += 1 -print(successes/trials) +print(successes_3/trials_3) + +successes_4 = 0 +trials_4 = 1000000 +for trial in tqdm(range(trials_4)): + dice.roll() + if dice.is_four_of_a_kind(): + successes_4 += 1 + +print(successes_4/trials_4) + +test_dice = FiveDice() +counter = Counter(test_dice.faces()) +print(counter.keys()) diff --git a/play.py b/play.py index a8e683c..6feacef 100644 --- a/play.py +++ b/play.py @@ -3,12 +3,32 @@ from yahtzee_goals import ( GoalOnes, GoalTwos, GoalThrees, + GoalFours, + GoalFives, + GoalSixes, + Goal_Three_of_a_Kind, + Goal_Four_of_a_Kind, + GoalFullHouse, + GoalSmallStraight, + GoalLargeStraight, + GoalYahtzee, + GoalChance ) goals = [ GoalOnes(), GoalTwos(), GoalThrees(), + GoalFours(), + GoalFives(), + GoalSixes(), + Goal_Three_of_a_Kind(), + Goal_Four_of_a_Kind(), + GoalFullHouse(), + GoalSmallStraight(), + GoalLargeStraight(), + GoalYahtzee(), + GoalChance() ] game = Yachtzee(goals) diff --git a/pyproject.toml b/pyproject.toml index 1934113..aff548f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,6 @@ name = "lab-dice" version = "0.1.0" description = "" authors = ["Chris Proctor "] -readme = "README.md" [tool.poetry.dependencies] python = "^3.10" diff --git a/yahtzee.py b/yahtzee.py index 8ae2b41..93c9424 100644 --- a/yahtzee.py +++ b/yahtzee.py @@ -5,17 +5,28 @@ class Yachtzee: This version of Yahtzee is initialized with a list of goals. """ def __init__(self, goals): + '''Initializes a game of Yahtzee. The initial score is 0, and all goals are available. + Also creates the five dice that will be used for this game.''' self.score = 0 self.goals = goals self.dice = [Die() for num in range(5)] def play(self): - print("Welcome to Yachtzee!") + '''Plays a game of Yahtzee. + Starts by welcoming the player and will continue playing rounds until all goals have been used. + Once all goals have been used, the game ends and the final score is presented to the player.''' + print("Welcome to Yahtzee!") 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 Yahtzee. + Prints a line to divide this round from the previous round. + The five dice are rolled to start, and the player is shown the current game status. + The player can choose to re-roll up to three times with the ultimate end choosing a goal. + The round concludes when a player chooses a goal to apply their dice toward. + This goal is marked as used, and the appropriate score from that round is added to the total.''' print("=" * 80) self.rolls_left = 3 for die in self.dice: @@ -26,10 +37,16 @@ class Yachtzee: self.score += goal.score(self.dice) def show_status(self): + '''Shows the player their current status. + Displays the currrent values on the faces of the five dice, the current score and + the number of re-rolls remaining for this 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): + '''Finds which goals the player can choose from and whether or not the player can re-roll. + If the player has re-rolls remaining and chooses to re-roll, they can. + Otherwise, they choose a goal and this function returns the goal chosen.''' options = [] unused_goals = self.get_unused_goals() for goal in unused_goals: @@ -46,6 +63,11 @@ class Yachtzee: return unused_goals[choice] def get_choice(self, options): + '''Prompts the player to choose an option, either a goal or to re-roll + depending on whether the player has re-rolls remaining. + Each option is assigned a number. Checks to see if the player's response is + valid. Informs the player if their choice is invalid. When the player chooses + a valid option, the number corresponding to that option is returned''' print("What would you like to do?") for i, option in enumerate(options): print(f"{i}. {option}") @@ -56,6 +78,12 @@ class Yachtzee: return int(choice) def option_choice_is_valid(self, choice, options): + '''Checks to see if the option a player chose was valid. + First checks to see if the option chosen was presented in the form of a number. + If the choice was a number, then checks to see if that number was one of the + possible options presented to the player. If it was then the choice is valid. + If the choice was not a number or if the number was not a possible option, + then the choice is not valid.''' if not choice.isdigit(): return False if int(choice) < 0: @@ -65,9 +93,11 @@ class Yachtzee: return True def count_unused_goals(self): + '''Returns the number of goals the player has not yet used.''' return len(self.get_unused_goals()) def get_unused_goals(self): + '''Creates a list of goals the player has not yet used.''' unused_goals = [] for goal in self.goals: if not goal.used: @@ -75,6 +105,9 @@ class Yachtzee: return unused_goals def reroll(self): + '''Re-rolls the dice chosen by the player. + Decreases the number of re-rolls remaining for this round by 1 and + and rolls the dice that need to be re-rolled''' self.rolls_left -= 1 choices = self.get_reroll_choices() dice_to_reroll = self.get_dice_to_reroll(choices) @@ -82,6 +115,9 @@ class Yachtzee: die.roll() def get_dice_to_reroll(self, choice_ints): + '''This function returns the dice that need to be re-rolled. + The dice that the player wants to re-roll are removed and placed into a list + and this list of dice to re-roll is returned''' dice_to_reroll = [] for die in self.dice: if die.face in choice_ints: @@ -90,6 +126,10 @@ class Yachtzee: return dice_to_reroll def get_reroll_choices(self): + '''Prompts the player to select which dice they would like to re-roll. + Checks to see that the input is valid. If it is not valid, the player + is prompted to enter number values of dice they want to re-roll. + Returns a list of the dice values to re-roll.''' print("Which dice do you want to re-roll?") choices = input("> ") while not self.reroll_choices_are_valid(choices): @@ -99,6 +139,10 @@ class Yachtzee: return choice_ints def reroll_choices_are_valid(self, choices_str): + '''This function checks to see if choices for re-rolls are valid. + First checks if the values the player wants to re-roll are numbers. + Then checks that each value the player wants to re-roll corresponds + to a unique dice-value the player currently has.''' if not choices_str.isdigit(): return False choice_ints = [int(digit) for digit in choices_str] diff --git a/yahtzee_goals.py b/yahtzee_goals.py index fce4e5a..9755ff1 100644 --- a/yahtzee_goals.py +++ b/yahtzee_goals.py @@ -1,3 +1,5 @@ +from collections import Counter + class GoalOnes: "One point for each one" @@ -43,3 +45,191 @@ class GoalThrees: if die.face == 3: total += 3 return total + +class GoalFours: + "Four points for each four" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Fours ({potential_score})" + + def score(self, dice): + total = 0 + for die in dice: + if die.face == 4: + total += 4 + return total + +class GoalFives: + "Five points for each five" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Fives ({potential_score})" + + def score(self, dice): + total = 0 + for die in dice: + if die.face == 5: + total += 5 + return total + +class GoalSixes: + "Six points for each six" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Sixes ({potential_score})" + + def score(self, dice): + total = 0 + for die in dice: + if die.face == 6: + total += 6 + return total + +class Goal_Three_of_a_Kind: + "Total of all 5 dice" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Three of a Kind ({potential_score})" + + def score(self, dice): + total = 0 + faces = [] + for die in dice: + faces.append(die.face) + total += die.face + c = Counter(faces) + of_a_kind = max(c.values()) + if (of_a_kind >= 3): + return total + else: + return 0 + + +class Goal_Four_of_a_Kind: + "Total of all 5 dice" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Four of a Kind ({potential_score})" + + def score(self, dice): + total = 0 + faces = [] + for die in dice: + faces.append(die.face) + total += die.face + c = Counter(faces) + of_a_kind = max(c.values()) + if (of_a_kind >= 4): + return total + else: + return 0 + +class GoalFullHouse: + "25 Points" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Full House ({potential_score})" + + def score(self, dice): + faces = [] + for die in dice: + faces.append(die.face) + c = Counter(faces) + if len(c) == 2 and (3 in c.values()) and (2 in c.values()): + return 25 + else: + return 0 + +class GoalSmallStraight: + "30 Points" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Small Straight ({potential_score})" + + def score(self, dice): + faces = [] + for die in dice: + faces.append(die.face) + c = Counter(faces) + if (len(c) >= 4): + if (1 in c.keys()) and (2 in c.keys()) and (3 in c.keys()) and (4 in c.keys()): + return 30 + elif (2 in c.keys()) and (3 in c.keys()) and (4 in c.keys()) and (5 in c.keys()): + return 30 + elif (3 in c.keys()) and (4 in c.keys()) and (5 in c.keys()) and (6 in c.keys()): + return 30 + else: + return 0 + else: + return 0 + +class GoalLargeStraight: + "40 Points" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Large Straight ({potential_score})" + + def score(self, dice): + faces = [] + for die in dice: + faces.append(die.face) + c = Counter(faces) + if (len(c) >= 5): + if (1 in c.keys()) and (2 in c.keys()) and (3 in c.keys()) and (4 in c.keys()) and (5 in c.keys()): + return 40 + elif (2 in c.keys()) and (3 in c.keys()) and (4 in c.keys()) and (5 in c.keys()) and (6 in c.keys()): + return 40 + else: + return 0 + else: + return 0 + +class GoalYahtzee: + "50 Points" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Yahtzee ({potential_score})" + + def score(self, dice): + faces = [] + for die in dice: + faces.append(die.face) + c = Counter(faces) + of_a_kind = max(c.values()) + if (of_a_kind == 5): + return 50 + else: + return 0 + +class GoalChance: + "Total of all 5 dice" + used = False + + def prompt(self, dice): + potential_score = self.score(dice) + return f"Chance({potential_score})" + + def score(self, dice): + total = 0 + faces = [] + for die in dice: + total += die.face + return total