The Yahtzee Engine
A hard Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- hard
- Seniority
- L4
- Asked in
- Optimove
Problem
Given 5 dice, return a dict with scores for all Yahtzee categories: 'ones' through 'sixes' (sum of matching dice), 'three_of_a_kind' (sum of all if >=3 match else 0), 'four_of_a_kind', 'full_house' (25 if 3+2 pattern), 'small_straight' (30 if 4 consecutive), 'large_straight' (40 if 5 consecutive), 'yahtzee' (50 if all 5 match), 'chance' (sum of all).
Five dice. Six faces. Score it.
Example 1
Input:
dice = [1,1,1,2,3]
Output:
{"ones":3,"twos":2,"threes":3,"fours":0,"fives":0,"sixes":0,"three_of_a_kind":8,"four_of_a_kind":0,"full_house":0,"small_straight":0,"large_straight":0,"yahtzee":0,"chance":8}Example 2
Input:
dice = [1,2,3,4,5]
Output:
{"ones":1,"twos":2,"threes":3,"fours":4,"fives":5,"sixes":0,"three_of_a_kind":0,"four_of_a_kind":0,"full_house":0,"small_straight":30,"large_straight":40,"yahtzee":0,"chance":15}Worked solution and explanation
Why this problem exists in real interviews
Strip the game away and this is thirteen predicates all reading one shared face-count profile: how many dice show each face, the shape of those counts, and which faces are present at all. Anyone can sum five dice. What separates candidates is reading 'at least' as >= rather than ==, testing full house on the whole count profile rather than the biggest count, and finding straights by presence rather than by positions in a sorted list. Get any one wrong and a specific hand breaks: five identical dice become a full house, four fours lose their three-of-a-kind score, or the repeated 2 in 1, 2, 2, 3, 4 erases a small straight.
Break down the requirements
Step 1: Count die face frequencies
Build a face-count dict from the five dice with freq.get(d, 0) + 1. Almost every category downstream reads this one table instead of rescanning the raw list.
Step 2: Score the upper section (ones through sixes)
For ones through sixes, score face * freq.get(face, 0): only the dice matching that face contribute.
Step 3: Score n-of-a-kind, full house, and yahtzee
three_of_a_kind and four_of_a_kind pay the whole-hand total when max(freq.values()) clears the threshold, else 0. full_house needs a 3-and-2 split, so 2 in counts and 3 in counts. yahtzee needs max_freq == 5. Note that five-of-a-kind is a yahtzee, never a full_house.
Step 4: Detect the straights
A small_straight is any run of 4 consecutive faces, a large_straight a run of 5. Test each candidate run by asking whether every face in it is present in the frequency dict: membership is what matters, not order, so no sort is needed and a repeated face cannot shift the run out of place.
Step 5: Score chance
chance is just sum(dice), regardless of pattern.
The solution
def yahtzee_scores(dice: list) -> dict:
freq = {}
for d in dice:
freq[d] = freq.get(d, 0) + 1
total = sum(dice)
max_freq = max(freq.values())
scores = {}
names = ["ones", "twos", "threes", "fours", "fives", "sixes"]
for i in range(6):
face = i + 1
scores[names[i]] = face * freq.get(face, 0)
scores["three_of_a_kind"] = total if max_freq >= 3 else 0
scores["four_of_a_kind"] = total if max_freq >= 4 else 0
counts = list(freq.values())
scores["full_house"] = 25 if (2 in counts and 3 in counts) else 0
has_small = False
for start in [1, 2, 3]:
if all(v in freq for v in range(start, start + 4)):
has_small = True
break
scores["small_straight"] = 30 if has_small else 0
has_large = False
for start in [1, 2]:
if all(v in freq for v in range(start, start + 5)):
has_large = True
break
scores["large_straight"] = 40 if has_large else 0
scores["yahtzee"] = 50 if max_freq == 5 else 0
scores["chance"] = total
return scoresCommon follow-up questions
- How would you add bonus `yahtzee` scoring? (Tests tracking whether the player already scored a Yahtzee and applying the 100-point bonus.)
- What if you needed to recommend the best category for a given roll? (Tests iterating all categories and selecting the maximum score.)
- How would you extend this to support custom dice (e.g., 8-sided)? (Tests parameterizing the face range and adjusting straight detection.)
- How would you implement the full game state with 13 rounds? (Tests stateful class design tracking used categories and cumulative score.)