mod corpus; mod layout; use rayon::prelude::*; use rand::prelude::*; use rand_pcg::*; use corpus::*; use layout::*; use std::time::Instant; const NUM_CONTESTANTS: usize = 128; const NUM_PERSIST: usize = 8; // 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, and index // finger threshold. struct Tournament<'a> { rngs: Vec, top_prelayouts: Vec<(Prelayout, u32)>, corpus: &'a Corpus, } impl<'a> Tournament<'a> { fn new_from_seed_prelayout(seed_prelayout: &Prelayout, corpus: &'a Corpus) -> Tournament<'a> { let mut rngs: Vec = Vec::new(); for _ in 0..NUM_CONTESTANTS { rngs.push(Pcg64::from_entropy()); } let mut top_prelayouts: Vec<(Prelayout, u32)> = Vec::new(); let score = corpus.prelayout_fitness(&seed_prelayout); for _ in 0..NUM_PERSIST { top_prelayouts.push((seed_prelayout.clone(), score)); } Tournament { rngs, top_prelayouts, corpus, } } fn run_round(&mut self) -> Option { let rngs = &mut self.rngs; let mut tournament = rngs .into_par_iter() .map(|mut rng| { let layout = Prelayout::new_random_from( &self.top_prelayouts[rng.gen_range(0..NUM_PERSIST)].0, &mut rng, ); return (layout, self.corpus.prelayout_fitness(&layout)); }) .collect::>(); let best = self.top_prelayouts[0].1; tournament.append(&mut self.top_prelayouts); tournament.sort_by(|(_, lscore), (_, rscore)| lscore.cmp(rscore)); let result; if tournament[0].1 < best { result = Some(tournament[0].0); } else { result = None; } for i in 0..NUM_PERSIST { self.top_prelayouts.push(tournament[i]); } result } } fn main() { let corpus = Corpus::load("books.txt").unwrap(); println!("{}", corpus); let seed_prelayout = Layout::from_verbose("QWERTYUIOPASDFGHJKL'ZXCVBNM,./") .unwrap() .as_prelayout(); let mut tournament = Tournament::new_from_seed_prelayout(&seed_prelayout, &corpus); let mut count: usize = 0; let mut current = Instant::now(); loop { let improvement = tournament.run_round(); if let Some(prelayout) = improvement { let layout = Layout::from_prelayout(&prelayout, &corpus); let evl = corpus.evaluate_layout(&layout); println!(""); println!("================================================================================\n\n{}", layout ); println!("{}", evl); } count += 1; if count > 65535 { let duration = current.elapsed(); eprint!( "\u{001b}[2K\u{001b}[1000D{} layouts/s", (count * NUM_CONTESTANTS) as f32 / duration.as_millis() as f32 * 1000.0 ); current = Instant::now(); count = 0; } } }