From 86e7811a16cc00bf8007ed1533f9a7352ff7fef7 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Sep 2022 18:10:18 +0200 Subject: [PATCH] add code for 84 (WIP) --- src/euler84/Cargo.toml | 2 + src/euler84/src/lib.rs | 200 +++++++++++++++++++++++++++++++++++++--- src/euler84/src/main.rs | 7 +- 3 files changed, 196 insertions(+), 13 deletions(-) diff --git a/src/euler84/Cargo.toml b/src/euler84/Cargo.toml index 1979f36..b532ff6 100644 --- a/src/euler84/Cargo.toml +++ b/src/euler84/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" [dependencies] rand = "0.8.5" +strum = "0.24.1" +strum_macros = "0.24.1" diff --git a/src/euler84/src/lib.rs b/src/euler84/src/lib.rs index ba699a2..92af57f 100644 --- a/src/euler84/src/lib.rs +++ b/src/euler84/src/lib.rs @@ -1,7 +1,15 @@ +use std::collections::{HashMap, VecDeque}; +use std::iter::Cycle; + use rand::prelude::*; use rand::seq::IteratorRandom; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; -enum Squares { +const NUMBER_SQUARES: usize = 40; + +#[derive(Debug, EnumIter, PartialEq, Hash, Eq, PartialOrd)] +pub enum Squares { Go = 0, A1 = 1, CC1 = 2, @@ -9,7 +17,7 @@ enum Squares { T1 = 4, R1 = 5, B1 = 6, - Ch1 = 7, + CH1 = 7, B2 = 8, B3 = 9, Jail = 10, @@ -24,7 +32,7 @@ enum Squares { D3 = 19, FP = 20, E1 = 21, - Ch2 = 22, + CH2 = 22, E2 = 23, E3 = 24, R3 = 25, @@ -32,10 +40,10 @@ enum Squares { F2 = 27, U2 = 28, F3 = 29, - G2j = 30, + G2J = 30, G1 = 31, G2 = 32, - Cc3 = 33, + CC3 = 33, G3 = 34, R4 = 35, Ch3 = 36, @@ -44,11 +52,13 @@ enum Squares { H2 = 39, } +#[derive(Debug, EnumIter, Clone)] enum CommunityChest { AdvanceToGo, GoToJail, } +#[derive(Debug, EnumIter, Clone)] enum Chance { AdvanceToGo, GoToJail, @@ -62,28 +72,194 @@ enum Chance { GoBack3Squares, } +#[derive(Debug)] enum Dice { FourSided = 4, SixSided = 6, } impl Dice { - fn throw(&self, rng: &mut ThreadRng) -> isize { + fn throw(&self, rng: &mut ThreadRng) -> usize { match self { - Self::SixSided => (1..=6).choose(rng).unwrap(), - Self::FourSided => (1..=4).choose(rng).unwrap(), + Self::SixSided => (1..=6).choose(rng).unwrap() as usize, + Self::FourSided => (1..=4).choose(rng).unwrap() as usize, } } } -struct Monopoly { - position: Squares, +pub struct Monopoly { + rng: ThreadRng, + board: Cycle, dice: Dice, + community_pile: VecDeque, + chance_pile: VecDeque, + number_consecutive_doubles: usize, } impl Monopoly { - fn throw_dice(&mut self) { + fn throw_dice(&mut self) -> (usize, bool) { + let mut iter = (0..2).map(|_| self.dice.throw(&mut self.rng)); + let t1 = iter.next().unwrap(); + let t2 = iter.next().unwrap(); + let is_double = t1 == t2; + // dbg!(t1, t2); + (t1 + t2, is_double) + } + + #[allow(clippy::new_without_default)] + pub fn new() -> Self { let mut rng = rand::thread_rng(); - let result: isize = (0..2).map(|_| self.dice.throw(&mut rng)).sum(); + let community_pile = Self::gen_random_community_pile(&mut rng); + let chance_pile = Self::gen_random_chance_pile(&mut rng); + Self { + rng, + board: Squares::iter().cycle(), + dice: Dice::SixSided, + community_pile, + chance_pile, + number_consecutive_doubles: 0, + } + } + + fn gen_random_chance_pile(rng: &mut ThreadRng) -> VecDeque { + let mut instances: Vec<_> = Chance::iter().collect(); + instances.shuffle(rng); + instances.into() + } + + fn gen_random_community_pile(rng: &mut ThreadRng) -> VecDeque { + let mut instances: Vec<_> = CommunityChest::iter().collect(); + instances.shuffle(rng); + instances.into() + } + + #[must_use] + pub fn make_move(&mut self) -> Squares { + let (distance, is_double) = self.throw_dice(); + if self.check_too_many_doubles(is_double) { + return self.go_to_jail(); + } + + let square = self.board.nth(distance).unwrap(); + match square { + Squares::G2J => self.go_to_jail(), + Squares::CC1 | Squares::CC2 | Squares::CC3 => self.handle_community_chest(), + Squares::CH1 | Squares::CH2 => self.handle_chance(), + _ => square, + } + } + + fn go_to_jail(&mut self) -> Squares { + let _ = (&mut self.board).skip_while(|c| *c != Squares::Jail); + Squares::Jail + } + + fn handle_chance(&mut self) -> Squares { + let card = self.draw_from_chance_pile_and_put_at_bottom(); + // dbg!(card); + match card { + Chance::AdvanceToGo => self.advance_to_go(), + Chance::GoToJail => self.go_to_jail(), + Chance::GoToC1 => self.go_to_square(Squares::C1), + Chance::GoToE3 => self.go_to_square(Squares::E3), + Chance::GoToH2 => self.go_to_square(Squares::H2), + Chance::GoToR1 => self.go_to_square(Squares::R1), + Chance::GoToNextR | Chance::GoToNextRAgain => self.go_to_railway(), + Chance::GoToNextU => self.go_to_utility(), + Chance::GoBack3Squares => self.go_back_3_squares(), + } + } + + fn go_to_square(&mut self, square: Squares) -> Squares { + let _ = (&mut self.board).skip_while(|c| *c != square); + square + } + + fn go_to_squares(&mut self, squares: &[Squares]) -> Squares { + let result = self.board.find(|c| squares.iter().any(|s| s == c)).unwrap(); + assert!(squares.contains(&result)); + result + } + + fn handle_community_chest(&mut self) -> Squares { + let card = self.draw_from_community_chest_pile_and_put_at_bottom(); + match card { + CommunityChest::AdvanceToGo => self.advance_to_go(), + CommunityChest::GoToJail => self.go_to_jail(), + } + } + + fn advance_to_go(&mut self) -> Squares { + let _ = (&mut self.board).skip_while(|c| *c != Squares::Go); + Squares::Go + } + + fn draw_from_chance_pile_and_put_at_bottom(&mut self) -> &Chance { + let card = self.chance_pile.pop_front().unwrap(); + self.chance_pile.push_back(card); + self.chance_pile.back().unwrap() + } + + fn draw_from_community_chest_pile_and_put_at_bottom(&mut self) -> &CommunityChest { + let card = self.community_pile.pop_front().unwrap(); + self.community_pile.push_back(card); + self.community_pile.back().unwrap() + } + + fn go_to_railway(&mut self) -> Squares { + self.go_to_squares(&[Squares::R1, Squares::R2, Squares::R3, Squares::R4]) + } + + fn go_to_utility(&mut self) -> Squares { + self.go_to_squares(&[Squares::U1, Squares::U2]) + } + + fn go_back_3_squares(&mut self) -> Squares { + self.board.nth(NUMBER_SQUARES - 3).unwrap() + } + + fn check_too_many_doubles(&mut self, is_double: bool) -> bool { + if is_double { + self.number_consecutive_doubles += 1; + self.number_consecutive_doubles == 3 + } else { + self.number_consecutive_doubles = 0; + false + } + } +} + +pub struct Statistics { + monopoly: Monopoly, + iterations: usize, + fields_count: HashMap, +} + +impl Statistics { + pub fn new(monopoly: Monopoly, iterations: usize) -> Self { + Self { + monopoly, + iterations, + fields_count: HashMap::from_iter(Squares::iter().zip([0].into_iter().cycle())), + } + } + + pub fn run(&mut self) { + for _ in 0..self.iterations { + let square = self.monopoly.make_move(); + *self.fields_count.get_mut(&square).unwrap() += 1; + } + } + + pub fn show(&self) { + let mut result: Vec<(&Squares, f64)> = self + .fields_count + .iter() + .map(|(k, v)| (k, *v as f64 / self.iterations as f64 * 100.)) + .collect(); + result.sort_unstable_by(|(_, v1), (_, v2)| v2.partial_cmp(v1).unwrap()); + for (s, v) in result { + println!("{s:?}: {v}"); + } } } diff --git a/src/euler84/src/main.rs b/src/euler84/src/main.rs index e7a11a9..fbd0fae 100644 --- a/src/euler84/src/main.rs +++ b/src/euler84/src/main.rs @@ -1,3 +1,8 @@ +mod lib; + fn main() { - println!("Hello, world!"); + let monopoly = lib::Monopoly::new(); + let mut statistics = lib::Statistics::new(monopoly, 100_000); + statistics.run(); + statistics.show(); }