Higher-order functions to
Order higher rolls. — CS7 Haiku
Introduction
In this project, you will develop a simulator and multiple strategies for the dice game Hog. You will need to use control statements and higher-order functions together, drawing on Sections 1.1 through 1.6 of Composing Programs.
In Hog, two players alternate turns trying to reach 100 points first. On each turn, the current player chooses how many dice to roll (up to 10). The turn score is the sum of the dice — unless any die shows a 1, in which case the score for that turn is only 1 point (the Pig Out rule).
Special Rules
Three special rules spice up the game. Try the interactive demos below to understand each one before you start coding.
Free Bacon
Roll zero dice to score 1 more than the largest digit in your opponent's total score.
Hog Wild
If the sum of both players' scores is a multiple of 7, the current player rolls four-sided dice instead of six-sided.
Swine Swap
If one player's score is exactly double the other's at the end of a turn, the players swap scores.
Project Files
Download all project code as a zip archive. You will only
need to edit hog.py.
hog.py |
A starter implementation of Hog. This is the only file you submit. |
dice.py |
Functions for rolling dice. |
ucb.py |
Utility functions for CS7. |
hog_gui.py |
A graphical user interface for Hog. |
hog_grader.py |
Tests to check the correctness of your implementation. |
autograder.py |
Utility functions for grading. |
Logistics
This is a 10-day project. You are strongly encouraged to work with a partner, although you may complete it alone.
Start early. The amount of time it takes to complete a project is unpredictable. Ask for help early and often — the TAs are here to help. Post on Piazza and come to office hours.
You and your partner submit one project. It is worth 40 points in total, split equally between two components:
| Component | Points | How |
|---|---|---|
| Code (Poseidon autograder) | 20 pts | 17 for correctness · 3 for program composition |
| Oral Evaluation | 20 pts | Conducted by a TA after submission — see below |
Submit only hog.py to Poseidon. Before you submit, read the
Composition guide — it explains what the 3 composition
points are looking for.
For functions we ask you to complete, some initial skeleton code is provided. You may delete it and start from scratch, but we advise keeping the skeleton. Do not modify any other functions or change any function signatures (names, argument order, or number of arguments).
Oral Evaluation
After the submission deadline, each pair will be assigned a TA who will conduct a short oral evaluation — a conversation to assess how well both partners understand the code they submitted. This is worth 20 points (half your total project score).
Submitting working code is not enough — you must be able to explain it. Oral evaluations ensure both partners genuinely understand their implementation and help us maintain academic integrity across the class.
Format. Your assigned TA will schedule a short session (roughly 10–15 minutes per pair) after the deadline. Both partners must attend. The TA will ask each person questions about specific parts of the code — your design choices, how a function works, what would happen if an input changed, and so on. You will not write any new code during the session.
Scoring. Each partner is assessed individually. Points are awarded based on depth of understanding:
| Score | Description |
|---|---|
| 18 – 20 | Deep understanding — can explain every function clearly, justify design decisions, and reason about edge cases without hesitation. |
| 14 – 17 | Good understanding — explains most functions correctly with minor gaps or prompting needed. |
| 10 – 13 | Partial understanding — understands the overall structure but struggles to explain specific implementation details. |
| 5 – 9 | Limited understanding — can describe what the code does at a surface level but cannot explain how or why. |
| 0 – 4 | Little to no understanding — unable to explain the submitted code. This may trigger an academic integrity review. |
What to expect. Your TA may ask questions like:
- Walk me through what happens when
roll_diceis called withnum_rolls=5. - What does
make_averagedreturn — a value or a function? How does it work? - If both scores were 0, what dice would
select_dicechoose and why? - Explain your
final_strategy— what cases does it handle and in what order?
The best preparation is simply to write the code yourself and understand every line. If you worked with a partner, make sure you can each explain the full project independently.
Graphical User Interface
A graphical user interface (GUI) is provided. It won't work until you implement the game logic, but once you finish Problem 4 you can play a fully interactive version of Hog.
pygame.
Install it once before running the GUI:
pip install pygame
If pip gives a permissions error, try pip3 install pygame or add the --user flag.
python3 hog_gui.py
After completing Problem 9, you can play against your own final strategy:
python3 hog_gui.py -f
Testing
Test your code frequently — it makes bugs easier to isolate.
Most tests live in the docstrings of hog.py. Additional tests are in
hog_grader.py. Run all tests until a failure is found:
python3 hog_grader.py
Run tests for a specific problem with -q:
python3 hog_grader.py -q 1
Call certain functions interactively from the terminal:
python3 hog.py -i roll_dice
roll_dice 2 pt
Implement roll_dice in hog.py, which returns the number of
points scored by rolling a fixed positive number of dice: either the sum of the dice or
1 (Pig Out). Call dice() exactly num_rolls times. The only rule
to enforce here is Pig Out.
As you work, add print statements to inspect your program. Remove them
when you're done.
python3 hog_grader.py -q 1
python3 hog.py -i roll_dice
take_turn 1 pt
Implement take_turn, which returns the number of points scored for the
turn. Enforce the Free Bacon rule here. You may assume
opponent_score is less than 100. Your implementation should call
roll_dice.
python3 hog_grader.py -q 2
select_dice 1 pt
Implement select_dice, a helper that enforces the Hog Wild
rule. The function takes the scores of both players and returns the correct type of dice
(four-sided or six-sided) for the current turn.
python3 hog_grader.py -q 3
play 3 pt
Implement play, which simulates a full game of Hog. Players alternate
turns using the strategies originally supplied, until one reaches the
goal score. Return the final scores of both players, Player 0's score
first.
- Enforce all special rules: use
select_dicefor Hog Wild and enforce Swine Swap at the end of each turn. - Use your
take_turnfunction. - Use the provided
otherfunction to get the opposing player number. - A strategy is a function that takes both scores and returns the number of dice to roll. Don't worry about strategies yet — they come in Phase 2.
python3 hog_grader.py -q 4
python3 hog.py -i play
Once finished, you can play the graphical version:
python3 hog_gui.py
Congratulations — you have finished Phase 1! 🎉
make_averaged 2 pt
Implement make_averaged. This higher-order function takes a function
fn and returns another function that returns the average value of
repeatedly calling fn on the same arguments. It should call fn
exactly num_samples times.
Note: if fn is non-pure (e.g. uses random),
so is the result.
You will need *args syntax to accept an arbitrary
number of arguments and forward them:
>>> def printed(fn):
... def print_and_return(*args):
... result = fn(*args)
... print('Result:', result)
... return result
... return print_and_return
>>> printed_pow = printed(pow)
>>> printed_pow(2, 8)
Result: 256
256
python3 hog_grader.py -q 5
max_scoring_num_rolls 2 pt
Implement max_scoring_num_rolls, which runs an experiment to determine
the number of rolls (1–10) that gives the highest average score per turn. Use
make_averaged and roll_dice. Print the average for each
number of rolls as shown in the doctest.
python3 hog_grader.py -q 6
Run the experiment on randomized dice:
python3 hog.py -r
For the rest of the project, feel free to modify run_experiments as
you wish. Calling average_win_rate lets you evaluate any strategy.
Some experiments may take up to a minute; reduce num_samples in
make_averaged to speed them up.
bacon_strategy 1 pt
Implement bacon_strategy. It should return 0 (roll zero
dice, take Free Bacon) whenever doing so would score at least
BACON_MARGIN points. Otherwise return BASELINE_NUM_ROLLS.
Both constants are defined just above always_roll.
python3 hog_grader.py -q 7
Update run_experiments to evaluate this strategy — it should win more
than half the time.
swap_strategy 2 pt
Implement swap_strategy to also exploit the Swine Swap rule:
- Roll 0 if it would trigger a beneficial swap (gaining points).
- Roll
BASELINE_NUM_ROLLSif it would trigger a harmful swap (losing points). - If no swap would occur, use the Free Bacon threshold logic from
bacon_strategy.
python3 hog_grader.py -q 8
Update run_experiments — this strategy should perform even better than
bacon_strategy on average.
Run the complete autograder to catch any remaining failures:
python3 hog_grader.py
final_strategy 3 pt
Implement final_strategy, combining all ideas (and any new ones) to
achieve a win rate of at least 0.59 against
always_roll(5). Some ideas to consider:
- Force your opponent into Hog Wild (four-sided dice) more often.
- Take fewer risks when ahead; take bigger risks when behind.
- Vary your roll count based on whether you'll roll four-sided or six-sided dice.
Note: you may want to increase num_samples for a better win-rate
estimate. Poseidon will compute your exact average win rate when you submit.
python3 hog_gui.py -f
The GUI will alternate which player is controlled by you so you can test your strategy head-to-head.
Congratulations — you have reached the end of your first CS7 project! 🏆