seed: curriculum content

This commit is contained in:
2026-05-07 14:32:44 +00:00
parent 9258534803
commit ec76f4f56b
100 changed files with 2846 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
---
type: module
title: "Strings"
description: "Text is half of every program — index it, slice it, transform it, count it, format it."
---
# Strings
Half of what programs do is move text around. Reading user input,
formatting reports, parsing log lines, validating passwords — all
strings. This module hands you the toolkit.
Seven blocks. About three and a half hours. No conditionals or loops
yet — those come next module — so every challenge here works through
indexing, slicing, methods, and built-in functions.
## What You'll Do
- Index and slice strings
- Apply the most-used string methods (`upper`, `strip`, `split`,
`replace`, ...)
- Validate, count, mirror, and format text
- Build a small mission report from raw fields
## Pace
About 3.5 hours. Each block is a small real program — no drills.

View File

@@ -0,0 +1,42 @@
---
type: challenge
title: "The Message"
xp: 50
duration: 25
difficulty: 2
---
# The Message
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, a string is a *sequence of characters*. You can pick any
> one out by its position (its *index*).
>
> Indexes start at 0:
>
> ```
> M I S S I O N
> 0 1 2 3 4 5 6
> ```
>
> Negative indexes count back from the end. `s[-1]` is the last
> character.
>
> Strings are *immutable* — you can read any character, but you can't
> change one in place. To "modify" a string, you build a new one.
>
> Implement `info(s)` that returns a dict with four keys: `first`,
> `sixth`, `last`, `length`.
>
> [END TRANSMISSION]
## Your Task
Open `starter/starter.py`. Use `s[0]`, `s[5]`, `s[-1]`, and `len(s)`
to fill in the dict.
## Objectives
- `info("MISSION-CONTROL-7")` returns `{"first": "M", "sixth": "O", "last": "7", "length": 17}`
- Works for any non-empty string of length ≥ 6

View File

@@ -0,0 +1,13 @@
def info(s):
"""Return a dict describing the string s.
Keys:
"first" — the character at index 0
"sixth" — the character at index 5
"last" — the character at index -1
"length" — total number of characters
info("MISSION-CONTROL-7") -> {"first": "M", "sixth": "O",
"last": "7", "length": 17}
"""
pass

View File

@@ -0,0 +1,16 @@
from solution import info
def test_mission_control():
r = info("MISSION-CONTROL-7")
assert r == {"first": "M", "sixth": "O", "last": "7", "length": 17}
def test_andromeda():
r = info("andromeda")
assert r == {"first": "a", "sixth": "m", "last": "a", "length": 9}
def test_alphabet():
r = info("ABCDEFGH")
assert r == {"first": "A", "sixth": "F", "last": "H", "length": 8}

View File

@@ -0,0 +1,45 @@
---
type: challenge
title: "The Slicer"
xp: 50
duration: 25
difficulty: 2
---
# The Slicer
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, *slicing* grabs a chunk of a string instead of one character.
> The syntax is `s[start:end]` — `start` is included, `end` is not.
>
> ```python
> s = "ANDROMEDA"
> s[0:3] # "AND"
> s[3:7] # "ROME"
> s[-2:] # "DA"
> ```
>
> Telemetry packets always arrive in this exact format (length 30):
>
> ```
> TIMESTAMP=YYYY-MM-DDTHH:MM:SSZ
> ```
>
> Implement `extract(header)` that returns a tuple `(date, time)`:
>
> - `date` = `YYYY-MM-DD` (10 chars)
> - `time` = `HH:MM:SS` (8 chars)
>
> Figure out which slice positions hold each piece.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, return `(date, time)` extracted with slicing.
## Objectives
- `extract("TIMESTAMP=2026-05-04T14:32:01Z")``("2026-05-04", "14:32:01")`
- Works for any header in the same fixed format

View File

