armor

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
23 if len(parts) != 2:
24 return None
25
26 left_side, right_side = parts[0], parts[1]
27
28 # Extract terms (addends) from the left side
29 terms = left_side.replace("+", " ").split()
30
31 # Edge Case: No terms on left side
32 if not terms:
33 return None
34
35 # Collect all unique letters
36 letters = set()
37 for term in terms + [right_side]:
38 letters.update(term)
39
40 # Edge Case: No letters in puzzle
41 if not letters:
42 return None
43
44 # Identify first letters (can't be zero)
45 first_letters = {term[0] for term in terms + [right_side]}
46
47 # Convert letters to a list for consistent ordering
48 letters = list(letters)
49
50 # Edge Case: More than 10 unique letters (impossible to assign unique digits)
51 if len(letters) > 10:
52 return None
53
54 # Helper function to convert a word to a number based on a letter-to-digit mapping
55 def word_to_number(word, mapping):
56 return int(''.join(str(mapping[letter]) for letter in word))
57
58 # Helper function to check if a mapping is valid
59 def is_valid_mapping(mapping):
60 # Check if any first letter is mapped to 0
61 for letter in first_letters:
62 if mapping[letter] == 0:
63 return False
64
65 # Calculate the sum of the terms
66 total = 0
67 for term in terms:
68 total += word_to_number(term, mapping)
69
70 # Check if it equals the right side
71 return total == word_to_number(right_side, mapping)
72
73 # Backtracking function to try digit assignments
74 def backtrack(index, used_digits, mapping):
75 # Base case: all letters have been assigned
76 if index == len(letters):
77 if is_valid_mapping(mapping):
78 return mapping.copy()
79 return None
80
81 letter = letters[index]
82
83 # Try each digit from 0 to 9
84 for digit in range(10):
85 # Skip if digit is already used
86 if digit in used_digits:
87 continue
88
89 # Assign the digit to the letter
90 mapping[letter] = digit
91 used_digits.add(digit)
92
93 # Recurse to the next letter
94 result = backtrack(index + 1, used_digits, mapping)
95 if result is not None:
96 return result
97
98 # Backtrack: remove the assignment
99 used_digits.remove(digit)
100 del mapping[letter]
101
102 return None
103
104 # Start the backtracking search
105 solution = backtrack(0, set(), {})
106 return solution
107
108# Handled Edge Cases: Empty or None puzzle, Invalid puzzle format, No terms on left 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.