finish leg/game randomly (untested)

This commit is contained in:
Joseph Montanaro 2023-01-01 16:06:51 -08:00
parent b3e7ae7900
commit 45deb93a4f
4 changed files with 177 additions and 14 deletions

66
Cargo.lock generated
View File

@ -2,11 +2,18 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cup" name = "cup"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"enum-map", "enum-map",
"rand",
] ]
[[package]] [[package]]
@ -29,6 +36,29 @@ dependencies = [
"syn", "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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.49" version = "1.0.49"
@ -47,6 +77,36 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.107"
@ -63,3 +123,9 @@ name = "unicode-ident"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

View File

@ -7,3 +7,4 @@ edition = "2021"
[dependencies] [dependencies]
"enum-map" = "2.4.2" "enum-map" = "2.4.2"
"rand" = "0.8.5"

View File

@ -1,4 +1,9 @@
use enum_map::{Enum, EnumMap}; use enum_map::{Enum, EnumMap};
use rand::{
Rng,
seq::SliceRandom,
distributions::{Distribution, Uniform},
};
use crate::stack::Stack; use crate::stack::Stack;
@ -8,7 +13,7 @@ pub enum Color {
#[default] Red, Green, Blue, Yellow, Purple, #[default] Red, Green, Blue, Yellow, Purple,
} }
// const COLORS: Stack<Color, 5> = Stack::from([ // const COLORS: Stack<Color, 5> = Stack::from_array([
// Color::Red, // Color::Red,
// Color::Green, // Color::Green,
// Color::Blue, // Color::Blue,
@ -16,6 +21,14 @@ pub enum Color {
// Color::Purple, // Color::Purple,
// ]); // ]);
const COLORS: [Color; 5] = [
Color::Red,
Color::Green,
Color::Blue,
Color::Yellow,
Color::Purple,
];
type ColorStack = Stack<Color, 5>; type ColorStack = Stack<Color, 5>;
@ -68,6 +81,20 @@ impl Game {
Self::default() 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<Color, bool>) { fn set_state(&mut self, squares: [Square; 16], dice: EnumMap<Color, bool>) {
self.squares = squares; self.squares = squares;
self.dice = dice; 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 // returns winner if there is one
fn advance(&mut self, die: Color, roll: usize) -> Option<Color> { fn advance(&mut self, die: Color, roll: usize) -> Option<Color> {
let src_sq = self.camels[die]; let src_sq = self.camels[die];
@ -144,6 +160,49 @@ impl Game {
self.dice[die] = true; self.dice[die] = true;
None None
} }
fn finish_leg_random(&mut self) -> Option<Color> {
let mut rng = rand::thread_rng();
let mut leg_dice: Stack<Color, 5> = 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])); assert_eq!(game.squares[3].assume_stack(), &Stack::from([Red, Purple, Yellow]));
// B, _, G, RPY // B, _, G, RPY
} }
} }

View File

@ -1,4 +1,4 @@
use std::ops::Index; use std::ops::{Index, IndexMut, RangeFull};
use std::iter::IntoIterator; use std::iter::IntoIterator;
@ -35,6 +35,13 @@ impl<T, const S: usize> Stack<T, S> {
pub fn iter(&self) -> impl Iterator<Item = &T> { pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter().take(self.len) 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<T, const S: usize> Index<usize> for Stack<T, S> { impl<T, const S: usize> Index<usize> for Stack<T, S> {
type Output = T; type Output = T;
fn index(&self, index: usize) -> &T { fn index(&self, index: usize) -> &T {
&self.data[index] &self.data[index]
} }
} }
impl<T, const S: usize> Index<RangeFull> for Stack<T, S> {
type Output = [T];
fn index(&self, _index: RangeFull) -> &[T] {
&self.data[..self.len]
}
}
impl<T, const S: usize> IndexMut<RangeFull> for Stack<T, S> {
fn index_mut(&mut self, _index: RangeFull) -> &mut [T] {
&mut self.data[..self.len]
}
}
impl<I, T, const S: usize> From<I> for Stack<T, S> impl<I, T, const S: usize> From<I> for Stack<T, S>
where where
@ -223,4 +242,20 @@ mod test {
assert_eq!(it.next(), Some(&3)); assert_eq!(it.next(), Some(&3));
assert_eq!(it.next(), None); 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]);
}
} }