@@ -0,0 +1,16 @@
def extract(header):
"""Extract date and time from a fixed-format packet header.
Header format (always exactly this shape):
TIMESTAMP=YYYY-MM-DDTHH:MM:SSZ
Length 30. Example:
TIMESTAMP=2026-05-04T14:32:01Z
Return a tuple (date, time) where:
date = "YYYY-MM-DD" (10 chars)
time = "HH:MM:SS" (8 chars)
extract("TIMESTAMP=2026-05-04T14:32:01Z")
-> ("2026-05-04", "14:32:01")
"""
pass

View File

@@ -0,0 +1,13 @@
from solution import extract
def test_basic():
assert extract("TIMESTAMP=2026-05-04T14:32:01Z") == ("2026-05-04", "14:32:01")
def test_apollo():
assert extract("TIMESTAMP=1969-07-20T13:32:00Z") == ("1969-07-20", "13:32:00")
def test_end_of_century():
assert extract("TIMESTAMP=2099-12-31T23:59:59Z") == ("2099-12-31", "23:59:59")

View File

@@ -0,0 +1,43 @@
---
type: challenge
title: "The Methods"
xp: 50
duration: 30
difficulty: 2
---
# The Methods
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, every string carries a toolkit of methods. The most-used:
>
> - `s.upper()` — uppercase copy
> - `s.lower()` — lowercase copy
> - `s.strip()` — drops leading/trailing whitespace
> - `s.replace(a, b)` — every `a` becomes `b`
>
> Methods can be *chained* — each returns a new string you can call
> the next on:
>
> ```python
> " Hello World ".strip().lower().replace(" ", "_")
> # → "hello_world"
> ```
>
> Implement `transform(s)` that returns a dict with three keys:
>
> - `upper` — input uppercased
> - `lower` — input lowercased
> - `clean` — input stripped, lowercased, spaces replaced by `_`
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, build the dict using the four methods.
## Objectives
- `transform(" Hello World ")["clean"]` returns `"hello_world"`
- All three keys present and correct for any string

View File

@@ -0,0 +1,15 @@
def transform(s):
"""Apply three string transformations and return them in a dict.
Keys:
"upper" — s.upper()
"lower" — s.lower()
"clean" — s stripped of leading/trailing whitespace,
lowercased, with spaces replaced by underscores
transform(" Hello World ") returns:
{"upper": " HELLO WORLD ",
"lower": " hello world ",
"clean": "hello_world"}
"""
pass

View File

@@ -0,0 +1,22 @@
from solution import transform
def test_padded_hello_world():
r = transform(" Hello World ")
assert r["upper"] == " HELLO WORLD "
assert r["lower"] == " hello world "
assert r["clean"] == "hello_world"
def test_mission_ready():
r = transform("MISSION READY")
assert r["upper"] == "MISSION READY"
assert r["lower"] == "mission ready"
assert r["clean"] == "mission_ready"
def test_andromeda_sector():
r = transform("andromeda sector")
assert r["upper"] == "ANDROMEDA SECTOR"
assert r["lower"] == "andromeda sector"
assert r["clean"] == "andromeda_sector"

View File

@@ -0,0 +1,44 @@
---
type: challenge
title: "The Validator"
xp: 75
duration: 30
difficulty: 2
---
# The Validator
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, every system that takes user input has to *validate* it.
>
> Comparison operators (`>=`, `<`, `==`) return `True` or `False`
> directly. String methods like `.isupper()` and `.isdigit()` also
> return booleans:
>
> ```python
> len("hello") >= 8 # False
> "Hello"[0].isupper() # True
> "abc1"[-1].isdigit() # True
> ```
>
> Implement `validate(pw)` that returns a dict with four checks on
> a non-empty password:
>
> - `length` — char count (int)
> - `long_enough` — `length >= 8` (bool)
> - `starts_capital` — first char uppercase (bool)
> - `ends_digit` — last char a digit (bool)
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, return the dict using `len()`, `>=`,
`.isupper()`, `.isdigit()`.
## Objectives
- All four keys present with correct types
- `validate("Andromeda1")` matches the expected dict exactly
- Works for any non-empty password

