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 { 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(kbd: &Layout, rng: &mut R) -> Layout { let mut layout = kbd.keys; // layout.shuffle(rng); let mut count = rng.gen_range(1..=15); while count > 0 { let a = rng.gen_range(0..NUM_KEYS); let b = rng.gen_range(0..NUM_KEYS); let k = layout[b]; layout[b] = layout[a]; layout[a] = k; count -= 1; } 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::() 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::() 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, 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; }