From 45deb93a4f88c3e43fa02be79ef4236c1b3d5b29 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Sun, 1 Jan 2023 16:06:51 -0800 Subject: [PATCH] finish leg/game randomly (untested) --- Cargo.lock | 66 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/game.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++-------- src/stack.rs | 39 ++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e16e568..92bfa5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "cup" version = "0.1.0" dependencies = [ "enum-map", + "rand", ] [[package]] @@ -29,6 +36,29 @@ dependencies = [ "syn", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.49" @@ -47,6 +77,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "syn" version = "1.0.107" @@ -63,3 +123,9 @@ name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/Cargo.toml b/Cargo.toml index 3ca664a..04d0d45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] "enum-map" = "2.4.2" +"rand" = "0.8.5" diff --git a/src/game.rs b/src/game.rs index 0a55e33..8be4481 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,9 @@ use enum_map::{Enum, EnumMap}; +use rand::{ + Rng, + seq::SliceRandom, + distributions::{Distribution, Uniform}, +}; use crate::stack::Stack; @@ -8,7 +13,7 @@ pub enum Color { #[default] Red, Green, Blue, Yellow, Purple, } -// const COLORS: Stack = Stack::from([ +// const COLORS: Stack = Stack::from_array([ // Color::Red, // Color::Green, // Color::Blue, @@ -16,6 +21,14 @@ pub enum Color { // Color::Purple, // ]); +const COLORS: [Color; 5] = [ + Color::Red, + Color::Green, + Color::Blue, + Color::Yellow, + Color::Purple, +]; + type ColorStack = Stack; @@ -68,6 +81,20 @@ impl Game { Self::default() } + // new game with random starting positions + fn new_random() -> Self { + let mut game = Self::default(); + let mut rng = rand::thread_rng(); + let mut dice = *&COLORS; + dice.shuffle(&mut rng); + for color in dice { + let roll = rng.gen_range(1..=3); + game.squares[roll - 1].assume_stack_mut().push(color); + game.camels[color] = roll - 1; + } + game + } + fn set_state(&mut self, squares: [Square; 16], dice: EnumMap) { self.squares = squares; self.dice = dice; @@ -80,17 +107,6 @@ impl Game { } } - fn update_positions(&mut self, target_sq: usize) { - match self.squares[target_sq] { - Square::Camels(stack) => { - for camel in stack.iter() { - self.camels[*camel] = target_sq; - } - } - _ => () - } - } - // returns winner if there is one fn advance(&mut self, die: Color, roll: usize) -> Option { let src_sq = self.camels[die]; @@ -144,6 +160,49 @@ impl Game { self.dice[die] = true; None } + + fn finish_leg_random(&mut self) -> Option { + let mut rng = rand::thread_rng(); + let mut leg_dice: Stack = Stack::new(); + for (color, rolled) in self.dice { + if !rolled { + leg_dice.push(color); + } + } + + (&mut leg_dice[..]).shuffle(&mut rng); + + for color in leg_dice.iter() { + let roll = rng.gen_range(1..=3); + if let Some(winner) = self.advance(*color, roll) { + return Some(winner); + } + } + None + } + + fn finish_game_random(&mut self) -> Color { + if let Some(winner) = self.finish_leg_random() { + return winner; + } + + // we are now guaranteed to be at the start of a new leg, + // so we don't need to check the dice state + let mut rng = rand::thread_rng(); + let roll_dist = Uniform::from(0..=3); + let mut dice = *&COLORS; // makes a copy of the constant + + loop { + // easiest if we shuffle at the start of the leg + dice.shuffle(&mut rng); + for i in 0..5 { + let roll = roll_dist.sample(&mut rng); + if let Some(winner) = self.advance(dice[i], roll) { + return winner; + } + } + } + } } @@ -187,4 +246,6 @@ mod test { assert_eq!(game.squares[3].assume_stack(), &Stack::from([Red, Purple, Yellow])); // B, _, G, RPY } + + } diff --git a/src/stack.rs b/src/stack.rs index 5a61d59..99f81e6 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,4 +1,4 @@ -use std::ops::Index; +use std::ops::{Index, IndexMut, RangeFull}; use std::iter::IntoIterator; @@ -35,6 +35,13 @@ impl Stack { pub fn iter(&self) -> impl Iterator { self.data.iter().take(self.len) } + + pub const fn from_array(array: [T; S]) -> Self { + Stack { + data: array, + len: S, + } + } } @@ -94,12 +101,24 @@ where T: Copy + Default impl Index for Stack { type Output = T; - fn index(&self, index: usize) -> &T { &self.data[index] } } +impl Index for Stack { + type Output = [T]; + fn index(&self, _index: RangeFull) -> &[T] { + &self.data[..self.len] + } +} + +impl IndexMut for Stack { + fn index_mut(&mut self, _index: RangeFull) -> &mut [T] { + &mut self.data[..self.len] + } +} + impl From for Stack where @@ -223,4 +242,20 @@ mod test { assert_eq!(it.next(), Some(&3)); assert_eq!(it.next(), None); } + + #[test] + fn test_from_array() { + let s = Stack::from_array([1, 2, 3]); + assert_eq!(s[0], 1); + assert_eq!(s[1], 2); + assert_eq!(s[2], 3); + assert_eq!(s.len(), 3); + } + + #[test] + fn test_slice_index() { + let mut s = Stack::<_, 5>::from([3, 4, 5]); + assert_eq!(s[..], [3, 4, 5]); + assert_eq!(&mut s[..], &mut [3, 4, 5]); + } }