chris

Finished
1def solve(puzzle: str) -> dict[str, int] | None:
2 pass
1from itertools import permutations
2from typing import Dict, Optional
3
4def solve(puzzle: str) -> Optional[Dict[str, int]]:
5 """
6 Solve an alphametics puzzle.
7
8 Args:
9 puzzle: A string representing the alphametics puzzle (e.g., "SEND + MORE == MONEY")
10
11 Returns:
12 A dictionary mapping letters to digits that solves the puzzle, or None if no solution exists.
13
14 Example:
15 >>> solve("SEND + MORE == MONEY")
16 {'S': 9, 'E': 5, 'N': 6, 'D': 7, 'M': 1, 'O': 0, 'R': 8, 'Y': 2}
17 """
18 # Parse the puzzle
19 # Edge Case: Empty or None puzzle
20 if not puzzle:
21 return None
22
23 # Remove spaces and split by '=='
24 parts = puzzle.replace(" ", "").split("==")
25
26 # Edge Case: Invalid puzzle format (no '==' or incorrect number of parts)
27 if len(parts) != 2:
28 return None
29
30 left_side, right_side = parts[0], parts[1]
31
32 # Edge Case: Empty right side
33 if not right_side:
34 return None
35
36 # Determine the operation type and parse left side accordingly
37 if "*" in left_side:
38 # Multiplication operation
39 operands = left_side.split("*")
40 operation = "multiply"
41 elif "-" in left_side:
42 # Subtraction operation
43 operands = left_side.split("-")
44 operation = "subtract"
45 else:
46 # Addition operation (default)
47 operands = left_side.split("+")
48 operation = "add"
49
50 # Edge Case: No operands on the left side
51 if not operands or not all(operands):
52 return None
53
54 # Extract all unique letters
55 letters = set()
56 for part in operands + [right_side]:
57 letters.update(part)
58
59 # Edge Case: No letters in puzzle
60 if not letters:
61 return None
62
63 # Extract first letters (can't be zero)
64 first_letters = {word[0] for word in operands + [right_side]}
65
66 # Edge Case: More than 10 unique letters (impossible to assign unique digits)
67 if len(letters) > 10:
68 return None
69
70 # Try all digit permutations for the letters
71 digits = range(10)
72 for perm in permutations(digits, len(letters)):
73 mapping = dict(zip(letters, perm))
74
75 # Edge Case: Leading zero in any word
76 if any(mapping[first_letter] == 0 for first_letter in first_letters):
77 continue
78
79 # Convert words to numbers
80 try:
81 operand_values = []
82 for operand in operands:
83 value = 0
84 for char in operand:
85 value = value * 10 + mapping[char]
86 operand_values.append(value)
87
88 right_value = 0
89 for char in right_side:
90 right_value = right_value * 10 + mapping[char]
91
92 # Check if the equation is correct based on operation type
93 if operation == "add":
94 if sum(operand_values) == right_value:
95 return mapping
96 elif operation == "subtract":
97 # For subtraction: first operand minus rest of operands
98 if len(operand_values) >= 2:
99 result = operand_values[0]
100 for val in operand_values[1:]:
101 result -= val
102 if result == right_value:
103 return mapping
104 elif operation == "multiply":
105 # For multiplication: first operand times rest of operands
106 if len(operand_values) >= 2:
107 result = operand_values[0]
108 for val in operand_values[1:]:
109 result *= val
110 if result == right_value:
111 return mapping
112 except KeyError:
113 # Edge Case: Mapping is incomplete (shouldn't happen with correct permutation logic)
114 continue
115
116 # Edge Case: No valid solution found
117 return None
118
119# Handled Edge Cases: Empty or None puzzle, Invalid puzzle format, No addends, Empty right side, No letters, More than 10 unique letters, Leading zero in any word, No valid solution found
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.