sub_solver/
lib.rs

1#[macro_use]
2extern crate lazy_static;
3
4use std::collections::{HashMap, HashSet};
5
6use input::clean_input;
7
8pub mod cache;
9pub mod cli;
10pub mod input;
11pub mod loading;
12pub mod solve;
13
14#[derive(Debug, Clone)]
15pub struct Word {
16    pub word: String,
17    pub candidates: HashSet<String>,
18    pub letter_map: HashMap<char, HashSet<char>>,
19}
20impl Word {
21    pub fn new(s: &str, candidates: &HashSet<String>) -> Self {
22        let mut letter_map = HashMap::new();
23
24        for word in candidates {
25            for (i, j) in s.chars().zip(word.chars()) {
26                letter_map.entry(i).or_insert(HashSet::new()).insert(j);
27            }
28        }
29
30        Word {
31            word: s.to_string(),
32            candidates: candidates.clone(),
33            letter_map,
34        }
35    }
36}
37
38/// Convert word to uppercase, and substitute all characters to be in alphabetical order.
39/// This makes words equivalent if they have the same charactaristics
40///
41/// ```rust
42/// use sub_solver::normalize;
43///
44/// assert_eq!(normalize("example"), "ABCDEFA");  // "example" has 2 'a's at the start and end
45/// assert_eq!(normalize("example"), normalize("squares"));  // "example" and "squares" have the same repeated character positions
46/// assert_eq!(normalize("testing"), "ABCADEF");  // "testing" does not have repeated characters at the start and end
47/// ```
48pub fn normalize(s: &str) -> String {
49    let mut result = s.chars().collect::<Vec<char>>();
50    let mut replacement = b'A';
51
52    for i in 0..result.len() {
53        if result[i].is_ascii_uppercase() {
54            continue;
55        }
56
57        // Replace all instances of the character with the replacement
58        result = result
59            .iter()
60            .map(|&c| {
61                if c == result[i] {
62                    replacement as char
63                } else {
64                    c
65                }
66            })
67            .collect();
68        replacement += 1;
69    }
70
71    result.into_iter().collect()
72}
73
74/// Load a wordlist from a file into a dictionary with normalized words
75pub fn load_wordlist(contents: &str) -> HashMap<String, HashSet<String>> {
76    let mut map = HashMap::new();
77
78    for word in contents.lines() {
79        let word = clean_input(word);
80        map.entry(normalize(&word))
81            .or_insert(HashSet::new())
82            .insert(word.to_string());
83    }
84
85    map
86}