parent
2044e43b34
commit
050f7577ec
@ -0,0 +1,104 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct Password(String);
|
||||||
|
impl Password {
|
||||||
|
pub fn new(s: &str) -> Self {
|
||||||
|
Password(s.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.has_increasing_straight() && !self.has_confusing_chars() &&
|
||||||
|
self.has_different_nonoverlapping_pairs()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&self) -> Self {
|
||||||
|
StringIncrementor::new(&self.0).map(|x| Self::new(&x)).find(|ref x| {
|
||||||
|
x.is_valid()
|
||||||
|
}).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_increasing_straight(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
.chars()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.windows(3)
|
||||||
|
.any(|w| w.windows(2).all(|x| (x[1] as i8) - (x[0] as i8) == 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_confusing_chars(&self) -> bool {
|
||||||
|
self.0.chars().any(|c| ['i', 'l', 'o'].contains(&c))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_different_nonoverlapping_pairs(&self) -> bool {
|
||||||
|
let slice = self.0.chars().collect::<Vec<_>>();
|
||||||
|
slice.windows(2).filter_map(|x| {
|
||||||
|
if x[0] == x[1] { Some(x[0]) } else { None }
|
||||||
|
}).collect::<HashSet<_>>().len() > 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Password> for String {
|
||||||
|
fn from(p: Password) -> String {
|
||||||
|
p.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringIncrementor {
|
||||||
|
s: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringIncrementor {
|
||||||
|
fn new(s: &str) -> Self {
|
||||||
|
StringIncrementor{s: s.into()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for StringIncrementor {
|
||||||
|
type Item = String;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<String> {
|
||||||
|
self.s = self.s.chars()
|
||||||
|
.rev()
|
||||||
|
.scan(true, |carry, x| {
|
||||||
|
if !*carry {
|
||||||
|
return Some(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if x == 'z' {
|
||||||
|
Some('a')
|
||||||
|
} else {
|
||||||
|
*carry = false;
|
||||||
|
Some((x as u8 + 1) as char)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.iter()
|
||||||
|
.map(|&x| x)
|
||||||
|
.rev()
|
||||||
|
.collect();
|
||||||
|
Some(self.s.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_incrementor() {
|
||||||
|
assert_eq!(StringIncrementor::new("abcdefgh").next(), Some("abcdefgi".into()));
|
||||||
|
assert_eq!(StringIncrementor::new("az").next(), Some("ba".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_password() {
|
||||||
|
assert!(!Password::new("hijklmmn").is_valid());
|
||||||
|
assert!(!Password::new("abbceffg").is_valid());
|
||||||
|
assert!(!Password::new("abbcegjk").is_valid());
|
||||||
|
assert!(!Password::new("ghjaaaaa").is_valid());
|
||||||
|
|
||||||
|
assert_eq!(Password::new("abcdefgh").next(), Password::new("abcdffaa"));
|
||||||
|
assert_eq!(Password::new("ghijklmn").next(), Password::new("ghjaabcc"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue