use factorial::Factorial;
use indicatif::{ParallelProgressIterator, ProgressStyle};
use rayon::iter::{ParallelBridge, ParallelIterator};
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::Duration;
use std::{cell::RefCell, collections::HashMap, vec};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Iner {
Nesre,
NesreI,
NesreII,
NesreIII,
NesreIV,
Pet,
PetI,
PetII,
PetIII,
Gabe,
GabeI,
GabeII,
GabeIII,
Shay,
}
impl Iner {
pub const fn base_score(&self) -> usize {
match self {
Iner::Nesre => 1,
Iner::NesreI => 2,
Iner::NesreII => 10,
Iner::NesreIII => 35,
Iner::NesreIV => 85,
Iner::Pet => 1,
Iner::PetI => 3,
Iner::PetII => 22,
Iner::PetIII => 105,
Iner::Gabe => 1,
Iner::GabeI => 5,
Iner::GabeII => 50,
Iner::GabeIII => 500,
Iner::Shay => 1,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum Technique {
ExtractI,
ExtractII,
ExtractIII,
ExtractIV,
FilterI,
FilterII,
FilterIII,
MingleI,
MingleII,
MingleIII,
CrystalI,
CrystalII,
CrystalIII,
}
pub fn add_pet_iner_score(game: &mut GameData, score: usize) {
*game.iner_score_mut(Iner::Pet) += score;
*game.iner_score_mut(Iner::PetI) += score;
*game.iner_score_mut(Iner::PetII) += score;
*game.iner_score_mut(Iner::PetIII) += score;
}
impl Technique {
pub fn operate_on_board(&self, data: &mut GameData) {
match self {
Technique::ExtractI => {
let input = data.iner(Iner::Nesre);
*data.iner_mut(Iner::Nesre) = 0.0;
*data.iner_mut(Iner::NesreI) += input * 2.0;
}
Technique::ExtractII => {
let input = data.iner(Iner::NesreI);
*data.iner_mut(Iner::NesreI) = 0.0;
*data.iner_mut(Iner::NesreII) += input * 2.0;
}
Technique::ExtractIII => {
let input = data.iner(Iner::NesreII);
*data.iner_mut(Iner::NesreII) = 0.0;
*data.iner_mut(Iner::NesreIII) += input * 2.4;
}
Technique::ExtractIV => {
let input = data.iner(Iner::NesreIII);
*data.iner_mut(Iner::NesreIII) = 0.0;
*data.iner_mut(Iner::NesreIV) += input;
data.extra_score += 5000 * data.empty_boards;
}
Technique::FilterI => {
let input = data.iner(Iner::Gabe);
*data.iner_mut(Iner::Gabe) = 0.0;
*data.iner_mut(Iner::GabeI) += input;
*data.iner_mut(Iner::Shay) += input;
}
Technique::FilterII => {
let input = data.iner(Iner::GabeI);
*data.iner_mut(Iner::GabeI) = 0.0;
*data.iner_mut(Iner::GabeII) += input * 0.8;
*data.iner_mut(Iner::Shay) += input * 1.2;
}
Technique::FilterIII => {
let input = data.iner(Iner::GabeII);
*data.iner_mut(Iner::GabeII) = 0.0;
*data.iner_mut(Iner::GabeIII) += input * 0.7;
*data.iner_mut(Iner::Shay) += input * 1.3;
}
Technique::MingleI => {
let pet = data.iner(Iner::Pet);
let shay = data.iner(Iner::Shay);
if pet != 0.0 && shay != 0.0 {
let sum = pet + shay;
*data.iner_mut(Iner::Pet) = 0.0;
*data.iner_mut(Iner::Shay) = 0.0;
*data.iner_mut(Iner::PetI) += sum / 2.0;
}
add_pet_iner_score(data, 5);
}
Technique::MingleII => {
let pet = data.iner(Iner::PetI);
let gabe = data.iner(Iner::GabeI);
if pet != 0.0 && gabe != 0.0 {
let sum = pet + gabe;
*data.iner_mut(Iner::PetI) = 0.0;
*data.iner_mut(Iner::GabeI) = 0.0;
*data.iner_mut(Iner::PetII) += sum / 2.0;
}
add_pet_iner_score(data, 15)
}
Technique::MingleIII => {
let nesre = data.iner(Iner::NesreIII);
let pet = data.iner(Iner::PetII);
if pet != 0.0 && nesre != 0.0 {
let sum = nesre + pet;
*data.iner_mut(Iner::PetII) = 0.0;
*data.iner_mut(Iner::NesreIII) = 0.0;
*data.iner_mut(Iner::PetIII) += sum / 2.0;
data.extra_score += 100 * data.iner(Iner::PetIII).ceil() as usize;
}
}
Technique::CrystalI => {
*data.iner_mut(Iner::Shay) *= 5.0;
}
Technique::CrystalII => {
*data.iner_mut(Iner::Shay) *= 8.0;
}
Technique::CrystalIII => {
*data.iner_mut(Iner::Shay) *= 9.0;
*data.iner_score_mut(Iner::Shay) += 1;
}
}
}
pub fn operate_in_technique_area(&self, data: &mut GameData) {
match self {
Technique::ExtractI | Technique::ExtractII | Technique::ExtractIII => {
self.operate_on_board(data);
}
_ => (),
}
}
}
#[derive(Debug, Clone)]
pub struct InerInfo {
pub amount: f64,
pub score: usize,
}
impl InerInfo {
pub fn new(iner: Iner, amount: f64) -> Self {
Self {
amount,
score: iner.base_score(),
}
}
}
#[derive(Debug, Clone)]
pub struct GameData {
pub extra_score: usize,
pub iner: HashMap<Iner, InerInfo>,
pub empty_boards: usize,
}
impl GameData {
pub fn new(nesre: usize, pet: usize, gabe: usize, shay: usize, empty_boards: usize) -> Self {
Self {
extra_score: 0,
iner: [
(Iner::Nesre, nesre),
(Iner::Pet, pet),
(Iner::Gabe, gabe),
(Iner::Shay, shay),
]
.into_iter()
.map(|(iner, amount)| (iner, InerInfo::new(iner, amount as f64)))
.collect(),
empty_boards,
}
}
pub fn iner_mut(&mut self, iner: Iner) -> &mut f64 {
&mut self
.iner
.entry(iner)
.or_insert(InerInfo::new(iner, 0.0))
.amount
}
pub fn iner(&self, iner: Iner) -> f64 {
if let Some(info) = self.iner.get(&iner) {
info.amount
} else {
0.0
}
}
pub fn iner_score_mut(&mut self, iner: Iner) -> &mut usize {
&mut self
.iner
.entry(iner)
.or_insert(InerInfo::new(iner, 0.0))
.score
}
}
#[derive(Debug, Clone)]
pub struct Game {
pub data: RefCell<GameData>,
pub board: [Option<Technique>; 6],
pub technique: Vec<Technique>,
}
impl Game {
pub fn run(self) -> (usize, GameData) {
for tech in &self.technique {
tech.operate_in_technique_area(&mut self.data.borrow_mut());
}
for tech in &self.board {
if let Some(tech) = tech {
tech.operate_on_board(&mut self.data.borrow_mut());
}
}
let mut score: f64 = 0.0;
for (
_,
&InerInfo {
amount: a,
score: s,
},
) in self.data.borrow().iner.iter()
{
score += a.ceil() * (s as f64);
}
score += self.data.borrow().extra_score as f64;
(score as usize, self.data.into_inner())
}
}
pub struct Simulation {
pub available_techniques: Vec<Technique>,
pub board_size: usize,
pub initial_nesre: usize,
pub initial_pet: usize,
pub initial_gabe: usize,
pub initial_shay: usize,
}
impl Simulation {
#[deprecated]
pub fn find_best_v1(&self) -> Game {
use itertools::*;
let tech_amount = self.available_techniques.len();
let best_game: Mutex<Option<Game>> = Mutex::new(None);
let best_score: Mutex<Option<usize>> = Mutex::new(None);
let total_iterations: u128 = (tech_amount as u128).factorial();
let iters = Arc::new(RwLock::new(0u128));
let iters1 = Arc::clone(&iters);
let handle = thread::spawn(move || {
let mut old = 0;
loop {
let iters = iters1.read().unwrap().to_owned();
println!(
"[{:.2}%]: {} / {}, {}M iterations per sec",
iters as f64 / total_iterations as f64 * 100.0,
iters,
total_iterations,
(iters - old) as f64 / 1_000_000.0
);
thread::sleep(Duration::from_secs(1));
if iters >= total_iterations {
break;
}
old = iters;
}
});
self.available_techniques
.iter()
.permutations(tech_amount)
.par_bridge()
.for_each(|permutation| {
let s = usize::min(tech_amount, self.board_size);
for i in 0..=s {
let mut board: [Option<Technique>; 6] = [None; 6];
board.iter_mut().enumerate().for_each(|(k, v)| {
if k < i {
*v = Some(*permutation[k]);
}
});
let technique = permutation[i..].iter().map(|&&t| t).collect_vec();
let data = GameData::new(
self.initial_nesre,
self.initial_pet,
self.initial_gabe,
self.initial_shay,
self.board_size - i,
);
let game = Game {
data: RefCell::new(data),
board,
technique,
};
let rem = game.clone();
let (score, _) = game.run();
if score > best_score.lock().unwrap().unwrap_or(0) {
*best_game.lock().unwrap() = Some(rem);
*best_score.lock().unwrap() = Some(score);
}
}
*iters.write().unwrap() += s as u128;
});
handle.join().unwrap();
let game = best_game.lock().unwrap().clone();
game.unwrap()
}
pub fn find_best_v2(&self) -> Game {
let tech_amount = self.available_techniques.len();
let best_game: Mutex<Option<Game>> = Mutex::new(None);
let best_score: Mutex<Option<usize>> = Mutex::new(None);
self.find_best_inner(tech_amount, best_score, &best_game);
let game = best_game.lock().unwrap().clone();
game.unwrap()
}
pub fn find_best_v2_scilenced(&self) -> Game {
let tech_amount = self.available_techniques.len();
let best_game: Mutex<Option<Game>> = Mutex::new(None);
let best_score: Mutex<Option<usize>> = Mutex::new(None);
self.find_best_inner(tech_amount, best_score, &best_game);
let game = best_game.lock().unwrap().clone();
game.unwrap()
}
fn find_best_inner(
&self,
tech_amount: usize,
best_score: Mutex<Option<usize>>,
best_game: &Mutex<Option<Game>>,
) {
use itertools::*;
self.available_techniques
.iter()
.permutations(tech_amount)
.par_bridge()
.progress_count(3628800)
.with_style(
ProgressStyle::with_template(
"[{elapsed_precise}] [{bar:40}] {pos:>7}/{len:7} {msg}",
)
.unwrap().progress_chars("=> "),
)
.for_each(|permutation| {
let s = usize::min(tech_amount, self.board_size);
for i in 0..=s {
let mut board: [Option<Technique>; 6] = [None; 6];
board.iter_mut().enumerate().for_each(|(k, v)| {
if k < i {
*v = Some(*permutation[k]);
}
});
let mut technique = vec![
Technique::ExtractI,
Technique::ExtractII,
Technique::ExtractIII,
];
technique.append(&mut permutation[i..].iter().map(|&&t| t).collect_vec());
let data = GameData::new(
self.initial_nesre,
self.initial_pet,
self.initial_gabe,
self.initial_shay,
self.board_size - i,
);
let game = Game {
data: RefCell::new(data),
board,
technique,
};
let rem = game.clone();
let (score, _) = game.run();
if score > best_score.lock().unwrap().unwrap_or(0) {
*best_game.lock().unwrap() = Some(rem);
*best_score.lock().unwrap() = Some(score);
}
}
});
}
}
pub fn printer(
progress: Arc<RwLock<(u128, u128)>>,
total_iterations: u128,
) -> thread::JoinHandle<()> {
let handle = thread::spawn(move || {
let mut old_iters = 0;
loop {
let (progress, iters) = progress.read().unwrap().to_owned();
println!(
"[{:06.2}%]: {:07} of {:07}, {:.2}k iter/s",
progress as f64 / total_iterations as f64 * 100.0,
progress,
total_iterations,
(iters - old_iters) as f64 / 1_000.0
);
old_iters = iters;
if progress >= total_iterations {
break;
}
thread::sleep(Duration::from_secs(1));
}
});
handle
}