-
Notifications
You must be signed in to change notification settings - Fork 545
Add Alphametics exercise. Fixes #182 #225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
e2ac07f
ac73592
8ae01a5
b5eacce
8523029
ff83051
44171e5
d3a5435
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ | |
| "wordy", | ||
| "tournament", | ||
| "custom-set", | ||
| "alphametics", | ||
| "anagram", | ||
| "nucleotide-codons", | ||
| "robot-name", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [package] | ||
| name = "alphametics" | ||
| version = "0.0.0" | ||
|
|
||
| [dependencies] | ||
| itertools = "0.5" | ||
| permutohedron = "0.2" | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [package] | ||
| name = "alphametics" | ||
| version = "0.0.0" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // This is a brute-force solution, use `cargo test --release` for faster testing | ||
|
|
||
| extern crate itertools; | ||
| extern crate permutohedron; | ||
|
|
||
| use itertools::Itertools; | ||
| use permutohedron::Heap as Permutations; | ||
|
|
||
|
|
||
| use std::collections::HashMap; | ||
| use std::collections::HashSet; | ||
| use std::char; | ||
|
|
||
| fn test_equation(puzzle: &str, substitutions: &HashMap<char, u8>) -> bool { | ||
| // Create a new String with characters changed to numbers | ||
| let puzzle: String = puzzle.chars().map(|c| { | ||
| if let Some(&n) = substitutions.get(&c) { | ||
| // If the character is in the substitutions, get the number and | ||
| // convert it to a char | ||
| char::from_digit(n as u32,10).unwrap() | ||
|
||
| } else { | ||
| // Otherwise just copy over the character | ||
| c | ||
|
||
| } | ||
| }).collect(); | ||
|
|
||
| // Split the puzzle into left and right side | ||
| let equation: Vec<&str> = puzzle.split("==").collect(); | ||
|
|
||
| // Parse the number on the right side | ||
| let right = equation[1].trim().parse::<u32>().unwrap(); | ||
|
|
||
| // Sum the parts on the left side | ||
| let left: u32 = equation[0].split('+').map(str::trim).map(|n| n.parse::<u32>().unwrap()).sum(); | ||
|
|
||
| // Create a String with just the numbers and spaces | ||
| let just_numbers = puzzle.chars().filter(|c| c.is_digit(10) || c.is_whitespace()).collect::<String>(); | ||
| // Split this into the numbers and check every number's first character | ||
| let no_leading_zeroes = just_numbers.split_whitespace().all(|number| number.chars().next().unwrap() != '0'); | ||
|
|
||
| // Return true if left and right side is equal and the equation doesnt | ||
| // contain leading zeroes. | ||
| left == right && no_leading_zeroes | ||
| } | ||
|
|
||
|
|
||
| pub fn solve(puzzle: &str) -> Option<HashMap<char, u8>> { | ||
| // Get unique letters from the puzzle | ||
| let letters: HashSet<char> = puzzle.chars().filter(|&c| c.is_alphabetic() && c.is_uppercase()).collect(); | ||
| let letters: Vec<char> = letters.into_iter().collect(); | ||
|
|
||
| // All available numbers for substitution | ||
| let numbers: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; | ||
|
|
||
| // Iterate every combination with the length of unique letters in the puzzle | ||
| for combinations in numbers.iter().combinations(letters.len()) { | ||
| let mut c = combinations; | ||
| let permutations = Permutations::new(&mut c); | ||
| // Iterate every permutation of a letter combination | ||
| for p in permutations { | ||
| let substitution: HashMap<char, u8> = letters.iter().zip(p).map(|(&c, &n)| (c, n)).collect(); | ||
| if test_equation(puzzle, &substitution) { | ||
| // We found a good substitution | ||
| return Some(substitution); | ||
| } | ||
| } | ||
| } | ||
| // If we tested every combination and did not found a solution then return None | ||
| None | ||
| } | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| use std::collections::HashMap; | ||
|
|
||
| pub fn solve(puzzle: &str) -> Option<HashMap<char, u8>> { | ||
| unimplemented!() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| extern crate alphametics; | ||
| use std::collections::HashMap; | ||
|
|
||
| fn test_helper(puzzle: &str, solution: &[(char, u8)]) { | ||
|
||
| let answer = alphametics::solve(puzzle).unwrap(); | ||
| let solution: HashMap<char, u8> = solution.iter().cloned().collect(); | ||
| assert_eq!(answer, solution); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_with_three_letters() { | ||
| test_helper("I + BB == ILL", | ||
| &[('I', 1), ('B', 9), ('L', 0)]); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_must_have_unique_value_for_each_letter() { | ||
| let answer = alphametics::solve("A == B"); | ||
| assert_eq!(answer, None); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_leading_zero_solution_is_invalid() { | ||
| let answer = alphametics::solve("ACA + DD == BD"); | ||
| assert_eq!(answer, None); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_puzzle_with_four_letters() { | ||
| test_helper("AS + A == MOM", &[('A', 9), ('S', 2), ('M', 1), ('O', 0)]); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_puzzle_with_six_letters() { | ||
| test_helper("NO + NO + TOO == LATE", | ||
| &[('N', 7), ('O', 4), ('T', 9), ('L', 1), ('A', 0), ('E', 2)]); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_puzzle_with_seven_letters() { | ||
| test_helper("HE + SEES + THE == LIGHT", | ||
| &[('E', 4), ('G', 2), ('H', 5), ('I', 0), ('L', 1), ('S', 9), ('T', 7)]); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_puzzle_with_eight_letters() { | ||
| test_helper("SEND + MORE == MONEY", | ||
| &[('S', 9), ('E', 5), ('N', 6), ('D', 7), | ||
| ('M', 1), ('O', 0), ('R', 8), ('Y', 2)]); | ||
| } | ||
|
|
||
| #[test] | ||
| #[ignore] | ||
| fn test_puzzle_with_ten_letters() { | ||
| test_helper("AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE", | ||
| &[ ('A', 5), ('D', 3), ('E', 4), ('F', 7), ('G', 8), | ||
| ('N', 0), ('O', 2), ('R', 1), ('S', 6), ('T', 9)]); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newline after?