Building from Pieces

Functions and Games


Learning Objectives

  • You can use a list to represent a roll of five dice.
  • You can divide a game rule into small functions and use their returned results in other functions.
  • You can test game logic without starting its graphical framework.
  • You can distinguish the code you are responsible for from unfamiliar framework code.

A graphical game may react to keys, draw shapes, track rounds, and calculate scores. You can work on the scoring rule without changing the rest of the game. Here, you will write three functions for Dice Combos.

The same roll should always receive the same score. We can write and test that calculation before connecting it to the game.

Representing a Dice Roll

Each roll contains five ordinary six-sided dice, represented by a list:

dice = [2, 5, 2, 1, 2]

Each item is an integer from 1 through 6. The scoring code can loop over these values without knowing about coordinates, colors, or keys. Their order does not affect the score; only the largest group of equal values matters. Above, 2 occurs three times, so the largest group has size 3.

Specify the Smallest Rule First

Begin with one focused question:

How many dice show a particular value?

The function boundary is:

Function: count_value(dice, target)
Parameters: a list of five die values and a target from 1 through 6.
Returns: the number of items in dice equal to target.
Does not: print or change dice.

For example:

roll = [2, 5, 2, 1, 2]
assert count_value(roll, 2) == 3
assert count_value(roll, 5) == 1
assert count_value(roll, 6) == 0
assert roll == [2, 5, 2, 1, 2]

The last assertion checks that the roll is unchanged. Counting selected values is familiar from Part 2; here, a function receives the list and returns the count.

Build a Larger Rule from the Smaller One

The next function answers:

What is the largest number of matching dice in this roll?

Its contract is:

Function: largest_group_size(dice)
Parameter: a list of five die values.
Returns: the size of the largest group of equal values. The result is from 1 through 5.
Uses: count_value to count each possible die value.
Does not: print or change dice.

For the roll [2, 5, 2, 1, 2], it should return 3.

This function can call count_value for each possible value and remember the largest answer. A loop through [1, 2, 3, 4, 5, 6] covers every face.

That is function composition:

dice and one target → count_value → one count
dice → largest_group_size → largest count
uses count_value

The loop remembers the largest count, while count_value does each count.

Turn the Group Size into Points

Dice Combos uses this scoring table:

Largest equal groupNamePoints
5five of a kind50
4four of a kind25
3three of a kind15
2one or more pairs5
1all values different0

The final function you will write has this contract:

Function: score_roll(dice)
Parameter: a list of five die values.
Returns: the integer score from the table.
Uses: largest_group_size rather than implementing the counting rule again.
Does not: print or change dice.

For example, both [3, 3, 3, 5, 5] and [3, 3, 3, 4, 5] score 15. The largest group has size 3 in both rolls. This game has no separate full-house rule.

Together the functions form this chain:

score_roll
→ largest_group_size
→ count_value

The scoring table can change without changing the counting algorithm, while a counting fix benefits both larger functions.

Test One Function and Rule at a Time

Test the smallest function before depending on it in a larger one. Then test the larger functions separately:

assert count_value([2, 5, 2, 1, 2], 2) == 3
assert count_value([2, 5, 2, 1, 2], 6) == 0
assert largest_group_size([1, 2, 3, 4, 5]) == 1
assert largest_group_size([6, 6, 2, 6, 3]) == 3
assert largest_group_size([4, 4, 4, 4, 4]) == 5
assert score_roll([1, 2, 3, 4, 5]) == 0
assert score_roll([6, 6, 2, 2, 3]) == 5
assert score_roll([3, 3, 3, 5, 5]) == 15
assert score_roll([4, 4, 4, 4, 2]) == 25
assert score_roll([5, 5, 5, 5, 5]) == 50

These cases cover an absent target, all-distinct dice, and every scoring group. If score_roll fails, check largest_group_size, then individual counts. Testing each function and rule helps locate where the first wrong result appears.

