viola

Finished
11def solve(puzzle: str) -> dict[str, int] | None:
2 pass
2 """
3 Solve an alphametics puzzle.
4
5 Args:
6 puzzle: A string representing the alphametics puzzle (e.g., "SEND + MORE == MONEY")
7
8 Returns:
9 A dictionary mapping letters to digits that solves the puzzle, or None if no solution exists.
10
11 Example:
12 >>> solve("SEND + MORE == MONEY")
13 {'S': 9, 'E': 5, 'N': 6, 'D': 7, 'M': 1, 'O': 0, 'R': 8, 'Y': 2}
14 """
15 # Edge Case: Empty or None puzzle
16 if not puzzle:
17 return None
18
19 # Parse the puzzle
20 parts = puzzle.replace("==", "=").replace(" ", "").split("=")
21
22 # Edge Case: Invalid puzzle format (no equals sign or too many parts)
23 if len(parts) != 2:
24 return None
25
26 left_side, right_side = parts[0], parts[1]
27
28 # Split the left side by '+' to get addends
29 addends = left_side.split("+")
30
31 # Check if right side is a numeric literal
32 is_numeric_result = right_side.isdigit()
33
34 # Edge Case: No addends on the left side
35 if not addends or any(not addend for addend in addends):
36 return None
37
38 # Edge Case: Empty right side
39 if not right_side:
40 return None
41
42 # Extract all unique letters
43 letters = set()
44 for part in addends:
45 letters.update(part)
46
47 # Only add letters from right_side if it's not purely numeric
48 is_numeric_result = right_side.isdigit()
49 if not is_numeric_result:
50 letters.update(right_side)
51
52 # Edge Case: No letters in puzzle
53 if not letters:
54 return None
55
56 # Extract first letters (can't be zero)
57 first_letters = {word[0] for word in addends}
58 # Only add first letter from right_side if it's not purely numeric
59 if not is_numeric_result:
60 first_letters.add(right_side[0])
61
62 # Edge Case: Any word is longer than 10 digits (impossible since we only have 10 digits)
63 if any(len(word) > 10 for word in addends):
64 return None
65 if not is_numeric_result and len(right_side) > 10:
66 return None
67
68 letters = list(letters)
69
70 # Edge Case: More than 10 unique letters (impossible to assign unique digits)
71 if len(letters) > 10:
72 return None
73
74 def word_to_number(word, mapping):
75 """Convert a word to a number using the given mapping."""
76 return int(''.join(str(mapping[char]) for char in word))
77
78 def is_valid_solution(mapping):
79 """Check if the current mapping is a valid solution."""
80 # Check if any first letter is mapped to 0
81 for letter in first_letters:
82 if mapping[letter] == 0:
83 return False
84
85 # Calculate the sum of addends
86 total = 0
87 for addend in addends:
88 total += word_to_number(addend, mapping)
89
90 # Check if it equals the right side
91 if is_numeric_result:
92 return total == int(right_side)
93 else:
94 return total == word_to_number(right_side, mapping)
95
96 def backtrack(index, used_digits, mapping):
97 """Backtracking function to find a valid digit assignment."""
98 # Base case: all letters have been assigned
99 if index == len(letters):
100 if is_valid_solution(mapping):
101 return mapping.copy()
102 return None
103
104 letter = letters[index]
105
106 # Try each digit from 0 to 9
107 for digit in range(10):
108 # Skip if digit is already used
109 if digit in used_digits:
110 continue
111
112 # Assign the digit to the letter
113 mapping[letter] = digit
114 used_digits.add(digit)
115
116 # Recurse to the next letter
117 result = backtrack(index + 1, used_digits, mapping)
118 if result is not None:
119 return result
120
121 # Backtrack: remove the assignment
122 used_digits.remove(digit)
123 del mapping[letter]
124
125 return None
126
127 # Start backtracking
128 solution = backtrack(0, set(), {})
129 return solution
130
131# Handled Edge Cases: Empty/None puzzle, invalid format, no addends, empty right side, no letters, words longer than 10 digits, more than 10 unique letters
Test NameStatus
test_leading_zero_solution_is_invalid
Pass
test_puzzle_with_eight_letters
Pass
test_puzzle_with_four_letters
Pass
test_puzzle_with_seven_letters
Pass
test_puzzle_with_six_letters
Pass
test_puzzle_with_ten_letters
Pass
test_puzzle_with_ten_letters_and_199_addends
Pass
test_puzzle_with_three_letters
Pass
test_puzzle_with_two_digits_final_carry
Pass
test_solution_must_have_unique_value_for_each_letter
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.