feat(curriculum): daily challenges 171-179 (#65495)

This commit is contained in:
Tom
2026-01-26 21:19:14 -06:00
committed by GitHub
parent e84c07be1c
commit c8b21dfc4a
21 changed files with 1625 additions and 1 deletions
@@ -0,0 +1,71 @@
---
id: 69738771fb5a7b8b24cca29d
title: "Challenge 171: Flatten the Array"
challengeType: 28
dashedName: challenge-171
---
# --description--
Given an array that contains nested arrays, return a new array with all values flattened into a single, one-dimensional array. Retain the original order of the items in the arrays.
# --hints--
`flatten([1, [2, 3], 4])` should return `[1, 2, 3, 4]`.
```js
assert.deepEqual(flatten([1, [2, 3], 4]), [1, 2, 3, 4]);
```
`flatten([5, [4, [3, 2]], 1])` should return `[5, 4, 3, 2, 1]`.
```js
assert.deepEqual(flatten([5, [4, [3, 2]], 1]), [5, 4, 3, 2, 1]);
```
`flatten(["A", [[[["B"]]]], "C"])` should return `["A", "B", "C"]`.
```js
assert.deepEqual(flatten(["A", [[[["B"]]]], "C"]), ["A", "B", "C"]);
```
`flatten([["L", "M", "N"], ["O", ["P", "Q", ["R", ["S", ["T", "U"]]]]], "V", ["W", ["X", ["Y", ["Z"]]]]])` should return `["L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]`.
```js
assert.deepEqual(flatten([["L", "M", "N"], ["O", ["P", "Q", ["R", ["S", ["T", "U"]]]]], "V", ["W", ["X", ["Y", ["Z"]]]]]), ["L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]);
```
`flatten([["red", ["blue", ["green", ["yellow", ["purple"]]]]], "orange", ["pink", ["brown"]]])` should return `["red","blue","green","yellow","purple","orange","pink","brown"]`.
```js
assert.deepEqual(flatten([["red", ["blue", ["green", ["yellow", ["purple"]]]]], "orange", ["pink", ["brown"]]]), ["red","blue","green","yellow","purple","orange","pink","brown"]);
```
# --seed--
## --seed-contents--
```js
function flatten(arr) {
return arr;
}
```
# --solutions--
```js
function flatten(arr) {
let result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result = result.concat(flatten(item));
} else {
result.push(item);
}
}
return result;
}
```
@@ -0,0 +1,73 @@
---
id: 69738771fb5a7b8b24cca29e
title: "Challenge 172: Letters-Numbers"
challengeType: 28
dashedName: challenge-172
---
# --description--
Given a string containing only letters and numbers, return a new string where a hyphen (`-`) is inserted every time the string switches from a letter to a number, or a number to a letter.
# --hints--
`separateLettersAndNumbers("ABC123")` should return `"ABC-123"`.
```js
assert.equal(separateLettersAndNumbers("ABC123"), "ABC-123");
```
`separateLettersAndNumbers("Route66")` should return `"Route-66`.
```js
assert.equal(separateLettersAndNumbers("Route66"), "Route-66");
```
`separateLettersAndNumbers("H3LL0W0RLD")` should return `"H-3-LL-0-W-0-RLD"`.
```js
assert.equal(separateLettersAndNumbers("H3LL0W0RLD"), "H-3-LL-0-W-0-RLD");
```
`separateLettersAndNumbers("a1b2c3d4")` should return `"a-1-b-2-c-3-d-4"`.
```js
assert.equal(separateLettersAndNumbers("a1b2c3d4"), "a-1-b-2-c-3-d-4");
```
# --seed--
## --seed-contents--
```js
function separateLettersAndNumbers(str) {
return str;
}
```
# --solutions--
```js
function separateLettersAndNumbers(str) {
if (str.length === 0) return str;
let result = str[0];
for (let i = 1; i < str.length; i++) {
const prev = str[i - 1];
const curr = str[i];
const prevIsLetter = /[a-zA-Z]/.test(prev);
const currIsLetter = /[a-zA-Z]/.test(curr);
if (prevIsLetter !== currIsLetter) {
result += "-";
}
result += curr;
}
return result;
}
```
@@ -0,0 +1,93 @@
---
id: 69738771fb5a7b8b24cca29f
title: "Challenge 173: Valid Pawn Moves"
challengeType: 28
dashedName: challenge-173
---
# --description--
Given the position of one of your pawns on a chessboard, return an array of all the valid squares it can move to in ascending order.
A standard chessboard is 8x8, with columns labeled `A` through `H` (left to right) and rows labeled `1` through `8` (bottom to top). It looks like this:
|**A8**|**B8**|**C8**|**D8**|**E8**|**F8**|**G8**|**H8**|
|-|-|-|-|-|-|-|-|
|**A7**|**B7**|**C7**|**D7**|**E7**|**F7**|**G7**|**H7**|
|**A6**|**B6**|**C6**|**D6**|**E6**|**F6**|**G6**|**H6**|
|**A5**|**B5**|**C5**|**D5**|**E5**|**F5**|**G5**|**H5**|
|**A4**|**B4**|**C4**|**D4**|**E4**|**F4**|**G4**|**H4**|
|**A3**|**B3**|**C3**|**D3**|**E3**|**F3**|**G3**|**H3**|
|**A2**|**B2**|**C2**|**D2**|**E2**|**F2**|**G2**|**H2**|
|**A1**|**B1**|**C1**|**D1**|**E1**|**F1**|**G1**|**H1**|
For this challenge:
- You are the player on the bottom of the board.
- Pawns can only move one square "up".
- Unless the pawn is in the starting row (row 2), then it can move one or two squares up.
For example, given `"D4"`, return `["D5"]`, the only square your pawn can move to.
Given `"B2"`, return `["B3", "B4"]`, because it's on the starting row and needs to be in ascending order.
# --hints--
`findPawnMoves("D4")` should return `["D5"]`.
```js
assert.deepEqual(findPawnMoves("D4"), ["D5"]);
```
`findPawnMoves("B2")` should return `["B3", "B4"]`.
```js
assert.deepEqual(findPawnMoves("B2"), ["B3", "B4"]);
```
`findPawnMoves("A7")` should return `["A8"]`.
```js
assert.deepEqual(findPawnMoves("A7"), ["A8"]);
```
`findPawnMoves("G2")` should return `["G3", "G4"]`.
```js
assert.deepEqual(findPawnMoves("G2"), ["G3", "G4"]);
```
`findPawnMoves("E3")` should return `["E4"]`.
```js
assert.deepEqual(findPawnMoves("E3"), ["E4"]);
```
# --seed--
## --seed-contents--
```js
function findPawnMoves(position) {
return position;
}
```
# --solutions--
```js
function findPawnMoves(position) {
const column = position[0].toUpperCase();
const row = parseInt(position[1]);
const moves = [];
if (row >= 8) return moves;
moves.push(`${column}${row + 1}`);
if (row === 2) moves.push(`${column}${row + 2}`);
return moves;
}
```
@@ -0,0 +1,117 @@
---
id: 69738771fb5a7b8b24cca2a0
title: "Challenge 174: Zodiac Finder"
challengeType: 28
dashedName: challenge-174
---
# --description--
Given a date string in the format `"YYYY-MM-DD"`, return the zodiac sign for that date using the following chart:
| Date Range | Zodiac Sign |
| - | - |
| March 21 - April 19 | `"Aries"` |
| April 20 - May 20 | `"Taurus"` |
| May 21 - June 20 | `"Gemini"` |
| June 21 - July 22 | `"Cancer"` |
| July 23 - August 22 | `"Leo"` |
| August 23 - September 22 | `"Virgo"` |
| September 23 - October 22 | `"Libra"` |
| October 23 - November 21 | `"Scorpio"` |
| November 22 - December 21 | `"Sagittarius"` |
| December 22 - January 19 | `"Capricorn"` |
| January 20 - February 18 | `"Aquarius"` |
| February 19 - March 20 | `"Pisces"` |
- Zodiac signs are based only on the month and day, you can ignore the year.
# --hints--
`getSign("2026-01-31")` should return `"Aquarius"`.
```js
assert.equal(getSign("2026-01-31"), "Aquarius");
```
`getSign("2001-06-10")` should return `"Gemini"`.
```js
assert.equal(getSign("2001-06-10"), "Gemini");
```
`getSign("1985-09-07")` should return `"Virgo"`.
```js
assert.equal(getSign("1985-09-07"), "Virgo");
```
`getSign("2023-03-19")` should return `"Pisces"`.
```js
assert.equal(getSign("2023-03-19"), "Pisces");
```
`getSign("2045-11-05")` should return `"Scorpio"`.
```js
assert.equal(getSign("2045-11-05"), "Scorpio");
```
`getSign("1985-12-06")` should return `"Sagittarius"`.
```js
assert.equal(getSign("1985-12-06"), "Sagittarius");
```
`getSign("2025-12-30")` should return `"Capricorn"`.
```js
assert.equal(getSign("2025-12-30"), "Capricorn");
```
`getSign("2018-10-08")` should return `"Libra"`.
```js
assert.equal(getSign("2018-10-08"), "Libra");
```
`getSign("1958-05-04")` should return `"Taurus"`.
```js
assert.equal(getSign("1958-05-04"), "Taurus");
```
# --seed--
## --seed-contents--
```js
function getSign(dateStr) {
return dateStr;
}
```
# --solutions--
```js
function getSign(dateStr) {
const [, monthStr, dayStr] = dateStr.split("-");
const month = Number(monthStr);
const day = Number(dayStr);
if ((month === 3 && day >= 21) || (month === 4 && day <= 19)) return "Aries";
if ((month === 4 && day >= 20) || (month === 5 && day <= 20)) return "Taurus";
if ((month === 5 && day >= 21) || (month === 6 && day <= 20)) return "Gemini";
if ((month === 6 && day >= 21) || (month === 7 && day <= 22)) return "Cancer";
if ((month === 7 && day >= 23) || (month === 8 && day <= 22)) return "Leo";
if ((month === 8 && day >= 23) || (month === 9 && day <= 22)) return "Virgo";
if ((month === 9 && day >= 23) || (month === 10 && day <= 22)) return "Libra";
if ((month === 10 && day >= 23) || (month === 11 && day <= 21)) return "Scorpio";
if ((month === 11 && day >= 22) || (month === 12 && day <= 21)) return "Sagittarius";
if ((month === 12 && day >= 22) || (month === 1 && day <= 19)) return "Capricorn";
if ((month === 1 && day >= 20) || (month === 2 && day <= 18)) return "Aquarius";
if ((month === 2 && day >= 19) || (month === 3 && day <= 20)) return "Pisces";
}
```
@@ -0,0 +1,98 @@
---
id: 69738771fb5a7b8b24cca2a1
title: "Challenge 175: Digital Detox"
challengeType: 28
dashedName: challenge-175
---
# --description--
Given an array of your login logs, determine whether you have met your digital detox goal.
Each log is a string in the format `"YYYY-MM-DD HH:mm:ss"`.
You have met your digital detox goal if both of the following statements are true:
- You logged in no more than once within any four-hour period.
- You logged in no more than 2 times on any single day.
# --hints--
`digitalDetox(["2026-02-01 08:00:00", "2026-02-01 12:30:00"])` should return `true`.
```js
assert.isTrue(digitalDetox(["2026-02-01 08:00:00", "2026-02-01 12:30:00"]));
```
`digitalDetox(["2026-02-01 04:00:00", "2026-02-01 07:30:00"])` should return `false`.
```js
assert.isFalse(digitalDetox(["2026-02-01 04:00:00", "2026-02-01 07:30:00"]));
```
`digitalDetox(["2026-01-31 08:21:30", "2026-01-31 14:30:00", "2026-02-01 08:00:00", "2026-02-01 12:30:00"])` should return `true`.
```js
assert.isTrue(digitalDetox(["2026-01-31 08:21:30", "2026-01-31 14:30:00", "2026-02-01 08:00:00", "2026-02-01 12:30:00"]));
```
`digitalDetox(["2026-01-31 10:40:21", "2026-01-31 15:19:41", "2026-01-31 21:49:50", "2026-02-01 09:30:00"])` should return `false`.
```js
assert.isFalse(digitalDetox(["2026-01-31 10:40:21", "2026-01-31 15:19:41", "2026-01-31 21:49:50", "2026-02-01 09:30:00"]));
```
`digitalDetox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 09:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"])` should return `true`.
```js
assert.isTrue(digitalDetox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 20:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 09:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"]));
```
`digitalDetox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 01:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"])` should return `false`.
```js
assert.isFalse(digitalDetox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 01:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"]));
```
# --seed--
## --seed-contents--
```js
function digitalDetox(logs) {
return logs;
}
```
# --solutions--
```js
function digitalDetox(logs) {
if (logs.length === 0) return true;
const times = logs
.map(log => new Date(log.replace(" ", "T") + "Z").getTime())
.sort((a, b) => a - b);
const ONE_DAY = 24 * 60 * 60 * 1000;
const FOUR_HOURS = 4 * 60 * 60 * 1000;
for (let i = 1; i < times.length; i++) {
if (times[i] - times[i - 1] < FOUR_HOURS) {
return false;
}
}
const dailyCounts = {};
for (const time of times) {
const day = new Date(time).toISOString().slice(0, 10);
dailyCounts[day] = (dailyCounts[day] || 0) + 1;
if (dailyCounts[day] > 2) {
return false;
}
}
return true;
}
```
@@ -0,0 +1,69 @@
---
id: 69738771fb5a7b8b24cca2a2
title: "Challenge 176: Groundhog Day"
challengeType: 28
dashedName: challenge-176
---
# --description--
Today is Groundhog Day, in which a groundhog predicts the weather based on whether or not it sees its shadow.
Given a value representing the groundhog's appearance, return the correct prediction:
- If the given value is the boolean `true` (the groundhog saw its shadow), return `"Looks like we'll have six more weeks of winter."`.
- If the value is the boolean `false` (the groundhog did not see its shadow), return `"It's going to be an early spring."`.
- If the value is anything else (the groundhog did not show up), return `"No prediction this year."`.
# --hints--
`groundhogDayPrediction(true)` should return `"Looks like we'll have six more weeks of winter."`.
```js
assert.equal(groundhogDayPrediction(true), "Looks like we'll have six more weeks of winter.");
```
`groundhogDayPrediction(false)` should return `"It's going to be an early spring."`.
```js
assert.equal(groundhogDayPrediction(false), "It's going to be an early spring.");
```
`groundhogDayPrediction(null)` should return `"No prediction this year."`.
```js
assert.equal(groundhogDayPrediction(null), "No prediction this year.");
```
`groundhogDayPrediction(" ")` should return `"No prediction this year."`.
```js
assert.equal(groundhogDayPrediction(" "), "No prediction this year.");
```
`groundhogDayPrediction("true")` should return `"No prediction this year."`.
```js
assert.equal(groundhogDayPrediction("true"), "No prediction this year.");
```
# --seed--
## --seed-contents--
```js
function groundhogDayPrediction(appearance) {
return appearance;
}
```
# --solutions--
```js
function groundhogDayPrediction(appearance) {
if (appearance === true) return "Looks like we'll have six more weeks of winter.";
if (appearance === false) return "It's going to be an early spring.";
return "No prediction this year.";
}
```
@@ -0,0 +1,56 @@
---
id: 69738771fb5a7b8b24cca2a3
title: "Challenge 177: String Mirror"
challengeType: 28
dashedName: challenge-177
---
# --description--
Given a string, return a new string that consists of the given string with a reversed copy of itself appended to the end of it.
# --hints--
`mirror("freeCodeCamp")` should return `"freeCodeCamppmaCedoCeerf"`.
```js
assert.equal(mirror("freeCodeCamp"), "freeCodeCamppmaCedoCeerf");
```
`mirror("RaceCar")` should return `"RaceCarraCecaR"`.
```js
assert.equal(mirror("RaceCar"), "RaceCarraCecaR");
```
`mirror("helloworld")` should return `"helloworlddlrowolleh"`.
```js
assert.equal(mirror("helloworld"), "helloworlddlrowolleh");
```
`mirror("The quick brown fox...")` should return `"The quick brown fox......xof nworb kciuq ehT"`.
```js
assert.equal(mirror("The quick brown fox..."), "The quick brown fox......xof nworb kciuq ehT");
```
# --seed--
## --seed-contents--
```js
function mirror(str) {
return str;
}
```
# --solutions--
```js
function mirror(str) {
const reversed = str.split("").reverse().join("");
return str + reversed;
}
```
@@ -0,0 +1,56 @@
---
id: 69738771fb5a7b8b24cca2a4
title: "Challenge 178: Truncate the Text"
challengeType: 28
dashedName: challenge-178
---
# --description--
Given a string, return it as-is if it's 20 characters or shorter. If it's longer than 20 characters, truncate it to the first 17 characters and append `"..."` to the end of it (so it's 20 characters total) and return the result.
# --hints--
`truncateText("Hello, world!")` should return `"Hello, world!"`.
```js
assert.equal(truncateText("Hello, world!"), "Hello, world!");
```
`truncateText("This string should get truncated.")` should return `"This string shoul..."`.
```js
assert.equal(truncateText("This string should get truncated."), "This string shoul...");
```
`truncateText("Exactly twenty chars")` should return `"Exactly twenty chars"`.
```js
assert.equal(truncateText("Exactly twenty chars"), "Exactly twenty chars");
```
`truncateText(".....................")` should return `"...................."`.
```js
assert.equal(truncateText("....................."), "....................");
```
# --seed--
## --seed-contents--
```js
function truncateText(text) {
return text;
}
```
# --solutions--
```js
function truncateText(text) {
if (text.length <= 20) return text;
return text.slice(0, 17) + "...";
}
```
@@ -0,0 +1,75 @@
---
id: 69738771fb5a7b8b24cca2a5
title: "Challenge 179: Pocket Change"
challengeType: 28
dashedName: challenge-179
---
# --description--
Given an array of integers representing the coins in your pocket, with each integer being the value of a coin in cents, return the total amount in the format `"$D.CC"`.
- 100 cents equals 1 dollar.
- In the return value, include a leading zero for amounts less than one dollar and always exactly two digits for the cents.
# --hints--
`countChange([25, 10, 5, 1])` should return `"$0.41"`.
```js
assert.equal(countChange([25, 10, 5, 1]), "$0.41");
```
`countChange([25, 10, 5, 1, 25, 10, 25, 1, 1, 10, 5, 25])` should return `"$1.43"`.
```js
assert.equal(countChange([25, 10, 5, 1, 25, 10, 25, 1, 1, 10, 5, 25]), "$1.43");
```
`countChange([100, 25, 100, 1000, 5, 500, 2000, 25])` should return `"$37.55"`.
```js
assert.equal(countChange([100, 25, 100, 1000, 5, 500, 2000, 25]), "$37.55");
```
`countChange([10, 5, 1, 10, 1, 25, 1, 1, 5, 1, 10])` should return `"$0.70"`.
```js
assert.equal(countChange([10, 5, 1, 10, 1, 25, 1, 1, 5, 1, 10]), "$0.70");
```
`countChange([1])` should return `"$0.01"`.
```js
assert.equal(countChange([1]), "$0.01");
```
`countChange([25, 25, 25, 25])` should return `"$1.00"`.
```js
assert.equal(countChange([25, 25, 25, 25]), "$1.00");
```
# --seed--
## --seed-contents--
```js
function countChange(change) {
return change;
}
```
# --solutions--
```js
function countChange(change) {
const totalCents = change.reduce((sum, coin) => sum + coin, 0);
const dollars = Math.floor(totalCents / 100);
const cents = totalCents % 100;
return `$${dollars}.${cents.toString().padStart(2, "0")}`;
}
```
@@ -0,0 +1,80 @@
---
id: 69738771fb5a7b8b24cca29d
title: "Challenge 171: Flatten the Array"
challengeType: 29
dashedName: challenge-171
---
# --description--
Given an array that contains nested arrays, return a new array with all values flattened into a single, one-dimensional array. Retain the original order of the items in the arrays.
# --hints--
`flatten([1, [2, 3], 4])` should return `[1, 2, 3, 4]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(flatten([1, [2, 3], 4]), [1, 2, 3, 4])`)
}})
```
`flatten([5, [4, [3, 2]], 1])` should return `[5, 4, 3, 2, 1]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(flatten([5, [4, [3, 2]], 1]), [5, 4, 3, 2, 1])`)
}})
```
`flatten(["A", [[[["B"]]]], "C"])` should return `["A", "B", "C"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(flatten(["A", [[[["B"]]]], "C"]), ["A", "B", "C"])`)
}})
```
`flatten([["L", "M", "N"], ["O", ["P", "Q", ["R", ["S", ["T", "U"]]]]], "V", ["W", ["X", ["Y", ["Z"]]]]])` should return `["L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(flatten([["L", "M", "N"], ["O", ["P", "Q", ["R", ["S", ["T", "U"]]]]], "V", ["W", ["X", ["Y", ["Z"]]]]]), ["L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"])`)
}})
```
`flatten([["red", ["blue", ["green", ["yellow", ["purple"]]]]], "orange", ["pink", ["brown"]]])` should return `["red","blue","green","yellow","purple","orange","pink","brown"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(flatten([["red", ["blue", ["green", ["yellow", ["purple"]]]]], "orange", ["pink", ["brown"]]]), ["red","blue","green","yellow","purple","orange","pink","brown"])`)
}})
```
# --seed--
## --seed-contents--
```py
def flatten(arr):
return arr
```
# --solutions--
```py
def flatten(arr):
result = []
for item in arr:
if isinstance(item, list):
result.extend(flatten(item))
else:
result.append(item)
return result
```
@@ -0,0 +1,82 @@
---
id: 69738771fb5a7b8b24cca29e
title: "Challenge 172: Letters-Numbers"
challengeType: 29
dashedName: challenge-172
---
# --description--
Given a string containing only letters and numbers, return a new string where a hyphen (`-`) is inserted every time the string switches from a letter to a number, or a number to a letter.
# --hints--
`separate_letters_and_numbers("ABC123")` should return `"ABC-123"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(separate_letters_and_numbers("ABC123"), "ABC-123")`)
}})
```
`separate_letters_and_numbers("Route66")` should return `"Route-66`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(separate_letters_and_numbers("ABC123"), "ABC-123")`)
}})
```
`separate_letters_and_numbers("H3LL0W0RLD")` should return `"H-3-LL-0-W-0-RLD"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(separate_letters_and_numbers("ABC123"), "ABC-123")`)
}})
```
`separate_letters_and_numbers("a1b2c3d4")` should return `"a-1-b-2-c-3-d-4"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(separate_letters_and_numbers("ABC123"), "ABC-123")`)
}})
```
# --seed--
## --seed-contents--
```py
def separate_letters_and_numbers(s):
return s
```
# --solutions--
```py
def separate_letters_and_numbers(s):
if not s:
return s
result = s[0]
for i in range(1, len(s)):
prev = s[i - 1]
curr = s[i]
prev_is_letter = prev.isalpha()
curr_is_letter = curr.isalpha()
if prev_is_letter != curr_is_letter:
result += "-"
result += curr
return result
```
@@ -0,0 +1,107 @@
---
id: 69738771fb5a7b8b24cca29f
title: "Challenge 173: Valid Pawn Moves"
challengeType: 29
dashedName: challenge-173
---
# --description--
Given the position of one of your pawns on a chessboard, return an array of all the valid squares it can move to in ascending order.
A standard chessboard is 8x8, with columns labeled `A` through `H` (left to right) and rows labeled `1` through `8` (bottom to top). It looks like this:
|**A8**|**B8**|**C8**|**D8**|**E8**|**F8**|**G8**|**H8**|
|-|-|-|-|-|-|-|-|
|**A7**|**B7**|**C7**|**D7**|**E7**|**F7**|**G7**|**H7**|
|**A6**|**B6**|**C6**|**D6**|**E6**|**F6**|**G6**|**H6**|
|**A5**|**B5**|**C5**|**D5**|**E5**|**F5**|**G5**|**H5**|
|**A4**|**B4**|**C4**|**D4**|**E4**|**F4**|**G4**|**H4**|
|**A3**|**B3**|**C3**|**D3**|**E3**|**F3**|**G3**|**H3**|
|**A2**|**B2**|**C2**|**D2**|**E2**|**F2**|**G2**|**H2**|
|**A1**|**B1**|**C1**|**D1**|**E1**|**F1**|**G1**|**H1**|
For this challenge:
- You are the player on the bottom of the board.
- Pawns can only move one square "up".
- Unless the pawn is in the starting row (row 2), then it can move one or two squares up.
For example, given `"D4"`, return `["D5"]`, the only square your pawn can move to.
Given `"B2"`, return `["B3", "B4"]`, because it's on the starting row and needs to be in ascending order.
# --hints--
`find_pawn_moves("D4")` should return `["D5"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(find_pawn_moves("D4"), ["D5"])`)
}})
```
`find_pawn_moves("B2")` should return `["B3", "B4"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(find_pawn_moves("B2"), ["B3", "B4"])`)
}})
```
`find_pawn_moves("A7")` should return `["A8"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(find_pawn_moves("A7"), ["A8"])`)
}})
```
`find_pawn_moves("G2")` should return `["G3", "G4"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(find_pawn_moves("G2"), ["G3", "G4"])`)
}})
```
`find_pawn_moves("E3")` should return `["E4"]`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(find_pawn_moves("E3"), ["E4"])`)
}})
```
# --seed--
## --seed-contents--
```py
def find_pawn_moves(position):
return position
```
# --solutions--
```py
def find_pawn_moves(position):
column = position[0].upper()
row = int(position[1])
moves = []
if row >= 8:
return moves
moves.append(f"{column}{row + 1}")
if row == 2:
moves.append(f"{column}{row + 2}")
return moves
```
@@ -0,0 +1,155 @@
---
id: 69738771fb5a7b8b24cca2a0
title: "Challenge 174: Zodiac Finder"
challengeType: 29
dashedName: challenge-174
---
# --description--
Given a date string in the format `"YYYY-MM-DD"`, return the zodiac sign for that date using the following chart:
| Date Range | Zodiac Sign |
| - | - |
| March 21 - April 19 | `"Aries"` |
| April 20 - May 20 | `"Taurus"` |
| May 21 - June 20 | `"Gemini"` |
| June 21 - July 22 | `"Cancer"` |
| July 23 - August 22 | `"Leo"` |
| August 23 - September 22 | `"Virgo"` |
| September 23 - October 22 | `"Libra"` |
| October 23 - November 21 | `"Scorpio"` |
| November 22 - December 21 | `"Sagittarius"` |
| December 22 - January 19 | `"Capricorn"` |
| January 20 - February 18 | `"Aquarius"` |
| February 19 - March 20 | `"Pisces"` |
- Zodiac signs are based only on the month and day, you can ignore the year.
# --hints--
`get_sign("2026-01-31")` should return `"Aquarius"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2026-01-31"), "Aquarius")`)
}})
```
`get_sign("2001-06-10")` should return `"Gemini"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2001-06-10"), "Gemini")`)
}})
```
`get_sign("1985-09-07")` should return `"Virgo"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("1985-09-07"), "Virgo")`)
}})
```
`get_sign("2023-03-19")` should return `"Pisces"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2023-03-19"), "Pisces")`)
}})
```
`get_sign("2045-11-05")` should return `"Scorpio"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2045-11-05"), "Scorpio")`)
}})
```
`get_sign("1985-12-06")` should return `"Sagittarius"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("1985-12-06"), "Sagittarius")`)
}})
```
`get_sign("2025-12-30")` should return `"Capricorn"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2025-12-30"), "Capricorn")`)
}})
```
`get_sign("2018-10-08")` should return `"Libra"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("2018-10-08"), "Libra")`)
}})
```
`get_sign("1958-05-04")` should return `"Taurus"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(get_sign("1958-05-04"), "Taurus")`)
}})
```
# --seed--
## --seed-contents--
```py
def get_sign(date_str):
return date_str
```
# --solutions--
```py
def get_sign(date_str):
_, month_str, day_str = date_str.split("-")
month = int(month_str)
day = int(day_str)
if (month == 3 and day >= 21) or (month == 4 and day <= 19):
return "Aries"
if (month == 4 and day >= 20) or (month == 5 and day <= 20):
return "Taurus"
if (month == 5 and day >= 21) or (month == 6 and day <= 20):
return "Gemini"
if (month == 6 and day >= 21) or (month == 7 and day <= 22):
return "Cancer"
if (month == 7 and day >= 23) or (month == 8 and day <= 22):
return "Leo"
if (month == 8 and day >= 23) or (month == 9 and day <= 22):
return "Virgo"
if (month == 9 and day >= 23) or (month == 10 and day <= 22):
return "Libra"
if (month == 10 and day >= 23) or (month == 11 and day <= 21):
return "Scorpio"
if (month == 11 and day >= 22) or (month == 12 and day <= 21):
return "Sagittarius"
if (month == 12 and day >= 22) or (month == 1 and day <= 19):
return "Capricorn"
if (month == 1 and day >= 20) or (month == 2 and day <= 18):
return "Aquarius"
if (month == 2 and day >= 19) or (month == 3 and day <= 20):
return "Pisces"
```
@@ -0,0 +1,114 @@
---
id: 69738771fb5a7b8b24cca2a1
title: "Challenge 175: Digital Detox"
challengeType: 29
dashedName: challenge-175
---
# --description--
Given an array of your login logs, determine whether you have met your digital detox goal.
Each log is a string in the format `"YYYY-MM-DD HH:mm:ss"`.
You have met your digital detox goal if both of the following statements are true:
- You logged in no more than once within any four-hour period.
- You logged in no more than 2 times on any single day.
# --hints--
`digital_detox(["2026-02-01 08:00:00", "2026-02-01 12:30:00"])` should return `True`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-02-01 08:00:00", "2026-02-01 12:30:00"]), True)`)
}})
```
`digital_detox(["2026-02-01 04:00:00", "2026-02-01 07:30:00"])` should return `False`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-02-01 04:00:00", "2026-02-01 07:30:00"]), False)`)
}})
```
`digital_detox(["2026-01-31 08:21:30", "2026-01-31 14:30:00", "2026-02-01 08:00:00", "2026-02-01 12:30:00"])` should return `True`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-01-31 08:21:30", "2026-01-31 14:30:00", "2026-02-01 08:00:00", "2026-02-01 12:30:00"]), True)`)
}})
```
`digital_detox(["2026-01-31 10:40:21", "2026-01-31 15:19:41", "2026-01-31 21:49:50", "2026-02-01 09:30:00"])` should return `False`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-01-31 10:40:21", "2026-01-31 15:19:41", "2026-01-31 21:49:50", "2026-02-01 09:30:00"]), False)`)
}})
```
`digital_detox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 09:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"])` should return `True`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 09:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"]), True)`)
}})
```
`digital_detox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 01:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"])` should return `False`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertIs(digital_detox(["2026-02-05 10:00:00", "2026-02-01 09:00:00", "2026-02-03 22:15:00", "2026-02-02 12:10:00", "2026-02-02 07:15:00", "2026-02-04 01:45:00", "2026-02-01 16:50:00", "2026-02-03 09:30:00"]), False)`)
}})
```
# --seed--
## --seed-contents--
```py
def digital_detox(logs):
return logs
```
# --solutions--
```py
from datetime import datetime
def digital_detox(logs):
if not logs:
return True
times = sorted(
datetime.strptime(log, "%Y-%m-%d %H:%M:%S")
for log in logs
)
FOUR_HOURS = 4 * 60 * 60
for i in range(1, len(times)):
diff = (times[i] - times[i - 1]).total_seconds()
if diff < FOUR_HOURS:
return False
daily_counts = {}
for time in times:
day = time.date()
daily_counts[day] = daily_counts.get(day, 0) + 1
if daily_counts[day] > 2:
return False
return True
```
@@ -0,0 +1,84 @@
---
id: 69738771fb5a7b8b24cca2a2
title: "Challenge 176: Groundhog Day"
challengeType: 29
dashedName: challenge-176
---
# --description--
Today is Groundhog Day, in which a groundhog predicts the weather based on whether or not it sees its shadow.
Given a value representing the groundhog's appearance, return the correct prediction:
- If the given value is the boolean `true` (the groundhog saw its shadow), return `"Looks like we'll have six more weeks of winter."`.
- If the value is the boolean `false` (the groundhog did not see its shadow), return `"It's going to be an early spring."`.
- If the value is anything else (the groundhog did not show up), return `"No prediction this year."`.
# --hints--
`groundhog_day_prediction(True)` should return `"Looks like we'll have six more weeks of winter."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(groundhog_day_prediction(True), "Looks like we'll have six more weeks of winter.")`)
}})
```
`groundhog_day_prediction(False)` should return `"It's going to be an early spring."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(groundhog_day_prediction(False), "It's going to be an early spring.")`)
}})
```
`groundhog_day_prediction(None)` should return `"No prediction this year."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(groundhog_day_prediction(None), "No prediction this year.")`)
}})
```
`groundhog_day_prediction(" ")` should return `"No prediction this year."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(groundhog_day_prediction(" "), "No prediction this year.")`)
}})
```
`groundhog_day_prediction("True")` should return `"No prediction this year."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(groundhog_day_prediction("True"), "No prediction this year.")`)
}})
```
# --seed--
## --seed-contents--
```py
def groundhog_day_prediction(appearance):
return appearance
```
# --solutions--
```py
def groundhog_day_prediction(appearance):
if appearance is True:
return "Looks like we'll have six more weeks of winter."
if appearance is False:
return "It's going to be an early spring."
return "No prediction this year."
```
@@ -0,0 +1,66 @@
---
id: 69738771fb5a7b8b24cca2a3
title: "Challenge 177: String Mirror"
challengeType: 29
dashedName: challenge-177
---
# --description--
Given a string, return a new string that consists of the given string with a reversed copy of itself appended to the end of it.
# --hints--
`mirror("freeCodeCamp")` should return `"freeCodeCamppmaCedoCeerf"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(mirror("freeCodeCamp"), "freeCodeCamppmaCedoCeerf")`)
}})
```
`mirror("RaceCar")` should return `"RaceCarraCecaR"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(mirror("RaceCar"), "RaceCarraCecaR")`)
}})
```
`mirror("helloworld")` should return `"helloworlddlrowolleh"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(mirror("helloworld"), "helloworlddlrowolleh")`)
}})
```
`mirror("The quick brown fox...")` should return `"The quick brown fox......xof nworb kciuq ehT"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(mirror("The quick brown fox..."), "The quick brown fox......xof nworb kciuq ehT")`)
}})
```
# --seed--
## --seed-contents--
```py
def mirror(s):
return s
```
# --solutions--
```py
def mirror(s):
reversed_s = s[::-1]
return s + reversed_s
```
@@ -0,0 +1,67 @@
---
id: 69738771fb5a7b8b24cca2a4
title: "Challenge 178: Truncate the Text"
challengeType: 29
dashedName: challenge-178
---
# --description--
Given a string, return it as-is if it's 20 characters or shorter. If it's longer than 20 characters, truncate it to the first 17 characters and append `"..."` to the end of it (so it's 20 characters total) and return the result.
# --hints--
`truncate_text("Hello, world!")` should return `"Hello, world!"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(truncate_text("Hello, world!"), "Hello, world!")`)
}})
```
`truncate_text("This string should get truncated.")` should return `"This string shoul..."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(truncate_text("This string should get truncated."), "This string shoul...")`)
}})
```
`truncate_text("Exactly twenty chars")` should return `"Exactly twenty chars"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(truncate_text("Exactly twenty chars"), "Exactly twenty chars")`)
}})
```
`truncate_text(".....................")` should return `"...................."`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(truncate_text("....................."), "....................")`)
}})
```
# --seed--
## --seed-contents--
```py
def truncate_text(text):
return text
```
# --solutions--
```py
def truncate_text(text):
if len(text) <= 20:
return text
return text[:17] + "..."
```
@@ -0,0 +1,89 @@
---
id: 69738771fb5a7b8b24cca2a5
title: "Challenge 179: Pocket Change"
challengeType: 29
dashedName: challenge-179
---
# --description--
Given an array of integers representing the coins in your pocket, with each integer being the value of a coin in cents, return the total amount in the format `"$D.CC"`.
- 100 cents equals 1 dollar.
- In the return value, include a leading zero for amounts less than one dollar and always exactly two digits for the cents.
# --hints--
`count_change([25, 10, 5, 1])` should return `"$0.41"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([25, 10, 5, 1]), "$0.41")`)
}})
```
`count_change([25, 10, 5, 1, 25, 10, 25, 1, 1, 10, 5, 25])` should return `"$1.43"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([25, 10, 5, 1, 25, 10, 25, 1, 1, 10, 5, 25]), "$1.43")`)
}})
```
`count_change([100, 25, 100, 1000, 5, 500, 2000, 25])` should return `"$37.55"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([100, 25, 100, 1000, 5, 500, 2000, 25]), "$37.55")`)
}})
```
`count_change([10, 5, 1, 10, 1, 25, 1, 1, 5, 1, 10])` should return `"$0.70"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([10, 5, 1, 10, 1, 25, 1, 1, 5, 1, 10]), "$0.70")`)
}})
```
`count_change([1])` should return `"$0.01"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([1]), "$0.01")`)
}})
```
`count_change([25, 25, 25, 25])` should return `"$1.00"`.
```js
({test: () => { runPython(`
from unittest import TestCase
TestCase().assertEqual(count_change([25, 25, 25, 25]), "$1.00")`)
}})
```
# --seed--
## --seed-contents--
```py
def count_change(change):
return change
```
# --solutions--
```py
def count_change(change):
total_cents = sum(change)
dollars = total_cents // 100
cents = total_cents % 100
return f"${dollars}.{cents:02d}"
```
@@ -686,6 +686,42 @@
{
"id": "696655d24b614176d4c9b78d",
"title": "Challenge 170: Odd or Even Day"
},
{
"id": "69738771fb5a7b8b24cca29d",
"title": "Challenge 171: Flatten the Array"
},
{
"id": "69738771fb5a7b8b24cca29e",
"title": "Challenge 172: Letters-Numbers"
},
{
"id": "69738771fb5a7b8b24cca29f",
"title": "Challenge 173: Valid Pawn Moves"
},
{
"id": "69738771fb5a7b8b24cca2a0",
"title": "Challenge 174: Zodiac Finder"
},
{
"id": "69738771fb5a7b8b24cca2a1",
"title": "Challenge 175: Digital Detox"
},
{
"id": "69738771fb5a7b8b24cca2a2",
"title": "Challenge 176: Groundhog Day"
},
{
"id": "69738771fb5a7b8b24cca2a3",
"title": "Challenge 177: String Mirror"
},
{
"id": "69738771fb5a7b8b24cca2a4",
"title": "Challenge 178: Truncate the Text"
},
{
"id": "69738771fb5a7b8b24cca2a5",
"title": "Challenge 179: Pocket Change"
}
]
}
@@ -685,6 +685,42 @@
{
"id": "696655d24b614176d4c9b78d",
"title": "Challenge 170: Odd or Even Day"
},
{
"id": "69738771fb5a7b8b24cca29d",
"title": "Challenge 171: Flatten the Array"
},
{
"id": "69738771fb5a7b8b24cca29e",
"title": "Challenge 172: Letters-Numbers"
},
{
"id": "69738771fb5a7b8b24cca29f",
"title": "Challenge 173: Valid Pawn Moves"
},
{
"id": "69738771fb5a7b8b24cca2a0",
"title": "Challenge 174: Zodiac Finder"
},
{
"id": "69738771fb5a7b8b24cca2a1",
"title": "Challenge 175: Digital Detox"
},
{
"id": "69738771fb5a7b8b24cca2a2",
"title": "Challenge 176: Groundhog Day"
},
{
"id": "69738771fb5a7b8b24cca2a3",
"title": "Challenge 177: String Mirror"
},
{
"id": "69738771fb5a7b8b24cca2a4",
"title": "Challenge 178: Truncate the Text"
},
{
"id": "69738771fb5a7b8b24cca2a5",
"title": "Challenge 179: Pocket Change"
}
]
}
@@ -13,7 +13,7 @@ const { MONGOHQ_URL } = process.env;
// Number challenges in the dev-playground blocks
// Update this if the number of challenges changes
const EXPECTED_CHALLENGE_COUNT = 170;
const EXPECTED_CHALLENGE_COUNT = 179;
// Date to set for the first challenge, second challenge will be one day later, etc...
// **DO NOT CHANGE THIS AFTER RELEASE (if seeding production - okay for local dev)**