View File

@@ -0,0 +1,17 @@
def validate(pw):
"""Check four properties of a non-empty password and return a dict.
Keys:
"length" — number of characters (int)
"long_enough" — True if length >= 8 (bool)
"starts_capital" — True if first char is uppercase (bool)
"ends_digit" — True if last char is a digit (bool)
Helpers:
pw[0].isupper() — True if first char is uppercase
pw[-1].isdigit() — True if last char is a digit
validate("Andromeda1") -> {"length": 10, "long_enough": True,
"starts_capital": True, "ends_digit": True}
"""
pass

View File

@@ -0,0 +1,41 @@
from solution import validate
def test_strong_password():
r = validate("Andromeda1")
assert r == {
"length": 10,
"long_enough": True,
"starts_capital": True,
"ends_digit": True,
}
def test_short_lowercase_no_digit():
r = validate("abc")
assert r == {
"length": 3,
"long_enough": False,
"starts_capital": False,
"ends_digit": False,
}
def test_capital_no_digit():
r = validate("Hello")
assert r == {
"length": 5,
"long_enough": False,
"starts_capital": True,
"ends_digit": False,
}
def test_long_lowercase_with_digit():
r = validate("spaceX2026")
assert r == {
"length": 10,
"long_enough": True,
"starts_capital": False,
"ends_digit": True,
}

View File

@@ -0,0 +1,43 @@
---
type: challenge
title: "The Mirror"
xp: 75
duration: 30
difficulty: 3
---
# The Mirror
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, a *palindrome* reads the same forwards and backwards.
> `racecar`. `level`. `madam`.
>
> A reverse-slice flips a string in one move:
>
> ```python
> "Hello"[::-1] # "olleH"
> ```
>
> A string is a palindrome when it equals its reverse. The `==`
> operator returns `True` or `False` directly.
>
> Implement `mirror(s)` that returns a dict with three keys:
>
> - `original` — the input
> - `reversed` — the input reversed
> - `palindrome` — `True` if equal to its reverse
>
> Match case exactly — `racecar` and `RaceCar` are different inputs.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, build the dict using `[::-1]` and `==`.
## Objectives
- `mirror("racecar")["palindrome"]` is `True`
- `mirror("hello")["palindrome"]` is `False`
- All three keys correct for any input string

View File

@@ -0,0 +1,19 @@
def mirror(s):
"""Detect if s is a palindrome and return a dict.
Keys:
"original" — s unchanged
"reversed" — s reversed
"palindrome" — True if s equals its reverse, False otherwise
Reverse a string with [::-1]:
"Hello"[::-1] -> "olleH"
mirror("racecar") -> {"original": "racecar",
"reversed": "racecar",
"palindrome": True}
mirror("hello") -> {"original": "hello",
"reversed": "olleh",
"palindrome": False}
"""
pass

View File

@@ -0,0 +1,27 @@
from solution import mirror
def test_palindrome():
r = mirror("racecar")
assert r == {"original": "racecar", "reversed": "racecar", "palindrome": True}
def test_not_palindrome():
r = mirror("hello")
assert r == {"original": "hello", "reversed": "olleh", "palindrome": False}
def test_level():
r = mirror("level")
assert r["palindrome"] is True
def test_andromeda():
r = mirror("andromeda")
assert r["palindrome"] is False
assert r["reversed"] == "ademordna"
def test_noon():
r = mirror("noon")
assert r == {"original": "noon", "reversed": "noon", "palindrome": True}

View File

@@ -0,0 +1,35 @@
---
type: challenge
title: "The Counter"
xp: 75
duration: 30
difficulty: 3
---
# The Counter
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, time to count things in text. Three counts:
>
> - **Total length** — `len(s)`
> - **Word count** — `len(s.split())` (split breaks on whitespace)
> - **`a` count** — `s.lower().count("a")` returns how many `a`s
>
> Implement `count(s)` that returns a dict with three keys:
>
> - `length` — total chars including spaces
> - `words` — word count
> - `a_count` — number of lowercase `a`s after lowercasing
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, build the dict using `len()`, `.split()`,
`.lower()`, `.count()`.
## Objectives
- `count("Hello andromeda")``{"length": 15, "words": 2, "a_count": 2}`
- All three keys present and correct for any sentence