Use the ordinary Python editor below to run these checks without opening the game. Copy only the three scoring functions from your exercise solution into it, then add assertions below the definitions. Leave the game framework in the exercise. When the checks pass, copy your finished functions back into the game starter.

No files opened Select a file to edit

How the Rules Connect to the Framework

The complete framework is in the exercise. These excerpts only show how the scoring functions connect to the game:

# Inside largest_group_size:
count = count_value(dice, target)
# Inside score_roll:
group_size = largest_group_size(dice)
# Inside the provided framework after a new roll:
self.roll_score = score_roll(self.dice)

The exercise framework also ends with this guarded launch:

if __name__ == "__main__":
import random
import pyxel
DiceCombosGame()

Running the file executes this block and opens the game. Importing it for tests skips the block, so the ordinary functions can be tested without starting Pyxel.

The framework and its launch code are provided. Write the three marked functions; later parts of the course will explain the remaining code.

Suggested Workflow

Work from the smallest responsibility outward:

  1. implement and test count_value;
  2. implement and test largest_group_size using the first function;
  3. implement and test score_roll using the second function;
  4. run all of the function tests together;
  5. start the game and check several displayed rolls against the scoring table.

The function tests use rolls you choose, so you can repeat the same checks after each change.

The exercise provides the Pyxel starter and marks the three functions to complete. Follow the workflow above, then try the game.

Dice combos

0 / 35 points

Complete the scoring rules for Dice Combos. The provided Pyxel framework rolls five dice when the player presses Space, displays the current roll and score, and starts a new game when the player presses R.

Work in the three functions between YOUR CODE STARTS HERE and YOUR CODE ENDS HERE. The framework, random-number code, class, drawing code, and import guard are provided; you do not need to modify them. Keep the framework class and import guard in place so the game can run.

The framework always gives the functions a list of exactly five integers from 1 through 6. The functions do not need to validate that promise. None of the three functions should print anything or change the list it receives.

Implement these functions:

  1. count_value(dice, target) returns how many items in dice equal target. The selected-count pattern from Part 2 is useful here.

  2. largest_group_size(dice) returns the size of the largest group of equal values. Call count_value for each possible face and remember the largest count. You can loop through [1, 2, 3, 4, 5, 6].

  3. score_roll(dice) calls largest_group_size and returns the score from this table:

    Largest groupPoints
    550
    425
    315
    25
    10

A roll with two pairs still has a largest group of 2 and scores 5. A roll such as [3, 3, 3, 5, 5] has a largest group of 3 and scores 15; this game has no separate full-house rule.

Build the larger rules from the smaller functions. The loops, conditions, parameters, and return values taught so far are enough for all three functions.

Test the functions with the ordinary Python editor on this page before trying the game. Copy only your three functions into that editor and add assertions below them. Once the checks pass, copy the functions back into the game starter and run it.

Explain the Separation

After the game exercise, you should be able to explain this boundary:

  • the three functions you wrote decide what a roll means and how many points it earns;
  • the framework decides when a roll happens, which random values appear, how the dice are drawn, and how the running total is displayed.

The list holds the five die values that the scoring functions need.

Part 6 will return to framework code like this and explain classes and object state more formally.

Check Your Understanding

  1. Why is a list a natural representation for the five dice?
  2. What information enters and leaves count_value?
  3. Why should largest_group_size call count_value instead of copying its counting loop?
  4. Why can the three scoring functions be tested without starting Pyxel?
  5. What does the import guard prevent when a test imports the file?

AI Study Aid

Create a chapter diagram

We're looking into to what extent AI-generated diagrams could help with studying.

Use this study aid to generate an AI-generated visual summary of the material you just studied. Each diagram style emphasizes the content in a different way, so you can choose the focus that feels most useful.

Using the diagram generator is voluntary and does not affect your course progress. We encourage you to try it out and see if it helps, but it's totally up to you! Your answers help us understand how to make better study aids in the future.

Diagrams tried: 0