""" Problem archetypes with seeded test generators plus edge-case mutation. Three archetypes x three difficulty tiers = 9 canonical problem classes. """ import random from typing import Any, Dict, List def _make_rng(seed: int | None, offset: int) -> random.Random: base_seed = 42 if seed is None else seed return random.Random(base_seed + offset) def _gen_array_tests(task_id: int, difficulty: int, seed: int | None = None) -> List[Dict]: rng = _make_rng(seed, difficulty * 100) tests = [] if task_id == 0: for _ in range(5): n = rng.randint(3, 12 * difficulty) arr = [rng.randint(-10, 10) for _ in range(n)] best = arr[0] cur = arr[0] for x in arr[1:]: cur = max(x, cur + x) best = max(best, cur) tests.append({"input": f"{n}\n{' '.join(map(str, arr))}", "expected": str(best)}) elif task_id == 1: for _ in range(5): n = rng.randint(3, 10 * difficulty) arr = [rng.randint(1, 20) for _ in range(n)] target = arr[0] + arr[rng.randint(1, n - 1)] found = None for i in range(n): for j in range(i + 1, n): if arr[i] + arr[j] == target: found = f"{i + 1} {j + 1}" break if found: break if found: tests.append({"input": f"{n} {target}\n{' '.join(map(str, arr))}", "expected": found}) elif task_id == 2: for _ in range(5): n = rng.randint(3, 8 * difficulty) arr = [rng.randint(1, 15) for _ in range(n)] dp = [1] * n for i in range(1, n): for j in range(i): if arr[j] < arr[i]: dp[i] = max(dp[i], dp[j] + 1) tests.append({"input": f"{n}\n{' '.join(map(str, arr))}", "expected": str(max(dp))}) return tests def _gen_graph_tests(task_id: int, difficulty: int, seed: int | None = None) -> List[Dict]: import heapq rng = _make_rng(seed, 99 + difficulty * 100) tests = [] if task_id == 0: for _ in range(4): n = rng.randint(3, 5 * difficulty) edges = [] path = list(range(1, n + 1)) for i in range(len(path) - 1): edges.append((path[i], path[i + 1], rng.randint(1, 10))) for _ in range(n): u = rng.randint(1, n) v = rng.randint(1, n) if u != v: edges.append((u, v, rng.randint(1, 10))) adj = [[] for _ in range(n + 1)] for u, v, w in edges: adj[u].append((v, w)) adj[v].append((u, w)) dist = [float("inf")] * (n + 1) dist[1] = 0 pq = [(0, 1)] while pq: d, u = heapq.heappop(pq) if d > dist[u]: continue for v, w in adj[u]: nd = dist[u] + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (dist[v], v)) edge_lines = "\n".join(f"{u} {v} {w}" for u, v, w in edges) tests.append({ "input": f"{n} {len(edges)}\n{edge_lines}", "expected": str(dist[n]) if dist[n] != float("inf") else "-1", }) elif task_id == 1: for _ in range(4): n = rng.randint(3, 6 * difficulty) edges = [] side_a = list(range(1, n // 2 + 1)) side_b = list(range(n // 2 + 1, n + 1)) for a in side_a: if side_b: edges.append((a, rng.choice(side_b))) color = [-1] * (n + 1) adj = [[] for _ in range(n + 1)] for u, v in edges: adj[u].append(v) adj[v].append(u) from collections import deque is_bip = True for start in range(1, n + 1): if color[start] != -1: continue color[start] = 0 queue = deque([start]) while queue: node = queue.popleft() for nxt in adj[node]: if color[nxt] == -1: color[nxt] = 1 - color[node] queue.append(nxt) elif color[nxt] == color[node]: is_bip = False edge_lines = "\n".join(f"{u} {v}" for u, v in edges) tests.append({ "input": f"{n} {len(edges)}\n{edge_lines}", "expected": "YES" if is_bip else "NO", }) elif task_id == 2: for _ in range(4): n = rng.randint(4, 8 * difficulty) edges = [] for _ in range(n // 2): u = rng.randint(1, n) v = rng.randint(1, n) if u != v: edges.append((u, v)) parent = list(range(n + 1)) def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x for u, v in edges: pu, pv = find(u), find(v) if pu != pv: parent[pu] = pv edge_lines = "\n".join(f"{u} {v}" for u, v in edges) if edges else "" tests.append({ "input": f"{n} {len(edges)}\n{edge_lines}".strip(), "expected": str(len(set(find(i) for i in range(1, n + 1)))), }) return tests def _gen_dp_tests(task_id: int, difficulty: int, seed: int | None = None) -> List[Dict]: rng = _make_rng(seed, 77 + difficulty * 100) tests = [] if task_id == 0: coins = [1, 2, 5, 10] for _ in range(5): amount = rng.randint(5, 18 * difficulty) dp = [float("inf")] * (amount + 1) dp[0] = 0 for idx in range(1, amount + 1): for coin in coins: if coin <= idx and dp[idx - coin] + 1 < dp[idx]: dp[idx] = dp[idx - coin] + 1 tests.append({ "input": f"{amount}\n{' '.join(map(str, coins))}", "expected": str(dp[amount]) if dp[amount] != float("inf") else "-1", }) elif task_id == 1: for _ in range(5): n = rng.randint(2, 10 * difficulty) a, b = 1, 1 for _ in range(2, n + 1): a, b = b, a + b tests.append({"input": str(n), "expected": str(b)}) elif task_id == 2: for _ in range(5): la = rng.randint(3, 5 * difficulty) lb = rng.randint(3, 5 * difficulty) chars = "abcde" a = "".join(rng.choice(chars) for _ in range(la)) b = "".join(rng.choice(chars) for _ in range(lb)) dp = [[0] * (lb + 1) for _ in range(la + 1)] for i in range(1, la + 1): for j in range(1, lb + 1): if a[i - 1] == b[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) tests.append({"input": f"{a}\n{b}", "expected": str(dp[la][lb])}) return tests ARCHETYPES = { "array": { "name": "Array Algorithms", "tasks": [ "maximum subarray sum", "two sum (find indices)", "longest increasing subsequence", ], "optimal_complexity": ["O(N)", "O(N)", "O(N log N)"], "descriptions": [ ( "Given an array of {n} integers (possibly negative), " "find the maximum sum of any contiguous subarray.\n" "Input: First line n, second line n space-separated integers.\n" "Output: Single integer - the maximum subarray sum." ), ( "Given an array of {n} positive integers and a target k, " "find two distinct indices i < j such that arr[i] + arr[j] == k.\n" "Input: First line: n k. Second line: n integers.\n" "Output: Two 1-indexed integers i j. Guaranteed a solution exists." ), ( "Given an array of {n} integers, find the length of the " "longest strictly increasing subsequence.\n" "Input: First line n, second line n integers.\n" "Output: Single integer." ), ], "generator": _gen_array_tests, }, "graph": { "name": "Graph Traversal", "tasks": [ "shortest path node 1 to node n", "bipartite graph check", "number of connected components", ], "optimal_complexity": ["O((V+E) log V)", "O(V+E)", "O(V+E)"], "descriptions": [ ( "Given a weighted undirected graph with {n} nodes and {m} edges, " "find the shortest path distance from node 1 to node n.\n" "Input: First line: n m. Then m lines: u v w.\n" "Output: Shortest distance, or -1 if unreachable." ), ( "Given an undirected graph with {n} nodes and {m} edges, " "determine if it is bipartite.\n" "Input: First line: n m. Then m lines: u v.\n" "Output: YES or NO." ), ( "Given an undirected graph with {n} nodes and {m} edges, " "count the number of connected components.\n" "Input: First line: n m. Then m lines: u v.\n" "Output: Single integer." ), ], "generator": _gen_graph_tests, }, "dp": { "name": "Dynamic Programming", "tasks": [ "minimum coins to make amount", "ways to climb n stairs", "longest common subsequence", ], "optimal_complexity": ["O(N*C)", "O(N)", "O(N*M)"], "descriptions": [ ( "Given a target amount and coin denominations [1,2,5,10], " "find the minimum number of coins to make the amount.\n" "Input: First line: amount. Second line: coin denominations.\n" "Output: Minimum coins, or -1 if impossible." ), ( "You can climb 1 or 2 steps at a time. " "In how many distinct ways can you climb n stairs?\n" "Input: Single integer n.\n" "Output: Number of ways." ), ( "Given two strings A and B, find the length of their " "longest common subsequence.\n" "Input: Two lines, each a string of lowercase letters.\n" "Output: Single integer." ), ], "generator": _gen_dp_tests, }, } def _compute_expected(archetype: str, task_id: int, payload: dict[str, Any]) -> str: if archetype == "array" and task_id == 0: arr = payload["arr"] best = cur = arr[0] for value in arr[1:]: cur = max(value, cur + value) best = max(best, cur) return str(best) if archetype == "array" and task_id == 1: arr = payload["arr"] target = payload["target"] for i in range(len(arr)): for j in range(i + 1, len(arr)): if arr[i] + arr[j] == target: return f"{i + 1} {j + 1}" return "1 1" if archetype == "array" and task_id == 2: arr = payload["arr"] tails: list[int] = [] for value in arr: lo, hi = 0, len(tails) while lo < hi: mid = (lo + hi) // 2 if tails[mid] < value: lo = mid + 1 else: hi = mid if lo == len(tails): tails.append(value) else: tails[lo] = value return str(len(tails)) if archetype == "graph" and task_id == 0: import heapq n = payload["n"] edges = payload["edges"] adj = [[] for _ in range(n + 1)] for u, v, w in edges: adj[u].append((v, w)) adj[v].append((u, w)) dist = [float("inf")] * (n + 1) dist[1] = 0 pq = [(0, 1)] while pq: d, u = heapq.heappop(pq) if d > dist[u]: continue for v, w in adj[u]: nd = d + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (nd, v)) return str(dist[n]) if dist[n] != float("inf") else "-1" if archetype == "graph" and task_id == 1: from collections import deque n = payload["n"] edges = payload["edges"] adj = [[] for _ in range(n + 1)] for u, v in edges: adj[u].append(v) adj[v].append(u) color = [-1] * (n + 1) for start in range(1, n + 1): if color[start] != -1: continue color[start] = 0 queue = deque([start]) while queue: node = queue.popleft() for nxt in adj[node]: if color[nxt] == -1: color[nxt] = 1 - color[node] queue.append(nxt) elif color[nxt] == color[node]: return "NO" return "YES" if archetype == "graph" and task_id == 2: n = payload["n"] parent = list(range(n + 1)) def find(x: int) -> int: while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x for u, v in payload["edges"]: pu, pv = find(u), find(v) if pu != pv: parent[pu] = pv return str(len({find(i) for i in range(1, n + 1)})) if archetype == "dp" and task_id == 0: amount = payload["amount"] coins = payload["coins"] dp = [float("inf")] * (amount + 1) dp[0] = 0 for idx in range(1, amount + 1): for coin in coins: if coin <= idx and dp[idx - coin] + 1 < dp[idx]: dp[idx] = dp[idx - coin] + 1 return str(dp[amount]) if dp[amount] != float("inf") else "-1" if archetype == "dp" and task_id == 1: n = payload["n"] if n <= 1: return "1" a, b = 1, 1 for _ in range(2, n + 1): a, b = b, a + b return str(b) if archetype == "dp" and task_id == 2: a = payload["a"] b = payload["b"] dp = [[0] * (len(b) + 1) for _ in range(len(a) + 1)] for i in range(1, len(a) + 1): for j in range(1, len(b) + 1): if a[i - 1] == b[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return str(dp[len(a)][len(b)]) raise ValueError(f"Unsupported archetype/task combo: {archetype}/{task_id}") def _format_case(archetype: str, task_id: int, payload: dict[str, Any]) -> Dict[str, str]: if archetype == "array" and task_id == 0: arr = payload["arr"] return {"input": f"{len(arr)}\n{' '.join(map(str, arr))}", "expected": _compute_expected(archetype, task_id, payload)} if archetype == "array" and task_id == 1: arr = payload["arr"] return {"input": f"{len(arr)} {payload['target']}\n{' '.join(map(str, arr))}", "expected": _compute_expected(archetype, task_id, payload)} if archetype == "array" and task_id == 2: arr = payload["arr"] return {"input": f"{len(arr)}\n{' '.join(map(str, arr))}", "expected": _compute_expected(archetype, task_id, payload)} if archetype == "graph" and task_id == 0: lines = "\n".join(f"{u} {v} {w}" for u, v, w in payload["edges"]) return {"input": f"{payload['n']} {len(payload['edges'])}\n{lines}".strip(), "expected": _compute_expected(archetype, task_id, payload)} if archetype == "graph" and task_id in {1, 2}: lines = "\n".join(f"{u} {v}" for u, v in payload["edges"]) return {"input": f"{payload['n']} {len(payload['edges'])}\n{lines}".strip(), "expected": _compute_expected(archetype, task_id, payload)} if archetype == "dp" and task_id == 0: return {"input": f"{payload['amount']}\n{' '.join(map(str, payload['coins']))}", "expected": _compute_expected(archetype, task_id, payload)} if archetype == "dp" and task_id == 1: return {"input": str(payload["n"]), "expected": _compute_expected(archetype, task_id, payload)} if archetype == "dp" and task_id == 2: return {"input": f"{payload['a']}\n{payload['b']}", "expected": _compute_expected(archetype, task_id, payload)} raise ValueError(f"Unsupported archetype/task combo: {archetype}/{task_id}") def _build_adversarial_case(archetype: str, task_id: int, difficulty: int, rng: random.Random) -> Dict[str, str]: if archetype == "array" and task_id == 0: arr = [-(rng.randint(1, 4)) for _ in range(max(6, difficulty * 12))] arr[rng.randrange(len(arr))] = difficulty * 9 return _format_case(archetype, task_id, {"arr": arr}) if archetype == "array" and task_id == 1: arr = [difficulty * 3] * max(4, difficulty * 6) arr[-1] = difficulty * 5 return _format_case(archetype, task_id, {"arr": arr, "target": arr[0] + arr[1]}) if archetype == "array" and task_id == 2: arr = [5] * max(5, difficulty * 7) arr[-1] = 6 return _format_case(archetype, task_id, {"arr": arr}) if archetype == "graph" and task_id == 0: n = max(4, difficulty * 4) edges = [(i, i + 1, 20) for i in range(1, n)] + [(i, i + 1, 1) for i in range(1, n)] return _format_case(archetype, task_id, {"n": n, "edges": edges}) if archetype == "graph" and task_id == 1: n = max(3, difficulty * 4 + 1) edges = [(1, 2), (2, 3), (3, 1)] + [(3, node) for node in range(4, n + 1)] return _format_case(archetype, task_id, {"n": n, "edges": edges}) if archetype == "graph" and task_id == 2: n = max(5, difficulty * 5) edges = [(1, 2), (2, 3), (4, 5)] return _format_case(archetype, task_id, {"n": n, "edges": edges}) if archetype == "dp" and task_id == 0: return _format_case(archetype, task_id, {"amount": difficulty * 11, "coins": [1, 2, 5, 10]}) if archetype == "dp" and task_id == 1: return _format_case(archetype, task_id, {"n": difficulty * 10}) if archetype == "dp" and task_id == 2: alphabet = "abcde" a = "".join(rng.choice(alphabet) for _ in range(max(4, difficulty * 4))) return _format_case(archetype, task_id, {"a": a, "b": a[::-1]}) raise ValueError(f"Unsupported archetype/task combo: {archetype}/{task_id}") def generate_test_cases(archetype: str, task_id: int, difficulty: int, seed: int | None = None) -> List[Dict]: """Generate seeded cases plus one adversarial hidden case.""" arch = ARCHETYPES[archetype] tests = list(arch["generator"](task_id, difficulty, seed)) tests.append(_build_adversarial_case(archetype, task_id, difficulty, _make_rng(seed, 1000 + task_id * 17))) return tests def get_problem_description(archetype: str, task_id: int, difficulty: int) -> str: """Return formatted problem statement.""" arch = ARCHETYPES[archetype] desc = arch["descriptions"][task_id] n_vals = {1: "small (n <= 100)", 2: "medium (n <= 1000)", 3: "large (n <= 100000)"} return desc.replace("{n}", n_vals.get(difficulty, "n")).replace("{m}", "varies") def build_problem(archetype: str, task_id: int, difficulty: int, seed: int | None = None) -> Dict[str, Any]: """Build a complete problem dict ready for the environment.""" arch = ARCHETYPES[archetype] test_cases = generate_test_cases(archetype, task_id, difficulty, seed=seed) public_test_count = min(2, len(test_cases)) public_test_cases = test_cases[:public_test_count] hidden_test_cases = test_cases[public_test_count:] or test_cases[-1:] desc = get_problem_description(archetype, task_id, difficulty) return { "archetype": archetype, "task_id": task_id, "difficulty": difficulty, "title": f"{arch['name']} - {arch['tasks'][task_id]}", "description": desc, "input_format": desc, "output_format": "See description above.", "constraints": f"Difficulty tier {difficulty}/3", "test_cases": test_cases, "public_test_cases": public_test_cases, "hidden_test_cases": hidden_test_cases, "optimal_complexity": arch["optimal_complexity"][task_id], "variant_seed": seed, }