Back

Activity 3.3 Designing with Functions: Stepwise Refinement

divider

Introduction

Designing with Functions

Breaking Down Problems Step by Step

What Is Stepwise Refinement?

Stepwise refinement is the process of starting with a broad goal and gradually dividing it into smaller, more manageable tasks.

Each step adds more detail until your program is complete and ready to run.

Why Use Stepwise Refinement?

  • Organization: Plan before coding. Avoid writing everything in one big block of code.
  • Focus: Build and test one small part at a time.
  • Reusability: Each function can be reused or replaced later.
  • Debugging: When something breaks, you know exactly where to look.

Example of Refinement

Suppose we want to build a simple dice game. Instead of writing the whole game at once, we can break it down:

  1. Roll a single die
  2. Roll two dice and calculate the total
  3. Apply the game rules for a round
  4. Repeat rounds until the game ends

Each step becomes a function. Together, they form a complete game.

Our Goal

We'll design a simplified version of the dice game Craps using stepwise refinement.

Each function will handle one small, focused task to make the code easier to test and read.

'F' → Fullscreen

Objectives

  • icon Apply stepwise refinement to design a small game.
  • icon Use parameters and return values to control program flow.
  • icon Refactor working code into clean, reusable functions.
divider

Activity Tasks

  • icon Create a new JavaScript file named 3-3-CrapsGame.
  • icon Build and run after each step. Do not skip ahead.
divider

Task 1: Roll a Single Die (print only)

  • icon Implement rollDie() that prints the die result.
  • icon Call it several times from the bottom of your file to verify randomness.
craps.js
function rollDie() {
let die = Math.floor(Math.random() * 6) + 1;
console.log("Rolled: " + die);
}
console.log("--- CRAPS ---");
// Call several times to verify randomness
rollDie();
rollDie();
rollDie();
divider

Task 2: Return Instead of Print

  • icon Modify rollDie() so it returns the value instead of printing it.
  • icon In your top-level code, call rollDie() twice and print both results.
craps.js
function rollDie() {
let die = Math.floor(Math.random() * 6) + 1;
console.log("Rolled: " + die);
return Math.floor(Math.random() * 6) + 1;
}
console.log("--- CRAPS ---");
rollDie();
rollDie();
rollDie();
let d1 = rollDie();
let d2 = rollDie();
console.log("You rolled: " + d1 + " and " + d2);
divider

Task 3: Roll Two Dice

  • icon Create rollDice() that calls rollDie() twice, prints the breakdown, and returns the total.
  • icon Test rollDice() by calling it and printing the total.
craps.js
function rollDie() {
return Math.floor(Math.random() * 6) + 1;
}
function rollDice() {
let die1 = rollDie();
let die2 = rollDie();
let total = die1 + die2;
console.log("You rolled " + die1 + " + " + die2 + " = " + total);
return total;
}
console.log("--- CRAPS ---");
let total = rollDice();
console.log("Total: " + total);
divider

Task 4: Come-Out Roll Rules

  • icon Write evaluateComeOutRoll(roll) to print the outcome and return whether the round is over (true or false).
  • icon If the round continues, store the roll in a point variable.
craps.js
function rollDie() {
return Math.floor(Math.random() * 6) + 1;
}
function rollDice() {
6 collapsed lines
let d1 = rollDie();
let d2 = rollDie();
let total = d1 + d2;
console.log("You rolled " + d1 + " + " + d2 + " = " + total);
return total;
}
function evaluateComeOutRoll(roll) {
if (roll == 7 || roll == 11) {
console.log("Rolled a " + roll + "! You win!");
return true;
}
else if (roll == 2 || roll == 3 || roll == 12) {
console.log("Rolled a " + roll + ". You lose.");
return true;
}
else {
console.log("Rolled a " + roll + ".");
return false;
}
}
console.log("--- CRAPS ---");
let point = 0;
let roll = rollDice();
let roundOver = evaluateComeOutRoll(roll);
if (!roundOver) {
point = roll;
console.log("The point is now " + point + ".");
// Next step: loop while not over and resolve the point.
}
divider

Task 5: Resolve the Point

  • icon Add evaluatePointRoll(roll, point) and use a while loop to keep rolling until the player wins or loses.
  • icon Print a final message when the game is over.
craps.js
function rollDie() {
return Math.floor(Math.random() * 6) + 1;
}
function rollDice() {
6 collapsed lines
let d1 = rollDie();
let d2 = rollDie();
let total = d1 + d2;
console.log("You rolled " + d1 + " + " + d2 + " = " + total);
return total;
}
function evaluateComeOutRoll(roll) {
12 collapsed lines
if (roll == 7 || roll == 11) {
console.log("Rolled a " + roll + "! You win!");
return true;
}
else if (roll == 2 || roll == 3 || roll == 12) {
console.log("Rolled a " + roll + ". You lose.");
return true;
}
else {
console.log("Rolled a " + roll + ".");
return false;
}
}
// Step 5: resolve after point is set
function evaluatePointRoll(roll, point) {
if (roll == point) {
console.log("Rolled a " + roll + "! You win!");
return true;
}
else if (roll == 7) {
console.log("Rolled a 7. You lose.");
return true;
}
else {
console.log("Rolled a " + roll + ". Still shooting for " + point + ".");
return false;
}
}
console.log("--- CRAPS ---");
let point = 0;
let gameOver = false;
// Come-out roll
let roll = rollDice();
gameOver = evaluateComeOutRoll(roll);
if (!gameOver) {
point = roll;
console.log("The point is now " + point + ".");
// Keep rolling until win (hit point) or lose (7)
while (!gameOver) {
roll = rollDice();
gameOver = evaluatePointRoll(roll, point);
}
}
console.log("Thanks for playing!");
divider

Sample Output

Sample Output
--- CRAPS ---
You rolled 4 + 3 = 7
Rolled a 7! You win!
Thanks for playing!
divider

Reflection Questions

  1. Which function did you write first, and why was that a good starting point?
  2. How did return values help you control the game flow?
  3. After refactoring, what does your top-level code focus on now?
divider

Submission

Submit your completed .js file and reflection answers to the appropriate dropbox.

Activity Complete