curriculum/challenges/english/blocks/review-algorithmic-thinking-by-building-a-dice-game/657e230500602983e01fff6e.md
For the last portion of the game, you will need to create an algorithm that checks for the presence of a straight. A small straight is when four of the dice have consecutive values in any order (Ex. in a roll of 41423, we have 1234) resulting in a score of 30 points. A large straight is when all five dice have consecutive values in any order (Ex. in a roll of 35124, we have 12345) resulting in a score of 40 points.
Declare a checkForStraights function which accepts an array of numbers. If the user gets a large straight, update the fifth radio button with a score of 40. If the user gets a small straight, update the fourth radio button with a score of 30. Regardless, it should always update the last radio button to display a score of 0, with the correct attributes.
Call your checkForStraights function when the rollDiceBtn is clicked to complete your dice game!
Your checkForStraights variable should be a function.
assert.isFunction(checkForStraights);
If a small straight is rolled, your checkForStraights function should enable the fourth radio button, set the value to 30, update the displayed text to , score = 30 and leave the fifth radio button disabled.
const assertSmallStraight = (_diceValuesArr) => {
resetRadioOptions();
checkForStraights(_diceValuesArr);
assert.isTrue(scoreInputs[4].disabled);
assert.isFalse(scoreInputs[3].disabled);
assert.strictEqual(scoreInputs[3].value, "30");
assert.strictEqual(scoreSpans[3].innerText, ", score = 30");
}
// Basic straights
assertSmallStraight([1,1,2,3,4])
assertSmallStraight([2,3,4,5,5])
assertSmallStraight([3,4,5,6,6])
// 5 unique numbers, but only small straight
assertSmallStraight([1,2,3,4,6])
assertSmallStraight([1,3,4,5,6])
// Straights with duplicates in middle
assertSmallStraight([1,2,2,3,4])
assertSmallStraight([2,3,3,4,5])
assertSmallStraight([3,4,5,5,6])
// Out of order straights
assertSmallStraight([1,3,2,1,4])
assertSmallStraight([5,4,3,3,2])
assertSmallStraight([3,4,5,6,1])
If a large straight is rolled, your checkForStraights function should enable the fourth button, set the value to 30, update the displayed text to , score = 30. Additionally, the function should enable the fifth radio button, set the value to 40, and update the displayed text to , score = 40.
const assertLargeStraight = (_diceValuesArr) => {
resetRadioOptions();
checkForStraights(_diceValuesArr);
assert.isFalse(scoreInputs[3].disabled);
assert.strictEqual(scoreInputs[3].value, "30");
assert.strictEqual(scoreSpans[3].innerText, ", score = 30");
assert.isFalse(scoreInputs[4].disabled);
assert.strictEqual(scoreInputs[4].value, "40");
assert.strictEqual(scoreSpans[4].innerText, ", score = 40");
}
// Basic straights
assertLargeStraight([1,2,3,4,5])
assertLargeStraight([2,3,4,5,6])
// Backward straights
assertLargeStraight([5,4,3,2,1])
assertLargeStraight([6,5,4,3,2])
// Out of order straights
assertLargeStraight([1,5,3,4,2])
assertLargeStraight([2,4,6,5,3])
If no straight is rolled, your checkForStraights function should not enable the fourth or fifth radio button.
const assertNoStraight = (_diceValuesArr) => {
resetRadioOptions();
checkForStraights(_diceValuesArr);
assert.isTrue(scoreInputs[3].disabled);
assert.isTrue(scoreInputs[4].disabled);
}
// Simple cases
assertNoStraight([1,4,4,4,4]);
assertNoStraight([2,2,3,3,3]);
assertNoStraight([5,5,5,5,5]);
assertNoStraight([6,5,1,5,6]);
// Almost a straight, but broken in middle
assertNoStraight([1,2,3,5,6]);
assertNoStraight([1,2,4,5,6]);
// Almost a straight with duplicates in middle
assertNoStraight([1,2,2,3,5]);
assertNoStraight([2,4,4,5,6]);
// Repeat of last 4 cases, but not in order
assertNoStraight([1,5,6,2,3]);
assertNoStraight([5,2,4,1,6]);
assertNoStraight([2,1,5,3,2]);
assertNoStraight([2,4,5,4,6]);
If no straight is rolled, your checkForStraights function should enable the final radio button set the value to 0, and update the displayed text to , score = 0.
resetRadioOptions();
checkForStraights([1,1,1,1,1]);
assert.isFalse(scoreInputs[5].disabled);
assert.strictEqual(scoreInputs[5].value, "0");
assert.strictEqual(scoreSpans[5].innerText, ", score = 0");
You should call the checkForStraights function when the rollDiceBtn is clicked.
resetRadioOptions();
const origMathRandom = Math.random;
// Temporarily modifies Math.random to guarantee a straight.
const myMathRandom = (() => {
let i = 0;
const values = [4/6,2/6,5/6,3/6,1/6];
return () => values[i++ % 5];
})();
Math.random = myMathRandom;
try {
rollDiceBtn.click();
} finally {
Math.random = origMathRandom;
}
assert.isFalse(scoreInputs[4].disabled);
assert.strictEqual(scoreInputs[4].value, "40");
assert.strictEqual(scoreSpans[4].innerText, ", score = 40");
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Advanced Dice Game</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header>
<h1>Advanced Dice Game</h1>
<button class="btn" id="rules-btn" type="button">Show rules</button>
<div class="rules-container">
<h2>Rules</h2>
<ul>
<li>There are total of six rounds</li>
<li>You can only roll the dice three times per round</li>
<li>To start the game, roll the dice</li>
<li>
Then, choose from one of the selected scores or roll the dice again
</li>
<li>
If you choose a selected score, then you will move to the next round
</li>
<li>
If you decline to choose a selected score, then you can roll the
dice again two more times
</li>
</ul>
<h2 class="points">Points</h2>
<ul>
<li>Three of a kind: Sum of all five dice</li>
<li>Four of a kind: Sum of all five dice</li>
<li>Full house: Three of a kind and a pair - 25 points</li>
<li>
Small straight: Four of the dice have consecutive values - 30 points
</li>
<li>
Large straight: All five dice have consecutive values - 40 points
</li>
</ul>
</div>
</header>
<main>
<form id="game">
<div id="dice">
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
<div class="die"></div>
</div>
<p class="rounds-text">
<strong>Rolls:</strong> <span id="current-round-rolls">0</span> |
<strong>Round:</strong> <span id="current-round">1</span>
</p>
<div id="score-options">
<div>
<input type="radio" name="score-options" id="three-of-a-kind" value="three-of-a-kind" disabled />
<label for="three-of-a-kind">Three of a kind<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="four-of-a-kind" value="four-of-a-kind" disabled />
<label for="four-of-a-kind">Four of a kind<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="full-house" value="full-house" disabled />
<label for="full-house">Full house<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="small-straight" value="small-straight" disabled />
<label for="small-straight">Small straight<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="large-straight" value="large-straight" disabled />
<label for="large-straight">Large straight<span></span></label>
</div>
<div>
<input type="radio" name="score-options" id="none" value="none" disabled />
<label for="none">None of the above<span></span></label>
</div>
</div>
<button class="btn" id="keep-score-btn" type="button">
Keep the above selected score
</button>
<button class="btn" id="roll-dice-btn" type="button">
Roll the dice
</button>
</form>
<div id="scores">
<h3>Score history (Total score: <span id="total-score">0</span>)</h3>
<ol id="score-history"></ol>
</div>
</main>
<script src="./script.js"></script>
</body>
</html>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--dark-grey: #1b1b32;
--light-grey: #f5f6f7;
--black: #000;
--white: #fff;
--grey: #3b3b4f;
--golden-yellow: #fecc4c;
--yellow: #ffcc4c;
--gold: #feac32;
--orange: #ffac33;
--dark-orange: #f89808;
}
body {
background-color: var(--dark-grey);
}
header {
color: var(--light-grey);
text-align: center;
}
h1 {
font-size: 2.5rem;
margin: 25px 0;
}
.rules-container {
display: none;
background-color: var(--light-grey);
color: var(--black);
width: 50%;
margin: 20px auto;
height: 300px;
border-radius: 10px;
overflow-y: scroll;
}
.rules-container ul {
list-style-type: none;
}
.points {
margin-top: 15px;
}
main {
background-color: var(--light-grey);
padding: 20px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 1rem;
margin: auto;
justify-items: center;
width: 50%;
border-radius: 10px;
}
#dice {
display: flex;
justify-content: space-around;
margin-bottom: 15px;
}
.die {
width: 40px;
height: 40px;
text-align: center;
margin-right: 15px;
border: 4px solid var(--black);
padding: 10px;
}
.rounds-text {
text-align: center;
}
input[type="radio"]:disabled + label {
color: var(--grey);
}
#score-history {
margin-top: 15px;
text-align: center;
list-style-position: inside;
}
.btn {
cursor: pointer;
width: 200px;
margin: 10px 0 10px 0.5rem;
color: var(--black);
background-color: var(--gold);
background-image: linear-gradient(var(--golden-yellow), var(--orange));
border-color: var(--gold);
border-width: 3px;
}
.btn:hover:enabled {
background-image: linear-gradient(var(--yellow), var(--dark-orange));
}
@media (max-width: 992px) {
main {
width: 100%;
}
}
@media (max-width: 500px) {
.btn {
width: 120px;
}
}
const listOfAllDice = document.querySelectorAll(".die");
const scoreInputs = document.querySelectorAll("#score-options input");
const scoreSpans = document.querySelectorAll("#score-options span");
const roundElement = document.getElementById("current-round");
const rollsElement = document.getElementById("current-round-rolls");
const totalScoreElement = document.getElementById("total-score");
const scoreHistory = document.getElementById("score-history");
const rollDiceBtn = document.getElementById("roll-dice-btn");
const keepScoreBtn = document.getElementById("keep-score-btn");
const rulesContainer = document.querySelector(".rules-container");
const rulesBtn = document.getElementById("rules-btn");
let diceValuesArr = [];
let isModalShowing = false;
let score = 0;
let round = 1;
let rolls = 0;
const rollDice = () => {
diceValuesArr = [];
for (let i = 0; i < 5; i++) {
const randomDice = Math.floor(Math.random() * 6) + 1;
diceValuesArr.push(randomDice);
};
listOfAllDice.forEach((dice, index) => {
dice.textContent = diceValuesArr[index];
});
};
const updateStats = () => {
rollsElement.textContent = rolls;
roundElement.textContent = round;
};
const updateRadioOption = (index, score) => {
scoreInputs[index].disabled = false;
scoreInputs[index].value = score;
scoreSpans[index].textContent = `, score = ${score}`;
};
const updateScore = (selectedValue, achieved) => {
score += parseInt(selectedValue);
totalScoreElement.textContent = score;
scoreHistory.innerHTML += `<li>${achieved} : ${selectedValue}</li>`;
};
const getHighestDuplicates = (arr) => {
const counts = {};
for (const num of arr) {
if (counts[num]) {
counts[num]++;
} else {
counts[num] = 1;
}
}
let highestCount = 0;
for (const num of arr) {
const count = counts[num];
if (count >= 3 && count > highestCount) {
highestCount = count;
}
if (count >= 4 && count > highestCount) {
highestCount = count;
}
}
const sumOfAllDice = arr.reduce((a, b) => a + b, 0);
if (highestCount >= 4) {
updateRadioOption(1, sumOfAllDice);
}
if (highestCount >= 3) {
updateRadioOption(0, sumOfAllDice);
}
updateRadioOption(5, 0);
};
const detectFullHouse = (arr) => {
const counts = {};
for (const num of arr) {
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
const hasThreeOfAKind = Object.values(counts).includes(3);
const hasPair = Object.values(counts).includes(2);
if (hasThreeOfAKind && hasPair) {
updateRadioOption(2, 25);
}
updateRadioOption(5, 0);
};
const resetRadioOptions = () => {
scoreInputs.forEach((input) => {
input.disabled = true;
input.checked = false;
});
scoreSpans.forEach((span) => {
span.textContent = "";
});
};
const resetGame = () => {
diceValuesArr = [0, 0, 0, 0, 0];
score = 0;
round = 1;
rolls = 0;
listOfAllDice.forEach((dice, index) => {
dice.textContent = diceValuesArr[index];
});
totalScoreElement.textContent = score;
scoreHistory.innerHTML = "";
rollsElement.textContent = rolls;
roundElement.textContent = round;
resetRadioOptions();
};
--fcc-editable-region--
rollDiceBtn.addEventListener("click", () => {
if (rolls === 3) {
alert("You have made three rolls this round. Please select a score.");
} else {
rolls++;
resetRadioOptions();
rollDice();
updateStats();
getHighestDuplicates(diceValuesArr);
detectFullHouse(diceValuesArr);
}
});
--fcc-editable-region--
rulesBtn.addEventListener("click", () => {
isModalShowing = !isModalShowing;
if (isModalShowing) {
rulesBtn.textContent = "Hide rules";
rulesContainer.style.display = "block";
} else {
rulesBtn.textContent = "Show rules";
rulesContainer.style.display = "none";
}
});
keepScoreBtn.addEventListener("click", () => {
let selectedValue;
let achieved;
for (const radioButton of scoreInputs) {
if (radioButton.checked) {
selectedValue = radioButton.value;
achieved = radioButton.id;
break;
}
}
if (selectedValue) {
rolls = 0;
round++;
updateStats();
resetRadioOptions();
updateScore(selectedValue, achieved);
if (round > 6) {
setTimeout(() => {
alert(`Game Over! Your total score is ${score}`);
resetGame();
}, 500);
}
} else {
alert("Please select an option or roll the dice");
}
});