mod config; mod corpus; mod layout; use rayon::prelude::*; use rand::prelude::*; use rand_pcg::*; use corpus::*; use layout::*; const NUM_CONTESTANTS: usize = 24; const SURVIVE_THRESHOLD: usize = 2; // 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 // consider index fingers) to balance rows. // TODO: command line arguments to start at a given layout string, maybe read // from file? Might as well make num_contestants and survive_threshold // configurable, and number of swaps when generating new layout fn main() { let corpus = Corpus::load("new.txt").unwrap(); println!("{}", corpus); let starting_layout = 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(); let mut rng = Pcg64::from_entropy(); type Fun = Vec<(Layout, u32)>; let mut best = std::u32::MAX; let mut layouts: Fun = (0..NUM_CONTESTANTS) .into_iter() .map(|_| { let layout; layout = Layout::new_random_from(&starting_layout, &mut rng); return (layout, corpus.layout_fitness(&layout)); }) .collect(); loop { let new_layouts = layouts .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) } else { kbd.clone() } }) .collect::>(); new_layouts .par_iter() .map(|layout| { return (*layout, corpus.layout_fitness(layout)); }) .collect_into_vec(&mut layouts); layouts.sort_by(|(_, lfit), (_, rfit)| lfit.cmp(rfit)); if layouts[0].1 < best { best = layouts[0].1; let evl = corpus.evaluate_layout(&layouts[0].0); println!(""); println!("================================================================================\nLayout:\n{}", layouts[0].0 ); println!("{}\n{}", evl, layouts[0].1); } } }