Finished
11def solve(puzzle: str) -> dict[str, int] | None:
2 pass
2 """
3 Solve an alphametics puzzle.
4
5 Args:
6 puzzle (str): A string representing the alphametics puzzle (e.g., "SEND + MORE == MONEY")
7
8 Returns:
9 dict[str, int] | None: A dictionary mapping letters to digits that solves the puzzle,
10 or None if no solution exists.
11
12 Example:
13 >>> solve("SEND + MORE == MONEY")
14 {'S': 9, 'E': 5, 'N': 6, 'D': 7, 'M': 1, 'O': 0, 'R': 8, 'Y': 2}
15 """
16 # Edge Case: Empty or None puzzle
17 if not puzzle:
18 return None
19
20 # Parse the puzzle
21 # Check if using single equals (invalid format)
22 if " = " in puzzle and " == " not in puzzle:
23 return None
24 if "=" in puzzle and "==" not in puzzle:
25 return None
26 parts = puzzle.replace(" ", "").split("==")
27
28 # Edge Case: Invalid puzzle format
29 if len(parts) != 2:
30 return None
31
32 right_side = parts[1]
33 left_sides = parts[0].split("+")
34
35 # Edge Case: No terms on left side
36 if not left_sides or not all(left_sides):
37 return None
38
39 # Edge Case: Empty right side
40 if not right_side:
41 return None
42
43 # Get all unique letters
44 letters = set()
45 for part in left_sides + [right_side]:
46 letters.update(part)
47
48 # Edge Case: No letters in puzzle
49 if not letters:
50 return None
51
52 # Get leading letters (can't be zero)
53 leading_letters = {word[0] for word in left_sides + [right_side]}
54
55 # Convert to list for consistent ordering
56 letters = list(letters)
57
58 # Edge Case: More than 10 unique letters (impossible to solve)
59 if len(letters) > 10:
60 return None
61
62 def is_valid_solution(mapping):
63 """Check if the current mapping is a valid solution."""
64 # Check if any leading letter is mapped to 0
65 for letter in leading_letters:
66 if mapping[letter] == 0:
67 return False
68
69 # Convert words to numbers
70 left_values = []
71 for word in left_sides:
72 value = 0
73 for letter in word:
74 value = value * 10 + mapping[letter]
75 left_values.append(value)
76
77 right_value = 0
78 for letter in right_side:
79 right_value = right_value * 10 + mapping[letter]
80
81 # Check if the equation is satisfied
82 return sum(left_values) == right_value
83
84 def backtrack(index, mapping, used_digits):
85 """Backtracking function to find a valid solution."""
86 # Base case: all letters have been assigned
87 if index == len(letters):
88 if is_valid_solution(mapping):
89 return mapping.copy()
90 return None
91
92 # Try assigning each unused digit to the current letter
93 letter = letters[index]
94 for digit in range(10):
95 # Skip if digit is already used
96 if digit in used_digits:
97 continue
98
99 # Skip if this is a leading letter and digit is 0
100 if letter in leading_letters and digit == 0:
101 continue
102
103 # Assign the digit
104 mapping[letter] = digit
105 used_digits.add(digit)
106
107 # Recurse
108 result = backtrack(index + 1, mapping, used_digits)
109 if result is not None:
110 return result
111
112 # Backtrack
113 used_digits.remove(digit)
114
115 # If we get here, no valid assignment was found
116 return None
117
118 # Start the backtracking search
119 result = backtrack(0, {}, set())
120 return result
121
122# Handled Edge Cases: Empty/None puzzle, Invalid puzzle format, No terms on left side, Empty right side, No letters in puzzle, 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.