diff --git a/klout/Cargo.lock b/klout/Cargo.lock index bd86659..6b6aeec 100644 --- a/klout/Cargo.lock +++ b/klout/Cargo.lock @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", diff --git a/klout/src/main.rs b/klout/src/main.rs index 174dc4b..5e758e5 100644 --- a/klout/src/main.rs +++ b/klout/src/main.rs @@ -49,9 +49,9 @@ const RINDEX: Finger = F!(Right, Index); #[rustfmt::skip] const MATRIX_EFFORT: &[f64; MATRIX_COUNT] = &[ - 100., 3., 5., 3., 30., 30., 3., 5., 3., 100., - 50., 2., 1., 1., 15., 15., 1., 1., 2., 50., - 25., 7., 5., 7., 30., 1., 5., 7., 5., 100., + 100., 6., 10., 6., 40., 40., 10., 10., 10., 100., + 50., 2., 0.1, 0.1, 15., 15., 0.1, 0.1, 2., 50., + 10., 14., 10., 20., 40., 30., 6., 14., 6., 100., ]; #[rustfmt::skip] @@ -86,7 +86,7 @@ const CHAR_FREQ_LOOKUP: [f64; char_data_max_index()] = { macro_rules! d { ($ch:expr, $val:expr) => {{ - data[$ch as usize] = $val * 100.; + data[$ch as usize] = $val * 1000.; }}; } @@ -146,18 +146,62 @@ impl Layout { out } + + fn key_to_xy(&self, ch: char) -> (u8, u8) { + let index = self.key_indices[ch as usize]; + let y = index / 10; + let x = index % 10; + + (x as u8, y as u8) + } + + fn balance_penalty(&self) -> f64 { + let km = self.to_key_matrix(); + + let mut left_freq = 0.; + for x in 0..5 { + for y in 0..3 { + let i = y * 10 + x; + let ch = km[i]; + left_freq += CHAR_FREQ_LOOKUP[ch as usize]; + } + } + + let mut right_freq = 0.; + for x in 5..10 { + for y in 0..3 { + let i = y * 10 + x; + let ch = km[i]; + right_freq += CHAR_FREQ_LOOKUP[ch as usize]; + } + } + + (left_freq - right_freq).abs() * 10. + } +} + +#[test] +fn test_key_to_xy() { + assert_eq!(INITIAL_LAYOUT.key_to_xy('g'), (4, 1)); } fn key_effort(ch: char, l: &Layout) -> f64 { CHAR_FREQ_LOOKUP[ch as usize] * MATRIX_EFFORT[l.key_indices[ch as usize]] } - +/* #[rustfmt::skip] const INITIAL_LAYOUT: Layout = Layout::from_key_matrix(&[ 'q', 'w', 'f', 'p', 'b', 'j', 'l', 'u', 'y', '\'', 'a', 'r', 's', 't', 'g', 'm', 'n', 'e', 'i', 'o', 'x', 'c', 'd', 'v', 'z', 'k', 'h', ',', '.', '/', ]); +*/ +#[rustfmt::skip] +const INITIAL_LAYOUT: Layout = Layout::from_key_matrix(&[ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '\'', + 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', +]); type NGramFreqs = HashMap<&'static str, f64>; @@ -186,7 +230,7 @@ unsafe fn init_bigram_freqs() { ("ng", 0.01053385), ] .into_iter() - .map(|(s, v)| (s, v * 100.)) + .map(|(s, v)| (s, v * 4000.)) .collect::(); unsafe { BIGRAM_FREQS = Some(freqs) }; @@ -203,8 +247,39 @@ fn bigram_effort(bigram: &str, l: &Layout) -> f64 { let finger1 = MATRIX_FINGERS[l.key_indices[ch1 as usize]]; let finger2 = MATRIX_FINGERS[l.key_indices[ch2 as usize]]; + + let ch1xy = l.key_to_xy(ch1 as char); + let ch2xy = l.key_to_xy(ch2 as char); + let x_diff = ch1xy.0.abs_diff(ch2xy.0); + let y_diff = ch1xy.1.abs_diff(ch2xy.1); + if finger1 == finger2 { - eff *= 10.; + if y_diff > 1 { + eff *= 1000.; + } else { + eff *= 500.; + } + } + + if finger1.hand != finger2.hand { + eff /= 2.; + } else if x_diff == 1 { + let mult = if (finger1.hand == Hand::Left && ch1xy.0 < ch2xy.0) + || (finger1.hand == Hand::Right && ch1xy.0 > ch2xy.0) + { + 10. + } else { + 1. + }; + + if y_diff == 0 { + eff /= 1000. * mult; + } else { + eff /= 250. * mult; + } + } else { + eff *= (x_diff + 1) as f64; + eff *= (y_diff + 1) as f64; } eff @@ -227,9 +302,10 @@ fn all_bigrams() -> &'static [String] { unsafe { ALL_BIGRAMS.as_ref().unwrap_unchecked() } } -const N_WORKERS: usize = 20; +const N_WORKERS: usize = 6; -fn mutate_layout(layout: &mut Layout, rng: &mut ThreadRng, num_swaps: usize) { +fn mutate_layout(layout: &mut Layout, rng: &mut ThreadRng, max_swaps: usize) { + let num_swaps = rng.gen_range(1..=max_swaps); for _ in 0..num_swaps { let ch1 = CHARACTERS .chars() @@ -252,8 +328,8 @@ struct WorkerThreadInit { /// reverting back to the currently-known best layout max_dev: f64, - /// The number of swaps to make between iterations - swaps: usize, + /// The maximum number of swaps to make between iterations + max_swaps: usize, } fn worker_thread_run(init: WorkerThreadInit) { @@ -271,6 +347,8 @@ fn worker_thread_run(init: WorkerThreadInit) { .map(|b| bigram_effort(b, &layout)) .sum::(); + eff *= layout.balance_penalty(); + if eff < min_effort { min_effort = eff; best_layout = layout; @@ -279,7 +357,7 @@ fn worker_thread_run(init: WorkerThreadInit) { layout = best_layout; } - mutate_layout(&mut layout, &mut rng, init.swaps); + mutate_layout(&mut layout, &mut rng, init.max_swaps); } } @@ -349,8 +427,8 @@ fn main() -> Result<()> { worker_thread_run(WorkerThreadInit { initial_layout: INITIAL_LAYOUT, report: tx, - max_dev: 500., - swaps: 5, + max_dev: 1000., + max_swaps: 10, }) }) })