Challenges
What the models are asked to do. Each challenge is a frozen spec plus a pytest suite; a reference solution must pass the suite before a challenge can ship, so a model failure is always a model finding — never a broken test. Grading: tests 60% · deliverables 20% · content checks 20%.
Expression evaluator (no eval)calculator
Python
7
pytest cases
1
deliverables
1
content checks
49
reference SLOC
20
reference complexity
// the spec given to every model
Create calc.py with exactly one public function:
evaluate(expr: str) -> float
Evaluate an arithmetic expression containing numbers (integers or decimals), the binary operators + - * /, unary minus, parentheses, and arbitrary whitespace. Normal precedence: * and / bind tighter than + and -; operators of equal precedence associate left to right. Division is true division. Examples: evaluate("2+3*4") == 14, evaluate("(2+3)*4") == 20, evaluate("2*-3") == -6.
You must parse the expression yourself: using eval() or exec() is forbidden and is checked by the test suite. Use only the Python standard library.
the pytest suite (7 cases)
test_calc.py · 36 lines · 688 B
from pathlib import Path from calc import evaluate def test_precedence(): assert evaluate("2+3*4") == 14 def test_parentheses(): assert evaluate("(2+3)*4") == 20 assert evaluate("2*(3+(4-1))") == 12 def test_true_division(): assert evaluate("10/4") == 2.5 def test_left_associativity(): assert evaluate("10-3-2") == 5 assert evaluate("16/4/2") == 2 def test_unary_minus(): assert evaluate("-3+5") == 2 assert evaluate("2*-3") == -6 def test_whitespace_and_decimals(): assert evaluate(" 1.5 + 2.25 ") == 3.75 def test_does_not_use_eval(): src = Path("calc.py").read_text() assert "eval(" not in src assert "exec(" not in src
FizzBuzzfizzbuzz
Python
3
pytest cases
1
deliverables
1
content checks
12
reference SLOC
5
reference complexity
// the spec given to every model
Create fizzbuzz.py with exactly one function:
fizzbuzz(n: int) -> list[str]
Return a list for the numbers 1 through n inclusive: "FizzBuzz" for multiples of both 3 and 5, "Fizz" for multiples of 3, "Buzz" for multiples of 5, otherwise the number as a string. fizzbuzz(0) returns an empty list.
Use only the Python standard library.
the pytest suite (3 cases)
test_fizzbuzz.py · 16 lines · 322 B
from fizzbuzz import fizzbuzz def test_first_fifteen(): assert fizzbuzz(15) == [ "1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz", ] def test_single(): assert fizzbuzz(1) == ["1"] def test_zero_is_empty(): assert fizzbuzz(0) == []
JSON Pointer (RFC 6901)json-pointer
Python
10
pytest cases
1
deliverables
1
content checks
23
reference SLOC
9
reference complexity
// the spec given to every model
Create jsonpointer.py with exactly one function:
resolve(doc, pointer: str)
Resolve an RFC 6901 JSON Pointer against doc (nested dicts/lists) and return the referenced value.
Rules:
- The empty pointer "" refers to the whole document.
- A pointer is a sequence of /-prefixed reference tokens: "/a/b" means doc["a"]["b"].
- In a token, the escape ~1 decodes to "/" and ~0 decodes to "~" (decode ~1 before ~0).
- Tokens indexing a list are decimal indices: "/items/0" is the first element.
- Raise KeyError for anything that does not resolve (missing key, bad or out-of-range index, pointer not starting with "/", descending into a scalar).
Use only the Python standard library.
the pytest suite (10 cases)
test_jsonpointer.py · 56 lines · 996 B
import pytest from jsonpointer import resolve DOC = { "foo": ["bar", "baz"], "": 0, "a/b": 1, "m~n": 8, "nested": {"list": [{"x": 42}]},} def test_whole_document(): assert resolve(DOC, "") is DOC def test_object_key(): assert resolve(DOC, "/foo") == ["bar", "baz"] def test_array_index(): assert resolve(DOC, "/foo/0") == "bar" def test_empty_key(): assert resolve(DOC, "/") == 0 def test_escaped_slash(): assert resolve(DOC, "/a~1b") == 1 def test_escaped_tilde(): assert resolve(DOC, "/m~0n") == 8 def test_deep_path(): assert resolve(DOC, "/nested/list/0/x") == 42 def test_missing_key_raises(): with pytest.raises(KeyError): resolve(DOC, "/nope") def test_bad_index_raises(): with pytest.raises(KeyError): resolve(DOC, "/foo/2") with pytest.raises(KeyError): resolve(DOC, "/foo/x") def test_pointer_without_slash_raises(): with pytest.raises(KeyError): resolve(DOC, "foo")
LRU cachelru-cache
Python
5
pytest cases
1
deliverables
3
content checks
16
reference SLOC
4
reference complexity
// the spec given to every model
Create lru.py with a class:
class LRUCache:
def __init__(self, capacity: int)
def get(self, key) -> value # return the stored value, or -1 if absent
def put(self, key, value) -> None # insert or update
Semantics: the cache holds at most `capacity` entries. Both get() and put() count as a use of the key. When put() would exceed capacity, evict the least-recently-used key first. put() on an existing key updates its value and makes it most-recently-used.
Use only the Python standard library.
the pytest suite (5 cases)
test_lru.py · 46 lines · 842 B
from lru import LRUCache def test_basic_put_get(): c = LRUCache(2) c.put("a", 1) assert c.get("a") == 1 assert c.get("missing") == -1 def test_eviction_order(): c = LRUCache(2) c.put("a", 1) c.put("b", 2) c.put("c", 3) assert c.get("a") == -1 assert c.get("b") == 2 assert c.get("c") == 3 def test_get_refreshes_recency(): c = LRUCache(2) c.put("a", 1) c.put("b", 2) c.get("a") c.put("c", 3) assert c.get("b") == -1 assert c.get("a") == 1 def test_put_overwrite_refreshes(): c = LRUCache(2) c.put("a", 1) c.put("b", 2) c.put("a", 10) c.put("c", 3) assert c.get("b") == -1 assert c.get("a") == 10 def test_capacity_one(): c = LRUCache(1) c.put("a", 1) c.put("b", 2) assert c.get("a") == -1 assert c.get("b") == 2
Palindrome checkpalindrome
Python
5
pytest cases
1
deliverables
1
content checks
3
reference SLOC
3
reference complexity
// the spec given to every model
Create palindrome.py with exactly one function:
is_palindrome(text: str) -> bool
Return True if text reads the same forwards and backwards, ignoring letter case and ignoring every character that is not a letter or digit. Text with no letters or digits at all (including the empty string) counts as a palindrome.
Use only the Python standard library.
the pytest suite (5 cases)
test_palindrome.py · 22 lines · 480 B
from palindrome import is_palindrome def test_simple_palindrome(): assert is_palindrome("racecar") is True def test_simple_non_palindrome(): assert is_palindrome("hello") is False def test_ignores_case_and_punctuation(): assert is_palindrome("A man, a plan, a canal: Panama!") is True def test_empty_is_palindrome(): assert is_palindrome("") is True def test_digits(): assert is_palindrome("12321") is True assert is_palindrome("12345") is False
Temperature conversiontemperature
Python
4
pytest cases
1
deliverables
2
content checks
4
reference SLOC
1
reference complexity
// the spec given to every model
Create temperature.py with exactly two functions:
c_to_f(celsius: float) -> float
Convert Celsius to Fahrenheit: F = C * 9/5 + 32.
f_to_c(fahrenheit: float) -> float
Convert Fahrenheit to Celsius: C = (F - 32) * 5/9.
Use only the Python standard library.
the pytest suite (4 cases)
test_temperature.py · 20 lines · 357 B
from temperature import c_to_f, f_to_c def test_freezing(): assert c_to_f(0) == 32 assert f_to_c(32) == 0 def test_boiling(): assert c_to_f(100) == 212 assert f_to_c(212) == 100 def test_negative(): assert c_to_f(-40) == -40 assert f_to_c(-40) == -40 def test_round_trip(): assert abs(f_to_c(c_to_f(37.5)) - 37.5) < 1e-9
Text statistics moduletext-stats
Python
9
pytest cases
1
deliverables
3
content checks
13
reference SLOC
2
reference complexity
// the spec given to every model
Create textstats.py, a small text-statistics module with exactly these three functions: 1. word_count(text: str) -> int Words are maximal runs of ASCII letters, digits, and apostrophes (regex [A-Za-z0-9']+). Return how many words text contains. Empty or word-free text returns 0. 2. top_words(text: str, n: int) -> list[tuple[str, int]] Count words case-insensitively (lowercase them). Return the n most frequent (word, count) pairs, most frequent first; break count ties alphabetically by word. 3. reading_time(text: str) -> int Estimated minutes to read at 200 words per minute, rounded up, with a minimum of 1 (even for empty text). Use only the Python standard library.
the pytest suite (9 cases)
test_textstats.py · 38 lines · 873 B
from textstats import word_count, top_words, reading_time def test_word_count_basic(): assert word_count("the cat sat on the mat") == 6 def test_word_count_empty(): assert word_count("") == 0 def test_word_count_punctuation(): assert word_count("Hi, there! Hi.") == 3 def test_word_count_apostrophes(): assert word_count("don't stop") == 2 def test_top_words_orders_by_count(): assert top_words("a b a c a b", 2) == [("a", 3), ("b", 2)] def test_top_words_ties_alphabetical(): assert top_words("beta alpha", 2) == [("alpha", 1), ("beta", 1)] def test_top_words_case_insensitive(): assert top_words("The the THE", 1) == [("the", 3)] def test_reading_time_rounds_up(): assert reading_time(" ".join(["w"] * 401)) == 3 def test_reading_time_minimum_one(): assert reading_time("hello") == 1 assert reading_time("") == 1
Vowel countvowel-count
Python
5
pytest cases
1
deliverables
1
content checks
2
reference SLOC
3
reference complexity
// the spec given to every model
Create vowels.py with exactly one function:
count_vowels(text: str) -> int
Return how many characters of text are vowels (a, e, i, o, u), counting both uppercase and lowercase.
Use only the Python standard library.
the pytest suite (5 cases)
test_vowels.py · 21 lines · 329 B
from vowels import count_vowels def test_basic(): assert count_vowels("hello") == 2 def test_empty(): assert count_vowels("") == 0 def test_uppercase(): assert count_vowels("AEIOU") == 5 def test_no_vowels(): assert count_vowels("xyz") == 0 def test_mixed(): assert count_vowels("Programming") == 3
Word ladder (shortest path)word-ladder
Python
5
pytest cases
1
deliverables
1
content checks
19
reference SLOC
8
reference complexity
// the spec given to every model
Create ladder.py with exactly one function:
ladder_length(begin: str, end: str, word_list: list[str]) -> int
A transformation sequence goes from begin to end, changing exactly one letter per step; every intermediate word (and end itself) must appear in word_list. Return the number of words in the SHORTEST such sequence counting both endpoints, or 0 if none exists. begin does not need to be in word_list. All words are lowercase and the same length.
Example: begin="hit", end="cog", word_list=["hot","dot","dog","lot","log","cog"] -> 5 (hit -> hot -> dot -> dog -> cog).
Use only the Python standard library. A breadth-first search is the intended approach; brute-force recursion will be too slow for the larger test.
the pytest suite (5 cases)
test_ladder.py · 27 lines · 744 B
import time from ladder import ladder_length def test_classic_example(): assert ladder_length("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"]) == 5 def test_unreachable_end(): assert ladder_length("hit", "cog", ["hot", "dot", "dog", "lot", "log"]) == 0 def test_single_step(): assert ladder_length("a", "c", ["a", "b", "c"]) == 2 def test_shortest_is_found(): words = ["hot", "hit", "hat", "cat", "cot", "cog", "dog"] assert ladder_length("hit", "cot", words) == 3 def test_larger_input_is_fast(): words = [a + b + c for a in "abcdefgh" for b in "abcdefgh" for c in "abcdefgh"] start = time.monotonic() assert ladder_length("aaa", "hhh", words) == 4 assert time.monotonic() - start < 2.0