View File

@@ -0,0 +1,16 @@
def count(s):
"""Count three things in a sentence and return a dict.
Keys:
"length" — total characters in s (including spaces)
"words" — number of whitespace-separated words
"a_count" — count of lowercase 'a's after lowercasing s
Helpers:
len(s) — char count
len(s.split()) — word count
s.lower().count("a") — number of 'a's, case-insensitive
count("Hello andromeda") -> {"length": 15, "words": 2, "a_count": 2}
"""
pass

View File

@@ -0,0 +1,21 @@
from solution import count
def test_hello_andromeda():
assert count("Hello andromeda") == {"length": 15, "words": 2, "a_count": 2}
def test_all_systems_nominal():
assert count("All systems nominal") == {"length": 19, "words": 3, "a_count": 2}
def test_caps_with_a():
assert count("MISSION READY ALPHA") == {"length": 19, "words": 3, "a_count": 3}
def test_quick_brown_fox():
assert count("the quick brown fox jumps over") == {
"length": 30,
"words": 6,
"a_count": 0,
}

View File

@@ -0,0 +1,47 @@
---
type: challenge
title: "The Report"
xp: 100
duration: 35
difficulty: 3
---
# The Report
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, capstone for the strings module. Combine f-strings,
> repetition, methods, and `\n` joining into one formatted output.
>
> Repetition: `"=" * 30` produces a string of 30 equals signs.
>
> Implement `format_report(name, status, location, time)` that
> returns this exact 8-line report as a single string (lines joined
> with `\n`):
>
> ```
> ==============================
> MISSION REPORT
> ==============================
> Name: <NAME IN UPPERCASE>
> Status: <status as entered>
> Location: <location as entered>
> Time: <time as entered>
> ==============================
> ```
>
> Only the name is uppercased; other fields print as entered.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.py`, build the report string. Use `"=" * 30`
for borders, `name.upper()` for the name, and join lines with `\n`.
## Objectives
- `format_report("apollo", "ok", "moon", "13:32")` returns the
exact 8-line string
- Only the name is uppercased
- Borders are 30 `=` characters

View File

@@ -0,0 +1,19 @@
def format_report(name, status, location, time):
"""Return a formatted mission report as a single string.
The report is 8 lines, joined by '\n'. The borders are 30 equals
signs ("=" * 30). Only the name is uppercased; other fields are
inserted as-is.
format_report("apollo", "ok", "moon", "13:32") returns:
==============================
MISSION REPORT
==============================
Name: APOLLO
Status: ok
Location: moon
Time: 13:32
==============================
"""
pass

View File

@@ -0,0 +1,36 @@
from solution import format_report
def test_apollo():
expected = (
"==============================\n"
"MISSION REPORT\n"
"==============================\n"
"Name: APOLLO\n"
"Status: ok\n"
"Location: moon\n"
"Time: 13:32\n"
"=============================="
)
assert format_report("apollo", "ok", "moon", "13:32") == expected
def test_voyager():
result = format_report("voyager", "active", "interstellar", "1977-09-05")
assert "Name: VOYAGER" in result
assert "Status: active" in result
assert "Location: interstellar" in result
assert "Time: 1977-09-05" in result
assert result.startswith("=" * 30 + "\n")
assert result.endswith("\n" + "=" * 30)
def test_perseverance():
result = format_report("perseverance", "roving", "jezero crater", "2026-05-04")
assert "Name: PERSEVERANCE" in result
assert "Location: jezero crater" in result
def test_eight_lines():
result = format_report("apollo", "ok", "moon", "13:32")
assert result.count("\n") == 7