diff options
| author | tslil clingman <tslil@posteo.de> | 2022-09-25 00:12:14 +0200 |
|---|---|---|
| committer | tslil clingman <tslil@posteo.de> | 2022-09-25 00:12:14 +0200 |
| commit | 1cac34b7ea939e0d0d1157de59e90c70b2831812 (patch) | |
| tree | a862e3bbc8b5b5175353e660b9efcdba2c9137ab /src/layout.rs | |
| parent | 5ec1655971087ae556d3fc27e7b8a1eaa23944c5 (diff) | |
More efficient?
Diffstat (limited to 'src/layout.rs')
| -rw-r--r-- | src/layout.rs | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/layout.rs b/src/layout.rs new file mode 100644 index 0000000..0ce3fdc --- /dev/null +++ b/src/layout.rs @@ -0,0 +1,212 @@ +use crate::config::*; + +use rand::prelude::*; +use std::fmt; + +#[derive(Copy, Clone)] +pub struct Layout { + keys: [char; NUM_KEYS], + // index in KEY_CHAR -> index in layout + translate_index: [usize; NUM_KEYS], +} + +impl Layout { + pub fn get_key(&self, index: usize) -> char { + self.keys[index] + } + + pub fn translate_index(&self, i: usize) -> usize { + self.translate_index[i] + } + + fn keys_to_layout(layout: [char; NUM_KEYS]) -> Layout { + let mut translate_index: [usize; NUM_KEYS] = [0; NUM_KEYS]; + + for (i, seek) in KEY_CHARS.iter().enumerate() { + for (j, found) in layout.iter().enumerate() { + if seek == found { + translate_index[i] = j; + } + } + } + + return Layout { + keys: layout, + translate_index, + }; + } + + pub fn from_verbose(inp: &str) -> Option<Layout> { + let mut layout: [char; NUM_KEYS] = ['x'; NUM_KEYS]; + + let mut k: usize = 0; + for c in inp.chars() { + let valid = (c.is_uppercase() && c.is_alphabetic()) + || c == '.' + || c == '/' + || c == ',' + || c == '\''; + if valid { + layout[k] = c; + k += 1; + } + if k > NUM_KEYS { + return None; + } + } + + return Some(Layout::keys_to_layout(layout)); + } + + pub fn new_random_from<R: RngCore>(kbd: &Layout, rng: &mut R) -> Layout { + let mut layout = kbd.keys; + layout.shuffle(rng); + Layout::keys_to_layout(layout) + } +} + +impl fmt::Display for Layout { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&format_block_output(self.keys.into_iter())) + } +} + +#[derive(Copy, Clone)] +pub struct Evaluation { + keypress: [u32; NUM_KEYS], + total_keypress: u32, + sfb: [u32; 8], +} + +impl Evaluation { + pub fn new(keypress: [u32; NUM_KEYS], total_keypress: u32, sfb: [u32; 8]) -> Evaluation { + Evaluation { + keypress, + sfb, + total_keypress, + } + } + + fn output_eval(&self) -> String { + let mut result = String::new(); + let tot = self.total_keypress as f32; + + result += &format!( + "Total keypresses: {}\nPercent per key:\n", + self.total_keypress + ); + result += &format_block_output(self.keypress.into_iter().map(|k| 100.0 * k as f32 / tot)); + + let mut finger_usages = [0; 8]; + for (i, &c) in self.keypress.iter().enumerate() { + finger_usages[KEY_TO_FINGER[i]] += c; + } + + result += "finger usage: "; + for (i, &u) in finger_usages.iter().enumerate() { + result += &format!( + "{:>5.2}%{}", + u as f32 / tot * 100.0, + if i < 7 { ", " } else { "" } + ); + } + + result += "\nsame finger bigrams: "; + for (i, &u) in self.sfb.iter().enumerate() { + result += &format!( + "{:>6.3}%{}", + u as f32 / tot * 100.0, + if i < 7 { ", " } else { "" } + ); + } + + result += &format!( + "\ntotal sfb: {:.2}%", + self.sfb.iter().sum::<u32>() as f32 / tot * 100.0 + ); + + return result; + } + + // TODO + pub fn fitness(&self) -> f32 { + // let mut finger_usages = [0; 8]; + // let tot = self.total_keypress as f32; + // for (i, &c) in self.keypress.iter().enumerate() { + // finger_usages[KEY_TO_FINGER[i]] += c; + // } + // const MAX_FINGER_USAGES: [f32; 8] = [8.0, 11.0, 21.0, 21.0, 21.0, 21.0, 11.0, 8.0]; + // const MAX_FINGER_USAGES: [f32; 8] = + // [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0]; + // if finger_usages + // .iter() + // .enumerate() + // .any(|(i, &c)| c as f32 / tot * 100.00 > MAX_FINGER_USAGES[i]) + // { + // std::u32::MAX + // } else { + // self.sfb + // .iter() + // .enumerate() + // .map(|(i, &s)| s as f32 / finger_usages[i] as f32 * 50.0) + // .sum() + // } + self.sfb.iter().sum::<u32>() as f32 + } +} + +impl fmt::Display for Evaluation { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&self.output_eval()) + } +} + +// This is a silly amount of work to genericise the below... +trait ToMyString { + const BLANK_STRING: &'static str; + fn to_my_string(&self) -> String; +} + +impl ToMyString for char { + const BLANK_STRING: &'static str = " "; + fn to_my_string(&self) -> String { + self.to_string() + } +} + +impl ToMyString for u32 { + const BLANK_STRING: &'static str = " "; + fn to_my_string(&self) -> String { + format!("{:5}", *self) + } +} + +impl ToMyString for f32 { + const BLANK_STRING: &'static str = " "; + fn to_my_string(&self) -> String { + format!("{:>4.1}", *self) + } +} + +fn format_block_output<T: Iterator<Item = S>, S: ToMyString>(things: T) -> String { + let mut result = String::new(); + for (i, t) in things.enumerate() { + result += &t.to_my_string(); + if i < NUM_KEYS - 1 { + result.push(' '); + } + if (i + 1) % 10 == 0 { + result.push('\n'); + if NUM_KEYS < 30 && i == 19 { + result += S::BLANK_STRING; + result.push(' ') + } + } else if (i < 20 && (i + 1) % 5 == 0) || (i == (NUM_KEYS - 20) / 2 + 19) { + result.push(' '); + } + if i + 1 >= NUM_KEYS { + break; + } + } + return result; +} |
