1use factorial::Factorial;
2use indicatif::{ParallelProgressIterator, ProgressStyle};
3use rayon::iter::{ParallelBridge, ParallelIterator};
4use std::sync::{Arc, Mutex, RwLock};
5use std::thread;
6use std::time::Duration;
7use std::{cell::RefCell, collections::HashMap, vec};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum Iner {
11 Nesre,
13 NesreI,
15 NesreII,
17 NesreIII,
19 NesreIV,
21 Pet,
23 PetI,
25 PetII,
27 PetIII,
29 Gabe,
31 GabeI,
33 GabeII,
35 GabeIII,
37 Shay,
39}
40
41impl Iner {
42 pub const fn base_score(&self) -> usize {
44 match self {
45 Iner::Nesre => 1,
46 Iner::NesreI => 2,
47 Iner::NesreII => 10,
48 Iner::NesreIII => 35,
49 Iner::NesreIV => 85,
50 Iner::Pet => 1,
51 Iner::PetI => 3,
52 Iner::PetII => 22,
53 Iner::PetIII => 105,
54 Iner::Gabe => 1,
55 Iner::GabeI => 5,
56 Iner::GabeII => 50,
57 Iner::GabeIII => 500,
58 Iner::Shay => 1,
59 }
60 }
61}
62
63#[derive(Debug, Copy, Clone)]
64pub enum Technique {
65 ExtractI,
67 ExtractII,
68 ExtractIII,
69 ExtractIV,
70 FilterI,
72 FilterII,
73 FilterIII,
74 MingleI,
76 MingleII,
77 MingleIII,
78 CrystalI,
80 CrystalII,
81 CrystalIII,
82}
83
84pub fn add_pet_iner_score(game: &mut GameData, score: usize) {
86 *game.iner_score_mut(Iner::Pet) += score;
87 *game.iner_score_mut(Iner::PetI) += score;
88 *game.iner_score_mut(Iner::PetII) += score;
89 *game.iner_score_mut(Iner::PetIII) += score;
90}
91
92impl Technique {
93 pub fn operate_on_board(&self, data: &mut GameData) {
95 match self {
96 Technique::ExtractI => {
98 let input = data.iner(Iner::Nesre);
99 *data.iner_mut(Iner::Nesre) = 0.0;
100 *data.iner_mut(Iner::NesreI) += input * 2.0;
101 }
102 Technique::ExtractII => {
104 let input = data.iner(Iner::NesreI);
105 *data.iner_mut(Iner::NesreI) = 0.0;
106 *data.iner_mut(Iner::NesreII) += input * 2.0;
107 }
108 Technique::ExtractIII => {
111 let input = data.iner(Iner::NesreII);
112 *data.iner_mut(Iner::NesreII) = 0.0;
113 *data.iner_mut(Iner::NesreIII) += input * 2.4;
114 }
115 Technique::ExtractIV => {
118 let input = data.iner(Iner::NesreIII);
119 *data.iner_mut(Iner::NesreIII) = 0.0;
120 *data.iner_mut(Iner::NesreIV) += input;
121 data.extra_score += 5000 * data.empty_boards;
122 }
123 Technique::FilterI => {
126 let input = data.iner(Iner::Gabe);
127 *data.iner_mut(Iner::Gabe) = 0.0;
128 *data.iner_mut(Iner::GabeI) += input;
129 *data.iner_mut(Iner::Shay) += input;
130 }
131 Technique::FilterII => {
134 let input = data.iner(Iner::GabeI);
135 *data.iner_mut(Iner::GabeI) = 0.0;
136 *data.iner_mut(Iner::GabeII) += input * 0.8;
137 *data.iner_mut(Iner::Shay) += input * 1.2;
139 }
140 Technique::FilterIII => {
143 let input = data.iner(Iner::GabeII);
144 *data.iner_mut(Iner::GabeII) = 0.0;
145 *data.iner_mut(Iner::GabeIII) += input * 0.7;
146 *data.iner_mut(Iner::Shay) += input * 1.3;
147 }
148 Technique::MingleI => {
152 let pet = data.iner(Iner::Pet);
153 let shay = data.iner(Iner::Shay);
154 if pet != 0.0 && shay != 0.0 {
155 let sum = pet + shay;
157 *data.iner_mut(Iner::Pet) = 0.0;
158 *data.iner_mut(Iner::Shay) = 0.0;
159 *data.iner_mut(Iner::PetI) += sum / 2.0;
160 }
161 add_pet_iner_score(data, 5);
162 }
163 Technique::MingleII => {
167 let pet = data.iner(Iner::PetI);
168 let gabe = data.iner(Iner::GabeI);
169 if pet != 0.0 && gabe != 0.0 {
170 let sum = pet + gabe;
172 *data.iner_mut(Iner::PetI) = 0.0;
173 *data.iner_mut(Iner::GabeI) = 0.0;
174 *data.iner_mut(Iner::PetII) += sum / 2.0;
175 }
176 add_pet_iner_score(data, 15)
177 }
178 Technique::MingleIII => {
182 let nesre = data.iner(Iner::NesreIII);
183 let pet = data.iner(Iner::PetII);
184 if pet != 0.0 && nesre != 0.0 {
185 let sum = nesre + pet;
187 *data.iner_mut(Iner::PetII) = 0.0;
188 *data.iner_mut(Iner::NesreIII) = 0.0;
189 *data.iner_mut(Iner::PetIII) += sum / 2.0;
190 data.extra_score += 100 * data.iner(Iner::PetIII).ceil() as usize;
191 }
192 }
193 Technique::CrystalI => {
195 *data.iner_mut(Iner::Shay) *= 5.0;
196 }
197 Technique::CrystalII => {
199 *data.iner_mut(Iner::Shay) *= 8.0;
200 }
201 Technique::CrystalIII => {
204 *data.iner_mut(Iner::Shay) *= 9.0;
205 *data.iner_score_mut(Iner::Shay) += 1;
206 }
207 }
208 }
209
210 pub fn operate_in_technique_area(&self, data: &mut GameData) {
213 match self {
214 Technique::ExtractI | Technique::ExtractII | Technique::ExtractIII => {
215 self.operate_on_board(data);
216 }
217 _ => (),
218 }
219 }
220}
221
222#[derive(Debug, Clone)]
223pub struct InerInfo {
224 pub amount: f64,
225 pub score: usize,
226}
227
228impl InerInfo {
229 pub fn new(iner: Iner, amount: f64) -> Self {
230 Self {
231 amount,
232 score: iner.base_score(),
233 }
234 }
235}
236
237#[derive(Debug, Clone)]
238pub struct GameData {
239 pub extra_score: usize,
240 pub iner: HashMap<Iner, InerInfo>,
241 pub empty_boards: usize,
242}
243
244impl GameData {
245 pub fn new(nesre: usize, pet: usize, gabe: usize, shay: usize, empty_boards: usize) -> Self {
246 Self {
247 extra_score: 0,
248 iner: [
249 (Iner::Nesre, nesre),
250 (Iner::Pet, pet),
251 (Iner::Gabe, gabe),
252 (Iner::Shay, shay),
253 ]
254 .into_iter()
255 .map(|(iner, amount)| (iner, InerInfo::new(iner, amount as f64)))
256 .collect(),
257 empty_boards,
258 }
259 }
260
261 pub fn iner_mut(&mut self, iner: Iner) -> &mut f64 {
262 &mut self
263 .iner
264 .entry(iner)
265 .or_insert(InerInfo::new(iner, 0.0))
266 .amount
267 }
268
269 pub fn iner(&self, iner: Iner) -> f64 {
270 if let Some(info) = self.iner.get(&iner) {
271 info.amount
272 } else {
273 0.0
274 }
275 }
276
277 pub fn iner_score_mut(&mut self, iner: Iner) -> &mut usize {
278 &mut self
279 .iner
280 .entry(iner)
281 .or_insert(InerInfo::new(iner, 0.0))
282 .score
283 }
284}
285
286#[derive(Debug, Clone)]
287pub struct Game {
288 pub data: RefCell<GameData>,
290 pub board: [Option<Technique>; 6],
292 pub technique: Vec<Technique>,
294}
295
296impl Game {
297 pub fn run(self) -> (usize, GameData) {
298 for tech in &self.technique {
299 tech.operate_in_technique_area(&mut self.data.borrow_mut());
300 }
301 for tech in &self.board {
302 if let Some(tech) = tech {
303 tech.operate_on_board(&mut self.data.borrow_mut());
304 }
305 }
306 let mut score: f64 = 0.0;
307 for (
308 _,
309 &InerInfo {
310 amount: a,
311 score: s,
312 },
313 ) in self.data.borrow().iner.iter()
314 {
315 score += a.ceil() * (s as f64);
316 }
317 score += self.data.borrow().extra_score as f64;
318 (score as usize, self.data.into_inner())
319 }
320}
321
322pub struct Simulation {
323 pub available_techniques: Vec<Technique>,
324 pub board_size: usize,
325 pub initial_nesre: usize,
326 pub initial_pet: usize,
327 pub initial_gabe: usize,
328 pub initial_shay: usize,
329}
330
331impl Simulation {
332 #[deprecated]
333 pub fn find_best_v1(&self) -> Game {
334 use itertools::*;
335 let tech_amount = self.available_techniques.len();
336 let best_game: Mutex<Option<Game>> = Mutex::new(None);
337 let best_score: Mutex<Option<usize>> = Mutex::new(None);
338 let total_iterations: u128 = (tech_amount as u128).factorial();
339 let iters = Arc::new(RwLock::new(0u128));
340 let iters1 = Arc::clone(&iters);
341 let handle = thread::spawn(move || {
342 let mut old = 0;
343 loop {
344 let iters = iters1.read().unwrap().to_owned();
345 println!(
346 "[{:.2}%]: {} / {}, {}M iterations per sec",
347 iters as f64 / total_iterations as f64 * 100.0,
348 iters,
349 total_iterations,
350 (iters - old) as f64 / 1_000_000.0
351 );
352 thread::sleep(Duration::from_secs(1));
353 if iters >= total_iterations {
354 break;
355 }
356 old = iters;
357 }
358 });
359 self.available_techniques
360 .iter()
361 .permutations(tech_amount)
362 .par_bridge()
363 .for_each(|permutation| {
364 let s = usize::min(tech_amount, self.board_size);
365 for i in 0..=s {
366 let mut board: [Option<Technique>; 6] = [None; 6];
367 board.iter_mut().enumerate().for_each(|(k, v)| {
368 if k < i {
369 *v = Some(*permutation[k]);
370 }
371 });
372 let technique = permutation[i..].iter().map(|&&t| t).collect_vec();
373 let data = GameData::new(
374 self.initial_nesre,
375 self.initial_pet,
376 self.initial_gabe,
377 self.initial_shay,
378 self.board_size - i,
379 );
380 let game = Game {
381 data: RefCell::new(data),
382 board,
383 technique,
384 };
385 let rem = game.clone();
386 let (score, _) = game.run();
387 if score > best_score.lock().unwrap().unwrap_or(0) {
388 *best_game.lock().unwrap() = Some(rem);
389 *best_score.lock().unwrap() = Some(score);
390 }
391 }
392 *iters.write().unwrap() += s as u128;
393 });
394 handle.join().unwrap();
395 let game = best_game.lock().unwrap().clone();
396 game.unwrap()
397 }
398
399 pub fn find_best_v2(&self) -> Game {
400 let tech_amount = self.available_techniques.len();
401 let best_game: Mutex<Option<Game>> = Mutex::new(None);
402 let best_score: Mutex<Option<usize>> = Mutex::new(None);
403 self.find_best_inner(tech_amount, best_score, &best_game);
404 let game = best_game.lock().unwrap().clone();
405 game.unwrap()
406 }
407
408 pub fn find_best_v2_scilenced(&self) -> Game {
409 let tech_amount = self.available_techniques.len();
410 let best_game: Mutex<Option<Game>> = Mutex::new(None);
411 let best_score: Mutex<Option<usize>> = Mutex::new(None);
412 self.find_best_inner(tech_amount, best_score, &best_game);
413 let game = best_game.lock().unwrap().clone();
414 game.unwrap()
415 }
416
417 fn find_best_inner(
418 &self,
419 tech_amount: usize,
420 best_score: Mutex<Option<usize>>,
421 best_game: &Mutex<Option<Game>>,
422 ) {
423 use itertools::*;
424 self.available_techniques
425 .iter()
426 .permutations(tech_amount)
427 .par_bridge()
428 .progress_count(3628800)
429 .with_style(
430 ProgressStyle::with_template(
431 "[{elapsed_precise}] [{bar:40}] {pos:>7}/{len:7} {msg}",
432 )
433 .unwrap().progress_chars("=> "),
434 )
435 .for_each(|permutation| {
436 let s = usize::min(tech_amount, self.board_size);
437 for i in 0..=s {
438 let mut board: [Option<Technique>; 6] = [None; 6];
439 board.iter_mut().enumerate().for_each(|(k, v)| {
440 if k < i {
441 *v = Some(*permutation[k]);
442 }
443 });
444 let mut technique = vec![
445 Technique::ExtractI,
446 Technique::ExtractII,
447 Technique::ExtractIII,
448 ];
449 technique.append(&mut permutation[i..].iter().map(|&&t| t).collect_vec());
450 let data = GameData::new(
451 self.initial_nesre,
452 self.initial_pet,
453 self.initial_gabe,
454 self.initial_shay,
455 self.board_size - i,
456 );
457 let game = Game {
458 data: RefCell::new(data),
459 board,
460 technique,
461 };
462 let rem = game.clone();
463 let (score, _) = game.run();
464 if score > best_score.lock().unwrap().unwrap_or(0) {
465 *best_game.lock().unwrap() = Some(rem);
466 *best_score.lock().unwrap() = Some(score);
467 }
468 }
469 });
470 }
471}
472
473pub fn printer(
474 progress: Arc<RwLock<(u128, u128)>>,
475 total_iterations: u128,
476) -> thread::JoinHandle<()> {
477 let handle = thread::spawn(move || {
478 let mut old_iters = 0;
479 loop {
480 let (progress, iters) = progress.read().unwrap().to_owned();
481 println!(
482 "[{:06.2}%]: {:07} of {:07}, {:.2}k iter/s",
483 progress as f64 / total_iterations as f64 * 100.0,
484 progress,
485 total_iterations,
486 (iters - old_iters) as f64 / 1_000.0
487 );
488 old_iters = iters;
489 if progress >= total_iterations {
490 break;
491 }
492 thread::sleep(Duration::from_secs(1));
493 }
494 });
495 handle
496}