// Copyright (C) 2022 tslil clingman
//
// This file is part of srchr.
//
// srchr is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// srchr is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// srchr. If not, see .
mod config;
mod corpus;
mod evaluation;
mod layout;
mod output;
use rayon::prelude::*;
use rand::prelude::*;
use rand_pcg::*;
use config::*;
use corpus::*;
use evaluation::*;
use layout::*;
use std::time::Instant;
struct Tournament<'a> {
rngs: Vec,
top_prelayouts: Vec<(Prelayout, f32)>,
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, f32)> = Vec::new();
let score = prelayout_fitness(&corpus, &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, prelayout_fitness(&self.corpus, &layout));
})
.collect::>();
let best = self.top_prelayouts[0].1;
tournament.append(&mut self.top_prelayouts);
tournament.sort_by(|(_, lscore), (_, rscore)| {
if lscore < rscore {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
});
let result;
if tournament[0].1 + 1e-7 < 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;
if LOAD_STATS {
corpus = Corpus::load_from_json_file(STATS_FILE_NAME).unwrap();
} else {
corpus = Corpus::load_from_text_file(CORPUS_FILE_NAME).unwrap();
}
let seed_layout = Layout::from_verbose(STARTING_LAYOUT_STRING).unwrap();
let seed_evl = Evaluation::evaluate_layout(&corpus, &seed_layout);
let seed_prelayout = seed_layout.as_prelayout();
println!("Starting with\n{}{}", seed_layout, seed_evl);
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 = Evaluation::evaluate_layout(&corpus, &layout);
println!("\n================================================================================\n\n{}{}",
layout, evl
);
}
count += 1;
if count > 65535 {
let duration = current.elapsed();
let rps = count as f32 / duration.as_millis() as f32 * 1000.0;
eprint!(
"\u{001b}[2K\u{001b}[1000D{} layouts/s and {} tournaments/s",
rps * NUM_CONTESTANTS as f32,
rps
);
current = Instant::now();
count = 0;
}
}
}