aboutsummaryrefslogtreecommitdiff
path: root/src/layout.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout.rs')
-rw-r--r--src/layout.rs212
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;
+}