diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.rs | 4 | ||||
| -rw-r--r-- | src/corpus.rs | 72 | ||||
| -rw-r--r-- | src/layout.rs | 169 | ||||
| -rw-r--r-- | src/main.rs | 65 |
4 files changed, 222 insertions, 88 deletions
diff --git a/src/config.rs b/src/config.rs index 4449b7b..bc86613 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,6 @@ -pub const NUM_KEYS: usize = 30; // you should not change this +// Do not change +pub const NUM_KEYS: usize = 30; +pub const ROW_LENGTH: usize = 10; pub const KEY_CHARS: [char; NUM_KEYS] = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', diff --git a/src/corpus.rs b/src/corpus.rs index 2fab5a4..2677cd2 100644 --- a/src/corpus.rs +++ b/src/corpus.rs @@ -51,55 +51,45 @@ impl Corpus { }); } - pub fn layout_fitness(&self, layout: &Layout) -> u32 { + pub fn prelayout_fitness(&self, layout: &Prelayout) -> u32 { let mut sfb: u32 = 0; - // TODO: We make assumptions about NUM_KEYS here - for i in 0..8 { - let ind = if i < 4 { i } else { i + 2 }; - - let k1 = layout.get_key(ind + 10 * 0); - let k2 = layout.get_key(ind + 10 * 1); - let k3 = layout.get_key(ind + 10 * 2); + for i in 0..6 { + let keys = layout.get_standard_column(i); + let k1 = keys[0]; + let k2 = keys[1]; + let k3 = keys[2]; sfb += self.bigram_count[pair_to_index(k1, k2)] + self.bigram_count[pair_to_index(k1, k3)] + self.bigram_count[pair_to_index(k2, k3)]; + } - if i == 3 { - let k4 = layout.get_key(4 + 10 * 0); - let k5 = layout.get_key(4 + 10 * 1); - let k6 = layout.get_key(4 + 10 * 2); - sfb += self.bigram_count[pair_to_index(k4, k5)] - + self.bigram_count[pair_to_index(k4, k6)] - + self.bigram_count[pair_to_index(k5, k6)] - + self.bigram_count[pair_to_index(k1, k4)] - + self.bigram_count[pair_to_index(k1, k5)] - + self.bigram_count[pair_to_index(k1, k6)] - + self.bigram_count[pair_to_index(k2, k4)] - + self.bigram_count[pair_to_index(k2, k5)] - + self.bigram_count[pair_to_index(k2, k6)] - + self.bigram_count[pair_to_index(k3, k4)] - + self.bigram_count[pair_to_index(k3, k5)] - + self.bigram_count[pair_to_index(k3, k6)]; - } else if i == 4 { - let k4 = layout.get_key(5 + 10 * 0); - let k5 = layout.get_key(5 + 10 * 1); - let k6 = layout.get_key(5 + 10 * 2); - sfb += self.bigram_count[pair_to_index(k4, k5)] - + self.bigram_count[pair_to_index(k5, k6)] - + self.bigram_count[pair_to_index(k4, k6)] - + self.bigram_count[pair_to_index(k1, k4)] - + self.bigram_count[pair_to_index(k1, k5)] - + self.bigram_count[pair_to_index(k1, k6)] - + self.bigram_count[pair_to_index(k2, k4)] - + self.bigram_count[pair_to_index(k2, k5)] - + self.bigram_count[pair_to_index(k2, k6)] - + self.bigram_count[pair_to_index(k3, k4)] - + self.bigram_count[pair_to_index(k3, k5)] - + self.bigram_count[pair_to_index(k3, k6)]; - } + for i in 0..2 { + let keys = layout.get_index_column(i); + let k1 = keys[0]; + let k2 = keys[1]; + let k3 = keys[2]; + let k4 = keys[3]; + let k5 = keys[4]; + let k6 = keys[5]; + sfb += self.bigram_count[pair_to_index(k1, k2)] + + self.bigram_count[pair_to_index(k1, k3)] + + self.bigram_count[pair_to_index(k2, k3)] + + self.bigram_count[pair_to_index(k4, k5)] + + self.bigram_count[pair_to_index(k4, k6)] + + self.bigram_count[pair_to_index(k5, k6)] + + self.bigram_count[pair_to_index(k1, k4)] + + self.bigram_count[pair_to_index(k1, k5)] + + self.bigram_count[pair_to_index(k1, k6)] + + self.bigram_count[pair_to_index(k2, k4)] + + self.bigram_count[pair_to_index(k2, k5)] + + self.bigram_count[pair_to_index(k2, k6)] + + self.bigram_count[pair_to_index(k3, k4)] + + self.bigram_count[pair_to_index(k3, k5)] + + self.bigram_count[pair_to_index(k3, k6)]; } + sfb } diff --git a/src/layout.rs b/src/layout.rs index 4c77be4..fe853ad 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -4,6 +4,120 @@ use rand::prelude::*; use std::fmt; #[derive(Copy, Clone)] +pub struct Prelayout { + standard_columns: [[char; 3]; 6], + index_columns: [[char; 6]; 2], +} + +impl Prelayout { + pub fn get_standard_column(&self, index: usize) -> &[char; 3] { + &self.standard_columns[index] + } + + pub fn get_index_column(&self, index: usize) -> &[char; 6] { + &self.index_columns[index] + } + + pub fn new_random_from<R: RngCore>(pl: &Prelayout, rng: &mut R) -> Prelayout { + let mut standard_columns = pl.standard_columns.clone(); + let mut index_columns = pl.index_columns.clone(); + + let mut count = rng.gen_range(1..=NUM_KEYS / 2); + while count > 0 { + let source_index: bool = rng.gen(); + let target_index: bool = rng.gen(); + + let saved; + let target_col: usize; + let target_idx: usize; + if target_index { + target_col = rng.gen_range(0..2); + target_idx = rng.gen_range(0..6); + saved = index_columns[target_col][target_idx]; + } else { + target_col = rng.gen_range(0..6); + target_idx = rng.gen_range(0..3); + saved = standard_columns[target_col][target_idx]; + } + + let source_col: usize; + let source_idx: usize; + if source_index { + source_col = rng.gen_range(0..2); + source_idx = rng.gen_range(0..6); + if target_index { + index_columns[target_col][target_idx] = index_columns[source_col][source_idx]; + } else { + standard_columns[target_col][target_idx] = + index_columns[source_col][source_idx]; + } + index_columns[source_col][source_idx] = saved; + } else { + source_col = rng.gen_range(0..6); + source_idx = rng.gen_range(0..3); + if target_index { + index_columns[target_col][target_idx] = + standard_columns[source_col][source_idx]; + } else { + standard_columns[target_col][target_idx] = + standard_columns[source_col][source_idx]; + } + standard_columns[source_col][source_idx] = saved; + } + count -= 1; + } + Prelayout { + standard_columns, + index_columns, + } + } + + fn from_char_array(ca: &[char; NUM_KEYS]) -> Prelayout { + let mut standard_columns = [['x'; 3]; 6]; + let mut index_columns = [['x'; 6]; 2]; + + for i in 0..6 { + let ind = if i < 3 { i } else { i + 4 }; + for j in 0..3 { + standard_columns[i][j] = ca[ind + j * ROW_LENGTH]; + } + } + + for j in 0..3 { + index_columns[0][j] = ca[3 + j * ROW_LENGTH]; + index_columns[1][j] = ca[6 + j * ROW_LENGTH]; + index_columns[0][j + 3] = ca[4 + j * ROW_LENGTH]; + index_columns[1][j + 3] = ca[5 + j * ROW_LENGTH]; + } + + Prelayout { + standard_columns, + index_columns, + } + } + + fn to_char_array(&self) -> [char; NUM_KEYS] { + let mut result = ['x'; NUM_KEYS]; + + for i in 0..6 { + let ind = if i < 3 { i } else { i + 4 }; + for j in 0..3 { + result[ind + j * ROW_LENGTH] = self.standard_columns[i][j]; + } + } + + for j in 0..3 { + result[3 + j * ROW_LENGTH] = self.index_columns[0][j]; + result[6 + j * ROW_LENGTH] = self.index_columns[1][j]; + result[4 + j * ROW_LENGTH] = self.index_columns[0][j + 3]; + result[5 + j * ROW_LENGTH] = self.index_columns[1][j + 3]; + } + + result + } +} + +#[derive(Copy, Clone)] pub struct Layout { keys: [char; NUM_KEYS], // index in KEY_CHAR -> index in layout @@ -11,15 +125,16 @@ pub struct Layout { } impl Layout { - pub fn get_key(&self, index: usize) -> char { - self.keys[index] + pub fn as_prelayout(&self) -> Prelayout { + Prelayout::from_char_array(&self.keys) } - pub fn translate_index(&self, i: usize) -> usize { - self.translate_index[i] + pub fn from_prelayout(pl: &Prelayout) -> Layout { + // TODO: something more clever about permuting colums, keys within columns etc + Layout::char_array_to_layout(pl.to_char_array()) } - fn keys_to_layout(layout: [char; NUM_KEYS]) -> Layout { + fn char_array_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() { @@ -36,6 +151,14 @@ 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] + } + pub fn from_verbose(inp: &str) -> Option<Layout> { let mut layout: [char; NUM_KEYS] = ['x'; NUM_KEYS]; @@ -55,23 +178,23 @@ impl Layout { } } - return Some(Layout::keys_to_layout(layout)); + return Some(Layout::char_array_to_layout(layout)); } - pub fn new_random_from<R: RngCore>(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) - } + // pub fn new_random_from<R: RngCore>(kbd: &Layout, rng: &mut R) -> Layout { + // let mut layout = kbd.keys; + // // layout.shuffle(rng); + // let mut count = rng.gen_range(1..=NUM_KEYS / 2); + // 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::char_array_to_layout(layout) + // } } impl fmt::Display for Layout { @@ -129,10 +252,8 @@ impl Evaluation { ); } - result += &format!( - "\ntotal sfb: {:.2}%", - self.sfb.iter().sum::<u32>() as f32 / tot * 100.0 - ); + let sfb = self.sfb.iter().sum::<u32>(); + result += &format!("\ntotal sfb: {:.2}% ({})", sfb as f32 / tot * 100.0, sfb); return result; } diff --git a/src/main.rs b/src/main.rs index cee008c..3fee6a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,8 +10,10 @@ use rand_pcg::*; use corpus::*; use layout::*; +use std::time::Instant; + const NUM_CONTESTANTS: usize = 24; -const SURVIVE_THRESHOLD: usize = 2; +const SURVIVE_THRESHOLD: usize = 4; // TODO: Layout is really pre-layout, have something that takes a layout and // re-orders columns to balance hands, and keys per column (block really @@ -25,61 +27,80 @@ fn main() { let corpus = Corpus::load("new.txt").unwrap(); println!("{}", corpus); - let starting_layout = Layout::from_verbose( + let starting_prelayout = Layout::from_verbose( " Y W F L M K P O , Q U R S N H D T E A I Z X C V J B G ' . / ", ) - .unwrap(); + .unwrap() + .as_prelayout(); let mut rng = Pcg64::from_entropy(); - type Fun = Vec<(Layout, u32)>; + type Fun = Vec<(Prelayout, u32)>; let mut best = std::u32::MAX; - let mut layouts: Fun = (0..NUM_CONTESTANTS) + let mut prelayouts: Fun = (0..NUM_CONTESTANTS) .into_iter() - .map(|_| { - let layout; - layout = Layout::new_random_from(&starting_layout, &mut rng); - return (layout, corpus.layout_fitness(&layout)); + .map(|i| { + if i > 0 { + let layout; + layout = Prelayout::new_random_from(&starting_prelayout, &mut rng); + return (layout, std::u32::MAX); + } else { + (starting_prelayout, std::u32::MAX) + } }) .collect(); + let mut count: usize = 0; + let mut current = Instant::now(); loop { - let new_layouts = layouts + let new_prelayouts = prelayouts .iter() .enumerate() .map(|(i, (kbd, _))| { if i > SURVIVE_THRESHOLD { let parent = rng.gen_range(0..=SURVIVE_THRESHOLD); - Layout::new_random_from(&layouts[parent].0, &mut rng) + Prelayout::new_random_from(&prelayouts[parent].0, &mut rng) } else { kbd.clone() } }) - .collect::<Vec<Layout>>(); + .collect::<Vec<Prelayout>>(); - new_layouts + new_prelayouts .par_iter() .map(|layout| { - return (*layout, corpus.layout_fitness(layout)); + return (*layout, corpus.prelayout_fitness(layout)); }) - .collect_into_vec(&mut layouts); + .collect_into_vec(&mut prelayouts); + + prelayouts.sort_by(|(_, lfit), (_, rfit)| lfit.cmp(rfit)); - layouts.sort_by(|(_, lfit), (_, rfit)| lfit.cmp(rfit)); + count += 1; + if count > 65535 { + let duration = current.elapsed(); + eprint!( + "\u{001b}[2K\u{001b}[1000D{} iters/s", + count as f32 / duration.as_millis() as f32 * 1000.0 + ); + current = Instant::now(); + count = 0; + } - if layouts[0].1 < best { - best = layouts[0].1; - let evl = corpus.evaluate_layout(&layouts[0].0); + if prelayouts[0].1 < best { + best = prelayouts[0].1; + let layout = Layout::from_prelayout(&prelayouts[0].0); + let evl = corpus.evaluate_layout(&layout); println!(""); println!("================================================================================\nLayout:\n{}", - layouts[0].0 - ); + layout + ); - println!("{}\n{}", evl, layouts[0].1); + println!("{}", evl); } } } |
