Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions project_enigma/clarifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The Interrogation — clarifications.md
Welcome back from the Himalayas! A few questions so the production decoder
behaves correctly on real traffic rather than just the sample set.
## 1. Output semantics: is the per-cycle sum intended?
The tests only pass if each output integer is the sum of the values in a
cycle (e.g. abbcc -> [2, 6] where 6 = c + c). Can you confirm the output
should be cycle sums, and not the raw list of individual values? If individual
values are ever needed, we'd want a separate flag or function.
## 2. The underscore _ — value vs. terminator
We inferred two roles for _:
- as a value, it counts as 0 in the cycle sum;
- as a count, it ends the whole string and emits a single 0.
Is that intentional? Specifically, should _ad really discard the trailing ad
(current behaviour, giving [0]), or should decoding resume after the _? This is
the biggest production risk: silently dropping data.
## 3. Spaces: separator or value?
We treat a space as a separator between independent strings (_ _ -> [0, 0]).
Is that correct, or should a space be an ordinary 0 value within one string?
## 4. Distinguishing 26 from a "continue"
Because z always means "+26 and continue", the only way to encode a value of
exactly 26 is z followed by a zero-terminator such as z_ (= 26). Is z_ the
canonical encoding for 26? And can a trailing z with no terminator legitimately
appear?
## 5. Malformed / truncated cycles
If a cycle declares a count of 5 but the string ends after 2 values, should we
(a) emit the partial sum (current behaviour), (b) emit 0, or (c) raise an error?
## 6. Character set beyond a-z, _, and space
What is the full set of legal characters? Should uppercase, digits, or other
symbols be (a) treated as 0 like _, (b) rejected with an error, or (c) ignored?
## Scenarios that look contradictory in the current test set
- _ad -> [0] (data after _ dropped) vs. aab___ -> [1, 0, 0] (_ as a value kept).
- __ -> [0] vs. _ _ -> [0, 0] (only consistent if space is a separator).
For production we'd recommend making these rules explicit in the spec and,
ideally, rejecting truly malformed input loudly rather than silently producing 0.
71 changes: 71 additions & 0 deletions project_enigma/decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
def get_char_value(char: str) -> int:
"""Convert a character to its value: a=1 ... z=26. Any other char (like '_') = 0."""
if "a" <= char <= "z":
return ord(char) - ord("a") + 1
return 0

def read_number(encoded_string: str, index: int):
"""Read one full number starting at index.
'z' means +26 and keep going; the first non-'z' char ends the number and adds its value.
Returns (value, next_index)."""
total = 0
while index < len(encoded_string) and encoded_string[index] == "z":
total += 26
index += 1
if index < len(encoded_string):
total += get_char_value(encoded_string[index])
index += 1
return total, index

def decode_measurements(encoded_string: str) -> list[int]:
"""Main function: a space separates independent strings,
so we decode each segment on its own and combine the results."""
results = []
for segment in encoded_string.split(" "):
results.extend(decode_segment(segment))
return results

def decode_segment(encoded_string: str) -> list[int]:
results = []
index = 0
while index < len(encoded_string):
count, index = read_number(encoded_string, index)
if count == 0: # '_' where a count is expected = terminated/corrupt string
results.append(0)
break # stop reading this segment
total = 0
for _ in range(count):
if index >= len(encoded_string):
break
value, index = read_number(encoded_string, index)
total += value
results.append(total)
return results

if __name__ == "__main__":
cases = [
("aa", [1]),
("abbcc", [2, 6]),
("dz_a_aazzaaa", [28, 53, 1]),
("a_", [0]),
("abcdabcdab", [2, 7, 7]),
("abcdabcdab_", [2, 7, 7, 0]),
("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]),
("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]),
("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]),
("_", [0]),
("_ad", [0]),
("a_", [0]),
("_zzzb", [0]),
("__", [0]),
("", []),
("_ _", [0, 0]),
("aab___", [1, 0, 0]),
]
passed = 0
for inp, expected in cases:
got = decode_measurements(inp)
ok = got == expected
passed += ok
print(f"{'PASS' if ok else 'FAIL'} decode({inp!r}) = {got} expected {expected}")
print(f"\n{passed}/{len(cases)} passed")
104 changes: 70 additions & 34 deletions project_enigma/decoding_utils.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,76 @@
def decode_measurements(encoded_string: str) -> list[int]:
"""This function decodes an encoded string into a list of integers.
RULE 1: A generic logic should be implemented without handling edge cases using if statements for specific inputs.
RULE 2: The generic logic should handle all inputs and generate the expected outputs.
def get_char_value(char: str) -> int:
"""Convert a character to its value: a=1 ... z=26. Any other char (like '_') = 0."""
if "a" <= char <= "z":
return ord(char) - ord("a") + 1
return 0

Args:
encoded_string (str): The encoded string to decode.
def read_number(encoded_string: str, index: int):
"""Read one full number starting at index.
'z' means +26 and keep going; the first non-'z' char ends the number and adds its value.
Returns (value, next_index)."""
total = 0
while index < len(encoded_string) and encoded_string[index] == "z":
total += 26
index += 1
if index < len(encoded_string):
total += get_char_value(encoded_string[index])
index += 1
return total, index

Returns:
list[int]: The list of decoded integers.
"""
pass # Remove this pass and place your logic here to decode the string into a list of integers based on the specified encoding rules.
return [] # Placeholder return statement; replace with actual decoding logic.
def decode_measurements(encoded_string: str) -> list[int]:
"""Main function: a space separates independent strings,
so we decode each segment on its own and combine the results."""
results = []
for segment in encoded_string.split(" "):
results.extend(decode_segment(segment))
return results

def decode_segment(encoded_string: str) -> list[int]:
results = []
index = 0
while index < len(encoded_string):
count, index = read_number(encoded_string, index)
if count == 0: # '_' where a count is expected = terminated/corrupt string
results.append(0)
break # stop reading this segment
total = 0
for _ in range(count):
if index >= len(encoded_string):
break
value, index = read_number(encoded_string, index)
total += value
results.append(total)
return results

if __name__ == "__main__":
test_cases = [
("aa", [1]),
("abbcc", [2, 6]),
("dz_a_aazzaaa", [28, 53, 1]),
("a_", [0]),
("abcdabcdab", [2, 7, 7]),
("abcdabcdab_", [2, 7, 7, 0]),
("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]),
("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]),
("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]),
("_", [0]),
("_ad", [0]),
("a_", [0]),
("_zzzb", [0]),
("__", [0]),
("", []),
("_ _", [0, 0]),
("aab___", [1, 0, 0]),
]
cases = [
("aa", [1]),
("abbcc", [2, 6]),
("dz_a_aazzaaa", [28, 53, 1]),
("a_", [0]),
("abcdabcdab", [2, 7, 7]),
("abcdabcdab_", [2, 7, 7, 0]),
("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]),
("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]),
("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]),
("_", [0]),
("_ad", [0]),
("a_", [0]),
("_zzzb", [0]),
("__", [0]),
("", []),
("_ _", [0, 0]),
("aab___", [1, 0, 0]),
]
passed = 0
for inp, expected in cases:
got = decode_measurements(inp)
ok = got == expected
passed += ok
print(f"{'PASS' if ok else 'FAIL'} decode({inp!r}) = {got} expected {expected}")
print(f"\n{passed}/{len(cases)} passed")





for encoded, expected in test_cases:
result = decode_measurements(encoded)
status = "PASS" if result == expected else "FAIL"
print(f"{status}: decode_measurements({encoded!r}) = {result} (expected {expected})")